最后验证:2026-10-02T15:08:38Z

sing-box 功能实测报告(DNS 泄露 / 广告拦截 / 分流 / Podman)

测试时间:2026-10-01 20:4x 环境:CachyOS,sing-box 1.14.2,TUN(stack: system),规则集全部本地化
测试方式:全部为真实流量实测(dig / curl / podman 容器),非推断。

追注(2026-10-02 晚):本文全部在 TUN 模式下完成,属于当时快照。当前运行模式已是 eBPF
(local.data_plane: cgroup + shared.data_plane: packet_rewrite on virbr0,TCX 挂载;本机没有 tun0)。
下文凡依赖 tun0/TUN 接管路径的结论都只在 TUN 模式下成立,各节末尾的”追注”给出 eBPF 模式复测结果。

一、DNS 泄露 —— 53 端口已完全劫持 ✅

判据:同一个域名问 4 个不同 DNS 服务器,看答案是否被统一。

域名 @192.168.31.1(路由器/电信) @223.5.5.5(阿里) @1.1.1.1(CF) 系统 resolved
www.google.com 142.251.152.119 142.251.150.119 142.251.153.119 142.251.151.119

四个来源返回的都是真实 Google IP(142.251.x)。若未劫持,路由器/电信 DNS 对 google 会返回被污染的国内 IP。
→ 所有 53 端口查询都被 sing-box 接管,系统 DNS、路由器 DNS 都不再被直接使用。

  • resolvectl query github.com → -- link: tun0,查询确实进入隧道
  • 本地 53 监听者:127.0.0.53/127.0.0.54(systemd-resolved)、192.168.122.1(libvirt dnsmasq,供虚拟机用)—— 这三条最终都会经过主机 → TUN → 被劫持

追注(2026-10-02 晚,eBPF 模式):劫持结论仍然成立,但路径不是 TUN:
resolvectl query github.com 现在显示 -- link: wlan0,/etc/resolv.conf 走 127.0.0.53 存根,
wlan0 的上游是路由器(fd00:6868:6868::1 / 192.168.31.1);本机没有 tun0,ip rule / ip -6 rule 里也没有 TUN 规则。
劫持发生在 eBPF local cgroup 数据面(本机进程的 53 流量在 cgroup 层被接走)。
复测口径与本文一致:@192.168.31.1、@223.5.5.5、@1.1.1.1、系统 resolved 问 www.google.com
四个来源返回的都是同一批真实 Google IP(142.251.x)。

⚠️ 一个需要你知道的设计取舍(不是 bug)

域名类型 DNS 走向 运营商能看到吗
海外域名(geolocation-!cn、google、AI) DoH(Cloudflare / Google)经代理 ❌ 看不到
国内域名(geosite/cn、apple@cn) 明文 UDP 发往 223.5.5.5(阿里公共 DNS,直连) ✅ 能看到你查了哪些国内域名

这是国内分流的标准取舍(为了拿到准确的国内 CDN 解析)。想彻底不留痕可以改成 DoH —— 这正是
NetProxy-Magisk 的做法(它的 ali/tencent 都是
type: "https",并用 hosts 服务预解析 DoH 域名以打破自举循环):

"servers": [
  { "type": "hosts", "tag": "hosts-bootstrap",
    "predefined": { "dns.alidns.com": ["223.5.5.5", "223.6.6.6"], "doh.pub": ["120.53.53.53", "1.12.12.12"] } },
  { "type": "https", "tag": "dns-direct-ali", "server": "dns.alidns.com",
    "domain_resolver": "hosts-bootstrap", "detour": "direct" },
  { "type": "https", "tag": "dns-direct-tencent", "server": "doh.pub",
    "domain_resolver": "hosts-bootstrap", "detour": "direct" }
]

代价:多一次 TLS 握手(有缓存影响很小);若阿里 DoH 不可用,国内解析会失败。要不要上,你决定,我可以直接改好并校验。

更正(2026-10-02 晚实测):上面”国内域名 = 明文 UDP 发往 223.5.5.5 → 运营商能看到”这一行
已不适用于当前配置,这段建议已经落地(不再是可选项):/etc/sing-box/config.json 里
dns-direct-ali(type: https → dns.alidns.com)与 dns-direct-tencent(doh.pub)都用
hosts-bootstrap 预解析、直连 443,dns-direct 组也被 apple@cn / cn 规则引用。
也就是说国内解析走 DoH,运营商看不到你查了哪些国内域名(只剩到 DoH 端点的 SNI/连接元数据)。
eBPF 模式下结论相同(dig 复测结果与 TUN 时代一致)。

