使用 CoreDNS 來應對 DNS 污染

原文連接:https://fuckcloudnative.io/posts/install-coredns-on-macos/html

CoreDNS 是 Golang 編寫的一個插件式 DNS 服務器,是 Kubernetes 1.13 後所內置的默認 DNS 服務器。CoreDNS 的目標是成爲 cloud-native 環境下的 DNS 服務器和服務發現解決方案,即:linux

Our goal is to make CoreDNS the cloud-native DNS server and service discovery solution.nginx

它有如下幾個特性:git

  • 插件化(Plugins)github

    基於 Caddy 服務器框架,CoreDNS 實現了一個插件鏈的架構,將大量應用端的邏輯抽象成 plugin 的形式(如 Kubernetes 的 DNS 服務發現,Prometheus 監控等)暴露給使用者。CoreDNS 以預配置的方式將不一樣的 plugin 串成一條鏈,按序執行 plugin 的邏輯。從編譯層面,用戶選擇所需的 plugin 編譯到最終的可執行文件中,使得運行效率更高。CoreDNS 採用 Go 編寫,因此從具體代碼層面來看,每一個 plugin 其實都是實現了其定義的 interface 的組件而已。第三方只要按照 CoreDNS Plugin API 去編寫自定義插件,就能夠很方便地集成於 CoreDNS。macos

  • 配置簡單化json

    引入表達力更強的 DSL,即 Corefile 形式的配置文件(也是基於 Caddy 框架開發)。windows

  • 一體化的解決方案後端

    區別於 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 解析
  • server 將加載哪些插件

常見地,一個典型的 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 協議,也支持 TLSgRPC,即 DNS-over-TLS 和 DNS-over-gRPC 模式:

tls://example.org:1443 {
#...
}

2. 插件的工做模式


當 CoreDNS 啓動後,它將根據配置文件啓動不一樣 server ,每臺 server 都擁有本身的插件鏈。當有 DNS 請求時,它將依次經歷以下 3 步邏輯:

  1. 若是有當前請求的 server 有多個 zone,將採用貪心原則選擇最匹配的 zone;
  2. 一旦找到匹配的 server,按照 plugin.cfg 定義的順序執行插件鏈上的插件;
  3. 每一個插件將判斷當前請求是否應該處理,將有如下幾種可能:
  • 請求被當前插件處理

    插件將生成對應的響應並回給客戶端,此時請求結束,下一個插件將不會被調用,如 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 個區塊),分別監聽在 530053 端口。其邏輯圖可以下所示:

每一個進入到某個 server 的請求將按照 plugin.cfg 定義順序執行其已經加載的插件。

從上圖,咱們須要注意如下幾點:

  • 儘管在 .:53 配置了 health 插件,可是它併爲在上面的邏輯圖中出現,緣由是:該插件並未參與請求相關的邏輯(即並無在插件鏈上),只是修改了 server 配置。更通常地,咱們能夠將插件分爲兩種:
    • Normal 插件:參與請求相關的邏輯,且插入到插件鏈中;
    • 其餘插件:不參與請求相關的邏輯,也不出如今插件鏈中,只是用於修改 server 的配置,如 healthtls 等插件;

4. 配置 CoreDNS


既然 CoreDNS 如此優秀,我用它來抵禦偉大的防火長城豈不美哉?研究了一圈,發現技術上仍是可行的,惟一的一個缺點是不支持使用代理,不過你能夠經過 proxychians-ngproxifier 來強制使用代理。下面開始折騰。

具體的思路其實很是簡單,就是將國內的域名查詢請求轉發到 114 等國內的公共 DNS 服務器,將國外的域名查詢請求轉發到 8.8.8.8 等國外的公共 DNS 服務器。然而 CoreDNS 的插件鏈有點反直覺,同一個插件鏈上的每個插件只能出現一次,若是隻使用 forward 插件是知足不了需求的。

