├── .github └── workflows │ ├── lint-test.yml │ └── release.yml ├── .helmignore ├── LICENSE ├── README.md ├── bitwarden.png └── charts └── bitwarden ├── Chart.yaml ├── ci └── noingress-nobackup-values.yaml ├── templates ├── NOTES.txt ├── _helpers.tpl ├── backup-claim.yaml ├── configmap.yaml ├── data-claim.yaml ├── database-backup.yaml ├── deployment.yaml ├── ingress.yaml ├── secret.yaml ├── service.yaml └── tests │ └── test-http.yaml └── values.yaml /.github/workflows/lint-test.yml: -------------------------------------------------------------------------------- 1 | name: Lint and Test Charts 2 | 3 | on: pull_request 4 | 5 | jobs: 6 | lint-test: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - name: Checkout 10 | uses: actions/checkout@v1 11 | 12 | - name: Set up Helm 13 | uses: azure/setup-helm@v1 14 | with: 15 | version: v3.6.3 16 | 17 | - uses: actions/setup-python@v2 18 | with: 19 | python-version: 3.7 20 | 21 | - name: Setup chart-testing 22 | uses: helm/chart-testing-action@v2.1.0 23 | 24 | - name: Run chart-testing (list-changed) 25 | id: list-changed 26 | run: | 27 | changed=$(ct list-changed) 28 | if [[ -n "$changed" ]]; then 29 | echo "::set-output name=changed::true" 30 | fi 31 | 32 | - name: Run chart-testing (lint) 33 | run: ct lint 34 | 35 | - name: Create kind cluster 36 | uses: helm/kind-action@v1.2.0 37 | if: steps.list-changed.outputs.changed == 'true' 38 | 39 | - name: Run chart-testing (install) 40 | run: ct install 41 | if: steps.list-changed.outputs.changed == 'true' 42 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release Charts 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | 8 | jobs: 9 | release: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: Checkout 13 | uses: actions/checkout@v1 14 | 15 | - name: Configure Git 16 | run: | 17 | git config user.name "$GITHUB_ACTOR" 18 | git config user.email "$GITHUB_ACTOR@users.noreply.github.com" 19 | 20 | - name: Run chart-releaser 21 | uses: helm/chart-releaser-action@v1.2.1 22 | env: 23 | CR_TOKEN: "${{ secrets.GITHUB_TOKEN }}" 24 | -------------------------------------------------------------------------------- /.helmignore: -------------------------------------------------------------------------------- 1 | # Patterns to ignore when building packages. 2 | # This supports shell glob matching, relative path matching, and 3 | # negation (prefixed with !). Only one pattern per line. 4 | .DS_Store 5 | # Common VCS dirs 6 | .git/ 7 | .gitignore 8 | .bzr/ 9 | .bzrignore 10 | .hg/ 11 | .hgignore 12 | .svn/ 13 | # Common backup files 14 | *.swp 15 | *.bak 16 | *.tmp 17 | *~ 18 | # Various IDEs 19 | .project 20 | .idea/ 21 | *.tmproj 22 | .vscode/ 23 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Kubernetes deployment via Helm 2 | [![Build Status](https://travis-ci.com/Skeen/helm-bitwarden_rs.svg?branch=master)](https://travis-ci.com/Skeen/helm-bitwarden_rs) 3 | Helm chart to deploy a fully functional and secure [`bitwarden_rs`](https://github.com/dani-garcia/bitwarden_rs) application in [Kubernetes](https://kubernetes.io/). 4 | 5 | ## Requirements 6 | Requires a Kubernetes cluster setup, with dns, storage and [Helm and Tiller](https://docs.helm.sh/) configured. 7 | 8 | A cluster for testing, can be setup (on Ubuntu) using: 9 | ``` 10 | snap install microk8s --classic 11 | microk8s.start 12 | microk8s.status --wait-ready 13 | microk8s.enable dns dashboard 14 | microk8s.status --wait-ready 15 | snap install helm --classic 16 | helm init --wait 17 | ``` 18 | 19 | ## Usage 20 | The minimal deployment using all default values; 21 | ``` 22 | DOMAIN=bitwarden.yourdomain.com 23 | helm install --wait --set "ingress.hosts={$DOMAIN},ingress.tls[0].hosts={$DOMAIN},ingress.tls[0].secretName=bitwarden-tls-secret" . 24 | ``` 25 | This will setup `bitwarden_rs` with a persistent storage and a backup volume, with backups being shot at 3:00 every night. 26 | 27 | HTTPS certificates will automatically be generated using [Let's Encrypt](https://letsencrypt.org/) and HTTPS will be terminated at the Ingress Controller. 28 | *This assumes that a [Kubernetes NGINX Ingress Controller](https://github.com/kubernetes/ingress-nginx) is running, and that [cert-manager](https://github.com/jetstack/cert-manager) has been set up and configured. See [here (ingress)](https://github.com/icicimov/kubernetes-bitwarden_rs#nginx-proxy) and [here (cert-manager)](https://github.com/icicimov/kubernetes-bitwarden_rs#lets-encrypt) for examples on how to set either up.* 29 | 30 | If you do not have Ingress and cert-manager set up, you can disable ingressing completely, by deploying with: 31 | ``` 32 | helm install --wait --set "ingress.enabled=false" 33 | ``` 34 | You will however need another way to access the bitwarden pod, for instance via port-forwarding: 35 | ``` 36 | export POD_NAME=$(kubectl get pods --namespace default -l "app.kubernetes.io/name=bitwarden" -o jsonpath="{.items[0].metadata.name}") 37 | kubectl port-forward $POD_NAME 31111:80 38 | ``` 39 | 40 | # Removal 41 | To remove the installation run: 42 | ``` 43 | helm delete --purge $(helm list | grep "bitwarden" | cut -f1) 44 | ``` 45 | 46 | 47 | ## Configuration 48 | Several konfiguration options are available, they can be seen in [`values.yaml`](https://github.com/Skeen/helm-bitwarden_rs/blob/master/charts/bitwarden/values.yaml), and override like above using `--set` or using `--values`, see more [here](https://docs.helm.sh/helm/#helm-install). 49 | 50 | ## Screenshot 51 | 52 | ![bitwarden_rs](bitwarden.png "bitwarden_rs") 53 | -------------------------------------------------------------------------------- /bitwarden.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Skeen/helm-bitwarden_rs/3d2b3cee513250e82b65c3bf72781eaf0989bdb0/bitwarden.png -------------------------------------------------------------------------------- /charts/bitwarden/Chart.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | appVersion: 1.21.0 3 | description: A Helm chart to install bitwarden_rs 4 | name: bitwarden 5 | version: 0.5.0 6 | home: https://github.com/Skeen/helm-bitwarden_rs 7 | maintainers: 8 | - name: Skeen 9 | email: sovende@gmail.com 10 | -------------------------------------------------------------------------------- /charts/bitwarden/ci/noingress-nobackup-values.yaml: -------------------------------------------------------------------------------- 1 | ingress: 2 | enabled: false 3 | backup: 4 | enabled: false 5 | -------------------------------------------------------------------------------- /charts/bitwarden/templates/NOTES.txt: -------------------------------------------------------------------------------- 1 | 1. Get the application URL by running these commands: 2 | {{- if .Values.ingress.enabled }} 3 | {{- range $host := .Values.ingress.hosts }} 4 | {{- range $.Values.ingress.paths }} 5 | http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host }}{{ .path }} 6 | {{- end }} 7 | {{- end }} 8 | {{- else if contains "NodePort" .Values.service.type }} 9 | export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "bitwarden.fullname" . }}) 10 | export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}") 11 | echo http://$NODE_IP:$NODE_PORT 12 | {{- else if contains "LoadBalancer" .Values.service.type }} 13 | NOTE: It may take a few minutes for the LoadBalancer IP to be available. 14 | You can watch the status of by running 'kubectl get svc -w {{ include "bitwarden.fullname" . }}' 15 | export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "bitwarden.fullname" . }} -o jsonpath='{.status.loadBalancer.ingress[0].ip}') 16 | echo http://$SERVICE_IP:{{ .Values.service.port }} 17 | {{- else if contains "ClusterIP" .Values.service.type }} 18 | export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "bitwarden.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}") 19 | echo "Visit http://127.0.0.1:8080 to use your application" 20 | kubectl port-forward $POD_NAME 8080:80 21 | {{- end }} 22 | -------------------------------------------------------------------------------- /charts/bitwarden/templates/_helpers.tpl: -------------------------------------------------------------------------------- 1 | {{/* vim: set filetype=mustache: */}} 2 | {{/* 3 | Expand the name of the chart. 4 | */}} 5 | {{- define "bitwarden.name" -}} 6 | {{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} 7 | {{- end -}} 8 | 9 | {{/* 10 | Create a default fully qualified app name. 11 | We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). 12 | If release name contains chart name it will be used as a full name. 13 | */}} 14 | {{- define "bitwarden.fullname" -}} 15 | {{- if .Values.fullnameOverride -}} 16 | {{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} 17 | {{- else -}} 18 | {{- $name := default .Chart.Name .Values.nameOverride -}} 19 | {{- if contains $name .Release.Name -}} 20 | {{- .Release.Name | trunc 63 | trimSuffix "-" -}} 21 | {{- else -}} 22 | {{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} 23 | {{- end -}} 24 | {{- end -}} 25 | {{- end -}} 26 | 27 | {{/* 28 | Create chart name and version as used by the chart label. 29 | */}} 30 | {{- define "bitwarden.chart" -}} 31 | {{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} 32 | {{- end -}} 33 | 34 | {{/* 35 | Create chart name and version as used by the chart label. 36 | */}} 37 | {{- define "bitwarden.ingress.annotations" -}} 38 | {{- if .Values.ingress.annotations -}} 39 | {{- toYaml .Values.ingress.annotations }} 40 | {{- else -}} 41 | kubernetes.io/ingress.class: nginx 42 | {{- if .Values.ingress.tls }} 43 | kubernetes.io/tls-acme: "true" 44 | kubernetes.io/ssl-redirect: "true" 45 | {{- end -}} 46 | {{- end -}} 47 | {{- end -}} 48 | -------------------------------------------------------------------------------- /charts/bitwarden/templates/backup-claim.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.storage.enabled -}} 2 | {{- if .Values.backup.enabled -}} 3 | {{- $fullName := include "bitwarden.fullname" . -}} 4 | apiVersion: v1 5 | kind: PersistentVolumeClaim 6 | metadata: 7 | name: {{ $fullName }}-backup-pv 8 | labels: 9 | app.kubernetes.io/name: {{ include "bitwarden.name" . }} 10 | helm.sh/chart: {{ include "bitwarden.chart" . }} 11 | app.kubernetes.io/instance: {{ .Release.Name }} 12 | app.kubernetes.io/managed-by: {{ .Release.Service }} 13 | spec: 14 | accessModes: 15 | - ReadWriteOnce 16 | resources: 17 | requests: 18 | storage: {{ .Values.backup.size }} 19 | {{- if .Values.backup.storageClassName }} 20 | storageClassName: {{ .Values.backup.storageClassName }} 21 | {{ end }} 22 | status: {} 23 | {{- end }} 24 | {{- end }} 25 | -------------------------------------------------------------------------------- /charts/bitwarden/templates/configmap.yaml: -------------------------------------------------------------------------------- 1 | {{- $fullName := include "bitwarden.fullname" . -}} 2 | apiVersion: v1 3 | kind: ConfigMap 4 | metadata: 5 | name: {{ $fullName }}-conf 6 | labels: 7 | app.kubernetes.io/name: {{ include "bitwarden.name" . }} 8 | helm.sh/chart: {{ include "bitwarden.chart" . }} 9 | app.kubernetes.io/instance: {{ .Release.Name }} 10 | app.kubernetes.io/managed-by: {{ .Release.Service }} 11 | data: 12 | {{- range $key, $val := .Values.bitwarden }} 13 | {{ $key | upper }}: {{ $val | quote }} 14 | {{- end}} 15 | ENABLE_DB_WAL: {{ .Values.database.wal | quote }} 16 | {{ if .Values.storage.enabled -}} 17 | DATA_FOLDER: {{ .Values.storage.path }} 18 | {{ end }} 19 | -------------------------------------------------------------------------------- /charts/bitwarden/templates/data-claim.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.storage.enabled -}} 2 | {{- $fullName := include "bitwarden.fullname" . -}} 3 | apiVersion: v1 4 | kind: PersistentVolumeClaim 5 | metadata: 6 | name: {{ $fullName }}-pv 7 | labels: 8 | app.kubernetes.io/name: {{ include "bitwarden.name" . }} 9 | helm.sh/chart: {{ include "bitwarden.chart" . }} 10 | app.kubernetes.io/instance: {{ .Release.Name }} 11 | app.kubernetes.io/managed-by: {{ .Release.Service }} 12 | spec: 13 | accessModes: 14 | - ReadWriteOnce 15 | resources: 16 | requests: 17 | storage: {{ .Values.storage.size }} 18 | {{- if .Values.storage.className }} 19 | storageClassName: {{ .Values.storage.className }} 20 | {{ end }} 21 | status: {} 22 | {{- end }} 23 | -------------------------------------------------------------------------------- /charts/bitwarden/templates/database-backup.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.storage.enabled -}} 2 | {{- if .Values.backup.enabled -}} 3 | {{- $fullName := include "bitwarden.fullname" . -}} 4 | {{- if .Capabilities.APIVersions.Has "batch/v1"}} 5 | apiVersion: batch/v1 6 | {{- else }} 7 | apiVersion: batch/v1beta1 8 | {{- end }} 9 | kind: CronJob 10 | metadata: 11 | name: {{ $fullName }}-database-backup 12 | labels: 13 | app.kubernetes.io/name: {{ include "bitwarden.name" . }} 14 | helm.sh/chart: {{ include "bitwarden.chart" . }} 15 | app.kubernetes.io/instance: {{ .Release.Name }} 16 | app.kubernetes.io/managed-by: {{ .Release.Service }} 17 | spec: 18 | schedule: {{ .Values.backup.schedule | quote }} 19 | # Disallow concurrency if writing the same file 20 | {{ if .Values.backup.timestamp -}} 21 | concurrencyPolicy: Allow 22 | {{ else }} 23 | concurrencyPolicy: Forbid 24 | {{ end }} 25 | jobTemplate: 26 | spec: 27 | template: 28 | spec: 29 | containers: 30 | - name: {{ $fullName }}-database-backup 31 | image: "{{ .Values.backup.image.repository }}:{{ .Values.backup.image.tag }}" 32 | imagePullPolicy: {{ .Values.backup.image.pullPolicy }} 33 | env: 34 | - name: SOURCE_DATABASE 35 | value: {{ .Values.storage.path}}/db.sqlite3 36 | - name: BACKUP_DATABASE 37 | value: {{ .Values.backup.path }}/backup.sqlite3 38 | - name: TIMESTAMP_BACKUP 39 | value: {{ .Values.backup.timestamp | quote}} 40 | - name: TIMESTAMP_FILE 41 | value: {{ .Values.backup.timestamp_file | quote}} 42 | volumeMounts: 43 | - mountPath: {{ .Values.storage.path }} 44 | name: data-storage 45 | - mountPath: {{ .Values.backup.path }} 46 | name: backup-storage 47 | {{- with .Values.backup.resources }} 48 | resources: 49 | {{- toYaml . | nindent 14 }} 50 | {{- end }} 51 | volumes: 52 | - name: data-storage 53 | persistentVolumeClaim: 54 | claimName: {{ $fullName }}-pv 55 | - name: backup-storage 56 | persistentVolumeClaim: 57 | claimName: {{ $fullName }}-backup-pv 58 | restartPolicy: OnFailure 59 | {{- with .Values.backup.nodeSelector }} 60 | nodeSelector: 61 | {{- toYaml . | nindent 12 }} 62 | {{- end }} 63 | {{- with .Values.backup.affinity }} 64 | affinity: 65 | {{- toYaml . | nindent 12 }} 66 | {{- end }} 67 | {{- with .Values.backup.tolerations }} 68 | tolerations: 69 | {{- toYaml . | nindent 12 }} 70 | {{- end }} 71 | {{- end }} 72 | {{- end }} 73 | -------------------------------------------------------------------------------- /charts/bitwarden/templates/deployment.yaml: -------------------------------------------------------------------------------- 1 | {{- $fullName := include "bitwarden.fullname" . -}} 2 | apiVersion: apps/v1 3 | kind: Deployment 4 | metadata: 5 | name: {{ $fullName }} 6 | labels: 7 | app.kubernetes.io/name: {{ include "bitwarden.name" . }} 8 | helm.sh/chart: {{ include "bitwarden.chart" . }} 9 | app.kubernetes.io/instance: {{ .Release.Name }} 10 | app.kubernetes.io/managed-by: {{ .Release.Service }} 11 | spec: 12 | replicas: 1 13 | selector: 14 | matchLabels: 15 | app.kubernetes.io/name: {{ include "bitwarden.name" . }} 16 | app.kubernetes.io/instance: {{ .Release.Name }} 17 | template: 18 | metadata: 19 | labels: 20 | app.kubernetes.io/name: {{ include "bitwarden.name" . }} 21 | app.kubernetes.io/instance: {{ .Release.Name }} 22 | spec: 23 | {{- if .Values.securityContext.enabled }} 24 | securityContext: 25 | fsGroup: {{ .Values.securityContext.fsGroup }} 26 | {{- end }} 27 | containers: 28 | - name: {{ .Chart.Name }} 29 | image: "{{ .Values.deployment.image.repository }}:{{ .Values.deployment.image.tag }}" 30 | imagePullPolicy: {{ .Values.deployment.image.pullPolicy }} 31 | envFrom: 32 | - configMapRef: 33 | name: {{ $fullName }}-conf 34 | {{- if or (eq .Values.database.type "mysql") (eq .Values.database.type "postgresql") }} 35 | - secretRef: 36 | {{- if .Values.database.existingSecret }} 37 | name: {{ .Values.database.existingSecret }} 38 | {{- else }} 39 | name: {{ $fullName }}-database 40 | {{- end }} 41 | {{- end }} 42 | {{- if .Values.deployment.secrets }} 43 | {{- range .Values.deployment.secrets }} 44 | - secretRef: 45 | name: {{ . }} 46 | {{- end }} 47 | {{- end }} 48 | ports: 49 | - name: http 50 | containerPort: {{ .Values.bitwarden.rocket_port }} 51 | protocol: TCP 52 | {{- if .Values.bitwarden.websocket_enabled }} 53 | - name: ws 54 | containerPort: {{ .Values.bitwarden.websocket_port }} 55 | protocol: TCP 56 | {{- end }} 57 | livenessProbe: 58 | httpGet: 59 | path: / 60 | port: http 61 | readinessProbe: 62 | httpGet: 63 | path: / 64 | port: http 65 | {{- if .Values.securityContext.enabled }} 66 | securityContext: 67 | runAsNonRoot: {{ .Values.securityContext.runAsNonRoot }} 68 | runAsUser: {{ .Values.securityContext.runAsUser }} 69 | {{- end }} 70 | {{ if .Values.storage.enabled -}} 71 | volumeMounts: 72 | - mountPath: {{ .Values.storage.path }} 73 | name: data-storage 74 | {{ end }} 75 | {{- with .Values.deployment.resources }} 76 | resources: 77 | {{- toYaml . | nindent 10 }} 78 | {{- end }} 79 | {{ if .Values.storage.enabled -}} 80 | volumes: 81 | - name: data-storage 82 | persistentVolumeClaim: 83 | claimName: {{ $fullName }}-pv 84 | {{ end }} 85 | {{- with .Values.deployment.nodeSelector }} 86 | nodeSelector: 87 | {{- toYaml . | nindent 8 }} 88 | {{- end }} 89 | {{- with .Values.deployment.affinity }} 90 | affinity: 91 | {{- toYaml . | nindent 8 }} 92 | {{- end }} 93 | {{- with .Values.deployment.tolerations }} 94 | tolerations: 95 | {{- toYaml . | nindent 8 }} 96 | {{- end }} 97 | -------------------------------------------------------------------------------- /charts/bitwarden/templates/ingress.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.ingress.enabled -}} 2 | {{- $fullName := include "bitwarden.fullname" . -}} 3 | {{- $ingressPaths := .Values.ingress.paths -}} 4 | {{- $newNetworkingAPI := .Capabilities.APIVersions.Has "networking.k8s.io/v1" }} 5 | {{- $webSocketEnabled := .Values.bitwarden.websocket_enabled }} 6 | {{- if $newNetworkingAPI }} 7 | apiVersion: networking.k8s.io/v1 8 | {{- else }} 9 | apiVersion: extensions/v1beta1 10 | {{- end }} 11 | kind: Ingress 12 | metadata: 13 | name: {{ $fullName }}-ingress 14 | labels: 15 | app.kubernetes.io/name: {{ include "bitwarden.name" . }} 16 | helm.sh/chart: {{ include "bitwarden.chart" . }} 17 | app.kubernetes.io/instance: {{ .Release.Name }} 18 | app.kubernetes.io/managed-by: {{ .Release.Service }} 19 | annotations: 20 | {{- include "bitwarden.ingress.annotations" . | nindent 4 }} 21 | spec: 22 | {{- if .Values.ingress.className }} 23 | ingressClassName: {{ .Values.ingress.className }} 24 | {{- end }} 25 | {{- if .Values.ingress.tls }} 26 | tls: 27 | {{- range .Values.ingress.tls }} 28 | - hosts: 29 | {{- range .hosts }} 30 | - {{ . | quote }} 31 | {{- end }} 32 | secretName: {{ .secretName }} 33 | {{- end }} 34 | {{- end }} 35 | rules: 36 | {{- range .Values.ingress.hosts }} 37 | - host: {{ . | quote }} 38 | http: 39 | paths: 40 | {{- range $ingressPaths }} 41 | - path: {{ .path }} 42 | pathType: {{ .pathType }} 43 | backend: 44 | {{- if $newNetworkingAPI }} 45 | service: 46 | name: {{ $fullName }} 47 | port: 48 | name: http 49 | {{- else }} 50 | serviceName: {{ $fullName }} 51 | servicePort: http 52 | {{- end }} 53 | {{- end }} 54 | {{- if $webSocketEnabled }} 55 | - path: /notifications/hub 56 | pathType: ImplementationSpecific 57 | backend: 58 | {{- if $newNetworkingAPI }} 59 | service: 60 | name: {{ $fullName }} 61 | port: 62 | name: ws 63 | {{- else }} 64 | serviceName: {{ $fullName }} 65 | servicePort: ws 66 | {{- end }} 67 | - path: /notifications/hub/negotiate 68 | pathType: ImplementationSpecific 69 | backend: 70 | {{- if $newNetworkingAPI }} 71 | service: 72 | name: {{ $fullName }} 73 | port: 74 | name: http 75 | {{- else }} 76 | serviceName: {{ $fullName }} 77 | servicePort: http 78 | {{- end }} 79 | {{- end }} 80 | {{- end }} 81 | {{- end }} 82 | -------------------------------------------------------------------------------- /charts/bitwarden/templates/secret.yaml: -------------------------------------------------------------------------------- 1 | {{- if or (eq .Values.database.type "mysql") (eq .Values.database.type "postgresql") -}} 2 | {{- if not .Values.database.existingSecret -}} 3 | {{- $fullName := include "bitwarden.fullname" . -}} 4 | apiVersion: v1 5 | kind: Secret 6 | metadata: 7 | name: {{ $fullName }}-database 8 | labels: 9 | app.kubernetes.io/name: {{ include "bitwarden.name" . }} 10 | helm.sh/chart: {{ include "bitwarden.chart" . }} 11 | app.kubernetes.io/instance: {{ .Release.Name }} 12 | app.kubernetes.io/managed-by: {{ .Release.Service }} 13 | data: 14 | DATABASE_URL: {{ .Values.database.url | b64enc }} 15 | --- 16 | {{- end }} 17 | {{- end }} 18 | -------------------------------------------------------------------------------- /charts/bitwarden/templates/service.yaml: -------------------------------------------------------------------------------- 1 | {{- $fullName := include "bitwarden.fullname" . -}} 2 | apiVersion: v1 3 | kind: Service 4 | metadata: 5 | name: {{ $fullName }} 6 | labels: 7 | app.kubernetes.io/name: {{ include "bitwarden.name" . }} 8 | helm.sh/chart: {{ include "bitwarden.chart" . }} 9 | app.kubernetes.io/instance: {{ .Release.Name }} 10 | app.kubernetes.io/managed-by: {{ .Release.Service }} 11 | spec: 12 | type: {{ .Values.service.type }} 13 | ports: 14 | - port: {{ .Values.service.port }} 15 | targetPort: {{ .Values.service.target_port }} 16 | protocol: TCP 17 | name: http 18 | {{- if .Values.bitwarden.websocket_enabled }} 19 | - port: {{ .Values.bitwarden.websocket_port }} 20 | targetPort: {{ .Values.bitwarden.websocket_port }} 21 | protocol: TCP 22 | name: ws 23 | {{- end }} 24 | selector: 25 | app.kubernetes.io/name: {{ include "bitwarden.name" . }} 26 | app.kubernetes.io/instance: {{ .Release.Name }} 27 | -------------------------------------------------------------------------------- /charts/bitwarden/templates/tests/test-http.yaml: -------------------------------------------------------------------------------- 1 | {{- $fullName := include "bitwarden.fullname" . -}} 2 | apiVersion: v1 3 | kind: Pod 4 | metadata: 5 | name: "{{ .Release.Name }}-http-test" 6 | annotations: 7 | "helm.sh/hook": test-success 8 | spec: 9 | containers: 10 | - name: curl 11 | image: curlimages/curl 12 | command: ["sh", "-c", "set -o pipefail && curl -s {{ $fullName }}:{{ .Values.service.port }} | grep 'Bitwarden Web Vault'"] 13 | restartPolicy: Never 14 | -------------------------------------------------------------------------------- /charts/bitwarden/values.yaml: -------------------------------------------------------------------------------- 1 | # Override all names 2 | nameOverride: "" 3 | fullnameOverride: "" 4 | 5 | # Bitwarden configuration. 6 | # The below values are used to construct a config map, which will be provided 7 | # to the bitwarden_rs container on start up. 8 | # For details about the options, see: https://github.com/dani-garcia/bitwarden_rs#configuring-bitwarden-service 9 | # 10 | # Besides the options listed below, Rocket can be further configured (though it shouldn't be required). 11 | # See: https://github.com/dani-garcia/bitwarden_rs#other-configuration 12 | bitwarden: 13 | # # Allow registration of new users 14 | # # See: https://github.com/dani-garcia/bitwarden_rs#disable-registration-of-new-users 15 | # signups_allowed: true 16 | 17 | # # Allow current users invite new users even if registrations are otherwise disabled. 18 | # # See: https://github.com/dani-garcia/bitwarden_rs#disable-invitations 19 | # invitations_allowed: true 20 | 21 | # # Email of the unique server administrator account 22 | # # See: https://github.com/dani-garcia/bitwarden_rs#configure-server-administrator 23 | # server_admin_email: admin@example.com 24 | 25 | # # Websocket support for notifications 26 | # # See: https://github.com/dani-garcia/bitwarden_rs#enabling-websocket-notifications 27 | websocket_enabled: true 28 | websocket_port: 3012 29 | 30 | # # Needed for U2F authentification 31 | # # See: https://github.com/dani-garcia/bitwarden_rs#enabling-u2f-authentication 32 | # # TODO: Not supported currently 33 | # # TODO: Automatically template this into the ingress configuration 34 | # domain: https://bw.domain.tld 35 | 36 | # # Configuration of YubiKey OTP 37 | # # See: https://github.com/dani-garcia/bitwarden_rs#enabling-yubikey-otp-authentication 38 | # yubico_client_id: YUBICO_CLIENT_ID 39 | # yubico_secret_key: YUBICO_SECRET_KEY 40 | 41 | # # API request size limits 42 | # # See: https://github.com/dani-garcia/bitwarden_rs#changing-the-api-request-size-limit 43 | # rocket_limits: "{json=10485760}" 44 | 45 | # # Number of workers to spin up for the service 46 | # # See: https://github.com/dani-garcia/bitwarden_rs#changing-the-number-of-workers 47 | # rocket_workers: 10 48 | 49 | # # SMTP settings, for sending out emails 50 | # # See: https://github.com/dani-garcia/bitwarden_rs/blob/master/README.md#smtp-configuration 51 | # smtp_host: smtp.domain.tld 52 | # smtp_from: bitwarden@domain.tld 53 | # smtp_port: 587 54 | # smtp_ssl: true 55 | # smtp_username: username 56 | # smtp_password: password 57 | 58 | # Show password hint instead of sending it via email 59 | # See: https://github.com/dani-garcia/bitwarden_rs#password-hint-display 60 | show_password_hints: false 61 | 62 | # Enable Vault interface, when disabled, only API is served 63 | # See: https://github.com/dani-garcia/bitwarden_rs#disabling-or-overriding-the-vault-interface-hosting 64 | # TODO: enable settings web-vault content; init-container + shared storage + web-vault path? 65 | web_vault_enabled: true 66 | 67 | # # Logging to a specific file 68 | # # See: https://github.com/dani-garcia/bitwarden_rs#logging 69 | # log_file=/data/bitwarden.log 70 | 71 | # Port to serve http requests on. Define it accordingly to the service's target_port 72 | rocket_port: 8080 73 | 74 | # NOTE: HTTPS cannot be set here, as it is derived from the ingress configuration below. 75 | # NOTE: DATA_FOLDER cannot be set here, as it is derived from the storage.path below. 76 | # NOTE: ENABLE_DB_WAL cannot be set here, as it is derived from the database.wal below. 77 | # TODO: Support ATTACHMENTS_FOLDER, ICON_CACHE_FOLDER 78 | # TODO: Support fail2ban? 79 | 80 | database: 81 | # Database type, 82 | # must be one of: 'sqlite', 'mysql' or 'postgresql'. 83 | type: sqlite 84 | # Enable DB Write-Ahead-Log for SQLite, 85 | # disabled for other databases. https://github.com/dani-garcia/bitwarden_rs/wiki/Running-without-WAL-enabled 86 | wal: true 87 | # URL for external databases (mysql://user:pass@host:port/database or postgresql://user:pass@host:port/database). 88 | # MySQL/MariaDB: https://github.com/dani-garcia/vaultwarden/wiki/Using-the-MariaDB-%28MySQL%29-Backend 89 | # Postgresql: https://github.com/dani-garcia/vaultwarden/wiki/Using-the-PostgreSQL-Backend 90 | url: "" 91 | # Otherwise you can use an existing secret with key `DATABASE_URL` 92 | existingSecret: null 93 | 94 | deployment: 95 | # Image used for the deployment 96 | # See: https://www.github.com/dani-garcia/bitwarden_rs 97 | image: 98 | repository: vaultwarden/server 99 | tag: 1.21.0 100 | pullPolicy: IfNotPresent 101 | # Resources, etc, for the deployment pod 102 | resources: {} 103 | nodeSelector: {} 104 | tolerations: [] 105 | affinity: {} 106 | 107 | # Passwords and sensitive data can also be provided via secrets. This can be used for ADMIN_TOKEN and SMTP_PASSWORD. 108 | # Just provide the names of your secret resources that are already deployed to your namespace. 109 | secrets: [] 110 | 111 | securityContext: 112 | enabled: false 113 | runAsNonRoot: true 114 | fsGroup: 1000 115 | runAsUser: 1000 116 | 117 | # Should not be changed 118 | service: 119 | type: ClusterIP 120 | port: 80 121 | target_port: 8080 122 | 123 | 124 | # Settings regarding persistent storage 125 | # TODO: Support ATTACHMENTS_FOLDER + ICON_CACHE_FOLDER 126 | # See: https://github.com/dani-garcia/bitwarden_rs#changing-persistent-data-location 127 | storage: 128 | # Whether storing persistent data is enabled or not 129 | enabled: true 130 | # Where to store persistent data 131 | path: "/bw-data" 132 | # The maximum size of the persisted data 133 | size: 100Mi 134 | # what storge name to be used for data storage 135 | # className: "gp2" 136 | 137 | 138 | # Settings regarding persistent backup storage 139 | backup: 140 | # Whether backup is enabled or not 141 | enabled: true 142 | # Where to store the backup data 143 | path: "/bw-backup" 144 | # The maximum size of the persisted backup data 145 | size: 100Mi 146 | 147 | # what storge name to be used for back up storage 148 | # storageClassName: "gp2" 149 | 150 | # Cron schedule for the backup job (currently at 3:00 every night) 151 | schedule: "* 3 * * *" 152 | 153 | # Name the backup backup.sqlite3[TIMESTAMP] instead of backup.sqlite3 154 | timestamp: false 155 | # Create a file containing the timestamp of the backup 156 | timestamp_file: "/bw-backup/timestamp" 157 | 158 | # Image used for backup run 159 | # See: https://github.com/Skeen/bitwarden_rs_backup 160 | image: 161 | repository: skeen/bitwarden_rs_backup 162 | tag: latest 163 | pullPolicy: Always 164 | 165 | # Resources, etc, for the backup pod(s) 166 | resources: {} 167 | nodeSelector: {} 168 | tolerations: [] 169 | affinity: {} 170 | 171 | 172 | # Ingress configuration 173 | ingress: 174 | enabled: true 175 | annotations: {} 176 | # kubernetes.io/ingress.class: nginx 177 | # kubernetes.io/tls-acme: "true" 178 | # kubernetes.io/ssl-redirect: "true" 179 | # className: nginx 180 | paths: 181 | - path: '/' 182 | pathType: ImplementationSpecific 183 | hosts: 184 | - bitwarden.domain.tld # host 185 | tls: 186 | - secretName: bitwarden-tls-secret 187 | hosts: 188 | - bitwarden.domain.tld # host 189 | --------------------------------------------------------------------------------