Self-hosting Bitwarden on a Beagle Bone Black
Password managers are very useful utilities that store (and generate) unique and lengthy passwords. Many utilities exist to store passwords locally (pass, EncryptionWizard, etc), but I need my passwords synced across several devices. Dozens of password managers exist that perform multi-device sync. But, many services require storing passwords on their servers. Some allow storing encrypted stores on your cloud (Dropbox, OneDrive, etc). However, I want my password stores to exist 100% under my local control. ARM development boards, like the Beagle Bone Black Wireless, provide a nice low-cost, low-power platform to run a password manager store. Additionally, the device can be easily powered down to take the password store offline. A Raspberry Pi should also work, but I was lacking one on hand.
Bitwarden is the only open source password manager I've discovered that allows self-hosting the server and also provides open source iOS, Android, Linux, OS X, and Windows clients. Unfortunately, the official Bitwarden server does not support ARM because of a mssql
dependency. Joshua Stein wrote a nice Ruby server supporting the Bitwarden API that can be self-hosted on ARM devices. (Servers written in golang and Rust also exist.)
Running rubywarden on the Beagle Bone Black Wireless only allows syncing passwords between devices when they are on the same network as the BBBW. Trading the "inconvenience" of local-only sync for 100% control of my password store is well worth it, in my opinion.
Note: 8bit Solutions LLC has graciously open sourced Bitwarden. Show your support for open source companies by purchasing a premium membership even if you self-host. High quality software does not write itself.
Initial Setup
- Install dependencies
# apt-get install bundler libsqlite3-dev
# gem install bundler
- To slightly improve security, a utility account named
rubywarden
will be used to run the server.
# adduser --disabled-password --disabled-login rubywarden
-
Clone the
rubywarden
repository into/opt
$ cd /opt
# git clone https://github.com/jcs/rubywarden
# chown -R rubywarden /opt/rubywarden
# sudo su rubywarden
-
Create the necessary directory structure for
rubywarden
$ cd rubywarden
$ mkdir -p db/production
-
Install the necessary ruby dependencies
$ bundle install
-
Before the first run, the
rubywarden
database must be initialized$ env RACK_ENV=production bundle exec rake db:migrate
-
rubywarden
does not allow new user sign-up unless the environmental variableALLOW_SIGNUPS
is true. To launch the server and allow sign-ups run the following command. Subsequent launches do not require the environmental variable.$ env RACK_ENV=production ALLOW_SIGNUPS=1 bundle exec rackup -p 4567 config.ru
-
Bitwarden provides a variety of client installs. Choose the appropriate one and click the gear icon on the splash screen to add the self-hosted server.
- Create an account and start managing passwords! Note: If testing with the iOS client, please read the dedicated iOS section below.
systemd
It's really useful to have rubywarden
run when the BeagleBone is powered up. Writing a systemd unit file to provide startup functionality is fairly straightforward.
Create /etc/systemd/system/rubywarden.service
and add the following:
[Unit]
Description=rubywarden password manager service
[Service]
Type=simple
User=rubywarden
WorkingDirectory=/opt/rubywarden
Environment="RACK_ENV=production"
Environment="ALLOW_SIGNUPS=1"
ExecStart=/usr/local/bin/bundle exec rackup -p 4567 config.ru
Restart=always
[Install]
WantedBy=multi-user.target
Enable and start the service. Use journalctl -u rubywarden
to debug any issues.
# systemctl enable rubywarden.service
# systemctl start rubywarden.service
Compatility with iOS app
The Bitwarden AppImage seems to function just fine without rubywarden
using HTTPS. By default, it is only using HTTP. However, the iOS client requires HTTPS.
In order to support HTTPS, the Apache webserver (already running on the BBBW) will be configured to serve HTTPS and function as a proxy to the rubywarden
server. Since rubywarden
is not internet accessible, Let's Encrypt certificates don't make sense; instead a self-signed certificate will be used for HTTPS. In order for the self-signed certificate to be usable on iOS, a Certificate Authority certificate will need created and installed on the iOS device.
Note: Apple changed trusted certificate requirements in iOS 13 requiring an extendedKeyUsage
flag to be set in the certificate.
-
Create the CA certificate
$ openssl genrsa -out rubywardenCA.key 2048
$ openssl req -x509 -sha256 -new -key rubywardenCA.key -out rubywardenCA.crt -subj /CN="rubywarden CA"
-
Send the
rubywardenCA.crt
certificate to the iOS device via e-mail and follow the prompts to install. After installation, use the Settings app to navigate toGeneral->About->Certificate Trust Settings
and togglerubywarden CA
on. This means that iOS will treat any certificate signed by the CA as a valid HTTPS connection. -
Generate a certificate for Apache to use
$ openssl genrsa -out rubywarden.key 2048
$ openssl req -new -out rubywarden.req -key rubywarden.key -subj /CN=beaglebone.local
$ openssl x509 -req -sha256 -in rubywarden.req -out rubywarden.crt -CAkey myCA.key -CA myCA.cer -days 365 -CAcreateserial -CAserial serial -extfile <(printf "extendedKeyUsage = serverAuth\nsubjectAltName=DNS:beaglebone.local")
-
The keys created above can be used to perform mitm attacks if they are compromised. To improve security (slightly, the SD card can just be removed from the BBBW) move them to
/root/certs/beaglebone.local/
and make the keys read-only.# mkdir -p /root/certs/beaglebone.local/
# mv rubywarden.* /root/certs/beaglebone.local
# chmod 400 /root/certs/beaglebone.local/*.key
-
Finally, set up Apache to serve as an HTTPS proxy. Append the following VirtualHost entry to
/etc/apache2/sites-enabled/000-default.conf
Relaunch Apache with# systemctl restart apache2.service
after making the edits.
Listen 4566
NameVirtualHost *:4566
<VirtualHost *:4566>
ServerName beaglebone.local
ProxyPreserveHost On
ProxyRequests Off
ProxyPass / http://beaglebone.local:4567/
ProxyPassReverse / http://beaglebone.local:4567/
SSLEngine on
SSLCertificateFile "/root/certs/beaglebone.local/rubywarden.crt"
SSLCertificateKeyFile "/root/certs/beaglebone.local/rubywarden.key"
</VirtualHost>
Did your security posture improve because of this post? Consider saying thanks by using my Amazon Affilliate URL and help to keep this site ad & analytics free.