Hardening a Selfhosted Vaultwarden Password Manager
Vaultwarden is an alternative implementation of the Bitwarden server API written in Rust. It is compatible with the regular upstream Bitwarden clients such as the Desktop apps for Windows, MacOS and Linux as well as the Android and iOS mobile applications.
It is a more lightweight implementation of the official Bitwarden password manager solution and perfect for self-hosted deployments where running the official resource-heavy Bitwarden services might not be possible. It runs on Docker and supports various architectures such as ARM and AMD. This allows spawning your own password manager instance by e.g. using a small Raspberry Pi single-board computer.
What You Will End Up With
In this blog post we will focus on securing a Vaultwarden Docker instance with a dockerized Fail2ban IDS container. With this setup you will be able to detect and effectively block automated login attacks by malicious threat actors which try to gain unauthorized access. Furthermore, you will learn about implementing Telegram notifications about actual IP bans.
This can be an additional protection mechanism besides having multi factor authentication enabled as well as email notifications when a successful login occurs from an unknown device or new location.
Setup and Installation
In order to secure and harden a Vaultwarden docker instance, we first have to get an instance up and running. Furthermore, we are going to use an open-source tool called Fail2ban, also running as Docker container, in order to detect and prevent malicious hacking attempts on our password manager's login area based on log data.
Note that I will not explain how Docker works or how to adjust a Docker compose configuration file. Feel free to browse the web to keep on learning.
Spawning a Vaultwarden Docker Instance
You can use the following exemplary docker-compose.yml
to spawn a Vaultwarden instance with persisted data. Please adjust the volume mappings as well as the SMTP settings to enable mail communication.
Afterwards, start the Docker container with the following command:
docker compose up -d
CF-Connecting-IP
.If you are using Nginx Proxy Manager as reverse proxy, please add the following configuration to the advanced section of all your proxy hosts behind Cloudflare:
real_ip_header CF-Connecting-IP;
Spawning a Fail2ban Docker Instance
You can use the following exemplary docker-compose.yml
to spawn a Fail2ban instance with persisted data. We will use a Docker image provided by crazymax and utilize the host
network of our server. Please adjust the volume mappings and environment variables to your needs.
Afterwards, start the Docker container with the following command:
docker compose up -d
Configuring Fail2ban
Upon starting our Fail2ban Docker container, we will notice four new folders at the persisted data storage we defined in the above docker-compose.yml
file:
- action.d
- db
- filter.d
- jail.d
These folders are necessary and used to define our actual Fail2ban configuration in order to detect malicious behavior in log files as well as ban the IP address of misbehaving threat actors. Note that the db
folder can be ignored for now since it only holds the sqlite3 database for Fail2ban.
Put the following configuration file called jail.local
inside the jail.d
directory. This is our main configuration file for Fail2ban. It defines various settings for our jails such as which log file to monitor and when as well as how long a threat actor should be banned. Adjust to your needs and liking. Consult the Fail2ban documentation for more details regarding configuration options and syntax.
ignoreip
parameter. This ensures that we are not banning ourselves or our CDN provider by accident.Put the following configuration file called vaultwarden_login_bruteforce.conf
inside the filter.d
directory. This configuration file is used to define which Vaultwarden log entries are relevant for Fail2ban to monitor and act on:
failregex
parameter specifies a typical log entry format of a failed Vaultwarden login attempt. If such entries occur multiple times, it is an indicator of an ongoing brute force attack to gain unauthorized access.Finally, put the following two configuration files inside the action.d
directory.
The first configuration file is used to ban threat actors on Cloudflare itself using the Cloudflare API. This is only necessary if you are using Cloudflare as CDN provider with the orange cloud symbol enabled for your Vaultwarden DNS entry. If so, please adjust the below configuration file and define your Cloudflare API credentials at the last two configuration lines.
Alternatively or additionally, use the following configuration file if you want to ban the threat actor's IP address via iptables
on your server directly. Note that we have to use the DOCKER-USER
chain, since we are using Docker containers only.
Furthermore, we will utilize the iptable's netfilter extension to ban the real IP address of a threat actor in the X-Forwarded-For
header. This is necessary if you are running Vaultwarden behind one or multiple proxies such as Cloudflare > Reverse Proxy > Vaultwarden Docker instance
. Iptables can only see the source IP address of packets, namely the IP of your proxy, but not the real visitor's IP address commonly defined in HTTP headers like X-Real-IP
or X-Forwarded-For
etc.
Note: Newer OS versions seem to use nftables only. Since nftables do not support string matching, your IP bans via fail2ban are not effective. Install and configure legacy iptables if you want to use this feature. Otherwise, you have to use Cloudflare and its API for banning.
You can list kernel modules with the command
ls /lib/modules/`uname -r`/kernel/net/netfilter/
and search for the required xt_string.ko
kernel module. If you do not get a hit, your kernel seems not yet to support iptables' string matching extension.If all configuration files are set up, please restart the Fail2ban Docker container to reflect all changes. For example via the following bash command:
Configuring Telegram Notifications
Note that you can specify multiple actionban
and actionunban
actions. Currently, we only ban a misbehaving IP address of a threat actor via iptables and the Cloudflare API. However, we can also implement additional Telegram notifications about actual IP bans.
For this, append an additional line of code at the actionban
and actionunban
definitions, which calls a shell script that sends Telegram notifications. As an example, a proper configuration should then look like this:
The corresponding bash script to send notifications via your Telegram bot can be obtained via the following file download. Place the script within the action.d
directory and define your Telegram API token and chat ID inside the script.
Testing our Setup
Finally, we should test our Fail2ban IDS setup for proper configuration and that it really works. Head over to your mobile phone, disconnect from your local Wi-Fi network and start using a mobile LTE connection. This ensures that your soon to be made malicious requests are not originating from a whitelisted IP address within the ignoreip
parameter of the jail.local
Fail2ban configuration file.
Then proceed accessing your Vaultwarden instance via your mobile web browser. Start inserting a valid Vaultwarden login username (important!) but an invalid user password and click login. Proceed with these invalid login attempts (at least 10 times since this is the current threshold defined in Fail2ban) until you notice a ban of your IP address. You should not be able to access the Vaultwarden web vault anymore from your mobile phone.
Also consult the Fail2ban Docker logs for more details. You should see a log entry notifying you about an IP address (your LTE IP) behaving maliciously. You can also login at Cloudflare and inspect the WAF or firewall rules to confirm an actual IP ban on Cloudflare CDN.
To unban yourself again, use the following bash command inside the Fail2ban docker container:
Additional Recommendations
Besides protecting the login area of Vaultwarden, I recommend further security improvements in general:
- Enable 2FA for all of your Vaultwarden accounts and backup your 2FA recovery codes.
- Enable emergency access for all of your Vaultwarden accounts.
- Whitelist Cloudflare IPv4 and IPv6 IP addresses on your reverse proxy only and deny any other requests not originating from Cloudflare CDN.
- Implement additional Fail2ban jails and filters to monitor for excessive HTTP requests resulting in 404/403 HTTP errors. Generally known as forceful browsing hacking attempts to identify interesting HTTP endpoints, directories and files.
- Regularly backup your Vaultwarden data. For example by ttionya/vaultwarden-backup or Bruceforce/vaultwarden-backup. Adhere to the popular 3-2-1 backup policy.
Member discussion