Guide to Setting up Bitwarden behind an Apache reverse proxy configuration

Guide to Setting up Bitwarden behind an Apache reverse proxy configuration

Using a Reverse Proxy

In this guide we will cover how to set up a Self-hosted Bitwarden Server, accessed via an Apache2 Reverse Proxy.

A reverse proxy is an application that sits between end-users and the servers and services that they wish to access. There are many reason a user may wish to implement a reverse proxy, which can be read about here:

https://httpd.apache.org/docs/2.4/howto/reverse_proxy.html

In particular, many users implement reverse proxies so that only a single endpoint is exposed to the internet.

General Overview

In this guide we will create the following setup:

The guide begins with traffic destined for vault.example.com hitting the router, which is configured to forward traffic to ports 80 and 443 of an Apache reverse proxy. This reverse proxy is hosted on a host machine running Debian.

Apache will forward the traffic depending on the subdomain requested. In the guide and code examples below, a single route is configured:

vault.example.com consists of a standard Bitwarden installation with a self-signed certificate, running in Docker on the same Debian host as the Apache Reverse Proxy

Certificate Termination Considerations

Bitwarden must be configured to use a TLS certificate. When using Bitwarden in a web-facing configuration, this is terminated at the nginx container on the Bitwarden server itself. The various options available are described in the Help Guide here:

When a reverse proxy server is used, the certificate should instead be terminated at the reverse proxy. Despite this, a self-signed certificate must still be available to Bitwarden, the configuration steps for which are described later on in this article.

Certificate Renewal Considerations

With the end-user certificate being handed over to the Apache server, the LetsEncrypt renewal services used by the Bitwarden server can no longer be used, and the end-user is now responsible for this action.

While this can be handled in many ways, this guide will demonstrate how to use Certbot with a CRON job to perform this task.

Debian host and Apache Reverse Proxy Configuration

First we’ll look at the Apache Reverse-Proxy Server, which in this example is running on a Debian host.

Static IP configuration

This server will need a static IP on the network in order to receive incoming traffic from the router. This is generally be configured with netplan:

https://wiki.debian.org/NetworkConfiguration#Configuring_the_interface_manually

Apache2 & Required Modules Installation

There are many ways to configure and install Apache, including building from source, running inside a container via Docker and via APT. Using the APT package manager is probably the simplest way to do so:

sudo apt install apache2
sudo a2enmod headers
sudo a2enmod proxy
sudo a2enmod proxy_http
sudo a2enmod xml2enc

Note that Apache2 requires additional modules to be installed to function as a reverse proxy.

You can check that the installation was successful by running:

apachectl -v

On Debian, Apache2 should automatically be picked up by Systemd and set to run as a service. Some useful commands to manage Apache2 are:

sudo apachectl -t # tests your nginx configuration file

systemctl restart apache2 # restarts the Apache2 server, taking into account any changes made to the configuration file

Writing the Apache2 Configuration File

Similar to nginx, Apache2 works from a ‘sites available’ list, which contains potential configuration files for sites and services you wish to expose via the server. You can then use the Apache2 CLI tool to enable and disable these as you wish.

In this example, we only wish to make a single service available - a Bitwarden server running at vault.example.com We’ll start out by creating a configuration file at:

/etc/apache2/sites-available/vault.example.com.conf

Writing a complete Apache2 configuration is not within the scope of this article, but comprehensive documentation can be found here:

https://httpd.apache.org/docs/

Many applications also come with suggested Apache2 configurations, which are an excellent starting point to add code-blocks for other proxy-destinations and virtual-hosts.

In the guide below, we will manually specify the configuration, and then run Certbot to make the necessary certificates available.

Apache2 configuration

We first need to provide two VirtualHost blocks, which will provide the configuration for our Bitwarden server over both http and https. A sample configuration file can be found here:

Note: We are redirecting http and https requests for vault.bitwarden.com to ports 81 and 444 on the local machine respectively. Since the default ports for Bitwarden are 80 and 443 for these protocols, further configuration to the Bitwarden server will be required.

Note: There are headers and other variables present in the Bitwarden (vault.example.com) Server block, which are used to ensure that the traffic flowing to the Bitwarden server via the Apache2 reverse proxy is handled correctly.

Enable the configuration

Once we have finalised our configuration file, we need to make it available in the ‘sites-enabled’ list. This can be performed via the following commands:

sudo a2ensite vault.example.com.conf
sudo systemctl reload apache2
sudo systemctl restart apache2

Install Certbot

Certbot is a free, open source software tool to automatically generate Let’s Encrypt certificates. Their website is located here: https://certbot.eff.org/

