Resource icon

Tutorial Securing your Vaultwarden install

Greetings... Finally a direct contribution to This Place, I would like to give some hints on making your Vaultwarden install a little more secure. I have the view that (a) my password manager is THE most important content in my digital life, and (b) I am by no means an expert in software or digital security, so therefore I have to do what I can to protect My Secret Stuff from nefarious persons.

In the Real World away from forums and the like, I have exactly two friends who have installed Vaultwarden on their NAS and they each found a basic tuto in their native tongue and followed it. Job done, without any attempt to further secure it. If, by chance, you have never looked at the truly excellent Vaultwarden wiki then this is my attempt to publicise the effort that clever people have put in to explain and document their work. I do not include all the points here, because some of it I do not understand, although I have picked out what I can and offer it, in the hope that your personal Vaultwarden solutions can become a little safer.

This page is quite long and not as simple as the Rusty tutorial. However it is trying to accomplish more. As to whether all this is necessary, I do not know. Some say that with https and 2FA you do not need anything else. For me, if Security is supposed to be multi-layered, then I'll try and do that.

By the end you should be able to: disable signups to Vaultwarden, use 2FA, enable emails from your Vaultwarden vault and have some protection against brute force attacks. If you have a clever router with some kind of intrusion detection system inside, then perhaps you do not need this, however my Unifi USG kills the throughput with this switched on so I do not use it.

You would be right in pointing out that DSM includes Auto block functionality, as per:

DSM Autoblock.png


Alas, this will not work for Docker containers, so I believe the fail2ban solution is a useful addition to protect your vault.

My personal start point was the excellent tutorial from Rusty on the initial setup of Vaultwarden. However, I use docker-compose files. This is simply because my source material talks in terms of docker-compose, so if you refer to the relevant github pages for more info, there will be a simple read across.

So... I assume you are familar with Rusty's tutorial. If not and you are new to Docker, I suggest you follow it, just to learn the basics. (Even if you do not use it.) You should also have certificates setup with your own domain name. All of that is outside the scope of this page.

When you are finished, you will have three packages installed in your Docker environment:
  • Vaultwarden
  • Caddy (this is a webserver)
  • fail2ban (for brute force protection)

Although this exercise took me weeks to get going, this is not my work. I have used the excellent pages of sosandroid on github, who uses the source packages (e.g. dani-garcia for vaultwarden, as used by Rusty) and provides the config files to work around occasional limitations imposed by Synology. Therefore keep the following pages to hand when following this guide:

sosandroid / docker-bitwarden_rs-caddy-synology
sosandroid / docker-fail2ban-synology

These are good pages. The exercise took me a while because in a few areas I misinterpreted the words and then went wrong. Hopefully this will avoid for you some of the mistakes I made.
[Note: for the moment these sosandroid pages are not updated to the renaming of bitwarden-rs to Vaultwarden that has recently taken place. I will add some additional words at various places for the bits you need to change.]

Introduction - docker-compose
There is nothing to do here. docker-compose was installed on your Syno when you installed docker, so it is available for use. I was initially reluctant to use this, but in the end it is just slightly different commands in your ssh session, so not as scary as I thought it was.

Step 1 - Network for Vaultwarden
The tutorial from Rusty uses the standard bridge network that is setup when Docker is installed. That is fine and it works. I took the decision to setup a specific network for my Vaultwarden install, to provide a basic level of isolation.

Within your Docker package, go to the Network tab on the left and then select Add. Enter the network name and you are done, as per the picture:

New Network.png

For this example I use bitwNet.

Step 2 - Reverse Proxy
This is a little different to the initial tutorial of Rusty. I assume you have a domain name and hence you will access your Vaultwarden via port 443 (as Rusty recommends), so you will need two Reverse Proxy setups as follows:


RP1.png

In this example The outside https port 443 maps to port 3012. (Port 3012 is not visible from the outside therefore there is no reason to change it, unlike in Rusty's example.) In this https RP, you need to go into Custom Header and click Create -> Websocket.

RP2.png



For this http RP, you do not create the Websocket.

Step 3 - setup docker compose for Vaultwarden.
Step 3a - create the compose file
sosandroid includes both Bitwarden and Caddy in one docker-compose file. For the main reason of understanding how it worked, I split the single docker-compose file into two, for each of Vaultwarden and Caddy. (This is not needed though; using one file (find it on the github page) will shorten the steps later.)
Here is my compose file for VaultWarden (called OnlyVW.yml in this example) (.yml is the file extension for compose files):

YAML:
#---
#---
#Docker-compose file for Vaultwarden proxied by caddy 2.0
#--- 
version: "3"
services: 
  vaultwarden: 
    restart: always
    # Dani Garcia image https://github.com/dani-garcia/vaultwarden
    image: "vaultwarden/server:latest"
    container_name: vaultwarden
    environment:
      # Timezone settings, important for Fail2ban to work
      - TZ=Europe/Paris
      # Logging connection attemps
      - LOG_FILE=/data/vaultwarden.log
      - EXTENDED_LOGGING='true'
      - LOG_LEVEL=warn
      # Beef up a bit
      - ROCKET_WORKERS=10
      - WEBSOCKET_ENABLED=true
      # Hardening a bit
      # Uncomment SIGNUPS_ALLOWED after first account created.
      #- SIGNUPS_ALLOWED=false
      #- DISABLE_ADMIN_TOKEN='true'
      - ADMIN_TOKEN=longTextStringAsPsswordForAdminPage
      - [email protected]
      - SHOW_PASSWORD_HINT=false
      - DOMAIN=https://bitw.mydomain.com:443
      #- DISABLE_ICON_DOWNLOAD='true'
      - SMTP_HOST=smtp.gmail.com
      - SMTP_PORT=587
      - SMTP_SSL=true
      #- SMTP_EXPLICIT_TLS=true
      - [email protected]
      - [email protected]
      - SMTP_PASSWORD=my_emailPass
    expose:
      - "80"
    networks:
      - bitwNet
    volumes: 
      - /volume1/docker/vaultwarden:/data
      
networks:
  bitwNet:
    external: true

(For info, I have stored my Vaultwarden compose file in the folder docker/vaultwarden. The others are stored similarly.)

In this compose file there are a few important things:
  • TZ - Timezone. make sure this is accurate to your location!
  • ADMIN_TOKEN - this is an environment variable to give access to an Admin page inside your Vaultwarden server. The Admin page allows you to make major admin changes to your Vaultwarden server. You can also view and delete registered users. Try not to alter too much this way though, because it will override the environment variables. It should be a long random text string.
  • ORG_CREATION_USERS - This allows only the named users to create an organisation.
  • DOMAIN - this is the full address for your server, with port number and https included.
  • various SMTP variables. If you take the trouble to complete these then the server will send you useful emails in certain circumstances. (e.g. this morning, I installed the Vaultwarden client on another android phone. I got an email telling me there was an access from somewhere new.) Please note that the parameters here are correct for gmail. A different provider will have different entries for port number, SMTP_SSL,...
  • Note the occurrences of bitwNet, the bridge network we created earlier.

Step 3b - create folders on NAS
You need to create the folders you need. This example uses docker/vaultwarden.

Step 4 - Setup docker compose for Caddy
Step 4a - Create the compose file
My compose file for Caddy as follows:

YAML:
#---
#Docker-compose file for Vaultwarden proxied by caddy 2.0
#---
version: "3"
services:
  caddy:
    restart: always
    #Official Caddy 2.0 image
    image: "caddy:latest"
    container_name: Caddy_proxy
    environment:
      - TZ=Europe/Paris
      - LOG_FILE=/data/logs/caddy.log
      # Update this if SSL required according to the use of your own cert or requuest one from Let's Encrypt
      #- SSLCERTIFICATE=/path/to/ssl/fullcert.pem
      #- SSLKEY=/path/to/ssl/key.pem
      #- ACMEE_AGREE='true'
      #- DOMAIN=cad.example.org
      #- [email protected]
    ports:
      - 3012:80
      #- 8443:443
    networks:
      - bitwNet
    volumes:
      - /volume1/docker/caddy-data/config/Caddyfile:/etc/caddy/Caddyfile
      - /volume1/docker/caddy-data/data:/data
      - /volume1/docker/caddy-data/sites:/var/www/html
      - Certfiles:/root/.caddy

volumes:
  Certfiles:

networks:
  bitwNet:
    external: true

The important items in this file:
  • TZ - timezone
  • - 3012:80 This is the mapping from port 3012 to the port 80 used by Vaultwarden.
  • Note the two references to the network name bitwNet I am using for vaultwarden (bitwNet).
  • Note the lines related to the certificate are commented out. I use the standard Synology method for my certificate and so this is not needed.

Step 4b - create and populate folders on NAS
You need to create the various folders. This example uses docker/caddy-data.
There must be files inside the folders as well. The easiest way to do this is to download the info from the github page mentioned earlier: sosandroid / docker-bitwarden_rs-caddy-synology
You find this by clicking on the green Code button on the page and downloading a zip file.
Unzip it and put the caddy-data part under the docker folder.
When this is done, you will have the following folder structure:
docker/caddy-data/config
docker/caddy-data/data
docker/caddy-data/sites

In the config folder you will find one file: Caddyfile
In the sites folder you will find one file: index.html

[Note: you need to change the content of the Caddyfile by replacing instances of bitwardenrs by vaultwarden. You will note that there are many lines in this file beginning with a '#' character. This means the line is a comment only and is not important. Therefore there is a grand total of only three places to change bitwardenrs to vaultwarden. In each place the line begins with reverse_proxy.]

Note: If you have modified port 3012 then you will need to modify the Caddyfile with your new port number.

Step 5 - Run your new containers
(With the two .yml files, you need two SSH connections into your NAS. If you used only one .yml file, then you only need one SSH connection.)
Using your SSH connection on the NAS and using root with sudo -i, run the following commands:
cd /volume1/docker/vaultwarden docker-compose -f OnlyVW.yml pull # This will download the images you need. docker-compose -f OnlyVW.yml up # This will 'test' run the image. You need to Ctrl-C to stop it running

With a second SSH connection on the NAS (sudo -i) perform the following:
cd /volume1/docker/caddy-data docker-compose -f OnlyCaddy.yml pull # This will download the images you need. docker-compose -f OnlyCaddy.yml up # This will 'test' run the image. You need to Ctrl-C to stop it running

As explained in the github page, test your access to your Vaultwarden server and make any adjustments to your vault in the Admin page (using the text string you created earlier as password)

Then Create your first user.
When all is good, stop each of your two containers with CTRL-C.

When all is good, you can run your server in 'production' mode with:
Code:
docker-compose -f OnlyVW.yml up -d
docker-compose -f OnlyCaddy.yml up -d
This runs them in normal detached mode and you see the command prompt return each time.

Step 6 - Disable others from creating their own accounts on your server.
Uncomment the line in OnlyVW.yml that reads:
  • SIGNUPS_ALLOWED=false
This will stop someone from landing on your login page and creating their own vault. However, it does NOT stop you inviting your family members to create their account.

Step 7 - Time to reflect and take a beer.
At this stage you have (essentially) performed the original Rusty tutorial, only in a more complicated fashion.
However, you do achieve:
  • stop users creating organisations willy-nilly and tying themselves in knots as a result (ask my dear spouse).
  • receive emails from your server when something exciting happens
At this stage, I thoroughly recommend you perusing dani-garcia Vaultwarden wiki page
Many pages referenced here, some related to this tutorial, some other things. Mostly it is about securing your installation, in one way or another. If you can understand things I am not explaining here, please offer a write-up. I will be very grateful!

Now for more interesting stuff.

Step 8 - 2FA (Two Factor Authorization)
Easy this one, no mucking about with Docker required.

In your server Vault, click on Setting and then Two-step login. Follow the guides to integrate your Authy or other 2FA authenticator. It also supports Yubikeys!

While you are here, review the Options and create any Organisations you might want.

Step 9 - Setup protection against brute-force attacks
Now you will install your third container: fail2ban. In a simple fashion, this container interrogates log files for repeated log-in attempts and then bans the addresses if necessary. Very clever indeed. It can send you an email when an address is banned and you can unban that address if necessary. (Took me ages to get working, but that was my fault in the end.)
As fail2ban is not tied to Vaultwarden in any way, you can use it to protect other containers too.

The reference page for this is: sosandroid github page

sosandroid has done the hard work getting around the Synology foibles, so we can more easily benefit from the original source package. Again, I recommend you read this page in parallel with this tutorial as you proceed.

Step 9a - create the compose file.
My example compose file for fail2ban as follows:

YAML:
# /volumeX/docker/fail2ban/docker-compose.yml

version: '3'
services:
  fail2ban:
    container_name: fail2ban
    restart: always
    image: crazymax/fail2ban:latest
    environment:
      - TZ=Europe/Paris
      - F2B_DB_PURGE_AGE=30d
      - F2B_LOG_TARGET=/data/fail2ban.log
      - F2B_LOG_LEVEL=INFO
      - F2B_IPTABLES_CHAIN=INPUT
      - SSMTP_HOST=smtp.gmail.com
      - SSMTP_PORT=587
      - SSMTP_HOSTNAME=yourHostname
      - [email protected]
      - SSMTP_PASSWORD=my_password
      - SSMTP_TLS=YES
      - SSMTP_STARTTLS=YES

    volumes:
      - /volume1/docker/fail2ban:/data
      - /volume1/docker/vaultwarden:/vaultwarden:ro

    network_mode: "host"

    privileged: true
    cap_add:
      - NET_ADMIN
      - NET_RAW

The important items in this file:
  • TZ - Timezone setting!
  • F2B_DB_PURGE - A banned IP address is removed from the database, in my example, in 30 days.
  • SSMTP settings - For sending emails. This is important for this container, as you probably want to be informed when an attempt is made to access your vault.

Step 9b - Create and populate necessary folders on your NAS
You need to create the various folders needed by fail2ban. This example uses the docker/fail2ban location.
You need some files inside also. The easiest way to do this is to download the info from the github page mentioned earlier: sosandroid github page
You find this by clicking on the green Code button and downloading a zip file.
Unzip it and put the fail2ban folder under the docker folder.
When this is done, you will have the following folder structure:
docker/fail2ban/action.d
docker/fail2ban/filter.d
docker/fail2ban/jail.d


In the action.d folder you will find one file: iptables-common.local
In the filter.d folder you will find two files: bitwarden-admin.conf, bitwarden.conf
In the jail.d folder you will find two files: bitwarden-admin.conf, bitwarden.conf. You will create an additional file here also, described further down.

[Note: These files you downloaded need some changes because bitwardenrs has name changed to vaultwarden. Hence I introduce Step 9b-Names here...]

Step 9b-Names
i) In the filter.d folder, please change the names of the two files to vaultwarden-admin.conf and vaultwarden.conf. The content of these two files does not change.
ii) In the jail.d folder, please change the names of the two files to vaultwarden-admin.conf and vaultwarden.conf. Here, the content WILL change.
iii) Inside the two files in jail.d you will find various occurrences of bitwarden. Change all of these to vaultwarden.
[End of Step 9b-Names]


