深入讲解一下 Tailscale 的源码
核心是 tailscaled:这个守护进程是所有逻辑的中心。分层清晰:cmd (入口) -> ipn (状态机) -> wgengine (隧道) / control (控制) / net (连接)。NAT 穿越是关键技术:net/stun 和 net/derp 是 Tailscale 能够在复杂网络环境下工作的“魔法”所在。控制平面与数据平面分离:协调服务器只处理元数据和信令,不碰用户流量,保证
我们来深入讲解一下 Tailscale 的源码。这是一个非常优秀的项目,代码质量很高,结构清晰,是学习 Go 语言、网络编程和系统设计的绝佳范例。
我会从高到低,分层次地为你解析这个项目。
1. 核心思想与技术栈 (The Big Picture)
在深入代码之前,先理解 Tailscale 是什么,以及它解决了什么问题。
-
它是什么? Tailscale 是一个“零配置 VPN”(Zero-config VPN)。它基于 WireGuard® 协议,为你所有的设备(电脑、手机、服务器等)创建一个安全、扁平的私有网络,无论这些设备身处何方。
-
核心优势?
-
简单易用:不需要复杂的防火墙配置、证书管理和 IP 地址规划。通过 SSO(如 Google, GitHub)登录即可。
-
点对点连接:尽可能在设备之间建立直接的加密连接,延迟低、速度快。
-
NAT 穿越:这是 Tailscale 的技术精髓之一。它能神奇地穿透大多数家庭/公司路由器和防火墙,实现设备间的直接通信。
-
身份认证:它不是基于传统的 IP 地址或密钥文件来认证,而是基于你的身份(Identity Provider)。
-
-
技术栈:
-
语言:绝大部分代码使用 Go 语言编写。这使得它天生具备高并发、跨平台和优秀的网络库支持。
-
核心隧道协议:WireGuard。Tailscale 负责自动化的密钥交换、配置和连接管理,而底层的加密和隧道传输则完全依赖 WireGuard。
-
NAT 穿越:使用 STUN 协议和自定义的 DERP(Detoured Encrypted Routing Protocol)协议。
-
2. 项目架构:三大组成部分
Tailscale 的系统可以分为三个主要部分:
-
客户端 (Client):就是你在 github.com/tailscale/tailscale 这个仓库里看到的主要代码。它运行在你每一台设备上,通常以后台服务/守护进程(tailscaled)的形式存在。
-
协调服务器 (Coordination Server / Control Plane):这是 Tailscale 的“大脑”。它是一个中心化的服务,这部分代码是闭源的,不包含在这个 GitHub 仓库里。它负责:
-
用户认证(通过 Google, Microsoft, GitHub 等)。
-
公钥交换。
-
网络拓扑管理(生成“网络地图” Netmap)。
-
分发 IP 地址(在 100.x.x.x CGNAT 地址空间内)。
-
协调 NAT 穿越。
-
注意:开源社区实现了一个兼容的协调服务器叫 Headscale,可以用于自建。
-
-
DERP 中继服务器 (Relay Servers):当两个设备无法建立点对点连接时(例如在严格的对称 NAT 后面),它们的流量会通过 DERP 服务器进行加密中继。DERP 服务器是全球分布的,其代码是开源的,就在这个仓库的 derp/ 目录下。
关键交互流程:你的设备上的客户端 (tailscaled) 只会和协调服务器交换控制信息(公钥、IP 等),而数据流量会尝试直接点对点传输。只有在直连失败时,数据流量才会走 DERP 中继服务器。所有数据流量始终是端到端加密的,协调服务器和 DERP 服务器都无法解密你的流量。
3. 源码目录结构导览 (Diving into the Code)
现在我们来看 github.com/tailscale/tailscale 这个仓库的核心目录:
cmd/ - 入口程序
这是所有可执行文件的入口。Go 项目的标准布局。
-
cmd/tailscale/:命令行工具 tailscale 的源码。这是用户交互的前端,它通过本地 IPC 与后台的 tailscaled 进程通信。例如,你运行 tailscale up 或 tailscale status,就是这个程序在工作。
-
cmd/tailscaled/:核心的守护进程 tailscaled 的源码。这是 Tailscale 的心脏,它在后台持续运行,负责管理 WireGuard 接口、与协调服务器通信、处理 NAT 穿越等所有核心逻辑。
-
cmd/derper/:DERP 中继服务器的源码。如果你想自己搭建一个 DERP 服务器,就会用到它。
ipn/ - 核心状态机与后端逻辑
ipn 代表 "Inter-Process Networking"(进程间网络)或者 "IP-level Networking"。这是客户端的核心逻辑层和状态管理。
-
它定义了客户端的状态(如 Running, Starting, Stopped)。
-
处理来自协调服务器的指令(比如更新网络地图 Netmap)。
-
管理偏好设置(如是否接受子网路由、是否作为出口节点等)。
-
它是 tailscaled 和具体平台实现之间的桥梁。
wgengine/ - WireGuard 引擎
这是对 WireGuard 的封装层。Tailscale 需要在不同的操作系统上与 WireGuard 交互(有的是内核模块,有的是用户态实现),这个目录就是为了提供一个统一的接口。
-
wgengine/router/:负责配置操作系统的路由表,将发往 Tailscale IP 的流量导入 WireGuard 隧道。
-
wgengine/netstack/:一个非常酷的部分。它集成了一个用户态网络协议栈 (gvisor/netstack)。这使得 Tailscale 可以在不获取管理员权限的情况下,通过拦截特定应用的流量来工作(例如 tailscale ssh)。
net/ - 网络魔法的聚集地
所有与网络连接、NAT 穿越相关的底层代码都在这里。
-
net/stun/:STUN (Session Traversal Utilities for NAT) 协议的实现。tailscaled 用它来向 STUN 服务器发送请求,以发现自己在外网的公网 IP 和端口。
-
net/derp/:DERP 协议的客户端和服务器端实现。这是 Tailscale 的备用方案。如果 STUN 失败,流量就通过它来中继。
-
net/dns/:MagicDNS 的实现。它负责拦截 DNS 请求,并将 *.ts.net 格式的域名解析为对应的 Tailscale IP 地址。
-
net/netcheck/:网络状况探测。客户端启动时,它会运行一系列检查,探测网络的类型、是否支持 Hairpinning、IPv6 等,并将报告发送给协调服务器,以便更好地进行 NAT 穿越。
control/ - 与协调服务器的通信
这是客户端与协调服务器通信的客户端实现。
-
它负责使用 Noise 协议(和 WireGuard 使用的加密协议同源)与协调服务器建立一个安全的长时间连接。
-
通过这个连接,客户端进行登录、发送公钥、上报网络状况,并接收协调服务器下发的网络地图 (Netmap)。
-
注意:这里是客户端代码,不是服务器代码。
tsnet/ - 将 Tailscale 嵌入你的应用
这是一个非常强大的库。它允许你将整个 Tailscale 网络栈直接嵌入到你的 Go 应用程序中。你的应用程序不需要 tailscaled 在后台运行,就可以直接监听一个 Tailscale IP 地址,或者连接到你私有网络中的其他服务。这对于构建安全的内部服务非常有用。
types/ - 公共数据结构
定义了整个项目中广泛使用的核心数据结构。
-
netmap/ (types/netmap/netmap.go): 定义了 NetMap 结构。这是协调服务器下发的最重要的数据,包含了你网络中所有节点(Peers)的公钥、IP 地址、允许的 IP、DERP 区域信息等。tailscaled 根据这个“地图”来配置 WireGuard。
-
key/:定义了各种类型的密钥(公钥、私钥等)。
4. 一个典型的连接流程 (Putting It All Together)
为了更好地理解代码是如何协同工作的,我们来看一下当一个新设备 tailscale up 时发生了什么:
-
启动与认证 (cmd/tailscale, cmd/tailscaled, control/)
-
用户运行 tailscale up。
-
tailscale CLI 程序通过 IPC 通知后台的 tailscaled 守护进程。
-
tailscaled 生成一对新的公私钥。
-
tailscaled 中的 control 客户端向协调服务器发起连接,并发送一个登录请求。
-
协调服务器返回一个认证 URL。CLI 将此 URL 显示给用户。
-
用户在浏览器中打开 URL,通过 Google/GitHub 等登录。
-
登录成功后,协调服务器将 tailscaled 的公钥与用户的身份关联起来。
-
-
获取网络地图 (control/, types/netmap/)
-
认证成功后,tailscaled 会收到协调服务器下发的 NetMap。
-
这个 NetMap 包含了网络中所有其他设备的信息:它们的公钥、已知的公网 IP(Endpoint)、允许的 DERP 区域等。
-
-
配置 WireGuard (wgengine/)
-
tailscaled 解析 NetMap。
-
它调用 wgengine,使用自己的私钥和网络中其他设备(Peers)的公钥来配置本地的 WireGuard 接口(tailscale0)。
-
同时,wgengine/router 会配置系统的路由表,将流向 100.x.x.x 地址段的流量都指向 tailscale0 接口。
-
-
建立点对点连接 (net/netcheck/, net/stun/, net/derp/)
-
现在,假设你想 ping 你的另一台设备 peer-B。
-
你的设备 peer-A 的 tailscaled 从 NetMap 中查找 peer-B 的信息。
-
peer-A 会并行地尝试多种方式联系 peer-B:
-
直接向 peer-B 在 NetMap 中记录的所有已知 IP 地址(内网 IP、公网 IP)发送加密的 WireGuard 握手包。
-
如果直连失败,peer-A 和 peer-B 都会通过 STUN 协议发现各自的公网 IP 和端口,并将这些新发现的“候选地址”报告给协调服务器。协调服务器再将这些地址广播给网络中的其他节点。然后双方会根据这些新的地址再次尝试直连。这个过程就是 NAT 穿越。
-
如果所有直连尝试都失败了,peer-A 会将加密的 WireGuard 数据包封装在 DERP 协议里,发送给离它最近的 DERP 中继服务器。DERP 服务器再将数据包转发给 peer-B。
-
-
-
通信
-
一旦路径建立(无论是直连还是通过 DERP),两台设备之间的 WireGuard 隧道就打通了。
-
所有后续流量都在这个加密隧道中传输,实现了安全通信。
-
总结
-
核心是 tailscaled:这个守护进程是所有逻辑的中心。
-
分层清晰:cmd (入口) -> ipn (状态机) -> wgengine (隧道) / control (控制) / net (连接)。
-
NAT 穿越是关键技术:net/stun 和 net/derp 是 Tailscale 能够在复杂网络环境下工作的“魔法”所在。
-
控制平面与数据平面分离:协调服务器只处理元数据和信令,不碰用户流量,保证了性能和隐私。
如果你想开始阅读代码,我建议从 cmd/tailscaled/main.go 开始,然后跟进到 ipn 包和 wgengine 包,这会让你对它的核心运作有一个很好的了解。
更多推荐
所有评论(0)