2222 字
11 分钟
Tailscale 是怎么做到"像在同一个局域网"的

用了大半年 Tailscale,最大的感受不是”网速快”,而是它把一整类问题从根上消掉了。

传统组网你得处理:公网 IP、端口转发、DDNS、路由器防火墙规则、两边能不能互通、运营商 CGNAT……每一个都是单独的坑。Tailscale 的做法是:这些全都不需要你想。

它到底怎么做到的?


一、先说结论:这是”控制平面 / 数据平面”分离的经典范式#

理解 Tailscale 只要抓住一句话:

协调服务器只负责”介绍大家认识”,数据从来不经过它。

┌─────────────────────────────────────────────────────────┐
│ 控制平面(Control Plane)—— 只交换元数据,永不见明文 │
│ │
│ coordination server │
│ ├─ 认证:这个设备是谁? │
│ ├─ 分发:把 peer 的公钥 和 当前可达 endpoint 告诉 everyone│
│ └─ 策略:ACL / 子网路由 / DNS 记录 │
└─────────────────────────────────────────────────────────┘
│
交换公钥 + endpoint 地址(就这些)
│
┌───────────────────────────┴─────────────────────────────┐
│ 数据平面(Data Plane)—— 设备之间直连,端到端加密 │
│ │
│ 笔记本 100.x.y.z ◄──── WireGuard (UDP) ────► 家里 PC │
│ │
│ 打洞失败时降级: │
│ 笔记本 ◄── WireGuard over DERP(TLS 443) ──► 家里 PC │
└─────────────────────────────────────────────────────────┘

这个切分带来的直接后果:协调服务器被入侵,攻击者拿到的是一堆公钥和 IP 地址,不是你的文件内容。 数据平面的加密密钥在设备本地,服务器没有。


二、阶段 A:把设备加进同一个 tailnet#

sequenceDiagram autonumber participant D as 新设备(笔记本) participant C as 协调服务器
controlplane.tailscale.com participant A as 已在线的设备
(家里 PC) D->>D: 生成设备密钥 (curve25519) Note over D: 私钥永不离开本机,只上传公钥 D->>C: 发起登录 (SSO / auth key) C->>C: 验证身份,绑定到你的 tailnet C-->>D: 下发节点密钥 + 网络策略(ACL) + DNS 配置 D->>C: 上报本机 endpoint
(公网 IP:随机端口列表) C-->>A: 推送 peer 信息:新设备的公钥 + endpoint C-->>D: 推送 peer 列表:家里 PC 的公钥 + endpoint Note over D,A: 两端现在互相知道对方"是谁"和"在哪" Note over D,A: 但还没有任何一包数据通过

这里有个容易被忽略的点:协调服务器给的是公钥,不是私钥。 所以它从架构上就不可能解密你的流量。

身份体系是另一层东西。Tailscale 的设备身份有三类密钥:

密钥作用
设备密钥(machine key)标识”这是哪台设备”,设备身份的生命周期
节点密钥(node key)WireGuard 会话密钥,每次连接协商
预授权密钥(auth key / preauth key)给无头设备(服务器、树莓派、容器)用,免交互加入

第三类在自动化场景里非常有用。我 NAS 上跑的 Tailscale 就是用预授权密钥直接加入的,没有弹过任何登录页面:

Terminal window
tailscale up --authkey=tskey-auth-xxxxx --advertise-routes=192.168.1.0/24
NOTE

预授权密钥的泄露风险比密码高 —— 它是可重复使用的,能直接往你的 tailnet 里加设备。所以一般签发一次性(ephemeral)版本,且定期轮转。


三、阶段 B:NAT 打洞,然后直连#

拿到对方 endpoint 之后,两台设备同时开始发包,目标是让中间的 NAT 设备在状态表里打开一个洞。

sequenceDiagram autonumber participant L as 笔记本 (NAT 后) participant D as DERP 中继
(协调服务器附属) participant P as 家里 PC (NAT 后) Note over L,P: 第一步:先经 DERP 完成真正的握手 L->>D: 中继包裹(内含 WireGuard 握手发起) D->>P: 转发给 P P->>D: 中继包裹(WireGuard 握手响应) D->>L: 转发给 L Note over L,P: ★ 隧道已建立,且双方都知道了
对方的 NAT 映射地址:端口 par 同时打洞 L->>P: UDP 直发(经对方 NAT 映射地址) and P->>L: UDP 直发(经对方 NAT 映射地址) end Note over L,P: 双向流量都通过了 → NAT 表已打开 L->>P: ⚡ WireGuard 直连,绕开 DERP P->>L: ⚡ WireGuard 直连,绕开 DERP Note over D: DERP 此后只在心跳阶段参与,不再承载数据

这是整个设计里最漂亮的一步。DERP 在这里的作用不是”中转流量”,而是”牵个线”:

NAT 穿越的难点在于:你不知道对方在 NAT 后的映射地址,而且通常只有一方能主动建连(对称型 NAT 双方都难)。DERP 让双方先通过中继完成握手,握手过程中双方互相通告了自己的源地址:源端口,然后同时向这个地址发包。

NAT 设备看到”有出站流量到 X”,就会为返回流量在状态表里开一个洞。两边都打,就都开了。

打洞失败怎么办#

不是所有网络都允许 UDP —— 有些企业防火墙、有些酒店 Wi-Fi、有些移动网络会直接封 UDP。这时候自动降级到 DERP 中继,数据走 WireGuard over TLS 443:

Terminal window
tailscale status
# 100.x.y.z laptop linux - 直接连接
# 100.x.y.z nas linux relay "iad" 走 DERP 中继

功能完全正常,只是延迟高一点。tailscale netcheck 可以看当前网络的 NAT 类型和 UDP 可用性。

TIP

这也是为什么 Tailscale 在受限网络里”总能用”—— 它有一个永远能通的降级路径。中继走 443/TLS,几乎不可能被企业防火墙完全拦死。


四、拿到”局域网体验”的三层能力#

直连只是基础。让人觉得”跟在一个局域网里”的其实是这三样:

1. MagicDNS —— 不用记 IP#

Terminal window
tailscale status
# 100.101.102.103 my-nas linux -
# 100.104.105.106 my-laptop windows -
Terminal window
ssh my-nas
ping my-laptop

背后是 Tailscale 内置的 DNS resolver,把 my-nas 解析到 100.x.y.z,再由 netfilter 把这个网段的流量送进 WireGuard 隧道。

WARNING

100.64.0.0/10 是 CGNAT 保留段(RFC 6598)。Tailscale 选它是为了几乎不可能和你的真实局域网冲突 —— 家宽和运营商内网正好也用这个段,所以反向的情况(你的设备真实地址就在 100.x 里)才是要小心的那个。Tailscale 也确实因此和某些 CGNAT 场景下的本地路由撞车,官方文档里明确列了这个已知冲突。

2. 子网路由 —— 访问”根本不在 tailnet 里”的机器#

如果家里有一台没装 Tailscale 的打印机或摄像头,可以让装了 Tailscale 的那台 PC 当路由器:

Terminal window
# 在家里 PC 上
tailscale up --advertise-routes=192.168.1.0/24
# 在管理后台批准这条路由(必须手动批准)

批准后,其他 tailnet 设备就能直接访问 192.168.1.0/24,只有一台设备需要装 Tailscale。IP 转发和 SNAT 由 tailscaled 自动开好。

3. Exit Node —— 把整个出口搬到别处#

Terminal window
tailscale up --exit-node=my-nas

所有设备的默认流量都从 NAS 出去。典型用途是:人在国外想让国内设备走回国出口,或者反过来。

CAUTION

Exit Node 和子网路由别同时无脑开。Exit Node 会接管默认路由,容易和本地 VPN(Clash 之类)打架 —— 这也是 Tailscale 和 Clash 共存时最常见的冲突点。配合使用时,一般要么让 Clash 跑在 Tailscale 之上(子网路由/Exit Node 出口),要么反过来明确指定 --exit-node 只处理特定流量。


五、和 OpenTunnel 怎么选#

上一篇写了用 OpenTunnel 把 opencode 暴露到公网。这里对比一下,因为两者经常被放在一起问:

TailscaleOpenTunnel
形态私有网络,设备间互联单点公网入口,一个 URL
暴露面只有 tailnet 内的设备任何人拿到 URL 都能访问(要自己加鉴权)
加密位置WireGuard,内核态,端到端TLS 在本地终结,盲转发
移动端装 App浏览器直接开
适合多台设备互联、访问内网服务临时把本地 web 服务甩到公网

实际用法是组合拳:

Tailscale ──▶ 手机安全地回到家里内网
OpenTunnel ──▶ 把某个服务单独甩到公网(带密码)
TIP

如果你只是想让手机访问家里几个服务,只用 Tailscale 就够了,别开 OpenTunnel —— 前者暴露面是”你的设备列表”,后者是”一个公网 URL”。安全边界完全不同。


小结#

Tailscale 之所以能用得这么舒服,是因为它把三个本该由用户操心的问题各自交给了正确的层:

  • 身份问题 → 协调服务器(但只管公钥,不管数据)
  • 可达性问题 → DERP 中继做握手辅助 + NAT 打洞,失败自动降级中继
  • 加密问题 → WireGuard 端到端,密钥在设备本地,服务端物理上无法解密

学 networking 的时候,“VPN = 加密隧道”这个印象会束缚你很久。Tailscale 告诉你的是:加密隧道只是数据平面,前面还需要一个不碰数据的控制平面;而可达性问题,可以用”先借道、再打洞、最后直连”的时序来解决。

Tailscale 是怎么做到"像在同一个局域网"的
https://ycxdev.com/posts/tailscale-p2p-how-it-works/
作者
YCX
发布于
2026-10-11
许可协议
CC BY-NC-SA 4.0