HAProxy with Geo-IP Blocking and honeypot
From OISecWiki
To setup a HAProxy loadbalancing cluster with Geo-IP blocking, a honeypot and credential bashing blocking, do the following:
Setup a machine with Debian/Ubuntu and install the haproxy package.
In the setup we are going to build the following requirements are set:
- We have two Microsoft Terminal Servers
- The terminal servers need to be only accessible from The Netherlands, Belgium and Germany
- The terminal servers need to block connections when they are being hammered from the outside
- We need to be able to whitelist IP addresses
- The tables that have the IP's that have been temporarily blocked due to hammering, or hitting the honeypot, need to be replicated to it's peer (10.10.20.30)
- We want to expose an additional rdp server to the outside world that is not being used by any legitimate user and make it function as a honeypot to update blocklists.
The following additional files next to the haproxy.cfg will be needed:
| Filename | Description |
|---|---|
| /etc/haproxy/blacklist.lst | IP addresses that are permanently banned, overriding all other entries |
| /etc/haproxy/whitelist.lst | IP addresses that are permanently allowed, overriding all blocks, even hammering |
| /etc/haproxy/geoip.txt | IP Subnets matched to countries, generated by the cronjob |
The following configuration will do this:
global
log /dev/log local0
log /dev/log local1 notice
chroot /var/lib/haproxy
stats socket /run/haproxy/admin.sock mode 660 level admin expose-fd listeners
stats timeout 30s
user haproxy
group haproxy
daemon
# Default SSL material locations
ca-base /etc/ssl/certs
crt-base /etc/ssl/private
ssl-default-bind-ciphers ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:RSA+AESGCM:RSA+AES:!aNULL:!MD5:!DSS
ssl-default-bind-options no-sslv3
ssl-server-verify none
defaults
log global
mode http
option httplog
option tcplog
option dontlognull
timeout connect 5000
timeout client 50000
timeout server 50000
errorfile 400 /etc/haproxy/errors/400.http
errorfile 403 /etc/haproxy/errors/403.http
errorfile 408 /etc/haproxy/errors/408.http
errorfile 500 /etc/haproxy/errors/500.http
errorfile 502 /etc/haproxy/errors/502.http
errorfile 503 /etc/haproxy/errors/503.http
errorfile 504 /etc/haproxy/errors/504.http
peers abusereplication
bind 10.10.10.30:9002
server lb02 10.10.20.30:9002
server lb01
## Statistics Web FrontEnd
frontend stats
mode http
bind 10.10.10.30:9000
acl acl_management src 10.10.10.0/24 10.10.30.0/24
tcp-request connection reject if !acl_management
stats enable
stats uri /haproxy_stats
stats refresh 300s
stats realm HAproxy\ Statistics
stats auth admin:SUPERSECRETPASSWORD
stats admin if TRUE
## Generic Backends
backend Abuse
stick-table type ip size 1m expire 30m store conn_rate(90s),conn_cur,gpc0,http_req_rate(10s),http_err_rate(20s) peers abusereplication
backend LTAbuse
stick-table type ip size 1m expire 90m store conn_rate(24h),conn_cur,gpc0,http_req_rate(10s),http_err_rate(20s) peers abusereplication
backend HoneyPot
stick-table type ip size 1m expire 360m store conn_rate(6h),conn_cur,gpc0,http_req_rate(10s),http_err_rate(20s) peers abusereplication
## Frontend Servers
frontend honeypot_ft_rdp
mode tcp
bind 10.10.10.30:3389 name rdp
timeout client 1h
log global
option tcplog
acl acl_geoloc_nl src,map_ip(/etc/haproxy/geoip.txt) -m reg -i (NL|BE|DE)
acl acl_internal src 10.0.0.0/8 192.168.0.0/16
tcp-request inspect-delay 2s
tcp-request connection accept if { src -f /etc/haproxy/whitelist.lst }
tcp-request connection track-sc0 src table HoneyPot
tcp-request content reject if { src -f /etc/haproxy/blacklist.lst }
tcp-request content reject if !acl_geoloc_nl !acl_internal
tcp-request content reject if { src_conn_rate(Abuse) ge 10 }
tcp-request content reject if { src_conn_rate(LTAbuse) ge 100 }
tcp-request content reject if { src_conn_rate(HoneyPot) ge 1 }
tcp-request content accept if RDP_COOKIE
default_backend rdpf01_bk_rdp
frontend rdpf01_ft_rdp
mode tcp
bind 10.10.10.36:3389 name rdp
timeout client 1h
log global
option tcplog
acl acl_geoloc_nl src,map_ip(/etc/haproxy/geoip.txt) -m reg -i (NL|BE|DE)
acl acl_internal src 10.0.0.0/8 192.168.0.0/16
tcp-request inspect-delay 2s
tcp-request connection accept if { src -f /etc/haproxy/whitelist.lst }
tcp-request connection track-sc0 src table Abuse
tcp-request connection track-sc1 src table LTAbuse
tcp-request content reject if { src -f /etc/haproxy/blacklist.lst }
tcp-request content reject if !acl_geoloc_nl !acl_internal
tcp-request content reject if { src_conn_rate(Abuse) ge 10 }
tcp-request content reject if { src_conn_rate(LTAbuse) ge 100 }
tcp-request content reject if { src_conn_rate(HoneyPot) ge 1 }
tcp-request content accept if RDP_COOKIE
default_backend rdpf01_bk_rdp
## Backend Servers
backend rdpf01_bk_rdp
mode tcp
balance leastconn
persist rdp-cookie
timeout server 1h
timeout connect 4s
log global
option tcp-check
tcp-check connect port 3389 ssl
default-server inter 3s rise 2 fall 3
server terminal03.rds.local 10.20.10.43:3389 weight 10 check
server terminal04.rds.local 10.20.10.44:3389 weight 10 check
To fetch the geo-ip list, run the following script in a cronjob. We are using the excellent geo-ip list from iwik
#!/bin/sh
TEMP_FILE=$(mktemp)
wget -O ${TEMP_FILE} http://iwik.org/ipcountry/geoip.txt 2>/dev/null >/dev/null
WGET_EXIT=$?
if [ $WGET_EXIT -eq 0 ]; then
DL_SIZE=$(wc -c $TEMP_FILE | awk '{print $1}')
echo "Downloaded size is ${DL_SIZE}"
if [ $DL_SIZE -ge 5000000 ]; then
echo "Updating geoip database haproxy"
cp /etc/haproxy/geoip.txt /etc/haproxy/geoip.txt.old
cp ${TEMP_FILE} /etc/haproxy/geoip.txt
fi
else
echo "Download not succesful"
fi
