Skip to main content

Bitwarden-rs Jail

My company provides a password manager, so I don't need this.  But what they provide is closed source.  I may just switch over to bitwarden, but perhaps little by little.  For now, I just want to get this working.

Like the password manager I'm provided for work, this is a zero-knowledge setup; i.e., the database is stored in an encrypted state, locked by my complex pass phrase.  If someone someone gains control of the jail, or even the host system, the database of records/credentials does them no good.  It's still scary to put up a publicly-accessible instance, but I'm doing it anyway.  Besides, I'll only use it in limited capacity for now, and perhaps I'll put it behind a VPN (Wiregaurd?) at some point.

Advanced Prep (nullfs)

(Relocate database outside the jail)

If we ever have a problem with this jail and need to blow it away, it would be nice for the database to live on.  We can do this!  In fact, this is probably one of several steps that could/should be taken to ensure data not specific to the jail is saved outside the jail.   We already did this for the BookStack jail (and the majority of this section is a straight copy).  Carrying on:

zfs create -o compress=lz4 -o atime=off zroot/data/dbs/bitwarden

Bitwarden-rs stores the database in a /data directory.  The standard install creates the /data directory in  /home/bitwardenrs/bitwarden_rs_dist.   Due to the fact that this is a thin jail, the /home directory (a few directories deep) where the null mount would go cannot be used; so instead we'll mount the /data directory over top of the new data dir we'll  create. 

We will need to adjust the .env file accordingly.

bastille console bw_jail
cd /var && mkdir -p db/data

 

Then exit from the su and exit from the console.

bastille stop bw_jail

Adjust the jail's fstab.

# Device                      Mountpoint                                                     FStype  Options Dump  Pass#

/usr/local/bastille/releases/12.1-RELEASE /usr/local/bastille/jails/bw_jail/root/.bastille   nullfs  ro      0     0

/usr/local/data/dbs/bitwarden             /usr/local/bastille/jails/bw_jail/root/var/db/data nullfs  rw,late 0     0
bastille start bw_jail

Below, you'll need to set ownership or permissions on this /var/db/data, otherwise bitwarden-rs can't write to it.

On With It

First of all, let's not run tzsetup on this jail.  That gave me problems with 2FA on another instance.  Let's try without it.

Grab initial packages/dependencies.

bastille pkg bw_jail install -y sqlite3 nginx git sudo vim-console bash node npm python27-2.7.18

Adjust

# We've got to do a bit of work inside the jail (it'll be easier there)

bastille console bw_jail

# some npm dependency will need to have python2.7 and will fail with python3

cd /usr/local/bin/

# set the symlink 

ln -s /usr/local/bin/python2.7 python

cd -

Set up user.

Add new bitwardenrs user to the jail.  Set the user below to: bitwardenrs.  Enter every line, no need for other configs, only your password

adduser -s bash

Adjust priv's and log in:

# allow sudo, we will use it later

visudo

# ADD

bitwardenrs ALL=(ALL) ALL


# change to the new user to build and execute our service

su bitwardenrs

cd 

id

# should look like: uid=1001(bitwardenrs) gid=1001(bitwardenrs) groups=1001(bitwardenrs)

One add'l step needed: (maybe... try skipping it)

bitwardenrs@bw_jail:~ $ exit

root@bw_jail:~ # chmod 1777 /tmp

root@bw_jail:~ # su bitwardenrs

Another add'l step, and you can't skip this:

Within jail, as root , cd /var/db && chown -R bitwardenrs:bitwardenrs data)

Install rust

# install latest rust version, pkg version may be outdated and can't build bitwarden_rs

curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

# include the rust env variables

source $HOME/.cargo/env

Time to build.

# get back to the users home
cd ..


# checkout the latest bitwarden_rs release 

git clone https://github.com/dani-garcia/bitwarden_rs/

cd bitwarden_rs/

git checkout "$(git tag --sort=v:refname | tail -n1)"


# and build it with sqlite support

cargo build --features sqlite --release

cargo install diesel_cli --no-default-features --features sqlite-bundled

cd ..

"If you need web-vault, we will build it here."  Of course we need the web vault.

# WEB-VAULT

# clone the repository

git clone https://github.com/bitwarden/web.git web-vault

cd web-vault


# switch to the latest tag, is not working here, dani-garcia/bw_web_builds lacks v2.12.0 patch
# export WEB_VERSION="$(git tag --sort=v:refname | tail -n1)"
# lets use the last working version

#  ^^ use that `export` command instead of the below
export WEB_VERSION=v2.14.0

git checkout ${WEB_VERSION}

# download and apply the bitwarden_rs patch

curl https://raw.githubusercontent.com/dani-garcia/bw_web_builds/master/patches/${WEB_VERSION}.patch >${WEB_VERSION}.patch

git apply ${WEB_VERSION}.patch -v

"Install dependencies and fix some issues."

# there is no native freebsd version from node-sass 4.11, lets bump it to 4.12.0

cat package.json |sed -e 's/"node-sass": "^4.11.0",/"node-sass": "4.13.0",/' | tee package.json  
# I deleted a ^ and changed to 13, from the original write-up I found


# download submodules

npm run sub:init


# manually install angular/compiler-cli

npm i @angular/compiler-cli


# install all the other dependencies 

npm install


# sweetalert used to fail with the latest angular2, but it's been fixed

Finally, Build the web-vault

npm run dist

