Add Dockerized Bind DNS Server to GNS3

I posted a while ago on how to install and configure the Bind DNS server on Ubuntu 18.04, and got a request from a reader with help on getting Dockerized Bind into GNS3. This post is the result of my tinkering with that lab.

The organization that oversees the Bind open source project also releases an official Docker image through the Docker hub that anyone can access. Docker container technology can be a tricky at first for systems and network engineers to wrap their heads around. Docker containers are not an entire operating system – full operating systems are designed to run many processes at once. Docker containers are designed to run one process and one process only. They only contain the software and libraries needed to run that process.

GNS3 has a very cool integration with Docker, however. It allows you to add full network adapters to your containers and copies in some handy tools to make the command line environment usable. But since many of the familiar OS tools are not included in most Docker containers like they would be with a standard OS, it can be challenging to get things working right.

If you are using Ubuntu Linux, feel free to check out my guide on installing GNS3 and Docker on Ubuntu 20.04. If you are using Windows or Mac with the GNS3 VM, Docker is already installed on the VM.

Topology

My topology is simple – a single vlan and IP subnet of 10.0.0.0/24. My Bind DNS server will reside at 10.0.0.3, with two Alpine Linux containers at 10.0.0.1 and 10.0.0.2. I walk through getting Alpine Linux containers installed on the post I linked above, if you need help.

Build your own image based off the official ISC Bind image

First open up a shell or terminal on the GNS3 VM or wherever the GNS3 server is located. If you don’t know how to open a shell, they walk you through it on the official GNS3 docs:

https://docs.gns3.com/docs/emulators/create-a-docker-container-for-gns3/

Create a directory where you can write your Dockerfile and build the image:

mkdir jamesbind
cd jamesbind

vi Dockerfile

Feel free to use whatever text editor you like, I’m a vi person. We’re going to write a Dockerfile that looks like this:

FROM internetsystemsconsortium/bind9:9.11
RUN apt-get update
RUN apt-get install vim -y

Basically all this does is pull the official Bind Docker image, and run some commands on the image. Namely we are updating apt-get and installing vi. We need to do this because this docker image does not have a text editor installed, and we have to edit the Bind configuration files.

Full disclosure: there is another, much better way than manually editing config files from inside the container. You can write the config files in the same folder as the Dockerfile, and add them to the Docker image when you build it. However, I think it’s best for learning and troubleshooting purposes to manually edit the text files, so that’s the route I’m going.

Build your image(-t switch gives it a “tag”, which is basically a name):

docker build -t jamesbind .

Don’t forget the period at the end, that’s important. You should now have a fresh docker image with bind and vi installed in it.

Add your image to GNS3

From the GNS3 preferences window, you can now add your image to the list of devices available.

Click through and use the defaults except when you get to the “Start command” window. You’ll want to set that to /bin/bash:

Now you’re ready to use your image in GNS3!

Fire up Bind

Drag all the containers out, connect and double click on them to get a terminal. You should be able to configure IP settings normally using iproute2 commands (ip addr add 10.0.0.1/24 dev eth0, etc). For the Bind container, let’s write our config files. As I mentioned many cycles ago in my Bind server post, there are three Bind config files:

/etc/bind/named.conf.options –> Configures BIND9 options
/etc/bind/named.conf.local –> Sets zone file name and gives its location
/etc/bind/zones/db.jamesmcclay.com –> The actual zone file with DNS records.

First let’s hop into our Bind container (just double click on it) and configure named.conf.options. Mine looks like this:

options {
        directory "/var/cache/bind";
        listen-on { any; };
};

Now on to named.conf.local. This is where you declare your zone. Mine is going to be jamesmcclay.com, I just made it up.

zone "jamesmcclay.com" {
    type master;
    file "/etc/bind/zones/db.jamesmcclay.com";
};

Now for the zone file that we indicated above. It needs to be created, so lets create both the zones folder and jamesmcclay.com zone file:

mkdir zones
cd zones
vi db.jamesmcclay.com
@               IN      SOA     ns.jamesmcclay.com.    root.jamesmcclay.com. (
                                2               ; Serial
                                604800	        ; Refresh
                                86400           ; Retry
                                2419200         ; Expire
                                604800 )        ; Negative Cache TTL
;
@               IN      NS      ns.jamesmcclay.com.
ns              IN      A       10.0.0.3
alpine1         IN      A       10.0.0.1
alpine2         IN      A       10.0.0.2

