其他
使用 mosdns 提前进行 dns 进行分流

使用 mosdns 提前进行 dns 进行分流

前言

之前我是用 clash 的 dns 接管的路由的 dns,然后 clash dns 服务器内国外分流到 mosdns(因为我想国外 ipv4 优先)

后面想了一下,能不能提前就用 mosdns 作为主 dns 服务器,并且 mosdns 是专门搞 dns 的,性能处理上应该是比 clash 要好的并且还能额外实现很多的特性,以及实现一些特殊的逻辑。

于是就有这篇文章进行实验,个人使用一个月下来,感觉没什么问题,并且 redir-host 和 fake-ip 都可以使用。

fake-ip 模式下遵循路由规则,即使 openclash 配置了不代理 ipv6,也会跟着路由规则走。比如访问 IPv6 only 的资源,域名路由规则到 Match 设置为默认走代理,那么访问当前域名就会通过代理节点访问,必须要代理节点支持 ipv6,否则访问不通,Match 设置为直连的话就是直连

OpenClash 的“实验性:绕过中国大陆 IP“功能是通过 Dnsmasq 进行实现,旨在将国内 IP 通过防火墙转发规则,国内 IPv4 和 IPv6 的流量不经过 Clash(Mihomo)内核处理,增强直连性能,降低 OpenWRT 软路由的资源占用。对于进阶使用用户来说,在使用上存在诸多不便,例如无法与 MosDNS、Adguard Home 完美配合。

本文将使用 mosdns 作为 dns + openclash(mihomo 内核) 作为代理 + 手动防火墙前置绕过大陆 IP( 这步可以不用,推荐直接用 openclash 自带的绕过,下面那一块实验性:绕过中国大陆 IP 的原理 就可以不用看了,直接看 配置流程 部分 )。

实验性:绕过中国大陆 IP 的原理 (后面发现其实可以不用这个)

OpenClash 的大部分核心外功能实现基本都是通过 /etc/init.d/openclash 这个启动文件实现。在文件中,涉及实验性:绕过中国大陆 IP 的主要功能部分包括:

  • ip set/nft set 管理,也就是 IP 集合管理
  • China_ip_route 规则匹配及重定向
  • 利用 Dnsmasq 进行国内域名列表(/etc/openclash/accelerated-domains.china.conf)内的域名解析

IP 集合

OpenClash 内置了两个 IP 集合,分别为China_ip_route集合和China_ip_route_pass集合,通过插件设置 - 大陆白名单订阅 进行维护,没有使用 GeoIP:CN 进行维护。

China_ip_route 规则匹配

在 Openclash 选择绕过大陆 IP 时,通过 fw4 进行进行流量检测,当检测到目标 IP 地址属于中国大陆时,通过openclash链、openclash_mangle链、以及openclash_mangle_out链进行return操作,使流量按原链继续进行,不再经由 clash 内核进行处理。

openclash_mangle 链

openclash_mangle 链的作用是用于修改流量的特性,通常用于更改数据包的标记、TTL(生存时间)等,对流量进行细颗粒度控制,标记流量进行后续处理等。

可以在这里看到源代码

以 ipv4 的为例子:

if [ "$china_ip_route" != "0" ]; then
    if [ "$china_ip_route" = "1" ]; then
      rule="ip daddr @china_ip_route"
    elif [ "$china_ip_route" = "2" ]; then
      rule="ip daddr != @china_ip_route"
    fi
    [ "$enable_redirect_dns" != "2" ] && rule="$rule ip daddr != @china_ip_route_pass"
    nft "add rule inet fw4 openclash_mangle $rule counter return"
fi
  • 检查变量 china_ip_route 是否不等于 0。如果是,表示启用了绕过中国大陆 IP 或者是海外用户回国模式。
  • 检查变量 enable_redirect_dns 是否不等于 2。这里 0 是不劫持 DNS,1 是通过 dnsmasq 劫持,2 是通过防火墙劫持。
  • 绕过中国大陆 IP 情况下,如果 enable_redirect_dns 不等于 2,即不使用防火墙劫持:nft 'add rule inet fw4 openclash_mangle ip daddr @china_ip_route ip daddr != @china_ip_route_pass counter return’
  • 绕过中国大陆 IP 情况下,如果 enable_redirect_dns 如果等于 2:nft 'add rule inet fw4 openclash_mangle ip daddr @china_ip_route counter return’
  • 海外用户回国模式同上。

