DNS DHCP server

From OptionC

Table of contents

Introduction

The purpose of this document is to list how to make a DNS and DHCP server. These can be combined in a single machine or split. As with the other documents there is little or no reference to Xen, emphasising that the Xen virtual machine is, effectively, simply another Linux machine.

Base Installation

Start with a small base system, such as the one detailed here. For both the DNS and the DHCP server you should probably not use DHCP to get your network configuration (IP address, nameserver, etc), but assign them statically.

DNS

The type of DNS configuration depends upon the needs of the network. I wanted to set up a small LAN DNS, serving a small office. I needed to have a non-official TLD with a couple of internal mail servers and a web server. The following are the steps I took.

Install BIND (will also install dependencies):

   # apt-get install bind9 dnsutils

Configuration:
We want to create our own domain. In order to not upset anyone out in the "real world", we'll use a completely made-up domain name: ournet.blah (as of now, Internet Corporation for Assigned Names and Numbers (ICANN) has not yet approved ".blah" as top level domain, although, considering the amount of pretentious nonsense available on the Interet, I'm sure it's only a matter of time).

First, if your machine is configured via DHCP then it will have the wrong nameserver defined -- since you're attempting to define the local machine as a nameserver, anything else that's defined is, by definition, wrong. If your machine has not been configured via DHCP (better for a DNS server), you'll still need to perform the following steps to ensure that searching for the "ournet.blah" machines occurs in the right place.

Edit /etc/resolv.conf:

   search ournet.blah
   nameserver 127.0.0.1

To prevent resolv.conf from being reset by DHCP, type the following command:

   # chattr +i /etc/resolv.conf

(If you choose, at a later time you can remove this attribute by using -i instead of +i)

In /etc/bind/named.conf.local, add the following:

   zone "ournet.blah" {
       type master;
       notify no;
       file "/etc/bind/ournet.blah";
   };

Create the /etc/bind/ournet.blah zone file, similar to the following:

   ;
   ; Zone file for ournet.blah
   ;
   ; The full zone file
   ;
   $TTL 3D
   @       IN      SOA     ns.ournet.blah. hostmaster.ournet.blah. (
                           200506241       ; serial # (date + #)
                           8H              ; refresh time
                           2H              ; retry time
                           4W              ; expires in...
                           1D )            ; minimum
   ;
                   NS      ns              ; Address of name server
                   MX      10 mail.ournet.blah.    ; Primary Mail Exchanger
                   MX      20 mail2.ournet.blah.   ; Secondary Mail Exchanger
   ;
   localhost       A       127.0.0.1
   gw              A       192.168.64.1
                   TXT     "The gateway to the world"
   ns              A       192.168.64.2
                   MX      10 mail
                   MX      20 mail2
   mail            A       192.168.64.10
   mail2           A       192.168.64.11
   www             A       192.168.64.20
   ftp             CNAME   www

This data defines the following:

  • SOA (Start Of Authority) record
  • NS (Nameserver) record, giving the address of the name server
  • MX (Mail eXchange) records, for a primary and secondary mail server

Finally, some specific A (address) records are set up. Note that ftp.ournet.blah is an alias to www.ournet.blah, using a CNAME (Canonical NAME) record.

To test what we've got so far, we need to reload the nameserver daemon.

   # rndc reload

We use dig to test:

   # dig ournet.blah axfr
   ; <<>> DiG 9.2.4 <<>> ournet.blah axfr
   ;; global options:  printcmd
   ournet.blah.            259200  IN      SOA     ns.ournet.blah. hostmaster.ournet.blah. 200506241 28800 7200 2419200 86400
   ournet.blah.            259200  IN      NS      ns.ournet.blah.
   ournet.blah.            259200  IN      MX      10 mail.ournet.blah.
   ournet.blah.            259200  IN      MX      20 mail2.ournet.blah.
   ftp.ournet.blah.        259200  IN      CNAME   www.ournet.blah.
   gw.ournet.blah.         259200  IN      A       192.168.64.1
   gw.ournet.blah.         259200  IN      TXT     "The gateway to the world"
   localhost.ournet.blah.  259200  IN      A       127.0.0.1
   mail.ournet.blah.       259200  IN      A       192.168.64.10
   mail2.ournet.blah.      259200  IN      A       192.168.64.11
   ns.ournet.blah.         259200  IN      A       192.168.64.2
   ns.ournet.blah.         259200  IN      MX      10 mail.ournet.blah.
   ns.ournet.blah.         259200  IN      MX      20 mail2.ournet.blah.
   www.ournet.blah.        259200  IN      A       192.168.64.20
   ournet.blah.            259200  IN      SOA     ns.ournet.blah. hostmaster.ournet.blah. 200506241 28800 7200 2419200 86400
   ;; Query time: 1 msec
   ;; SERVER: 127.0.0.1#53(127.0.0.1)
   ;; WHEN: Fri Jun 24 15:45:23 2005
   ;; XFR size: 15 records

