Vaultwarden

Vaultwarden #

Introduction #

I began using password managers with my installation of LastPass in September of 2017. I became obsessed with securing my accounts following this: I would use 2FA and generated complex passwords at any chance I got. I transitioned away from an (unfortunately) common practice of using the same, or a variation of the same password across multiple services.

However, most of this was done using LastPass. As time passed, I became more discontent and disappointed in the lack of features and closed-source nature of LastPass.

Eventually, I came across Bitwarden and the Vaultwarden project (formerly known as bitwarden_rs) and went through the process of building it from binary rather than the standard practice of using Docker.

Why? Although Docker was obviously the easiest choice to deploy a Vaultwarden instance, I just liked the idea and challenge of building it from binary and launching it myself.

… also I’m still not sold on the idea of containerized applications just yet.

Installation #

Prerequisites #

This doc uses a base install of Linux Debian 10, but the process should be similar on other distributions.

Begin with installing a couple base programs using your package manager.
Debian and similar distros can use apt:

apt install git wget curl build-essential pkg-config openssl libssl-dev

Install one of the following database backends:

SQLite3:

apt install libsqlite3-dev

MySQL:

apt install libmariadb-dev-compat libmariadb-dev

PostgreSQL:

apt install libpq-dev pkg-config

Create a user account for Bitwarden and follow the prompts:

adduser bitwarden

Switch to the new ‘bitwarden’ user and install Rust and Cargo:

su bitwarden
curl https://sh.rustup.rs -sSf | sh

To use Cargo, either restart your current shell or run:

source "$HOME/.cargo/env"

Building vaultwarden #

While still in the bitwarden user account, begin by cloning the repository into your home directory, then checking out the latest tag (e.g. 1.27.0):

cd ~
git clone https://github.com/dani-garcia/vaultwarden && cd vaultwarden
git checkout tags/1.27.0

Now build using cargo, and specify which backend you are using:

  • SQLite:
cargo clean && cargo build --features sqlite --release
  • MySQL:
cargo clean && cargo build --features mysql --release
  • PostgreSQL:
cargo clean && cargo build --features postgresql --release
  • All backends:
cargo clean && cargo build --features sqlite,mysql,postgresql --release

Now you can return back to the original user:

exit

Setting up environment #

Most of these are not necessarily required and are customizable by the user, depending on the systemd file detailed later.
Move the vaultwarden binary to /usr/bin/

mv /home/bitwarden/vaultwarden/target/release/vaultwarden /usr/bin/

Copy the template environment file in the base directory of the repo to /etc/

cp /home/bitwarden/vaultwarden/.env.template /etc/vaultwarden.env

Create a directory under /var/lib/ to store the vaultwarden data:

mkdir -p /var/lib/vaultwarden/data

Installing the web-vault #

Main page on vaultwarden’s GitHub wiki

Now from here, there’s two options. You can choose to compile the web-vault yourself (manually) or you can download an already compiled version:

  1. Go to the pre-patched releases page and download the latest build
wget https://github.com/dani-garcia/bw_web_builds/releases/download/vX.XX.XX/bw_web_vX.XX.XX.tar.gz
  1. Extract the contents of the tar
tar -xvf bw_web_vX.XX.XX.tar.gz
  1. Move the extracted web-vault directory to /var/lib/vaultwarden/
mv web-vault/ /var/lib/vaultwarden/
  1. Clone the vaultwarden web-vault repository:
git clone https://github.com/dani-garcia/bw_web_builds.git bw_web_builds
cd bw_web_builds
  1. Use one of the following options to build the web-vault:
  • Using docker to build and extract the web-vault:
make docker-extract
  • Or using the host’s npm and node:
make full

After installing the web-vault, change the owner of the directory and its children to bitwarden:

chown -R bitwarden:bitwarden /var/lib/vaultwarden/

Pre-launch configuration #

Begin by creating and setting up a database corresponding to the backend you chose earlier:

If you have not set up MariaDB, do that first:

mysql_secure_installation

Then connect to mysql:

mysql

Create a new database:

CREATE DATABASE bitwarden CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

Then create a new database user and grant rights:

CREATE USER 'bitwarden'@'localhost' IDENTIFIED BY 'yourpassword';
GRANT ALL ON bitwarden.* TO 'bitwarden'@'localhost';
FLUSH PRIVILEGES;
EXIT;

If you are using MySQL v8.x, you need to create the user like this:

-- Use this on MySQLv8 installations
CREATE USER 'bitwarden'@'localhost' IDENTIFIED WITH mysql_native_password BY 'yourpassword';
GRANT ALL ON bitwarden.* TO 'bitwarden'@'localhost';
FLUSH PRIVILEGES;

If you created the user already and want to change the password type:

-- Change password type from caching_sha2_password to native
ALTER USER 'bitwarden'@'localhost' IDENTIFIED WITH mysql_native_password BY 'yourpassword';

I do not use PostgreSQL, and cannot find a guide that uses PostgreSQL to quickly follow. Unfortunately, I do not have the time to learn how to use PostgreSQL meaning that I cannot fill this section currently. If you would like to help me complete this, please contact me.

https://github.com/dani-garcia/vaultwarden/wiki/Using-the-PostgreSQL-Backend

Similarly, I have found little official documentation or any guides using SQLite3 as a backend for vaultwarden. If you would like to help me complete this, please contact me.

Now, navigate to the environment file we copied into /etc/ earlier with your favorite text editor, I will be using nano:

nano /etc/vaultwarden.env

There’s a lot of options, and it’s up to you personally to tune the instance to your liking, but to get it up and running, we’ll only need to change a few.

The template environment file does a great job at explaining what each option does, so I won’t go into much depth here. The following is an example of what needs to change before launching for the first time.

DATABASE_URL=mysql://bitwarden:yourpassword@localhost/bitwarden
ADMIN_TOKEN=1234567890abcdefghijklmnopqrstuvwxyz
DOMAIN=https://bw.domain.tld/

As recommended, change the admin token to a long, random string. You can use the following command to generate one:

openssl rand -base64 48

Setting up systemd service #

In order to launch vaultwarden on system startup, and have access to the other systemd tools, it’s required to have a .service file:

nano /etc/systemd/system/bitwarden.service

The official vaultwarden wiki provides a good template that this doc has been following, which requires minimal change if you have been following this doc verbatim:

[Unit]
Description=Bitwarden Server (Rust Edition)
Documentation=https://github.com/dani-garcia/vaultwarden
# If you use a database like mariadb,mysql or postgresql, 
# you have to add them like the following and uncomment them 
# by removing the `# ` before it. This makes sure that your 
# database server is started before vaultwarden ("After") and has 
# started successfully before starting vaultwarden ("Requires").

# Only sqlite
# After=network.target

# MariaDB
# After=network.target mariadb.service
# Requires=mariadb.service

# Mysql
# After=network.target mysqld.service
# Requires=mysqld.service

# PostgreSQL
# After=network.target postgresql.service
# Requires=postgresql.service


[Service]
# The user/group vaultwarden is run under. the working directory (see below) should allow write and read access to this user/group
User=bitwarden
Group=bitwarden
# The location of the .env file for configuration
EnvironmentFile=/etc/vaultwarden.env
# The location of the compiled binary
ExecStart=/usr/bin/vaultwarden
# Set reasonable connection and process limits
LimitNOFILE=1048576
LimitNPROC=64
# Isolate vaultwarden from the rest of the system
PrivateTmp=true
PrivateDevices=true
ProtectHome=true
ProtectSystem=strict
# Only allow writes to the following directory and set it to the working directory (user and password data are stored here)
WorkingDirectory=/var/lib/vaultwarden
ReadWriteDirectories=/var/lib/vaultwarden
# Allow vaultwarden to bind ports in the range of 0-1024
AmbientCapabilities=CAP_NET_BIND_SERVICE

[Install]
WantedBy=multi-user.target

After making your changes, reload systemd:

systemctl daemon-reload

From here, you can now start, stop, and restart the service. To enable autostart, run:

systemctl enable bitwarden.service

Initial start #

Now that the service file has been configured, you can start bitwarden using systemd:

systemctl start bitwarden

Checking the status with

systemctl status bitwarden