openclash 链

openclash 链主要用于处理进入的流量。这个链通常用于根据特定的规则对流量进行分类和处理。

源代码:

 
if [ "$china_ip_route" != "0" ]; then
    if [ "$china_ip_route" = "1" ]; then
      rule="ip daddr @china_ip_route"
    elif [ "$china_ip_route" = "2" ]; then
      rule="ip daddr != @china_ip_route"
    fi
    [ "$enable_redirect_dns" != "2" ] && rule="$rule ip daddr != @china_ip_route_pass"
    nft "add rule inet fw4 openclash $rule counter return"
fi

条件判断同上。

openclash_mangle_output 链

openclash_mangle_output 链专门用于处理输出流量,即从本地系统发出的流量(路由器本机流量)。

源代码:

if [ "$china_ip_route" != "0" ]; then
    if [ "$china_ip_route" = "1" ]; then
      rule="ip daddr @china_ip_route"
    elif [ "$china_ip_route" = "2" ]; then
      rule="ip daddr != @china_ip_route"
    fi
    [ "$enable_redirect_dns" != "2" ] && rule="$rule ip daddr != @china_ip_route_pass"
    nft "add rule inet fw4 openclash_output skuid != 65534 $rule counter return"
fi

大部分判断条件同上,其中多出来的部分,skuid≠65534为非特权用户,即nobody用户。

China_ip_route_pass 规则匹配

China_ip_route_pass 这个 IP 集是通过劫持 Dnsmasq,使用设定的 dns 对国内常见域名(也就是/etc/openclash/accelerated-domains.china.conf 文件内域名)进行解析并生成的 IP 集。这部分非常依赖于 Dnsmasq,但在我们这里由于已经有 MosDNS 进行分流,所以不用过于关注。

配置流程

openclash 的配置 (使用 openclash 配置绕过大陆)

  • 流量控制 - 实验性:绕过指定区域 IP 设置为 绕过中国大陆
  • 禁用 DNS设置 - *本地 DNS 劫持

openclash 的配置 (手动添加防火墙规则)

  • 禁用 流量控制 - 实验性:绕过指定区域 IP选项,因为我们要手动添加防火墙规则进行绕过
  • 禁用 DNS设置 - *本地 DNS 劫持
  • 覆写设置 - DNS设置 全部关闭

clash 的 DNS 配置文件:

dns:
  enable: true
  listen: 0.0.0.0:7874
  respect-rules: false
  ipv6: true
  use-hosts: true
  prefer-h3: true # 是否开启 DOH 的 http/3。
  default-nameserver:
    - 223.5.5.5
    - 119.29.29.29
  proxy-server-nameserver:
    - https://223.5.5.5/dns-query
    - https://1.12.12.12/dns-query
  nameserver:
    - https://1.0.0.1/dns-query#dns # 这里使用 dns 代理组内的节点进行解析,没有 dns代理组 的话改成自己的代理组
    - https://8.8.8.8/dns-query#dns # 同上,避免 DNS 泄露

添加防火墙规则

由于防火墙规则采用顺序匹配,如果我们采用 add 的方式,无论如何只会附加规则至每条防火墙规则链的底部,所以这里采用 insert 方式。可以直接添加在 openclash 内的开发者选项内。

ipv4 的规则:

nft 'insert rule inet fw4 openclash ip daddr @china_ip_route ip daddr != @china_ip_route_pass counter return'
nft 'insert rule inet fw4 openclash_mangle ip daddr @china_ip_route ip daddr != @china_ip_route_pass counter return'
nft 'insert rule inet fw4 openclash_mangle_output skuid != 65534 ip daddr @china_ip_route ip daddr != @china_ip_route_pass counter return'

ipv6 的规则(没开启 ipv6 代理不需要配置):

nft 'insert rule inet fw4 openclash_v6 ip6 daddr @china_ip6_route ip daddr != @china_ip6_route_pass counter return'
nft 'insert rule inet fw4 openclash_mangle_v6 ip6 daddr @china_ip6_route ip daddr != @china_ip6_route_pass counter return'
nft 'insert rule inet fw4 openclash_mangle_output_v6 skuid != 65534 ip6 daddr @china_ip6_route ip daddr != @china_ip6_route_pass counter return'

