服务器大改造(一)————Tailscale异地无感组网实践

1.碎碎念/前言

由于近期给服务器加装了硬盘,心血来潮决定把服务器上的虚机重新规划一下…而从组网方式入手是一个很好的开始————为什么要组网呢,一是直接把服务器完全暴露在公网并不安全,二是没有公网IP(硬伤啊!)在改造前,服务器中有一台Windows虚机用于挂网课、跑tailscale宣告子网路由之类的用途,正常来说是没有什么太大问题的,直到上周用于运行游戏服务器发现,好友连进来会有奇怪的丢包现象,而不经过OpenWRT旁路由就不会有这个问题,并且能拿到一个v6的路由,于是决定把Tailscale转移到旁路由(网关)上,同时在宿舍的路由(Padavan)也安装Tailscale并宣告路由,这样 两个内网下的设备就无需额外安装客户端即可相互访问了。

本来以为会很好折腾,但是,足足折腾了我一个晚上啊!!

2. 环境条件

  • 内网 A (主服务器网络)
    • 网关: 10.0.0.254 (旁路由)
    • 网段: 10.0.0.0/24
    • OS: OpenWRT
  • 内网 B (宿舍网络)
    • 网关:192.168.8.1
    • 网段:192.168.8.0/24
    • OS: Padavan
  • 内网 C
    • 此网络不直接参与上述两个子网的无缝互联,但目标是让内网 A 和 B 中的设备能通过内网地址访问到内网 C 中的特定设备。
    • 网段:192.168.40.0/24
    • 设备 C (在内网 C 中运行 Tailscale): 192.168.40.200

首先,确保你已在两端的路由器上安装好 Tailscale。Padavan 系统通常自带 Tailscale,在 扩展功能 菜单中启用即可;对于 OpenWRT,可以参考 openwrt-tailscale-enabler 进行安装。

接着,在两台路由器上启动 Tailscale,并使用 --advertise-routes 参数来宣告各自的本地子网。添加 --accept-dns=false 参数是为了防止 Tailscale 的 DNS 设置与路由器上其他可能存在的 DNS 服务(如 AdGuard Home等)冲突。

# 子网 A (OpenWRT, 10.0.0.0/24) 启动命令:
tailscale up --advertise-routes=10.0.0.0/24 --accept-dns=false --accept-routes=true

# 子网 B (Padavan, 192.168.8.0/24) 启动命令:
tailscale up --advertise-routes=192.168.8.0/24 --accept-dns=false --accept-routes=true

对于 Padavan 路由器,请务必在后台管理的 Tailscale 设置页面中,勾选 【开启】Tailscale 自定义参数 选项,并将对应的启动命令填入输入框后应用设置。

特别注意: 不要直接通过 SSH 命令行来启动 Tailscale 并设置参数,因为这些设置很可能在路由器重启或 Web UI 配置刷新时被覆盖掉!(我在这里排查了一个小时的故障,最后才发现是 Web UI 的启动配置覆盖了我手动输入的命令导致无法访问。。)

配置完成后,你应该会发现子网 A 和 B 已经可以互相访问了(如果不行,尝试重启路由器或稍等片刻待路由表更新)。但这时遇到一个新的问题:192.168.40.200已经向tailnet宣告了自己的IP地址,为什么还是无法使用内网C的IP地址访问那台运行 Tailscale 的设备(192.168.40.200)呢?我们来分析一下数据包的流向:

  • 去程: 内网 A 设备 -> 路由器 A (OpenWRT) -> Tailscale 网络 -> 设备 C (192.168.40.200) -> 内网 C 的目标设备
  • 回程: 内网 C 的目标设备 -> 设备 C (192.168.40.200) -> ? (设备 C 缺少返回路由) -> 内网 C 的默认网关

问题在于设备 C(192.168.40.200)。因为它没有启用 --accept-routes 选项来接收来自 Tailscale 网络的路由信息,所以当它收到来自内网 A 设备的回复包时,它不知道如何将这个包路由回 Tailscale 网络。同时,数据包的源地址也没有被转换(SNAT)。最终,回复包被错误地发送到了内网 C 的默认网关,而不是通过 Tailscale 网络转发回内网 A,导致访问失败。

理论上, Tailscale 的 --snat-subnet-routes 参数应该能处理源地址转换,但在Padavan下似乎未生效(OpenWRT由于条件限制未验证),或者我的理解有偏差,这个问题后面有空的时候再看一下

有两种解决方案:

解决方案 1:在子网 B 的路由器 (Padavan) 上配置 SNAT

对需要访问内网 C 设备的流量,在 OpenWRT 路由器上添加 iptables 规则进行源地址转换,使其看起来像是由 OpenWRT 本身发起的。

