Skip to content
Daniel M. Capella edited this page Nov 12, 2020 · 51 revisions

Setup Fail2ban will prevent attackers to brute force your vault logins. This is particularly important if your instance is publicaly available.

Table of Contents

Pre-requisite

  • Commands below are using vi. The basics can be found there. However, you can use whatever text editor you want.
  • From Release 1.5.0, Bitwarden_rs supports logging to file. Please set this up : Logging
  • Try to log to web vault with a false account and check the log files for following format
	[YYYY-MM-DD hh:mm:ss][bitwarden_rs::api::identity][ERROR] Username or password is incorrect. Try again. IP: XXX.XXX.XXX.XXX. Username: email@domain.com.

Installation

Debian / Ubuntu / Raspberry Pi OS

	sudo apt-get install fail2ban -y

Fedora / Centos

EPEL repository is necessary (CentOS 7)

	sudo yum install epel-release
	sudo yum install fail2ban -y

Synology DSM

With Synology, a bit more work is needed for various reasons. The full solution is pushed with Docker Compose there. The main issues are:

  1. The embedded IP ban system does not work for Docker's containers
  2. The iptables embedded do no support the REJECT blocktype
  3. The Docker GUI does not allow some advanced settings
  4. Modifying system configuration is not upgrade-proof

Therefore, we will use Fail2ban in a Docker container. Crazy-max/docker-fail2ban provides a good solution and the Synology's Docker GUI will be ignored. From command line through SSH, here the steps. As a convention volumeX is to be adapted to your Synology's config.

  1. Get root
	sudo -i
  1. Creating persistent folders
	mkdir -p /volumeX/docker/fail2ban/action.d/
	mkdir -p /volumeX/docker/fail2ban/jail.d/
	mkdir -p /volumeX/docker/fail2ban/filter.d/
  1. Replace REJECT by DROP blocktype
	vi /volumeX/docker/fail2ban/action.d/iptables-common.local
Copy and paste the following content
	[Init]
	blocktype = DROP
	[Init?family=inet6]
	blocktype = DROP
  1. Create docker-compose file
	vi /volumeX/docker/fail2ban/docker-compose.yml
Copy and paste the following content
	version: '3'
	services:
	  fail2ban:
		container_name: fail2ban
		restart: always
		image: crazymax/fail2ban:latest
		environment: 
		- TZ=Europe/Paris
		- F2B_DB_PURGE_AGE=30d
		- F2B_LOG_TARGET=/data/fail2ban.log
		- F2B_LOG_LEVEL=INFO
		- F2B_IPTABLES_CHAIN=INPUT

		volumes:
		- /volumeX/docker/fail2ban:/data
		- /volumeX/docker/bw-data:/bitwarden:ro

		network_mode: "host"

		privileged: true
		cap_add:
			- NET_ADMIN
			- NET_RAW
  1. Start the container using command line
	cd /volumeX/docker/fail2ban
	docker-compose up -d

You should see the container running in Synology's Docker GUI. You will have to reload after configuring the filters and jails

Setup for web vault

As a convention, path_f2b means the path needed for Fail2ban to work. This depends on your system. E.g. on Synology, we are talking about /volumeX/docker/fail2ban/ where on some other systems we are talking about /etc/fail2ban/

Filter

Create and fill the following file

	vi path_f2b/filter.d/bitwarden_rs.local
Copy and paste the following content
	[INCLUDES]
	before = common.conf

	[Definition]
	failregex = ^.*Username or password is incorrect\. Try again\. IP: <ADDR>\. Username:.*$
	ignoreregex =

Tip: If you get the following error message in fail2ban.log (CentOS 7, Fail2Ban v0.9.7)
fail2ban.filter [5291]: ERROR No 'host' group in '^.*Username or password is incorrect\. Try again\. IP: <ADDR>\. Username:.*$'
Please Use <HOST> instead of <ADDR> in bitwarden_rs.local

Tip: If you see 127.0.0.1 as the IP address of failed logins in bitwarden.log, then you're probably using a reverse proxy and fail2ban won't work correctly:

[YYYY-MM-DD hh:mm:ss][bitwarden_rs::api::identity][ERROR] Username or password is incorrect. Try again. IP: 127.0.0.1. Username: email@example.com.

To remedy this, forward the true remote address to bitwarden_rs via the X-Real-IP header. How to do this varies depending on the proxy you use. For example, in Caddy 2.x, when you define the reverse-proxy, define header_up X-Real-IP {remote_host}. See Proxy examples for more info.

