CoreDNS[1] 是 Golang 編寫的一個插件式 DNS 伺服器,是 Kubernetes 1.13 後所內置的默認 DNS 伺服器。CoreDNS 的目標是成為 cloud-native 環境下的 DNS 伺服器和服務發現解決方案,即:
Our goal is to make CoreDNS the cloud-native DNS server and service discovery solution.
它有以下幾個特性:
插件化(Plugins)
基於 Caddy 伺服器框架,CoreDNS 實現了一個插件鏈的架構,將大量應用端的邏輯抽象成 plugin 的形式(如 Kubernetes 的 DNS 服務發現,Prometheus 監控等)暴露給使用者。CoreDNS 以預配置的方式將不同的 plugin 串成一條鏈,按序執行 plugin 的邏輯。從編譯層面,用戶選擇所需的 plugin 編譯到最終的可執行文件中,使得運行效率更高。CoreDNS 採用 Go 編寫,所以從具體代碼層面來看,每個 plugin 其實都是實現了其定義的 interface 的組件而已。第三方只要按照 CoreDNS Plugin API 去編寫自定義插件,就可以很方便地集成於 CoreDNS。
配置簡單化
引入表達力更強的 DSL[2],即 Corefile 形式的配置文件(也是基於 Caddy 框架開發)。
一體化的解決方案
區別於 kube-dns,CoreDNS 編譯出來就是一個單獨的二進位可執行文件,內置了 cache,backend storage,health check 等功能,無需第三方組件來輔助實現其他功能,從而使得部署更方便,內存管理更為安全。
其實從功能角度來看,CoreDNS 更像是一個通用 DNS 方案(類似於 BIND),然後通過插件模式來極大地擴展自身功能,從而可以適用於不同的場景(比如 Kubernetes)。正如官方博客所說:
CoreDNS is powered by plugins.
1. Corefile 介紹Corefile 是 CoreDNS 的配置文件(源於 Caddy 框架的配置文件 Caddyfile),它定義了:
server 以什麼協議監聽在哪個埠(可以同時定義多個 server 監聽不同埠)server 負責哪個 zone 的權威(authoritative)DNS 解析常見地,一個典型的 Corefile 格式如下所示:
ZONE:[PORT] {
[PLUGIN] ...
}
ZONE : 定義 server 負責的 zone,PORT 是可選項,默認為 53;PLUGIN : 定義 server 所要加載的 plugin。每個 plugin 可以有多個參數;比如:
. {
chaos CoreDNS-001
}上述配置文件表達的是:server 負責根域 . 的解析,其中 plugin 是 chaos 且沒有參數。
定義 server一個最簡單的配置文件可以為:
.{}即 server 監聽 53 埠並不使用插件。**如果此時在定義其他 server,要保證監聽埠不衝突;如果是在原來 server 增加 zone,則要保證 zone 之間不衝突,**如:
. {}
.:54 {}另一個 server 運行於 54 埠並負責根域 . 的解析。
又如:
example.org {
whoami
}
org {
whoami
}同一個 server 但是負責不同 zone 的解析,有不同插件鏈。
定義 Reverse Zone跟其他 DNS 伺服器類似,Corefile 也可以定義 Reverse Zone(反向解析 IP 地址對應的域名):
0.0.10.in-addr.arpa {
whoami
}或者簡化版本:
10.0.0.0/24 {
whoami
}可以通過 dig 進行反向查詢:
$ dig -x 10.0.0.1
使用不同的通信協議CoreDNS 除了支持 DNS 協議,也支持 TLS 和 gRPC,即 DNS-over-TLS[3] 和 DNS-over-gRPC 模式:
tls://example.org:1443 {
#...
}
2. 插件的工作模式當 CoreDNS 啟動後,它將根據配置文件啟動不同 server ,每臺 server 都擁有自己的插件鏈。當有 DNS 請求時,它將依次經歷如下 3 步邏輯:
如果有當前請求的 server 有多個 zone,將採用貪心原則選擇最匹配的 zone;一旦找到匹配的 server,按照 plugin.cfg[4] 定義的順序執行插件鏈上的插件;每個插件將判斷當前請求是否應該處理,將有以下幾種可能:請求被當前插件處理
插件將生成對應的響應並回給客戶端,此時請求結束,下一個插件將不會被調用,如 whoami 插件;
請求被當前插件以 Fallthrough 形式處理
如果請求在該插件處理過程中有可能將跳轉至下一個插件,該過程稱為 fallthrough,並以關鍵字 fallthrough 來決定是否允許此項操作,例如 host 插件,當查詢域名未位於 /etc/hosts,則調用下一個插件;
請求在處理過程被攜帶 Hint
請求被插件處理,並在其響應中添加了某些信息(hint)後繼續交由下一個插件處理。這些額外的信息將組成對客戶端的最終響應,如 metric 插件;
3. CoreDNS 如何處理 DNS 請求如果 Corefile 為:
coredns.io:5300 {
file db.coredns.io
}
example.io:53 {
log
errors
file db.example.io
}
example.net:53 {
file db.example.net
}
.:53 {
kubernetes
proxy . 8.8.8.8
log
health
errors
cache
}從配置文件來看,我們定義了兩個 server(儘管有 4 個區塊),分別監聽在 5300 和 53 埠。其邏輯圖可如下所示:
每個進入到某個 server 的請求將按照 plugin.cfg 定義順序執行其已經加載的插件。
從上圖,我們需要注意以下幾點:
儘管在 .:53 配置了 health 插件,但是它並為在上面的邏輯圖中出現,原因是:該插件並未參與請求相關的邏輯(即並沒有在插件鏈上),只是修改了 server 配置。更一般地,我們可以將插件分為兩種:Normal 插件:參與請求相關的邏輯,且插入到插件鏈中;其他插件:不參與請求相關的邏輯,也不出現在插件鏈中,只是用於修改 server 的配置,如 health,tls 等插件;4. 配置 CoreDNS既然 CoreDNS 如此優秀,我用它來抵禦偉大的防火長城豈不美哉?研究了一圈,發現技術上還是可行的,唯一的一個缺點是不支持使用代理,不過你可以通過 proxychians-ng[5] 或 proxifier[6] 來強制使用代理。下面開始折騰。
具體的思路其實非常簡單,就是將國內的域名查詢請求轉發到 114 等國內的公共 DNS 伺服器,將國外的域名查詢請求轉發到 8.8.8.8 等國外的公共 DNS 伺服器。然而 CoreDNS 的插件鏈有點反直覺,同一個插件鏈上的每一個插件只能出現一次,如果只使用 forward 插件是滿足不了需求的。
CoreDNS 原來還有個插件叫 proxy,功能和 forward 類似,目測好像同時利用 proxy 和 forward 插件就可以實現咱的需求了。但理想與現實的差距總是很大,不知道從什麼時候開始,CoreDNS 官方編譯的二進位文件已經沒有 proxy 插件了,真是氣人。
dnsredir偶然間發現了一個第三方插件 dnsredir[7],目測可以解決我的所有問題。該插件綜合了 proxy 和 forward 插件的所有優點,支持 UDP、TCP、DNS-over-TLS 和 DNS-over-HTTPS,也支持多個後端,還具備健康檢查和故障轉移的功能,真是太香了!
它的語法是這樣的:
dnsredir FROM... {
to TO...
}
FROM... 是一個文件列表,包含了匹配的域名和解析該域名的伺服器,說白了就是 dnsmasq 所使用的格式,直接看例子:server=/0-100.com/114.114.114.114
server=/0-100.com/114.114.114.114為什麼要用這種格式呢?當然是為了方便啦。
為什麼這樣會方便呢?當然是為了可以直接用上 FelixOnMars的大陸區域名列表[8]了。。。FelixOnMars 同時還提供了 Google 和 Apple 的域名列表,這在某些地區某些ISP可以得到國內鏡像的 IP,從而加速訪問,想想就刺激。
當然,除了使用文件列表外,還可以使用 .,類似於上面所說的根域。這個插件最大的亮點是可以在插件鏈中重複使用 dnsredir 插件,只要 FROM... 不重複就行。
to TO... 用來將 DNS 解析請求發給上遊 DNS 伺服器。支持幾乎所有 DNS 協議,例如:
dns://1.1.1.1
8.8.8.8
tcp://9.9.9.9
udp://2606:4700:4700::1111
tls://1.1.1.1@one.one.one.one
tls://8.8.8.8
tls://dns.quad9.net
doh://cloudflare-dns.com/dns-query
json-doh://1.1.1.1/dns-query
json-doh://dns.google/resolve
ietf-doh://dns.quad9.net/dns-query
增強版 CoreDNSdnsredir 雖香,但大家別忘了,它是第三方插件,官方默認的二進位文件是不包含該插件的。你可以選擇自己編譯,但如果經常需要升級怎麼辦?總不能每次都手動編譯吧,也太累了。
好在有位大佬已經通過 CI/CD 流程將所需的第三方插件都集成編譯進去了,並定期更新,簡直就是我等的福音。大佬的項目地址為:
https://github.com/missdeer/coredns_custom_build[9]現在只需要下載對應作業系統的二進位文件,到處拷貝,就可以運行了。
下面統統以 MacOS 為例作講解。Openwrt 的玩法也一樣,參考本文的方法論即可,具體本文就不展開了。
直接下載二進位文件:
$ wget 'https://appveyorcidatav2.blob.core.windows.net/missdeer-15199/coredns-custom-build/1-7-1-514/idbodwxwywg1xgdg/distrib/coredns-linux-amd64.zip?sv=2015-12-11&sr=c&sig=BhMWcOVtDuaETyz2DcjpOr9GdvkpNVOqoIa7iWFpFNQ%3D&st=2020-12-23T15%3A26%3A19Z&se=2020-12-23T15%3A32%3A19Z&sp=r'
$ $ tar zxf coredns-linux-amd64.zip
$ mv coredns-linux-amd64/coredns /usr/local/bin/
配置要深入了解 CoreDNS,請查看其文檔[10],及 plugins 的介紹[11]。下面是我的配置文件:
cat > /usr/local/etc/Corefile <<EOF
# https://coredns.io/plugins/cache/
(global_cache) {
cache {
# [5, 60]
success 65536 3600 300
# [1, 10]
denial 8192 600 60
prefetch 1 60m 10%
}
}
.:7913 {
ads {
default-lists
blacklist https://raw.githubusercontent.com/privacy-protection-tools/anti-AD/master/anti-ad-domains.txt
whitelist https://files.krnl.eu/whitelist.txt
log
auto-update-interval 24h
list-store ads-cache
}
errors
hosts {
fallthrough
}
health
prometheus :9153
import global_cache
template ANY AAAA {
rcode NXDOMAIN
}
dnsredir accelerated-domains.china.conf google.china.conf apple.china.conf mydns.conf {
expire 15s
max_fails 3
health_check 3s
policy round_robin
path_reload 2s
to 114.114.114.114 223.5.5.5 119.29.29.29
}
dnsredir . {
expire 60s
max_fails 5
health_check 5s
policy random
spray
to tls://8.8.8.8@dns.google tls://8.8.4.4@dns.google
to tls://1.1.1.1@1dot1dot1dot1.cloudflare-dns.com tls://1.0.0.1@1dot1dot1dot1.cloudflare-dns.com
# Global TLS server name
# tls_servername cloudflare-dns.com
}
log
loop
reload 6s
}
EOF
hosts : hosts 是 CoreDNS 的一個 plugin,這一節的意思是加載 /etc/hosts 文件裡面的解析信息。hosts 在最前面,則如果一個域名在 hosts 文件中存在,則優先使用這個信息返回;fallthrough : 如果 hosts 中找不到,則進入下一個 plugin 繼續。缺少這一個指令,後面的 plugins 配置就無意義了;cache : 溯源得到的結果,緩存指定時間。類似 TTL 的概念;reload : 多久掃描配置文件一次。如有變更,自動加載;dnsredir : 這是重點插件。第一段 dnsredir 配置使用了 4 個文件列表,均是 FelixOnMars的大陸區域名列表[12],這裡我還加了一個自定義的文件列表 mydns.conf。第二段 dnsredir 配置表示默認的解析配置,可以理解為故障轉移,如果某個域名沒有匹配到任何一個文件列表,就使用第二段 dnsredir 的上遊 DNS 伺服器進行解析。通過這樣的配置方式,就實現了將國內的域名查詢請求轉發到 114 等國內的公共 DNS 伺服器,將國外的域名查詢請求轉發到 8.8.8.8 等國外的公共 DNS 伺服器。講一下我自己的理解:
最外面一級的大括號,對應『服務』的概念。多個服務可以共用一個埠;往裡面一級的大括號,對應 plugins 的概念,每一個大括號都是一個 plugin。這裡可以看出,plugins 是 CoreDNS 的一等公民;服務之間順序有無關聯沒有感覺,但 plugins 之間是嚴重順序相關的。某些 plugin 必須用 fallthrough 關鍵字流向下一個 plugin;從 plugins[13] 頁面的介紹看,CoreDNS 的功能還是很強的,既能輕鬆從 bind 遷移,還能兼容 old-style dns server 的運維習慣;從 CoreDNS 的性能指標看,適合做大型服務。注意:該方案的前提是能夠強制讓 CoreDNS 使用代理,或者更精確一點,讓 8.8.8.8 和 8.8.4.4 使用代理。這裡的方法比較複雜一點,本文就不介紹了。如果你實在不知道怎麼辦,可以將 8.8.8.8 這一行刪除,直接使用 Cloudflare 提供的 DNS 服務,雖然響應有點慢,但好在可以訪問。
如果你無法忍受 Cloudflare 的響應速度,可以考慮使用國內的無汙染 DNS:紅魚 DNS[14]。然後直接一勞永逸:
cat > /usr/local/etc/Corefile <<EOF
# https://coredns.io/plugins/cache/
(global_cache) {
cache {
# [5, 60]
success 65536 3600 300
# [1, 10]
denial 8192 600 60
prefetch 1 60m 10%
}
}
.:7913 {
ads {
default-lists
blacklist https://raw.githubusercontent.com/privacy-protection-tools/anti-AD/master/anti-ad-domains.txt
whitelist https://files.krnl.eu/whitelist.txt
log
auto-update-interval 24h
list-store ads-cache
}
errors
hosts {
fallthrough
}
health
prometheus :9153
import global_cache
template ANY AAAA {
rcode NXDOMAIN
}
dnsredir accelerated-domains.china.conf google.china.conf apple.china.conf mydns.conf {
expire 15s
max_fails 3
health_check 3s
policy round_robin
path_reload 2s
to 114.114.114.114 223.5.5.5 119.29.29.29
}
dnsredir . {
expire 60s
max_fails 5
health_check 5s
policy random
spray
to doh://13800000000.rubyfish.cn
}
log
loop
reload 6s
}
EOF這樣 CoreDNS 就不用擔心走代理的問題了。
定時更新國內域名列表大陸域名列表每天都會更新,所以還需要寫個腳本來更新文件列表。不用檢查文件是否存在了,直接簡單粗暴無腦更新:
$ cat > /usr/local/bin/update_coredns.sh <<EOF
#!/bin/bash
rm accelerated-domains.china.conf
wget https://cdn.jsdelivr.net/gh/felixonmars/dnsmasq-china-list/accelerated-domains.china.conf -O /usr/local/etc/accelerated-domains.china.conf
rm apple.china.conf
wget https://cdn.jsdelivr.net/gh/felixonmars/dnsmasq-china-list/apple.china.conf -O /usr/local/etc/apple.china.conf
rm google.china.conf
wget https://cdn.jsdelivr.net/gh/felixonmars/dnsmasq-china-list/google.china.conf -O /usr/local/etc/google.china.conf
EOF
$ sudo chmod +x /usr/local/bin/update_coredns.sh先執行一遍該腳本,更新 Corefile 的配置:
$ /usr/local/bin/update_coredns.sh然後通過 Crontab 製作定時任務,每隔兩天下午兩點更新域名列表:
$ crontab -l
0 14 */2 * * /usr/local/bin/update_coredns.sh
開機自啟MacOS 可以使用 launchctl 來管理服務,它可以控制啟動計算機時需要開啟的服務,也可以設置定時執行特定任務的腳本,就像 Linux crontab 一樣, 通過加裝 *.plist 文件執行相應命令。Launchd 腳本存儲在以下位置, 默認需要自己創建個人的 LaunchAgents 目錄:
~/Library/LaunchAgents : 由用戶自己定義的任務項/Library/LaunchAgents : 由管理員為用戶定義的任務項/Library/LaunchDaemons : 由管理員定義的守護進程任務項/System/Library/LaunchAgents : 由 MacOS 為用戶定義的任務項/System/Library/LaunchDaemons : 由 MacOS 定義的守護進程任務項我們選擇在 /Library/LaunchAgents/ 目錄下創建 coredns.plist 文件,內容如下:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>coredns</string>
<key>ProgramArguments</key>
<array>
<string>/usr/local/bin/coredns</string>
<string>-conf</string>
<string>/usr/local/etc/Corefile</string>
</array>
<key>StandardOutPath</key>
<string>/var/log/coredns.stdout.log</string>
<key>StandardErrorPath</key>
<string>/var/log/coredns.stderr.log</string>
<key>KeepAlive</key>
<true/>
<key>RunAtLoad</key>
<true/>
</dict>
</plist>設置開機自動啟動 coredns:
$ sudo launchctl load -w /Library/LaunchAgents/coredns.plist查看服務:
$ sudo launchctl list|grep coredns
61676 0 coredns$ sudo launchctl list coredns
{
"StandardOutPath" = "/var/log/coredns.stdout.log";
"LimitLoadToSessionType" = "System";
"StandardErrorPath" = "/var/log/coredns.stderr.log";
"Label" = "coredns";
"TimeOut" = 30;
"OnDemand" = false;
"LastExitStatus" = 0;
"PID" = 61676;
"Program" = "/usr/local/bin/coredns";
"ProgramArguments" = (
"/usr/local/bin/coredns";
"-conf";
"/usr/local/etc/Corefile";
);
};查看埠號:
$ sudo ps -ef|egrep -v grep|grep coredns
0 81819 1 0 2:54下午 ?? 0:04.70 /usr/local/bin/coredns -conf /usr/local/etc/Corefile
$ sudo lsof -P -p 81819|egrep "TCP|UDP"
coredns 81819 root 5u IPv6 0x1509853aadbdf853 0t0 TCP *:5302 (LISTEN)
coredns 81819 root 6u IPv6 0x1509853acd2f39ab 0t0 UDP *:5302
coredns 81819 root 7u IPv6 0x1509853aadbdc493 0t0 TCP *:53 (LISTEN)
coredns 81819 root 8u IPv6 0x1509853acd2f5a4b 0t0 UDP *:53
coredns 81819 root 9u IPv6 0x1509853ac63bfed3 0t0 TCP *:5301 (LISTEN)
coredns 81819 root 10u IPv6 0x1509853acd2f5d03 0t0 UDP *:5301大功告成,現在你只需要將系統的 DNS IP 設置為 127.0.0.1 就可以了。
驗證$ doggo www.youtube.com @udp://127.0.0.1
NAME TYPE CLASS TTL ADDRESS NAMESERVER
www.youtube.com. CNAME IN 293s youtube-ui.l.google.com. 127.0.0.1:53
youtube-ui.l.google.com. A IN 293s 172.217.14.110 127.0.0.1:53
youtube-ui.l.google.com. A IN 293s 172.217.11.174 127.0.0.1:53
youtube-ui.l.google.com. A IN 293s 172.217.5.206 127.0.0.1:53
youtube-ui.l.google.com. A IN 293s 172.217.5.78 127.0.0.1:53
youtube-ui.l.google.com. A IN 293s 172.217.14.78 127.0.0.1:53
youtube-ui.l.google.com. A IN 293s 142.250.72.238 127.0.0.1:53
youtube-ui.l.google.com. A IN 293s 216.58.193.206 127.0.0.1:53
youtube-ui.l.google.com. A IN 293s 142.250.68.110 127.0.0.1:53
youtube-ui.l.google.com. A IN 293s 142.250.68.78 127.0.0.1:53
youtube-ui.l.google.com. A IN 293s 172.217.4.142 127.0.0.1:53
youtube-ui.l.google.com. A IN 293s 142.250.68.14 127.0.0.1:53搞定。
什麼?你問我 doggo 是個啥?掃描下方二維碼關注公眾號:
公眾號後臺回復 doggo 即可獲取你想要的東西😬
參考參考資料[1]CoreDNS: https://github.com/coredns/coredns
[2]DSL: https://www.wikiwand.com/zh/%E9%A2%86%E5%9F%9F%E7%89%B9%E5%AE%9A%E8%AF%AD%E8%A8%80
[3]DNS-over-TLS: https://www.wikiwand.com/zh/DNS_over_TLS
[4]plugin.cfg: https://github.com/coredns/coredns/blob/master/plugin.cfg
[5]proxychians-ng: https://github.com/rofl0r/proxychains-ng
[6]proxifier: https://github.com/yangchuansheng/love-gfw#%E7%95%AA%E5%A4%96%E7%AF%87
[7]dnsredir: https://github.com/leiless/dnsredir
[8]FelixOnMars的大陸區域名列表: https://github.com/felixonmars/dnsmasq-china-list
[9]https://github.com/missdeer/coredns_custom_build: https://github.com/missdeer/coredns_custom_build
[10]文檔: https://coredns.io/manual/toc
[11]plugins 的介紹: https://coredns.io/plugins/
[12]FelixOnMars的大陸區域名列表: https://github.com/felixonmars/dnsmasq-china-list
[13]plugins: https://coredns.io/plugins/
[14]紅魚 DNS: https://www.rubyfish.cn/dns/solutions/
[15]CoreDNS 使用與架構分析: https://zhengyinyong.com/coredns-basis.html
[16]CoreDNS搭建無汙染DNS: https://blog.minidump.info/2019/07/coredns-no-dns-poisoning/