架构
TSLink 底层工作原理
场景指南:分享一个应用,而非整台机器。
概述
TSLink 为电脑、服务器或云主机提供应用访问与管理。Tailscale 提供私有传输与 HTTPS,TSLink 管理每台主机上的已有应用;一个共用 daemon 管理每服务独立内嵌节点。它不安装应用、不隔离主机进程、不创建 VPC 或汇总多主机。
虚线表示管理,实线表示访问;每行服务都属于这台主机。私有 portal 与请求、访客 gate、scoped MCP、期限和有界历史已交付。多主机汇总、admin REST/dashboard、Docker 发现、middleware、自定义 ACME 与 metrics 仍在规划中。
嵌入式 tsnet 节点
TSLink 的核心是每个服务对应一个嵌入式 tsnet 节点。与需要 tailscaled 守护进程的传统 Tailscale 设置不同,tsnet 完全在 TSLink 进程内运行:
- 无需系统级 Tailscale — TSLink 管理自己的 Tailscale 身份。
- 每个服务一个节点(微分段) — 每个注册的服务都有自己的 tsnet 节点,拥有独立的主机名和 WireGuard 身份。代理/文件 HTTP 服务使用 HTTPS 证书;原始 TCP 不在 TSLink 中终止 TLS。这是网络层的分段,并非主机或进程隔离:TSLink 不会沙箱化本地进程,因此某个服务后端被攻破后,影响本身不会自动止步于该服务——完整边界见标准对齐。
- 独立状态 — 每个节点的 WireGuard 密钥和配置存储在
~/.config/tslink/nodes/<服务名>/中。 - 直接集成 — TSLink 控制每个节点的生命周期,在服务添加或移除时启动和停止它们。
多节点服务器架构
TSLink 使用 Server 结构体管理 N 个 ServiceNode 实例。每个 ServiceNode 封装一个 tsnet.Server 及其关联的处理器(代理、文件或 TCP):
┌─────────────────────────────────────────────┐
│ TSLink Server │
│ │
│ ┌─────────────┐ ┌─────────────┐ │
│ │ ServiceNode │ │ ServiceNode │ ... │
│ │ "webapp" │ │ "api" │ │
│ │ │ │ │ │
│ │ tsnet.Server│ │ tsnet.Server│ │
│ │ WireGuard │ │ WireGuard │ │
│ │ TLS (HTTP) │ │ TLS (HTTP) │ │
│ │ Handler │ │ Handler │ │
│ │ Access logs │ │ Access logs │ │
│ └─────────────┘ └─────────────┘ │
│ │
│ ┌─────────────────────────────────────┐ │
│ │ Registry Watcher (fsnotify) │ │
│ └─────────────────────────────────────┘ │
└─────────────────────────────────────────────┘每个服务节点独立运行,拥有自己的 WireGuard 身份;HTTPS 证书适用于代理/文件 HTTP 服务。这种按服务的分隔是网络分段的一种形式:每个服务拥有独立的加密身份,可以独立关闭,不影响其他服务。
WireGuard 网络
Tailscale 网络基于 WireGuard 构建,这是一种现代 VPN 协议。tailnet 段的流量在 tailnet 对端之间保持加密,即使本地网络被攻破也是如此——这是零信任架构假设网络不可信的关键属性。从 TSLink 节点到本地服务的短跳转是独立的,可能是明文。当 TSLink 启动时:
- 每个嵌入式 tsnet 节点与 tailnet 上的其他设备建立 WireGuard 隧道。
- 访问设备与 TSLink 节点之间的流量由 WireGuard 加密;从节点到本地服务的跳转由你配置,可能是明文。
- Tailscale 处理 NAT 穿透;宿主机仍须有策略允许的协调服务器及对端/中继网络连接。
- 默认不暴露任何服务到公网——攻击面仅限于已认证的 tailnet 成员。
NAT 穿透和 DERP 中继
TSLink 依赖 Tailscale 的 NAT 穿透基础设施:
- 在可能的情况下,使用 UDP 打洞建立直接连接。
- DERP 中继回退 — 当直接连接失败时(例如在限制性 NAT 或防火墙后面),流量通过 Tailscale 全球分布的 DERP(Designated Encrypted Relay for Packets)服务器中继。DERP 中继流量在 tailnet 对端之间保持 WireGuard 加密——中继无法读取。
服务类型
反向代理
对于代理类型的服务(--proxy),TSLink 使用 Go 的 httputil.ReverseProxy 运行 HTTP 反向代理:
tailnet 上的客户端 → WireGuard 隧道 → TSLink → localhost:port反向代理:
- 在 TSLink 节点处终止 TLS。
- 将请求转发到指定的本地
host:port。 - 通过
SetURL将出站Host改为后端目标。 - 通过
SetXForwarded从入站请求重建X-Forwarded-For、X-Forwarded-Host、X-Forwarded-Proto,不保留客户端提交的转发头。原始请求主机名位于X-Forwarded-Host,见 Go 代理重写契约。 - WhoIs 能解析可用调用者身份时,注入 Tailscale 身份头:
| 请求头 | 描述 |
|---|---|
X-Tailscale-User-Login | 已认证用户的登录名 |
X-Tailscale-User-Name | 已认证用户的显示名称 |
X-Tailscale-User-Picture | 用户头像的 URL(可用时) |
X-Tailscale-Node | 来源节点的 computed name(可用时) |
代理身份头是 best-effort:TSLink 先移除传入的 X-Tailscale-* 头,只有 WhoIs 解析出用户资料时才注入替代值。只有没有适用人员策略或 --allow 限制时,无法解析身份的请求才可能不带身份头转发。人员检查与已配置的 --allow 都会 fail closed。
只有在受控的 TSLink 到后端路径中,确保调用者不能绕过代理时,才能信任这些头。应用若要求身份,就应拒绝缺失值并保留所需认证与授权。移除伪造头保护的是代理路径,不能保护直接可达的后端。
文件服务器
对于目录类型的服务(--dir),TSLink 使用 Go 内置的 http.FileServer 提供文件:
tailnet 上的客户端 → WireGuard 隧道 → TSLink → 文件系统目录分享通过 HTTPS 只读提供内容并启用目录列表;单个普通文件分享只提供选定文件,拒绝同目录其它文件和列表。文件服务通过 WhoIs 执行已配置的 HTTP --allow 并记录访问日志,不注入代理身份头。
TCP 转发器
对于 TCP 类型的服务(--tcp),TSLink 使用双向 io.Copy 执行原始 TCP 代理:
tailnet 上的客户端 → WireGuard 隧道 → TSLink → localhost:port (TCP)这对于数据库(PostgreSQL、MySQL)、Redis 以及任何其他基于 TCP 的协议非常有用。连接以逐字节方式转发,不进行 HTTP 处理。实现了正确的半关闭语义——当一方关闭其写通道时,TSLink 将半关闭传播到另一方,而不是立即断开整个连接。
原始 TCP 使用主机名和端口端点。TSLink 在此路径不添加 HTTP 身份头、HTTP --allow 过滤或应用 TLS 终止。认证及需要的应用/后端 TLS 应保留在目标服务和客户端;WireGuard 保护的是独立的 tailnet 段。
中间件管道
已交付 HTTP 路径会应用内置 allowed_users ACL 和访问日志。Metrics instrumentation 尚未实现。可配置 middleware(限流、Basic Auth、IP 白名单、CORS)属于 roadmap/experimental。完整管道详情请参阅实验性与路线图页面。
凭证系统
默认入网不需要存储凭据。人完成 needs_login / auth_url 授权及 tailnet 要求的审批后,节点归用户所有且不带标签。可选存储凭据启用另一种节点认证模式。API-token 模式不把令牌直接传给节点,而是按服务实际标签及 ephemeral 标志派生短期、单次使用的认证密钥。密钥有效期短不代表每个节点都临时。API token 本身是全权限 tailnet 凭据,不是带 scope 的节点密钥。
可选存储模式支持两种凭证类型:
| 凭证 | 前缀 | 行为 |
|---|---|---|
| API 访问令牌 | tskey-api-* | 通过 Tailscale API 验证。当前对 tag/device operations 和认证材料派生支持最完整。会定期过期。 |
| OAuth 客户端密钥 | tskey-client-* | 直接存储且不会周期性过期。由 tsnet 直接用于认证,但当前 Tailscale REST 标签/设备自动化路径更窄。无人值守前请验证。 |
凭据优先使用系统钥匙串(macOS Keychain、Linux Secret Service、Windows Credential Manager)。明文 0600 文件回退仅在 macOS/Linux 证明旧钥匙串值不存在或已删除后允许;钥匙串不可达或状态不确定时拒绝写入,应恢复访问后重试。Windows 不允许凭据文件回退。无头运行本身不保证可以回退。
旧版支持:如果不存在 API 密钥或 OAuth 密钥,则检查 ~/.config/tslink/authkey(仅向后兼容)。
TLS 证书
代理/文件 HTTP 服务通过 LocalAPI 使用 Tailscale 自动 HTTPS 证书配置,前提是 tailnet 已启用 HTTPS,且控制服务器支持所需证书流程:
- 证书从 Tailscale 协调服务器自动获取。
- 它们是有效的、公开信任的证书(通过 Let's Encrypt)。
- 证书续期由 tsnet 库透明处理。
- 无需手动证书管理。
- 每个代理/文件服务获得独立证书——按主机名配置,不共享。
请读取实际运行时主机名,例如 <服务名>.<tailnet名>.ts.net。原始 TCP 使用 TCP 监听器,不走此 HTTPS 证书路径。配置自定义控制 URL 本身不能证明可以签发证书。
自定义域名和 ACME 属于 roadmap/experimental。当前没有 --domain / --acme-email 标志或 domain / acme_email registry 字段。旧键即使为空也会以 unknown_config_key 拒绝,请删除并使用默认 tailnet 主机名。
注册表和热重载
服务注册表(~/.config/tslink/registry.json)是 TSLink 的声明式配置。热重载系统工作方式如下:
- fsnotify 监控器监视注册表文件的变化。
- 获取**文件锁(mutex)**以防止竞态条件。
- 检测到变化时,加载新的注册表并与当前状态进行差异比较。
- 新服务使用自己的 tsnet 节点和 HTTP/TCP 处理器启动。
- 移除的服务关闭其节点并注销处理器。
- 已更改的服务重建受影响的 runtime;是否重置注册状态取决于实际认证身份是否变化。
- 未更改的服务保持运行,不受中断。
新增或重启服务仍须完成注册并就绪;字段及 runtime 重建与注册重置的区别见热重载。
关于 Docker 集成、集群支持、管理 API 和 Prometheus 指标的详细信息,请参阅实验性与路线图。
结构化日志
tslink logs 查看 stderr 网关诊断(tslink.err.log)与 daemon stdout(tslink.out.log)。访问历史用独立有界本地 JSONL 记录 HTTP/文件、TCP 与 guest 事件,再合并独立 journal 中的 intent/completion 与生命周期 receipt。WhoIs 可用时证明身份;日志可能丢失或有崩溃缺口,只有 intent 不代表成功完成。
Tailscale Funnel
开放 proxy 发布可以通过 --funnel --public 使用 Tailscale Funnel 可选地公开暴露。这是显式公共暴露:服务可以从公共互联网通过相同的 https://<服务名>.<tailnet名>.ts.net URL 访问,无需访问设备在你的 tailnet 上,且不受 TSLink 身份执行保护。文件和 TCP 服务不支持 Funnel。
标签系统
配置拥有所需策略权限的 API 访问令牌且显式选择 --manage-acl 时,TSLink 可以创建普通 ACL 标签所有者条目。未传该标志时,有存储凭据的 login/serve 不执行普通远端 ACL 变更,并报告其计划;零凭据 serve 不 ensure 普通远端标签。
显式普通 ACL 配置
login --manage-acl
→ 存储凭据,仅请求普通默认标签 ensure
→ 不收集 registry 标签,也不启动服务节点
serve --manage-acl(有存储凭据且拥有所需策略权限)
→ 收集有效注册服务使用的普通标签
(包括服务实际使用的已配置默认标签)
→ 请求普通 tag-owner ensure,然后启动/协调服务节点Ensure 对已有 tag-owner 是幂等的。结果仍取决于凭据支持和策略权限;login 可能报告 degraded 配置,并不表示已成功创建 owner。显式选择契约见ACL 变更。
显式公共 Funnel(--funnel --public)有独立的默认启用配置路径,用于共享标签所有者和 nodeAttrs 授权,除非关闭 auto-provisioning。该路径仍需要可用 API 访问和策略权限。普通 --manage-acl 是另一条边界;本地清理不会删除共享 Funnel 授权。
热重载与标签
无存储凭据模式不广播 registry 中保存的标签,因此修改标签会在启动和热重载时保留用户所有节点的入网状态。存储凭据模式下,实际带标签身份发生变化时,受影响节点会停止、清理本地状态,再用更新的认证材料重启。远端陈旧设备清理需要精确所有权证明;无法证明的匹配候选会报告为 protected/skipped。
默认标签
存储凭据模式创建节点时,未显式指定 --tags 的服务使用默认标签(tag:tsmain,可通过 tslink tags set-default 覆盖)。无存储凭据的节点仍归用户所有且不带标签,即使 registry 条目存有标签;保存标签不会创建远端标签所有权。
tslink tags 命令#
本地标签注册及远端操作见标签配置。保存本地标签不会应用远端策略;远端删除要求 delete-remote --force --manage-acl 及其授权/安全检查。
访问控制
TSLink 在多个层级实施访问控制,实现纵深防御:
- 代理/文件 HTTP ACL(
--allow) — 将服务限制为特定 Tailscale 身份(通过邮箱指定用户或使用tag:prod等标签)。已交付的 ACL 检查以尽力而为方式从 TailscaleWhoIs解析调用方身份(按来源 IP 缓存约 60 秒);配置--allow后为 fail-closed:对无法授权的调用返回403 Forbidden。原始 TCP 没有 HTTP ACL。 - Tailscale ACL — Tailnet 访问由 Tailscale 网络策略控制。Funnel 策略允许节点公开服务,不认证公网访客;访客授权应由后端应用负责。
- 中间件管道 — Roadmap/experimental。速率限制、IP 白名单、Basic Auth 等额外防御层在成为已交付保护前需要 runtime wiring。
进程生命周期
TSLink 使用基于 PID 的进程管理:
- 前台模式:进程在当前终端会话中运行。
- 守护进程模式(
--daemon):进程 fork 到后台,将 stdout/stderr 重定向到日志文件,并将 PID 写入~/.config/tslink/tslink.pid。 - 停止:Unix 发送
SIGTERM,最多等待五秒确认退出;超时返回错误,不会强制终止。Windows 请求立即终止并确认退出。维护前请按确认停机操作。 - 单实例:TSLink 在启动前检查现有 PID,防止重复网关。
- 信号处理:Unix
SIGTERM和SIGINT发起监听器/节点关闭;每个 HTTP 服务器有自己的排空预算,与 CLI 进程退出等待不同。 - 自动启动:
tslink install将网关注册为系统登录服务(macOS 的 LaunchAgent、Linux 的 systemd 用户服务、Windows 的 Task Scheduler 与内建 supervisor)。
公网与私有访问
浏览器访客链接采用明确公开的 Funnel 和强制 gate;链接/PIN 可转发,不是人的身份。同一应用私有连接仍独立执行人员/allow 检查。开放 Funnel 是另一种无访问者身份检查的发布。Portal 每台主机保持私有,请求需明确 requestable 私有 HTTP/文件、符合条件成员和主人审批。Scoped MCP 是管理权限边界,不是系统 sandbox。