Backup and Restore Procedures for Vaultwarden
Backing up Vaultwarden is crucial to ensure the security and availability of your sensitive data. Vaultwarden, being a self-hosted password manager, stores all your passwords, personal information, and other sensitive credentials. In the event of data corruption, accidental deletion or hardware failure, having a reliable backup can mean the difference between a minor inconvenience and a catastrophic data loss.
To create backups I chose Duplicati. As explained in part 1, is a versatile backup solution that supports a wide range of destinations where data can be securely stored.
Automated backup solutions, like using Duplicati with Vaultwarden, streamline the backup process, making it more reliable and less prone to human error, thereby enhancing your overall data protection strategy and ensuring business continuity.
To have a predictable, consistent, repeatable process, the installation of Duplicati is also automated with an Ansible playbook.
The Duplicati playbook
Much like the Vaultwarden playbook it also refers to vault.yml
. But everything is already configure.
It will perform these actions:
- Ensure Podman is installed.
- Ensure the Duplicati port is disabled in the firewall. We don’t want this port to be accessible. We will create an SSH tunnel to access the portal.
- Enable and start firewalld.
- Create Quadlet folder location.
- Create and configure Quadlet for Duplicati, based on a template file
templates/duplicati.j2
- Enable user lingering if needed. This allows the container to run even if the user is not logged on.
- Inform Systemd about the new services.
- Start the Duplicati service.
# Ansible script to run Duplicati with Podman on RHEL and derivate systems
- hosts: localhost
remote_user: root
vars_files:
- vars/vault.yml
tasks:
- name: Packages | Ensure podman is installed
ansible.builtin.package:
name: podman
state: present
become: true
- name: "Firewall | Ensure Duplicati {{ DUPLICATI_PORT }}/tcp is disabled"
ansible.posix.firewalld:
port: "{{ DUPLICATI_PORT }}/tcp"
permanent: true
state: disabled
become: true
notify:
- Restart firewalld
- name: Systemd | Ensure firewalld is enabled and started
ansible.builtin.systemd_service:
name: firewalld
enabled: true
state: started
become: true
- name: Users | Create vaultwarden user
ansible.builtin.user:
name: "{{ VAULTWARDEN_USER }}"
state: present
become: true
- name: Vaultwarden | Create Quadlet folder location
ansible.builtin.file:
path: ~/.config/containers/systemd
state: directory
become_user: "{{ VAULTWARDEN_USER }}"
become: true
- name: Duplicati | Create Quadlet
ansible.builtin.template:
src: templates/duplicati.j2
dest: ~/.config/containers/systemd/duplicati.container
mode: '0600'
become_user: "{{ VAULTWARDEN_USER }}"
become: true
- name: Systemd | Check if user is lingering
ansible.builtin.stat:
path: "/var/lib/systemd/linger/{{ VAULTWARDEN_USER }}"
register: user_lingering
- name: Systemd | Enable lingering is needed
ansible.builtin.command: "loginctl enable-linger {{ VAULTWARDEN_USER }}"
when:
- not user_lingering.stat.exists
become: true
- name: Systemd | Inform Systemd about the new services
ansible.builtin.systemd_service:
daemon_reload: true
scope: user
become_user: "{{ VAULTWARDEN_USER }}"
become: true
- name: Systemd | Start the Duplicati service
ansible.builtin.systemd_service:
name: "{{ DUPLICATI_CONTAINER_NAME }}"
daemon_reload: true
state: started
scope: user
become_user: "{{ VAULTWARDEN_USER }}"
become: true
## Finished message
- name: Finished
debug:
msg:
- "Finished installing duplicati"
- "Access to the Duplicati admin port is blocked ({{ DUPLICATI_PORT }}/tcp)"
- "You can access the Duplicati admin portal the through an SSH tunnel:"
- "ssh -N -L localhost:{{ DUPLICATI_PORT }}:<server>:{{ DUPLICATI_PORT }} <user>@<server> -i <key>"
handlers:
- name: Restart firewalld
ansible.builtin.service:
name: firewalld
state: restarted
become: true
To run the playbook
ansible-playbook ./duplicati.yml --ask-vault-pass
Enter the vault password. The Playbook will take a few minutes to finish.
To verify, we can look at the ports in use. We know that Vaultwarden listens on port 8200.
We can also use curl
to access the site.
Access the Duplicati portal
Duplicati is now running in a container as an unprivileged user. But all ports are blocked. Nothing is allowed through the firewall. How can we get to the Duplicati web interface?
The answer is to create an SSH tunnel. I’ve written an article about that. If you want a read up here it is. We want to set up a local port forward.
The syntax is:
ssh -L localhost:localport:remoteserver:remoteport ssh_user@ssh_address
You can combine with the -N switch to not execute a remote command and -i to specify a private key.
Now browse to http://localhost:8200.
We can set a password if we want to. But this interface is only accessible through an SSH tunnel.
Create a backup
The duplicati.yml
prepared a volume to easily backup the data from Vaultwarden. The Vaultwarden volume is mapped inside the Duplicati container as tobackup
.
Let’s create a backup. Click Add Backup
, select Configure a new backup
and click Next
.
On the next page, fill the name and choose a password to encrypt the backup. Click Next
.
One of the main reasons why I chose Duplicati as a backup solution is the sheer amount of suported backup destinations.
I chose Google Drive. If you choose anything else, the following screens will be different for you.
When I click on AuthID
, a new window pops up to authenticate against Google.
After following onscreen instructions and clicking the Test connection
button.
On the next window, we need to select what to backup. As mentioned earlier, select the folder tobackup
. This folder contains all the data from Vaultwarden. Click Next
.
Configure the Schedule as you find appropriate. Click Next
.
Finaly set the remote volume size and the backup retention. The default of 50 MB seems reasonable. For the backup retention I choose the Smart backup retention
.
There you have it. A daily backup of the sensitive data contained in Vaultwarden.
Restore
The last step is to be able to restore everything. Because we set everything up with playbooks, there is no doubt. We don’t need to think anymore.
There are two playbooks. vaultwarden.yml
and duplicati.yml
. The idea is that in a disater recovery, you first run the duplicati.yml
playbook. This will allow us to restore the data used by the Vaultwarden container. After that we can run the vaultwarden.yml
playbook.
So the recovery procedure is:
- Get a system, install the OS, Ansible and Git.
- Clone the repo.
- Run the Duplicati playbook first.
- Create an SSH tunnel with local portforwarding.
- Open the web interface with http://localhost:8200.
- Click the
Restore
option. - Fill in the appropriate backup location and password.
- Select the date to restore from and select the
tobackup
folder.
- Click restore. The files are now recovered in the location where the Vaultwarden container expects them to be. - Run the Vaultwarden playbook. - Configure the access from the Internet. - Have a beer.
This concludes the series. I’ve written this mainly for myself in the future. But I hope someone else finds it usefull too.