loading...

Ubuntu Server 18.04 – Setting up DNS with bind

Initial Server Setup with CentOS 8

I’m sure most of you are familiar with the purpose of a Domain Name System (DNS) server. Its simplest definition is that it’s a service that’s responsible for matching an IP address to a domain or hostname. When you connect to the internet, name-to-IP matching happens constantly as you browse. After all, it’s much easier to connect to https://www.google.com/ with its domain name, than it is to remember its IP address. When you connect to the internet, your workstation or server will connect to an external DNS server in order to figure out the IP addresses for the websites you attempt to visit.

It’s also very common to run a local DNS server internally in your organization. The benefit is that you’ll be able to resolve your local hostnames as well, something that an external DNS server would know nothing about. For example, if you have an intranet site that you intend to make available to your co-workers, it would be easier to give everyone a local domain that they can access than it would be to make everyone its IP address. With a local DNS server, you would create what is known as a Zone File, which would contain information regarding the hosts and IP address in use within your network so that local devices would be able to resolve them. In the event that your local DNS server is unable to fulfill your request (such as a request for an external website), the server would pass the request along to an external DNS server, which would then carry out the request.

A detailed discussion of DNS, how it functions, and how to manage it is outside the scope of this tutorial. However, a basic understanding is really all you need in order to make use of a DNS server within your network. In this section, I’ll show you how to set up your very own DNS server to allow your devices to resolve local hostnames, which should greatly enhance your network.

First, we’ll need to install the Berkeley Internet Name Daemon (BIND) package on our server:

sudo apt install bind9 

At this point, we have the bind9 service running on our server, though it’s not actually configured to do much at this point. The most basic function of bind is to act as what’s called a Caching Name Server, which means that the server doesn’t actually match any names itself. Instead, it caches responses from an external server. We’ll configure bind with actual hosts later, but setting up a caching name server is a good way to get started.

To do so, open the /etc/bind/named.conf.options file in your favorite text editor.

Within the file, you should see a block of text that looks similar to the following:

// forwarders { 
//      0.0.0.0; 
// }; 

Uncomment these lines. The forward slashes are the comment marks as far as this configuration file is concerned, so remove them. Then, we can add a few external DNS server IP addresses. For these, you can use the IP addresses for your ISP’s DNS servers, or you could simply use Google’s DNS servers (8.8.8.8 and 8.8.4.4) instead:

forwarders { 
  8.8.8.8; 
  8.8.4.4; 
}; 

After you save the file, restart the bind9 service:

sudo systemctl restart bind9 

To be sure that everything is running smoothly, check the status of the service. It should report that it’s active (running):

systemctl status bind9 
Checking the status of the bind9 service

As long as you’ve entered everything correctly, you should now have a working DNS server. Of course, it isn’t resolving anything, but we’ll get to that. Now, all you should need to do is configure other devices on your network to use your new DNS server. The easiest way to do this is to reconfigure the isc-dhcp-server service we set up in the previous section. Remember the section that designates a pool of addresses from the server to the clients? It also contained a section to declare the DNS server your clients will use as well. Here’s that section again, with the relevant lines in bold:

subnet 192.168.1.0 netmask 255.255.255.0 { 
range 192.168.1.100 192.168.1.240; 
option routers 192.168.1.1; 
option domain-name-servers 192.168.1.1; 
} 

To configure the devices on your network to use your new DNS server, all you should need to do is change the configuration option domain-name-servers 192.168.1.1; to point to the IP address of your new server. When clients request a DHCP lease (or attempt to renew an existing lease), they will be configured with the new DNS server automatically.

With the caching name server we just set up, hosts that utilize it will check it first for any hosts they attempt to look up. If they look up a website or host that is not within your local network, their requests will be forwarded to the forwarding addresses you configured for bind. In my example, I used Google’s DNS servers, so if you used my configuration your hosts will first check your local server and then check Google’s servers when resolving external names. Depending on your network hardware and configuration, you might even see a slight performance boost. This is because the DNS server you just set up is caching any lookups done against it. For example, if a client looks up https://www.packtpub.com in a web browser, your DNS server will forward the request along since that site doesn’t exist locally and it will also remember the result. The next time a client within your network looks up that site, the response will be much quicker because your DNS server cached it.

To see this yourself, execute the following command twice on a node that is utilizing your new DNS server:

dig www.packtpub.com 

In the response, look for a line toward the end that gives you your query time. It will look similar to the following:

;; Query time: 98 msec 