A 1G RAM VPS instance will run out of memory.  Interestingly, it acted incapable of using the swapfile, though I wonder if I could have forced it to. 

I had to resize the droplet to get 2G of RAM, and then export NODE_OPTIONS=--max_old_space_size=4096 (from within the bash shell).  And then it works, right?  Right.

"At this point we have every components and will have to put them together"

cd


# copy bitwarden_rs dist

cp -r ~/bitwarden_rs/target/release bitwarden_rs_dist

cd bitwarden_rs_dist


# and copy the web-vault files

cp -r ../web-vault/build web-vault

Config

There are .env file settings to change.  From bitwardenrs user's home dir:

cp bitwarden_rs/.env.template bitwarden_rs_dist/.env

Edit accordingly (remember, we chose a different data dir to null-fs mount).

## Main data folder
 DATA_FOLDER=/var/db/data

{...}

## Domain settings
DOMAIN=https://vault.mydomain.tld

Set up nginx.

su 		# be root

bash


# create nginx.conf 

cat << EOF >/usr/local/etc/nginx/nginx.conf
worker_processes  auto;

events {
    worker_connections  1024;
}

http {
    include       mime.types;
    default_type  application/octet-stream;
    sendfile        on;
    keepalive_timeout  6h;

#server {
    #listen 		80;
    #server_name vault.mydomain.tld;
    #return      301 https://$server_name$request_uri;
#}

server {

    listen 10.101.10.120:80;
    server_name vault.mydomain.tld;

    #ssl_session_cache  builtin:1000  shared:SSL:10m;
    #ssl_protocols  TLSv1 TLSv1.1 TLSv1.2;
    #ssl_ciphers HIGH:!aNULL:!eNULL:!EXPORT:!CAMELLIA:!DES:!MD5:!PSK:!RC4;
    #ssl_prefer_server_ciphers on;

    access_log            /var/log/nginx/bitwarden_rs_web_vault.log;

    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://10.101.10.120:8000;
      proxy_read_timeout  90;

      #proxy_redirect      http://10.101.10.120:8000 https://10.101.10.120;
    }
  }
}
EOF

# enable and start nginx 

sysrc nginx_enable="YES"

nginx -t		# test

service nginx start

That's right.  Just port 80.  It's behind Caddy, remember?

"Create the bitwardenrs init script"

mkdir -p /usr/local/etc/rc.conf.d/


# limit the rocket server only to localhost 

echo "ROCKET_ADDRESS=10.101.10.120" >/usr/local/etc/rc.conf.d/bitwardenrs   # changed to actual


# 

cat <<EOF > /usr/local/etc/rc.d/bitwardenrs
#!/bin/sh

# PROVIDE: bitwardenrs
# REQUIRE: LOGIN DAEMON NETWORKING
# KEYWORD: jail rust

# Enable this script by adding:
# bitwardenrs_enable="YES"
# ... to /etc/rc.conf

. /etc/rc.subr

name="bitwardenrs"
rcvar="bitwardenrs_enable"
bitwardenrs_chdir=/home/bitwardenrs/bitwarden_rs_dist
# This is the tool init launches
command="/usr/sbin/daemon"

pidfile="/var/run/\${name}.pid"

# This is the tool daemon launches
task="./bitwarden_rs"
procname="/bin/bash"

command_args="-u bitwardenrs -p \${pidfile} \${task}"

load_rc_config $name
run_rc_command "\$1"
EOF


sudo sysrc bitwardenrs_enable="YES"

sudo chmod +x /usr/local/etc/rc.d/bitwardenrs

sudo service bitwardenrs start

Before going lower, be sure to create a CNAME record to catch vault.mydomain.tld.   Done?  Let's proceed.

Adjust pf.conf to allow connections.

Just kidding!   We're not touching PF. We've got caddy.  Modify the Caddyfile in the caddy_jail.  Add the following:

vault.mydomain.tld {

  gzip

  # The negotiation endpoint is also proxied to Rocket
  proxy /notifications/hub/negotiate 10.101.10.120:80 {
    transparent
  }

  # Notifications redirected to the websockets server
  proxy /notifications/hub 10.101.10.120:3012 {
    websocket
  }

  # Proxy the root directory to Rocket
  proxy / 10.101.10.120:80 {
    transparent
  }
}

Or is it encode gzip?  No, that's v2.  Your welcome, future self.

Then reload caddy.

bastille service caddy-jail caddy restart

After Setup!  Clean Up!

First, log onto your beautiful self-hosted, powered-by-rust password manager site, and set up an account with an uncrackable password.  Then...

This server is open to others to sign up and use.  Go into the .env file and shut off new user signups!

## Controls if new users can register
 SIGNUPS_ALLOWED=false

And then restart the service or restart the jail. (If you just restart the service, you may be stuck in the terminal, so I just restart the jail.) When you visit and try to sign up again with a new account, it'll pretend to allow you, and then give you a failure warning.

Also, 2FA

Bitwarden-rs allows you to various methods of 2FA.  The simplest and most common is an Authenticator app.  Do it right away.

References

Adapted from: https://www.ixsystems.com/community/threads/how-to-build-your-own-bitwarden_rs-jail.81389/

More here: https://www.reddit.com/r/Bitwarden/comments/dg78bi/building_selfhosted_bitwarden_via_bitwarden_rs/

Also: https://dennisnotes.com/note/20181112-bitwarden-server/ (Ubuntu, Docker, nginx, script install, backup procedure)