Subnet routers

问题

  1. 如何让 tailnet 访问那些无法/不便安装 Tailscale 客户端的设备?
  2. 快速将一个子网加入 tailnet

tailscale 解决方案

https://tailscale.com/docs/features/subnet-routers

在目标物理网络中, 指定一台已经安装 tailscale 的设备作为 “网关 / 代理”. 这台设备向 tailnet 宣告 (Advertise) 自己能够代表整个子网 (例如: 192.168.1.0/24)

当 Tailnet 中的其他设备需要访问该子网内任意设备时, 流量会被自动导向这个”网关”, 再由它转发到目标设备

工作流程

  • 宣告: 指定一台 tailscale 客户端通过 --advertise-routes 参数向 tailscale 的协调服务器宣告它可路由的子网
  • 批准: 管理员必须在 tailscale 管理后台批准该路由, 它才生效
  • 路由注入: 批准后, 协调服务器会将此路由信息注入到 Tailscale 中所有已启用接收子网路由 --accept-routes 的客户端 (默认启用)
  • 访问: 当客户端访问目标子网时, 系统会根据路由表将流量通过加密隧道发送给”网关”, 再由其转发至目标

注意

  • 默认无限制, 任意 tailnet 客户端都可以访问宣告的子网, 可在 ACL 配置权限

  • 同子网冲突:

    • 若你的 tailscale 客户端所在的本地网络与它接收到的子网路由完全重叠 (例如: 都是 192.168.1.0/24)
    • 这时候客户端路由表会出现两条完全相同的路由, 操作系统可能会错误地将本地设备的流量发到远程的子网路由器, 导致本地网络访问失败
  • Linux 必须开启 IP forwarding: 内核默认不转发跨网卡的包, 不开的话”宣告了、后台也批准了、就是 ping 不通”. Exit Node 同理

    echo 'net.ipv4.ip_forward = 1' | sudo tee -a /etc/sysctl.d/99-tailscale.conf
    echo 'net.ipv6.conf.all.forwarding = 1' | sudo tee -a /etc/sysctl.d/99-tailscale.conf
    sudo sysctl -p /etc/sysctl.d/99-tailscale.conf
  • 默认开启 SNAT: 子网内设备看到的源 IP 是子网路由器自己, 而不是发起访问的那台 tailnet 设备的 100.x 地址. 若子网侧要按原始 IP 做访问控制/审计, 用 --snat-subnet-routes=false 关闭 (关闭后需自行保证子网侧有回程路由指向路由器)

使用示例

注意: 需要在管理后台批准该路由后才生效

  • tailscale 客户端A, 位于网段 192.168.1.0/24
    • 共享整个网段: tailscale set --advertise-routes=192.168.1.0/24
    • 仅共享单个子网设备: tailscale set --advertise-routes=192.168.1.213/32
  • tailscale 客户端B, 位于网段 192.168.31.0/24
    • 启用接受子网路由: tailscale set --accept-routes
    • 测试连通性: tailscale ping 192.168.1.213
    • 随后直接访问 192.168.1.213 即可
# 查看路由表
 
C:\Users\25868>route PRINT -4
 
IPv4 路由表
===========================================================================
活动路由:
网络目标        网络掩码          网关       接口   跃点数
  ...
    192.168.1.213  255.255.255.255  100.100.100.100   100.78.210.111      5
 

Exit Node

Info

默认情况下, tailscale 只路由 tailnet 之间的流量, 不会影响设备的公共互联网流量. 部分场景下, 你可能希望路由所有互联网流量:

  1. 你在咖啡店/机场/图书馆, WIFI 不受信任
  2. 你需要将 公司网络 / 家庭网络作为流量出口

https://tailscale.com/docs/features/exit-nodes

此时, 可以将 tailnet 中的指定设备设置为 Exit Node (出口节点), 然后配置其他设备访问公共互联网的流量由该节点发出

工作流程

Exit Node 的启用与 Subnet Routers 相似, 但在路由注入上具有最高优先级

  1. 宣告: Exit 节点通过 --advertise-exit-node 向协调服务器宣告自己作为公网出口角色
  2. 批准: 管理员必须在后台批准, 它才生效
  3. 路由注入: 协调服务器将默认路由 (IPv4 的 0.0.0.0/0 与 IPv6 的 ::/0) 注入到 tailnet 中所有开启使用 exit node (--exit-node=<ip|name>) 选项的客户端
  4. 全流量劫持与转发: 客户端操作系统根据新的路由表, 将所有非本地局域网 (LAN) 的流量通过加密隧道发给 Exit 节点, Exit 节点收到后, 进行源地址转换(SNAT), 用自己的公网 IP 发出请求, 收到响应后原路加密返回

关键机制

Exit Node 能够不依赖复杂代理协议 (如 HTTP CONNECT) 而直接工作, 依赖如下两个关键机制:

  1. 默认路由覆盖与策略路由
    • tailscale 利用操作系统 策略路由/优先级最高的路由条目, 精准将公网流量指向 tailscale0 虚拟网卡
    • 确保只有访问互联网的流量被代理, 访问本地局域网/Tailnet 的流量直连
  2. NAT 与 反向路径过滤
    • 公网服务器看到的请求源 IP 是 Exit 节点的公网 IP, 服务器回包只能到 Exit 节点
    • 因此, Exit 节点必须对发出的数据包执行 Masquerade (伪装/SNAT), 并维护连接跟踪表 (Conntrack), 才能将收到的回包正确还原并转发给对应客户端

注意

  • 本地局域网访问阻断: 配置 exit node 后, Tailscale 会劫持所有流量, 包括访问本地局域网(如 192.168.1.1)的流量. 若需访问本地设备, 必须显式开启 --exit-node-allow-lan-access

使用示例

注意: 需要在管理后台批准该 exit node 后才生效

  • tailscale 客户端A, 位于家庭宽带网络 (具备稳定的公网出口能力)
    • 宣告为出口节点: tailscale set --advertise-exit-node
  • tailscale 客户端B, 位于网段 192.168.31.0/24 (例如咖啡厅或公司网络)
    • 指定客户端A为默认公网出口: tailscale set --exit-node=<客户端A ip|name> --exit-node-allow-lan-access
    • 访问公网 (例如 curl ifconfig.me), 检查出口 IP 地址

命令含义见 2.常用命令