bitwarden_rs with cf & fail2ban integration + admin portal protection - detailed guide / tutorial


Recommended Posts

Hi let me first start of by saying, that i would have liked to contribute to unraid community a long time ago,
but time has been getting in my way.

And now that lastpass has decided to limit their free service, which force many users to change password manager and a lot of people is turning to bitwarden,
i thought it would be a good idea for me to share in detail exactly how to protect your selfhosted bitwarden (bitwarden_rs) 
with fail2ban and connect it to cloudflare IP Access Rules.
And also protect /admin 😋

 

So in this guide we are going to be using the following.
1. Swag from linuxserver/swag
2. Bitwardenrs from bitwardenrs/server

3. GilbN's cloudflare-apiv4 template

 

So let's say some unknown user comes along and tries to login with unauthorized credentials / start hammering the portal

1207762974_bitwardenloginerror.thumb.png.770373d6c5a6d92edf954e914e71264b.png

 

 

30 seconds ~ 1 min and poff banned via cloudflare.

559565822_cfban.thumb.png.a086b0ce5524ff70e8e3e981b8f3ca9c.png

 

 

Here is how we set this up. 

(my bitwardenrs container is named bitwarden)

First we go into the settings of bitwardenrs container and we edit the template like I have in the images below.

2102106156_bitwardencontainer.thumb.png.2ba841d7541e038ccd26ad2ac0ab8630.png

 

And inside bitwarden logs you add this.

1076247623_containersettings.png.c76a843418514e4a21237c1ba24d836c.png

 

The /data/ is equal to /mnt/cache/appdata/bitwarden/

 

695751196_bitwardenpath.png.bb66f99d2791f41deccdced184999461.png

 

Now it's important to know that the bitwarden.log I have there is not going to be generated untill you add the varaibles in the container,
then restart the container. Once this is done the .log will show up in /appdata/bitwarden/

 

Lets now head into the fail2ban folder that is located inside 

/mnt/cache/appdata/swag/fail2ban

 

And let's open jail.local (your might be different) but these are the settings i use for my fail2ban

89832948_jailsettings.png.37f1bfe2343fef151a83715ed5d799b9.png

 

if we scroll down below to the very end, and we add our own jail for bitwarden this is what you type. (ofc you add your own IP) :)

 

940745603_bitwardenjailsettings.png.542b02b38f67d576c267b4579390c4e4.png

Save the jail and cd into, /mnt/cache/appdata/swag/fail2ban/filter.d
 

Create a new file in here called bitwarden_rs.conf

In this file you type this, 

1468648715_bitwardenDefinition.png.e51766dbfcbcb1bf263fb6dddbc125b5.png

Then save it.

 

Now cd into /mnt/cache/appdata/swag/fail2ban/action.d

Create a new file in here called cloudflare-apiv4.conf

inside this file you copy the excellent template from GilbN which you can find on his blog here
https://technicalramblings.com/blog/cloudflare-fail2ban-integration-with-automated-set_real_ip_from-in-nginx/

 

Not much needs to be changed in here just at the bottom, cfuser = and cftoken = 

and you can find those from https://dash.cloudflare.com/profile/api-tokens (Global API Key)

 

Next let's head into Swag container, and add a path like i have done in the image below.

1994870757_swagbitwarden.thumb.png.f02158f920d6e69e4fe15d05e4227250.png

 

And same thing here, 

the /bitwarden/  is equal to /mnt/cache/appdata/bitwarden/
695751196_bitwardenpath.png.bb66f99d2791f41deccdced184999461.png

 

Next you save and restart swag

 

Following commands should now work.

In unraid webui > webterminal

type > docker exec -it swag bash > cat bitwarden/bitwarden.log

A tail should work also, 

webterminal > docker exec swag tail -f /bitwarden/bitwarden.log

 

Don't know if you need it but, this is how my volume mapping looks for bitwarden.
Yours should look the same.

1842778051_volmappings.png.9bf310ca74f1dd56518a3b192b1b5c5d.png

 

 

I wanted to add this section. 
As I have CF Real IP in my nginx.conf located in /mnt/cache/appdata/swag/nginx/nginx.conf

	##
	# CF Real IP
	##
	include /config/nginx/cf_real-ip.conf;
	real_ip_header X-Forwarded-For;

right under my Gzip Settings

If i do a simple cat on cf_real-ip.conf it looks like this.

