Enterprise Installation of DNS and BIND

General Information

DNS is at the very core of the Internet infrastructure and stands for Domain Name Server. DNS is a distributed, hierarchical database without which we would be typing IP addresses in our browsers instead of something like www.example.com. BIND can serve 1000s of host names and probably 1000s of zones. This how-to is aimed at that kind of installation. I will describe installing, configuring and maintaining BIND on a fresh FreeBSD installation in a secure chroot jail. I will also show how to set up zone files for one zone, multiple zones and a slave for other zones. I will also describe how to maintain your zones in a way that is easy. I will also include some tips. I will not cover caching or forwarding name servers. This document is meant to get you started — it is not meant to be comprehensive.

Requirements

If you know you are going to serve 100s of host names and 100s of zones and be a slave for many zones it is always best to install as much memory as you can purchase. At this level of using DNS you should ALWAYS build another server to use as your secondary. Also, you should make arrangements to have an off-network DNS arrangement with someone that acts as slave to your zones. If your DNS goes down you want some redundancy. You should always make arrangements to backup your configuration files and your zone data files.

I suggest that you do a fresh install of FreeBSD 6+. Then become the root user. For server installations I rarely install X so this installation is done from the command line. Let’s get started.

Change directories to /usr

# cd /usr

If you are connected to a network then you should next download the most recent ports collection from: ftp://ftp.freebsd.org/pub/FreeBSD/ports/ports-current/ports.tar.gz.

# ftp ftp://ftp.freebsd.org/pub/FreeBSD/ports/ports-current/ports.tar.gz

Extract the ports.tar.gz file that you downloaded:

# tar zxvf ports.tar.gz

After the file is extracted you can remove ports.tar.gz:

# rm ports.tar.gz

You are now ready to begin installing BIND.

Installation

Change directory to /usr/ports/dns/bind9

# cd /usr/ports/dns/bind9

Then type the following:

# make PORT_REPLACES_BASE_BIND9=yes all install clean

You will then see the following screen:

+--------------------------------------------------------------------+
¦                     Options for bind9 9.3.4                        ¦
¦ +----------------------------------------------------------------+ ¦
¦ ¦[X] REPLACE_BASE  Replace base BIND with this version           ¦ ¦
¦ ¦[ ] THREADS       Compile with thread support (NOT RECOMMENDED!)¦ ¦
¦ ¦                                                                ¦ ¦
¦ ¦                                                                ¦ ¦
¦ ¦                                                                ¦ ¦
¦ ¦                                                                ¦ ¦
¦ ¦                                                                ¦ ¦
¦ ¦                                                                ¦ ¦
¦ ¦                                                                ¦ ¦
¦ ¦                                                                ¦ ¦
¦ ¦                                                                ¦ ¦
¦ ¦                                                                ¦ ¦
¦ ¦                                                                ¦ ¦
¦ ¦                                                                ¦ ¦
¦ ¦                                                                ¦ ¦
+-+----------------------------------------------------------------+-¦
¦                       [  OK  ]       Cancel                        ¦
+--------------------------------------------------------------------+

Press the space bar and check REPLACE_BASE. There is no need to check THREADS. Tab to the OK button and press Enter.

This will start the download and installation of the most recent version of BIND 9. It will also download and install the necessary dependencies.

When it has finished you will see the following:

*************************************************************************
*           _  _____ _____ _____ _   _ _____ ___ ___  _   _             *
*          / \|_   _|_   _| ____| \ | |_   _|_ _/ _ \| \ | |            *
*         / _ \ | |   | | |  _| |  \| | | |  | | | | |  \| |            *
*        / ___ \| |   | | | |___| |\  | | |  | | |_| | |\  |            *
*       /_/   \_\_|   |_| |_____|_| \_| |_| |___\___/|_| \_|            *
*                                                                       *
*       BIND 9 requires a good source of randomness to operate.         *
*       It also requires configuration of rndc, including a             *
*       "secret" key.  If you are using FreeBSD 4.x, visit              *
*       http://people.freebsd.org/~dougb/randomness.html for            *
*       information on how to set up entropy gathering. Users           *
*       of FreeBSD 5.x or later do not need to do this step. If         *
*       you are running BIND 9 in a chroot environment, make            *
*       sure that there is a /dev/random device in the chroot.          *
*                                                                       *
*       The easiest, and most secure way to configure rndc is           *
*       to run 'rndc-confgen -a' which will generate the proper         *
*       conf file, with a new random key, and appropriate file          *
*       permissions.                                                    *
*                                                                       *
*************************************************************************
===>   Compressing manual pages for bind9-base-9.3.4
===>   Registering installation for bind9-base-9.3.4
===> SECURITY REPORT:
      This port has installed the following files, which may act as network
      servers and may therefore pose a remote security risk to the system.
