Making DNS Smarter on OS X

So, if you're a web designer like me, configuration is one of the things that you dread. In spite of the fact that there are ample automation tools at our disposal, we typically end up doing a lot of things by hand. One of these things for Web Developers is adding Virtual Hosts on Apache, and adding entries into our /etc/hosts file so that the applications we're working on can have cool hostnames that actually reference our local computer. Like, "project1.dev" or one that I use "projectnamehere.farmmac.local". None of these steps really take that long, but it would be nice if we made them unnecessary, right? Of course it would. So, this is start of what will hopefully become a series on here of tips, tricks, and hacks that are designed to make the lives of the people who write software easier. In this first article, I'm going to go over a method of configuring the built in DNS server on Mac OS X to have it behave intelligently.

Essentially, we're going to set up a DNS server on our computer that will handle all requests to the "local" domain space. I'm going to be repeating a lot of the instructions from Geoff Hankerson's how-to blog post, with some details tweaked. However, it is worth noting that I came into this already understanding how DNS zones work, so I'm going to do a little bit more explaining just in case you don't.

Hopefully, this will not only get you to the point where you have a working "local" DNS zone to play in, but you'll walk away with the ability to read and understand a DNS zone file, and abstract what you read here to add other DNS zones, as you feel you need to. You can find the rest of the article after the jump.

Prerequisites

The rest of this article assumes a few things. First is that you have a basic understanding of how to work the command line on your computer. If I just crossed the line from understanding to not understanding, then you should probably turn around and go somewhere else. No hard feelings.

Additionally, this article is written from the point of view of a Mac user, and all commands were run on Mac OS X 10.6 Snow Leopard. The same principles will likely apply on any Unix/Linux system, but you'll likely have to do some legwork, software installing, etc. To the best of my knowledge, the only major piece you need installed is bind, but someone feel free to correct me if I'm wrong.

That said, let's get rolling!

Getting Out of the Starting Gate with named.conf

The first step I had to perform was to generate the /etc/rndc.key file. You can do this at any point before you need to check your configuration file, but it makes sense to me to do it first, since it's basically prep work for bind configuration. It's actually really straightforward. It's actually just one command:

$ sudo rndc-confgen -a

After that you'll have a working rndc.key file and bind will be happy. So, the next step is to actually add our new zone to the named.conf file that tells bind where to look for name information. Of course, the first thing to do is open /etc/named.conf in the text editor of your choice.

Essentially, this file consists of a list of "zones" that tell the nameserver where to look based on the ending of the domain you enter into your browser. Real, "big boy" nameservers will have zone entries for "com", "net", "org", and whatnot. Of course, their configurations are a lot more complex then what we're doing here. Essentially, we're going to define a new zone called "local". Then, anytime we type a domain name ending in ".local" (say, "awesome.local") that zone will be used to attempt to look up the computer to connect to.

So, immediately below the localhost zone definition we want to add our new zone. The localhost definition starts on the line that reads "zone "localhost" IN {" and ends on the line containing "};". So, start a new line after that semicolon, and plug in the following:

zone "local" IN {
	type master;
	file "local.zone";
};

Essentially we're just saying that we're declaring the new local zone, it's a master zone, and the data for the zone can be found in a file named local.zone. Now, these file names all refer to files under /var/named - so the full path will look something like /var/named/local.zone (and you'll need to create that file as root using sudo).

That file will look something like this:

local. 7200    IN       SOA     local. root.local. (
              2008031801     ; Serial
              15             ; Refresh every 15 minutes
               3600          ; Retry every hour
               3000000       ; Expire after a month+
               86400 )       ; Minimum ttl of 1 day
                IN      NS      local.
                IN      MX      10 local.

                IN      A       127.0.0.1
*.local.        IN      A       127.0.0.1

Unfortunately, all my tabs got screwed up, so the columns aren't exactly aligned. But you get the general idea. We're approaching the territory where my understanding of the DNS system isn't perfect, so if I smudge a few details then I apologize. The general gist is that if you wanted to rename the zone to something other than local, just replace every instance of the word "local" with whatever you want.

The first record, which stretches the first 6 lines because of the comments, sets some of the basic parameters of the zone. If you were configuring a large scale DNS server, you'd want to make sure you set these parameters correctly. However, for our purposes, these default values are fine.

The seventh line indicates that the nameserver for the hostname local is located at "local". It seems like we're stating the obvious to me too, but it's not important. Just roll with it. The eighth line is a MX record, which stands for "Mail Exchange". This tells email servers what computer to deliver email to for email addressed to "local" domains. The "10" that is in the third field is the priority of the mail record. In real world situations, one domain will have multiple "MX" records of different priority. So, if one goes down, email can still be delivered. Since we're not worried about mail, we'll just use the default here as well.

That brings us to the last two lines, which are the A records. These tell web browsers and other software what IP address to look to when they're trying to resolve domains that end in "local". The first line is a blank A record which tells the nameserver that if I were to type just "local" into my browser, it should point to my local machine (which is always 127.0.0.1). That's not really novel. The last line is where our power comes in.

The last line is a wildcard record that tells the nameserver to point all subdomains of local to 127.0.0.1. That means I can use "thing1.local", "thing2.local", "dr.seuss.local" - all without any additional configuration.

At this point it's time to save our new zone file and check our configuration. You can do that by running these two commands:

$ sudo named-checkconf /etc/named.conf
$ sudo named-checkzone local /var/named/local.zone

If both of these commands are successful, then you're good to go. If not, something broke - and you'll need to fix it. However, assuming we're all good you can now move onto the next step, which is actually starting up bind and having it start when your system starts.

To do that, you'll run the following command:

$ sudo launchctl load -w /System/Library/LaunchDaemons/org.isc.named.plist

After that, you should have a working nameserver running on your computer. All that is left to do is to configure your networking setup to actually use your shiny new nameserver.

Networking Setup

Now all that's left to cover is the networking setup on your personal computer. It's actually pretty simple on OS X. Just jump into the System Preferences application (click the apple menu, then "System Preferences"), then click "Network". Then you'll want to click the "Advanced" button and then, in the dialog screen that pops up, select "DNS". Then just add "127.0.0.1" to the list of DNS servers. So, when you're done it should look something like this:

Then, you should be all set. You should be able to go to Terminal, and ping anything ending in ".local" - and it should resolve to 127.0.0.1. If it works, you're good to go.

Next Steps

There are a lot of things that you can do to improve on this design. If you're working in a corporate network that has its own custom zones, then you'll need to do some additional configuration to ensure that you can still connect to the corporate assets. I recommend reading this article on different DNS zone configuration types if you want to learn about all the different types of zone that can be defined.

If you guys have any suggestions on improvements, give me a shout in the comments! I'd love to hear from you!

Next Up: Make Apache Smarter

In the next article in this series, I'm going to share some clever Virtual Host configuration I ran across that allows you to add new Virtual Hosts without touching Apache's configuration files. Of course, I didn't find this solution adequate alone because I'm far to picky for that. I have projects that I'm doing for my company that uses our code base, and work for a client that we're subcontracting for that uses their codebase. So I came up with a configuration that allows me to have "realms" of automatically configured virtual hosts in a nice, clean directory tree.

As always, leave me some comment love and I'll see you on the flip side.