Skip to content

Mark Embling

DHCP and Dynamic DNS

My network at home up until recently has centred entirely around a cheap ADSL router from O2. This is fine, but I decided to move the DHCP duty over to my little home server. As part of this, I thought it would be nice for it to also handle DNS, and dynamically assign DNS names to the machines it learns about through DHCP requests.

This post is just a brief overview of the steps I went through in order to get this working. The end result is that every machine which connects to my network is assigned the domain name <hostname>.home.markembling.info.

Background

Basically the need for this arose from having to know the IP address of the machine I'm after in order to connect to it. A particular annoyance if I want to SSH from my iMac into my laptop. I had to do one of the following:

  • Statically assign an IP on the device (the iMac was set up this way)
  • Remember/guess the IP that the router's DHCP had given the target device (my laptop)

Neither of these options was very good. Resolving by hostname was an option since my router supports the very setup I'm discussing here, but very badly. And everything ended up named <hostname>.local which just plain irritated me.

In order to resolve this, I installed a DHCP server and Bind on my server and linked the two together. This means that I have much finer control over the DHCP setting and the hostnames (the router had no options to change the .local part). And it ended up working properly. And I no longer need to have any of my machines (well beside the server and a few others) statically assigned IPs.

That said, I should admit that part of the reason for doing this is just because I can, and to see how it is actually done. It's a win-win situation.

Setting Up

This was written based on Ubuntu 11.10 and involved a fair bit of trial and error. I'd guess that it'll work without much change in past or future versions though, along with other Debian-based distros and any others if you make the appropriate changes for where your config files and whatnot might be.

First of all, I made sure that both the Bind and DHCP server packages were installed:

sudo apt-get install bind9 isc-dhcp-server

DNS

The next step for me was to set up the DNS records. Because these records are actually going to get updated based on the addresses handed out via DHCP, I'm storing the zone files in /var/lib/bind - AppArmor will be happy to see these files written to as well as read from.

My network is based on the 10.25.0.0/16 address range (a little strange and probably overkill for a home network for sure, but I had my reasons) and I decided to use the home.markembling.info domain. Therefore, I created the following two files to handle the forward and reverse lookups:

db.home.markembling.info

This file contains all the information for the home.markembling.info zone, including all those hosts which I actually do want to assign statically - the server itself, the router and a couple of others.

$ORIGIN .
$TTL 86400      ; 1 day
home.markembling.info   IN SOA  cobalt.home.markembling.info. dnsadmin.markembling.info. (
                                3541726342 ; serial
                                7200       ; refresh (2 hours)
                                3600       ; retry (1 hour)
                                604800     ; expire (1 week)
                                86400      ; minimum (1 day)
                                )
                        NS      cobalt.home.markembling.info.
                        A       10.25.2.4
cobalt                  A       10.25.2.4
drobo                   A       10.25.2.2
router                  A       10.25.1.1
wifi                    A       10.25.1.2

Just to clarify, cobalt is the server machine itself. I chose to use a subdomain of my main one, although other people may choose a shorter one such as '.local' or '.home'. The filename and contents would be adjusted accordingly.

db.10.25

$ORIGIN .
$TTL 86400      ; 1 day
25.10.in-addr.arpa      IN SOA  cobalt.home.markembling.info. dnsadmin.markembling.info. (
                                3541726342 ; serial
                                7200       ; refresh (2 hours)
                                3600       ; retry (1 hour)
                                604800     ; expire (1 week)
                                86400      ; minimum (1 day)
                                )
                        NS      cobalt.home.markembling.info.
$ORIGIN 1.25.10.in-addr.arpa.
1                       PTR     router.home.markembling.info.
2                       PTR     wifi.home.markembling.info.
$ORIGIN 2.25.10.in-addr.arpa.
2                       PTR     drobo.home.markembling.info.
4                       PTR     cobalt.home.markembling.info.

Configuration

Bind also needs to be told about the zone files we've just created. I updated /etc/bind/named.conf.local to look like this:

