背景
- 在火山引擎云服务器上安装好了 OpenClaw,并且按照官方文档安装 Tailscale 并启用 MagicDNS 后,再执行 apt 安装其他软件的时候失败
- 检查公网的 DNS 解析也发现出现超时(通过
nslookup zhenxxin.com) :;; communications error to 127.0.0.53#53: timed out;; communications error to 127.0.0.53#53: timed out - 网络搜索一番可以确信是 Tailscale 的 MagicDNS 影响了我们机器的 DNS 解析。但是呢,又不想直接放弃 MagicDNS 这个有用的东西一禁了之(即启动时加上
--accept-dns=false)。因此我们有了如下目标:让服务器同时满足以下三点:- 能解析火山引擎内网服务(依赖内网 DNS)
- 能使用 Tailscale MagicDNS(
*.ts.net域名) - 能正常解析公网域名
第一步:切换 resolv.conf 为 stub 模式
初始状态:/etc/resolv.conf -> /var/run/systemd/resolve/resolv.conf,内容为真实上游 DNS IP,DNS 请求绕过了 systemd-resolved 的 stub listener,导致 Tailscale 注入的 MagicDNS 规则完全不生效。
将 resolv.conf 切换为 stub 模式,让所有 DNS 请求走 127.0.0.53,再由 systemd-resolved 统一转发:
sudo ln -sf /run/systemd/resolve/stub-resolv.conf /etc/resolv.conf
同时将上游 DNS 写入 resolved 配置:
sudo mkdir -p /etc/systemd/resolved.conf.d/
sudo tee /etc/systemd/resolved.conf.d/upstream.conf << EOF
[Resolve]
DNS=100.96.0.2 100.96.0.3
FallbackDNS=1.1.1.1
DNSStubListener=yes
EOF
sudo systemctl restart systemd-resolved
重新执行 tailscale up --accept-dns 后,resolvectl status 已正确显示 MagicDNS 接管了 tailscale0 接口:

第二步:定位真正的超时原因
切换后 nslookup 依然超时,查看 systemd-resolved 状态:
systemctl status systemd-resolved

日志中反复出现:
Using degraded feature set TCP instead of UDP for DNS server 100.96.0.2
Using degraded feature set UDP instead of TCP for DNS server 100.96.0.2
说明 stub listener 正常,但它联系不上上游 DNS 100.96.0.2/3,在 TCP/UDP 之间反复重试导致超时。
第三步:确认上游 DNS 不可达
ip route get 100.96.0.2
ping -c 3 100.96.0.2
ip route show

路由表显示 100.96.0.2 走 eth0,但 ping 100% 丢包。
根因:火山引擎的 DNS IP 100.96.0.2/3 恰好落在 100.64.0.0/10 这个 CGNAT 地址段内,而 Tailscale 会无差别拦截这整个网段的所有流量。
第四步:找到 Tailscale 的拦截机制
查看策略路由:
ip rule show

注意到 5270: from all lookup 52,这是 Tailscale 的私有路由表,优先级高于 main 表(32766)。
查看表 52 内容:
ip route show table 52
输出:
100.79.205.86 dev tailscale0
100.100.100.100 dev tailscale0
100.103.212.87 dev tailscale0
100.123.222.99 dev tailscale0
表 52 里并没有 100.96.0.x 的路由,说明问题不在路由层,继续排查 iptables:
iptables -L ts-input -n -v
在 ts-input 链中发现关键规则:
DROP all -- !tailscale0 * 100.64.0.0/10 0.0.0.0/0
所有从非 tailscale0 接口进来的 100.64.0.0/10 流量全部被 DROP,已命中 661 个包。这就是根因。
第五步:在 DROP 规则之前插入豁免规则
观察 ts-input 链中已有一条对 100.115.92.0/23 的 RETURN 豁免,照此模式为我们的 DNS IP 添加豁免,插在 DROP 规则之前:
iptables -I ts-input 2 -s 100.96.0.2/32 ! -i tailscale0 -j RETURN
iptables -I ts-input 3 -s 100.96.0.3/32 ! -i tailscale0 -j RETURN
测试:
ping -c 3 100.96.0.2 # 通了
nslookup zhenxxin.com # 正常解析
第六步:持久化规则
由于系统 iptables 后端是 nftables:
iptables --version
# iptables v1.8.10 (nf_tables)
且 ts-input 链是 Tailscale 每次启动时动态创建的,重启后规则会丢失。因此最可靠的方式是通过 systemd drop-in,让规则在 Tailscale 启动后自动补充:
sudo mkdir -p /etc/systemd/system/tailscaled.service.d/
sudo tee /etc/systemd/system/tailscaled.service.d/dns-bypass.conf << 'EOF'
[Service]
ExecStartPost=/bin/sh -c 'sleep 2 && \
iptables -I ts-input 2 -s 100.96.0.2/32 ! -i tailscale0 -j RETURN && \
iptables -I ts-input 3 -s 100.96.0.3/32 ! -i tailscale0 -j RETURN'
EOF
sudo systemctl daemon-reload
验证:
sudo systemctl restart tailscaled
sleep 3
iptables -L ts-input -n -v | grep 100.96
ping -c 3 100.96.0.2
nslookup zhenxxin.com
重启整机后规则依然生效。
最终架构
应用程序
│
▼
127.0.0.53 ← stub listener (systemd-resolved)
│
├─── *.ts.net / MagicDNS 域 ──→ 100.100.100.100 (走 tailscale0)
│
└─── 公网 / 内网域名 ──────────→ 100.96.0.2 / 100.96.0.3
(iptables 豁免,走 eth0)
踩坑总结
| 现象 | 原因 |
|---|---|
| nslookup 超时 | resolv.conf 未走 stub,MagicDNS 规则不生效 |
| resolved 日志 TCP/UDP 反复切换 | 上游 DNS 被 Tailscale iptables 拦截不可达 |
| ping 100.96.0.2 路由正确但丢包 | ts-input 链 DROP 了整个 100.64.0.0/10 段 |
| nftables 持久化报错 | iptables-nft 管理的表不能直接用 nft 保存 |
| 规则重启丢失 | ts-input 链由 Tailscale 动态创建,需 systemd drop-in 补救 |