Now we need the reverse zone so that programs can convert addresses to DNS names. Many services use reverse DNS to decide if you should have access.

Add the following to /etc/bind/named.conf.local:

   zone "64.168.192.in-addr.arpa" {
           type master;
           notify no;
           file "/etc/bind/192.168.64";
   };

Create the /etc/bind/192.168.64 file:

   ;
   ; Reverse Zone file for ournet.blah
   ;
   $TTL 3D
   @       IN      SOA     ns.ournet.blah. hostmaster.ournet.blah. (
                           200506241       ; serial # (date + #)
                           8H              ; refresh, seconds
                           2H              ; retry, seconds
                           4W              ; expire, seconds
                           1D )            ; minimum, seconds
   ;
                   NS      ns.ournet.blah.
   ;
   1               PTR     gw.ournet.blah.
   2               PTR     ns.ournet.blah.
   10              PTR     mail.ournet.blah.
   11              PTR     mail2.ournet.blah.
   20              PTR     www.ournet.blah.

Restart the name daemon, named, and use dig to check the results:

   # rndc reload
   # dig -x 192.168.64.20
   ; <<>> DiG 9.2.4 <<>> -x 192.168.64.20
   ;; global options:  printcmd
   ;; Got answer:
   ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 15849
   ;; flags: qr aa rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 1, ADDITIONAL: 1

   ;; QUESTION SECTION:
   ;20.64.168.192.in-addr.arpa.   IN      PTR

   ;; ANSWER SECTION:
   20.64.168.192.in-addr.arpa. 259200 IN  PTR     www.ournet.blah.64.168.192.in-addr.arpa.

   ;; AUTHORITY SECTION:
   64.168.192.in-addr.arpa. 259200 IN      NS      ns.ournet.blah.

   ;; ADDITIONAL SECTION:
   ns.ournet.blah.         259200  IN      A       192.168.64.2  

   ;; Query time: 0 msec
   ;; SERVER: 127.0.0.1#53(127.0.0.1)
   ;; WHEN: Fri Jun 24 15:55:09 2005
   ;; MSG SIZE  rcvd: 119

If you have a small network with fixed IP addresses then you now have a useful DNS system. Simply update the two files -- /etc/bind/ournet.blah and /etc/bind/192.168.64 -- with your machine names and IP addresses.

DHCP Server

The DNS server as it stands works well. Not being willing to leave well enough alone, I decided to make it into a DHCP server as well.

First, install the daemon, dhcpd:

   # apt-get install dhcpd

If you're installing DHCP for the first time then you'll probably get some warning messages about needing to create/edit the configuration. If you simply want to assign IP addresses at random, then use something like the following:

   # option definitions common to all supported networks...
   option domain-name "ournet.blah";
   option domain-name-servers ns.ournet.blah;

   option subnet-mask 255.255.255.0;
   default-lease-time 3600;
   max-lease-time 7200;

   subnet 192.168.64.0 netmask 255.255.255.0 {
       range 192.168.64.150 192.168.64.200;
   }

To restart the daemon,

   # /etc/init.d/dhcp restart

If you want to assign static IP addresses to specific machines (for example, servers) then you need to use slightly more complicated configuration file:

   option domain-name "ournet.blah";
   option domain-name-servers ns.ournet.blah;

   option subnet-mask 255.255.255.0;
   default-lease-time 3600;
   max-lease-time 7200;

   subnet 192.168.64.0 netmask 255.255.255.0 {
       range 192.168.64.150 192.168.64.200;
       host ns.ournet.blah { hardware ethernet 00:0C:D1:99:BB:01; fixed-address 192.168.64.2; }
       host mail.ournet.blah { hardware ethernet 00:0C:D1:99:BB:DD; fixed-address 192.168.64.10; }
       host mail2.ournet.blah { hardware ethernet 00:0C:D1:99:BB:EE; fixed-address 192.168.64.11; }
       host www.ournet.blah { hardware ethernet 00:0C:D1:99:BB:CC; fixed-address 192.168.64.20; }
   }

