Install Bitwarden Server (nginx proxy, fail2ban, backup)

🍪 7 min read

What is it?

Bitwarden is a password manager which uses a server which can be selfhosted. It provides various frontends, ranging from browser plugins over desktop application to mobile apps for all major browsers and plattforms. In this note I want to show you how I set up my Bitwarden server. In this note I want to show how I set up my Bitwarden server behind a nginx proxy with fail2ban and a daily backup.

Nginx proxy preparation

I assume you have a server and nginx already installed. If not just look at my notes Secure Ubuntu 18.04 server setup as well as Ubuntu 18.04 server: nginx web server + Let’s Encrypt.

Obtain Let’s Encrypt certificate

To SSL encrypt the connection to our Bitwarden server, a certificate is required. We’ll use a Let’s encrypt certificate. Start by creating a nginx configuration file for our Bitwarden instance. The examples use the subdomain bitwarden.dennisnotes.com, change it according to the domain you want to use.

sudo vim /etc/nginx/sites-available/bitwarden.dennisnotes.com.conf

As in our basic nginx setup we start with a simple nginx configuration which just handles standard HTTP serving for our subdomain.

server {
    listen 80;
    listen [::]:80 ipv6only=on;

    server_name bitwarden.dennisnotes.com;
    root /var/www/dennisnotes.com;

    index index.html;
    location / {
            try_files $uri $uri/ =404;
    }
}

After creating the configuration file test it and restart nginx to enable it.

sudo ln -s /etc/nginx/sites-available/bitwarden.dennisnotes.com.conf /etc/nginx/sites-enabled/bitwarden.dennisnotes.com.conf
sudo nginx -t
sudo systemctl stop nginx
sudo systemctl start nginx
sudo systemctl status nginx

Now let certbot obtain a certificate for us and apply the default nginx SSL configuration like follows:

sudo certbot --rsa-key-size 4096 --nginx

Select bitwarden.dennisnotes.com, fill in information like email etc.

nginx setup

Next we will edit the configuration file again to use nginx as a reverse proxy for our bitwarden instance.

sudo vim /etc/nginx/sites-available/bitwarden.dennisnotes.com.conf

Here is a example configuration file, which I use (using the port 5178 which will be mapped to 80 when accessing bitwarden.dennisnotes.com):

server {

        server_name bitwarden.dennisnotes.com;
        root /var/www/dennisnotes.com;

        index index.html;

        location / {
            proxy_pass http://localhost:5178/;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;

            client_max_body_size 0;
            add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload";
            add_header Referrer-Policy "same-origin";

            access_log /var/log/nginx/bitwarden.access.log;
            error_log /var/log/nginx/bitwarden.error.log;
        }

    listen [::]:443 ssl http2; # managed by Certbot
    listen 443 ssl http2; # managed by Certbot
    ssl_certificate /etc/letsencrypt/live/bitwarden.dennisnotes.com/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/bitwarden.dennisnotes.com/privkey.pem; # managed by Certbot
    ssl_trusted_certificate /etc/letsencrypt/live/bitwarden.dennisnotes.com/chain.pem;
    include /etc/nginx/snippets/ssl.conf;
}

server {
    if ($host = bitwarden.dennisnotes.com) {
        return 301 https://$host$request_uri;
    } # managed by Certbot

        listen 80;
        listen [::]:80;

        server_name bitwarden.dennisnotes.com;
    return 404; # managed by Certbot
}

Bitwarden Installation

The installation of Bitwarden is quite simple and runs via docker-compose and installation scripts. Here I only show the short version, more information can be found on the Bitwarden website. You need docker and docker-composed to be installed on your server. During installation it will ask for a installation ID and key, you can get them here. It will also ask if you would like to use Let’s Encrypt or a own SSL certificate, enter no for all of these options, because we will use a SSL nginx proxy.

mkdir ~/bitwarden # you can use any dir you want
curl -s -o bitwarden.sh \
    https://raw.githubusercontent.com/bitwarden/core/master/scripts/bitwarden.sh \
    && chmod +x bitwarden.sh
./bitwarden.sh install

After the basic install, edit the configuration file at ./bwdata/config.yml. Most of it should be fine after running the installation script, just change the HTTP and SSL port accordingly to your configuration. e.g.

[...]
# Docker compose file port mapping for HTTP. Leave empty to remove the port mapping.
# Learn more: https://docs.docker.com/compose/compose-file/#ports
http_port: 5178
#
# Docker compose file port mapping for HTTPS. Leave empty to remove the port mapping.
# Learn more: https://docs.docker.com/compose/compose-file/#ports
# This port will not be used in our configuration.
https_port: 5179
[...]

