---
title: "架构"
description: "TSLink 底层工作原理"
url: "https://tslink.md/zh/docs/architecture"
locale: "zh"
product_version: "0.1.1"
source: "https://github.com/anydoor7/tslink/blob/v0.1.1/docs/architecture.md"
---

> Documentation index: https://tslink.md/zh/llms.txt · Installed binary is authoritative: `tslink manifest`.

场景指南：[分享一个应用，而非整台机器](https://tslink.md/zh/docs/share-one-app.md)。

## 概述

TSLink 为电脑、服务器或云主机提供应用访问与管理。Tailscale 提供私有传输与 HTTPS，TSLink 管理每台主机上的已有应用；一个共用 daemon 管理每服务独立内嵌节点。它不安装应用、不隔离主机进程、不创建 VPC 或汇总多主机。

[一台电脑或云主机：私有 Tailscale 传输、可选公网 HTTPS/Funnel、共用 daemon 与各应用节点。](https://tslink.md/service-map-light.svg)

[一台电脑或云主机：私有 Tailscale 传输、可选公网 HTTPS/Funnel、共用 daemon 与各应用节点。](https://tslink.md/service-map-dark.svg)

虚线表示管理，实线表示访问；每行服务都属于这台主机。[私有 portal 与请求](https://tslink.md/zh/docs/portal-requests.md)、[访客 gate](https://tslink.md/zh/docs/guest-links.md)、[scoped MCP](https://tslink.md/zh/docs/mcp-scopes.md)、[期限](https://tslink.md/zh/docs/durations.md)和[有界历史](https://tslink.md/zh/docs/access-history.md)已交付。多主机汇总、admin REST/dashboard、Docker 发现、middleware、自定义 ACME 与 metrics 仍在[规划中](https://tslink.md/zh/docs/experimental-roadmap.md)。

## 嵌入式 tsnet 节点

TSLink 的核心是每个服务对应一个嵌入式 [tsnet](https://pkg.go.dev/tailscale.com/tsnet) 节点。与需要 `tailscaled` 守护进程的传统 Tailscale 设置不同，tsnet 完全在 TSLink 进程内运行：

* **无需系统级 Tailscale** — TSLink 管理自己的 Tailscale 身份。
* **每个服务一个节点（微分段）** — 每个注册的服务都有自己的 tsnet 节点，拥有独立的主机名和 WireGuard 身份。代理/文件 HTTP 服务使用 HTTPS 证书；原始 TCP 不在 TSLink 中终止 TLS。这是网络层的分段，并非主机或进程隔离：TSLink 不会沙箱化本地进程，因此某个服务后端被攻破后，影响本身不会自动止步于该服务——完整边界见[标准对齐](https://tslink.md/zh/docs/standards-alignment.md)。
* **独立状态** — 每个节点的 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](https://www.wireguard.com/) 构建，这是一种现代 VPN 协议。tailnet 段的流量在 tailnet 对端之间保持加密，即使本地网络被攻破也是如此——这是零信任架构假设网络不可信的关键属性。从 TSLink 节点到本地服务的短跳转是独立的，可能是明文。当 TSLink 启动时：

1. 每个嵌入式 tsnet 节点与 tailnet 上的其他设备建立 WireGuard 隧道。
2. 访问设备与 TSLink 节点之间的流量由 WireGuard 加密；从节点到本地服务的跳转由你配置，可能是明文。
3. Tailscale 处理 NAT 穿透；宿主机仍须有策略允许的协调服务器及对端/中继网络连接。
4. 默认不暴露任何服务到公网——攻击面仅限于已认证的 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 代理重写契约](https://pkg.go.dev/net/http/httputil#ProxyRequest.SetURL)。
* 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](https://tslink.md/zh/docs/experimental-roadmap.md#%E4%B8%AD%E9%97%B4%E4%BB%B6)。完整管道详情请参阅实验性与路线图页面。

## 凭证系统

默认入网不需要存储凭据。人完成 `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 的声明式配置。热重载系统工作方式如下：

1. **fsnotify 监控器**监视注册表文件的变化。
2. 获取\*\*文件锁（mutex）\*\*以防止竞态条件。
3. 检测到变化时，加载新的注册表并与当前状态进行差异比较。
4. 新服务使用自己的 tsnet 节点和 HTTP/TCP 处理器启动。
5. 移除的服务关闭其节点并注销处理器。
6. 已更改的服务重建受影响的 runtime；是否重置注册状态取决于实际认证身份是否变化。
7. 未更改的服务保持运行，不受中断。

新增或重启服务仍须完成注册并就绪；字段及 runtime 重建与注册重置的区别见[热重载](https://tslink.md/zh/docs/configuration.md#%E7%83%AD%E9%87%8D%E8%BD%BD)。

关于 Docker 集成、集群支持、管理 API 和 Prometheus 指标的详细信息，请参阅[实验性与路线图](https://tslink.md/zh/docs/experimental-roadmap.md)。

## 结构化日志

`tslink logs` 查看 stderr 网关诊断（tslink.err.log）与 daemon stdout（tslink.out.log）。[访问历史](https://tslink.md/zh/docs/access-history.md)用独立有界本地 JSONL 记录 HTTP/文件、TCP 与 guest 事件，再合并独立 journal 中的 intent/completion 与生命周期 receipt。WhoIs 可用时证明身份；日志可能丢失或有崩溃缺口，只有 intent 不代表成功完成。

## Tailscale Funnel

开放 proxy 发布可以通过 `--funnel --public` 使用 [Tailscale Funnel](https://tailscale.com/kb/1223/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 变更](https://tslink.md/zh/docs/configuration.md#acl-%E5%8F%98%E6%9B%B4%E9%BB%98%E8%AE%A4%E5%85%B3%E9%97%AD--manage-acl)。

显式公共 Funnel（`--funnel --public`）有独立的默认启用配置路径，用于共享标签所有者和 `nodeAttrs` 授权，除非关闭 auto-provisioning。该路径仍需要可用 API 访问和策略权限。普通 `--manage-acl` 是另一条边界；本地清理不会删除共享 Funnel 授权。

### 热重载与标签

无存储凭据模式不广播 registry 中保存的标签，因此修改标签会在启动和热重载时保留用户所有节点的入网状态。存储凭据模式下，实际带标签身份发生变化时，受影响节点会停止、清理本地状态，再用更新的认证材料重启。远端陈旧设备清理需要精确所有权证明；无法证明的匹配候选会报告为 protected/skipped。

### 默认标签

存储凭据模式创建节点时，未显式指定 `--tags` 的服务使用默认标签（`tag:tsmain`，可通过 `tslink tags set-default` 覆盖）。无存储凭据的节点仍归用户所有且不带标签，即使 registry 条目存有标签；保存标签不会创建远端标签所有权。

### `tslink tags` 命令

本地标签注册及远端操作见[标签配置](https://tslink.md/zh/docs/configuration.md#%E6%A0%87%E7%AD%BE%E9%85%8D%E7%BD%AE)。保存本地标签不会应用远端策略；远端删除要求 `delete-remote --force --manage-acl` 及其授权/安全检查。

## 访问控制

TSLink 在多个层级实施访问控制，实现纵深防御：

* **代理/文件 HTTP ACL（`--allow`）** — 将服务限制为特定 Tailscale 身份（通过邮箱指定用户或使用 `tag:prod` 等标签）。已交付的 ACL 检查以尽力而为方式从 Tailscale `WhoIs` 解析调用方身份（按来源 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 请求立即终止并确认退出。维护前请按[确认停机](https://tslink.md/zh/docs/daemon.md#%E7%A1%AE%E8%AE%A4%E5%81%9C%E6%9C%BA)操作。
* **单实例**：TSLink 在启动前检查现有 PID，防止重复网关。
* **信号处理**：Unix `SIGTERM` 和 `SIGINT` 发起监听器/节点关闭；每个 HTTP 服务器有自己的排空预算，与 CLI 进程退出等待不同。
* **自动启动**：`tslink install` 将网关注册为系统登录服务（macOS 的 LaunchAgent、Linux 的 systemd 用户服务、Windows 的 Task Scheduler 与内建 supervisor）。

## 公网与私有访问

[浏览器访客链接](https://tslink.md/zh/docs/guest-links.md)采用明确公开的 Funnel 和强制 gate；链接/PIN 可转发，不是人的身份。同一应用私有连接仍独立执行人员/allow 检查。开放 Funnel 是另一种无访问者身份检查的发布。Portal 每台主机保持私有，请求需明确 requestable 私有 HTTP/文件、符合条件成员和主人审批。Scoped MCP 是管理权限边界，不是系统 sandbox。