/usr/sbin/rndc-confgen
/usr/sbin/named-checkconf
/usr/sbin/dnssec-keygen
/usr/sbin/rndc
/usr/sbin/lwresd
/usr/bin/nsupdate
/usr/bin/dig
/usr/sbin/named
/usr/bin/host
/usr/sbin/dnssec-signzone
/usr/bin/nslookup
/usr/sbin/named-checkzone

At this point you can remove the ports files if you like:

# cd /usr
# rm -Rf ports/*
# rm ports.tar.gz

At this point you do not have a usable name server. We next need to create chroot directories and jail BIND.

Create the chroot Environment

We need to now create a FreeBSD 6.x chrooted environment. Perform the following as root user.

# mkdir -p /var/chroot/named/etc/namedb/log
# mkdir -p /var/chroot/named/dev
# mkdir -p /var/chroot/named/var/run
# cd /var/chroot
# chown -R bind:bind named
# chmod 700 named
# cp /etc/localtime /var/chroot/named/etc
# cp /etc/namedb/named.root /var/chroot/named/etc/namedb/
# cd /var/chroot/named/dev
# mknod zero c 2 12
# ln -s /dev/random .
# mknod null c 2 2
# chmod 666 zero random null

What have we done here? Most of it is obvious; we created directories, set ownership, set modes and created links. The man page for mknod says it is deprecated but we use it here to build a couple of nodes. One is called zero and the other is called null. Finally we set the modes for these nodes in the last line. Each of the commands above are critical for creating the chroot environment.

Next we can remove or move the /etc/namedb directory and then create a symbolic link to the /etc directory.

# cd /etc
# mv namedb old.namedb
# ln -s /var/chroot/named/etc/namedb .

At this point we are ready to start working on getting the name server up and running.

Configuration

The first thing we want to do is create the rndc key. This is easy to do. While still in the /etc directory:

# rndc-confgen -a -c /etc/namedb/rndc.conf -k dnsadmin -b 256

When it completes you will see the following written out to the console.

wrote key file "/etc/namedb/rndc.conf"

The contents of the file looks like the following:

key "dnsadmin" {
        algorithm hmac-md5;
        secret "1fA/Awv2C/SBkF/pAUfjMDjzQdezXQUkHwxR3bQVI5w=";
};

Note: Don’t be lazy and copy the contents you see above. Actually, create the file with your own key.

Next we need to make some additions to the /etc/rc.conf. This will make certain that named will start when FreeBSD reboots.

named_enable="YES"
named_program=/"usr/local/sbin/named"
named_chrootdir="/var/chroot/named"
named_flags="-c /etc/namedb/named.conf"
named_pidfile="/var/run/named.pid"

Write and save the file when you finish.

We next build a named.conf file. We can start from scratch as there are certain entries you must have in named.conf. I always start with the options statement. Use your favorite editor.

options {
   directory "/etc/namedb";
   pid-file "/var/run/named.pid";
   allow-transfer { 123.123.123.123; };
   // Only allow trusted hosts to freely lookup any host
   allow-query { trusted; };
   recursion no;
};

Note: In the named.conf file a comment can be // . In your zone data files it is different. We will cover the zone data files shortly. The // in the named.conf file is the only comment token you can make and the comment continues to the end of the line.

You should never allow recursion for security and performance reasons on a public facing DNS server unless there is a reason to allow it. Also as a security precaution I would always set up an internal DNS that sits behind a firewall that does name resolution for your client computers on your LAN. This will help prevent cache poisoning. This link gives an excellent description of the problem. There are other measures you can take to secure your DNS.

For testing purposes you may allow transfers and queries from your internal network only. I don't advise it however.

Next, we can add the controls statement:

controls {
   inet 127.0.0.1 port 953 allow { 127.0.0.1; } keys { dnsadmin; };
};

Next add the contents of the rndc.conf file we created earlier:

key "dnsadmin" {
   algorithm hmac-md5;
   secret "551/aS/MXIbmWRZTb6nhUA==";
}

Next is an example of the logging statement you can use:

logging {
   channel systemlog {
      file "log/named.log";
      severity debug;
      print-time yes;
   };
   channel audit_log {
      file "log/security.log";
      severity debug;
      print-time yes;
   };
   channel xfer_log {
      file "log/xfer.log";
      severity debug;
      print-time yes;
   };
   category default { systemlog; };
   category security { audit_log; systemlog; };
   category config { systemlog; };
   category xfer-in { xfer_log; };
   category xfer-out { xfer_log; };
   category notify { audit_log; };
   category update { audit_log; };
   category queries { audit_log; };
   category lame-servers { audit_log; };
};

The above is rather straightforward. It tells named how you will log DNS events.

Next you must configure three things; a hints file, the localhost forward lookup file and the localhost reverse lookup or the in-addr.arpa file. We will cover these zone files and other zone files shortly. We will add them to the end of the named.conf file.

zone "." {
   type hint;
   file "named.root";
};
zone "localhost" {
   type master;
   file "db.localhost";
};
zone "1.0.0.127.in-addr.arpa" {
   type master;
   file "db.127.0.0";
};

It is important to note that any syntax errors you have will prevent named from starting up. Forgetting a ";" or a "{ }" or using a "#" instead of "//" generally will cause named to fail to start up. We will cover this later.

You next need to create the named.root file also known as the hints file. This is simple to do. Make sure you are in the same directory as named.conf (/etc/namedb).

# cd /etc/namedb

Then use dig with no arguments or parameters and press enter

# dig > named.root

You can cat the file and you will see something similar to the following;

# cat named.root
; <<>> DiG 9.3.4 <<>>
;; global options:  printcmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 43680
;; flags: qr rd ra; QUERY: 1, ANSWER: 13, AUTHORITY: 0, ADDITIONAL: 0

;; QUESTION SECTION:
;.                              IN      NS

;; ANSWER SECTION:
.                       517251  IN      NS      E.ROOT-SERVERS.NET.
.                       517251  IN      NS      F.ROOT-SERVERS.NET.
.                       517251  IN      NS      G.ROOT-SERVERS.NET.
.                       517251  IN      NS      H.ROOT-SERVERS.NET.
.                       517251  IN      NS      I.ROOT-SERVERS.NET.
.                       517251  IN      NS      J.ROOT-SERVERS.NET.
.                       517251  IN      NS      K.ROOT-SERVERS.NET.
.                       517251  IN      NS      L.ROOT-SERVERS.NET.
.                       517251  IN      NS      M.ROOT-SERVERS.NET.
.                       517251  IN      NS      A.ROOT-SERVERS.NET.
.                       517251  IN      NS      B.ROOT-SERVERS.NET.
.                       517251  IN      NS      C.ROOT-SERVERS.NET.
.                       517251  IN      NS      D.ROOT-SERVERS.NET.

;; Query time: 106 msec
;; SERVER: 208.67.222.222#53(208.67.222.222)
;; WHEN: Sat Jul 14 19:14:26 2007
;; MSG SIZE  rcvd: 228

Look at the ANSWER SECTION. What you are seeing are the root name servers that most if not all the Internet uses to find and resolve names on the Internet. These servers point down the DNS hierarchy and tells other name servers how to find zones. You can use DiG to look up these name servers if you like and if you do you will see how it resolves these names to an ip address.

Next we need to create a db.localhost forward zone and a db.127.0.0 reverse zone. You can give them any name so long as the named.conf file points to the correct filename. My convention for years is to begin the name of the file with db then the name of the zone for forward zones. For reverse zones I start again with db then the network number of the reverse zones or the first 3 octets of the network. We will create the two following files:

  1. db.localhost
  2. db.127.0.0
--db.localhost--
$TTL    604800
@       IN      SOA     localhost. root.localhost. (
                        2007070700     ; Serial
                        604800         ; Refresh
                         86400         ; Retry
                       2419200         ; Expire
                        604800 )       ; Negative Cache TTL
        IN      NS      localhost.
        IN      A       127.0.0.1
IN CNAME localdomain.

  --db.127.0.0--