:~# cat /mnt/cache/appdata/swag/nginx/cf_real-ip.conf
## Version 2021/02/06

set_real_ip_from 103.21.244.0/22;
set_real_ip_from 103.22.200.0/22;
set_real_ip_from 103.31.4.0/22;
set_real_ip_from 104.16.0.0/12;
set_real_ip_from 108.162.192.0/18;
set_real_ip_from 131.0.72.0/22;
set_real_ip_from 141.101.64.0/18;
set_real_ip_from 162.158.0.0/15;
set_real_ip_from 172.64.0.0/13;
set_real_ip_from 173.245.48.0/20;
set_real_ip_from 188.114.96.0/20;
set_real_ip_from 190.93.240.0/20;
set_real_ip_from 197.234.240.0/22;
set_real_ip_from 198.41.128.0/17;
set_real_ip_from 2400:cb00::/32;
set_real_ip_from 2606:4700::/32;
set_real_ip_from 2803:f800::/32;
set_real_ip_from 2405:b500::/32;
set_real_ip_from 2405:8100::/32;
set_real_ip_from 2c0f:f248::/32;
set_real_ip_from 2a06:98c0::/29;


 

When it comes to the reverse proxy for some reason i could not get the default template from linuxserver.io to work for me.

So i decided to add my reverse proxy and my ssl.conf that i use for bitwarden. (gives A+ on securityheaders.com)
It's based on Roxedus template (i think...)

 

## Version 2020/12/09
server {
	listen 443 ssl http2;
	listen [::]:443 ssl http2;
	server_name bw.FQDN.TLD;

# TLS settings
	include /config/nginx/bitssl.conf;
	
# Organizr Authentication
	include /config/nginx/auth.conf;
	
# Custom Error Pages
	include /config/nginx/errorpages.conf;
	
# Maxmind Geographic IP Block
#	include /config/nginx/geoblock.conf;

	client_max_body_size 128M;
	
	location /{
		proxy_pass http://IP:PORT;
		proxy_redirect off;
		proxy_set_header Host $http_host;
		proxy_set_header X-Real-IP $remote_addr;
		proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
		proxy_set_header X-Forwarded-Proto $scheme;
		proxy_set_header X-Forwarded-Protocol $scheme;
		proxy_set_header X-Url-Scheme $scheme;
		proxy_hide_header X-Frame-Options;
		proxy_hide_header "x-webkit-csp";
		proxy_hide_header "content-security-policy";
		proxy_set_header Accept-Encoding "";
		sub_filter
		'</head>'
		'<link rel="stylesheet" type="text/css" href="https://gilbn.github.io/theme.park/CSS/themes/bitwarden/plex.css">
		</head>';
		sub_filter_once on;
	}
	location /admin {
		auth_request /auth-0;
        include /config/nginx/proxy.conf;
        resolver 127.0.0.11 valid=30s;
        set $upstream_app bitwarden;
        set $upstream_port 8242;
        set $upstream_proto http;
		proxy_pass http://IP:PORT;
	}	
	location /notifications/hub/negotiate {
		proxy_pass http://IP:PORT;
	}
	location /notifications/hub {
		proxy_pass http://IP:PORT;
		proxy_set_header Upgrade $http_upgrade;
		proxy_set_header Connection "upgrade";
  }
}

 

Error pages can be found from here 

https://docs.organizr.app/books/setup-features/page/custom-error-pages

 

Here is my bitssl.conf

## Version 2019/06/19 - Changelog: https://github.com/linuxserver/docker-letsencrypt/commits/master/root/defaults/ssl.conf

# session settings
	ssl_session_timeout 1d;
	ssl_session_cache shared:SSL:50m;
	ssl_session_tickets off;

# Diffie-Hellman parameter for DHE cipher suites
	ssl_dhparam /config/nginx/dhparams.pem;

# ssl certs
	ssl_certificate /config/keys/letsencrypt/fullchain.pem;
	ssl_certificate_key /config/keys/letsencrypt/privkey.pem;

# protocols
	ssl_protocols TLSv1.2 TLSv1.3;
	ssl_prefer_server_ciphers on;
	ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256';

# OCSP Stapling
	ssl_stapling on;
	ssl_stapling_verify on;
	resolver 127.0.0.11 valid=30s; # Docker DNS Server

