Overview

DNS is a very important service and topic in networks, and it’s frequently used in daily work. Previously, I wrote an article introducing DNS and system settings: DNS and related configuration files on Linux, but today I want to talk about deploying a DNS server myself.

Before, I also wrote a tutorial on building a DNS server: Setting up a local DNS server on Mac. However, in that article, I used DNSMasq, which I also used in my previous job because our product was more IaaS-oriented, so the choice was relatively traditional. Recently, since our product is more cloud-native, we switched to CoreDNS. So, I’ll provide a brief summary here.

CoreDNS Concept

The implementation of CoreDNS differs from other DNS servers (I copied this from the documentation; I haven’t actually looked into the specifics of other servers 😂). CoreDNS is characterized by its flexibility, which comes from its plugin system. This means CoreDNS delegates most of its functions to various plugins, which can be configured and combined to achieve the desired functionality.

In CoreDNS, there are two very important configurations related to plugins:

Plugins

Plugins are a core concept of CoreDNS. They are essentially pieces of code that implement a specific interface. Whenever a DNS query comes in, the configured plugins are called and passed three parameters:

The required plugins are configured in plugin.cfg at compile time. It’s important to note that the order of plugins in this file is significant because it determines their order of execution at runtime. Although plugins are also defined in the Corefile, that order is not meaningful; the real order is defined in plugin.cfg. For example, here’s an official configuration:

  1. [root@liqiang.io]# cat plugin.cfg
  2. root:root
  3. metadata:metadata
  4. geoip:geoip
  5. cancel:cancel
  6. tls:tls
  7. timeouts:timeouts
  8. reload:reload
  9. nsid:nsid
  10. bufsize:bufsize
  11. bind:bind
  12. debug:debug
  13. trace:trace
  14. ready:ready
  15. health:health
  16. pprof:pprof
  17. prometheus:metrics
  18. errors:errors
  19. log:log
  20. dnstap:dnstap
  21. local:local
  22. dns64:dns64
  23. acl:acl
  24. any:any
  25. chaos:chaos
  26. loadbalance:loadbalance
  27. tsig:tsig
  28. cache:cache
  29. rewrite:rewrite
  30. header:header
  31. dnssec:dnssec
  32. autopath:autopath
  33. minimal:minimal
  34. template:template
  35. transfer:transfer
  36. hosts:hosts
  37. route53:route53
  38. azure:azure
  39. clouddns:clouddns
  40. k8s_external:k8s_external
  41. kubernetes:kubernetes
  42. file:file
  43. auto:auto
  44. secondary:secondary
  45. etcd:etcd
  46. loop:loop
  47. forward:forward
  48. grpc:grpc
  49. erratic:erratic
  50. whoami:whoami
  51. on:github.com/coredns/caddy/onevent
  52. sign:sign
  53. view:view

Configuration

By default, CoreDNS will look for a Corefile in the running directory as the startup configuration, but you can specify the startup configuration with -conf. The Corefile configuration is quite simple and is composed of multiple Server Blocks (though you can specify only one or none). Each Server Block follows the pattern:

  1. <zones> {
  2. # Plugins defined here.
  3. }

As you can see, a Server Block contains two parts: zones and plugins:

Here is an example configuration:

  1. [root@liqiang.io]# cat Corefile
  2. dns://google.com:53 {
  3. errors
  4. forward . 8.8.8.8
  5. }
  6. # 兜底配置,前面没有满足的都会使用这个配置
  7. dns://.:53 {
  8. errors
  9. forward . 223.5.5.5:53
  10. }

Practice

Installation

CoreDNS supports multiple installation methods, which can generally be divided into precompiled binaries and containers. Typically, we run CoreDNS in a Kubernetes (K8S) environment, so using containers is more common. Additionally, we may need to run it locally to debug issues. Here’s a simple example of running CoreDNS using Docker:

  1. [root@liqiang.io]# docker run -d --rm --name coredns \
  2. --volume=/tmp/coredns:/root/ \
  3. -p 53:53/udp \
  4. coredns/coredns \
  5. -conf /root/Corefile

With this, we have a running instance of CoreDNS. Since CoreDNS is a stateless service, we can quickly scale it horizontally without needing additional configuration.

Verification

Once CoreDNS is running, you can verify it by using DNS-related commands to specify the DNS server. For example, you can use dig to test:

  1. [root@liqiang.io]# dig @127.0.0.1 google.com
  2. ; <<>> DiG xxxxxx <<>> @127.0.0.1 google.com
  3. ; (1 server found)
  4. ;; global options: +cmd
  5. ;; Got answer:
  6. ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 30520
  7. ;; flags: qr rd ra; QUERY: 1, ANSWER: 6, AUTHORITY: 0, ADDITIONAL: 1
  8. ;; OPT PSEUDOSECTION:
  9. ; EDNS: version: 0, flags:; udp: 1232
  10. ; COOKIE: 754b24dbebe1a964 (echoed)
  11. ;; QUESTION SECTION:
  12. ;google.com. IN A
  13. ;; ANSWER SECTION:
  14. google.com. 30 IN A 142.251.175.113
  15. google.com. 30 IN A 142.251.175.102
  16. google.com. 30 IN A 142.251.175.101
  17. google.com. 30 IN A 142.251.175.138
  18. google.com. 30 IN A 142.251.175.139
  19. google.com. 30 IN A 142.251.175.100
  20. ;; Query time: 11 msec
  21. ;; SERVER: 127.0.0.1#53(127.0.0.1) (UDP)
  22. ;; WHEN: Mon Jul 22 00:20:15 UTC 2024
  23. ;; MSG SIZE rcvd: 207