Bitwarden sets up Certbot in Docker to generate LetsEncrypt signed certificates as part of the installation script, and regularly calls this tool as part of updates and the ‘renewcert’ shell command. Because in this example the server is being terminated at the host’s Apache2 server, and not at the Bitwarden installation, we will need to configure Certbot manually. For this guide, we’re going to use Certbot with its built-in Cloudflare DNS plugin. On Debian, this can be installed with the following:

sudo apt install certbot python3-certbot-apache
pip3 install certbot-dns-cloudflare

We should then place our CloudFlare API key in a secured file called ‘credentials.ini’, and ensure that a DNS entry exists for the vault.example.com.

Use Certbot to generate SSL certificates

Once this is done, Certbot can obtain a certificate via the following command (assuming the credentials.ini file is located at ~/certbot/credentials.ini):

sudo certbot certonly --dns-cloudflare --dns-cloudflare-credentials ~/certbot/credentials.ini -d vault.example.com

Once run, check that the SSL certificates have been placed in /etc/letsencrypt/live/vault.example.com/, as expected by the Apache2 configuration.

Bitwarden Certificate Configuration - Using a Self Signed Certificate

Even though Bitwarden is being fronted by the Apache2 server, which is handling certificate termination, it is best practice to configure Bitwarden to use a self-signed certificate in order to encrypt its internal communications. How you do this will depend on whether you are installing Bitwarden from scratch, or converting an existing Bitwarden server to run behind the proxy.

New Bitwarden Installations

If you have not yet installed your Bitwarden server, the installation scripts will take care of generating and configuring a self-signed certificate for you.

Follow the standard installation guide (Linux Standard Deployment | Bitwarden Help Center), but when prompted whether to use a LetsEncrypt certificate in step #5, select no, and no again when asked if you have an SSL certificate to use. The installer will then prompt you to generate a self-signed SSL certificate, at which point please select yes to do so.

Post-install configuration

Post-install configuration steps should be completed as normal, including the setup of admin users and an SMTP server:

Finally, start Bitwarden when you have made all the necessary configuration changes:

Configuring an Existing Bitwarden Installation

If you have an existing Bitwarden server, configured with either a LetsEncrypt or CA provided certificate, this will need to be swapped over to a self-signed certificate once the reverse proxy has been put in place.

Generate and configure Self Signed certificate

Generating and configuring Bitwarden to use a Self Signed certificate is documented at the link below:

Once you have performed these steps, you will need to rebuild and then restart your server using the Bitwarden shell script commands.

Note: If you are moving away from using a SelfSigned certificate managed by LetsEncrypt, Bitwarden will still attempt to renew this, even though it is no longer being used by the Bitwarden Server. You can stop this from happening by deleting or renaming the /bwdata/letsencrypt/live directory (e.g. mv live live_bak)

Note: If you are moving away from using a LetsEncrypt certificate, don’t forget to set the ssl_managed_lets_encrypt variable to false in /bwdata/config.yml

Changing the Bitwarden http and https ports

Finally, as Apache2 will be running on ports 80 and 443 on the Debian host, these will not be available to Bitwarden, which we must therefore configure to use alternative ports. This is achieved by modifying the /bwdata/config.json as shown below:

# Docker compose file port mapping for HTTP. Leave empty to remove the port mapping.
# Learn more: https://docs.docker.com/compose/compose-file/#ports
http_port: 81
#
# Docker compose file port mapping for HTTPS. Leave empty to remove the port mapping.
# Learn more: https://docs.docker.com/compose/compose-file/#ports
https_port: 444

Because changes have been made to the config.json file, we must run ./bitwarden rebuild and then ./bitwarden.start to take these into account.

Testing

At this stage, assuming that all has gone well, you should be able to access your Bitwarden server at vault.example.com via a browser! You should also be able to configure your Bitwarden clients to access the server as described below:

Automate Certificate Renewals with Certbot

Finally we can use CRON to automatically renew our SSL certificates on the Docker Host, now that Bitwarden can no longer take care of this task for us.

Certbot comes with this functionality already built in - in order to test your renewals you can use the command

sudo certbot renew –dry-run

After checking that no errors are shown in the output, it is convenient to add a cronjob to renew this on a regular basis.

Although the certificates last for a long time, Certbot recommends running the renewal script regularly. If the certificates are not due to be renewed then no action will be taken, while running regularly will help should a Let’sEncrypt initiated revocation take place for any reason.

It is good practise to choose a random minute to on which to run the scripts (i.e., replace 51 with a random number between 0 and 59):

crontab -e

Add the following line to your crontab

# m h  dom mon dow   command
51 6 * * * certbot renew --post-hook "systemctl  reload apache2" >/dev/null 2>&1

Other Resources

We also have a guide with example code using nginx as a Reverse Proxy. This guide differs slightly in that the proxy is situated on a different machine, and the Certbot nginx plugin is used to modify the nginx config. This could serve as inspiration for modifications to this guide using Apache.

1 Like