Tailscale:WireGuard 网格与 NAT 穿透
同样是 WireGuard,为什么它不用你配任何 IP、开任何端口就能连上。
学完这节你能做到
- 解释控制面与数据面分离的收益,说出协调服务器看不到什么
- 说清 NAT 穿透失败时 DERP 中继接管的条件
- 按需求选择 subnet router、exit node 还是逐机安装
建议先学跳过这几节会看不懂本节的部分推导
手工 WireGuard 的成本在哪
上一节的配置只有两个节点。加到 10 个节点、要两两直连呢?
每个节点要认识另外 9 个 peer
10 × 9 = 90 个隧道端点配置
新增 1 个节点 → 要改 10 台机器的配置
传统办法是退回中心辐射(hub-and-spoke):所有人连一台集中器。代价是绕路—— 纽约的客户端访问纽约的服务器,却要先跑一趟旧金山的集中器,延迟付两遍。
Tailscale 的解法是:把"交换配置"这件事自动化,把"传数据"这件事保持点对点。
控制面与数据面分开
控制面(中心辐射,只传密钥和策略,几乎没有流量)
┌──────────────────┐
│ 协调服务器 │
└──┬────┬────┬─────┘
│ │ │
┌──▼─┐┌─▼──┐┌▼───┐
│节点││节点││节点│
└──┬─┘└─┬──┘└┬───┘
└────┼────┘
数据面(全互联网格,WireGuard 直连)
官方对这个设计的说法很直白:控制面是中心辐射的,但这不重要,因为它几乎不承载流量; 数据面才是网格。好处是处理能力随节点数一起增长,不存在集中器那样的吞吐天花板。
数据面用的是 WireGuard 的用户态实现(wireguard-go),所以不依赖内核模块,
容器和各种奇怪平台上都能跑。
密钥交换:一个公钥投递箱
协调服务器的角色,官方原话是"a shared drop box for public keys":
- 每个节点自己生成密钥对
- 上传公钥 + 当前能被访问到的地址 + 所属域
- 下载同域内其它节点的公钥和地址
- 用这些信息配置本地的 WireGuard
私钥永远不离开节点。 所以协调服务器看得到公钥、节点地址、域归属和 ACL 策略, 看不到私钥,因此也无法解密任何流量。
身份认证外包给 IdP(OAuth2 / OIDC / SAML,比如 Google Workspace、Entra ID)—— 它已经掌握了你的用户列表、密码和 2FA,不需要再维护第二套账号体系。
NAT 穿透:为什么不用 UPnP
节点在咖啡馆、在 4G、在层层 NAT 后面,两边都没有开放端口。
历史上的做法是 UPnP,而官方评价是它"很少能用,而且真能用的时候通常危险地好用, 直到管理员把它关掉"。Tailscale 走的是 STUN / ICE 那套标准技术, 在很多你以为不可能的场景下也能打通直连。
收益是不需要在防火墙上开任何入向端口——这本身就消掉了一大类人为配置错误。
DERP:打不通时的中继
有些网络直接封禁 UDP,STUN/ICE 无能为力。这时候 Tailscale 会退到 DERP (Designated Encrypted Relay for Packets):
| 特点 | 说明 |
|---|---|
| 角色 | 相当于 ICE 里的 TURN,但用 HTTPS 流 + WireGuard 密钥 |
| 触发条件 | 兜底,直连打不通时才用 |
| 路由方式 | 非对称:各自走离接收方最近的中继 |
| 能否解密 | 不能 —— 私钥不离开节点,它只是转发已加密的字节 |
| 附带好处 | 等于给 WireGuard 补上了它原生没有的 TCP 回退 |
这正好回答了上一节结尾的问题:纯 WireGuard 在 UDP 被封的网络里没救,Tailscale 有 DERP 兜底。
# 看每个 peer 是直连还是走中继
tailscale status
100.64.0.1 laptop user@ linux -
100.64.0.2 nas user@ linux idle, tx 1832 rx 2140
↑ 后面标 "direct 203.0.113.5:41641" 是直连
↑ 标 "relay \"tok\"" 就是在走 DERP
# NAT 类型与各中继延迟的体检
tailscale netcheck
# 测某个节点走的哪条路
tailscale ping nas
中继会明显增加延迟。tailscale status 里如果某个 peer 一直标着 relay,
就该查为什么打不通直连:出口是不是封了 UDP、是不是对称 NAT、
或者干脆给其中一端开个公网端口(--advertise-exit-node 的机器尤其值得这么做)。
tailscale netcheck 会直接报告 UDP 是否可用、NAT 是否"hairpinning"、各 DERP 的 RTT,
比猜快得多。
ACL:中心定策略,每个节点自己执行
网格架构没有集中的卡点,那策略在哪执行?答案是"在每一个节点上": 每个节点在解密时就把不允许的入向连接丢掉。
还有一层更彻底的保护:协调服务器只会把"允许访问你"的那些节点的公钥发给你。 未获授权的机器连发起请求的资格都没有——官方的说法是"就像那些机器不存在一样"。
策略用 HuJSON 写,集中在协调服务器上维护:
{
"tagOwners": {
"tag:prod": ["group:sre"],
"tag:ci": ["group:sre"],
},
"acls": [
// SRE 组可以访问生产机器的 SSH 与 K8s API
{ "action": "accept", "src": ["group:sre"], "dst": ["tag:prod:22,6443"] },
// CI 只能访问镜像仓库
{ "action": "accept", "src": ["tag:ci"], "dst": ["tag:registry:443"] },
// 其余默认拒绝
],
}
对比一下传统做法:规则写在集中器的防火墙上、以 IP 为单位、散落在多台设备上各配一遍。 这里是以身份为单位、写一次、自动分发。
接入遗留网络:两个开关
不可能给所有设备都装客户端(打印机、老服务器、网络设备)。两个机制解决:
# 子网路由器:把一整个内网段接进 tailnet
tailscale up --advertise-routes=192.168.50.0/24,10.20.0.0/16
# 其它节点要显式接受
tailscale up --accept-routes
# 出口节点:把所有出网流量从这台机器发出去
tailscale up --advertise-exit-node
tailscale set --exit-node=<node>
两者都要在管理后台批准(避免任意节点自己宣告路由)。
官方文档明确写了这一点:子网路由器要解密流量再转发进物理内网, 所以从子网路由器到内网设备的那一段是明文的。
这不是缺陷而是必然——内网里那台老设备不会说 WireGuard。但要意识到: 子网路由器是一个信任集中点,它的安全等级应该按网关来对待,而不是按普通节点。
什么时候用它,什么时候不用
| 需求 | 建议 |
|---|---|
| 几台机器互联,成员基本不变 | 自建 WireGuard 就够,别引入依赖 |
| 成员经常变、设备到处跑、都在 NAT 后 | Tailscale,省下的是持续的运维 |
| 要用现有 IdP 做认证与离职回收 | Tailscale / Pritunl |
| 不接受控制面在第三方 | Headscale(开源的协调服务器实现)自建控制面 |
| 需要 OpenVPN 客户端兼容性、组织化管理与审计 | Pritunl(下一节) |
| 极致吞吐、机器固定在机房 | 自建内核态 WireGuard,用户态实现有额外开销 |
判断的核心不是性能,是你在为什么付代价:Tailscale 让你用一点控制权换掉大量密钥分发和 NAT 穿透的运维;自建则相反。
检查点
Tailscale 的协调服务器能看到什么?
tailscale status 显示某个节点一直标着 relay。这意味着什么?
关于 Tailscale 的 subnet router,下面哪个说法正确?
这节课的落点
- 控制面中心辐射(只传密钥与策略),数据面全互联网格(WireGuard 直连)
- 协调服务器是"公钥投递箱":私钥永不离开节点,所以它解不了流量
- 认证外包给 IdP,不再维护第二套账号,离职回收走原有流程
- NAT 穿透用 STUN/ICE 而不是 UPnP,不需要开放任何入向端口
- DERP 是兜底中继:用 HTTPS + WireGuard 密钥,读不到内容,顺带补上 TCP 回退
- ACL 中心定义、每个节点执行;未授权节点连公钥都拿不到
subnet router与exit node接入遗留网络,但 subnet router 是端到端加密的例外- 排障三条命令:
tailscale status(直连还是中继)、netcheck(NAT 与 DERP 体检)、ping - 不想把控制面交给第三方就用 Headscale 自建