# HG changeset patch # User Kim Alvefur # Date 1699803467 -3600 # Node ID ad5c7779375062a17776b933f14a4c065a493722 # Parent 0ac4545cb4f911de18d4dd7c0d8b834051c00ec5 mod_firewall: Add FROM COUNTRY condition based on GeoIP DB diff -r 0ac4545cb4f9 -r ad5c77793750 mod_firewall/README.markdown --- a/mod_firewall/README.markdown Sun Nov 12 16:14:09 2023 +0100 +++ b/mod_firewall/README.markdown Sun Nov 12 16:37:47 2023 +0100 @@ -301,6 +301,31 @@ stanza. It is not advisable to perform access control or similar rules on JIDs in these chains (see the [chain documentation](#chains) for more info). +#### GeoIP matching + + Condition Matches + ---------------- -------------------------------------------------------------- + `FROM COUNTRY` Two or three letter country code looked up in GeoIP database + +This condition uses a GeoIP database to look up the origin country of +the IP attached to the current session. + +For example: + + # 3 letter country code + FROM COUNTRY: SWE + + # or 2 letter + FROM COUNTRY: SE + + # Explicit + FROM COUNTRY: code=SE + FROM COUNTRY: code3=SWE + +**Note:** This requires that the `lua-geoip` and `geoip-database` +packages are installed (on Debian, package names may differ on other +operating systems). + #### INSPECT INSPECT takes a 'path' through the stanza to get a string (an attribute diff -r 0ac4545cb4f9 -r ad5c77793750 mod_firewall/conditions.lib.lua --- a/mod_firewall/conditions.lib.lua Sun Nov 12 16:14:09 2023 +0100 +++ b/mod_firewall/conditions.lib.lua Sun Nov 12 16:37:47 2023 +0100 @@ -381,4 +381,30 @@ }; end +-- FROM COUNTRY: SE +-- FROM COUNTRY: code=SE +-- FROM COUNTRY: SWE +-- FROM COUNTRY: code3=SWE +-- FROM COUNTRY: continent=EU +-- FROM COUNTRY? --> NOT FROM COUNTRY: -- (for unknown/invalid) +-- TODO list support? +function condition_handlers.FROM_COUNTRY(geoip_spec) + local condition = "=="; + if not geoip_spec then + geoip_spec = "--"; + condition = "~="; + end + local field, country = geoip_spec:match("(%w+)=(%w+)"); + if not field then + if #geoip_spec == 3 then + field, country = "code3", geoip_spec; + elseif #geoip_spec == 2 then + field, country = "code", geoip_spec; + else + error("Unknown country code type"); + end + end + return ("get_geoip(session.ip, %q) %s %q"):format(field:lower(), condition, country:upper()), { "geoip_country" }; +end + return condition_handlers; diff -r 0ac4545cb4f9 -r ad5c77793750 mod_firewall/mod_firewall.lua --- a/mod_firewall/mod_firewall.lua Sun Nov 12 16:14:09 2023 +0100 +++ b/mod_firewall/mod_firewall.lua Sun Nov 12 16:37:47 2023 +0100 @@ -263,7 +263,41 @@ }; scan_list = { global_code = [[local function scan_list(list, items) for item in pairs(items) do if list:contains(item) then return true; end end end]]; - } + }; + iplib = { + global_code = [[local iplib = require "util.ip";]]; + }; + geoip_country = { + global_code = [[ +local geoip_country = require "geoip.country"; +local geov4 = geoip_country.open(module:get_option_string("geoip_ipv4_country", "/usr/share/GeoIP/GeoIP.dat")); +local geov6 = geoip_country.open(module:get_option_string("geoip_ipv6_country", "/usr/share/GeoIP/GeoIPv6.dat")); +local function get_geoip(ips, what) + if not ips then + return "--"; + end + local ip = iplib.new_ip(ips); + if not ip then + return "--"; + end + if ip.proto == "IPv6" and geov6 then + local geoinfo = geoinfo:query_by_addr6(ip.addr); + if geoinfo then + return geoinfo[what or "code"]; + end + elseif ip.proto == "IPv4" and geov4 then + local geoinfo = geoinfo:query_by_addr(ip.addr); + if geoinfo then + return geoinfo[what or "code"]; + end + end + return "--"; +end + ]]; + depends = { + "iplib" + } + }; }; local function include_dep(dependency, code)