Using BIND9 and DHCP3 to setup a dynamic DNS solution.

Background

For the last four years I’ve been using the DHCP and DNS server roles on my Microsoft 2003 R2 server to form a DDNS (Dynamic DNS) solution and it’s been working well. It’s a fairly simple setup where:

  • A client device on the local subnet makes a DHCP request.
  • DHCP server responds with an IP lease.
  • The IP address is passed to the forward and reverse lookup zone of the DNS for resolution.

No mystery what’s happening there. For the server in question, the roles had expanded to include being an AD master, WSUS server and web server for our intranet, again nothing too major for a small organisation. BUT… one of my constant gripes was the time it took the server to load after each restart. Some cursory research pointed to the fact that it was waiting for the DNS server to startup and that was causing some delays/problems for the AD. To add to motivation, there was only one DNS server, which meant when it went down… NO DNS RESOLUTION!

So the idea was born to recycle a few of the Pentium III Dell Optiplex’s we had gathering dust into a primary DNS servers, with a secondary slave. I didn’t want to load Windows Server onto these boxes because (A) They were too low spec, and (B) I didn’t want to breach licencing obligations.

Ubuntu to the rescue.

It’s no secret I like Ubuntu with its solid packages and extensive support, so the 32-bit version of the server software was perfect for what I wanted to do. The plan: Two boxes running BIND9 (the DNS service) and one running DHCP3-SERVER, with updates from “Master” to “Slave” and the same forward and reverse lookup zones I have with Windows.

The Base OS Install

The specifications of your hardware are dependant on the size of your organisation and the ultimately the number of “requests” you expect to get. We’re only small and I only needed it to service resolution for the internal subnet so the PIII’s were going to be fine. I threw as much memory into the units as I could, but based on some of the reading it should be >= 320MB.

The OS install was standard, with me defining a static IP address and configuring a SWAP size of 2x the amount of physical RAM. At the end of Ubuntu’s install you can select the DNS server as additional software to be installed but for this guide I’ll assume you didn’t.

Once the install is finished it’s a good idea to check for any updates.

sudo aptitude update
sudo aptitude safe-upgrade

Packages

First, we need BIND9, and I also include the dnsutils packages for testing and troubleshooting.

sudo aptitude install bind9 dnsutils

Next, the DHCP3 package.

sudo aptitude install dhcp3-server

Primary Master

Overview

There a many ways to configure BIND9. Some of the most common configurations are a caching nameserver, primary master, and as a secondary master.

When configured as a caching nameserver BIND9 will find the answer to name queries and remember the answer when the domain is queried again.

As a primary master server BIND9 reads the data for a zone from a file on it’s host and is authoritative for that zone.

In a secondary master configuration BIND9 gets the zone data from another nameserver authoritative for the zone.

Preliminary setup

The first thing we do is setup a few references and settings in BIND9’s conf file. Note: some lines may already be present in the file, so make sure you avoid duplicates.

cd /etc/bind
sudo vi named.conf

acl "trusted" {
    1.2.3.0/24;
    localhost;
    localnets;
};
controls {
        inet 127.0.0.1 allow {127.0.0.1; 1.2.3.4; } keys { "rndc-key"; } ;
};
include "/etc/bind/named.conf.options";
include "/etc/bind/named.conf.local";
include "/etc/bind/named.conf.default-zones";

In the lines above, we have created an “access control list” called trusted, and told BIND9 that it relates to the subnet 1.2.3.0/24 (which is in CIDR format and equals 1.2.3.0/255.255.255.0 in the more common “subnet mask” format), the localhost, and any localnets. This is fairly open and if you wanted to you could restrict in even further by inclusion of specific IP addresses.

The controls statement declares control channels to be used by system administrators to control the operation of the nameserver. You can find an expanded definition here if you like. The include lines should be included in the file by default.

Since this nameserver isn’t replicating the entire internet, the first thing we do is to configure it to act as a “Caching Nameserver” (described above).

sudo vi named.conf.options

forwarders {
        9.8.7.6;
        5.4.3.2;
};
auth-nxdomain no;    # conform to RFC1035
listen-on-v6 { none; };
listen-on { 1.2.3.4; };
allow-query { trusted; };
allow-recursion { trusted; };
allow-query-cache { trusted; };

You can find an expanded definition of the options used here. The important part here is that 9.8.7.6 and 5.4.3.2 should be replaced with the IP addresses of actual nameservers (i.e. your ISP’s nameserver is always good!)

Forward Zone File

A forward zone allows DNS to resolve a name to an address. To add a DNS zone to BIND9, turning BIND9 into a Primary Master server, the first step is to edit

sudo vi etc/bind/named.conf.local

include "/etc/bind/rndc.key";
zone "local.example" {
        type master;
        file "/var/lib/bind/local.example.zone";
        allow-update { key "rndc-key"; };
        allow-transfer {1.2.3/24; };
};

WARNING:

AppArmor only allows read rights in /etc/bind but allows read/write in /var/lib/bind and /var/cache/bind. So unless you want to play around with the AppArmor settings (not a big deal), then put your zone file(s) in the latter locations. If you check the AppArmor profile in /etc/appamor.d/ it defines /var/lib/bind for dynamically updated zone (and journal) files (which is what I consider these files to be…) and /var/cache/bind is for slave/stub data.

Now use an existing zone file as a template to create the /var/lib/bind/example.com.zone file.

sudo cp /etc/bind/db.local /var/lib/bind/local.example.zone

Edit the new zone file /var/lib/bind/local.example.zone and change localhost. to the FQDN of your server, leaving the additional “.” at the end. Change 127.0.0.1 to the nameserver’s IP Address and root.localhost to a valid email address, but with a “.” instead of the usual “@” symbol, again leaving the “.” at the end.

sudo vi /var/lib/bind/local.example.zone

$ORIGIN .
$TTL 86400      ; 1 day
local.example           SOA  ns.local.example. root.local.example. (
                                2          ; serial
                                604800     ; refresh (1 week)
                                86400      ; retry (1 day)
                                2419200    ; expire (4 weeks)
                                86400      ; minimum (1 day)
                                )
                        NS      ns.local.example.
$ORIGIN local.example.
ns           A       1.2.3.4

You must increment the Serial Number every time you make changes to the zone file. If you make multiple changes before restarting BIND9, simply increment the Serial once. Now, you can add DNS records to the bottom of the zone file as required. Note: the use of the label “IN” refers to an “internet” record.

Once you have made a change to the zone file, BIND9 will need to be restarted for the changes to take effect.

sudo /etc/init.d/bind9 restart

It is also a good idea to add the IP address of this nameserver to its own resolv.conf so it can resolve against itself.

sudo vi /etc/resolv.confsearch local.example

nameserver 1.2.3.4

Try a forward lookup to see if it works.

dig host.local.example

Reverse Zone File

Now that the zone is setup and resolving names to IP Adresses a reverse zone is also required. A reverse zone allows DNS to resolve an address to a name.

sudo vi /etc/bind/named.conf.local

zone "3.2.1.in-addr.arpa" {
        type master;
        file "/var/lib/bind/rev.3.2.1.in-addr.arpa";
        allow-update { key "rndc-key"; };
        allow-transfer {1.2.3/24;};
};

Replace 3.2.1 with the first three octets of whatever network you are using. Also, name the zone file /var/lib/bind/rev.3.2.1.in-addr.arpa appropriately. In this example it matches the first three octets of your network.

Now create the /var/lib/bind/rev.3.2.1.in-addr.arpa file.

sudo cp /etc/bind/db.127 /var/lib/bind/rev.3.2.1.in-addr.arpa

Next edit /var/lib/bind/rev.3.2.1.in-addr.arpa basically changing the same options as /var/lib/bind/local.example.zone.

sudo vi /var/lib/bind/rev.3.2.1.in-addr.arpa

