阿里云 CGNAT 地址与 Tailscale 的 DNS 冲突:一次真实的排查记录与可行方案
本文基于一次真实案例,记录一台阿里云 ECS 在启用 Tailscale 后突然失去外网访问能力的全过程,并给出五种经过验证的解决方案,帮助你在不牺牲功能的前提下优雅地规避冲突。
目录
-
问题现象:服务器突然“失联” -
初步排查:把嫌疑锁定在 DNS -
深入追踪:Tailscale 的防火墙规则 -
根源分析:RFC 6598 与阿里云 CGNAT 地址 -
五种可行方案与利弊对照 -
如何快速验证方案是否生效 -
小结与下一步建议
1. 问题现象:服务器突然“失联”
在阿里云(华北 2,VPC 网络)上运行的 Ubuntu 22.04 ECS,原本通过 Tailscale 与多台节点组成了一个虚拟子网。某天运维同事反馈:
-
☾ SSH 还能连上(走 Tailscale 虚拟网卡 tailscale0
)。 -
☾ 但服务器本身无法 curl https://example.com
,也无法apt update
。 -
☾ 重启 Tailscale 服务后短暂恢复,几分钟后再次失去外网。
2. 初步排查:把嫌疑锁定在 DNS
2.1 看网络通不通
$ ping 8.8.8.8
PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.
64 bytes from 8.8.8.8: icmp_seq=1 ttl=115 time=3.05 ms
IP 层没问题,继续看 DNS:
$ dig @100.100.2.136 example.com
;; connection timed out; no servers could be reached
100.100.2.136 正是阿里云内网 DNS 的默认地址。DNS 解析失败,服务器自然“失联”。
2.2 怀疑 Magic DNS
Tailscale 的 Magic DNS 会把节点名解析为 100.x 地址。最初我们怀疑是 Magic DNS 把系统 /etc/resolv.conf
改坏了,于是关闭 Magic DNS:
sudo tailscale set --accept-dns=false
重启 Tailscale,问题依旧。Magic DNS 并不是真凶。
3. 深入追踪:Tailscale 的防火墙规则
3.1 打印 iptables 日志
为了把包到底被哪条规则丢掉,我们在 filter
表上加 LOG:
sudo iptables -I INPUT 1 -j LOG --log-prefix "INPUT_PKT: "
复现问题后查看 /var/log/kern.log
,发现:
INPUT_PKT: IN=eth0 OUT= MAC=... SRC=100.100.2.136 DST=172.31.x.x PROTO=UDP DPT=53
包被丢弃,但还不知道是哪条规则干的。
3.2 找到 Tailscale 注入的规则
sudo iptables -S | grep 100.64
-A ts-input -s 100.64.0.0/10 ! -i tailscale0 -j DROP
这条由 tailscaled
自动插入的规则意思是:
-
☾ 源地址在 100.64.0.0/10
(即 CGNAT 段); -
☾ 不是从 tailscale0
网卡进来的包; -
☾ 直接丢弃。
阿里云内网 DNS 的地址 100.100.2.136
刚好落在这个段内,于是被误杀。
4. 根源分析:RFC 6598 与阿里云 CGNAT 地址
4.1 什么是 CGNAT
100.64.0.0/10
在 RFC 6598 中被保留为 运营商级 NAT (Carrier-Grade NAT) 地址段,用于 ISP 做 NAT 时避免与内网冲突。Tailscale 把它当成“只允许来自 Tailscale 接口的地址段”是符合规范的。
4.2 阿里云为何用 CGNAT 当内网 DNS
阿里云在 VPC 内把 100.100.2.136
、100.100.2.138
作为内网 DNS 服务地址,初衷是:
-
☾ 地址在公网不可路由,避免外泄; -
☾ 与经典网络互通时不会冲突。
但这与 RFC 6598 的本意并不完全一致,于是与 Tailscale 的默认防火墙策略产生了冲突。
5. 五种可行方案与利弊对照
下面给出五种经过验证的方案,按“侵入性”从低到高排序。你可以根据业务场景挑一条最合适的。
方案 | 一句话描述 | 优点 | 缺点 | 适用场景 |
---|---|---|---|---|
① 手动删除 DROP 规则 | iptables -D ts-input ... |
立即生效 | 每次重启 Tailscale 会恢复 | 临时调试 |
② 顶部插入白名单规则 | iptables -I ts-input ... 允许 100.100.2.136 |
对业务无感 | 规则可能被 Tailscale 覆盖 | 单台机器、不频繁重启 |
③ 脚本轮询维护 | cron 每分钟检查并修复 | 自动化 | 系统复杂度增加 | 多台机器、能写脚本 |
④ 改走公共 DNS | 把 /etc/resolv.conf 换成 8.8.8.8、1.1.1.1 |
根治冲突 | 无法解析阿里云内网域名,OSS、RDS 内网地址失效 | 不使用阿里云内网产品 |
⑤ 关闭 Tailscale 防火墙 | tailscale up --netfilter-mode=off |
不碰 iptables | 失去子网路由、exit-node 等高级功能 | 仅做点对点互联 |
5.1 方案 ①:临时删除规则
sudo iptables -D ts-input -s 100.64.0.0/10 ! -i tailscale0 -j DROP
验证:
dig @100.100.2.136 example.com
能解析即成功。但重启 Tailscale 后规则会再次出现。
5.2 方案 ②:插入白名单
sudo iptables -I ts-input 1 \
-s 100.100.2.136/32 -j ACCEPT
sudo iptables -I ts-input 1 \
-s 100.100.2.138/32 -j ACCEPT
建议把这两条写进 /etc/rc.local
或 systemd service,确保开机后最早执行。
5.3 方案 ③:守护脚本
/usr/local/bin/fix-ts-dns.sh
#!/bin/bash
while true; do
if ! iptables -C ts-input -s 100.100.2.136/32 -j ACCEPT 2>/dev/null; then
iptables -I ts-input 1 -s 100.100.2.136/32 -j ACCEPT
fi
sleep 30
done
配合 systemd service:
# /etc/systemd/system/fix-ts-dns.service
[Unit]
Description=Keep Aliyun DNS whitelist in Tailscale chain
After=tailscaled.service
[Service]
Type=simple
ExecStart=/usr/local/bin/fix-ts-dns.sh
Restart=always
[Install]
WantedBy=multi-user.target
5.4 方案 ④:改用公共 DNS
sudo rm -f /etc/resolv.conf
echo "nameserver 8.8.8.8" | sudo tee /etc/resolv.conf
如果你用 NetworkManager,把 DNS 改为 8.8.8.8,1.1.1.1
并重启连接即可。
注意:OSS、RDS、SLB 的内网域名将解析成公网地址,流量会走公网,可能 产生额外费用 或 性能下降。
5.5 方案 ⑤:关闭 Tailscale 防火墙
sudo tailscale up --netfilter-mode=off
验证:
sudo iptables -S | grep ts-
应无任何 ts-
链。副作用是:
-
☾ 不能通过 Tailscale 子网路由访问局域网; -
☾ 不能当 exit-node; -
☾ 不能限制来源流量。
6. 如何快速验证方案是否生效
-
DNS 测试
dig @100.100.2.136 example.com +short
出现 IP 即成功。
-
外网连通性
curl -I https://example.com
-
Tailscale 功能
tailscale ping some-node tailscale status
-
规则持久化
重启机器或systemctl restart tailscaled
后再次检查,确保规则仍在。
7. 小结与下一步建议
-
☾ 冲突本质:Tailscale 遵循 RFC 6598,阿里云把 CGNAT 段用作内网 DNS,二者目标不同导致“误伤”。 -
☾ 最稳妥方案:在 Tailscale 的 ts-input
链最前面插入两条白名单,配合 systemd 守护,兼顾功能与稳定。 -
☾ 长期展望:已向阿里云和 Tailscale 双方反馈,未来版本可能内置豁免逻辑,届时可移除临时脚本。
如果你在实施过程中遇到新的状况,欢迎在评论区留言,一起把踩过的坑补全,让更多运维同学少走弯路。