Jail

Create and fill the following file

	vi path_f2b/jail.d/bitwarden_rs.local
Copy and paste the following content
	[bitwarden_rs]
	enabled = true
	port = 80,443,8081
	filter = bitwarden_rs
	banaction = %(banaction_allports)s
	logpath = /path/to/bitwarden.log
	maxretry = 3
	bantime = 14400
	findtime = 14400

Note: Docker uses the FORWARD chain instead of the default INPUT chain. Therefore replace the banaction line with the following action when using Docker:

	action = iptables-allports[name=bitwarden_rs, chain=FORWARD]

NOTE:
Do not use this if you use a reverse proxy before Docker container. If proxy, like apache2 or nginx is used, use the ports of the proxy and do not use chain=FORWARD, only when using Docker without proxy!

NOTE on the NOTE above:
That's at least not true for running on Docker (CentOS 7) with caddy as reverse proxy. chain=FORWARD is absolutely fine and working with caddy as reverse proxy.

Reload fail2ban for changes to take effect:

sudo systemctl reload fail2ban

Feel free to change the options as you see fit.

Setup for admin page

If you've enabled the admin console by setting the ADMIN_TOKEN environment variable, you can prevent an attacker brute-forcing the admin token using Fail2Ban. The process is the same as for the web vault.

Filter

Create and fill the following file

	vi path_f2b/filter.d/bitwarden-admin.local
Copy and paste the following content
	[INCLUDES]
	before = common.conf

	[Definition]
	failregex = ^.*Invalid admin token\. IP: <ADDR>.*$
	ignoreregex =

Jail

Create and fill the following file

	vi path_f2b/jail.d/bitwarden_rs-admin.local
Copy and paste the following content
	[bitwarden_rs-admin]
	enabled = true
	port = 80,443
	filter = bitwarden_rs-admin
	banaction = %(banaction_allports)s
	logpath = /path/to/bitwarden.log
	maxretry = 3
	bantime = 14400
	findtime = 14400

Note: Docker uses the FORWARD chain instead of the default INPUT chain. Therefore replace the banaction line with the following action when using Docker:

	action = iptables-allports[name=bitwarden_rs, chain=FORWARD]

Reload fail2ban for changes to take effect:

sudo systemctl reload fail2ban

Testing Fail2Ban

Now just try to login to bitwarden using any email (it doesn't have to be a valid email, just an email format) If it works correctly and your IP is banned, you can unban the IP by running:

Without Docker:
sudo fail2ban-client set bitwarden_rs unbanip XX.XX.XX.XX

With Docker:
sudo docker exec -t fail2ban fail2ban-client set bitwarden_rs unbanip XX.XX.XX.XX

If Fail2Ban does not appear to be functioning, verify that the path to the Bitwarden log file is correct. For Docker: If the specified log file is not being generated and/or updated, make sure the EXTENDED_LOGGING env variable is set to true (which is default) and that the path to the log file is the path inside the Docker (when you use /bw-data/:/data/ the log file should be in /data/... to be outside the container).

Also verify that the timezone of the Docker container matches the timezone of the host. Check this by comparing the time shown in the logfile with the host OS time. If they differ, there are various ways to fix this. One option is to start Docker with the option -e "TZ=<timezone>". A list of valid timezones is here (eg. -e "TZ=Australia/Melbourne")

If you are using podman instead of Docker it seems that setting the timezone via -e "TZ=<timezone>" does not work. This can be solved (when using the alpine image) by following this guide: https://wiki.alpinelinux.org/wiki/Setting_the_timezone.

SELinux Problems

When you are using SELinux it is possible that SELinux hinders fail2ban to read the logs. If so, follow these steps: sudo tail /var/log/audit/audit.log. There you should see something along the lines of this (of course the actual audit ID will vary in your case):

type=AVC msg=audit(1571777936.719:2193): avc:  denied  { search } for  pid=5853 comm="fail2ban-server" name="containers" dev="dm-0" ino=1144588 scontext=system_u:system_r:fail2ban_t:s0 tcontext=unconfined_u:object_r:container_var_lib_t:s0 tclass=dir permissive=0

To actually find out the reason you can use grep 'type=AVC msg=audit(1571777936.719:2193)' /var/log/audit/audit.log | audit2why. audit2allow -a will give you specific instructions on how to create a module and allow fail2ban to access these logs. Follow these steps and you're done! fail2ban should now work correctly.

Clone this wiki locally