$TTL    604800
@       IN      SOA     localhost. root.localhost. (
                         2007070700     ; Serial
                         604800         ; Refresh
                          86400         ; Retry
                        2419200         ; Expire
                         604800 )       ; Negative Cache TTL
        IN      NS      localhost.
1        IN      PTR     localhost.

Going into detail here on the meaning of the following terms is beyond the scope of this how-to. But here is a brief explanation of what you see.

$TTL - Time To Live, the number of seconds remaining on a cached record before it is purged. For authoritative records the TTL is fixed at a specific length. If a record is cached, the server providing the record will provide the time remaining on the TTL rather then the original length it was given.

@ - Is a BIND macro.

IN - short for Internet

SOA - Start Of Authority record

NS - Name Server record

A - Address record

PTR - Pointer record

MX - Mail Exchanger record

CNAME - canonical name or alias record which points to the canonical or original name of the device.

The important point here is the Serial. Any time you make a change in a zone file YOU MUST INCREMENT the serial by at least one. Failure to do so or chopping off part of the serial will cause your name server to not update all the other name server in the world. In other words, the change you have made will not be reflected on the Internet.

Also, any syntax error in zone data files will cause your zone to not load even though the named service will be started. One of these syntax errors is not including the "." after the hostname. For example look at localhost. We will continue to do this in the other zone data files.

Configuring A Zone You Own

To keep things orderly, you should create a separate zone data file for each zone. In order for this to work you must purchase a name from an Internet Registrar. A list of registrars can be found here: http://www.internic.net/alpha.html at Internic. If you skip this step it is OK but the Internet will not be able to query your zone until the name is registered. You can set up a DNS zone and test it locally before you purchase your name. I will use as my new name myexample.net.

You can use your localhost file as a template if you like and that is what I will do here. Again make sure you are in the /etc/namedb directory. My example shows a DNS with a private IP address inside a DMZ. The firewall will map a "static" public ip address to this address :

# cd /etc/namedb
# cp db.localhost db.myexample.com
# cp db.127.0.0 db.192.168.1

  --db.myexample.com--
$TTL    604800
@       IN      SOA     dns.myexample.com. root.localhost. (
                        2007070700     ; Serial
                        604800         ; Refresh
                         86400         ; Retry
                       2419200         ; Expire
                        604800 )       ; Negative Cache TTL
  ; list master and slave name servers below
  IN      NS      dns.myexample.com.
  IN      NS      dns.someother.com.
; My cable modem is internet facing
cablemodem       IN      A       123.456.777.1
; My firewall is ip'ed behind the the cablemodem which has a public IP address
firewall         IN      A       123.456.777.2
; My DNS server
dns              IN      A       123.456.777.3
; My Web server
www              IN      A       123.456.777.4
; My glue points to my domain
myexample.com.   IN      A       123.456.777.5
; mail server and aliases
mail             IN      A       123.456.777.6
mailhost         IN      CNAME   mail.myexample.com.
myexample.com.   IN      MX      10  mail.myexample.com.
myexample.com.   IN      MX      20  mail.someother.com.
smtp             IN      CNAME   mail.myexample.com.
pop3             IN      CNAME   mail.myexample.com.
imap             IN      CNAME   mail.myexample.com.

So on and so forth. Keep in mind that all these servers are in a DMZ and are using private IP addresses. You will have to change this data file from private IPs to public IPs and also map static IP addresses to their internal private IP addresses. If your DNS is a public name server serving your public IP address you have to have tcp/udp port 53 open on your firewall. Otherwise the Internet will not be able to look up your public names. You should also note that there is a FQDN (Fully Qualified Domain Name) that is in the form: host.domain.TLD or hostname.YourDomain.TopLevelDomain.

MAIL EXCHANGE RECORDS - If you have mail delivered to your domain for example juser@myexample.com, you will need MX records. To accomplish this you can map your domain name to mail.myexample.com with a high priority (low number).

REVERSE ZONE DATA FILES - The DNS RFCs state that every host on the Internet should have a reverse lookup. Even though it is a good idea this is not always followed. You should have reverse lookups for mail since many mail servers check for this.

Below is an example reverse lookup zone (in-addr.arpa zone) data file:

  --db.123.456.789--
$TTL    604800
@       IN      SOA     dns.myexample.com root.myexample.com. (
                         2007070700     ; Serial
                         604800         ; Refresh
                          86400         ; Retry
                        2419200         ; Expire
                         604800 )       ; Negative Cache TTL
        IN      NS      dns.myexample.com.
        IN      NS      dns.someother.org.

1               IN      PTR     cablemodem.myexample.com.
2               IN      PTR     firewall.myexample.com.
3               IN      PTR     dns.myexample.com.
4               IN      PTR     www.myexample.com.
6               IN      PTR     mail.myexample.com.
5               IN      PTR     myexample.com.

Now you need to add these to the bottom of your named.conf and you are then ready to start named. Make the following entries in your named.conf file so named can know they are there and can find them. Below is the completed named.conf file.

# cd /etc/namedb

With your favorite editor edit named.conf:

options {
   directory "/etc/namedb";
   pid-file "/var/run/named.pid";
   allow-transfer { 123.123.123.123; };
   // Only allow trusted hosts to freely lookup any host
   allow-query { trusted; };
   recursion no;
};
controls {
   inet 127.0.0.1 port 953 allow { 127.0.0.1; } keys { dnsadmin; };
};
key "dnsadmin" {
   algorithm hmac-md5;
   secret "551/aS/MXIbmWRZTb6nhUA==";
};
logging {
   channel systemlog {
      file "log/named.log";
      severity debug;
      print-time yes;
   };
   channel audit_log {
      file "log/security.log";
      severity debug;
      print-time yes;
   };
   channel xfer_log {
      file "log/xfer.log";
      severity debug;
      print-time yes;
   };
   category default { systemlog; };
   category security { audit_log; systemlog; };
   category config { systemlog; };
   category xfer-in { xfer_log; };
   category xfer-out { xfer_log; };
   category notify { audit_log; };
   category update { audit_log; };
   category queries { audit_log; };
   category lame-servers { audit_log; };
};

zone "." {
   type hint;
   file "named.root";
};
zone "localhost" {
   type master;
   file "db.localhost";
};
zone "1.0.0.127.in-addr.arpa" {
   type master;
   file "db.127.0.0";
};
// ZONE myexample.com
zone "myexample.com" {
   type master;
   file "db.myexample.com";
};
// ZONE reverse zone for myexample.com
zone "789,456.123.in-addr.arpa" {
  type master;
  file "db.123.456.789";
};

Make note of the reverse zone. The octets are reversed for that zone. Complete explanation of reverse zones and everything else I have covered here are in the definitive "Cricket" book, DNS and BIND 4th edition.

Configuring Zones For Multiple Domains

Suppose someone realizes how good you are at DNS and they don't want to mess with it and asks you if you would host their zone. How is this done? They are on a separate network, have a different domain and don't know how to run their own DNS. It is simple you can do this the same way every time. Here are the steps.

  1. Register the name with an Internet Registrar.
  2. Re-configure named.conf and add forward and reverse zones for the new domain.
  3. Create the forward and reverse zone data files.
  4. Include the necessary glue and MX records.
  5. Restart named.

Here's an example. We will host a new zone someother.org:

# cd /etc/namedb

Edit the named.conf file with your favorite editor add the following lines:

zone "someother.org" {
   type master;
   file "db.someother.org";
};
zone "543.211.999.in-addr.arpa" {
   type master;
   file "db.999.211.543";
};

Next you must create the files your are pointing to:

  1. Edit db.someother.org as shown above for myexample.com
  2. Edit db.999.211.543 as shown above for myexample.com

Finally, restart named and check your work by doing an nslookup or using DiG to lookup the new name.

That is all there is to it.

Configuring named To Be A Slave To Other Zones

This is rather simple to do but requires cooperation between the DNS servers. The master server for the zone must allow transfers from the slave server. If you find this does not work when you first try it make sure the master server located somewhere else is allowing transfers from your slave server. Again you will need to edit the named.conf file and add the following lines at the bottom of the file.

// Forward zone
zone "myotherzone.net" {
   type slave;
   masters { 222.333.444.2; };
   file "db.myotherzone.net";
};

// Reverse zone
zone "444.333.222.in-addr.arpa" {
   type slave;
   masters { 222.333.444.2; };
   file "db.444.333.222.in-addr.arpa";
};

Save the named.conf file and restart named and you are done. You can check when the name server has restarted by the presence of the files.

Running an authoritative DNS server is not difficult but you must take care in your configuration and zone data files.

Speak Your Mind

*