当前位置: 首页 > 科技观察

使用CoreDNS应对DNS污染

时间:2023-03-14 20:23:23 科技观察

CoreDNS是CNCF新孵化的项目。日前已经正式从CNCF毕业,正式成为Kubernetes的DNS服务器。CoreDNS的目标是成为云原生环境下的DNS服务器和服务发现解决方案,即:我们的目标是让CoreDNS成为云原生环境下的DNS服务器和服务发现解决方案。其具有以下特点:PluginsCoreDNS基于Caddy服务器框架,实现了插件链式架构,将大量应用端逻辑抽象为插件形式(如KubernetesDNS服务发现、Prometheus监控等)。并将它们暴露给用户。CoreDNS将不同的插件以预先配置的方式串成一条链,依次执行插件的逻辑。从编译层面,用户选择需要的插件,编译成最终的可执行文件,使得运行更加高效。CoreDNS是用Go编写的,所以从具体的代码来看,每个插件其实就是一个组件,实现了自己定义的接口。第三方只要根据CoreDNSPluginAPI编写自定义插件,就可以轻松集成到CoreDNS中。配置简化引入了更具表现力的DSL,即Corefile形式的配置文件(也是基于Caddy框架开发的)。集成方案不同于kube-dns。CoreDNS编译成单个二进制可执行文件,内置缓存、后端存储、健康检查等功能,无需第三方组件协助实现其他功能,部署更方便,内存管理更安全。其实从功能上来说,CoreDNS更像是一个通用的DNS解决方案(类似于BIND),然后通过插件的方式极大的扩展了自身的功能,从而可以适用于不同的场景(例如Kubernetes)。正如官方博客所说:CoreDNS由plugins.1提供支持。Corefile介绍Corefile是CoreDNS的配置文件(来自Caddy框架的配置文件Caddyfile),它定义了:服务器在哪个端口上监听什么协议(可以同时定义多个)每个服务器监听不同的端口)服务器负责哪个zone的权威(authoritative)DNS解析服务器会加载哪些插件通常典型的Corefile格式如下:ZONE:[PORT]{[PLUGIN]...}ZONE:定义负责的zone对于服务器,PORT是可选的,默认是53;PLUGIN:定义服务器要加载的插件。每个插件可以有多个参数;例如:{chaosCoreDNS-001}上面的配置文件表示:服务器负责解析根域.,这里的插件是chaos,没有参数。定义服务器最简单的配置文件可以是:.{},即服务器监听53端口,不使用插件。如果此时定义的是其他服务器,必须保证监听端口不冲突;如果在原来的服务器上添加一个区域,必须保证区域之间没有冲突,比如:.{}.:54{}另一台服务器运行在54端口,并负责根域的解析。又如:example.org{whoami}org{whoami}有相同的服务器但负责不同zone的解析,有不同的插件链。定义反向区域类似于其他DNS服务器。Corefile还可以定义一个ReverseZone(反向解析IP地址对应的域名):0.0.10.in-addr.arpa{whoami}或者简化版:10.0.0.0/24{whoami}可以进行反向查询通过dig:$dig-x10.0.0.1使用了不同的通信协议CoreDNS除了支持DNS协议外,还支持TLS和gRPC,即DNS-over-TLS和DNS-over-gRPC模式:tls://example.org:1443{#...}2。插件工作方式CoreDNS启动时会根据配置文件启动不同的服务器,每个服务器都有自己的插件链。当有DNS请求时,会依次经过以下3步逻辑:如果当前请求的服务器有多个区域,则使用贪心原则选择最匹配的区域;一旦找到匹配的服务器,就会按照plugin.cfg中定义的顺序执行插件链上的插件;每个插件都会判断当前请求是否应该被处理,会有以下几种可能:请求被当前插件处理,插件会产生相应的响应返回给客户端。这个时候request就结束了,接下来的插件就不会被调用了,比如whoami插件;该请求由当前插件以Fallthrough的形式处理。如果在插件处理过程中请求可能会跳转到下一个插件,这个过程称为fallthrough,通过关键字fallthrough来决定是否允许这个操作,比如宿主插件调用下一个插件in当查询域名不在/etc/hosts中时;该请求由Hint请求处理,由插件处理,在响应中添加一些信息(提示)继续由下一个插件处理。这些附加信息将形成对客户端的最终响应,例如metricplugin;3.CoreDNS如何处理DNS请求如果Corefile是:coredns.io:5300{filedb.coredns.io}example.io:53{logerrorsfiledb.example.io}example.net:53{filedb.example.net}。:53{kubernetesproxy.8.8.8.8loghealthherrorscache}从配置文件中,我们定义了两个服务器(虽然有4个块),监听5300和53端口。其逻辑图如下所示:每个进入某个服务器的请求都会按照plugin.cfg中定义的顺序执行其加载的插件。从上图中,我们需要注意以下几点:健康插件虽然配置在.:53中,但是并没有出现在上面的逻辑图中,因为该插件不参与请求相关的逻辑(即,它不在插件链中),只需修改服务器配置即可。更一般地,我们可以将插件分为两种类型:普通插件:参与请求相关逻辑,被插入到插件链中;其他插件:不参与请求相关逻辑,不出现在插件链中,仅用于修改服务器的配置,如health、tls等插件;4.在MacOS上部署CoreDNS既然CoreDNS这么好,我用它来抵御防火墙不是很好吗?研究了一圈,发现技术上是可行的,***的一个缺点就是不支持使用代理,但是可以使用proxychians-ng或者proxifier强制使用代理。开始折腾吧。CoreDNS的安装是用golang写的,所以只需要下载对应操作系统的二进制文件,复制到各处,就可以运行了。下面将以MacOS为例进行说明。$cd~/Downloads$wgethttps://github.com/coredns/coredns/releases/download/v1.4.0/coredns_1.4.0_darwin_amd64.tgz$tarzxfcoredns_1.4.0_darwin_amd64.tgz$mv./coredns/usr/local/bin/这里加上,二进制版本的CoreDNS已经安装了所有的插件(plugins),不需要自己编译。建议下载二进制版本。配置要深入了解CoreDNS,请查看其文档和插件介绍。以下是我的配置文件:$cat</usr/local/etc/Corefile.{hosts{fallthrough}forward.tls://8.8.8.8tls://8.8.4.4{tls_servernamedns.googleforce_tcpmax_fails3expire10shealth_check5spolicysequentialexceptwww.baidu.com}proxy.117.50.11.11117.50.22.22{policyround_robin}cache120reload6slog."{local}:{port}-{>id}'{type}{class}{name}{proto}{size}{>do}{>bufsize}'{rcode}{>rflags}{rsize}{duration}"errors}EOFhosts:hosts是CoreDNS的一个插件,这一段的意思是加载/etc/hosts文件中的解析信息。hosts在最上面,如果hosts文件中存在域名,则优先使用该信息返回;fallthrough:如果在hosts中找不到,就会进入下一个plugin继续。如果没有这个指令,下面的插件配置是没有意义的;forward:这是另一个插件。.代表所有域名,后面的IP代表上游DNS服务器列表。追踪来源的顺序由以下政策指令决定;tls://8.8.8.8:这意味着使用DNS-over-TLS协议访问8.8.8.8。您还需要通过tls_servername指定DNS名称。force_tcp:强制使用TCP协议可追溯性。这就要求上游DNS必须支持TCP协议;expect:根据本插件的配置指定哪些域名不被追踪。这个主要用来排除国内域名,然后通过下面的代理转到国内DNS解析。这里排除的域名只有www.baidu.com,后面我们会通过脚本填写所有国内域名;proxy:解析转发排除的域名。.代表所有域名,后面的IP代表上游DNS服务器列表。这里我选择onedns。追踪来源的顺序由下面的策略命令决定;缓存:将从源跟踪中获得的结果缓存指定的时间。类似于TTL的概念;reload:扫描配置文件的频率。如果更改则自动加载;日志:打印/存储访问日志。日志格式参考:https://coredns.io/plugins/log/;错误:打印/存储错误日志;说说自己的理解:配置文件类似于nginx配置文件的格式;最外层的花括号,对应“服务”的概念。多个服务可以共享一个端口;里面一层的大括号对应插件的概念,每个大括号就是一个插件。从这里可以看出插件是CoreDNS的一等公民;没有服务顺序是否相关的感觉,但是插件的顺序严重相关。有些插件必须使用fallthrough关键字才能流到下一个插件;插件内的配置选项与顺序无关;从plugins页面的介绍来看,CoreDNS的功能还是很强大的,可以很方便的从bind迁移过来,同时也兼容老式dnsserver的运维习惯;从CoreDNS的性能指标来看,适合做大规模服务。当然上面的配置文件也可以升级,我就不具体解释了:.{hosts{fallthrough}forward.127.0.0.1:5301127.0.0.1:5302{max_fails3expire10shealth_check5spolicysequentialexceptwww.baidu.com}proxy.117.50.11.11117.50.22.22{policyround_robin}cache120reload6slog."{local}:{port}-{>id}'{type}{class}{name}{proto}{size}{>do}{>bufsize}'{rcode}{>rflags}{rsize}{duration}"错误}.:5301{forward.tls://8.8.8.8tls://8.8.4.4{tls_servernamedns.googleforce_tcpmax_fails3expire10shealth_check5spolicysequential}}.:5302{forward.tls://1.1.1.1tls://1.0.0.1{tls_servername1dot1dot1dot1.cloudflare-dns.comforce_tcpmax_fails3expire10shealth_check5spolicysequential}}定期更新国内域名列表写一个shell脚本更新Corefile中排除的国内域名列表:$brewinstallgnu-sed$cat</usr/local/bin/update_coredns.sh#!/bin/bashchinadns=$(curl-sLhttps://raw.githubusercontent.com/felixonmars/dnsmasq-china-list/master/accelerated-domains.china.conf|awk-F"/"'{print$2}')touchupdate_coredns.sed&&echo"">update_coredns.sedforiin$chinadns;doecho"/except/s/$/$i/">>update_coredns.sed;donegsed-i"s/\(except\).*/\1/"/usr/local/etc/Corefilegsed-i-fupdate_coredns.sed/usr/local/etc/CorefileEOF$sudochmod+x/usr/local/bin/update_coredns.sh先执行脚本更新Corefile的配置:$/usr/local/bin/update_coredns.sh然后通过crontab做定时任务,每两天下午2:00更新域名列表:$crontab-l014*/2**/usr/local/bin/update_coredns.shmacOS可以使用launchctl来管理services,可以控制电脑启动时需要启动的服务,也可以设置定时执行特定任务的脚本,就像linux的crontab一样,通过安装*.plist文件执行相应的命令Launchd脚本存放在以下位置,需要默认创建自己的LaunchAgents目录:~/Library/LaunchAgents:用户自己定义的任务项/Library/LaunchAgents:管理员为用户定义的任务项/Library/LaunchDaemons:managedUser-defineddaemontaskitems/System/Library/LaunchAgents:MacOSUser-definedtaskitems/System/Library/LaunchDaemons:MacOSdefinedDaemonprocesstaskitems我们选择在/Library/LaunchAgents/目录下创建coredns。plist文件,内容如下:LabelcorednsProgramArguments/usr/local/bin/coredns-conf/usr/local/etc/CorefileStandardOutPath/var/log/coredns.stdout.log标准错误路径/var/log/coredns.stderr.logKeepAliveRunAtLoad设置启动时自动启动coredns:$sudolaunchctlload-w/Library/LaunchAgents/coredns.plist查看服务:$sudolaunchctllist|grepcoredns616760coredns$sudolaunchctllistcoredns{"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/核心文件";);};查看端口号:$sudops-ef|egrep-vgrep|grepcoredns081819102:54pm??0:04.70/usr/local/bin/coredns-conf/usr/local/etc/Corefile$sudolsof-P-p81819|egrep"TCP|UDP"coredns81819root5uIPv60x1509853aadbdf8530t0TCP*:5302(LISTEN)coredns81819root6uIPv60x1509853acd2f39ab0t0UDP*:5302coredns81819root7uIPv60x1509853aadbdc4930t0TCP*:53(LISTEN)coredns81819root8uIPv60x1509853acd2f5a4b0t0UDP*:53coredns81819root9uIPv60x1509853ac63bfed30t0TCP*:5301(LISTEN)coredns81819root10uIPv60x1509853acd2f5d030t0UDP*:5301大功告成,现在你只需要将TheDNSIPofthesystemissetto127.0.0.1.验证$digwww.google.com;<<>>DiG9.10.6<<>>www.google.com;;globaloptions:+cmd;;Gotanswer:;;->>HEADER<<-opcode:QUERY,status:NOERROR,id:49942;;flags:qrrdra;QUERY:1,ANSWER:6,AUTHORITY:0,ADDITIONAL:1;;OPTPSEUDOSECTION:;EDNS:version:0,flags:;udp:4096;;QUESTIONSECTION:;www.google.com.INA;;答案:www.google.com.64INA108.177.97.147www.google.com.64INA108.177.97.105www.google.com.64INA108.177.97.106www.google.com.64INA108.177.97.103www.google.com.64INA108.177.97.104www.google.com.64INA108.177.97.99;;查询时间:0毫秒;;服务器:127.0.0.1#53(127.0.0.1);;时间:WedMar0613:23:31CST2019;;MSGSIZercvd:223搞定。