从 SmartDNS 到 mosdns 的迁徙
· 技术家里的「家里云」服务器上有一个虚拟机,这个虚拟机里安装有一个名为 SmartDNS 的 DNS 转发器,是我经历几次 DNS 问题后、为防后患部署的服务。但是,这几天维护的时候,原有的配置在重启 SmartDNS 之后无法使用、也不知道到底是因为什么报错。想起有个大手子在很久之前推荐了同样为 DNS 转发器的 mosdns,所以就浅尝试了一下。
什么是「DNS 转发器」
顾名思义,这类服务是接收 DNS 查询请求、并发到多个上游,再将最快应答的结果返回给客户端,以此提升网络访问速度。此外,它们提供了许多额外功能,用户可以根据自己的需求进行功能配置、实现自己想要的效果。
例如,使用 DNS 转发器对我有一个额外好处:国内的 DNS(包括运营商下发的)可能存在部分国内外网站被污染、以及 SLA 不稳定的情况, 此时我只需要配置多家服务商作为上游,完美避开了以上顾虑,还得到了更优的访问速度。
这里仅为一个概括,实际不可能只用国内 DNS 解析所有网站的。
为什么不是......
下面是我用过的三款 DNS 服务,请让我来说说 why not them(
SmartDNS
纯粹是配置文件不知道在哪里出问题了,且 journalctl -u smartdns
无法获取到有用的报错信息、可能是我姿势不对吧。就先不用了。
AdguardHome
看了很多有关 AdguardHome 的文章。貌似 AdguardHome 注重于广告拦截和反跟踪,DNS 分流这边倒是没有什么太详细的资料,因此没有选择它。
Technitium DNS
和 AdguardHome 没有区别吧......
手工配置 mosdns 系统服务
mosdns 内置有一个 系统服务管理工具,但是这玩意可用性不保证,自己写一个配置其实不难。
下载下来并解压之后的 mosdns 只有一个二进制文件和 YAML 配置文件。对于这个二进制文件,我直接将它扔到了 /usr/local/bin
下面,这样就可以直接命令行运行 mosdns:
$ mosdns
不一定要扔在
/usr/local/bin
下面,只要是个放终端能直接执行的可执行文件的存放目录即可。
接下来,我将 mosdns 工作目录定为 /etc/mosdns
。来创建它:
$ mkdir /etc/mosdns
将上面提到的 YAML 配置文件移到这个目录下:
$ mv config.yaml /etc/mosdns
最后来写一个系统服务配置:
// /etc/systemd/system/mosdns.service
[Unit]
Description=A DNS forwarder.
After=network.target
[Service]
Type=simple
ExecStart=/usr/local/bin/mosdns start -c /etc/mosdns/config.yaml -d /etc/mosdns # 防止出什么奇奇怪怪的问题,所以用了绝对路径
Restart=always
RestartSec=5
User=root # 我的虚拟机上只有 root,填这个没问题吧
WorkingDirectory=/etc/mosdns
[Install]
WantedBy=multi-user.target
这样就可以使用 systemctl [action] mosdns
来控制 mosdns 了。
将 SmartDNS 配置迁移到 mosdns
SmartDNS 使用的是 .conf
配置文件格式,而 mosdns 通常是 YAML。不仅如此,两个 DNS 转发器的配置方法还是有点子差别在的,需要一定的耐力(确信)去迁移。
加载规则集
这里以 Adrules 为例。
SmartDNS 通过引用同样为 .conf
配置文件格式的规则集文件加载对应规则集:
conf-file anti-ad-smartdns.conf
迁移到 mosdns,需要写一个域名集插件(Plugin),并引用相应的 .txt
文件加载规则集:
plugins:
- tag: adrules
type: domain_set # 插件类型,adrules 提供的配置文件是域名集。
args:
files:
- "/path/to/adrules-mosdns.txt"
接下来的配置迁移到 mosdns 都需要写成一个插件。
配置 DNS 上游
SmartDNS 可直接在配置文件里面指定 DNS 上游服务器信息:
server-tcp 9.9.9.9
server-https https://dns.quad9.net/dns-query
server-tcp 8.8.8.8
server-https https://dns.google/dns-query
server-tcp 1.1.1.1
server-https https://1.1.1.1/dns-query
迁移到 mosdns,需要写一个转发插件:
plugins:
- tag: forward_aboard
type: forward
args:
concurrent: 6
upstreams:
- addr: https://8.8.8.8/dns-query
enable_pipeline: true
- addr: https://8.8.4.4/dns-query
enable_pipeline: true
- addr: https://1.1.1.1/dns-query
enable_pipeline: true
- addr: https://1.0.0.1/dns-query
enable_pipeline: true
- addr: https://9.9.9.9/dns-query
enable_pipeline: true
- addr: https://149.112.112.112
enable_pipeline: true
- addr: https://185.222.222.222/dns-query
enable_pipeline: true
- addr: https://45.11.45.11
enable_pipeline: true
- addr: https://94.140.14.14/dns-query
enable_pipeline: true
- addr: https://94.140.15.15/dns-query
enable_pipeline: true
- tag: forward_cn
type: forward
args:
concurrent: 4
upstreams:
- addr: https://119.29.29.29/dns-query
enable_pipeline: true
- addr: https://223.5.5.5/dns-query
enable_pipeline: true
- addr: https://223.6.6.6/dns-query
enable_pipeline: true
- addr: https://106.75.165.71/dns-query
enable_pipeline: true
- addr: https://101.226.4.6/dns-query
enable_pipeline: true
- addr: https://218.30.118.6/dns-query
enable_pipeline: true
注:加载了对应的分流规则后,SmartDNS 可以在 DNS 配置后面增加一个
-group [groupName]
进行一个分流。由于这里不提供分流规则,因此不作示范。
网络上大部分 mosdns 配置都用到了 fallback 插件。但就我目前的需求来看,mosdns 的文档说的很对:「绝大多数(99%)的用户只需设置 forward 并发多个上游,只有极少数用户需要这个插件」。因此我就写成了 forward 插件。
配置 mosdns sequence 插件
mosdns 的 sequence 插件可以做非常非常非常多的事情。在这里,我写了一个 sequence 插件、组合了上述已经迁移过来配置的插件,或许可以看作是转发器的主线程。
plugins:
- tag: query
type: sequence
args:
- matches:
- qname $adrules
exec: reject 3
- exec: $cache
- matches:
- has_resp
exec: accept
- matches:
- resp_ip $cn_ips
- qname $cn_domains
exec: $forward_cn
- matches:
- resp_ip $aboard_ips
- qname $aboard_domains
exec: $forward_aboard
- exec: forward 202.103.225.68 202.103.224.68
让我们来看看这个主线程做了什么:
- 匹配请求的 QNAME 是否存在于 Adrules 的列表,如果有则返回 NXDOMAIN(3) 屏蔽;
- 查找缓存(我额外写了一个缓存插件,没有列出),如果找到缓存直接结束主线程;
- 匹配请求的 QNAME / IP 是否位于中国大陆境内,如果是则由
forward_cn
插件处理; - 匹配请求的 QNAME / IP 是否位于海外,如果是则由
forward_aboard
插件处理; - 有漏网之鱼?那就用运营商 DNS(这里是广西电信的)进行处理吧。
琢磨出来的配置大概就是这样,后续使用上的感受与 SmartDNS 基本无异。
启动 DNS 服务器
SmartDNS 仅需要写一行 bind [::]:53
即可监听所有的 DNS 请求。而 mosdns 需要写相应的 DNS 服务器插件:
plugins:
- tag: udp_server
type: udp_server
args:
listen: ":53"
entry: query
- tag: tcp_server
type: tcp_server
args:
listen: ":53"
entry: query
不难发现:服务器插件接到请求之后,会将请求转交给上面的主线程 sequence 插件进行处理。这便是 SmartDNS 与 mosdns 的一个最大可感受差别,你需要自行定义 mosdns 如何工作。
还有吗
肯定不止这些,但这些对我来说够用了。感兴趣的可自行深入研究研究。