CoreDNS 原來還有個插件叫 proxy,功能和 forward 相似,目測好像同時利用 proxyforward 插件就能夠實現咱的需求了。但理想與現實的差距老是很大,不知道從何時開始,CoreDNS 官方編譯的二進制文件已經沒有 proxy 插件了,真是氣人。

dnsredir

偶然間發現了一個第三方插件 dnsredir,目測能夠解決個人全部問題。該插件綜合了 proxyforward 插件的全部優勢,支持 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的大陸區域名列表了。。。FelixOnMars 同時還提供了 GoogleApple 的域名列表,這在某些地區某些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

加強版 CoreDNS

dnsredir 雖香,但你們別忘了,它是第三方插件,官方默認的二進制文件是不包含該插件的。你能夠選擇本身編譯,但若是常常須要升級怎麼辦?總不能每次都手動編譯吧,也太累了。

好在有位大佬已經經過 CI/CD 流程將所需的第三方插件都集成編譯進去了,並按期更新,簡直就是我等的福音。大佬的項目地址爲:

如今只須要下載對應操做系統的二進制文件,處處拷貝,就能夠運行了。

下面通通以 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,請查看其文檔,及 plugins 的介紹。下面是個人配置文件:

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 : 多久掃描配置文件一次。若有變動,自動加載;
  • errors : 打印/存儲錯誤日誌;
  • dnsredir : 這是重點插件。第一段 dnsredir 配置使用了 4 個文件列表,均是 FelixOnMars的大陸區域名列表,這裏我還加了一個自定義的文件列表 mydns.conf。第二段 dnsredir 配置表示默認的解析配置,能夠理解爲故障轉移,若是某個域名沒有匹配到任何一個文件列表,就使用第二段 dnsredir 的上游 DNS 服務器進行解析。經過這樣的配置方式,就實現了將國內的域名查詢請求轉發到 114 等國內的公共 DNS 服務器,將國外的域名查詢請求轉發到 8.8.8.8 等國外的公共 DNS 服務器。

講一下我本身的理解:

  1. 配置文件相似於 nginx 配置文件的格式;
  2. 最外面一級的大括號,對應『服務』的概念。多個服務能夠共用一個端口;
  3. 往裏面一級的大括號,對應 plugins 的概念,每個大括號都是一個 plugin。這裏能夠看出,plugins 是 CoreDNS 的一等公民;
  4. 服務之間順序有無關聯沒有感受,但 plugins 之間是嚴重順序相關的。某些 plugin 必須用 fallthrough 關鍵字流向下一個 plugin;
  5. plugin 內部的配置選項是順序無關的;
  6. plugins 頁面的介紹看,CoreDNS 的功能仍是很強的,既能輕鬆從 bind 遷移,還能兼容 old-style dns server 的運維習慣;
  7. 從 CoreDNS 的性能指標看,適合作大型服務。

注意:該方案的前提是可以強制讓 CoreDNS 使用代理,或者更精確一點,讓 8.8.8.8 和 8.8.4.4 使用代理。這裏的方法比較複雜一點,本文就不介紹了。若是你實在不知道怎麼辦,能夠將 8.8.8.8 這一行刪除,直接使用 Cloudflare 提供的 DNS 服務,雖然響應有點慢,但好在能夠訪問。

若是你沒法忍受 Cloudflare 的響應速度,能夠考慮使用國內的無污染 DNS:紅魚 DNS。而後直接一勞永逸:

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 便可獲取你想要的東西😬

5. 參考資料



Kubernetes 1.18.2 1.17.5 1.16.9 1.15.12離線安裝包發佈地址http://store.lameleg.com ,歡迎體驗。 使用了最新的sealos v3.3.6版本。 做了主機名解析配置優化,lvscare 掛載/lib/module解決開機啓動ipvs加載問題, 修復lvscare社區netlink與3.10內核不兼容問題,sealos生成百年證書等特性。更多特性 https://github.com/fanux/sealos 。歡迎掃描下方的二維碼加入釘釘羣 ,釘釘羣已經集成sealos的機器人實時能夠看到sealos的動態。

相關文章
相關標籤/搜索