CoreDNS: File Plugin for Lab Testing

Author: Brandon B. Jozsa

Sometimes you need a quick, real DNS server for testing and you don't want to always have to edit your own home-lab DNS server. Well, have you ever thought to use CoreDNS? If you're interested in how to set up CoreDNS, using a Docker container (of course), then I'll cover two flexible options which may come in handy for your lab testing scenarios. You may even want to use these methods to replace your current BIND 9 deployment.

This blog post will cover some quick and easy-to-use methods for configuring CoreDNS for lab use. You may even want to explore using CoreDNS for your longer standing home lab like I have. Each example below will include a Corefile and other requirements. Finally, with each plugin example I will include a script that you can copy/paste for a direct, out-of-the-box, working example.

Let's get started.

Corefile: The Brains of the Operation

In order to get CoreDNS working properly, you must configure a Corefile. The Corefile is where all of the magic occurs. It's where you configure any plugins, which we'll get into, zone information, and how the server operates (ports, protocols, etc). This file is parsed like a Caddyfile. For each of the plugin examples I cover, I will also give any changes that are required in the Corefile.

Plugin: File

One of the quickest and easiest ways to get CoreDNS running for most people is by using the file plugin. The file plugin uses an RFC 1035-style master file. If you have ever configured a Berkeley Internet Name Domain (BIND) DNS server, then this approach should look familiar to you.

What's really nice about this option is that you can run it with very little resources, as it just takes a single container to run without any additional requirements. There is one downside; however, in that you will need to restart the service or container whenever you want to update your Corefile or example.com CoreDNS files. Let's discuss how to get this type of deployment working.

Corefile (File)

Here's an example of the Corefile when using the file plugin:

jinkit.io:53 {
    log stdout
    file /data/jinkit.io
}
.:53 {
    proxy . 8.8.8.8:53
    log stdout
}
Corefile for the File Plugin

This Corefile has a few things that may be interesting. The first is that we're logging to stdout, which means that if you want to run this service as a long standing Docker container, I would suggest you collect that logging data via Fluentd or Fluent Bit. Otherwise, for lab purposes just use docker log commands to retrieve any errors or lookup requests.

I'm also telling CoreDNS where my RFC-1035 file is located. If you're using Docker, make sure to mount this volume correctly.

I'm also using the proxy option. I'm not going to get into all the things you can use proxy for, as CoreDNS has great documentation on this. I will say that in this case, I wanted any records that are not associated with jinkit.io to be sent to Google DNS servers (8.8.8.8), which is the reason why this is configured in this example. Basically, I want to be authoritative for jinkit.io, because my testing examples will be part of this domain.

Domain File (File)

Below is an example of the RFC-1035-based domain file for a domain I own: jinkit.io.

$TTL    1M
$ORIGIN jinkit.io.

jinkit.io.		    IN	SOA	sns.dns.icann.org. noc.dns.icann.org. 2015082541 7200 3600 1209600 3600
jinkit.io.		    IN	NS	b.iana-servers.net.
jinkit.io.		    IN	NS	a.iana-servers.net.
jinkit.io.		    IN	A	127.0.0.1

; Flagship: Test A Record
test.jinkit.io.	    IN	A	192.168.3.21

; Flagship: Test TXT Record
text.jinkit.io.	    IN	TXT	"This is a test text record"

; Flagship: Test CNAME Record
cname.jinkit.io.	IN	CNAME	www.jinkit.net.

; Flagship: Test SRV Record
service.jinkit.io.	IN	SRV	8080 10 10 jinkit.io.

; Flagship: Kubernetes ETCD Server SRV Records
_etcd-server._tcp.jinkit.io.   300     IN      SRV 0 0 2380    fs-etcd05.jinkit.io.
_etcd-server._tcp.jinkit.io.   300     IN      SRV 0 0 2380    fs-etcd04.jinkit.io.
_etcd-server._tcp.jinkit.io.   300     IN      SRV 0 0 2380    fs-etcd03.jinkit.io.
_etcd-server._tcp.jinkit.io.   300     IN      SRV 0 0 2380    fs-etcd02.jinkit.io.
_etcd-server._tcp.jinkit.io.   300     IN      SRV 0 0 2380    fs-etcd01.jinkit.io.
_etcd-server._tcp.jinkit.io.   300     IN      SRV 0 0 2380    kubetcd01.jinkit.io.

; Flagship: Kubernetes ETCD Client SRV Records
_etcd-server._tcp.jinkit.io.   300     IN      SRV 0 0 2379    fs-etcd05.jinkit.io.
_etcd-server._tcp.jinkit.io.   300     IN      SRV 0 0 2379    fs-etcd04.jinkit.io.
_etcd-server._tcp.jinkit.io.   300     IN      SRV 0 0 2379    fs-etcd03.jinkit.io.
_etcd-server._tcp.jinkit.io.   300     IN      SRV 0 0 2379    fs-etcd02.jinkit.io.
_etcd-server._tcp.jinkit.io.   300     IN      SRV 0 0 2379    fs-etcd01.jinkit.io.
_etcd-client._tcp.jinkit.io.   300     IN      SRV 0 0 2379    kubetcd01.jinkit.io.

; Flagship: ETCD Member A Records
kubetcd01             IN      A       192.168.3.21

; Flagship: Kubernetes Member A Records
fs-etcd05             IN      A       192.168.3.25
fs-etcd04             IN      A       192.168.3.24
fs-etcd03             IN      A       192.168.3.23
fs-etcd02             IN      A       192.168.3.22
fs-etcd01             IN      A       192.168.3.21
kubernetes             IN      A       192.168.3.21

; Flagship: Kubernetes/ETCD Member A Records
*.apps                  IN      CNAME   master

; Flagship: Custom User Provided Entries
openshift               IN      A       192.168.1.40
master                  IN      A       192.168.1.40
galvatron               IN      A       192.168.70.25
openstack               IN      A       192.168.70.21
quay                    IN      A       172.29.248.34
kubenode01              IN      A       192.168.3.21
kubenode02              IN      A       192.168.3.22
kubenode03              IN      A       192.168.3.23
kubenode04              IN      A       192.168.3.24
kubenode05              IN      A       192.168.3.25
RFC-1035-Style Domain File for the File Plugin

I am including a broad set of examples within this file. Again, if you're familiar with configuring a BIND server, I would suggest you try to play with these settings until you find what you're looking for.

Example

Putting this all together, you can create a script called coreup.sh and run this on any Linux system. I have a couple of suggestions though.

  • If you're using an ARM-based device, like a Raspberry Pi, make sure to look for the appropriate CoreDNS image (tag) that matches your system.
  • If you're running this on macOS, be sure to configure your volumes correctly before running this script.
  • Edit the variables used at the top of the script. They're important. They will automatically configure the domain, and some additional examples for you.
  • Make sure to read the script carefully. Don't just run things you find on the internet unless you know what you're doing.
  • Be sure to restart the container if you change your domain file. CoreDNS will not automatically pick up changes for the file plugin. If you want changes to be picked up automatically, take a look at the etcd plugin below.

This script will do the following:

  1. Set useful variables for the script (read through the top part of the script)
  2. Create the directory for the Corefile and example.com domain files
  3. tee input of Corefile and example.com and redirects them to files
  4. Stops the running CoreDNS container (if one has been created/running already)
  5. Tests the running container
  6. Adds additional SRV records as a test
  7. Restarts the container
  8. Tests the additional SRV records

Please feel free to modify and use this script for your own environment. I have not made many changes to mine. Please take note of the name of the running container as an example of this.

#!/bin/bash
## Prepare any variables used for this script:
export network_endpoints_dns_coredir="$(pwd)/scripts/deployments/coredns"
export network_endpoints_dns_fqdn="jinkit.io"
export network_endpoints_dns_forewarder="8.8.8.8"
export network_endpoints_dns_kubernetes_api="kubernetes"
export node_bootstrap_addr=("192.168.3.21")
export network_endpoints_dns_bootstrap_name="kubetcd01"
export node_master_addr0=("192.168.3.21")
export node_master_addr1=("192.168.3.22")
export node_master_addr2=("192.168.3.23")
export node_master_addr3=("192.168.3.24")
export node_master_addr4=("192.168.3.25")
export node_master_dns_name0=("fs-etcd01")
export node_master_dns_name1=("fs-etcd02")
export node_master_dns_name2=("fs-etcd03")
export node_master_dns_name3=("fs-etcd04")
export node_master_dns_name4=("fs-etcd05")
export node_registry_addr0=("172.29.248.34")
export node_sample1_name=("galvatron")
export node_sample2_name=("openstack")
export node_sample1_addr0=("192.168.70.25")
export node_sample2_addr0=("192.168.70.21")

# Prepare directories for Coredns Corefile and custom domain files:
mkdir -p ${network_endpoints_dns_coredir}

# Write out CoreDNS Domain file:
rm -rf ${network_endpoints_dns_coredir}/Corefile
cat << EOF | tee -a ${network_endpoints_dns_coredir}/Corefile
${network_endpoints_dns_fqdn}:53 {
    log stdout
    file /data/${network_endpoints_dns_fqdn}
}
.:53 {
    proxy . ${network_endpoints_dns_forewarder}:53
    log stdout
}
EOF

# Write out CoreDNS Domain file:
rm -rf ${network_endpoints_dns_coredir}/${network_endpoints_dns_fqdn}
cat << EOF | tee -a ${network_endpoints_dns_coredir}/${network_endpoints_dns_fqdn}
\$TTL    1M
\$ORIGIN ${network_endpoints_dns_fqdn}.

${network_endpoints_dns_fqdn}.		    IN	SOA	sns.dns.icann.org. noc.dns.icann.org. 2015082541 7200 3600 1209600 3600
${network_endpoints_dns_fqdn}.		    IN	NS	b.iana-servers.net.
${network_endpoints_dns_fqdn}.		    IN	NS	a.iana-servers.net.
${network_endpoints_dns_fqdn}.		    IN	A	127.0.0.1

; Flagship: Test A Record
test.${network_endpoints_dns_fqdn}.	    IN	A	${node_bootstrap_addr}

; Flagship: Test TXT Record
text.${network_endpoints_dns_fqdn}.	    IN	TXT	"This is a test text record"

; Flagship: Test CNAME Record
cname.${network_endpoints_dns_fqdn}.	IN	CNAME	www.jinkit.net.

; Flagship: Test SRV Record
service.${network_endpoints_dns_fqdn}.	IN	SRV	8080 10 10 ${network_endpoints_dns_fqdn}.

; Flagship: Kubernetes ETCD Server SRV Records
_etcd-server._tcp.${network_endpoints_dns_fqdn}.   300     IN      SRV 0 0 2380    ${network_endpoints_dns_bootstrap_name}.${network_endpoints_dns_fqdn}.

; Flagship: Kubernetes ETCD Client SRV Records
_etcd-client._tcp.${network_endpoints_dns_fqdn}.   300     IN      SRV 0 0 2379    ${network_endpoints_dns_bootstrap_name}.${network_endpoints_dns_fqdn}.

; Flagship: ETCD Member A Records
${network_endpoints_dns_bootstrap_name}             IN      A       ${node_bootstrap_addr}

; Flagship: Kubernetes Member A Records
${network_endpoints_dns_kubernetes_api}             IN      A       ${node_bootstrap_addr}

; Flagship: Kubernetes/ETCD Member A Records
*.apps                  IN      CNAME   master

; Flagship: Custom User Provided Entries
openshift               IN      A       192.168.1.40
master                  IN      A       192.168.1.40
${node_sample1_name}               IN      A       ${node_sample1_addr0}	
${node_sample2_name}               IN      A       ${node_sample2_addr0}	
quay                    IN      A       ${node_registry_addr0}
kubenode01              IN      A       ${node_master_addr0}
kubenode02              IN      A       ${node_master_addr1}
kubenode03              IN      A       ${node_master_addr2}
kubenode04              IN      A       ${node_master_addr3}
kubenode05              IN      A       ${node_master_addr4}
EOF


# Run docker command:
docker stop flagship_coredns && docker rm flagship_coredns
docker run -d \
  --restart=always \
  --name flagship_coredns \
  --privileged \
  -v ${network_endpoints_dns_coredir}:/data:ro \
  -p "53:53/udp" -p "53:53/tcp" -p "9153:9153/tcp" \
  --cap-drop=all --cap-add=net_bind_service \
  coredns/coredns:1.2.5 -conf /data/Corefile

## Testing:
dig ${network_endpoints_dns_kubernetes_api}.${network_endpoints_dns_fqdn} @127.0.0.1
dig ${node_master_addr3} @127.0.0.1
dig srv _etcd-server._tcp.${network_endpoints_dns_fqdn}. @127.0.0.1

## Changes:
### A Records:
sed -i 's/.*Flagship: Kubernetes Member A Records.*/&\n'${node_master_dns_name0}'             IN      A       '${node_master_addr0}'/' ${network_endpoints_dns_coredir}/${network_endpoints_dns_fqdn}
sed -i 's/.*Flagship: Kubernetes Member A Records.*/&\n'${node_master_dns_name1}'             IN      A       '${node_master_addr1}'/' ${network_endpoints_dns_coredir}/${network_endpoints_dns_fqdn}
sed -i 's/.*Flagship: Kubernetes Member A Records.*/&\n'${node_master_dns_name2}'             IN      A       '${node_master_addr2}'/' ${network_endpoints_dns_coredir}/${network_endpoints_dns_fqdn}
sed -i 's/.*Flagship: Kubernetes Member A Records.*/&\n'${node_master_dns_name3}'             IN      A       '${node_master_addr3}'/' ${network_endpoints_dns_coredir}/${network_endpoints_dns_fqdn}
sed -i 's/.*Flagship: Kubernetes Member A Records.*/&\n'${node_master_dns_name4}'             IN      A       '${node_master_addr4}'/' ${network_endpoints_dns_coredir}/${network_endpoints_dns_fqdn}

