有时 VPC 里只有一台机器适合安装 Tailscale,但其他 VM 也需要访问 Tailscale 后面的远端内网段。这个场景可以把这台机器当成一个 proxy 节点:VPC 内机器把目标网段路由到 proxy,proxy 再通过 tailscale0 转发出去。
示例环境:
1 | A 节点: 172.16.10.8 |
推荐做法是 NAT 网关模式:远端只看到 proxy 的 Tailscale IP,看不到 A 节点真实 IP。这样配置最少,也不需要改远端节点路由。
先看数据包怎么走
A 节点访问 192.168.81.248 时,原始包大致是:
1 | src=172.16.10.8 |
A 把包交给 proxy 后,proxy 要做两件事:
- 允许包从
eth0转发到tailscale0。 - 把源地址改成 proxy 的 Tailscale 地址。
第一件事靠 FORWARD 规则。第二件事靠 MASQUERADE,也就是 NAT。
如果只有 FORWARD,没有 NAT,远端看到的来源还是:
1 | 172.16.10.8 |
但远端 Tailscale 网络通常不知道怎么回到 172.16.10.8,表现就是请求可能出去了,但回包回不来。
加了 NAT 后,远端看到的来源会变成:
1 | proxy 的 Tailscale IP |
这样远端只需要回给 proxy,Tailscale 网络天然知道怎么回。
一句话记住:
1 | FORWARD 负责「能不能转发」 |
推荐模式:NAT 网关
NAT 模式适合这些场景:
- VPC 内多台 VM 通过一个 proxy 访问 Tailscale 网段。
- 不希望把整个 VPC 暴露进 Tailscale。
- 不想改远端节点路由。
- 远端只需要知道 proxy,不需要知道每台 VPC VM。
它的代价也很明确:远端看到的都是 proxy 的 Tailscale IP,不是 A 节点的真实 IP。
proxy 节点配置
下面假设:
1 | VPC 网段: 172.16.10.0/24 |
启动 Tailscale 并接收远端路由
proxy 节点先启动 Tailscale,并接收远端发布的 subnet route:
1 | tailscale up \ |
这里我要访问的192.168.81.0/24这个网段,首先确认远端网段确实在 Tailscale 路由里:
1 | root@proxy:~# ip route show table 52 | grep 192.168.81 |
期望目标走 tailscale0。如果这里都没走 tailscale0,后面加 iptables 也没用。
开启 Linux 转发
1 | cat >/etc/sysctl.d/99-proxy-forward.conf <<'EOF' |
这里有两个关键配置:
| 配置 | 含义 |
|---|---|
net.ipv4.ip_forward=1 |
允许 proxy 帮其他机器转发 IP 包 |
rp_filter=0 |
避免多网卡、多路由表场景下,因为回程路径不完全一致而误丢包 |
rp_filter 在普通单网卡服务器上通常不容易碰到,但在 VPC 网卡、tailscale0、策略路由同时存在时,关掉它更稳。
配置转发和 NAT
1 | iptables -A FORWARD -i eth0 -o tailscale0 -j ACCEPT |
三条规则分别是:
| 规则 | 作用 |
|---|---|
eth0 -> tailscale0 ACCEPT |
允许 VPC 到 Tailscale 的转发流量 |
tailscale0 -> eth0 RELATED,ESTABLISHED |
只允许已建立连接的回包回来 |
POSTROUTING MASQUERADE |
把 VPC VM 的源地址动态改成 proxy 的 Tailscale 地址 |
第二条只放行已建立连接的回包,不开放远端主动访问 VPC。
MASQUERADE 是什么
MASQUERADE 是 iptables NAT 表里的一种动态 SNAT。
普通 SNAT 要明确写死出口源地址:
1 | iptables -t nat -A POSTROUTING \ |
这里的 100.x.x.x 是 proxy 的 Tailscale IP。
而 MASQUERADE 不需要写死这个 IP:
1 | iptables -t nat -A POSTROUTING \ |
它会在包从 tailscale0 发出去时,自动使用 tailscale0 当前 IP 作为源地址。
也就是说,它把:
1 | src=172.16.10.8 |
动态改成:
1 | src=proxy 的 tailscale0 当前 IP |
这里用 MASQUERADE 的原因是 Tailscale 地址由 Tailscale 分配,重连、迁移、重新登录后都有可能变化。用 MASQUERADE 不需要关心当前 Tailscale IP 是多少。
两者区别可以这样记:
| 方式 | 特点 | 适合场景 |
|---|---|---|
SNAT |
源地址写死 | 出口 IP 固定 |
MASQUERADE |
自动取出口网卡当前 IP | VPN、Tailscale、拨号网络这类出口地址可能变化的场景 |
A 节点或 VPC 路由配置
A 节点不需要安装 Tailscale,只需要让去往远端 Tailscale 网段的流量先到 proxy。
最直接的方式是在 A 节点上加静态路由:
1 | ip route add 192.168.81.0/24 via 172.16.10.3 dev eth0 |
验证单机路由:
1 | ip route get 192.168.81.248 |
如果 A 上有多块网卡,dev eth0 要替换成能到达 proxy 的那块网卡。
如果 VPC 内有多台 VM 都要访问这个远端网段,更推荐把这条路由放到 VPC 路由器或云平台路由表里:
1 | 目标网段: 192.168.81.0/24 |
这样整个 VPC 内的 VM 都可以通过 proxy 访问 192.168.81.0/24,不需要在每台 VM 上单独执行 ip route add。
需要注意的是,这只是把“入口路由”集中到了 VPC 路由器上。proxy 节点上的 ip_forward、FORWARD 和 MASQUERADE 仍然需要配置,否则包到了 proxy 以后还是转不出去,或者回包回不来。
如果云平台要求下一跳 VM 具备转发能力,还要在云平台侧开启对应的转发设置,或关闭类似 source/destination check 的限制。不同平台叫法不一样,核心都是允许 proxy 转发非本机源目的的流量。
排查思路
排查时按数据包路径一段一段确认,不要一上来就猜 NAT 或 Tailscale。
入口路由是否交给 proxy
1 | ip route get 192.168.81.248 |
如果是在 A 节点上直接加的静态路由,期望看到 via 172.16.10.3。
如果是在 VPC 路由器或云路由表里集中配置,A 节点上可能只看到默认网关,这是正常的。这时要检查 VPC 路由表里是否存在:
1 | 192.168.81.0/24 -> proxy 节点 172.16.10.3 |
也可以直接在 proxy 上抓 eth0:
1 | tcpdump -ni eth0 host 172.16.10.8 |
如果 A 发起访问时 proxy 的 eth0 能看到包,说明入口路由已经把流量交给 proxy。
proxy 是否走 Tailscale
1 | ip route get 192.168.81.248 |
如果 proxy 本机去远端网段都不走 tailscale0,先查 Tailscale route,而不是查 A。
iptables 规则是否命中
1 | iptables -L FORWARD -n -v |
看对应规则的 packet / byte 计数是否增长。
抓包看路径
在 proxy 上抓两侧接口:
1 | tcpdump -ni eth0 host 172.16.10.8 |
常见判断:
| 现象 | 优先检查 |
|---|---|
eth0 有包,tailscale0 没包 |
ip_forward、FORWARD、proxy 路由 |
tailscale0 有出包,但 A 收不到回包 |
MASQUERADE、回包 conntrack、远端防火墙 |
| proxy 自己也访问不了远端 | Tailscale route、远端 subnet route 是否发布和审批 |
不加 NAT 可以吗
可以,但那就变成纯路由模式。
纯路由模式下,远端会看到 A 的真实来源:
1 | 172.16.10.8 |
所以必须让 Tailscale 网络知道怎么回到 VPC 网段。也就是在 proxy 上发布 VPC 网段:
1 | tailscale up \ |
然后在 Tailscale 管理后台审批这个路由。
两种模式对比:
| 模式 | 优点 | 代价 |
|---|---|---|
| NAT 模式 | 配置简单,远端只看到 proxy,推荐日常使用 | 远端看不到 A 的真实 IP |
| 纯路由模式 | 远端能看到 A 的真实 IP | 需要发布 VPC 网段,并在后台审批路由 |
大多数场景下,我会先用 NAT 模式。只有远端必须区分具体 VPC VM 时,才考虑纯路由模式。
不要把公网网段随便丢给 proxy
比如这条路由:
1 | ip route add 139.198.183.0/24 via 172.16.10.3 |
只有在 proxy 的 Tailscale 路由里也存在这个网段时才有意义:
1 | ip route show table 52 | grep 139.198.183 |
如果这个网段不在 Tailscale 路由里,A 把包交给 proxy 也没用。公网 HTTP / HTTPS 访问更适合用应用层代理:
1 | export HTTP_PROXY=http://proxy:port |
路由和代理解决的是不同层的问题,不要混在一起。
总结
这个方案的核心很简单:
- 其他 VM 或 VPC 路由器:把 Tailscale 远端网段 route 到 proxy。
- proxy:负责
ip_forward、FORWARD放行、MASQUERADE改源地址。 FORWARD:决定包能不能穿过 proxy。MASQUERADE:动态 SNAT,决定远端回包能不能回到 proxy,再回到 A。
最终结论:
1 | 让其他 VM 通过 proxy 访问 Tailscale 网段时,光能转发还不够。 |