## Header security settings to reach A+ Grade on, htbridge.com/websec | securityheaders.io | ssllabs.com 
	add_header Content-Security-Policy "form-action 'self' https://xy.FQDN.TLD https://FQDN.TLD; base-uri 'self'; upgrade-insecure-requests; block-all-mixed-content; frame-ancestors https://xy.FQDN.TLD https://yx.FQDN.TLD https://FQDN.TLD";
	add_header "Content-Security-Policy-Report-Only" "report-uri https://xy.report-uri.com/r/d/csp/reportOnly";
	add_header Expect-CT max-age=604800,enforce,report-uri="https://xy.report-uri.com/r/d/ct/reportOnly";
	add_header Expect-Staple "max-age=31536000; includeSubDomains; preload";
	add_header Referrer-Policy "no-referrer-when-downgrade" always;
	add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;	
	add_header X-Content-Type-Options "nosniff" always;
	add_header X-Frame-Options "ALLOW-FROM https://yx.FQDN.TLD";
	add_header X-Robots-Tag none;
##/SSL SETTINGS

 

And since i'm using organizr as my http authentication i'll include that .conf too
Now if the bitwarden.conf fails just # the auth out or ask here in comments and i'll do my best to help out :)
 

# Version 2020/12/09 #
###################### 
# Organizr Auth  v2  #
######################
#	auth_request /auth-0;	#=Admin
#	auth_request /auth-1;	#=Co-Admin 
#	auth_request /auth-2;	#=Super User 
#	auth_request /auth-3;	#=Power User 
#	auth_request /auth-4;	#=User 
#	auth_request /auth-8;	# logged in
#	auth_request /auth-9;	#=Guest

location ~ /auth-([0-9]+) {
    internal;
    include /config/nginx/proxy.conf;
    resolver 127.0.0.11 valid=30s;
    set $upstream_organizr organizrv2;
    proxy_pass http://IP:PORT/api/v2/auth?group=$1;
    proxy_set_header Content-Length "";
}

 

After this restart swag.

And that's it 🙂

Everything should now be working as intended.
 

This guide was written & created by me Zidichy on February 21, 2021.
~ Fin

Edited by Zidichy
grammar correction.
  • Like 1
  • Thanks 6
Link to comment
  • 10 months later...

Thank you for this.

I have been trying to get swag fail2ban working for the last day with Authelia. I had it banning the IP address but it was not actually blocking the connection.

 

Here is the error just in case anyone knows what it is.

2022-01-26 13:06:07,664 fail2ban.utils          [1756]: ERROR   150757ff7190 -- exec: ip6tables -w -N f2b-authelia
ip6tables -w -A f2b-authelia -j RETURN
ip6tables -w -I DOCKER-USER -p tcp -j f2b-authelia
2022-01-26 13:06:07,664 fail2ban.utils          [1756]: ERROR   150757ff7190 -- stderr: 'ip6tables: Chain already exists.'
2022-01-26 13:06:07,665 fail2ban.utils          [1756]: ERROR   150757ff7190 -- stderr: 'ip6tables: No chain/target/match by that name.'
2022-01-26 13:06:07,665 fail2ban.utils          [1756]: ERROR   150757ff7190 -- returned 1

 

Anyway 10 mins after reading your post fail2ban is working with cloudflare.

  • Like 1
Link to comment
On 1/26/2022 at 5:48 AM, Selmak said:

Thank you for this.

I have been trying to get swag fail2ban working for the last day with Authelia. I had it banning the IP address but it was not actually blocking the connection.

 

Here is the error just in case anyone knows what it is.

2022-01-26 13:06:07,664 fail2ban.utils          [1756]: ERROR   150757ff7190 -- exec: ip6tables -w -N f2b-authelia
ip6tables -w -A f2b-authelia -j RETURN
ip6tables -w -I DOCKER-USER -p tcp -j f2b-authelia
2022-01-26 13:06:07,664 fail2ban.utils          [1756]: ERROR   150757ff7190 -- stderr: 'ip6tables: Chain already exists.'
2022-01-26 13:06:07,665 fail2ban.utils          [1756]: ERROR   150757ff7190 -- stderr: 'ip6tables: No chain/target/match by that name.'
2022-01-26 13:06:07,665 fail2ban.utils          [1756]: ERROR   150757ff7190 -- returned 1

 

Anyway 10 mins after reading your post fail2ban is working with cloudflare.

 

You're very welcome :)

Link to comment
  • 7 months later...

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.