阿里云 CGNAT 地址与 Tailscale 的 DNS 冲突:一次真实的排查记录与可行方案

数据中心内部布线

本文基于一次真实案例,记录一台阿里云 ECS 在启用 Tailscale 后突然失去外网访问能力的全过程,并给出五种经过验证的解决方案,帮助你在不牺牲功能的前提下优雅地规避冲突。


目录

  1. 问题现象:服务器突然“失联”
  2. 初步排查:把嫌疑锁定在 DNS
  3. 深入追踪:Tailscale 的防火墙规则
  4. 根源分析:RFC 6598 与阿里云 CGNAT 地址
  5. 五种可行方案与利弊对照
  6. 如何快速验证方案是否生效
  7. 小结与下一步建议

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/10RFC 6598 中被保留为 运营商级 NAT (Carrier-Grade NAT) 地址段,用于 ISP 做 NAT 时避免与内网冲突。Tailscale 把它当成“只允许来自 Tailscale 接口的地址段”是符合规范的。

4.2 阿里云为何用 CGNAT 当内网 DNS

阿里云在 VPC 内把 100.100.2.136100.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. 如何快速验证方案是否生效

  1. DNS 测试

    dig @100.100.2.136 example.com +short
    

    出现 IP 即成功。

  2. 外网连通性

    curl -I https://example.com
    
  3. Tailscale 功能

    tailscale ping some-node
    tailscale status
    
  4. 规则持久化
    重启机器或 systemctl restart tailscaled 后再次检查,确保规则仍在。


7. 小结与下一步建议

  • 冲突本质:Tailscale 遵循 RFC 6598,阿里云把 CGNAT 段用作内网 DNS,二者目标不同导致“误伤”。
  • 最稳妥方案:在 Tailscale 的 ts-input 链最前面插入两条白名单,配合 systemd 守护,兼顾功能与稳定。
  • 长期展望:已向阿里云和 Tailscale 双方反馈,未来版本可能内置豁免逻辑,届时可移除临时脚本。
服务器机房

如果你在实施过程中遇到新的状况,欢迎在评论区留言,一起把踩过的坑补全,让更多运维同学少走弯路。