### SRV Server Records:
sed -i 's/.*Flagship: Kubernetes ETCD Server SRV Records.*/&\n_etcd-server._tcp.'${network_endpoints_dns_fqdn}'.   300     IN      SRV 0 0 2380    '${node_master_dns_name0}'.'${network_endpoints_dns_fqdn}.'/' ${network_endpoints_dns_coredir}/${network_endpoints_dns_fqdn}
sed -i 's/.*Flagship: Kubernetes ETCD Server SRV Records.*/&\n_etcd-server._tcp.'${network_endpoints_dns_fqdn}'.   300     IN      SRV 0 0 2380    '${node_master_dns_name1}'.'${network_endpoints_dns_fqdn}.'/' ${network_endpoints_dns_coredir}/${network_endpoints_dns_fqdn}
sed -i 's/.*Flagship: Kubernetes ETCD Server SRV Records.*/&\n_etcd-server._tcp.'${network_endpoints_dns_fqdn}'.   300     IN      SRV 0 0 2380    '${node_master_dns_name2}'.'${network_endpoints_dns_fqdn}.'/' ${network_endpoints_dns_coredir}/${network_endpoints_dns_fqdn}
sed -i 's/.*Flagship: Kubernetes ETCD Server SRV Records.*/&\n_etcd-server._tcp.'${network_endpoints_dns_fqdn}'.   300     IN      SRV 0 0 2380    '${node_master_dns_name3}'.'${network_endpoints_dns_fqdn}.'/' ${network_endpoints_dns_coredir}/${network_endpoints_dns_fqdn}
sed -i 's/.*Flagship: Kubernetes ETCD Server SRV Records.*/&\n_etcd-server._tcp.'${network_endpoints_dns_fqdn}'.   300     IN      SRV 0 0 2380    '${node_master_dns_name4}'.'${network_endpoints_dns_fqdn}.'/' ${network_endpoints_dns_coredir}/${network_endpoints_dns_fqdn}

### SRV Client Records:
sed -i 's/.*Flagship: Kubernetes ETCD Client SRV Records.*/&\n_etcd-server._tcp.'${network_endpoints_dns_fqdn}'.   300     IN      SRV 0 0 2379    '${node_master_dns_name0}'.'${network_endpoints_dns_fqdn}.'/' ${network_endpoints_dns_coredir}/${network_endpoints_dns_fqdn}
sed -i 's/.*Flagship: Kubernetes ETCD Client SRV Records.*/&\n_etcd-server._tcp.'${network_endpoints_dns_fqdn}'.   300     IN      SRV 0 0 2379    '${node_master_dns_name1}'.'${network_endpoints_dns_fqdn}.'/' ${network_endpoints_dns_coredir}/${network_endpoints_dns_fqdn}
sed -i 's/.*Flagship: Kubernetes ETCD Client SRV Records.*/&\n_etcd-server._tcp.'${network_endpoints_dns_fqdn}'.   300     IN      SRV 0 0 2379    '${node_master_dns_name2}'.'${network_endpoints_dns_fqdn}.'/' ${network_endpoints_dns_coredir}/${network_endpoints_dns_fqdn}
sed -i 's/.*Flagship: Kubernetes ETCD Client SRV Records.*/&\n_etcd-server._tcp.'${network_endpoints_dns_fqdn}'.   300     IN      SRV 0 0 2379    '${node_master_dns_name3}'.'${network_endpoints_dns_fqdn}.'/' ${network_endpoints_dns_coredir}/${network_endpoints_dns_fqdn}
sed -i 's/.*Flagship: Kubernetes ETCD Client SRV Records.*/&\n_etcd-server._tcp.'${network_endpoints_dns_fqdn}'.   300     IN      SRV 0 0 2379    '${node_master_dns_name4}'.'${network_endpoints_dns_fqdn}.'/' ${network_endpoints_dns_coredir}/${network_endpoints_dns_fqdn}

## Now restart the container:
docker restart flagship_coredns

## Testing:
dig ${network_endpoints_dns_kubernetes_api}.${network_endpoints_dns_fqdn} @127.0.0.1
dig ${node_master_addr3} @127.0.0.1
dig srv _etcd-server._tcp.${network_endpoints_dns_fqdn}. @127.0.0.1

I will add another section in the near future on how to use an etcd backed CoreDNS implementation very soon (probably next week). For now...have fun!

Let me know what you think in the comments section. Any thoughts, suggestions, corrections, or just general information is super helpful and very welcome!