Tailscale是什么及其工作原理?
了解 Tailscale 如何通过 WireGuard 建立节点间加密隧道,协调服务器如何分发密钥和 ACL,以及 NAT 穿透失败时 DERP 中继如何转发流量。
什么是 Tailscale?
Tailscale 是一种 VPN。它让各台机器直接相互连接,而不是将所有流量都经由您运行的一个网关转发。每个节点都运行 WireGuard,因此数据包会从一台服务器加密传输到另一台服务器,路径中的任何设备都无法读取这些数据包。托管的协调服务器负责建立连接。它存储并分发公钥,并告知每个节点其他节点的位置。它还会下发您编写的访问规则。
这种分离就是整个设计。数据平面采用节点间的点对点加密连接。控制平面则是由 Tailscale 代您运行的服务。关于 Tailscale 的所有关键问题,包括有关信任的敏感问题,都源于这两个事实。如果您已经 手动在 VPS 上构建了 WireGuard VPN,那么 Tailscale 就是同样的隧道,只是由它代您完成密钥分发和防火墙穿透。
Tailscale 如何工作?
由节点组成的私有网络称为 tailnet。设备加入 tailnet 时会发生以下 4 件事。
tailscaled守护进程启动,生成 WireGuard 密钥对,并将状态保存在/var/lib/tailscale/tailscaled.state中。私钥始终保留在该设备上。Tailscale 的说法很明确:“私钥永远不会离开其节点。”- 节点登录协调服务器,并上传公钥,以及它认为自己可被访问的地址。Tailscale 将该服务器描述为“用于存放公钥的共享投递箱”。
- 协调服务器返回一份网络映射,其中包含公钥、tailnet 地址、设备名称,以及当前节点获准访问的每个节点的候选端点。
- 随后,每对节点都会尝试在彼此之间建立直接的 WireGuard 隧道。如果失败,则改为通过中继转发数据包。
每个节点都会从 100.64.0.0/10 中获得一个稳定地址。该地址范围属于运营商级 NAT,范围是 100.64.0.0 到 100.127.255.255。Tailscale 使用这一范围,是因为它专用于运营商基础设施,因此很少与服务器已经使用的私有地址冲突。在 Linux 上,该隧道会显示为名为 tailscale0 的接口。
WireGuard 实现位于用户空间中的 tailscaled 内,而不是内核模块中。因此,在 sudo modprobe wireguard 无法通过 Operation not supported 启动的容器虚拟化环境中,Tailscale 仍可启动。这也意味着,在给定设备上,其吞吐量上限低于内核 WireGuard。这是 Tailscale 与原生 WireGuard 的比较中讨论的取舍之一。
使用以下两个命令可以查看当前状态。
tailscale ip -4
tailscale statustailscale status 为每个节点输出一行,最后一列最重要。
100.101.102.103 web-1 you@ linux -
100.101.102.104 db-1 you@ linux active; direct 198.51.100.24:41641
100.101.102.105 ci-runner you@ linux active; relay "fra"direct 后跟地址和端口,表示两台设备已找到彼此之间的路径,网络流量采用点对点方式传输。relay "fra" 表示流量正在通过位于 Frankfurt 的 Tailscale 中继传输。- 表示当前没有与该节点建立活动会话,这是正常现象。
协调服务器能看到和不能看到的内容
协调服务器保存公钥和元数据。它知道您的机器名称、每个节点归哪个用户或标签所有、每个节点的 tailnet 地址、节点可通过哪些公网地址访问、每个节点最近一次在线的时间,以及您编写的策略文件。这构成了整个节点集群的完整映射。
协调服务器不持有任何私钥,因此无法解密两个节点之间的流量。WireGuard 对等节点之间进行端到端加密,而协调服务器不是对等节点。
协调服务器可以分发密钥。无论是托管的协调服务器还是自行运行的协调服务器,您都必须信任它向节点告知哪些公钥属于 tailnet。这是后文威胁模型的关键,也是 Headscale,即由您自行托管的开源协调服务器 存在的原因。
不同防火墙后的两台服务器如何直接通信
NAT(网络地址转换)允许多台计算机共享一个公网地址。您的 VPS 通常拥有独立的公网地址,但您希望加入 tailnet 的其他计算机往往没有公网地址,例如家庭服务器、办公网络中的构建运行器,或位于您无法修改的服务商防火墙之后的主机。
Tailscale 使用基于 STUN(NAT 会话穿越工具)和 ICE 标准的技术来查找路径。每个节点都会向 STUN 服务器发送一个小型 UDP 数据包,并获取路由器为该套接字分配的公网地址和端口。两个节点都会将这些候选地址报告给协调服务器,再由协调服务器传递给另一端。随后,两个节点会同时开始向对方发送数据包。每个路由器都会先看到出站数据包,因此会创建映射,并接受从同一地址返回的响应。双方都不需要配置入站防火墙规则。
这些端口有明确用途。直接 WireGuard 隧道使用 UDP,源端口默认为 41641。STUN 通过 UDP 3478 连接到 Tailscale 的中继服务器。控制连接和所有中继数据都通过 TCP 443 使用 HTTPS。大多数情况下无需开放任何入站端口,但如果网络使用较难穿透的 NAT,允许 UDP 41641 入站可以提高建立直接连接的可能性。
tailscale netcheck查看该报告中的两行。UDP: true 表示 UDP 数据包可以离开本机,UDP: false 表示来自此节点的所有连接都会通过中继。MappingVariesByDestIP: true 表示路由器会为每个目标分配不同的公网端口,因此上述地址预测无法工作,这些节点通常会一直使用中继连接。
Tailscale 改用 DERP 中继时
DERP(数据包指定加密中继)是备用路径。Tailscale 在多个区域运行中继节点,这些节点可通过 TCP 443 访问。无法建立直连路径的节点会将 WireGuard 数据包通过其中一个中继节点发送。
数据包始终处于加密状态。Tailscale 对此有明确说明:“DERP 服务器永远无法解密您的流量。它只会盲目地将已加密的流量从一个节点转发到另一个节点。”中继节点看到的是密文,也能看到哪个节点正在与哪个节点通信。
中继节点还会承载大多数连接的初始数据包。建立直连路径需要一些时间,因此会话通常先通过中继建立;两个节点找到彼此后,连接会在不中断会话的情况下升级。您可以观察这一过程。
tailscale ping db-1最初的响应通过 via DERP(fra) 返回,随后某一行会报告类似 via 198.51.100.24:41641 的信息。此变化表示连接已升级为直连隧道。如果状态始终不变,请在两端运行 tailscale netcheck。中继路径仍然可以正常工作,但会增加延迟,因为每个数据包都必须绕行一台第三方机器。
将 VPS 加入 tailnet
安装脚本支持 Ubuntu 和 Debian。
curl -fsSL https://tailscale.com/install.sh | sh
sudo tailscale upsudo tailscale up 会输出一个 URL。打开该 URL,完成身份验证后,节点就会出现在管理控制台中。随后确认 daemon 在重启后仍能恢复运行,因为这一步经常被跳过。
sudo systemctl is-enabled tailscaled
tailscale statusis-enabled 应输出 enabled,而 tailscale status 应列出新节点及其 100.x 地址。对于通过脚本创建的服务器,交互式 URL 没有用。请在管理控制台中生成 auth key,并将其传入命令,同时指定一个用于记录机器类型的标签。
sudo tailscale up --auth-key=tskey-auth-REPLACE-ME --advertise-tags=tag:server带标签的节点归标签而不是运行命令的用户所有,因此即使该用户的账户被删除,节点仍可继续运行。必须先在 tagOwners 下的策略文件中声明该标签,否则命令会被拒绝。标签还会影响机器计入套餐的方式,因为带标签的资源单独计费,不计入用户自己的设备;免费套餐实际涵盖的内容说明了这些限制。
对于节点集群,有两项设置需要注意。默认情况下,节点密钥在 180 days 后过期(截至 August 2026)。密钥过期后,“与指定端点之间的连接将停止工作”,直到有人再次登录。因此,对于无人值守的服务器,请在管理控制台中打开该机器所在的行,并选择 Disable Key Expiry。对于在 20 October 2022 或之后创建的 tailnet,MagicDNS 默认启用。它会为每个节点分配一个名称,例如 db-1.yak-bebop.ts.net,并由 100.100.100.100 处的 stub resolver 解析。请使用这些名称而不是地址,因为重建节点后会获得新地址,但名称会保留。
如果安装过程在 apt 或软件仓库处失败,请参阅Ubuntu 上常见的 Tailscale 安装错误,其中介绍了相应的修复方法。
访问绑定到 localhost 的服务
这正是 tailnet 发挥作用的地方,也是很多人卡住的地方。加入 tailnet 不会让 loopback 服务变得可访问。
ss -tlnp | grep 3000如果输出为 127.0.0.1:3000,说明该 socket 只接受目标地址为 127.0.0.1 的数据包。来自其他节点的请求会以本节点的 100.x 地址为目标到达,因此内核找不到监听该地址的服务,并返回 TCP reset。客户端会报告 Connection refused。隧道没有问题,问题在于监听地址。
有两种正确的解决方法。将服务绑定到本节点的 tailnet 地址。这样服务不会暴露在公网接口上,也不需要经过代理:传入 --bind 100.101.102.104,或在配置中使用等效选项;如果服务运行在容器中,则将端口发布为 -p 100.101.102.104:3000:3000。另一种方法是让服务继续监听 loopback,并在其前面使用 Tailscale。
tailscale serve 3000该命令会将请求代理到 http://127.0.0.1:3000,并在启用 tailnet 的 HTTPS 证书后,通过一个 ts.net 名称在 tailnet 内提供 HTTPS 访问。服务仍只对本节点开放。相同方案的公网版本是 Funnel,Tailscale serve 与 Funnel 的对比介绍了应选择哪一种。
另外两个相关任务有各自的页面。要访问未安装 Tailscale 的整个私有网络,需要配置VPS 上的子网路由器;要让某个节点的出站互联网流量经由另一个节点传输,需要配置出口节点。
关闭不再需要的端口
当所有管理员都能通过 tailnet 访问服务器后,公网端口 22 就不再承担任何作用。这是最实际的收益:关闭的端口无法被暴力破解,日志中也不会再充满连接尝试。
操作顺序很重要。先添加 tailnet 访问规则,从第二个会话确认可以通过 tailnet 登录,然后再删除公网规则。
sudo ufw allow in on tailscale0
sudo ufw status verbose之后,删除公网 SSH 规则,并使用 MagicDNS 名称重新连接。请注意 ufw allow in on tailscale0 的实际作用:它会信任通过隧道进入的所有流量,因此访问控制由 Tailscale policy 文件取代 ufw。编写该策略时必须考虑这一点。
使用容器的用户还需注意。已发布的 Docker 端口会安装自己的 NAT 规则,并绕过 ufw,因此 ufw deny 无法关闭该端口。Docker 已发布端口绕过 ufw 解释了其中的机制。像上文一样,将端口发布到 tailnet 地址即可避开这一问题。
Tailscale 保护什么,以及不保护什么
需要明确说明这一点,因为营销文案容易模糊两者的界限。
受保护的部分:两个节点之间的流量通过 WireGuard 进行端到端加密,中间的中继无法读取这些流量。私钥不会离开生成它的机器。节点不需要开放公网入站端口,因此互联网无法扫描 22 或 5432 端口。节点之间的访问由策略文件决定,而不是由知道某个地址的人决定。
不受保护的部分:协调服务器可以看到您的设备关系图。这些元数据本身就很敏感,因为机器名称、所有者、地址和在线时间会暴露您的基础设施信息。协调服务器还会分发密钥,这带来的风险更大。Tailscale 直言不讳地说明:“如果 Tailscale 具有恶意,并且在不被察觉的情况下向您的网络中插入新节点,那么 Tailscale 就可以向现有节点发送明文流量,或接收现有节点发送的明文流量。”您的单点登录提供商也处于同一信任链路中,因为任何能够在那里签发身份凭据的人都可以添加节点。被入侵的节点则是 tailnet 内的对等节点,因此它还能访问什么,取决于您的策略允许什么。这是否构成可接受的风险,取决于您要防御的对象;完整信任模型会逐一说明这些情况,包括被盗的身份账户实际上能够执行哪些操作。
密钥分发风险有两种应对方式。第一种是 tailnet lock。它要求现有的受信任节点先对新节点进行加密签名,其他节点才会接受该节点。控制平面如果在没有有效签名的情况下添加节点,其他节点会忽略它。管理控制台会为签名节点生成准确的 tailscale lock init 行,每个节点都可以确认自己看到的内容。
tailscale lock status所有节点都应报告相同的一组受信任签名密钥。第二种方式是自行运行控制平面。自行托管 Headscale 协调服务器使用相同协议与相同客户端通信,从而将设备关系图和密钥分发转移到您拥有的硬件上。此后,您也需要负责该服务器的可用性。如果您仍在比较自行托管的控制平面,而不是决定使用这一方案,NetBird 是另一种网状 VPN,其服务器可由您在单个 VPS 上完全自行运行。
第一天需要修改一个默认设置。新 tailnet 默认采用宽松策略:“默认 tailnet 策略文件允许 tailnet 内的所有设备相互通信。”只要添加 acls 部分,模型就会切换为默认拒绝,只有符合您规则的流量才能通过。
{
"tagOwners": {
"tag:server": ["autogroup:admin"]
},
"acls": [
{"action": "accept", "src": ["autogroup:member"], "dst": ["tag:server:22"]}
]
}该策略允许 tailnet 成员访问带有标签的服务器上的 SSH,除此之外不允许访问任何内容。应为每项服务添加单独的规则,不要保留通配符,因为通配符意味着一台被盗的笔记本电脑密钥就能访问您的数据库。
故障模式及您将看到的字符串
tailscale status 始终显示 relay。 两个节点从未建立直接路径。在两端运行 tailscale netcheck。UDP: false 表示出站 UDP 被阻止,因此只能使用中继。MappingVariesByDestIP: true 表示存在硬 NAT;在您可以控制的一侧允许入站 UDP 41641 通常可以解决问题。
某个已运行数月的节点消失了。 其节点密钥已在默认的 180 天期限后过期。管理控制台将该计算机显示为已过期,在该计算机上运行 sudo tailscale up 即可恢复。请在服务器上禁用密钥过期,避免问题再次发生。
节点已列出,但连接超时。 连通性正常,策略拒绝了该流量。检查 acls 部分,确认其中存在覆盖此源、目标和端口的规则。被拒绝的数据包会直接丢弃,而不会返回响应,因此您会得到超时,而不是 Connection refused。
MagicDNS 名称无法解析。 ping db-1 失败,而 ping 100.101.102.104 正常工作。某个程序替换了 /etc/resolv.conf,因此查询无法到达 100.100.100.100 上的 stub resolver。检查 cat /etc/resolv.conf 中的 100.100.100.100,并查看该计算机上其他会写入该文件的程序。这与 WireGuard 隧道内的 DNS 故障 属于同一类问题。
tailscale up 拒绝您的标签。 该标签未在策略文件的 tagOwners 下声明。添加后,再次运行该命令。
FAQ
Tailscale 是 VPN 还是网状网络?
这两个说法都准确,但描述的是不同层面。隧道使用 WireGuard,因此它属于 VPN。拓扑结构是网状网络,因为每个节点都会直接与其通信的节点建立隧道,而不是让所有数据包都经过一个中央服务器。协调服务器位于控制路径中,而不在数据路径中,因此即使协调服务器无法访问,现有隧道仍会继续传输流量。中断期间停止的是新节点加入,以及密钥或策略变更的下发。
Tailscale 能读取我的流量吗?
不能读取流量内容。节点之间使用 WireGuard 进行端到端加密,私钥不会离开节点,DERP 中继只负责转发无法解密的数据包。Tailscale 可以看到元数据:机器名称、所有者、公钥、端点地址,以及每个节点何时在线。Tailscale 还负责分发密钥,因此被攻破的协调服务器可能尝试插入一个节点,而您的整个 tailnet 随后会信任该节点。Tailnet lock 要求由您自己的受信任节点提供签名,可以阻止这种情况;Headscale 则将托管控制平面移出这一架构。
使用 Tailscale 需要开放防火墙端口吗?
几乎不需要开放入站端口。Tailscale 官方说明是:“大多数情况下,您不需要开放任何防火墙端口。”出站方向,节点需要通过 TCP 443 访问协调服务器和中继,还需要通过 UDP 3478 使用 STUN。直接隧道使用 UDP,源端口默认为 41641。允许 UDP 41641 入站是可选的,只能帮助直接连接在网络条件复杂时成功建立。
为什么其他节点无法通过端口 3000 访问我的服务?
首先使用 ss -tlnp 检查绑定地址。监听在 127.0.0.1:3000 上的服务会拒绝发往节点 100.x tailnet 地址的连接,因为该套接字只接受目标地址为 loopback 的连接,客户端会看到 Connection refused。将服务绑定到 tailnet 地址,或运行 tailscale serve 3000 对其进行代理。如果监听器已经绑定到 0.0.0.0,但连接超时而不是被拒绝,原因应是策略规则或主机防火墙,而不是绑定地址。
我应该运行 Headscale,而不是 Tailscale 的协调服务器吗?
当设备拓扑或密钥分发必须保留在您控制的基础设施上,或者 tailnet 必须不依赖外部服务运行时,可以运行 Headscale。客户端和协议相同。代价是您需要自行运维协调服务器;协调服务器中断后,新节点无法加入,策略变更也无法应用。对于小型设备群,启用 tailnet lock 的托管控制平面通常是更合理的取舍。