Introduction to CoreDNS in a Dockerised environment
23 Feb 2019
CoreDNS is a relatively new DNS server written in Go. CoreDNS was written keeping in mind the evolving needs of today and the ability to work well with cloud native applications. In Kubernetes 1.13, CoreDNS is the default cluster DNS server.
synopsis
Resources required
- Windows / Linux or MAC with Virtualbox installed.
- Ubuntu Server 16.04.4 LTS ISO image.
Prerequisites
- Basic understanding of DNS
- Basic understanding of Docker
Setting up the environment
-
Install Ubuntu Server on Virtualbox.
- Install docker on Ubuntu. Instructions here.
- Log in to your Ubuntu VM.
Bind on Docker
Before we start with CoreDNS, let’s take a look at how to run Bind as a Docker container. Since we are already familiar with Bind, this should help us get some context on CoreDNS by providing us a comparison between CoreDNS and Bind.
To begin, we wll create an ubuntu container and install Bind in it.
Run the below command to deploy the Ubuntu container for Bind.
docker run -it -p 53:53/udp --name bind --hostname bind ubuntu bash
Your prompt should have changed to something similar to the below.
root@bind:/#
You are now in the container where you will install Bind.
Let’s proceed with the installation.
Run the following commands in the container to install Bind and related tools. We will also install Vim which will let s edit configuration file.
apt update
apt install bind9 bind9utils vim -y
We will be configuring Bind as a simple forwarding server.
Run the below command to edit the configuration file.
vi /etc/bind/named.conf.options
Edit the configuration file as below.
options {
directory "/var/cache/bind";
forwarders {
10.192.3.10;
};
listen-on { any; };
};
Now, let’s start the DNS service.
service bind9 start
The expected output is as below.
root@bind:/# service bind9 start
* Starting domain name service... bind9 [ OK]
Detach from the container using the keyboard combination < CTRL+P Q >.
You should have detached from the container now.
Query the localhost for the A record of google.com.
The output should be as below.
# dig @localhost google.com
; <<>> DiG 9.11.3-1ubuntu1.2-Ubuntu <<>> @localhost google.com
; (2 servers found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 28564
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
; COOKIE: 50d02d3da9df35d4f87516ce5be9febb259bf180bbc6261a (good)
;; QUESTION SECTION:
;google.com. IN A
;; ANSWER SECTION:
google.com. 2 IN A 172.217.6.46
;; Query time: 6 msec
;; SERVER: ::1#53(::1)
;; WHEN: Mon Nov 12 22:29:15 UTC 2018
;; MSG SIZE rcvd: 83
We have now run Bind from a Docker container and tested the server.
Let’s move on to CoreDNS.
Before we move on, we will need to kill the Bind container to free up UDP port 53 on the host.
Run the below command to kill the container.
docker kill bind
Confirm that the container is no longer running with the ‘docker ps’ command. The output should be as below.
# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
Installing CoreDNS
CoreDNS does provide a docker image but we will be looking at how to run it manually from an Ubuntu container.
- Deploy an Ubuntu container with the name coredns-manual. Ensure that ports 53 on the host is mapped to the container.
The below command deploys the container.
docker run -it -p 53:53/udp --name coredns-manual --hostname coredns ubuntu bash
Your prompt should have changed to something similar to the below.
root@coredns:/#
- To install new packages, you must first update the package manager for Ubuntu. The package manage is ‘apt’. You can update ‘apt’ with the command ‘apt update’. The command and output are below.
root@coredns:/# apt update
Installing CoreDNS
Below are the steps to install CoreDNS
Precompiled binary executable file for CoreDNS can be obtained from the CoreDNS Github repository.
apt install wget vim -y
wget https://github.com/coredns/coredns/releases/download/v1.2.5/coredns_1.2.5_linux_amd64.tgz
mkdir ~/coredns
mv coredns_1.2.5_linux_amd64.tgz ~/coredns
cd ~/coredns
tar -xvzf coredns_1.2.5_linux_amd64.tgz
rm -f coredns_1.2.5_linux_amd64.tgz
This should produce an executable file named coredns.
root@coredns:~/coredns# ls -l
total 50440
-rwxr-xr-x 1 www-data www-data 39737920 Oct 24 20:10 coredns
Run the executable file.
The output should be as below.
root@coredns:~/coredns# ./coredns
.:53
2018/10/31 19:25:54 [INFO] CoreDNS-1.2.5
2018/10/31 19:25:54 [INFO] linux/amd64, go1.11.1, 204537b
CoreDNS-1.2.5
linux/amd64, go1.11.1, 204537b
Notice that the execution does not complete and go the prompt “root@coredns:~/coredns#” CoreDNS server is now running.
Test the CoreDNS server
Detach from the container using the keyboard combination < CTRL+P Q >.
Now you should have detached from the container and shuold be in the prompt of the VM.
Run the command ‘docker ps’. Output should be as below(except for the Container ID).
# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
858520e82135 ubuntu "bash" 4 hours ago Up 17 minutes 0.0.0.0:53->53/udp, 0.0.0.0:443->443/tcp coredns-manual
Under PORTS, “0.0.0.0:53->53/udp” shows that UDP port 53 on the host is mapped to UDP port 53 on the container.
Perform a few DNS queries against localhost. Notice that the response always looks similar to this.
# dig @localhost example.com
; <<>> DiG 9.11.3-1ubuntu1.2-Ubuntu <<>> @localhost example.com
; (2 servers found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 25683
;; flags: qr aa rd; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 3
;; WARNING: recursion requested but not available
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
; COOKIE: cbc94e442b78ed38 (echoed)
;; QUESTION SECTION:
;example.com. IN A
;; ADDITIONAL SECTION:
example.com. 0 IN A 172.17.0.1
_udp.example.com. 0 IN SRV 0 0 59496 .
;; Query time: 0 msec
;; SERVER: ::1#53(::1)
;; WHEN: Wed Oct 31 19:41:50 UTC 2018
;; MSG SIZE rcvd: 117
This is expected. Since no configuration has been done, CoreDNS loads a plugin called whoami that responds with the IP address and port of the client.
Configuring CoreDNS
Corefile
Configuration parameters for CoreDNS are defined in a file named ‘Corefile’. CoreDNS is designed to run multiple server instances on the same host. These servers can run on different ports or the same port. A server block is a block of configuration statements in the corefile which define a server. Zone statements are configured in server blocks. If there are multiple server blocks listening on the same port (eg: port 53), the server will send a response from the server block with the zone with the closest match to the domain in the queried.
Below is a sample server block
test.com{
}
By default the server listens on port 53. To configure the server to listen on another port, say 1053, define the server block as below.
test.com:53 {
}
Plugins
CoreDNS works by using plugins for different functionality. Plugin are programs that increase the functionality of CoreDNS. Plugins can be in-tree(from CoreDNS team) or external plugins(developed by the community or third parties).
Here are a few plugins which we will be working with:
- file: Reads zone data from a zone file.
- log: Logs queries. By default queries are logged to STDOUT.
- forward: Forward queries to a forwarder.
These are in-tree plugins and don’t require any additional setup. These plugins can be invoked by calling them from the ‘Corefile’ as seen below.
test.com:53 {
log
}
CoreDNS by default does not support recursively resolving queries. The ‘forward’ in-tree plugin can be used to forward queries to a forwarder. There are external plugins that can bring this functionality to CoreDNS
To get started attach to the container ‘coredns-manual’.
#docker attach coredns-manual
Stop the running process using the keyboard combination <CTRL + C>.
Confirm that the working directory is ‘/root/coredns’.
root@coredns:~/coredns# pwd
/root/coredns
If not, change directory to /root/coredns.
cd /root/coredns
Create a file named ‘Corefile’.
vi Corefile
Insert the below text into the file.
test.com {
file db.test.com
}
Create a file named db.test.com and insert the below text into the file.
$ORIGIN test.com.
@ 3600 IN SOA ns1.test.com. admin.test.com. (
2017042745 ; serial
7200 ; refresh (2 hours)
3600 ; retry (1 hour)
1209600 ; expire (2 weeks)
3600 ; minimum (1 hour)
)
3600 IN NS ns1.test.com.
3600 IN NS ns2.test.com.
www IN A 10.0.0.1
ns1 IN A 10.10.10.10
ns2 IN A 20.20.20.20
Start CoreDNS with the below command.
./coredns -conf Corefile
Detach from the container using the keyboard combination < CTRL+P Q >.
Query localhost for www.test.com. You should be able to see output as below.
# dig @localhost www.test.com 1 ↵
; <<>> DiG 9.11.3-1ubuntu1.2-Ubuntu <<>> @localhost www.test.com
; (2 servers found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 51555
;; flags: qr aa rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 2, ADDITIONAL: 0
;; QUESTION SECTION:
;www.test.com. IN A
;; ANSWER SECTION:
www.test.com. 3600 IN A 10.0.0.1
;; AUTHORITY SECTION:
test.com. 3600 IN NS ns1.test.com.
test.com. 3600 IN NS ns2.test.com.
;; Query time: 2 msec
;; SERVER: ::1#53(::1)
;; WHEN: Thu Nov 01 20:16:07 UTC 2018
;; MSG SIZE rcvd: 126
In the above excersise, we used the file plugin. The statement file db.test.com is to use the file ‘db.test.com’ as the zone file for the zone ‘test.com’.
Now we will look at the log plugin.
To see the ‘log’ plugin in action, let’s look at the logs for the ‘coredns-manual’ container. The command and output are below.
# docker logs coredns-manual --tail 10
root@coredns:~/coredns# ./coredns -conf Corefile
test.com.:53
2018/11/02 13:30:31 [INFO] CoreDNS-1.2.5
2018/11/02 13:30:31 [INFO] linux/amd64, go1.11.1, 204537b
CoreDNS-1.2.5
linux/amd64, go1.11.1, 204537b
Attach to the container again.
docker attach coredns-manual
Stop the running process using the keyboard combination <CTRL + C>.
Edit the file ‘Corefile’ and to match the below.
test.com {
file db.test.com
log
}
~
Start CoreDNS server.
./coredns -conf Corefile
Detach from the container using the keyboard combination < CTRL+P Q >.
Query localhost as above for www.test.com.
Now, check the logs for the container again.
You should see logs as below.
root@coredns:~/coredns#./coredns -conf Corefile
test.com.:53
2018/11/02 13:39:19 [INFO] CoreDNS-1.2.5
2018/11/02 13:39:19 [INFO] linux/amd64, go1.11.1, 204537b
CoreDNS-1.2.5
linux/amd64, go1.11.1, 204537b
172.17.0.1:60210 - [02/Nov/2018:13:48:05 +0000] 24028 "A IN www.test.com. udp 54 false 4096" NOERROR qr,aa,rd,ra 126 0.004729567s
Note that the logs now include query logs.
172.17.0.1:60210 - [02/Nov/2018:13:48:05 +0000] 24028 "A IN www.test.com. udp 54 false 4096" NOERROR qr,aa,rd,ra 126 0.004729567s
We are able to see these logs in docker logs since CoreDNS is running in a docker container. If CoreDNS was running on the host and not in a container, these logs would be printed to STDOUT.
Now, we will configure the forward plugin.
The forward plugin can forward queries to a specific set of servers. The plugin can also load balance using different methods between these servers.
We will configure a global forwarder and a forwarder for a specific domain name using the plugin.
Query localhost for google.com. The response should be as below.
# dig @localhost google.com 1 ↵
; <<>> DiG 9.11.3-1ubuntu1.2-Ubuntu <<>> @localhost google.com
; (2 servers found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: REFUSED, id: 4455
;; flags: qr rd; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 1
;; WARNING: recursion requested but not available
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
; COOKIE: d7358f9f438c2648 (echoed)
;; QUESTION SECTION:
;google.com. IN A
;; Query time: 0 msec
;; SERVER: ::1#53(::1)
;; WHEN: Fri Nov 02 14:09:53 UTC 2018
;; MSG SIZE rcvd: 51
Note that the query response is REFUSED.
Attach to the container again.
docker attach coredns-manual
Stop the running process using the keyboard combination <CTRL + C>.
Edit the file ‘Corefile’ and to match the below.
test.com {
file db.test.com
log
}
. {
forward . 10.192.3.10
}
Start CoreDNS server.
./coredns -conf Corefile
Detach from the container using the keyboard combination < CTRL+P Q >.
Query localhost for google.com. The response should be as below.
) # dig @localhost google.com 127 ↵
; <<>> DiG 9.11.3-1ubuntu1.2-Ubuntu <<>> @localhost google.com
In ; (2 servers found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 52732
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;google.com. IN A
;; ANSWER SECTION:
google.com. 36 IN A 216.58.192.14
;; Query time: 12 msec
;; SERVER: ::1#53(::1)
;; WHEN: Fri Nov 02 14:13:49 UTC 2018
;; MSG SIZE rcvd: 65
The query was forwarded the 10.192.3.10 and resolved.
Below are the newly added entries in the ‘Corefile’.
. {
forward . 10.192.3.10
}
The zone name here is ‘.’ (root). In the plugin statement we say ‘forward . 10.192.3.10’ forward all queries under root to 10.192.3.10.
Now query localhost for www.test.com. The query is resolving from the authoritative zone.
This is because, since both server blocks listen on the default port (53), the query for www.test.com is resolved from the server block for test.com and anything else would be resolved from the server block for root.
Now we will forward queries for a particular domain.
Below are the steps.
- Copy the coredns executable, the Corefile and the file db.test.com from the container to the host.
# docker cp coredns-manual:/root/coredns/coredns .
# docker cp coredns-manual:/root/coredns/Corefile .
# docker cp coredns-manual:/root/coredns/db.test.com .
- Confirm that the files have been copied.
# ls -l
total 38816
-rwxr-xr-x 1 root root 39737920 Oct 24 20:10 coredns
-rw-r--r-- 1 root root 73 Nov 2 14:11 Corefile
-rw-r--r-- 1 root root 367 Nov 1 20:25 db.test.com
- Modify the Corefile as below.
example.com {
file db.example.com
log
}
- Rename db.test.com to db.example.com
mv db.test.com db.example.com
- Replace all instances of the word ‘test.com’ in db.example.com with ‘example.com’
sed -i 's/test.com/example.com/g' db.example.com
- Create a new container named ‘coredns-forwarder’.
docker run -itd --name coredns-forwarder ubuntu bash
- Confirm that the container has been created.
# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
b91d9140f02b ubuntu "bash" 4 seconds ago Up 3 seconds coredns-forwarder
858520e82135 ubuntu "bash" 47 hours ago Up About an hour 0.0.0.0:53->53/udp, 0.0.0.0:443->443/tcp coredns-manual
- Copy the files to the new container.
docker cp coredns coredns-forwarder:/root/
docker cp Corefile coredns-forwarder:/root/
docker cp db.example.com coredns-forwarder:/root/
- Confirm that the files have been copied.
# docker exec coredns-forwarder ls /root
Corefile
coredns
db.example.com
- Attach to the container ‘coredns-forwarder’
docker attach coredns-forwarder
- Change directory to /root and run the CoreDNS server
root@b91d9140f02b:/# cd /root
root@b91d9140f02b:~# ./coredns -conf Corefile
example.com.:53
2018/11/02 14:39:23 [INFO] CoreDNS-1.2.5
2018/11/02 14:39:23 [INFO] linux/amd64, go1.11.1, 204537b
CoreDNS-1.2.5
linux/amd64, go1.11.1, 204537b
-
Detach from the container using the keyboard combination < CTRL+P Q >.
-
Find the IP address of the ‘coredns-forwarder’ container. This is done by inspecting the defaulr docker network (bridge).
# docker network inspect bridge | grep -A 3 coredns-forwarder
"Name": "coredns-forwarder",
"EndpointID": "09020d084787b9dcff41f82f8c6be6d6d415dbea3ad53e64b66e8cc2411d4eae",
"MacAddress": "02:42:ac:11:00:03",
"IPv4Address": "172.17.0.3/16",
- Attach to the container ‘coredns-manual’.
# docker attach coredns-manual
-
Stop the running CoreDNS server with the keyboard combination < CTRL + C >.
-
Edit the Corefile to match below but make sure that the IP address of the forwarder matches the IP address of ‘coredns-forwarder’.
test.com {
file db.test.com
log
}
. {
forward . 10.192.3.10
}
example.com {
forward example.com 172.17.0.3
}
- Start CoreDNS server
./coredns -conf Corefile
-
Detach from the container using the keyboard combination < CTRL+P Q >.
-
Query localhost for www.example.com
# dig @localhost www.example.com 1 ↵
; <<>> DiG 9.11.3-1ubuntu1.2-Ubuntu <<>> @localhost www.example.com
; (2 servers found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 47364
;; flags: qr aa rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 2, ADDITIONAL: 0
;; QUESTION SECTION:
;www.example.com. IN A
;; ANSWER SECTION:
www.example.com. 3600 IN A 10.0.0.1
;; AUTHORITY SECTION:
example.com. 3600 IN NS ns1.example.com.
example.com. 3600 IN NS ns2.example.com.
;; Query time: 0 msec
;; SERVER: ::1#53(::1)
;; WHEN: Fri Nov 02 16:58:20 UTC 2018
;; MSG SIZE rcvd: 144
Since port 53 on the host is mapped to port 53 on the container coredns-manual, any queries on port 53 to localhost lands on this container. The query is forwarded from this container to the container ‘coredns-forwarder’ where an authoritative server for the zone ‘example.com’ is running. This is how the query is resolved.
Serving Protocols
Currently CoreDNS accepts three different protocols: plain DNS, DNS over TLS and DNS over gRPC. The protocol must me specified on the server block in the Corefile.
To learn more
If you are interested in learning more about the topics discussed here, here are a few places to visit.