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:
- 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
- Extract the contents of the tar
tar -xvf bw_web_vX.XX.XX.tar.gz
- Move the extracted web-vault directory to
/var/lib/vaultwarden/
mv web-vault/ /var/lib/vaultwarden/
- Clone the vaultwarden web-vault repository:
git clone https://github.com/dani-garcia/bw_web_builds.git bw_web_builds
cd bw_web_builds
- 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
andnode
:
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
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;
}