Afterwards you can add additional settings in the environment variables file. I would recommend to set up a SMTP server for email notifications (e.g. change password, activate account etc.) as well as to deactivate user registration, if you do not want to let strangers use your Bitwarden server. To do so edit ./bwdata/env/global.override.env. Mine looks like this:

globalSettings__mail__replyToEmail=no-reply@bitwarden.dennisnotes.com
globalSettings__mail__smtp__host=smtp.dennisnotes.com
globalSettings__mail__smtp__username=yoursmtpuser@dennisnotes.com
globalSettings__mail__smtp__password=securepassword
globalSettings__mail__smtp__ssl=true
globalSettings__mail__smtp__port=587
globalSettings__mail__smtp__useDefaultCredentials=false
globalSettings__disableUserRegistration=true
adminSettings__admins=your@email.com

After setting everything up rebuild and start bitwarden like follows:

./bitwarden.sh rebuild
./bitwarden.sh start
./bitwarden.sh updatedb

You should now see the Bitwarden web interface when visiting your domain, e.g https://bitwarden.dennisnotes.com. You should now be able to create your user account. As a next step I would recommend to enable two-factor authentication for your account. This setting can be found under settings in the Bitwardens web interface.

Daily backup

Passwords are pretty important, so I want the bitwardens database to be backuped daily. For this I use borgbackup. I first encrypt the bwdata folder, which contains all data of bitwarden and store the encrypted file on a webdav server as my backup location.

Setup WebDAV

First of all we need to make our webdav drive mountable. Install the following packages:

sudo apt install ca-certificates davfs2

Create a folder for the webdav drive, e.g. /mnt/webdav and append the following line (with your webdavs data) to /etc/fstab.

https://webdav.yoururl.de  /mnt/webdav    davfs   noauto,user,rw  0       0

There are several webdav providers, so if you don’t have one yet, just google a bit for it. I use the free Magenta Cloud from German Telekom for my backup webdav drive. Next you’ll need to provide the credentials to the webdav drive. This can be done by appending them to /etc/davfs2/secrets. It should look like this:

https://webdav.yoururl.de  webdavusername@email.com  SecureWebDAVPassword

Afterwards you should be able to mount the webdav drive with your account. You can test it with:

mount /mnt/webdav

Now you should be able to see your webdav files in /mnt/webdav/ and also be able to add files there.

Borg Backup Initialization

First of all make sure, that your WebDAV drive is still mounted. Now we need to install borgbackup and initialize a backup repository in which the backup files will be stored.

sudo apt install borgbackup

borg init --encryption=repokey-blake2 /mnt/webdav/bitwarden_backup

This will lead you through a simple setup. Make sure you note your password, you will need it to create and decrypt backups.

Daily Backup Cronjob

Now we need to create our backup script, e.g. at /home/dennis/bitwarden_backup.sh. It should mount your webdav drive, then creating a backup of your bwdata folder. You can also specify how many backups you would like to store, see borg prune … in the script. My script looks like this.

#!/bin/sh

mount /mnt/webdav

REPOSITORY=/mnt/webdav/bitwarden_backup

# Bail if borg is already running, maybe previous run didn't finish
if pidof -x borg >/dev/null; then
    echo "Backup already running"
    exit
fi

# Setting this, so you won't be asked for your repository passphrase:
export BORG_PASSPHRASE='YourSecureBorgPassphrase'
# or this to ask an external program to supply the passphrase:
# export BORG_PASSCOMMAND='pass show backup'

# Backup all of /home and /var/www except a few
# excluded directories
borg create -v --stats                          \
    $REPOSITORY::'{hostname}-{now:%Y-%m-%d}'    \
    /home/dennis/bitwarden/bwdata

# Use the `prune` subcommand to maintain 7 daily, 4 weekly and 6 monthly
# archives of THIS machine. The '{hostname}-' prefix is very important to
# limit prune's operation to this machine's archives and not apply to
# other machine's archives also.
borg prune -v --list $REPOSITORY --prefix '{hostname}-' \
    --keep-daily=7 --keep-weekly=4 --keep-monthly=6
umount /mnt/webdav

After creating the script make it executable and create a cronjob which executes it.

chmod +x borg_bu.sh
crontab -e

My cronjob runs the script every day at 0:00 o’clock and looks like this:

0 0 * * * /home/dennis/bitwarden_backup.sh > /home/dennis/bitwarden_backup_log.txt 2>&1

Now your bitwardens bwdata folder should be backuped as a encrypted file to your webdav drive every night at 0:00 o’clock. The logs for the backup are accessible via the bitwarden_backup_log.txt, so if anything doesn’t work correctly, check this file first.

Fail2Ban configuration

It seems like there are no logfiles provided which contain IP adresses of failed login attempts, so currently I do not see a way to use fail2ban with Bitwarden. This will hopefully change in the feature.