Backup a Kubernetes PassBolt deployment to a NFS server

PassBolt is a great self-hosted password manager which has support for a highly available deployment using Kubernetes. I have been using PassBolt for almost a year now and while I wish I had the ability to store files into it in a similar fashion as with KeePass, the fact that I have access to my passwords from my computers as well as my mobile device has prompted me to exclusively use PassBolt.

I favoured a deployment based on the Helm chart using MariaDB as the database backend and even though the data is stored on a PersistentVolume hosted on a Ceph cluster which provides data replication and high availability, one has to remember that RAID is not a backup™ and thus, the PassBolt data that maintainers recommend backing up has to be just that – backed up. In this post, I will show you how I backup my Kubernetes-hosted PassBolt deployment to a NFS server.

Kubernetes CronJob

I am not interested in spending days resetting passwords everywhere so the 3-2-1 principle is especially important when it comes to keeping backup copies for my secrets and credentials. Because relying only on volume snapshots for backups would not have been sufficient, I wanted a way to store a copy on my Bacula server as well as another copy in the Cloud.

For this reason, I opted to export both the MariaDB database as well as the PassBolt GPG keys to a NFS share where Bacula is already setup to backup the local file systems. This is accomplished using the following CronJob, which is deployed in the same namespace as my PassBolt workload, namely passbolt. This is important because the CronJob leverages the secrets used by the PassBolt pods.

cronjob-backup.yaml
apiVersion: batch/v1
kind: CronJob
metadata:
  name: passbolt-nfs-backup