zone "home.markembling.info" in {
        type master;
        file "/var/lib/bind/db.home.markembling.info";
        allow-update { key "rndc-key"; };
        notify yes;
};

zone "25.10.in-addr.arpa" in {
        type master;
        file "/var/lib/bind/db.10.25";
        allow-update { key "rndc-key"; };
        notify yes;
};

include "/etc/bind/rndc.key";

I also updated /etc/bind/named.conf.options like this:

options {
        directory "/var/cache/bind";

        // ...bunch of comments here...

        forwarders {
                10.25.1.1;  # Forward on to the broadband router for unknown hosts
                            # If you prefer, you could enter OpenDNS or Google's public
                            # DNS here.
        };

        auth-nxdomain no;    # conform to RFC1035
        listen-on-v6 { any; };
};

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

DHCP

Now that Bind is configured to our liking, it's time to make the DHCP server do what we want too — and update the DNS records. I updated /etc/dhcp/dhcpd.conf as follows (I've omitted most of the comments and examples the file started with).

ddns-update-style interim;
ddns-domainname "home.markembling.info.";
ddns-ttl 60;            # 1 minute
ignore client-updates;  # Ignore Windows FQDN updates

option domain-name "home.markembling.info";
option domain-name-servers 10.25.2.4;  # this machine

include "/etc/bind/rndc.key";

zone home.markembling.info. {
    primary 127.0.0.1;
    key rndc-key;
}

default-lease-time 600;
max-lease-time 7200;

authoritative;

log-facility local7;

subnet 10.25.0.0 netmask 255.255.0.0 {
    range 10.25.10.1 10.25.10.254;
    option routers 10.25.1.1;

    zone 25.10.in-addr.arpa. {
        primary 10.25.2.4;
        key "rndc-key";
    }

    zone home.markembling.info. {
        primary 10.25.2.4;
        key "rndc-key";
    }
}

AppArmor

I had to update the AppArmor configuration so that the DHCP server could read the rndc-key file. Simply add the following line to the end of /etc/apparmor.d/local/usr.sbin.dhcpd:

/etc/bind/*.key r,

Make It So

The next step is to restart AppArmor, Bind and the DHCP server. Don't forget to turn off any other DHCP servers you might still have on the network (broadband router for example).

sudo service apparmor restart
sudo service bind9 restart
sudo service isc-dhcp-server restart

All being well, these should all restart without any problems and it is ready to go.

Testing and Troubleshooting

I found the easiest way to test this was literally to connect a device (or have an existing one such as my iPhone get a new DHCP lease). Hopefully it will be given an appropriate IP address, domain name and so on.

To test that the DNS had been updated appropriately, I just asked the server.

$ nslookup Marks-iPhone.home.markembling.info
Server:     10.25.2.4
Address:    10.25.2.4#53

Name:   Marks-iPhone.home.markembling.info
Address: 10.25.10.9

$ nslookup Marks-iPhone
Server:     10.25.2.4
Address:    10.25.2.4#53

Name:   Marks-iPhone.home.markembling.info
Address: 10.25.10.9$ nslookup Marks-iPhone
Server:     10.25.2.4
Address:    10.25.2.4#53

Name:   Marks-iPhone.home.markembling.info
Address: 10.25.10.9

$ nslookup 10.25.10.9
Server:     10.25.2.4
Address:    10.25.2.4#53

9.10.25.10.in-addr.arpa name = Marks-iPhone.home.markembling.info.

Assuming the correct answers came back like above, all is well.

If for some reason it doesn't seem to be working, the easiest thing is probably to just watch the log and it ought to give a pretty decent explanation of the problem.

tail -f /var/log/syslog

All in all, I find this setup much better than the previous one. It's just more convenient to be able to address all my machines by hostname whether they are statically assigned or via DHCP. Although I will admit that being able to address the iPhone like that might not be the best example of its usefulness.

As I said at the top, this involved a fair bit of trial and error and I've done this write up based partially on memory and by reading back through all my config files. Hopefully I've documented everything, but if it doesn't work and you find I've missed something, please do leave a comment.