When you run it again, the query time should be much lower:

;; Query time: 1 msec 

This is your caching name server in action! Even though we haven’t even set up any zone files to resolve your internal servers, your DNS server is already adding value to your network. You just laid the groundwork we’ll use for the rest of our configuration.

Now, let’s add some hosts to our DNS server so we can start fully utilizing it. The configuration file for bind is located at /etc/bind/named.conf. In addition to some commented lines, it will have the following three lines of configuration within it:

include "/etc/bind/named.conf.options"; 
include "/etc/bind/named.conf.local"; 
include "/etc/bind/named.conf.default-zones"; 

As you can see, the default bind configuration is split among several configuration files. Here, it includes three others: named.conf.options, named.conf.local, and named.conf.default-zones (the first of which we already took care of editing). In order to resolve local names, we need to create what is known as a Zone File, which is essentially a text file that includes some configuration, a list of hosts, and their IP addresses. In order to do this, we need to tell bind where to find the zone file we’re about to create. Within /etc/bind/named.conf.local, we need to add a block of code like the following to the end of the file:

zone "local.lan" IN { 
    type master; 
    file "/etc/bind/net.local.lan"; 
}; 

Notice that the zone is named local.lan, which is the same name I gave our domain in our DHCP server configuration. It’s best to keep everything consistent when we can. If you use a different domain name than the one I used in my example, make sure that it matches here as well. Within the block, we’re creating a master zone file and informing bind that it can find a file named net.local.lan, stored in the /etc/bind directory. This should be the only change we’ll need to make to the named.conf.local file; we’ll only create a single zone file (for the purpose of this section). Once you save this file, you’ll need to create the /etc/bind/net.local.lan file. So, go ahead and open that file in a text editor. Since we haven’t created it yet, it should be blank. Here’s an example of this zone file, completely filled out with some sample configuration:

$TTL 1D 
@ IN SOA local.lan. hostmaster.local.lan. ( 
 
201808161 ; serial 
 
8H ; refresh 
4H ; retry 
4W ; expire 
1D ) ; minimum 
IN A 192.168.1.1 
; 
@ IN NS hermes.local.lan. 
fileserv        IN  A   192.168.1.3 
hermes     IN A    192.168.1.1 
mailserv        IN  A   192.168.1.5 
mail            IN  CNAME   mailserv. 
web01           IN  A   192.168.1.7 

Feel free to edit this file to match your configuration. You can edit the list of hosts at the end of the file to match your hosts within your network, as the ones I included are merely examples. You should also ensure that the file matches the IP scheme for your network. Next, I’ll go over each line in order to give you a deeper understanding of what each line of this configuration file is responsible for:

$TTL 1D 

The Time to Live (TTL) determines how long a record may be cached within a DNS server. If you recall from earlier, where we practiced with the dig command, you saw that the second time you queried a domain with dig, the query time was less than the first time you ran the command. This is because your DNS server cached the result, but it won’t hold onto it forever. At some point, the lookup will expire. The next time you look up that same domain after the cached result expired, your server will go out and fetch the result from the DNS server again. In my examples, I used Google’s DNS servers. That means at some point, your server will query those servers again once the record times out:

@ IN SOA local.lan. hostmaster.local.lan. ( 

With the Start of Authority (SOA) line, we’re establishing that our DNS server is authoritative over the local.lan domain. We also set hostmaster@local.lan as the email address of the responsible party for this server, but we enter it here in a different format for bind (hostmaster.local.lan). This is obviously a fake address, but for the purposes of an internal DNS server, that’s not an issue we’ll need to worry about:

201808161 ; serial 

Of all the lines of configuration within a zone file, the serial is by far the one that will frustrate us the most. This is because it’s not enough to simply update the zone file any time we make a change to it (change an IP address, add or remove a host, and so on); we also need to remember to increase the serial number by at least one. If we don’t, bind won’t be aware that we’ve made any changes, as it will look at the serial before the rest of the file. The problem with this is that you and I are both human, and we’re prone to forgetting things. I’ve forgotten to update the serial many times and became frustrated when the DNS server refused to resolve new hosts that were recently added. Therefore, it’s very important for you to remember that any time you make a change to any zone file, you’ll need to also increment the serial. The format doesn’t really matter; I used 201808161, which is simply the year, two-digit month, two-digit day, and an extra number to cover us if we make more than one change in a day (which can sometimes happen). As long as you increment the serial by one every time you modify your zone file, you’ll be in good shape-regardless of what format you use. However, the sample format I gave here is actually quite common in the field:

8H ; refresh 
4H ; retry 
4W ; expire 
1D ) ; minimum 

