使用 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(生存时间)等,对流量进行细颗粒度控制,标记流量进行后续处理等。
可以在这里看到源代码
- https://github.com/vernesong/OpenClash/blob/fc741a805883d6105c989af9bb6688e0edd7ab32/luci-app-openclash/root/etc/init.d/openclash#L1424 (opens in a new tab)
- https://github.com/vernesong/OpenClash/blob/fc741a805883d6105c989af9bb6688e0edd7ab32/luci-app-openclash/root/etc/init.d/openclash#L1838 (opens in a new tab)
以 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 链主要用于处理进入的流量。这个链通常用于根据特定的规则对流量进行分类和处理。
源代码:
- https://github.com/vernesong/OpenClash/blob/fc741a805883d6105c989af9bb6688e0edd7ab32/luci-app-openclash/root/etc/init.d/openclash#L1379 (opens in a new tab)
- https://github.com/vernesong/OpenClash/blob/fc741a805883d6105c989af9bb6688e0edd7ab32/luci-app-openclash/root/etc/init.d/openclash#L1774 (opens in a new tab)
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 链专门用于处理输出流量,即从本地系统发出的流量(路由器本机流量)。
源代码:
- https://github.com/vernesong/OpenClash/blob/fc741a805883d6105c989af9bb6688e0edd7ab32/luci-app-openclash/root/etc/init.d/openclash#L1481 (opens in a new tab)
- https://github.com/vernesong/OpenClash/blob/fc741a805883d6105c989af9bb6688e0edd7ab32/luci-app-openclash/root/etc/init.d/openclash#L1799 (opens in a new tab)
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)