Finally, fire up Bind by running the “named -g” command. This will run it in the foreground, with debug output which will be handy. Alternatively, you can just run “named” and it’ll go in the background. When you run it, you’ll be looking for a line that says your zone file was loaded. “all zones loaded” seems to be a lie, if there’s errors on your zone, it’ll say that and then say all zones were loaded. Make sure you read the output carefully:

named -g
<...removed for brevity...>
26-Oct-2021 23:49:14.231 zone jamesmcclay.com/IN: loaded serial 2
26-Oct-2021 23:31:49.828 all zones loaded
26-Oct-2021 23:31:49.829 running

In your Alpine containers, add “nameserver 10.0.0.3” to resolv.conf to tell them to use the Bind server for DNS resolution:

echo "nameserver 10.0.0.3" > /etc/resolv.conf

Testing your setup

First let’s ping ns.jamesmcclay.com (the Bind container) from alpine-1:

ping ns.jamesmcclay.com

PING ns.jamesmcclay.com (10.0.0.3): 56 data bytes
64 bytes from 10.0.0.3: seq=0 ttl=64 time=1.080 ms
64 bytes from 10.0.0.3: seq=1 ttl=64 time=1.073 ms

It works! We can see in a wireshark packet capture the DNS request from 10.0.0.1 and response from 10.0.0.3:

Pinging to alpine2.jamesmcclay.com also works:

ping alpine2.jamesmcclay.com

PING alpine2.jamesmcclay.com (10.0.0.2): 56 data bytes
64 bytes from 10.0.0.2: seq=0 ttl=64 time=0.999 ms
64 bytes from 10.0.0.2: seq=1 ttl=64 time=1.087 ms

Troubleshooting

The Bind configuration files are really sensitive to anything that’s left out. Be sure and check to see if you forgot a semicolon or that your zone file is properly formatted with all required entries in place. And again, I highly recommend using the “named -g” when you are testing, it’ll give you some big hints as to what is wrong with your configuration.

If your Bind server is running with no config errors and something still isn’t working, it could be a network issue. Make sure and do a packet capture to see if packets are actually flowing and they’re what you expect! Sometimes after troubleshooting for a long time I do a packet capture only to find packets were never leaving the network interface due to something I forget, like adding an IP address or route somewhere.

Good luck! Feel free to reach out with questions about your lab, I’m always happy to help.

BIND9 DNS Server on Ubuntu 18.04 Linux (w/ Cisco Routing)

Background

BIND9 is an open-source DNS (Domain Name System – the text-to-ip-address resolution system we all rely upon) server application that many of the world’s DNS servers use to change IP addresses (e.g. 1.2.3.4) into strings of characters such as “questioncomputer.com”. DNS is an area of expertise in which some people spend their entire careers and as such, there is much to know about it. I’m no DNS expert, but since anyone can fire up their own DNS server with BIND9, I figured I’d give it a go. BIND9 of couse runs on Linux, today we’ll be using Ubuntu 18.04.

BIND9 is one of several applications that a non-profit company called “Internet Systems Consortium” produces, you can find their page on BIND9 here. They rely on companies that use BIND9 to purchase professional support, so if you work for an organization that does, please consider reaching out to them.

Topology

This topology includes 3 different routers and 3 different servers. The routers, aptly-named Router1, Router2, and Router3 each have 1 server connected. Two of the routers are Cisco IOSv routers, and Router3 is an Ubuntu box with Free Range Routing installed just for kicks. Routing is set up using OSPF, so everything can already ping everything else. If you’re interested in how set up an Ubuntu 18.04 server as a router with OSPF, please check out my post on installing FRR. In that post I ran EIGRP but configuring OSPF is pretty simple once you get FRR installed.

The DNS server at the top is running Ubuntu 18.04, as well as the generic Ubuntu 18.04 server at the bottom right. There is a Windows 10 desktop at the bottom left to provide some diversity.

Installation

Installation of BIND9 is pretty easy using Ubuntu’s package manager:

apt-get install bind9

Configuration

There are three text files needed to get a basic BIND9 configuration up and running, these are:
/etc/bind/named.conf.options –> Configures BIND9 options
/etc/bind/named.conf.local –> Sets zone file name and gives its location
/etc/bind/zones/db.jamesmcclay.com –> The actual zone file with DNS records.

First order of business is edit /etc/bind/named.conf.options to have a very, very basic configuration:

options {
        directory "/var/cache/bind";
        listen-on { any; };
};

Second, add a zone configuration to named.conf.local that indicates where the zone file will be kept:

zone "jamesmcclay.com" {
    type master;
    file "/etc/bind/zones/db.jamesmcclay.com";
};

