SSD Nodes Learn Hosting plans →
指南 Matt Connor作者: Matt Connor · 更新于 2026-10-15

Tailscale是什么?工作原理、WireGuard与DERP中继

了解 Tailscale 如何用 WireGuard 建立点对点隧道,协调服务器如何分发密钥和 ACL,以及 NAT 穿透失败时为何改用 DERP 中继。

什么是 Tailscale?

Tailscale 是一种 VPN,可让各台机器直接相互连接,而不是将所有流量都通过您运行的一个网关转发。每个节点都运行 WireGuard,因此数据包会在服务器之间加密传输,路径中的任何设备都无法读取这些数据。托管的协调服务器负责建立连接。它存储并分发公钥,告知每个节点其他节点的位置,并下发您编写的访问规则。

这一区分构成了整个设计。数据平面采用点对点连接,节点之间的通信经过加密。控制平面是由 Tailscale 代您运行的服务。关于 Tailscale 的所有关键问题,包括有关信任的敏感问题,都源于这两点。如果您已经在 VPS 上手动构建 WireGuard VPN,那么 Tailscale 就是同样的隧道,只是由它代您完成密钥分发和防火墙穿透。

Tailscale 如何工作?

由节点组成的私有网络称为 tailnet。计算机加入 tailnet 时,会发生以下 4 件事。

  1. tailscaled daemon 启动,生成 WireGuard 密钥对,并将状态保存在 /var/lib/tailscale/tailscaled.state 中。私钥保留在该计算机上。Tailscale 的表述很直接:“私钥永远不会离开所属节点。”
  2. 节点登录协调服务器,并上传公钥,以及它认为可用于访问自身的地址。Tailscale 将该服务器描述为“用于存放公钥的共享投递箱”。
  3. 协调服务器返回网络映射,其中包含公钥、tailnet 地址、计算机名称,以及该节点获准访问的每个节点的候选端点。
  4. 随后,每对节点都会尝试在彼此之间建立直接的 WireGuard 隧道。如果失败,它们会改为通过中继转发数据包。

每个节点都会从 100.64.0.0/10 中获得一个稳定地址。该地址范围属于运营商级 NAT,范围是 100.64.0.0 到 100.127.255.255。Tailscale 使用这一范围,是因为它专用于运营商基础设施,因此很少与服务器已有的私有地址冲突。在 Linux 上,该隧道会显示为名为 tailscale0 的接口。

WireGuard 的实现位于用户空间的 tailscaled 中,而不是内核模块中。这就是 Tailscale 能够在容器虚拟化环境中启动的原因;在该环境中,sudo modprobe wireguard 会因 Operation not supported 失败。这也意味着在特定主机上,其吞吐量上限低于内核 WireGuard。这是 Tailscale 与纯 WireGuard 的对比中介绍的权衡之一。

使用以下 2 个命令即可了解当前状态。

tailscale ip -4
tailscale status

tailscale 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" 表示流量正在通过法兰克福的 Tailscale 中继传输。- 表示当前没有与该节点建立活动会话,这是正常现象。

协调服务器可以看到什么,不能看到什么

协调服务器保存公钥和元数据。它知道您的机器名称、每个节点所属的用户或标签、每个节点的 tailnet 地址、节点可通过哪些公网地址访问、每个节点最近一次在线的时间,以及您编写的策略文件。这构成了您整个节点集群的完整映射。

它不保存任何私钥,因此无法解密两个节点之间的流量。流量在 WireGuard 对等节点之间端到端加密,协调服务器不是对等节点。

它能够分发密钥。无论是托管的协调服务器还是自行运行的协调服务器,都必须信任它向您的节点告知哪些公钥属于 tailnet。这是后文威胁模型的关键,也是 Headscale,一个由您自行托管的开源协调服务器 存在的原因。

不同防火墙后的两台服务器如何直接通信

NAT(网络地址转换)让多台计算机能够共享一个公网地址。您的 VPS 通常拥有独立的公网地址,但您希望加入 tailnet 的其他计算机往往没有,例如家庭服务器、办公网络中的构建运行器,或位于您无法修改其配置的服务商防火墙之后的主机。

Tailscale 使用基于 STUN(NAT 会话穿越实用工具)和 ICE 标准的技术来查找路径。每个节点都会向 STUN 服务器发送一个小型 UDP 数据包,并获知路由器为该套接字分配的公网地址和端口。两个节点都将这些候选地址报告给协调服务器,协调服务器再将其转发给另一端。随后,两个节点同时开始向对方发送数据包。每台路由器都会先看到出站数据包,因此会创建映射,并接受从同一地址返回的响应。两端都不需要配置入站防火墙规则。这也是家中的 NAS 或 Pi 在路由器未配置端口转发的情况下仍可保持可访问的原因;使用 Tailscale 代替端口转发将介绍这一配置,包括它无法实现的功能。

端口是固定的。直接建立的 WireGuard 隧道使用 UDP,源端口默认为 41641。STUN 通过 UDP 3478 与 Tailscale 的中继服务器通信。控制连接优先通过 TCP 80 上的普通 HTTP,再由 Tailscale 自有的加密传输进行封装;如果端口 80 无响应,则回退到 TCP 443 上的 HTTPS。通过 Tailscale DERP 服务器中继的数据使用 TCP 443 上的 HTTPS;如果您在自己的 tailnet 中运行对等中继,则通过 UDP 传输。大多数情况下无需开放任何入站端口,但在 NAT 行为复杂的网络中,允许 UDP 41641 入站可以提高建立直接连接的可能性。

tailscale netcheck

查看该报告中的两行。UDP: true 表示此计算机可以向互联网发送 UDP 数据包,UDP: false 表示无法发送,因此此节点发起的所有连接都将通过中继。MappingVariesByDestIP: true 表示路由器会为每个目标分配不同的公网端口,因此上述地址预测无法生效,这些节点通常会保持中继连接。

Tailscale 改用 DERP 中继的情况

DERP(指定的加密数据包中继)是备用路径。Tailscale 在多个区域运行中继服务,可通过 TCP 443 访问。无法建立直连路径的节点会将 WireGuard 数据包发送到其中一个中继。例外是配置了指定对等中继的 tailnet。此时,中继是由您自己的设备提供,并通过 UDP 转发流量。Tailscale 会优先尝试这些中继。只有在没有可用中继时,才会回退到 DERP,并且 tailscale status 随后会将连接显示为 peer-relay。

数据包始终处于加密状态。Tailscale 明确说明:“DERP 服务器永远无法解密您的流量。它只会盲目地将已加密的流量从一个节点转发到另一个节点。”中继可以看到密文,也可以看到哪个节点正在与哪个节点通信。

中继还会承载大多数连接的初始数据包。建立直连路径需要一些时间,因此会话通常先通过中继建立。两个节点找到彼此后,连接会原地升级为直连。您可以监视这一过程。

tailscale ping db-1

最初的响应显示为 via DERP(fra),稍后的行则会报告类似 via 198.51.100.24:41641 的内容。此变化表示连接已升级为直连隧道。如果状态始终不变,请在两端运行 tailscale netcheck。中继路径仍然可以正常工作,但会增加延迟,因为每个数据包都要绕行第三台设备。如果您遇到的是传输速度慢,而不是连接中断,请参阅为什么中继的 Tailscale 连接比直连慢得多,其中会测量两者的差距,并说明如何让 VPS 建立直连路径。

将 VPS 加入 tailnet

安装脚本支持 Ubuntu 和 Debian。

curl -fsSL https://tailscale.com/install.sh | sh
sudo tailscale up

sudo tailscale up 会输出一个 URL。打开该 URL,完成身份验证,节点就会出现在管理控制台中。随后确认守护进程在重启后能够恢复运行,因为这一步经常被忽略。

sudo systemctl is-enabled tailscaled
tailscale status

is-enabled 应输出 enabled,tailscale status 应列出新节点及其 100.x 地址。对于通过脚本构建的服务器,交互式 URL 没有用处。请在管理控制台中生成身份验证密钥,并传入该密钥以及用于记录机器类型的标签。

sudo tailscale up --auth-key=tskey-auth-REPLACE-ME --advertise-tags=tag:server

带标签的节点归标签所有,而不是归执行命令的用户所有。因此,即使该用户的账户被删除,节点仍可继续运行。必须先在策略文件的 tagOwners 下声明该标签,否则命令会被拒绝。添加标签还会改变机器计入套餐的方式,因为带标签的资源单独计费,不计入用户自己的设备;免费套餐实际涵盖的内容说明了这些限制的具体范围。

对于节点集群,有两个设置需要注意。节点密钥默认在 180 天后过期(截至 October 2026)。密钥过期后,“与给定端点之间的连接将停止工作”,直到设备重新进行身份验证。管理员可以在管理控制台中打开已过期机器的设置,选择 Temporarily extend key,将密钥临时延长 30 分钟,以便再次登录或禁用过期机制。使用标签进行身份验证的节点(例如上面的节点)默认禁用密钥过期。对于其他无人值守服务器,请在管理控制台中打开该机器所在的行,然后选择 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 不会让绑定到回环地址的服务变得可访问。

ss -tlnp | grep 3000

如果输出为 127.0.0.1:3000,说明该 socket 只接受目标地址为 127.0.0.1 的数据包。来自其他节点的请求会以本节点的 100.x 地址为目标地址到达,因此内核没有进程监听该地址,并返回 TCP 重置。客户端会报告 Connection refused。隧道本身正常,问题出在监听器上。

有两种直接的解决方法。将服务绑定到节点的 tailnet 地址。这样服务不会暴露在公网接口上,也无需经过代理:传入 --bind 100.101.102.104,或在配置中使用等效选项;对于容器,将端口发布为 -p 100.101.102.104:3000:3000。或者让服务继续监听回环地址,再将 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 策略文件负责,而不是 ufw。编写策略时应考虑这一点。

使用容器时还需注意。已发布的 Docker 端口会安装自己的 NAT 规则,并绕过 ufw,因此 ufw deny 无法关闭该端口。Docker 已发布端口绕过 ufw 解释了其中的机制。像上文一样将端口发布到 tailnet 地址,可以避开这个问题。如果应用运行在 Docker Compose 下,Compose 堆栈中的 Tailscale sidecar 可以进一步完全移除已发布端口,使容器只能通过其 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 即可恢复;在服务器上禁用密钥过期功能,可以避免问题重复发生。如果密钥未过期,请检查策略文件,因为 Tailscale 针对设备未出现在 tailscale status 中的建议是检查是否存在阻止连接的访问规则。

对等节点已列出,但连接超时。 如果 tailscale ping 收到回复,说明连通性正常,可能原因是策略拒绝了该流量。检查 acls 部分,确认其中是否存在覆盖此源、目标和端口的规则。被拒绝的数据包会直接丢弃,而不会返回响应,因此你会看到超时,而不是 Connection refused。

MagicDNS 名称无法解析。 ping db-1 失败,但 ping 100.101.102.104 正常工作。首先确认客户端接受 Tailscale 的 DNS 设置:使用 --accept-dns=false 启动的节点不会将查询发送到 100.100.100.100 中的 stub resolver。然后检查 cat /etc/resolv.conf,以及计算机上其他会写入该文件的程序。在使用 systemd-resolved 的系统上,该文件通常是指向 /run/systemd/resolve/stub-resolv.conf 的符号链接,因此其中没有列出 100.100.100.100 是正常现象。如果 tailscaled 直接写入该文件,DHCP 客户端或其他覆盖该文件的工具就会导致 MagicDNS 失效。如果这些情况都不适用,故障也可能存在于本地或网络 DNS 配置中。这与 WireGuard 隧道内的 DNS 故障属于同一类问题。

tailscale up 拒绝你的标签。 策略文件中的 tagOwners 下未声明该标签。将其添加到该位置,然后再次运行命令。

FAQ

Tailscale 是 VPN 还是网状网络?

这两个词都准确,但描述的是不同层次。隧道使用 WireGuard,因此这是 VPN。拓扑是网状网络,因为每个节点都会直接向与其通信的其他节点建立隧道,而不是将每个数据包都发送到一个中央服务器。协调服务器位于控制路径中,而不在数据路径中,因此即使它无法访问,现有隧道仍会继续传输流量。中断期间停止的是新节点加入,以及密钥或策略变更的下发。不过,它默认并不是隐私 VPN:在通过出口节点转发流量之前,网站看到的仍是您自己的公网地址;Tailscale 对网站和 ISP 隐藏的内容取决于具体配置。

Tailscale 能读取我的网络流量吗?

不能读取流量内容。节点之间的流量通过 WireGuard 端到端加密,私钥不会离开节点,DERP 中继只负责转发无法解密的数据包。Tailscale 可以看到元数据:机器名称、所有者、公钥、端点地址,以及每个节点的在线时间。它还负责分发密钥,因此遭到入侵的协调服务器可能会尝试插入一个节点,而您的设备组随后会信任该节点。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 地址的连接,因为该套接字只接受回环目标地址,客户端会看到 Connection refused。将服务绑定到 tailnet 地址,或者运行 tailscale serve 3000 对其进行代理。如果监听器已经位于 0.0.0.0,但连接超时而不是被拒绝,则问题不在绑定地址。首先检查策略规则和主机防火墙,然后检查网络防火墙,以及任一设备的身份验证是否已过期。

我应该使用 Headscale,而不是 Tailscale 的协调服务器吗?

当设备拓扑或密钥分发必须保留在您控制的基础设施上,或者 tailnet 必须在不依赖外部服务的情况下运行时,可以使用 Headscale。客户端和协议相同。代价是您需要自行运行协调服务器;协调服务器中断后,新节点无法加入,策略变更也无法生效。对于规模较小的设备组,启用 tailnet lock 的托管控制平面通常是更好的取舍。