These values control how often slave DNS servers will be instructed to check in for updates. With the refresh value, we’re instructing any slave DNS servers to check in every eight hours to see whether or not the zone records were updated. The retry field dictates how long the slave will wait to check in, in case there was an error doing so the last time. The last two options in this section, expire and minimum, set the minimum and maximum age of the zone file, respectively. As I mentioned though, a full discussion of DNS with bind could constitute an entire tutorial on its own. For now, I would just use these values until you have a reason to need to experiment:

IN A 192.168.1.1 
@ IN NS hermes.local.lan. 

Here, we identify the name server itself. In my case, the server is called hermes and it’s located at 192.168.1.1.

Next, in our file we’ll have several host entries to allow our resources to be resolved on our network by name. In my example, I have three hosts: fileserv, mailserv, and web01. In the example, these are all address records, which means that any time our server is asked to resolve one of these names, it will respond with the corresponding IP address. If our DNS server is set as a machine’s primary DNS server, it will respond with 192.168.1.3 when asked for fileserv and 192.168.1.7 when asked for web01. The entry for mail is special as it is not an address record, but instead a Canonical Name (CNAME) record. In this case, it just points back to mailserv. Essentially, that’s what a CNAME record does: it creates a pointer to another resource. In this case, if someone tries to access a server named mail, we redirect them to the actual server mailserv. Notice that on the CNAME record, we’re not inputting an IP address, but instead the hostname of the resource it’s linked to:

fileserv        IN  A   192.168.1.3 
hermes        IN  A    192.168.1.1 
mailserv        IN  A   192.168.1.5 
mail            IN  CNAME   mailserv. 
web01           IN  A   192.168.1.7 

In addition, you should also notice that I added the DNS server itself (hermes) to the file as well. You can see it on the second line above. I’ve found that if you don’t do this, the DNS server may complain and refuse to load the file.

Now that we have a zone file in place, we should be able to start using it. First, we’ll need to restart the bind9 service:

sudo systemctl restart bind9 

After the command finishes, check to see if there are any errors:

systemctl status bind9 
Checking the status of the bind9 service after adding a new zone

You should see that the service state is active (running), and in addition a line telling you that the serial number for your zone file was loaded. In my case, serial number 201808162 was loaded for zone local.lan. If you don’t see the service is running and/or your zone file was not loaded, you should see specific information in the output while checking the status that should point you in the right direction. If not, you can also check the system log for clues regarding bind as well:

cat /var/log/syslog | grep bind9  

The most common mistakes I’ve seen typically result from not being consistent within the file. For example, if you’re using a different IP scheme (such as 10.10.10.0/24), you’ll want to make sure you didn’t forget to replace any of my example IP addresses with the proper scheme. Assuming that everything went smoothly, you should be able to point devices on your network to use this new DNS server. Make sure you test not only pinging devices local to your network, but outside resources as well, such as websites. If the DNS server is working properly, it should resolve your local names, and then forward your requests to your external DNS servers (the two we set as forwarders) if it doesn’t find what you’re looking for locally. In addition, you’ll also want to make sure that port 53 is open in your network’s firewall, which is the port that DNS uses. It’s extremely rare that this would be an issue, but I have seen it happen.

To further test our DNS server, we can use the dig command, as we did before while we were experimenting with caching. Try dig against a local and external resource. For example, you could try dig against a URL as well as a local server:

dig webserv.local.lan 
dig www.packtpub.com 

You should see a response similar to the following:

;; Query time: 1 msec 
;; SERVER: 127.0.0.53#53(127.0.0.53) 
;; WHEN: Sat Feb 10 10:00:59 EST 2018 
;; MSG SIZE  rcvd: 83 

What you’re looking for here is that both local resources and external websites should be resolvable now. You’ll probably notice that the DNS server used in the output will most likely show up as a localhost address, such as it did in my output and not the DNS server we just set up. Actually, you can ignore this. Most distributions of Linux nowadays use local resolvers, which essentially cache DNS lookup results on your local computer. Your computer is still using the DNS server we set up, but there’s just an additional layer in between your computer and the DNS server. You can verify this with the following command:

systemd-resolve --status |grep DNS Servers 

The output will show you the IP address of the actual server that’s responding to your DNS lookups.

Comments are closed.

loading...