When BIND is built with GeoIP support, ACLs can be used for restricting access based on geographical location of the client’s IP address using the MaxMind API to query their GeoIP database, or databases in compatible formats.

BIND detects libmaxmind by default, but I explicitly specify the path to the library during configuration. (I find a bit confusing is that the switch to enable GeoIP is called maxminddb, but the rest of the configuration calls it geoip2.)

$ ./configure --prefix=/usr/local/bind9git \
	--with-openssl="${OSSL}" \
	--with-maxminddb=/usr/local/Cellar/libmaxminddb/1.6.0
...
checking for library containing MMDB_open... -lmaxminddb
configure: GeoIP2 default database path set to /usr/local/Cellar/libmaxminddb/1.6.0/share/GeoIP
...
Optional features enabled:                                                                                       
    Memory allocator: jemalloc                                                                                   
    GeoIP2 access control (--enable-geoip)  
...

GeoLite2 databases are free IP geo-location databases comparable to, but less accurate than, MaxMind’s GeoIP2 databases and require an account to download. I create an account and download the country and city databases, extract the *.mmdb files, and install them into an appropriate location:

$ ls -loh /var/named/geoip
-r--r--r--@ 1 jpm    71M Nov 23 14:55 GeoLite2-City.mmdb
-r--r--r--@ 1 jpm   5.7M Nov 23 14:48 GeoLite2-Country.mmdb

Using the mmdblookup utility from the libmaxminddb distribution with known IP addresses, I test the databases. Let’s see in which country ISC.org and which city PowerDNS.com are reported being in:

$ mmdblookup --file GeoLite2-Country.mmdb \
             --ip $(dig +short isc.org) country names en

  "United States" <utf8_string>

$ mmdblookup --file GeoLite2-City.mmdb \
             --ip $(dig +short www.powerdns.com) city names en

  "Amsterdam" <utf8_string>

Sounds about right. :-)

named

GeoIP ACLs are of the form

geoip db database field value
  • database specifies which GeoIP database to search for a match. If it isn’t specified, queries are first answered from the city and country databases if they are installed.
  • field indicates which field (country, region, city, continent, postal, isp) is to be searched for a match.
  • value is the value to search for within the database.
  • ACLs use a “first-match” logic rather than a “best-match”.

I can now create access control lists (ACLs) which use the GeoIP2 functionality to limit access to resources. Here I’m showing a simple ACL for permitting queries, but GeoIP2 ACLs are often used for matching access to views.

acl "yurpeans" {
     geoip city "Frankfurt am Main";
     geoip country Netherlands;
};

options {
	...
	geoip-directory "/var/named/geoip";

	allow-query { 
		localhost;
		yurpeans;
	};
};

ISC’s Using the GeoIP Features knowledge base article has further information and an example using match-clients in a view.

Create a custom GeoIP mmdb database

I thought it might be interesting to create my own GeoIP database and use that in BIND, somewhat along the lines of the experimental Location-Based (Geo-)DNS in a Private Network I did many years ago. (We’ve also seen something similar using GeoDNS with the PowerDNS GeoIP back-end.) The use-case I envision is permitting queries for certain domains to individual departments/divisions within large organizations by name instead of coding up ACLs with addresses in them. And of course I wanted to better understand how this all fits together.

It turns out BIND attempts to open databases by name in bin/named/geoip.c; I had assumed it would readdir() its way through the geoip-directory, but it doesn’t. I choose GeoIP2-ISP.mmdb as the one to use.

I create the mmdb file based upon MaxMind’s Build your own MMDB database blog post and the Vagrant setup in getting started, and I patch together this Perl program. My changes are indicated in # JPM comments. In particular I create a new field called “isp” and permit dumping of reserved networks. I query the result on the command line:

$ mmdblookup --file GeoIP2-ISP.mmdb --ip 192.168.33.4 isp
  "Jane" <utf8_string>

I configure named.conf to use the GeoIP database, specifying the database and the field to use to query for the value.

acl "humans" {
     // geoip db isp isp Jane;
     geoip db isp isp jpmens;
     geoip db isp isp rabbit;
};
...
zone "example.net" IN {
        type primary;
        file "example.net";
        allow-query { humans; };
};

As usual I keep an eye on the logs when named starts up to ensure the database can be found. In this case I already have the City and Country databases in the directory so named opens all three.

28-Nov-2021 10:21:25.216 opened GeoIP2 database '/var/named/geoip/GeoLite2-Country.mmdb'
28-Nov-2021 10:21:25.216 opened GeoIP2 database '/var/named/geoip/GeoLite2-City.mmdb'
28-Nov-2021 10:21:25.216 opened GeoIP2 database '/var/named/geoip/GeoIP2-ISP.mmdb'

Creating the code to populate custom mmdb files is likely not worth the effort.

DNS and Geo :: 27 Nov 2021 :: e-mail