Concerning the jail.d folder, you will note that there is one jail file (vaultwarden.conf file) for the usual vaultwarden gui entry point and another (vaultwarden-admin.conf) for the admin page. Inside these files, you can parameter the following:
  • ignoreip - if you wish certain addresses never to be banned (e.g. your local network)
  • bantime - the time period for which an IP address will be banned. By default 30 days. (This is in seconds.)
  • findtime - This is the window of time (in seconds) that fail2ban uses to judge if there has been an unauthorised access attempt. By default 86400 seconds = 24 hours.
  • maxretry - This is the allowed number of attempts within the window of findtime seconds that are allowed. By default 4 attempts.
As example: if an IP address makes four attempts to access the vault in a 23 hour period, that is allowed. If he immediately makes a fifth attempt (therefore 23 hours and some seconds), he will be banned (less than 24 hours).
If he waits two hours before making the fifth attempt there will be no ban. (25 hours has elapsed.)
For info, I use 4 attempts max in a window period of 20 minutes. I'd be interested in your thoughts...

Remember: when you make changes to one of the .conf files in jail.d then you need to make changes to the other .conf file (if you want them to behave in the same way).

Note: This container does not run on the bitwNet network we created earlier. Rather it runs in the host network in Docker.

There is an additional file you should create inside the jail.d folder. This file is called vaultwarden.local. It defines the content of the emails sent out. An example follows:

