使用 CoreDNS 来应对 DNS 污染

原文连接:fuckcloudnative.io/posts/insta…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。web

  • 配置简单化macos

    引入表达力更强的 DSL,即 Corefile 形式的配置文件(也是基于 Caddy 框架开发)。json

  • 一体化的解决方案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. 参考资料


相关文章
相关标签/搜索