Lastly, we’ll create “db.jamesmcclay.com” under the “zones” directory, because that’s what I put in named.conf.local. Obviously you’ll want to probably substitute something else for “jamesmcclay.com”:

@               IN      SOA     dns-server.jamesmcclay.com    dns-server.localhost (
                                2               ; Serial
                                604800  ; Refresh
                                86400           ; Retry
                                2419200 ; Expire
                                604800 )        ; Negative Cache TTL
;
@               IN      NS      dns-server
@               IN      A       172.16.0.2
Router1         IN      A       10.0.0.1
Router2         IN      A       10.0.0.2
Router3         IN      A       11.0.0.1
Win10           IN      A       192.168.0.2
U1804           IN      A       192.168.1.2
dns-server      IN      A       172.16.0.2

The values towards the top are mostly just setting default values for DNS, while the A records at the bottom are more important. Those are the IP addresses that will be returned for hostnames within “jamesmcclay.com”. Gotta make sure those are correct. They all correspond with the IP’s listed in the topology.

Now restart BIND9:

systemctl restart bind9

It should show green if your config is good:

$systemctl status bind9

Configuring the servers and desktops is as simple as setting the DNS server IP address to 172.16.0.2. On Ubuntu linux, you can do it with Netplan. The default config file for Netplan is at /etc/netplan/50-cloud-init.yaml, and I configured UbuntuServer18.04-2 at the bottom right of the topology like this:

network:
    ethernets:
        eth0:
            addresses: [192.168.1.2/24]
            dhcp4: false
            optional: true
            gateway4: 192.168.1.1
            nameservers:
                search: [jamesmcclay.com]
                addresses: [172.16.0.2]
    version: 2

For Windows 10 (and most others, I think) you can sift through the control panel to get to network adapter settings, but I just hit “window key + R” and type “ncpa.cpl” and press enter which takes me to network adapter configuration. My configuration on the Windows 10 desktop at the bottom left of the topology looks like this:

In “Advanced” I also set the search domain to “jamesmcclay.com” which appends that domain to any non-fully qualified hosts entered. You’ll see how that’s helpful in the verification section:

Verification

Simple pings will verify that you’re able to reach various hosts via their DNS names instead of IP addresses. On the UbuntuServer18.04-2 at the bottom right, I pinged the whole group using their fully qualified domain names:

$ping router1.jamesmcclay.com -c 1
64 bytes from 10.0.0.1 (10.0.0.1): icmp_seq=1 ttl=254 time=1.45 ms
$ping router2.jamesmcclay.com -c 1
64 bytes from 10.0.0.2 (10.0.0.2): icmp_seq=1 ttl=254 time=1.03 ms
$ping router3.jamesmcclay.com -c 1
64 bytes from 11.0.0.1: icmp_seq=1 ttl=64 time=0.635 ms
$ping win10.jamesmcclay.com -c 1
64 bytes from 192.168.0.2 (192.168.0.2): icmp_seq=1 ttl=126 time=2.11 ms
$ping dns-server.jamesmcclay.com -c 1
64 bytes from 172.16.0.2 (172.16.0.2): icmp_seq=1 ttl=62 time=0.913 ms

And since I entered search domains in my DNS server configurations, I can ping hosts without fully qualified domain names. From my Windows 10 desktop:

And as usual, you can do a packet capture to verify that the DNS packet flow is working correctly. If I look at packets leaving my Windows 10 desktop, I’ll see a DNS query from the desktop, and a response from BIND9:

Troubleshooting

If you’re having trouble, make sure all of your config files are formatted correctly. They get angry if you don’t format them right. Be sure and do a packet capture if you get stuck, that will pinpoint the problem pretty quickly. Also you can check the logs in the BIND9 server for messages that might have a clue. The command I use is “cat /var/log/syslog | grep bind”.

Apr 19 03:42:16 uvm1804 named[15145]: running as: named -f -u bind<br>Apr 19 03:42:16 uvm1804 named[15145]: loading configuration from '/etc/bind/named.conf'<br>Apr 19 03:42:16 uvm1804 named[15145]: reading built-in trust anchors from file '/etc/bind/bind.keys'<br>Apr 19 03:42:16 uvm1804 named[15145]: set up managed keys zone for view _default, file 'managed-keys.bind'<br>Apr 19 03:42:16 uvm1804 named[15145]: configuring command channel from '/etc/bind/rndc.key'<br>Apr 19 03:42:16 uvm1804 named[15145]: configuring command channel from '/etc/bind/rndc.key'<br>root@dns-server:/etc/netplan#