10 KiB
categories | date | lastmod | thumbnail | usePageBundles | featured | tags | title | ||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Self-Hosting | 2018-09-26T08:34:30Z | 2022-03-06 | i0UKdXleC.png | true | true |
|
BitWarden password manager self-hosted on free Google Cloud instance |
A friend mentioned the BitWarden password manager to me yesterday and I had to confess that I'd never heard of it. I started researching it and was impressed by what I found: it's free, open-source, feature-packed, fully cross-platform (with Windows/Linux/MacOS desktop clients, Android/iOS mobile apps, and browser extensions for Chrome/Firefox/Opera/Safari/Edge/etc), and even offers a self-hosted option.
I wanted to try out the self-hosted setup, and I discovered that the official distribution works beautifully on an n1-standard-1
1-vCPU Google Compute Engine instance - but that would cost me an estimated $25/mo to run after my free Google Cloud Platform trial runs out. And I can't really scale that instance down further because the embedded database won't start with less than 2GB of RAM.
I then came across this comment on Reddit which discussed in somewhat-vague terms the steps required to get BitWarden to run on the free e2-micro
instance, and also introduced me to the community-built vaultwarden project which is specifically designed to run a BW-compatible server on resource-constrained hardware. So here are the steps I wound up taking to get this up and running.
{{% notice note "bitwarden_rs -> vaultwarden"%}}
When I originally wrote this post back in September 2018, the containerized BitWarden solution was called bitwarden_rs
. The project has since been renamed to vaultwarden
, and I've since moved to the hosted version of BitWarden. I have attempted to update this article to account for the change but have not personally tested this lately. Good luck, dear reader!
{{% /notice %}}
Spin up a VM
Easier said than done, but head over to https://console.cloud.google.com/ and fumble through:
- Creating a new project (or just add an instance to an existing one).
- Creating a new Compute Engine instance, selecting
e2-micro
for the Machine Type and ticking the Allow HTTPS traffic box. - (Optional) Editing the instance to add an ssh-key for easier remote access.
Configure Dynamic DNS
Because we're cheap and don't want to pay for a static IP.
- Log in to the Google Domain admin portal and create a new Dynamic DNS record. This will provide a username and password specific for that record.
- Log in to the GCE instance and run
sudo apt-get update
followed bysudo apt-get install ddclient
. Part of the install process prompts you to configure things... just accept the defaults and move on. - Edit the
ddclient
config file to look like this, substituting the username, password, and FDQN from Google Domains:
sudo vim /etc/ddclient.conf # [tl! .cmd]
# torchlight! {"lineNumbers": true}
# Configuration file for ddclient generated by debconf
#
# /etc/ddclient.conf
protocol=googledomains,
ssl=yes,
syslog=yes,
use=web,
server=domains.google.com,
login='[USERNAME]', # [tl! highlight:3]
password='[PASSWORD]',
[FQDN]
sudo vi /etc/default/ddclient
and make sure thatrun_daemon="true"
:
# torchlight! {"lineNumbers": true}
# Configuration for ddclient scripts
# generated from debconf on Sat Sep 8 21:58:02 UTC 2018
#
# /etc/default/ddclient
# Set to "true" if ddclient should be run every time DHCP client ('dhclient'
# from package isc-dhcp-client) updates the systems IP address.
run_dhclient="false"
# Set to "true" if ddclient should be run every time a new ppp connection is
# established. This might be useful, if you are using dial-on-demand.
run_ipup="false"
# Set to "true" if ddclient should run in daemon mode [tl! focus:3]
# If this is changed to true, run_ipup and run_dhclient must be set to false.
run_daemon="true"
# Set the time interval between the updates of the dynamic DNS name in seconds.
# This option only takes effect if the ddclient runs in daemon mode.
daemon_interval="300"
- Restart the
ddclient
service - twice for good measure (daemon mode only gets activated on the second go because reasons):
sudo systemctl restart ddclient # [tl! .cmd:2]
sudo systemctl restart ddclient
- After a few moments, refresh the Google Domains page to verify that your instance's external IP address is showing up on the new DDNS record.
Install Docker
Steps taken from here.
- Update
apt
package index:
sudo apt-get update # [tl! .cmd]
- Install package management prereqs:
sudo apt-get install \ # [tl! .cmd]
apt-transport-https \
ca-certificates \
curl \
gnupg2 \
software-properties-common
- Add Docker GPG key:
curl -fsSL https://download.docker.com/linux/debian/gpg | sudo apt-key add - # [tl! .cmd]
- Add the Docker repo:
sudo add-apt-repository \ # [tl! .cmd]
"deb [arch=amd64] https://download.docker.com/linux/debian \
$(lsb_release -cs) \
stable"
- Update apt index again:
sudo apt-get update # [tl! .cmd]
- Install Docker:
sudo apt-get install docker-ce # [tl! .cmd]
Install Certbot and generate SSL cert
Steps taken from here.
- Install Certbot:
sudo apt-get install certbot # [tl! .cmd]
- Generate certificate:
sudo certbot certonly --standalone -d ${FQDN} # [tl! .cmd]
- Create a directory to store the new certificates and copy them there:
sudo mkdir -p /ssl/keys/ # [tl! .cmd:3]
sudo cp -p /etc/letsencrypt/live/${FQDN}/fullchain.pem /ssl/keys/
sudo cp -p /etc/letsencrypt/live/${FQDN}/privkey.pem /ssl/keys/
Set up vaultwarden
Using the container image available here.
- Let's just get it up and running first:
sudo docker run -d --name vaultwarden \ # [tl! .cmd]
-e ROCKET_TLS={certs='"/ssl/fullchain.pem", key="/ssl/privkey.pem"}' \
-e ROCKET_PORT='8000' \
-v /ssl/keys/:/ssl/ \
-v /bw-data/:/data/ \
-v /icon_cache/ \
-p 0.0.0.0:443:8000 \
vaultwarden/server:latest
- At this point you should be able to point your web browser at
https://[FQDN]
and see the BitWarden login screen. Click on the Create button and set up a new account. Log in, look around, add some passwords, etc. Everything should basically work just fine. - Unless you want to host passwords for all of the Internet you'll probably want to disable signups at some point by adding the
env
optionSIGNUPS_ALLOWED=false
. And you'll need to setDOMAIN=https://[FQDN]
if you want to use U2F authentication:
sudo docker stop vaultwarden # [tl! .cmd:2]
sudo docker rm vaultwarden
sudo docker run -d --name vaultwarden \
-e ROCKET_TLS={certs='"/ssl/fullchain.pem",key="/ssl/privkey.pem"'} \
-e ROCKET_PORT='8000' \
-e SIGNUPS_ALLOWED=false \
-e DOMAIN=https://[FQDN] \
-v /ssl/keys/:/ssl/ \
-v /bw-data/:/data/ \
-v /icon_cache/ \
-p 0.0.0.0:443:8000 \
vaultwarden/server:latest
Install vaultwarden as a service
So we don't have to keep manually firing this thing off.
- Create a script at
/usr/local/bin/start-vaultwarden.sh
to stop, remove, update, and (re)start thevaultwarden
container:
sudo vim /usr/local/bin/start-vaultwarden.sh # [tl! .cmd]
# torchlight! {"lineNumbers": true}
#!/bin/bash
docker stop vaultwarden
docker rm vaultwarden
docker pull vaultwarden/server
docker run -d --name vaultwarden \
-e ROCKET_TLS={certs='"/ssl/fullchain.pem",key="/ssl/privkey.pem"'} \
-e ROCKET_PORT='8000' \
-e SIGNUPS_ALLOWED=false \
-e DOMAIN=https://${FQDN} \
-v /ssl/keys/:/ssl/ \
-v /bw-data/:/data/ \
-v /icon_cache/ \
-p 0.0.0.0:443:8000 \
vaultwarden/server:latest
sudo chmod 744 /usr/local/bin/start-vaultwarden.sh # [tl! .cmd]
- And add it as a
systemd
service:
sudo vim /etc/systemd/system/vaultwarden.service # [tl! .cmd]
[Unit]
Description=BitWarden container
Requires=docker.service
After=docker.service
[Service]
Restart=always
ExecStart=/usr/local/bin/vaultwarden-start.sh # [tl! highlight]
ExecStop=/usr/bin/docker stop vaultwarden
[Install]
WantedBy=default.target
sudo chmod 644 /etc/systemd/system/vaultwarden.service # [tl! .cmd]
- Try it out:
sudo systemctl start vaultwarden # [tl! .cmd]
sudo systemctl status vaultwarden # [tl! .cmd focus:start]
● bitwarden.service - BitWarden container # [tl! .nocopy:start]
Loaded: loaded (/etc/systemd/system/vaultwarden.service; enabled; vendor preset: enabled)
Active: deactivating (stop) since Sun 2018-09-09 03:43:20 UTC; 1s ago
Process: 13104 ExecStart=/usr/local/bin/bitwarden-start.sh (code=exited, status=0/SUCCESS) # [tl! focus:end]
Main PID: 13104 (code=exited, status=0/SUCCESS); Control PID: 13229 (docker)
Tasks: 5 (limit: 4915)
Memory: 9.7M
CPU: 375ms
CGroup: /system.slice/vaultwarden.service
└─control
└─13229 /usr/bin/docker stop vaultwarden
Sep 09 03:43:20 vaultwarden vaultwarden-start.sh[13104]: Status: Image is up to date for vaultwarden/server:latest
Sep 09 03:43:20 vaultwarden vaultwarden-start.sh[13104]: ace64ca5294eee7e21be764ea1af9e328e944658b4335ce8721b99a33061d645 # [tl! .nocopy:end]
Conclusion
If all went according to plan, you've now got a highly-secure open-source full-featured cross-platform password manager running on an Always Free Google Compute Engine instance resolved by Google Domains dynamic DNS. Very slick!