省流——快速解决方案

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# 写入脚本
cat > /usr/local/bin/fix-ts-dns.sh << 'EOF'
#!/bin/bash
while true; do
if ! iptables -C ts-input -s 100.100.2.0/24 -i eth0 -j ACCEPT 2>/dev/null; then
iptables -I ts-input 1 -s 100.100.2.0/24 -i eth0 -j ACCEPT
fi
sleep 30
done
EOF

#添加权限
chmod +x /usr/local/bin/fix-ts-dns.sh

# 写入 systemd 服务
cat > /etc/systemd/system/fix-ts-dns.service << 'EOF'
[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
EOF

#运行
systemctl daemon-reload
systemctl enable --now fix-ts-dns.service

问题现象

在阿里云 ECS(VPC 网络)上安装并启动 Tailscale 后,服务器出现”失联”:SSH 能连但 curlapt update 全部超时,ping 外网 IP 却通。

原理

RFC 6598 与 CGNAT

100.64.0.0/10 是 RFC 6598 保留的运营商级 NAT(CGNAT)地址段,ISP 用于大规模 NAT 时避免与内网冲突。

冲突根源

  • Tailscale:启动后在 iptables 的 ts-input 链插入一条规则:

    1
    -A ts-input -s 100.64.0.0/10 ! -i tailscale0 -j DROP

    含义:源地址落在 CGNAT 段、但不从 tailscale0 虚拟网卡进入的流量,全部丢弃。这是合规范的防御策略。

  • 阿里云:VPC 内网将 100.100.2.0/24 网段用作内部服务地址,包括:

    • DNS 服务器:100.100.2.136100.100.2.138
    • 镜像源:100.100.2.158mirrors.cloud.aliyuncs.com

100.100.x.x 正好落在 100.64.0.0/10 范围内,阿里云内网服务的所有回复数据包都被 Tailscale 防火墙误杀。

为什么 ping 外网 IP 通但 curl 不通

ping 8.8.8.8 是 ICMP 协议,源 IP 是公网地址,不在 100.64.0.0/10 范围内,不被拦截。

curl / apt 需要先做 DNS 解析 —— 阿里云 DNS 回复的 UDP 包源地址是 100.100.2.136,被 Tailscale 丢弃,DNS 超时导致整个请求失败。

踩坑过程:为什么 DNS 白名单不够

查了一些资料后,第一版方案是只放行两台 DNS 服务器的 IP:

1
2
iptables -I ts-input 1 -s 100.100.2.136/32 -j ACCEPT
iptables -I ts-input 1 -s 100.100.2.138/32 -j ACCEPT

结果如下:

1
2
3
4
5
6
7
8
9
10
11
# DNS 解析恢复了
$ dig @100.100.2.136 mirrors.cloud.aliyuncs.com +short
100.100.2.158

# 但实际下载仍超时
$ curl -I http://mirrors.cloud.aliyuncs.com
curl: (28) Connection timed out

# 路由正常,走 eth0,不是 tailscale0
$ ip route get 100.100.2.158
100.100.2.158 via 172.16.79.253 dev eth0

只救了 DNS 解析,但镜像源返回的 TCP 数据包(HTTP 80/443)同样来自 100.100.2.0/24 网段,照样被 ts-input 的 DROP 规则丢掉。只放行 DNS IP 不够,镜像源一样被卡。

最终方案:子网白名单 + systemd 守护

将白名单从两个精确 IP 扩展到整个 /24 子网,一次性放通阿里云 VPC 内网所有服务(DNS + 镜像源 + OSS + RDS 内网地址等),同时 Tailscale 防火墙对其他 CGNAT 流量仍然有效,安全无损。

第 1 步:创建守护脚本

/usr/local/bin/fix-ts-dns.sh

1
2
3
4
5
6
7
#!/bin/bash
while true; do
if ! iptables -C ts-input -s 100.100.2.0/24 -i eth0 -j ACCEPT 2>/dev/null; then
iptables -I ts-input 1 -s 100.100.2.0/24 -i eth0 -j ACCEPT
fi
sleep 30
done

如果网卡名不是 eth0(极少数机型),先执行 ip addr 确认后替换。

脚本每 30 秒检查 ts-input 链顶部是否存在白名单规则,不存在就自动插入。即使 tailscaled 重启或更新后删除规则,最多 30 秒就能自动修复。

限定 -i eth0 确保仅放行从 VPC 内网网卡进入的 100.100.2.0/24 流量。如果某台 Tailscale 节点恰好也分配了该网段的 IP,其从其他接口进入的流量不受影响,仍走 Tailscale 原有的安全策略。

第 2 步:创建 systemd 服务

/etc/systemd/system/fix-ts-dns.service

1
2
3
4
5
6
7
8
9
10
11
[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

依赖 tailscaled.service,确保 Tailscale 先启动、脚本后启动。Restart=always 保证脚本异常退出后自动拉起。

第 3 步:部署

1
2
3
chmod +x /usr/local/bin/fix-ts-dns.sh
systemctl daemon-reload
systemctl enable --now fix-ts-dns.service

第 4 步:验证

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# DNS 解析
$ dig @100.100.2.136 mirrors.cloud.aliyuncs.com +short
100.100.2.158

# 外网连通
$ curl -I http://mirrors.cloud.aliyuncs.com
HTTP/1.1 200 OK

# 镜像源可用
$ apt update
Hit:1 http://mirrors.cloud.aliyuncs.com/ubuntu jammy InRelease
...

# Tailscale 功能正常
$ tailscale status
# 节点列表正常显示

# 规则持久化测试 — 重启 Tailscale 后确认规则自动恢复
$ systemctl restart tailscaled
$ sleep 35
$ iptables -S ts-input | head -1
-A ts-input -s 100.100.2.0/24 -i eth0 -j ACCEPT

为什么这个方案是安全的

  • 仅放行 100.100.2.0/24(阿里云 VPC 内网段),其余 100.64.0.0/10 范围内的流量仍被 Tailscale 拦截
  • 限定 -i eth0,即使某台 Tailscale 节点恰好分配了该网段 IP,其从非 VPC 网卡进入的流量不会被白名单误放行
  • 未关闭 Tailscale 防火墙(--netfilter-mode=off),子网路由和 exit-node 功能保留
  • 未改用公共 DNS,阿里云内网域名(OSS、RDS 等)解析不受影响,不产生额外公网流量费用
  • systemd 守护机制保证重启不丢失规则

参考资料