Geoblocking with MaxMind and geoip_lookup.py

Hi Guys

I have some problems getting geoip working correctly - I’m using the “recipe” from https://github.com/superstes/haproxy-geoip, but it doesn’t seem to work… no blocking and no mapping in file, and no obvious errors in logfiles

I have have Python Backend running in systemd, and from my point of view that part works:

curl “http://127.0.0.1:6970/?lookup=country&ip=8.8.8.8
US

I have the following files:

ls -lh /opt/haproxy/
total 8.0K
-rwxr-xr-x 1 haproxy haproxy 2.9K Jun 21 01:20 geoip_lookup.py
-rwxr-xr-x 1 root root 1.4K Jun 21 00:21 geoip_lookup_w_backend.lua

ls -lh /var/geoip/
total 6.3M
-rw-r–r-- 1 haproxy haproxy 6.3M Jun 18 14:01 GeoLite2-Country.mmdb
-rw-r–r-- 1 haproxy haproxy 0 Jun 21 00:40 haproxy_geoip_country.map

And I have installed maxminddb python module:

python3 -m pip install maxminddb

And the relevant part of haproxy.cfg:

global
log 127.0.0.1 local0
maxconn 6000
tune.ssl.default-dh-param 2048
daemon
chroot /var/lib/haproxy
uid 99
gid 99
ssl-default-server-options force-tlsv12 no-tls-tickets
ssl-default-server-ciphers ECDH+AESGCM:ECDH+CHACHA20:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:RSA+AESGCM:RSA+AES:!aNULL:!MD5:!DSS
ssl-default-bind-options force-tlsv12 no-tls-tickets
ssl-default-bind-ciphers ECDH+AESGCM:ECDH+CHACHA20:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:RSA+AESGCM:RSA+AES:!aNULL:!MD5:!DSS
lua-load /opt/haproxy/geoip_lookup_w_backend.lua
log stdout format raw local0
_
defaults
log global
timeout connect 5000ms
timeout client 50000ms
timeout server 50000ms
option httplog
mode http
_
frontend http
bind :80
mode http
redirect scheme https if !{ ssl_fc }
_
frontend http-SSL
bind :443 ssl crt
mode http
option httpclose
option forwardfor
acl private_nets src 192.168.0.0/16 172.16.0.0/12 10.0.0.0/8 127.0.0.0/8 ::1
http-request set-var(txn.geoip_country) str(00) if private_nets
acl geoip_country_in_map src,ipmask(24,48),map_ip(/var/geoip/haproxy_geoip_country.map) -m found
http-request set-var(txn.geoip_country) src,ipmask(24,48),map(/var/geoip/haproxy_geoip_country.map) if !private_nets geoip_country_in_map
http-request lua.lookup_geoip_country if !{ var(txn.geoip_country) -m found }
http-request capture var(txn.geoip_country) len 2
http-request set-map(/var/geoip/haproxy_geoip_country.map) %[src,ipmask(24,48)] %[var(txn.geoip_country)] if !private_nets !geoip_country_in_map
acl block_country src,map_ip(/var/geoip/haproxy_geoip_country.map) -m sub ru
acl block_country src,map_ip(/var/geoip/haproxy_geoip_country.map) -m sub cn
http-request deny deny_status 403 if block_country

The Haproxy version:

haproxy -v
HAProxy version 2.8.10-f28885f 2024/06/14 - https://haproxy.org/
Status: long-term supported branch - will stop receiving fixes around Q2 2028.
Known bugs: http://www.haproxy.org/bugs/bugs-2.8.10.html
Running on: Linux 4.12.14-150.47-default #1 SMP Wed Dec 18 15:05:52 UTC 2019 (8162e25) x86_64

Make settings:

make -j $(nproc) TARGET=linux-glibc USE_OPENSSL=1 USE_LUA=1 USE_PCRE2=1 USE_SYSTEMD=1

Hope there’s somebody out there with a grand idea :slight_smile:

Best regards
/Flemming

It could be interesting to check if the table gets populated using show map /var/geoip/haproxy_geoip_country.map command through the stats socket, because once haproxy is started, the map is only updated in-memory as it has no access to the disk.
You could also add some prints in the lua script to check if it successfully queries your backend.

Perhaps you could also ask the script’s author for some insights.

After messing around with chat gpt… I found one small error in my config file i didn’t notice:

chroot /var/lib/haproxy

but that only helped a little bit :slight_smile: - with the help from chatgpt I was able to rewrite the LUA script… and now the map file is being populated, and after messing around and changing some things in the backend… then my ACL’s started to work - but I still need some logging, but that seems to be a hazzle.

/Flemming

Greetings!

Nice that you find some use in the ‘template’ I’ve published :smiley:

You can easily log the results like this:

  • TCP-Mode:
    • tcp-request content capture var(txn.geoip_asn) len 10
    • tcp-request content capture var(txn.geoip_country) len 2
  • HTTP-Mode:
    • http-request capture var(txn.geoip_asn) len 10
    • http-request capture var(txn.geoip_country) len 2

Would’ve been interesting to know what the problem was. If anyone has issues with it - feel free to open an GitHub issue: Issues · superstes/haproxy-geoip · GitHub

I provision my HAProxies via Ansible: GitHub - ansibleguy/infra_haproxy: Ansible Role to provision HAProxy Community (with ACME, GeoIP and some WAF-Features)
For examples see: