Hack and /

Your Own Personal Server: DNS

Kyle Rankin

Issue #204, April 2011

Why let registrars have all the fun? Learn how to set up your own DNS server completely under your own control.

In this day and age, it's simple and popular to have someone else change your oil, grow your vegetables, remodel your house and host your services. However, I'd argue that it's far more rewarding, educational and not very difficult to manage these things yourself. This column is the second in a series about how to manage your own services on your own server. In the first column, I discussed how to make sure your home network is ready to host your own services. In this column, I start to get into the meat of the topic and discuss the first service you can (and should) set up at home: DNS.

A Short Primer on DNS

DNS (Domain Name System) is a system you use every day and one on which the Internet heavily depends. Every server (including your own) that has a presence on the Internet should have a public IP address. Since last month's column, you should have your home network set up for your server and have at least one public IP (hopefully static) you can use. It's true that all you really need to host many services on the Internet is an IP address; however, in practice, there are only so many IP addresses (like phone numbers) that the average person is going to commit to memory. As IPv6 becomes commonplace, this will be even more true. DNS allows you to register a domain name and associate individual host names (like www.example.com and mail.example.com) to IP addresses.

For instance, how many of you (besides you, Katherine) have www.linuxjournal.com's IP address memorized? If you did want to know the IP address, all you would need to do is perform a simple nslookup command:

$ nslookup www.linuxjournal.com
Server:		192.168.0.1
Address:	192.168.0.1#53

Non-authoritative answer:
Name:	www.linuxjournal.com
Address: 76.74.252.198

In this example, the first bit of output tells me that I'm getting this answer from a DNS server at 192.168.0.1 (my own personal DNS server) and that the IP address for www.linuxjournal.com is currently 76.75.252.198. There isn't enough space in this column to describe everything that happened to allow me to get that IP address, but essentially, my DNS server asked other DNS servers on the Internet for this IP address and was subsequently redirected to more and more DNS servers until it finally found the one that knew the answer. If you are interested in more detail on how this works, books like DNS and BIND do a good job of explaining it, or from the command line, you could run dig www.linuxjournal.com +trace.

Your Own DNS Server

DNS seems like a complicated service, yet it's relatively simple to set up a DNS server of your own. Now, there are a number of different DNS server software from which to choose that are easier to configure or that have fancy database back ends, but for this article, I'm going to choose the old standby, BIND. Although it's not as simple as other DNS servers, it isn't so bad, once you get the hang of it.

BIND should be packaged for most major distributions; however, there are slight differences in how each distribution packages BIND. For instance, under Red Hat, you install the bind package, but under Debian-based systems (like Ubuntu), you install bind9. Red Hat stores its core BIND configuration file at /etc/named.conf and all its zone files (files that contain name→IP address mappings for a domain, such as example.org, a subdomain, such as ny.example.org, or possibly both) under /var/named, while Debian-based systems put named.conf and any zone files under the /etc/bind/ directory. Even the init script is different on both systems: Red Hat uses /etc/init.d/named, and Debian-based systems use /etc/init.d/bind9. Once you get past the differences, however, the syntax inside the files should be similar. Just to simplify things, I'm going to base the rest of this article off a standard Ubuntu 10.04 LTS server, so we have some sort of baseline. If you use a different distribution, however, it shouldn't be too difficult to adapt these instructions to the different file paths.

Once BIND is installed on the system, the package should create a basic named.conf file and all of the base directories. In the case of this sample Ubuntu system, the default named.conf actually will be set up to act as a caching name server. So, out of the box you should be able to point to this server with other hosts on your network, and it will be able to resolve other domains on the Internet just like with your ISP's DNS server. In this case, though, we want to create a DNS master.

Master DNS Configuration

A DNS master contains its own zone files that have name→IP address mappings, and it doesn't have to consult any other source to answer queries for those names. By contrast, a DNS slave is configured to load all of its zone configurations from a DNS master. Any future changes are made on the master and propagate to each of the slaves. Any individual BIND instance acts as a DNS master, a DNS slave or a caching name server, or all three at the same time (although it can be a master or a slave only to any individual zone, not both).

For this example, let's set up a DNS master for example.org, and this master will have the following records:

  • ns1.example.org, which points to 123.12.34.56 (the public IP of the name server itself).

  • example.org, which points to 123.12.34.57.

  • www.example.org also points to 123.12.34.57.

To start, I create the zone file at /etc/bind/db.example.org (remember Red Hat stores these zones in a different places) and put the following information in it:

;
; BIND data file for example.org
;
$TTL 4h
@  IN  SOA ns1.example.org. root.example.org. (
	    2		; Serial
	    604800 	; Refresh
	    86400 	; Retry
	    2419200 	; Expire
	    604800 ) 	; Negative Cache TTL
;
@	IN NS	ns1.example.org.
@ 	IN A	123.12.34.57
www	IN A	123.12.34.57
ns1 	IN A 	123.12.34.56

Make sure this file has similar permissions to the other zone files you find in the /etc/bind directory. The first non-comment line in the file sets the TTL or Time To Live, the default time in which a remote DNS server will cache any answers it gets from your DNS server before it will ask it again. The value you put here will help determine how fast changes you make will propagate. BIND accepts seconds in this field, or you can use shorthand values like 1d for one day, 4h for four hours or 20m for 20 minutes. I set the TTL to four hours here; however, if you make frequent changes to your records (or know you are going to soon), you may want to make the TTL shorter. On the other hand, if you find you hardly ever change these values, you might want to bump up the TTL to a day to reduce load on your DNS server.

Something to note is that zone files use semicolons not hashes at the beginning of a line for comments. A common mistake is to put hashes in a zone file to make a comment, reload BIND and then wonder why your changes didn't take. When BIND sees a mistake like that, it just skips that particular zone.

To keep things simple, I'm going to skip the Retry, Refresh and other values here—just keep them with these defaults unless you know what you are doing. The Serial line is for DNS slaves, which I discuss later. Below those values, however, you'll see the syntax I used to define the different records:

@	IN NS	ns1.example.org.
@ 	IN A	123.12.34.57
www	IN A	123.12.34.57
ns1 	IN A 	123.12.34.56

The first record starts with @, which means it is a record for example.org itself. In this case, it is an NS record that defines the hostname I'm going to use for my name server. You can use any hostname you control here (including hostnames on a different domain, actually), but one popular convention is to use hostnames like ns1 and ns2 for the first and second name servers. The second record begins with an @ as well, only in this case, it's an A record. An A record is a fundamental DNS record that maps a hostname (like www) to an IP address (like 123.12.34.57). In this case, because the record starts with @, I am setting the IP address for example.org itself. The next two lines define two more A records, one for www.example.org and one for ns1.example.org. It's important if you used a name within this same domain for your name server (like ns1.example.org) that you be sure to add an A record so that it has an IP address.

Now that I have created my zone, next I need to modify the /etc/bind/named.conf file and add a new section at the end of the file to point to the /etc/bind/db.example.org file I just created:

zone "example.org" {
	type master;
	file "/etc/bind/db.example.org";
};

After the file is changed, I reload BIND, and I should be able to send DNS requests to my new DNS server:

$ sudo /etc/init.d/bind9 reload
* Reloading domain name server... bind [OK]

$ nslookup www.example.org localhost
Server:		localhost
Address:	127.0.0.1#53

Name:	www.example.org
Address: 123.12.34.56

If there is a problem with the BIND reload, it should tell you on the command line. Otherwise, if it still doesn't work, you may have to look in your syslog file (/var/log/syslog on Debian-based systems and /var/log/messages on Red Hat) for clues.

Slave DNS Configuration

Many registrars on the Internet require that any domain you register have at least two DNS servers configured with it. It's a good practice to have, because if you have a single DNS server and it goes down, it effectively will make all your servers under that domain inaccessible. This means you need to set up a second DNS server on a different IP, ideally on a different network, or have a friend with a DNS server act as a slave to your master DNS server. In either case, it's a relatively simple process. Let's say that my second DNS server is going to be at the IP address 98.76.54.32. First, I would log in to my Master DNS server and add the new NS and A records to my zone file:

;
; BIND data file for example.org
;
$TTL 4h
@  IN  SOA ns1.example.org. root.example.org. (
	    2		; Serial
	    604800 	; Refresh
	    86400 	; Retry
	    2419200 	; Expire
	    604800 ) 	; Negative Cache TTL
;
@	IN NS	ns1.example.org.
@	IN NS	ns2.example.org.
@ 	IN A	123.12.34.57
www	IN A	123.12.34.57
ns1 	IN A 	123.12.34.56
ns2 	IN A 	98.76.54.32

Next, I edit named.conf and add a line to the configuration of example.org so that it will allow zone transfers from my DNS slave:

zone "example.org" {
	type master;
	file "/etc/bind/db.example.org";
	allow-transfer { 98.76.54.32; };
};

Finally, I would install BIND on the second server, or if it already exists, all I would have to do is add a new entry at the end of the named.conf file to define the example.org zone and tell this server the IP address of the master:

zone "example.org" {
	type slave;
	file "/var/cache/bind/db.example.org";
	masters { 123.12.34.56; };
};

Note that in this case the slave zone is being stored under /var/cache/bind. That's the default location for slave zone files under Debian-based systems. Under Red Hat, you would store them under /var/named/. Once I reload BIND on the slave server, it will pull the new zone information from the master, and I should be able to perform DNS queries against it.

Once you have set up a slave, keep in mind that anytime you make a change to the master, you will need to increment the Serial field in the Master's zone file (in my example, it is set to 2, but a lot of administrators like to set it to the current date plus two extra number fields, such as 2010120500). When the slave needs to know whether its zone information is up to date, it compares its serial number with the serial number on the master. If the master's serial number for a zone is higher, it copies down the new zone information; otherwise, it sticks with what it has cached.

Domain Registration

Once you have a functioning DNS server, all that's left is to tell the world to use it. If you haven't already registered your domain with a registrar, find a domain registration service on the Internet (there are too many for me to list here, but a search for domain name registration should turn up plenty). When you register the domain, most registrars will let you use their own DNS servers for your domain, but you don't need them! When you get to the point in the registration process where it asks you about your DNS servers, just give them the public IP address for your own DNS server (in my case, it would be ns1.example.org or 123.12.34.56). Note that many registrars require you to have two DNS servers defined for a domain, so in that case, set up a slave DNS server and add its IP address as well. Once you complete the registration process and allow the new domain information time to propagate around the Internet, you will have the ability make IP changes for your Web, mail and other servers all from your own machines.

Kyle Rankin is a Systems Architect in the San Francisco Bay Area and the author of a number of books, including The Official Ubuntu Server Book, Knoppix Hacks and Ubuntu Hacks. He is currently the president of the North Bay Linux Users' Group.