DNS + DHCP

Note: This is considered to be insecure. There are ways to make it more secure, but they are beyond the scope of this document. The best way to ensure security is to have each machine name hard-coded in the DNS configuration files, and have the MAC address hard-coded in the DHCP configuration file. This is tedious, but more secure.

Having a DHCP server that doesn't actually talk to the DNS server can be annoying. It's much more efficient to have the DHCP addresses auto register themselves in DNS. Plus it gives a chance to write more on this HOWTO.

I've chosen to use the Debian autodns-dhcp package, a lightweight solution for automatically registering DHCP addresses in the DNS database.

Installation:

   # apt-get install autodns-dhcp

Configuration: First you'll need to reconfigure DHCP. The autodns-dhcp scripts need to have a single IP per line. For example, above we had the range set in /etc/dhcpd.conf as

   subnet 192.168.64.0 netmask 255.255.255.0 {
       range 192.168.64.150 192.168.64.200;
   }

The scripts want the ranges as follows (sorted sequentially). Note: every single IP address in the range needs to be listed. There is a one-liner perl script for that, which comes with the package's documentations. Here's a quick one: perl -le 'for ( my $i = 150; $i <= 200; ++$i ) { print " range 192.168.64.$i;\n" }' Change it so it will suite your needs.

   subnet 192.168.64.0 netmask 255.255.255.0 {
       range 192.168.64.150;
       range 192.168.64.151;
       range 192.168.64.152;
       range 192.168.64.153;
       range 192.168.64.154;
       ...
       range 192.168.64.196;
       range 192.168.64.197;
       range 192.168.64.198;
       range 192.168.64.199;
       range 192.168.64.200;
   }

Restart DHCP:

   # /etc/init.d/dhcp restart

Next you'll need to reconfigure BIND (/etc/bind/named.conf.local). You must set BIND up to allow dynamic DNS. Again, better use a script for that: perl -le 'for ( my $i = 150; $i <= 200; ++$i ) { print " 192.168.64.$i;\n" }'

   acl "okay_hosts" {
       192.168.64.150;
       192.168.64.151;
       192.168.64.152;
       192.168.64.153;
       ...
       192.168.64.198;
       192.168.64.199;
       192.168.64.200;
   };

   zone "ournet.blah" {
       type master;
       notify no;
       file "/etc/bind/ournet.blah";
       allow-update { "okay_hosts"; };
   };

   zone "1.168.192.in-addr.arpa" {
       type master;
       notify no;
       file "/etc/bind/192.168.64";
       allow-update { "okay_hosts"; };
   };

Edit your /etc/bind/ournet.blah (you can use the above configuration with slight changes) and /etc/bind/192.168.64. You can use the program "named-checkconf" and "named-checkzone" to make sure you wrote a valid syntax. Don't forget to increment the serial number in every edit, and don't forget that full domain names (such as domain.com, domain.co.uk, etc...) ends with a DOT (".") in BIND (domain.com., domain.co.uk., etc...).

Reload DNS configurations:

   # rndc reload


Finally, you'll need to edit the /etc/autodns-dhcp.conf file. It should look like the following:

   $DDNSHOME="/var/lib/autodns-dhcp";
   $DHCPD="/var/lib/dhcp/dhcpd.leases";
   $DOMAIN="ournet.blah";
   $NSUPDATE="/usr/bin/nsupdate";

Additional:
There may be issues with the different versions of the software available. The autodns-dhcp package was created to run with BIND8; BIND9 is installed if the instructions have been followed. As such, you may need to make some modifications to the autodns-dhcp scripts which are to be found in:

  • /usr/sbin/ddns.cron.pl
  • /usr/sbin/ndc.cron.pl

Extra Stuff

It was suggested that I also install a tftp server. I didn't, but it would seem to be a trivial thing (in other words, it's not been tested). The following installs the HPA tftpd which has been derived from OpenBSD tftp with some extras if you wish to use the PXE protocol.

   # apt-get install tftpd-hpa

Recommended Reading

The following references were useful in creating this document: