Coredns

tags: c4lab server

Target

I want to setup a DNS server for alias all the server in the internal networks.

It's possible to use ansible to update all /etc/hosts in each server. However it is not a elegant way.

Thus, I setup the coredns and try to figure out the configuration for supporting all commonly used protocols (plain, https, tls) and test them one by one. Finally, I will set this DNS IP:port in our main router.

Setup Coredns

Github: https://github.com/coredns/coredns
Document: https://coredns.io/manual/toc/

How to setup:
Option 1. Running in Docker

docker pull docker.io/coredns/coredns
docker run -it --rm -p 53:53/udp docker.io/coredns/coredns

Option 2. Compile and run the binary

git clone https://github.com/coredns/coredns
docker run -it --rm -v $PWD/coredns:/app -w /app golang:1.18 make
./coredns/coredns

Setup Testing tools dnslookup

https://github.com/ameshkov/dnslookup

I think this is the best tool, it cover all protocols and it is easy to use.

git clone https://github.com/ameshkov/dnslookup.git 
docker run -it --rm -v $PWD/dnslookup:/app -w /app golang:1.18 make

Test it on Cloudflare DNS

./dnslookup/dnslookup google.com https://cloudflare-dns.com/dns-query
./dnslookup/dnslookup google.com tls://one.one.one.one
./dnslookup/dnslookup google.com 1.1.1.1

Example output

dnslookup v. dev
dnslookup result:
;; opcode: QUERY, status: NOERROR, id: 43550
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0

;; QUESTION SECTION:
;google.com.    IN       A

;; ANSWER SECTION:
google.com.     263     IN      A       216.58.200.238

Coredns settings: plain DNS

Corefile

.:10053 {
    log
    hosts {
        192.168.208.206 rna
        192.168.208.206 exon
        fallthrough
    }
    rewrite name exact taiwania1 clogin1.twnia.nchc.org.tw
    forward . tls://1.1.1.1 {
        tls_servername cloudflare-dns.com 
    }
    whoami
}

You can custom your own alias in hosts blocks, it is the same format as /etc/hosts.

You can also use alias of name i.e. taiwania1 will response the IP of clogin1.twnia.nchc.org.tw.

If the query not match in hosts, it will catch the query and redirect to the upstream 1.1.1.1 (with TLS).

DNS: run and test

docker run parameters

docker run -it --rm -v $PWD/Corefile:/Corefile -p 10053:10053/tcp -p 10053:10053/udp docker.io/coredns/coredns

and test

apt install dnsutils
dig -p 10053 @c4lab.bime.ntu.edu.tw google.com
dig -p 10053 @c4lab.bime.ntu.edu.tw rna
nslookup -port=10053 google.com c4lab.bime.ntu.edu.tw

./dnslookup/dnslookup google.com c4lab.bime.ntu.edu.tw:10053

And coredns will logs:
[INFO] 10.0.2.100:45241 - 24292 "A IN google.com. udp 51 false 4096" NOERROR qr,rd,ra 478 0.089646393s

Coredns settings: DNS-over-TLS (DoT)

Append the Corefile with

tls://.:10054 {
    tls /opt/fullchain.pem /opt/privkey.pem
    forward . 127.0.0.1:10053
}

Our fullchain.pem and privkey.pem are got from Let's Encrypt me. You can set your own certifications and key file.

All the query will redirect to the plain DNS internally.

Run it and test

docker run parameters

docker run -it --rm -v $PWD/Corefile:/Corefile -p 10054:10054 -v $PWD/fullchain.pem:/opt/fullchain.pem -v $PWD/privkey.pem:/opt/privkey.pem docker.io/coredns/coredns

test

./dnslookup/dnslookup google.com tls://c4lab.bime.ntu.edu.tw:10054

# or kdig (see supplement)
apt install knot-dnsutils
kdig @c4lab.bime.ntu.edu.tw:10054 +tls-ca  google.com

Coredns settings: DNS-over-Http (DoH)

Append to Corefile with

https://.:10055 {
    tls /opt/fullchain.pem /opt/privkey.pem
    forward . 127.0.0.1:10053
}

The same as DoT, almost.

Run and test

docker parameters

docker run -it --rm -v $PWD/Corefile:/Corefile -p 10055:10055 -v $PWD/fullchain.pem:/opt/fullchain.pem -v $PWD/privkey.pem:/opt/privkey.pem docker.io/coredns/coredns