$ORIGIN .
$TTL 86400      ; 1 day
3.2.1.in-addr.arpa           SOA  ns.local.example. root.local.example. (
                                2          ; serial
                                604800     ; refresh (1 week)
                                86400      ; retry (1 day)
                                2419200    ; expire (4 weeks)
                                86400      ; minimum (1 day)
                                )
                        NS      ns.local.example.
$ORIGIN 3.2.1.in-addr.arpa.
1           PTR       ns.local.example.
2           PTR       another.local.example.

IMPORTANT:

REMEMBER TO INCLUDE THE TRAILING PERIOD (“.”) at the end of each hostname.

You must increment the serial number every time you make changes to the zone file. If you make multiple changes before restarting BIND9, simply increment the Serial once. For each A record you configure in /var/lib/bind/local.example.zone you need to create a PTR record in /var/lib/bind/rev.3.2.1.in-addr.arpa.

Once you have made a change to the zone file, BIND9 will need to be restarted for the changes to take effect.

sudo /etc/init.d/bind9 restart

Important note on manually changing zone files

We have created a small headache for ourselves now – or rather we will once we enable the dynamic updates to the zone files.

All changes made to a zone using dynamic updates are stored in the zone’s journal file. This file is automatically created by the server when the first dynamic update takes place. The name of the journal file is formed by appending the extension .jnl to the name of the corresponding zone file. It’s worthwhile mentioning the journal file is in binary format and should not be edited manually.

The server will also occasionally write (“dump”) the complete contents of the updated zone to its zone file. This is not done immediately after each dynamic update, because that would be too slow when a large zone is updated frequently. Instead, the dump is delayed by up to 15 minutes, allowing additional updates to take place.

When a server is restarted after a shutdown or crash, it will replay the journal file to incorporate into the zone any updates that took place after the last zone dump.

Changes that result from incoming incremental zone transfers are also journalled in a similar way.

The zone files of dynamic zones cannot normally be edited by hand because they are not guaranteed to contain the most recent dynamic changes – those are only in the journal file.

Furthermore, editing by hand and altering the serial number may result in a mismatch with the journal file and prevent the BIND9 process from starting.

The only way to ensure that the zone file of a dynamic zone is up to date is to run:

rndc stop

If you have to make changes to a dynamic zone manually, disable dynamic updates to the zone using:

rndc freeze zone

This will also remove the zone’s .jnl file and update the master file.

Edit the zone file, then run

rndc unfreeze zone

to reload the changed zone and re-enable dynamic updates. For consistancy, it’s probably a good idea to reload the BIND9 process.

sudo /etc/init.d/bind9 restart

Secondary Master

Once a Primary Master has been configured a Secondary Master is needed in order to maintain the availability of the domain should the Primary become unavailable.

Install the BIND9 package the same way as on the Primary. Then edit the /etc/bind/named.conf.local and add the following declarations for the Forward and Reverse zones.

sudo vi etc/bind/named.conf.local

include "/etc/bind/rndc.key";
zone "local.example" {
        type slave;
        file "/var/lib/bind/local.example.zone";
        masters {1.2.3.4; };
};
zone "3.2.1.in-addr.arpa" {
        type slave;
        file "/var/lib/bind/rev.3.2.1.in-addr.arpa";
        masters {1.2.3.4; };
};

I also add the forwarders to the Secondary so that in the event the Primary does go down it can resolve internet addresses as well!

sudo vi named.conf.options

forwarders {
        9.8.7.6;
        5.4.3.2;
};
auth-nxdomain no;    # conform to RFC1035
listen-on-v6 { none; };
listen-on { 1.2.3.5; };    # this is the address of the secondary

Restart BIND9 on the Secondary Master…

sudo /etc/init.d/bind9 restart

and in /var/log/syslog you should see something similar to this

slave zone "local.example" (IN) loaded (serial 6)
slave zone "3.2.1.in-addr.arpa" (IN) loaded (serial 3)

NOTE:

A zone is only transferred if the serial number on the Primary is larger than the one on the Secondary.

DHCP server

Overview

As you’re probably already aware, the Dynamic Host Configuration Protocol (DHCP) is a network service that enables host computers to be automatically assigned settings from a server as opposed to manually configuring each network host. Computers configured to be DHCP clients have no control over the settings they receive from the DHCP server, and the configuration is transparent to the computer’s user.

The most common settings provided by a DHCP server to DHCP clients include:

  • IP-Address and Netmask
  • DNS
  • WINS

However, a DHCP server can also supply configuration properties such as:

  • Host Name
  • Domain Name
  • Default Gateway
  • Time Server

The advantage of using DHCP is that changes to the network, for example a change in the address of the DNS server, need only be changed at the DHCP server, and all network hosts will be reconfigured the next time their DHCP clients poll the DHCP server. As an added advantage, it is also easier to integrate new computers into the network, as there is no need to check for the availability of an IP address. Conflicts in IP address allocation are also reduced.

In this section we will configure the DHCP3 server with some basic options, and also to dynamically update the DNS server (refered to as DDNS) with each lease it issues.

Installation

If you haven’t done it already, at a terminal prompt, enter the following command to install dhcpd:

sudo aptitude install dhcp3-server

You will probably need to change the default configuration by editing /etc/dhcp3/dhcpd.conf to suit your needs and particular configuration.

You also need to edit /etc/default/dhcp3-server to specify the interfaces dhcpd should listen to. By default it listens to eth0.

Configuration

First we need to get the rndc.key file from the /etc/bind directory so that it can be used for authenticating the updates.

sudo cp /etc/bind/rndc.key /etc/dhcp3/
sudo chown root:dhcpd rndc.key
sudo vi /etc/dhcp3/dhcpd.conf

Turn on DDNS and tell the DHCP3 server to use the rndc.key file…

ddns-update-style interim;
ddns-updates on;
ddns-domainname "local.example";
ddns-rev-domainname "in-addr.arpa";
include "/etc/dhcp3/rndc.key";

Then, add the forward and reverse zone information.

WARNING:

Take care to include the trailing period in the “zone” directive below.

zone local.example. {
        primary 1.2.3.4;
        key rndc-key;
}
zone 3.2.1.in-addr.arpa. {
        primary 1.2.3.4;
        key rndc-key;
}

Now, tell the clients that this DNS server is the authority for this domain…

authoritative;

and lastly, define some DHCP options for the client to use.

subnet 1.2.3.0 netmask 255.255.255.0 {
        range 1.2.3.30 1.2.3.50;
        option subnet-mask 255.255.255.0;
        option broadcast-address 255.255.255.254;
        option domain-name "local.example";
        option domain-name-servers 1.2.3.4, 1.2.3.5;
        option netbios-name-servers 1.2.3.1; # this is your WINS server if you have one
        one-lease-per-client on;
        default-lease-time 43200; 
        max-lease-time 43200;
        option routers 1.2.3.254;
}

When you’re happy with the settings that will be issued to your clients, restart both the BIND9 and DHCP3 processes.

sudo /etc/init.d/bind9 restart
sudo /etc/init.d/dhcp3-server restart

A last word on Windows settings?

I found that if the Windows client was configured to try and update the DNS by itself, it would cause an error in the /var/log/syslog by the named process.

What’s worst, the DNS update would fail and I was unable to resolve the machine.

Windows advanced TCPIP settingsEasy fix:

  1. Open the “Local Area Connection Properties” for the NIC wanting the lease.
  2. Locate the “Internet Protocol (TCP/IP)” or “Internet Protocol 4 (TCP/IPv4)” in Vista/Win7.
  3. Click the “Advanced…” button.
  4. Click the “DNS” tab.
  5. Make sure that the sections highlighted opposite are configured with the same settings.

The most important setting (of course) being the last one, which tells the machine to try and register its IP address in the DNS server servicing it (or not as this case demonstrates).