Setup & Configure CrowdSec HAProxy Bouncer
With the recent release of the official HAProxy bouncer for Crowdsec I thought I would give it a go. In doing this I found out that CrowdSec bouncer was detecting the IP using source IP, which required the use of CF-Connecting-IP to be set as the source IP. I was having issues because I have a loop in my HAProxy configuration that meant it was grabbing 127.0.0.1 as the client IP. I have resolved all of these issues and it is now working perfectly.
This guide assumes you have HAProxy 2.5 or higher running. I am using Ubuntu.
Requirements
- HAProxy 2.5 or higher
- Cloudflare account
- CrowdSec account
- Backend Web Services
Install CrowdSec Agent & Bouncer
There isn’t much to this. You simply need to run the following.
1
2
curl -s https://packagecloud.io/install/repositories/crowdsec/crowdsec/script.deb.sh | sudo bash
sudo apt install crowdsec
If you aren’t installing it on Ubuntu you can go here for instructions. The bouncer is also installed from apt.
1
sudo apt install crowdsec-haproxy-bouncer
CrowdSec Configuration
It is simple to add your CrowdSec agent to the console. Sign into your CrowdSec console and under the Instances section, click Instances.
Click on Add Instance and you will be supplied with the command to run on your server. It will look like the following.
1
sudo cscli console enroll abcd1e2fg3456hi789jklmn0o
Run that command and approve it on the console.
Bouncer Configuration
I had to manually add a bouncer for CrowdSec after installation to get a API key.
1
sudo cscli bouncers add haproxy
Your API key will be output as follows.
1
2
3
4
5
Api key for 'haproxy':
091cc8a021dbda47884a8870b1b1dd8a
Please keep this key since you will not be able to retrieve it!
Copy the API key and paste it into /etc/crowdsec/bouncers/crowdsec-haproxy-bouncer.conf
. You can also add the ban template path as below.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
ENABLED=true
API_KEY=091cc8a021dbda47884a8870b1b1dd8a
# haproxy
# path to community_blocklist.map
MAP_PATH=/var/lib/crowdsec/lua/haproxy/community_blocklist.map
# bounce for all type of remediation that the bouncer can receive from the local API
BOUNCING_ON_TYPE=all
FALLBACK_REMEDIATION=ban
REQUEST_TIMEOUT=3000
UPDATE_FREQUENCY=10
# live or stream
MODE=stream
# exclude the bouncing on those location
EXCLUDE_LOCATION=
#those apply for "ban" action
# /!\ REDIRECT_LOCATION and RET_CODE can't be used together. REDIRECT_LOCATION take priority over RET_CODE
# path to ban template
BAN_TEMPLATE_PATH=/var/lib/crowdsec/lua/haproxy/templates/ban.html
REDIRECT_LOCATION=
RET_CODE=
#those apply for "captcha" action
# ReCaptcha Secret Key
SECRET_KEY=
# Recaptcha Site key
SITE_KEY=
# path to captcha template
CAPTCHA_TEMPLATE_PATH=/var/lib/crowdsec/lua/haproxy/templates/captcha.html
CAPTCHA_EXPIRATION=3600
HAProxy Configuration
Under the global section you add the following
1
2
3
4
# Crowdsec bouncer
lua-prepend-path /usr/lib/crowdsec/lua/haproxy/?.lua
lua-load /usr/lib/crowdsec/lua/haproxy/crowdsec.lua
setenv CROWDSEC_CONFIG /etc/crowdsec/bouncers/crowdsec-haproxy-bouncer.conf
This is where my configuration is a bit fun. I have a loop in my HAProxy to allow for internal access to go directly to the servers and not through Cloudflare. See this post for an explanation of my HAProxy set up.
On my frontend that makes the decision on what frontend to send the connection to; I make sure only Cloudflare IP addresses are allowed to connect. This means no external connection can send a request from another IP and spoof the CF-Connecting-IP header. You will need to create a file with the Cloudflare IPs. This is simple by doing the following.
1
sudo wget -O /etc/haproxy/CF_ips.lst https://www.cloudflare.com/ips-v4
The frontend used for redirecting the connection is as follows.
1
2
3
4
5
6
7
8
9
10
frontend https-redirect
bind *:443
mode tcp
option tcplog
tcp-request inspect-delay 5s
tcp-request content accept if { req_ssl_hello_type 1 }
acl internal src 192.168.88.1/24
acl cloudflare src -f /etc/haproxy/CF_ips.lst
use_backend cloudflare if cloudflare
use_backend internal if internal
Make sure to set your internal IP ranges at acl internal src
. You can add multiple ranges here by separating them with a space like 192.168.88.1/24 192.168.89.1/24
. You could alternatively do the same as the Cloudflare acl and save a list of internal IP ranges in a file.
Since I only want CrowdSec working on my external (cloudflare) frontend, I only place the bouncer configuration there.
1
2
3
4
5
6
7
8
9
10
11
12
13
frontend cloudflare
bind *:7000 accept-proxy ssl crt domain.com.pem
# CloudFlare CF-Connecting-IP header to source IP for Crowdsec decisions
http-request set-src req.hdr(CF-Connecting-IP)
# Crowdsec bouncer
stick-table type ip size 10k expire 30m # declare a stick table to cache captcha verifications
http-request lua.crowdsec_allow # action to identify crowdsec remediation
http-request track-sc0 src if { var(req.remediation) -m str "captcha-allow" } # cache captcha allow decision
http-request redirect location %[var(req.redirect_uri)] if { var(req.remediation) -m str "captcha-allow" } # redirect to initial url
http-request use-service lua.reply_captcha if { var(req.remediation) -m str "captcha" } # serve captcha template if remediation is captcha
http-request use-service lua.reply_ban if { var(req.remediation) -m str "ban" } # serve ban template if remediation is ban
The important part that differentiates from the CrowdSec documentation is setting the CF-Connecting-IP as the source IP.
The final part is adding the 2 required backends. The names of the backends must be captcha_verifier
and crowdsec
1
2
3
4
5
6
7
# Backend for google to allow DNS resolution if using reCAPTCHA
backend captcha_verifier
server captcha_verifier www.google.com:443 check
# Backend for crowdsec to allow DNS resolution
backend crowdsec
server crowdsec localhost:8080 check
Captcha Configuration
There is nothing special to do here and you can follow the official documentation for this.
You should now have a working bouncer for HAProxy.