从 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

让我们来看看这个主线程做了什么:

  1. 匹配请求的 QNAME 是否存在于 Adrules 的列表,如果有则返回 NXDOMAIN(3) 屏蔽;
  2. 查找缓存(我额外写了一个缓存插件,没有列出),如果找到缓存直接结束主线程;
  3. 匹配请求的 QNAME / IP 是否位于中国大陆境内,如果是则由 forward_cn 插件处理;
  4. 匹配请求的 QNAME / IP 是否位于海外,如果是则由 forward_aboard 插件处理;
  5. 有漏网之鱼?那就用运营商 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 如何工作。

还有吗

肯定不止这些,但这些对我来说够用了。感兴趣的可自行深入研究研究。

Loading Artalk...