Loading different website content based on country with GeoDNS

Have you ever wanted to serve different DNS records out based on the location of the originating request?

Using the GeoDNS Bind patch we can have example.com resolve to a server in the US only if the requesting IP address loading the page is based in the US, and then have all other requests forward onto a server based in another country. You will be able to point a request for a domain from any country code to specified DNS records with this method.

This can be beneficial for many reasons, it will allow faster website page load times in different countries due to all content being served by the nearest server. It also allows you to run up different websites for the same domain in different countries as these will be running on different servers.

In this example we will be compiling Bind from source on our test server (CentOS 6.3) which is acting as the name server, that is it is used to serve out DNS.

Compiling Bind with the GeoDNS patch

Download and install Maxmind’s GeoIP C API – I’m using current latest version 1.4.8.

wget http://geolite.maxmind.com/download/geoip/api/c/GeoIP-1.4.8.tar.gz
tar xf GeoIP-1.4.8.tar.gz 
cd GeoIP-1.4.8
./configure --prefix=/usr/local/geoip
...
checking zlib.h usability... no
checking zlib.h presence... no
checking for zlib.h... no
configure: error: Zlib header (zlib.h) not found. Tor requires zlib to build. You may need to install a zlib development package.

To fix this error we just need to install the zlib development package (zlib1g-dev on Debian).

yum install zlib-devel.x86_64
./configure --prefix=/usr/local/geoip
make
make install

Download the GeoDNS patch for Bind.

cd ..
wget http://www.caraytech.com/geodns/bind-9.4.1-geodns-patch.tar.gz
tar xf bind-9.4.1-geodns-patch.tar.gz

Next download Bind.

Note that I am using this version of Bind (9.4.1-P1) because this is the version the current patch is for. I have not tested the patch with newer versions of Bind however it is believed to still work, please test this however before applying into production environments.

wget http://ftp.isc.org/isc/bind9/9.4.1-P1/bind-9.4.1-P1.tar.gz
tar xf bind-9.4.1-P1.tar.gz

Now we will be using the Patch command, this was not installed for me initially so it was installed using yum.

patch -p0 < bind-9.4.1-geodns-patch/patch.diff
bash: patch: command not found
yum install patch
...
patch -p0 < bind-9.4.1-geodns-patch/patch.diff
...
patching file bind-9.4.1-P1/lib/dns/acl.c
patching file bind-9.4.1-P1/lib/dns/include/dns/acl.h
patching file bind-9.4.1-P1/lib/isccfg/aclconf.c

Now we will compile Bind specifying our GeoIP installation in /usr/local/geoip/ from earlier.

cd bind-9.4.1-P1
CFLAGS="-I/usr/local/geoip/include" LDFLAGS="-L/usr/local/geoip/lib -lGeoIP" ./configure --prefix=/usr/local/bind
...
checking for C compiler default output file name... a.out
./a.out: error while loading shared libraries: libGeoIP.so.1: cannot open shared object file: No such file or directory
See `config.log' for more details.

This only happened with the CentOS 6.3 test I was running, and did not happen with Debian 6 - the fix is simple.

nano /etc/ld.so.conf
Add "/usr/local/geoip/lib/" into file and save.
ldconfig

Trying again should now succeed.

CFLAGS="-I/usr/local/geoip/include" LDFLAGS="-L/usr/local/geoip/lib -lGeoIP" ./configure --prefix=/usr/local/bind
make
make install

Configuration

Now that Bind is compiled and installed with the GeoDNS patch we just need to configure our DNS to how ever we want.

This is done through the named.conf file, I've created this file within /usr/local/bind/etc/ and it contains the following:

include "/etc/rndc.key";

controls {
        inet 127.0.0.1 allow { localhost; } keys { "rndc-key"; };
};

options {
    directory                "/var/named"; // the default
    pid-file                 "/var/run/named/named.pid";
    dump-file                "data/cache_dump.db";
    statistics-file          "data/named_stats.txt";
        allow-transfer {"none";};
}

logging {

    channel default_debug {
            file "data/named.run";
            severity dynamic;
    };
};

In this example, the included file "/etc/rndc.key" exists and contains the following.

key "rndc-key" {
        algorithm hmac-md5;
        secret "RSQjVF5KKE0pI0VNVA==";
};

The secret is a simple base 64 string.

Now under this we define our views. In this example we will be sending all North American users who request example.com to the site hosted at 1.1.1.1, while sending the rest of the world to 2.2.2.2.

view "north_america" {
      match-clients { country_US; country_CA; country_MX; };
      recursion no;
      zone "example.com" {
            type master;
            file "/var/named/example-us.com.db";
      };
};

view "other" {
      match-clients { any; };
      recursion no;
      zone "private-software.com" {
            type master;
            file "/var/named/example.com.db";
      };
};

So our named.conf file is used to specify which .db zone file should be used, in this example we have two files for example.com that give out different DNS as follows.

/var/named/example-us.com.db

; Zone file for example.com
$TTL 14400
example.com.   86400   IN      SOA     ns1.example.com.       admin.example.com.     (
  2012080500 ;Serial Number
  86400 ;refresh
  7200 ;retry
  3600000 ;expire
  86400   )
example.com.        300     IN      A       1.1.1.1

/var/named/example.com.db

; Zone file for example.com
$TTL 14400
example.com.   86400   IN      SOA     ns1.example.com.       admin.example.com.     (
  2012080500 ;Serial Number
  86400 ;refresh
  7200 ;retry
  3600000 ;expire
  86400   )
example.com.        300     IN      A       2.2.2.2

Once the changes are all in place named can be started.

cd /usr/local/bind/sbin/
./named

You will want to confirm that it has started with no errors, as problems with named.conf can cause this to fail. A good place to look for errors is /var/log/messages. Alternatively run netstat -antp and see if the server is listening on port 53 for DNS.

Summary

With this patch installed you will now be able to create different zone files for users requesting DNS from the server running Bind - the name server. These requests will be served different DNS based on the country the IP address originates from which can allow you to do a lot of really cool things.

  1. Hi Jarrod,

    Tried to follow your instructions, they were very accurate. well done!
    The thing is that now, in the bind configuration, I get some errors about some files missing (I checked, and the /var/named folder for instance indeed wasn’t created, as well as the /etc/rndc.key file.

    The process went smooth on Centos 6.4, what am I missing?

    Many thanks,
    Isaac.

  2. Hi,

    can you help me..
    i test it at debian 6 but i cannot see listening port 53?
    during process compailer I do not find error at all

    root@angga:/usr/local/bind/sbin# ./named-checkconf
    root@angga:/usr/local/bind/sbin# ./named
    root@angga:/usr/local/bind/sbin# netstat -npl | grep 53
    root@angga:/usr/local/bind/sbin# cat /etc/issue
    Debian GNU/Linux 6.0 \n \l

    thanks.

  3. Rikky Yoelanda Putra

    Hi, it’s a great tutorial.
    Anyway, I’m a new guy in this stuff. Can you tell me how we can use your system for multiple domains ?
    So I want to use this DNS system for about 5 or 7 domains.

  4. @angga: for debian you do not have to use this howto as debian bind package is already patched …

    hth
    regards tcpdump

  5. No start named?

Leave a Comment

NOTE - You can use these HTML tags and attributes:
<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>