二、广告拦截 —— 连接层 ✅ / DNS 层曾有 bug(已修)

规则集 geosite/category-ads-all 实测内容:162 个精确域名 + 748 个后缀,共 940 条。

连接层:全部拦截 ✅

广告域名 DNS HTTPS
doubleclick.net 142.250.73.78 ❌ Recv failure: 连接被对方重置(TCP RST)
pagead2.googlesyndication.com — ❌ RST
ad.doubleclick.net — ❌ RST
对照:github.com / www.google.com ✅ ✅ 200

reject 动作对 TCP 直接回 RST,表现就是”连接被对方重置”。

DNS 层:发现规则顺序 bug 并已修复 🐛

原配置里 DNS 规则顺序是:apple@cn → cn → google/AI → !cn → 广告 reject。
广告 reject 排在最后,于是同时属于其他分类的广告域名会先被路由出去,照样解析成功:

广告域名 为什么漏
pagead.l.google.com 命中 geosite/google(第 3 条)→ 解析成 142.251.169.155
adwords.com 同上 → 172.217.221.113
guanggaoad.youku.com 命中 geosite/cn(第 2 条)→ 182.242.82.251

而路由规则里 reject 排在第 4 条(在 google/cn 之前),所以连接层拦得住 —— 两边不一致。

修复:把广告 reject 提到 DNS 规则第 1 条。现在顺序为
广告 reject → apple@cn → cn → google/AI → !cn,已通过 sing-box check(exit=0)。
修复后这类域名在 DNS 阶段直接 NXDOMAIN,浏览器不会再去尝试连接。

追注(2026-10-02 晚实测):顺序与”第 1 条”仍然对,但动作已不是 reject:现配置第 1 条是
action: predefined + rcode: NXDOMAIN。原因:DNS 层的 reject 回的是 REFUSED,
systemd-resolved 收到上游 REFUSED 后不转告客户端而是去重试其他上游,应用会一直等到超时(约 5 秒);
predefined+NXDOMAIN 是正常应答,应用立刻失败并做负缓存(详见 docs/09 §3)。
另外广告拦截的规则集已从 1 份扩到 5 份(geosite/category-ads-all + Ads_AWAvenue + anti-AD +
ads-extra + adblockfilters),DNS 规则与路由规则两边同步引用。
复测:dig +short doubleclick.net → 空;dig random-zzq.doubleclick.net → status: NXDOMAIN;
对照域名 github.com 正常解析;dig +tcp 同样被拦。

注:criteo.com、outbrain.com 不在该规则集内,不会被拦 —— 需要的话可以加 Ads_AWAvenue 规则集。

更正(2026-10-02 晚实测):加上另外 4 份规则集后,这两个域名现在已经被拦:
dig +short criteo.com、dig +short outbrain.com 都返回空;
sing-box rule-set match -f binary 显示 criteo.com 命中 anti-ad.srs 与 adblockfilters.srs,
outbrain.com 同样命中这两份。

三、分流 —— 完全正确 ✅

目标 实测出口 结论
api.ipify.org(海外) <proxy-exit-ip> 走代理节点 ✅
myip.ipip.net(国内) 中国 四川 成都 电信 直连(真实 ISP IP)✅
ip.3322.net(国内) 直连 ✅

geosite/cn + geoip/cn → direct、geolocation-!cn → Proxy 都按预期命中。

