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:
plugin.cfg
: This is the configuration file used at compile time to determine which CoreDNS plugins are needed.Corefile
: This is the runtime configuration used to determine how CoreDNS responds to DNS query requests.
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:
context.Context
dns.ResponseWriter
: Used to write response data.*dns.Msg
: DNS request Client information.
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:
[root@liqiang.io]# cat plugin.cfg
root:root
metadata:metadata
geoip:geoip
cancel:cancel
tls:tls
timeouts:timeouts
reload:reload
nsid:nsid
bufsize:bufsize
bind:bind
debug:debug
trace:trace
ready:ready
health:health
pprof:pprof
prometheus:metrics
errors:errors
log:log
dnstap:dnstap
local:local
dns64:dns64
acl:acl
any:any
chaos:chaos
loadbalance:loadbalance
tsig:tsig
cache:cache
rewrite:rewrite
header:header
dnssec:dnssec
autopath:autopath
minimal:minimal
template:template
transfer:transfer
hosts:hosts
route53:route53
azure:azure
clouddns:clouddns
k8s_external:k8s_external
kubernetes:kubernetes
file:file
auto:auto
secondary:secondary
etcd:etcd
loop:loop
forward:forward
grpc:grpc
erratic:erratic
whoami:whoami
on:github.com/coredns/caddy/onevent
sign:sign
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:
<zones> {
# Plugins defined here.
}
As you can see, a Server Block contains two parts: zones
and plugins
:
zones
: The type of domain names, protocol, and port to listen on- For example, if you want to listen to all domain names, you can use
.
- If you are only interested in the google domain, you can use
google.com
- If you are only interested in DNS queries over HTTPS (HoD):
https://google.com
- For example, if you want to listen to all domain names, you can use
plugins
:- For the domains that match the
zones
condition, you can specify which plugins to use to respond to these domains. You can list the plugins here, but note that while you can arrange this list in any order, the actual execution order is determined byplugin.cfg
.
- For the domains that match the
Here is an example configuration:
[root@liqiang.io]# cat Corefile
dns://google.com:53 {
errors
forward . 8.8.8.8
}
# 兜底配置,前面没有满足的都会使用这个配置
dns://.:53 {
errors
forward . 223.5.5.5:53
}
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:
[root@liqiang.io]# docker run -d --rm --name coredns \
--volume=/tmp/coredns:/root/ \
-p 53:53/udp \
coredns/coredns \
-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:
[root@liqiang.io]# dig @127.0.0.1 google.com
; <<>> DiG xxxxxx <<>> @127.0.0.1 google.com
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 30520
;; flags: qr rd ra; QUERY: 1, ANSWER: 6, AUTHORITY: 0, ADDITIONAL: 1
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1232
; COOKIE: 754b24dbebe1a964 (echoed)
;; QUESTION SECTION:
;google.com. IN A
;; ANSWER SECTION:
google.com. 30 IN A 142.251.175.113
google.com. 30 IN A 142.251.175.102
google.com. 30 IN A 142.251.175.101
google.com. 30 IN A 142.251.175.138
google.com. 30 IN A 142.251.175.139
google.com. 30 IN A 142.251.175.100
;; Query time: 11 msec
;; SERVER: 127.0.0.1#53(127.0.0.1) (UDP)
;; WHEN: Mon Jul 22 00:20:15 UTC 2024
;; MSG SIZE rcvd: 207