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_rewriteonvirbr0,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,
源地址是宿主 ULAfd00: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