test

./dnslookup/dnslookup google.com https://c4lab.bime.ntu.edu.tw:10055/dns-query

# curl (see detail in supplement)
apt install curl
curl "https://c4lab.bime.ntu.edu.tw:10055/dns-query?dns=AAABAAABAAAAAAAABmdvb2dsZQNjb20AAAEAAQ" -k | xxd

Overall: Coredns setting

Corefile

.:10053 {
    whoami
    hosts {
        192.168.208.206 rna
        fallthrough
    }
    forward . tls://1.1.1.1 {
        tls_servername cloudflare-dns.com
    }
}

tls://.:10054 {
    tls /opt/fullchain.pem /opt/privkey.pem
    forward . 127.0.0.1:10053
}

https://.:10055 {
    tls /opt/fullchain.pem /opt/privkey.pem
    forward . 127.0.0.1:10053
}

and

docker run -it --rm \
    -p 10053:10053/tcp \
    -p 10053:10053/udp \
    -p 10054:10054 \
    -p 10055:10055 \
    -v $PWD/Corefile:/Corefile:ro \
    -v $PWD/coredns/coredns:/coredns:ro \
    -v $PWD/fullchain.pem:/opt/fullchain.pem:ro \
    -v $PWD/privkey.pem:/opt/privkey.pem:ro \
    docker.io/coredns/coredns

log is removed in production

Supplements

DoT https://datatracker.ietf.org/doc/rfc7858/
DoH https://datatracker.ietf.org/doc/rfc8484/

Test DoT by kdig

https://www.knot-dns.cz/

apt install knot-dnsutils
kdig -d @1.1.1.1 +tls-ca +tls-host=cloudflare-dns.com google.com

Example Output

;; TLS session (TLS1.3)-(ECDHE-X25519)-(ECDSA-SECP256R1-SHA256)-(AES-256-GCM)
;; ->>HEADER<<- opcode: QUERY; status: NOERROR; id: 56882
;; Flags: qr rd ra; QUERY: 1; ANSWER: 1; AUTHORITY: 0; ADDITIONAL: 1

;; EDNS PSEUDOSECTION:
;; Version: 0; flags: ; UDP size: 1232 B; ext-rcode: NOERROR
;; PADDING: 409 B

;; QUESTION SECTION:
;; google.com.                  IN      A

;; ANSWER SECTION:
google.com.             52      IN      A       142.250.66.110

;; Received 468 B
;; Time 2022-09-27 15:51:43 UTC
;; From 1.1.1.1@853(TCP) in 32.9 ms

Test DoH by curl

apt install curl

curl "https://cloudflare-dns.com/dns-query?dns=AAABAAABAAAAAAAABmdvb2dsZQNjb20AAAEAAQ" -k | xxd

output

00000000: 0000 8180 0001 0001 0000 0000 0667 6f6f  .............goo
00000010: 676c 6503 636f 6d00 0001 0001 c00c 0001  gle.com.........
00000020: 0001 0000 004d 0004 d83a c8ee            .....M...:..

The query sequence dns=... and the output is base62 encoded sequences (I don't know either).

However, Cloudflare provided anthor way to make the query in json format.
But this method doesn't support by coredns. issue

apt install jq
curl -H 'accept: application/dns-json' 'https://cloudflare-dns.com/dns-query?name=google.com&type=A' | jq

output

{
  "Status": 0,
  "TC": false,
  "RD": true,
  "RA": true,
  "AD": false,
  "CD": false,
  "Question": [
    {
      "name": "google.com",
      "type": 1
    }
  ],
  "Answer": [
    {
      "name": "google.com",
      "type": 1,
      "TTL": 151,
      "data": "142.250.66.142"
    }
  ]
}

Test DoH by doh

https://github.com/curl/doh

doh is another tool that can query in json format.

Thus, It doesn't support to query to coredns.

git clone https://github.com/curl/doh
docker run -it --rm -v $PWD/doh:/app -w /app golang:1.18 bash
  apt update
  apt install libcurl4-openssl-dev
  make
  exit
  
./doh/doh google.com https://cloudflare-dns.com/dns-query

output

[google.com]
TTL: 276 seconds
A: 142.250.66.142
AAAA: 2404:6800:4005:080d:0000:0000:0000:200e