# 允许内网 B (192.168.8.0/24) 访问内网 C 设备 (192.168.40.200,192.168.5.88),流量从 tailscale0 出去时做 SNAT
iptables -t nat -A POSTROUTING -s 192.168.8.0/24 -d 192.168.40.200/32 -o tailscale0 -j MASQUERADE
iptables -t nat -A POSTROUTING -s 192.168.8.0/24 -d 192.168.5.88/32 -o tailscale0 -j MASQUERADE

解决方案 2:在设备 C (192.168.40.200) 上启用–accept-routes=true

在设备 C 上启动 Tailscale 时添加 --accept-routes=true 参数即可。这样设备 C 就能学习到来自 Tailscale 网络(包括子网 A 和 B)的路由。

这样一来,子网 A 和 B 中的设备就可以通过 Tailscale 网络访问到内网 C 中的设备了。

4.性能瓶颈

不过,需要注意性能问题。在性能较弱的路由器上运行 Tailscale,通常只能满足基本的连通性(实现“从 0 到 1”的访问),无法满足大带宽场景的需要,为什么这么说呢?请看VCR:

测试 1: Tailscale 运行在 newifi 3 路由器上

[ ID] Interval           Transfer     Bitrate
[  5]   0.00-1.00   sec  2.74 MBytes  23.0 Mbits/sec                  
[  5]   1.00-2.00   sec  3.62 MBytes  30.4 Mbits/sec                  
[  5]   2.00-3.00   sec  3.38 MBytes  28.3 Mbits/sec                  
[  5]   3.00-4.00   sec  3.51 MBytes  29.5 Mbits/sec                  
[  5]   4.00-5.00   sec  3.67 MBytes  30.8 Mbits/sec                  
[  5]   5.00-6.00   sec  3.59 MBytes  30.1 Mbits/sec                  
[  5]   6.00-7.00   sec  3.24 MBytes  27.1 Mbits/sec                  
[  5]   7.00-8.00   sec  3.53 MBytes  29.6 Mbits/sec                  
[  5]   8.00-9.00   sec  3.24 MBytes  27.2 Mbits/sec                  
[  5]   9.00-10.00  sec  3.78 MBytes  31.7 Mbits/sec                  
[  5]  10.00-10.17  sec   495 KBytes  24.4 Mbits/sec                  
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval           Transfer     Bitrate
[  5]   0.00-10.17  sec  34.8 MBytes  28.7 Mbits/sec                  receiver
iperf Done.

**测试 2: Tailscale 运行在 PC 上 **

[ ID] Interval           Transfer     Bandwidth
[  4]   0.00-1.00   sec  22.1 MBytes   186 Mbits/sec
[  4]   1.00-2.00   sec  10.5 MBytes  88.1 Mbits/sec
[  4]   2.00-3.00   sec  12.8 MBytes   107 Mbits/sec
[  4]   3.00-4.00   sec  11.2 MBytes  93.9 Mbits/sec
[  4]   4.00-5.00   sec  10.8 MBytes  90.9 Mbits/sec
[  4]   5.00-6.00   sec  12.1 MBytes   101 Mbits/sec
[  4]   6.00-7.00   sec  11.5 MBytes  96.4 Mbits/sec
[  4]   7.00-8.00   sec  11.7 MBytes  98.1 Mbits/sec
[  4]   8.00-9.00   sec  11.1 MBytes  92.7 Mbits/sec
[  4]   9.00-10.00  sec  12.1 MBytes   102 Mbits/sec
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval           Transfer     Bandwidth       Retr
[  4]   0.00-10.00  sec   129 MBytes   108 Mbits/sec  4318             sender
[  4]   0.00-10.00  sec   126 MBytes   106 Mbits/sec                  receiver
iperf Done.

这是因为 Tailscale 通过加密隧道通信,需要消耗不少 CPU 资源进行加解密运算。以我的 newifi 3 为例,实测通过路由器转发的 Tailscale 速率只能达到 30 Mbps 左右。相比之下,在我的 PC 上直接运行 Tailscale 则可以轻松跑满 100 Mbps 的上行带宽。如果你对传输速度有较高要求,强烈建议在需要高速访问的终端设备(如 PC)上再单独运行一个 Tailscale 客户端(这与路由器上宣告子网路由并不冲突)。

附录

一些可能用上的排错命令,需要根据实际情况进行修改

iptables -t nat -nvL
ip route show table all | grep tailscale
tcpdump -i tailscale0 -n 'not port 22'
iptables -nvL #确保里面有ts-input和ts-forward这样的链,如果没有,用tailscale netcheck/tailscale status检查一下