Code:
# Added to send emails
[DEFAULT]
destemail = [email protected]
sender = [email protected]
action = %(action_mwl)s

The action line is the important bit. You will find more information referenced on the github page for the crazy-max fail2ban repository.

Step 10 - Run the fail2ban container
The process is similar to the previous containers. If you want to run it to test, use the following:
Code:
docker-compose -f fail2ban-compose.yml up

Add the -d when you are content it works correctly:
Code:
docker-compose -f fail2ban-compose.yml up -d

To test your Vaultwarden / fail2ban setup, try logging in (with a mobile phone on data) either to the vault in your browser or a client, such that you get the password wrong a sufficient number of times. If all goes well, at that point your page will hang and your email account will get a message telling you someone was banned.
Remember, do not try this from an address that is covered by the ignoreip ranges you set up above.

Conclusion
Hopefully, you now have a Vaultwarden install with 2FA and protection against brute force attacks. Mine has been running now for a couple of months, with a couple of NAS reboots along the way. I haven't touched it at all and this morning I did a failed login test and was successfully banned, so for now it runs well.

Here are a few commands to either unban a banned address or to manually ban an IP:

Unban IP normal page:
Code:
sudo docker exec -t fail2ban fail2ban-client set vaultwarden unbanip XX.XX.XX.XX
Unban IP Admin page:
Code:
sudo docker exec -t fail2ban-admin fail2ban-client set vaultwarden unbanip XX.XX.XX.XX
Manually ban an IP:
Code:
docker exec -t <CONTAINER> fail2ban-client set <JAIL> banip <IP>

In general, commands must be pre-fixed with sudo docker exec -t fail2ban


They require sudo privilege, hence they are quite long.

An additional item: backing up the vault... By default the garcia Vaultwarden uses an SQLite database that I have read is easy to backup because it is simply a file, rather than a super complicated database.
This suggests a backup would be risk-free with your favourite tool (at least in a family setting with intermittent access). However... if someone can describe a method I would be grateful. (I have heard talk of the Docker container bruceforce, although I have not tried it yet.)

Finally, the end. I hope I did not forget anything.



Latest updates

  1. Securing your Vaultwarden install

    Update for new name Vaultwarden
Top