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