should yield something similar to:

● bitwarden.service - Bitwarden Server (Rust Edition)
   Loaded: loaded (/etc/systemd/system/bitwarden.service; enabled; vendor preset: enabled)
   Active: active (running) since Wed 2021-07-21 02:35:27 EDT; 1s ago
     Docs: https://github.com/dani-garcia/vaultwarden
 Main PID: 12345 (vaultwarden)
    Tasks: 14 (limit: 9511)
   Memory: 6.1M
   CGroup: /system.slice/bitwarden.service
           └─16593 /usr/bin/vaultwarden

Jul 21 02:35:28 debian-server vaultwarden[16593]: Running migration 20200409235005
Jul 21 02:35:28 debian-server vaultwarden[16593]: Running migration 20200701214531
Jul 21 02:35:28 debian-server vaultwarden[16593]: Running migration 20200802025025
Jul 21 02:35:28 debian-server vaultwarden[16593]: Running migration 20201130224000
Jul 21 02:35:28 debian-server vaultwarden[16593]: Running migration 20201209173101
Jul 21 02:35:28 debian-server vaultwarden[16593]: Running migration 20210311190243
Jul 21 02:35:28 debian-server vaultwarden[16593]: Running migration 20210430233251
Jul 21 02:35:28 debian-server vaultwarden[16593]: Running migration 20210511205202
Jul 21 02:35:28 debian-server vaultwarden[16593]: Running migration 20210701203140
Jul 21 02:35:28 debian-server vaultwarden[16593]: [2021-07-21 02:35:28.209][start][INFO] Rocket has launched from http://0.0.0.0:8000

You should now be able to access the instance from your web browser via http://<SERVER>:8000/.

From here, you should configure the rest of your environment variables from the admin page (if you enabled it by generating an admin token during the pre-launch configuration) or by editing the .env file directly:

nano /etc/vaultwarden.env

Extras #

Setting up webserver #

The recommended method of accessing the vaultwarden instance is via reverse proxy.

You can accomplish this by either installing:

  • Apache
apt install apache2
  • Nginx
apt install nginx

I also highly recommend certbot, which allows you use Let’s Encrypt’s free SSL certificates to use HTTPS:

apt install certbot

Proxy Examples #

Apache

Enable mod_proxy_wstunnel and mod_proxy_http:

a2enmod proxy_wstunnel proxy_http
systemctl restart apache2

Then create a new virtual host file for bitwarden:

nano /etc/apache2/bitwarden.conf

And edit the following example to your liking:

<VirtualHost *:443>
    SSLEngine on
    ServerName bitwarden.$hostname.$domainname

    SSLCertificateFile ${SSLCERTIFICATE}
    SSLCertificateKeyFile ${SSLKEY}
    SSLCACertificateFile ${SSLCA}
    ${SSLCHAIN}

    ErrorLog \${APACHE_LOG_DIR}/bitwarden-error.log
    CustomLog \${APACHE_LOG_DIR}/bitwarden-access.log combined

    <IfModule mod_proxy.c>
            RewriteEngine On
            RewriteCond %{HTTP:Upgrade} =websocket [NC]
            RewriteRule /notifications/hub(.*) ws://localhost:3012/$1 [P,L]
            ProxyPass / http://localhost:8000/
            ProxyPassReverse / http://localhost:8000/
            RemoteIPHeader X-Forwarded-For
    </IfModule>
</VirtualHost>
Nginx
server {

    server_name bitwarden.$hostname.$domainname;

    location / {
        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;

        proxy_pass http://localhost:8000;
    }

    location /notifications/hub/negotiate {
        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;

        proxy_pass http://localhost:8000;
    }

    location /notifications/hub {
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header Forwarded $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;

        proxy_pass http://localhost:3012;
    }

    listen [::]:443 ssl;
    listen 443 ssl;
    ssl_certificate ${SSLCERTIFICATE};
    ssl_certificate_key ${SSLKEY};

}
server {
    if ($host = bitwarden.$hostname.$domainname) {
        return 301 https://$host$request_uri;
    }

    server_name bitwarden.$hostname.$domainname;

    listen [::]:80;
    listen 80;
    return 404;
}