spec:
  schedule: "0 1 * * *" # Every day at 01:00.
  jobTemplate:
    spec:
      template:
        spec:
          containers:
          - name: passbolt-nfs-backup
            securityContext:
              runAsUser: 1000
              runAsGroup: 1000
            volumeMounts:
              - name: nfs-passbolt-db-backups
                mountPath: /backups
              - name: nfs-passbolt-gpg-backups
                readOnly: true
                mountPath: /etc/passbolt-gpg
            image: docker.io/bitnami/mariadb:10.6.12-debian-11-r16
            imagePullPolicy: IfNotPresent
            env:
            - name: MARIADB_HOST
              value: "helm-passbolt-mariadb-primary.passbolt.svc.cluster.local"
            - name: MARIADB_DATABASE
              value: "passbolt"
            - name: MARIADB_USER
              value: "a7bedbe93"
            - name: MARIADB_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: helm-passbolt-mariadb
                  key: mariadb-password
            - name: TZ
              value: "America/New_York"
            command:
            - /bin/sh
            - -c
            - |
              export DATE=$(date +%Y%m%d_%H%M%S) TMPDIR=$(mktemp -d)
              echo "Backing up /etc/passbolt-gpg/serverkey*.asc"
              cp /etc/passbolt-gpg/serverkey*.asc ${TMPDIR}

              echo "Dumping the ${MARIADB_DATABASE} database from ${MARIADB_USER}:[hidden]@${MARIADB_HOST} to ${TMPDIR}/${MARIADB_DATABASE}_mysqldump_${DATE}.sql"
              mysqldump -h ${MARIADB_HOST} -u${MARIADB_USER} -p${MARIADB_PASSWORD} ${MARIADB_DATABASE} >${TMPDIR}/${MARIADB_DATABASE}_mysqldump_${DATE}.sql

              echo "Archiving GPG keys and MySQL database into PassBolt_Backup_${DATE}.tar.gz"
              cd ${TMPDIR}
              tar -zcvf /backups/PassBolt_Backup_${DATE}.tar.gz ./*

              echo "Deleting the following files from /backups, which are older than 30 days:"
              find /backups -type f -name "PassBolt_Backup_*" -mtime +30 -print -delete
          restartPolicy: OnFailure
          volumes:
          - name: nfs-passbolt-db-backups
            nfs:
              server: omv.homelab.local
              path: /export/passbolt-backups
          - name: nfs-passbolt-gpg-backups
            secret:
              secretName: helm-passbolt-sec-gpg

What’s going on in this CronJob?

Here is a breakdown of the important lines:

Line 6: Defines the frequency at which the CronJob will be executed. Because I’m the only user for my PassBolt workload, taking a single daily backup is more than enough for me. If you have multiple users, you may want to adjust the schedule to run a few times per day.

Lines 13 to 15: This represents the uid/gid associated with the permissions on the NFS share, i.e.: uid=1000 and gid=1000 have read/write permissions on my NFS share. If this is not set properly, the pod may fail with permission denied errors.

Line 22: The image defined here should ideally match the image you have running for the MariaDB pods in your PassBolt workload. It is probably okay if the CronJob’s image does not match the workload’s image but ideally, both should match or at least use similar versions.

Line 26: This represents the service DNS entry for your primary MariaDB replica.

Line 28 and 30: Make sure these two lines match what you have setup in your chart’s values.yaml file.

Line 34: Change this line to match the secret name for your workload.

Line 37: Because the date and time get appended to the backup file names, you may want to adjust this value to match your local time zone.

Lines 42 to 54: These lines are the actual script that creates a temporary directory, copies the GPG keys into it, then calls mysqldump to export the MariaDB database into it and creates a tarball of that temporary directory. Finally, the tarball is copied to the NFS server and backups older than 30 days are deleted.

Line 59 and 60: Change this line to either the hostname or IP address of your NFS server as well as the path to export the backup files into.

Execution logs and output files

With the CronJob running on a daily basis, you can see what was executed in its pod logs. Since I have only been running the CronJob for a few days, no files are currently being purged:

Backing up /etc/passbolt-gpg/serverkey*.asc
Dumping the passbolt database from a7bedbe93:[hidden]@helm-passbolt-mariadb-primary.passbolt.svc.cluster.local to /tmp/tmp.HlwmKXoXds/passbolt_mysqldump_20231212_192119.sql
Archiving GPG keys and MySQL database into PassBolt_Backup_20231212_192119.tar.gz
./passbolt_mysqldump_20231212_192119.sql
./serverkey.asc
./serverkey_private.asc
Deleting the following files from /backups, which are older than 30 days:

Here is the contents of the directory where the backup files are copied onto my NFS server:

user@omv:/export/passbolt-backups# ls -l
total 21724
-rw-r--r-- 1 user users 5521591 Dec 11 21:30 PassBolt_Backup_20231211_213022.tar.gz
-rw-r--r-- 1 user users 5522714 Dec 11 21:32 PassBolt_Backup_20231211_213249.tar.gz
-rw-r--r-- 1 user users 5524165 Dec 11 21:35 PassBolt_Backup_20231211_213520.tar.gz
-rw-r--r-- 1 user users 5666968 Dec 12 01:00 PassBolt_Backup_20231212_010008.tar.gz
user@omv:/export/passbolt-backups#

As for the contents of the tarball, you can see the MariaDB SQL dump as well as the GPG private and public keys:

user@omv:/export/passbolt-backups# tar -ztvf PassBolt_Backup_20231212_010008.tar.gz
-rw-r--r-- 1000/1000  25852235 2023-12-12 01:00 ./passbolt_mysqldump_20231212_010008.sql
-rw-r--r-- 1000/1000      2639 2023-12-12 01:00 ./serverkey.asc
-rw-r--r-- 1000/1000      5704 2023-12-12 01:00 ./serverkey_private.asc
user@omv:/export/passbolt-backups#

User GPG keys

I suspect that each users GPG keys are stored within the database, but the PassBolt maintainers also recommend having each user back up their GPG keys. Sadly, I found that the process is not well documented, here is how to do it:

First, head over to the /app/settings/keys path of your PassBolt web interface, i.e.: https://your-passbolt-url.domain.tld/app/settings/keys and click on the Public and Private buttons to download your GPG keys, as shown in figure 1 below:

Figure 1.

Conclusion

With the tarballs now accumulating on a daily basis on my NFS server, I ensure that three copies of my PassBolt data exist at all times by storing two local copies on my PassBolt deployment and another on my NFS server. I also send a third copy off-site in the cloud. Don’t forget to also have your users backup their GPG keys within the PassBolt user interface!

Leave a Reply

Related Posts