Table of Contents
Split-Horizon DNS Done Right
I've been using split-horizon DNS for years…and I've hated every minute of it. I hate having to maintain 2 sets of DNS records that refer to the same damn resource. It is my personal definition of “pain in the ass”. Recently, I started working with the Unbound resolver, and more specifically, the Python scripting module implementation it has. I was playing around with this for a project at work and it occurred to me while I was doing that, here is my answer to the plague that is split-horizon DNS.
I took it upon myself to write a nice little module that allows you to map external A records to internal IPs. This allows you to set up your external DNS names for “www.example.com” that point to 188.8.131.52, but is actually the web server on your internal network at 192.168.1.100.
The goal here is to walk you through setting up ub-split-map with a basic Unbound configuration to let you have a modern, fast internal resolver that transparently maps your external to internal addresses.
I'm not going to go into a lot of detail here; I assume anyone who has made it this far has some basic *nix technical knowhow and can do things like install necessary packages, etc.
I'm not going to say a lot here, but make sure you have Unbound installed with the Python module. You'll want to do one of the following:
sudo apt-get install unbound python-unbound sudo yum install unbound unbound-python USE=python emerge unbound ./configure --with-pythonmodule
That's it for now, we'll come back to configuration of unbound in a bit.
There's a lot of ways to do this as well. You can download the source from github, clone the git repository from github or use
pip to install it.
Installing From Source
I'm going to assume you know how to download and unzip file. Once you have the
ub-split-map-x.x.x dir. Do the standard Python install routine:
cd ub-split-map-x.x.x sudo python setup.py install
Installing From PyPi
This is pretty simple if you either have
sudo pip install ub-split-map # OR sudo easy_install ub-split-map
This is going to take a little bit of “file finding” in order to get set up, depending on how you installed
ub-split-map and how your system is configured, etc., but it's not too difficult. You should find
ub-split-map.ini.default in an
etc dir. This might be
/usr/local/etc, but it's there. Once you find that, move it to your
/path/to/etc/unbound and rename it to
ub-split-map.ini. For the purposes of this document, I'll assume your unbound config dir is
Opening up the file, you'll see a few sections. There is more info in the default ini file, but I'm going to go over the simplest, default config here.
You should see a
[maps] section in the file. All you have to do is add your IP address maps here. For this example, we'll say you have some static IPs and that 2 of them are
184.108.40.206 and these map to 2 different servers on your internal network at
192.168.0.55 respectively. All you have to add to your config file under
[maps] is the following:
220.127.116.11 = 192.168.0.54 18.104.22.168 = 192.168.0.55
That's it, you're done configuring your ub-split-map.
I'm not going to go into a full unbound configuration here, but instead I'll give you something very basic to get you up and running. There are a plethora of options for Unbound and I'd suggest reading through its docs for the full lowdown.
What I'm going to cover is, obviously, wiring up
ub-split-map and configuring an internal for any additional internal devices that don't map to the outside world.
IMPORTANT: Many installs of Unbound will set up a
chroot environment by default. This is by no means a bad idea, but I'm going to have you turn this off below as there are different challenges in running anything in a
chroot that is beyond the scope of this document.
Wiring Up ub-split-map
First, we'll add the couple of lines needed to get
ub-split-map working. For the purposes of the example, I'm going to assume that
ubsplitmap.py was installed at
If you open up
/etc/unbound/unbound.conf, you'll probably see a whole lot of commented out stuff. Feel free to read through it, but here's what we will need for now:
server: module-config: "validator python iterator" # There will probably be a whole lot of stuff here python: python-script: "/usr/lib/python2.6/site-packages/ubsplitmap.py"
That's it. You are just telling the server, in the server section, on the
module-config line to throw python in the mix. Then, in the python section, you are telling that python module where the script to use is. Pretty simple.
Setting up an internal zone
NOTE: This section is completely optional. If you don't have anything on your internal network that you want to be accessible by name, you can skip this section.
Here, we'll just set up a simple zone mapping for any internal-only devices you might have.
IMPORTANT: This writeup is based on Unbound 1.4.19. Older versions of Unbound do not support the use of the
include: config option as used below!
We'll say you want your internal zone to be
example.com. Here is what you need in your
unbound.conf file (in addition to the python stuff).
server: local-zone: example.com typetransparent include: /etc/unbound/internal/*.zone
Now, let's set up a “zone file”.
example.com zone file
As you probably noticed in the previous section, we have an
include: that looks for anything ending in
.zone in the
/etc/unbound/internal directory. Let's create that directory now:
Now, open up
/etc/unbound/internal/example.com.zone in your text editor of choice. You want to add
local-data: directives in here for any items that should have a domain name for your internal network only. Let's say you have a printer that you want to be at
printer.example.com (original, I know) on
192.168.0.10 and file server at
192.168.0.20. Here is what you need to add to your
local-data: "printer.example.com. IN A 192.168.0.10" local-data: "files.example.com. IN A 192.168.0.20"
IMPORTANT: Note the period at the end of the domain names!
You can continue to add as many
local-data: lines as you want to add for your zone.
It should also be noted that you can add other zones with their own zone files. Another cool thing you can do is add PTR records for your IPs with the
local-data-ptr: directive. Let's say you want to add PTRs for the file server and printer from the above example. Let's open up
/etc/unbound/internal/0.168.192.in-addr.arpa.zone with your editor and add the following:
local-data-ptr: "192.168.0.10 printer.example.com" local-data-ptr: "192.168.0.20 files.example.com"
Other unbound.conf stuff
As I noted above, make sure you have the following line in the server section of the config:
That will turn off the running of unbound in a
You will probably need to make other modifications to the
unbound.conf file to get things completely up and running, such as setting the
access-control: options and possibly a few other things depending on how your distro has configured you by default.
That should be it. You should be able to fire up Unbound and test your external to internal IP mappings as well as your internal only lookups.
If you run into any snags that you just can't figure out (and you've RTFM), feel free to shoot me an email with questions at email@example.com.