Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

VaultWarden backup: #148

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion charts/vaultwarden/Chart.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,5 @@ maintainers:
- name: guerzon
email: [email protected]
url: https://github.com/guerzon
version: 0.31.2
version: 0.31.3
kubeVersion: ">=1.12.0-0"
101 changes: 101 additions & 0 deletions charts/vaultwarden/backup.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
# Backup

This backup solution is designed for a SQLite local database and single-instance VaultWarden deployment. It can also be used for attachments, while persistent PostgreSQL/MySQL backups should be handled separately.

The backup utilizes the [vaultwarden-backup](https://github.com/ttionya/vaultwarden-backup) solution. Each scheduled backup creates an additional sidecar vaultwarden-backup container where the data folder is shared between the VaultWarden container and the backup sidecar container.

## VaultWarden Settings

For the backup to access the data, VaultWarden must run with an appropriate security context, matching the backup container. For example:

```yaml
podSecurityContext:
## @param runAsGroup group ID for VaultWarden and backup run with
## Same as default user for vaultwarden-backup
runAsUser: 1100
runAsGroup: 1100
fsGroup: 1100
```

## Parameters


| Name | Description | Value |
|-------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------|
| `backup.enabled` | Enable the backup | true/false |
| `backup.image` | Docker image, see https://hub.docker.com/r/ttionya/vaultwarden-backup | <docker image> |
| `backup.rcloneConfig` | [rClone config](https://github.com/ttionya/vaultwarden-backup?tab=readme-ov-file#configure-rclone-%EF%B8%8F-must-read-%EF%B8%8F). Recommended to keep it secure, sops, helm secrets | <config> |
| `backup.remoteName` | Backup remote name, see [RCLONE_REMOTE_NAME](https://github.com/ttionya/vaultwarden-backup?tab=readme-ov-file#rclone_remote_name) | |
| `backup.globalFlags` | rClone global flags [RCLONE_GLOBAL_FLAG](https://github.com/ttionya/vaultwarden-backup?tab=readme-ov-file#rclone_global_flag) | |
| `backup.zipPassword` | Password to encrypt backup archive with, see [ZIP_PASSWORD](https://github.com/ttionya/vaultwarden-backup?tab=readme-ov-file#zip_password) | |
| `backup.healthcheckPingKey` | See [Ping, Healthchecks.io](https://github.com/ttionya/vaultwarden-backup?tab=readme-ov-file#ping) | |
| `backup.timezone` | Backup timezone, see [TIMEZONE](https://github.com/ttionya/vaultwarden-backup?tab=readme-ov-file#timezone). | UTC |
| `backup.smtp...` | SMTP parameters, [Mail](https://github.com/ttionya/vaultwarden-backup?tab=readme-ov-file#mail) | |
| `backup.backups` | Array of backups, each of it's own schedule | |
| `backup.backups[].name` | Backup name, container to be named after it | "hourly", "weekly" etc |
| `backup.backups[].schedule` | Cron job syntax schedule | "5 * * * *" |
| `backup.backups[].keepDays` | Backup to be delete after these N days. 0 - keep forever | 7, 0 ... |
| `backup.backups[].fileDateSuffix` | Suffix for the each archive file | "-%H-%M-%S" |
| `backup.backups[].healthCheckPing` | healthchecks.io ping url, see [Ping](). Such as `https://hc-ping.com/{ping_key}/vaultwarden-<name>` Set it on most-frequent backup | https://hc-ping.com/{ping_key}/vaultwarden-main |
| | | |
| | | |

### Example
```yaml
podSecurityContext:
## @param runAsGroup group ID for VaultWarden and backup run with
## Same as default user for vaultwarden-backup
runAsUser: 1100
runAsGroup: 1100
fsGroup: 1100

backup:
enabled: true
backups:
- name: hourly
remoteDir: "/vaultWarden-main/hourly/"
# Every hour at 5 mins
cron: "5 * * * *"
keepDays: 7
fileDateSuffix: "-%H-%M-%S"
healthCheckPing: "https://hc-ping.com/<my-key>/vaultwarden-main"

- name: daily
remoteDir: "/vaultWarden-main/daily/"
# every day at 03:23
cron: "23 3 * * *"
keepDays: 60
fileDateSuffix: "-%H-%M-%S"

- name: monthly
remoteDir: "/vaultWarden-main/monthly/"
# every 20th day at 03:47
cron: "47 3 20 * *"
# Keep forever
keepDays: 0
fileDateSuffix: "-%H-%M-%S"
```

## Restore

If the deployment is lost, including PVs, it can be restored from the backup:

1. Download the backup archive from remote storage.
2. Have the `zipPassword` ready for unzipping the archive.
3. Run the restore script (requires functional kubectl):

`./restore.sh --archive <archive-file> --release <helm-release> --storage-class <Storage class>`

Example:

`./restore.sh --archive /tmp/backup.20221103-19-05-01.zip --release vaultwarden --storage-class "local-path"`

Kubernetes Namespace and context can be set with kubectl beforehand or passed as arguments.

The script will create a PV and PVC in the target cluster and namespace. When the VaultWarden helm chart is deployed, it will use this PV and PVC.

Use kubectl to check if another PVC is created in case of a mismatch. Adjust script parameters accordingly.




9 changes: 8 additions & 1 deletion charts/vaultwarden/templates/_helpers.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -91,4 +91,11 @@ Determine whether to use deployment or statefulset
{{- "StatefulSet" }}
{{- end }}
{{- end }}
{{- end }}
{{- end }}

{{/*
Do backup? Needs backup enabled and persistence
*/}}
{{- define "vaultwarden.doBackup" -}}
{{- and .Values.backup.enabled (hasKey .Values.storage "data") -}}
{{- end }}
86 changes: 82 additions & 4 deletions charts/vaultwarden/templates/_podSpec.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,30 @@ tolerations:
securityContext:
{{- toYaml . | nindent 2 }}
{{- end }}
{{- with .Values.initContainers }}
{{- if or .Values.initContainers (eq (include "vaultwarden.doBackup" .) "true") }}
initContainers:
{{- with .Values.initContainers }}
{{- toYaml . | nindent 2 }}
{{- end }}

{{- if eq (include "vaultwarden.doBackup" .) "true" }}
# Copy rclone config from read-only secret mount to writable share
# https://github.com/rclone/rclone/issues/3655
- name: copy-config
image: busybox:latest
command: ["sh", "-c", "cp -v /src-config/rclone.conf /config/"]
volumeMounts:
- name: backup-secret-conf
mountPath: "/src-config/"
readOnly: true
- name: config
mountPath: "/config/"
{{- end }}
{{- end }}
{{- if not .Values.enableServiceLinks }}
enableServiceLinks: false
{{- end }}

containers:
- image: {{ .Values.image.registry }}/{{ .Values.image.repository }}:{{ .Values.image.tag }}
imagePullPolicy: {{ .Values.image.pullPolicy }}
Expand Down Expand Up @@ -178,16 +195,77 @@ containers:
successThreshold: {{ .Values.startupProbe.successThreshold }}
failureThreshold: {{ .Values.startupProbe.failureThreshold }}
{{- end }}
{{- if eq (include "vaultwarden.doBackup" .) "true" }}
{{- range .Values.backup.backups }}
- image: {{ $.Values.backup.image }}
name: backup-{{ .name }}
securityContext:
allowPrivilegeEscalation: false
env:
- name: DATA_DIR
value: {{ default "/data" .path | quote }}
- name: RCLONE_REMOTE_NAME
value: {{ $.Values.backup.remoteName | quote }}
- name: RCLONE_REMOTE_DIR
value: {{ .remoteDir | quote }}
- name: RCLONE_GLOBAL_FLAG
value: "{{ $.Values.backup.globalFlags }} --config /config/rclone.conf"
- name: CRON
value: {{ .cron | quote }}
- name: ZIP_PASSWORD
value: {{ $.Values.backup.zipPassword | quote }}
- name: BACKUP_KEEP_DAYS
value: {{ .keepDays | quote }}
- name: BACKUP_FILE_DATE_SUFFIX
value: {{ .fileDateSuffix | quote }}
- name: TIMEZONE
value: {{ $.Values.backup.timezone | quote }}
{{- if .healthCheckPing }}
- name: PING_URL
value: {{ (tpl .healthCheckPing $) | quote }}
{{- end }}
{{- if $.Values.backup.smtp.enabled }}
- name: MAIL_SMTP_ENABLE
value: "true"
- name: MAIL_SMTP_VARIABLES
value: {{ $.Values.backup.smtp.smtpVariables | quote }}
- name: MAIL_TO
value: {{ $.Values.backup.smtp.mailTo | quote }}
- name: MAIL_WHEN_SUCCESS
value: {{ $.Values.backup.smtp.mailWhenSuccess | quote }}
- name: MAIL_WHEN_FAILURE
value: {{ $.Values.backup.smtp.mailWhenFailure | quote }}
{{- end }}
# When run as non-root, script cannot create crontabs in home folcer
volumeMounts:
- name: vaultwarden-data
mountPath: {{ default "/data" $.Values.storage.data.path }}
- name: config
mountPath: "/config/"
{{- end }}
{{- end }}
{{- with .Values.sidecars }}
{{- toYaml . | nindent 2 }}
{{- end }}
{{- if .Values.storage.existingVolumeClaim }}
{{- with .Values.storage.existingVolumeClaim }}
{{- if or .Values.storage.existingVolumeClaim (eq (include "vaultwarden.doBackup" .) "true" ) }}
volumes:
{{- if .Values.storage.existingVolumeClaim }}
{{- with .Values.storage.existingVolumeClaim }}
- name: vaultwarden-data
persistentVolumeClaim:
claimName: {{ .claimName }}
{{- end }}
{{- end }}
{{- end }}
{{- if eq (include "vaultwarden.doBackup" .) "true" }}
- name: backup-secret-conf
secret:
secretName: {{ include "vaultwarden.fullname" . }}-rclone
optional: false
# readable by user/owner
defaultMode: 0400
- name: config
emptyDir: {}
{{- end }}
{{- end }}
{{- if .Values.serviceAccount.create }}
serviceAccountName: {{ .Values.serviceAccount.name }}
Expand Down
12 changes: 12 additions & 0 deletions charts/vaultwarden/templates/secret-backup.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{{- if eq (include "vaultwarden.doBackup" .) "true" }}
apiVersion: v1
kind: Secret
metadata:
name: {{ include "vaultwarden.fullname" . }}-rclone
namespace: {{ .Release.Namespace }}
labels:
app.kubernetes.io/component: vaultwarden
type: Opaque
data:
rclone.conf: {{ .Values.backup.rcloneConfig | b64enc | quote }}
{{- end }}
49 changes: 49 additions & 0 deletions charts/vaultwarden/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -772,3 +772,52 @@ ingress:
## - Add support for using cert-manager.
## - Support for multiple TLS hostnames.
##

## @section Backup Configuration
## https://github.com/ttionya/vaultwarden-backup
backup:
## @param backup.enabled Enable backup
##
enabled: false

## @param backup.image Backup image FQDN
##
image: "ttionya/vaultwarden-backup:1.22.0"

## @param backup.rcloneConfig Raw rClone backup
## Recommended to provide as an encrypted content
rcloneConfig: ""

remoteName: "vaultWarden"

globalFlags: ""

# cron: "5 * * * *"

zipPassword: "changeme"

# https://blog.healthchecks.io/2021/09/new-feature-slug-urls/
# Obtain from https://healthchecks.io/projects/<project-id>/settings/
healthcheckPingKey: "get-from-healthchecks.io"

timezone: "UTC"

smtp:
enable: false
smtpVariables: ""
mailTo: ""
mailWhenSuccess: false
mailWhenFailure: true

## An array for backups
## Make sure they not happen at the same time!
## e.g:
## backups:
## - name: hourly
## remoteDir: "/BitwardenBackup/hourly/"
## cron: "5 * * * *"
## keepDays: 7
## fileDateSuffix: "-%H-%M-%S"
## healthCheckPing: https://hc-ping.com/{ping_key}/vaultwarden-utility
##
backups: []
Loading
Loading