追注(2026-10-02 晚,eBPF 模式):分流结论不变(复测 api.ipify.org 与
curl -x http://127.0.0.1:7892 都返回代理出口 IP,走 socks5 也一致;myip.ipip.net 返回国内电信地址),
但机制多了一层:eBPF 入站的 local.bypass_rule_set(geoip/cn、geoip/cn-fresh、cncidr-mihomo)
让命中这三份 CN IP 表的流量在 eBPF 层就绕过 sing-box 直接出网。
所以”所有流量都经 sing-box”这个绝对化说法是错的:国内 IP 直连是有意绕过,
分流由”eBPF 绕过表 + 路由规则 3~11 条”共同决定。
另外国内回显现在优先给 IPv6(240e:… 电信地址),因为 eBPF 入站开了 ipv6: true。

四、Podman 容器代理 —— TUN 下完全透明 ✅(eBPF 下不成立,先读追注)

追注(2026-10-02 晚,eBPF 模式,模式限定):本节结论只在 TUN 模式下成立。
当前模式是 eBPF,实测 rootless podman(pasta)容器内境外走不了代理(国内直连与 DNS 劫持仍正常):

检查项(eBPF 模式复测,podman 6.1.3 rootless / pasta) 结果
容器内访问境外 https://api.ipify.org ❌ can't connect to remote host: Connection refused(没有走代理)
Clash API 连接列表 容器那条境外连接不出现(确认没被接管)
容器内访问国内 https://myip.ipip.net ✅ <home-ip>(电信,直连正常)
容器内广告域名 DNS(nslookup doubleclick.net) ✅ NXDOMAIN(DNS 劫持生效)
podman run --network=host ✅ 出口 = 代理出口 IP(可用处置)
容器 /etc/resolv.conf 3 条:169.254.1.1、192.168.31.1、fd00:6868:6868::1;前两条 Connection refused,实际生效的是第 3 条

旧解释已被推翻:「pasta 不创建宿主 socket,所以 cgroup 钩子看不见」是错的 ——
ss -tnp 能看到 passt.avx2(进程名是 passt.avx2,不是 pasta)的宿主 socket,
源地址是宿主 ULA fd00:6868:6868::71b,DNS 转发与 TCP 转发都走宿主 socket。
所以”现象”(境外不通)成立,但”机制”未知;shared 能接管的是桥接进来的包,pasta 属用户态转发,两条路都不覆盖它。
处置:容器用 --network=host,或切回 TUN。eBPF 下对应的旋钮是 bypass_private_address 与
bypass_rule_set(不是 exclude_interface,下面那句注意用的是 TUN 时代的说法),
具体卡在哪一环尚未确证(见 docs/09 §6)。以下 TUN 时代的原文保留
(注意其中 slirp 是过时说法,podman 现在用 pasta)。

沙箱内 podman 6.1.2(rootless)实测:

检查项 结果
拉取镜像 ✅ 成功(走代理)
容器内 /etc/resolv.conf nameserver 169.254.1.1(podman 网关)→ 转发到主机 → 被 sing-box 劫持
容器出口 IP <proxy-exit-ip>(= 宿主机的代理出口) ✅
容器访问国内回显 (直连) ✅

结论:TUN 的 auto_route 已经把容器(含 podman 的 NAT / slirp 路径)的转发流量一并接管,
容器内不需要设 HTTP_PROXY、不需要 --network=host,分流规则也照样生效。

注意:如果以后在配置里加 exclude_interface(例如排除 podman0),容器就会绕过代理;
目前没有这类排除项,所以是接管状态。

五、其它功能

功能 结果
mixed 本地代理口 7892 ✅ curl -x http://127.0.0.1:7892 出口 = 代理 IP
UDP / QUIC ✅ 早前实测 HTTP/3 正常(cloudflare-quic 0.30s)
规则集本地化 ✅ 15 份全部 local,启动零网络依赖(2026-10-02 晚复核:原为 8 组;其中 6 份来自自建包 /usr/share/sing-box-rule-sets/)
面板 官方 dashboard :9091/dashboard/ ✅(复测 200);MetaCubeXD 已摘除(clash_api.external_ui 已删 → :9090/ui/ 复测 404,/var/lib/sing-box/ui 不存在)

六、结论与待办

没有发现 DNS 泄露到运营商的情况(海外域名全程 DoH 经代理)。

更正(2026-10-02 晚):原文下面这句”唯一’可见’的是国内域名的明文 UDP 查询,属设计取舍,
可按第一节改为 DoH”已经过时 —— 国内解析现在同样走 DoH(dns-direct-ali / dns-direct-tencent),
见第一节的更正块。

发现并修复的问题:DNS 广告拦截规则顺序错误(已移到第 1 条)。

待你执行(应用最新配置,含 12 项改动):

cd ~/Documents/deepseek-harness/default-workspace
sudo ./switch-to-local-rules.sh

追注(2026-10-02 晚):这份”待执行”已经完成 —— 配置早已部署,运行模式也从 TUN 迁到了 eBPF。
现在切模式用仓库里的 scripts/switch-to-ebpf.sh / scripts/switch-to-tun.sh;
switch-to-local-rules.sh 是 TUN 时代的脚本,只作历史留档。

应用后建议复测:

dig +short doubleclick.net            # 期望:空(修复后 DNS 层也拦了)
dig +short pagead.l.google.com        # 期望:空
curl -s https://api.ipify.org         # 期望:代理 IP
curl -s https://myip.ipip.net         # 期望:你的电信 IP