iptables 和 nft

两种的写法都差不多,就命令使用上的区别,有需求可以自己研究。

mosdns 配置

PS: mosdns 所使用的 geoX 文件,可以通过 ln -s 软连接 link 到 openclash 的 geoX 文件,这样就不用维护两份还方便管理了。

mosdns 使用自定义配置文件,并且打开 DNS 转发

log:
  level: info
  file: "/tmp/log/mosdns.log"
 
# API 入口设置
api:
  http: "0.0.0.0:9091"
 
include: []
 
plugins:
  # hosts
  - tag: hosts
    type: hosts
    args:
      files:
        - "/etc/mosdns/rule/hosts.txt"
 
  # 缓存
  - tag: lazy_cache
    type: cache
    args:
      size: 20000
      lazy_cache_ttl: 86400
 
  # 国内域名
  - tag: geosite_cn
    type: domain_set
    args:
      exps:
        - "lan"
        - "local"
        - "arpa"
      files:
        - "/etc/mosdns/rule/whitelist.txt"
        - "/var/mosdns/geosite_cn.txt"
 
  # 国外域名
  - tag: remote_domain
    type: domain_set
    args:
      files:
        - "/etc/mosdns/rule/greylist.txt"
 
  # 转发至本地服务器
  - tag: forward_local
    type: forward
    args:
      concurrent: 2
      upstreams:
        - addr: 223.5.5.5
        - addr: 119.29.29.29
 
  # 转发至远程服务器
  - tag: forward_remote
    type: forward
    args:
      concurrent: 3
      upstreams:
        - addr: tls://8.8.8.8
          enable_pipeline: true
        - addr: tls://1.0.0.1
          enable_pipeline: true
        - addr: tls://9.9.9.9
          enable_pipeline: false
 
  # 国内解析
  - tag: local_sequence
    type: sequence
    args:
      - exec: $forward_local
 
  # 用 clash 的 DNS 国外解析
  - tag: clash_sequence
    type: sequence
    args:
      - exec: prefer_ipv4
      - exec: forward 127.0.0.1:7874
      - exec: ttl 1
 
  # 国外解析 fallback 避免 openclash 没开时无法解析国外域名
  - tag: remote_sequence
    type: sequence
    args:
      - exec: prefer_ipv4
      - exec: $forward_remote
      - exec: ttl 1
 
  # 有响应终止返回
  - tag: has_resp_sequence
    type: sequence
    args:
      - matches: has_resp
        exec: accept
 
  # fallback 用远程服务器 sequence
  - tag: fallback
    type: fallback
    args:
      primary: clash_sequence
      secondary: remote_sequence
      threshold: 500
      # always_standby: secondary 会和 primary 一并执行。secondary 的结果将会在需要回滚时,立刻被采用。
      always_standby: false
 
  # 查询国内域名,灰名单内的域名,直接走 fallback
  - tag: query_is_local_domain
    type: sequence
    args:
      - matches: qname $remote_domain
        exec: return
      - matches: qname $geosite_cn
        exec: $local_sequence
 
  # 主要的运行逻辑插件
  # sequence 插件中调用的插件 tag 必须在 sequence 前定义,
  # 否则 sequence 找不到对应插件。
  - tag: main_sequence
    type: sequence
    args:
      - exec: $hosts
      - exec: jump has_resp_sequence
 
      # drop https query type
      - matches:
          - qtype 65
        exec: reject 3
 
      # handle local ptr
      - matches:
          - qtype 12
        exec: $local_sequence
      - exec: jump has_resp_sequence
 
      - exec: $lazy_cache
 
      - exec: $query_is_local_domain
      - exec: jump has_resp_sequence
 
      # 这里不是国内名单就直接走远程服务器,避免 dns 泄露或者有域名没被 clash 处理
      - exec: $fallback
 
  # 启动 udp 服务器。
  - tag: udp_server
    type: udp_server
    args:
      entry: main_sequence
      listen: ":5335"

参考

https://github.com/vernesong/OpenClash/issues/3467 (opens in a new tab)

https://songchenwen.com/tproxy-split-by-dns (opens in a new tab)

https://www.dolingou.com/article/experimental-bypass-china-ip-without-dnsmasq-mihomo (opens in a new tab)