---
title: "配置"
description: "TSLink 配置文件、全局设置、当前字段和状态管理"
url: "https://tslink.md/zh/docs/configuration"
locale: "zh"
product_version: "0.1.1"
source: "https://github.com/anydoor7/tslink/blob/v0.1.1/docs/credentials-and-tags.md"
---

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

## 全局配置

TSLink 将全局设置存储在 `~/.config/tslink/config.json` 中。使用 `tslink config` 命令管理：

```bash
# 设置值
tslink config set control-url https://headscale.example.com

# 获取值
tslink config get control-url

# 列出所有设置
tslink config list

# 清除值（恢复默认）
tslink config set control-url ""
```

### 可用设置

| 键             | 描述                                   | 默认值          |
| ------------- | ------------------------------------ | ------------ |
| `control-url` | 自定义 Tailscale 控制服务器 URL（如 Headscale） | Tailscale 默认 |

TSLink 不会将 Tailscale API 派生的节点 auth key 发给非 Tailscale control server，会以 `credential_control_url_mismatch` 拒绝。选择 Headscale URL 不证明入网或 HTTPS 兼容，端到端 Headscale 验证仍待完成。

`control-url` 也可以在 `registry.json` 中按服务覆盖，或在明确选择手动运行时通过 `tslink serve --control-url <url>` 按会话覆盖。受管理守护进程应优先使用持久化配置，并遵循[配置变更后重启](https://tslink.md/zh/docs/daemon.md#%E9%85%8D%E7%BD%AE%E5%8F%98%E6%9B%B4%E5%90%8E%E9%87%8D%E5%90%AF)；再启动一个 `serve` 不是重启，后台进程重启也可能不保留一次性标志。

### 远程 MCP 控制面配置键

可选的 `mcp` 块用于开启由 `tslink serve --mcp` 提供的 tailnet-only 远程 MCP 控制面。它需要手工编辑：`tslink config set` 管理受支持的 control-url/access-log keys；MCP bindings 需直接编辑。

| 键                            | 描述                                                                                                 | 默认值          |
| ---------------------------- | -------------------------------------------------------------------------------------------------- | ------------ |
| `mcp.enabled`                | 不传 `--mcp` 也提供控制面；flag 与该键任一为真即开启                                                                  | `false`      |
| `mcp.allow`                  | 允许调用该 endpoint 的登录邮箱和/或 `tag:` 条目。旧 owner 条目；启用需这些条目或有效明确 bindings                                 | 无            |
| `mcp.allow_elevated_invites` | Owner 显式允许 MCP 发送非 `member` role 的用户邀请或允许 exit-node use 的设备邀请；否则以 `mcp_elevated_invite_refused` 拒绝 | `false`      |
| `mcp.node_name`              | 专用控制面 tsnet 节点的主机名                                                                                 | `tslink-mcp` |
| `mcp.events_keepalive`       | `/events` 具名 SSE keepalive 间隔，Go duration，范围 `5s` 到 `5m`                                           | `20s`        |

```json
{
  "mcp": {
    "enabled": true,
    "allow": ["you@example.com", "tag:ops"],
    "node_name": "tslink-mcp"
  }
}
```

`mcp.allow` 中的每个 principal 都会获得高权限服务控制；提权邀请还要求 owner 通过 `mcp.allow_elevated_invites` 显式允许。请把它当作高权限访问列表对待。边界、client 可达性以及 stdio 的 `tslink mcp` 替代方案见[远程 MCP 控制面](https://tslink.md/zh/docs/mcp-server.md#%E8%BF%9C%E7%A8%8B-mcp-%E6%8E%A7%E5%88%B6%E9%9D%A2)。

### 优先级顺序

设置按以下顺序解析（从高到低）：

1. **服务级** — `registry.json` 中服务条目的 `control_url` 字段
2. **CLI 标志** — `tslink serve --control-url`
3. **全局配置** — `tslink config set control-url`
4. **默认值** — Tailscale 托管控制服务器

## 标签配置

默认零凭据（Tier 1）流程注册的是用户拥有的无标签节点。有存储凭据时可以使用配置的节点标签；远端 ACL 策略管理有独立的显式选择与权限要求。

### 默认标签

有存储凭据时，添加服务未指定 `--tags` 就会选择默认标签。默认值未修改时，以下是两种替代注册方式：

```bash
# 有存储凭据、默认 tag:tsmain 时的替代写法，执行其中一个
tslink add webapp --proxy localhost:3000
tslink add webapp --proxy localhost:3000 --tags tag:tsmain
```

默认标签为 `tag:tsmain`。有存储凭据时可应用默认标签；即使 registry 存储了标签，零凭据节点也保持用户拥有、无标签。修改 Tier 1 标签会保留已有用户注册状态。普通 tag-owner ensure 仅在 `login` / `serve` 显式传入 `--manage-acl` 时开启。API token 或 OAuth client secret 的可用操作取决于相应 scopes；否则手工管理远端 tags。

### 更改默认标签

使用 `tslink tags set-default` 更改默认标签。该值存储在 `~/.config/tslink/config.json` 的 `GlobalConfig.DefaultTag` 中。

```bash
# 设置自定义默认标签
tslink tags set-default tag:myteam

# 验证
tslink tags list
```

### ACL 变更（默认关闭，`--manage-acl`）

普通 tag-owner ensure **默认关闭**；`tslink login --manage-acl` 仅确保配置的默认标签；`tslink serve --manage-acl` 确保有效 registry 服务实际使用的普通标签，只有服务使用默认标签时才包含该默认标签。空 registry 或仅使用自定义标签的服务不会隐含追加默认标签。凭据必须拥有所需 policy scopes。更新使用 ETag，不在冲突时盲目重试。

**显式公开 Funnel 是另一条流程。** 对已通过 `--funnel --public` 确认的 proxy，TSLink 默认可以自动配置共享 `tagOwners` / `nodeAttrs` Funnel grant；这不要求额外传 `--manage-acl`，但仍须合适的凭据和策略权限。服务的 `--no-auto-provision` / `no_auto_provision` 关闭该服务自动配置；`tslink serve --no-auto-provision` 关闭该进程全部服务的自动配置。要让安装的后台服务保留该选择，使用 `tslink install --no-auto-provision`。

`tslink cleanup --manage-acl` 只报告本机是否仍使用共享 Funnel grant，**永不删除**共享 grant，也不据本机 registry 推断其它机器的使用情况。

### 使用 `tslink tags` 管理标签

`tslink tags` 命令组提供完整的标签生命周期管理：

```bash
# 列出服务及其配置的标签（Tier 1 不会广播这些标签）
tslink tags list

# 从 Tailscale API 打印标签
tslink tags pull

# 为特定服务添加标签
tslink tags add webapp tag:staging

# 用一个标签替换特定服务的标签
tslink tags set webapp tag:web

# 更改默认标签
tslink tags set-default tag:myteam

# 通过本地安全检查后全局移除 ACL 标签所有者规则
tslink tags delete-remote tag:deprecated --force --manage-acl
```

## 注册表文件

TSLink 将所有服务定义存储在：

```
~/.config/tslink/registry.json
```

这个文件是已注册服务的唯一真实来源。使用 `tslink add` 和 `tslink remove` 管理，或者手动编辑以进行高级配置。

### 完整注册表格式

只有服务的普通写入保留 schema version 1。启用人员授权的 registry 使用 version 2，包含顶层 `people` 和逐服务 `people_scoped`。旧 binary 会拒绝不支持的字段，不能静默丢弃授权；只有明确决定放弃人员授权时，才停止新 daemon 并恢复单独备份的 version 1 registry。以下仅含服务的示例仍有效。

```json
{
  "schema_version": 1,
  "services": [
    {
      "name": "webapp",
      "type": "proxy",
      "target": "http://localhost:3000",
      "path": "",
      "port": 0,
      "ephemeral": false,
      "tags": ["tag:web"],
      "allowed_users": ["user@example.com"],
      "funnel": false,
      "public_ack": false,
      "control_url": "",
      "created_at": "2026-03-01T00:00:00Z"
    },
    {
      "name": "shared-files",
      "type": "file",
      "path": "/Users/you/Documents/shared",
      "created_at": "2026-03-01T00:01:00Z"
    },
    {
      "name": "mydb",
      "type": "tcp",
      "target": "localhost:5432",
      "port": 5432,
      "created_at": "2026-03-01T00:02:00Z"
    }
  ]
}
```

### 服务字段

| 字段                  | 类型        | 描述                                                                                |
| ------------------- | --------- | --------------------------------------------------------------------------------- |
| `name`              | string    | 服务主机名（小写字母、连字符、字母数字）                                                              |
| `type`              | string    | `proxy`、`file` 或 `tcp`                                                            |
| `target`            | string    | 代理/TCP 目标（如 `http://localhost:3000`）                                              |
| `path`              | string    | 文件服务的绝对路径                                                                         |
| `port`              | int       | TCP 端口号                                                                           |
| `ephemeral`         | bool      | 请求临时节点；控制面在不活跃后清理，而非断开即移除；本地注册保留至主动移除                                             |
| `tags`              | string\[] | 有存储凭据时配置的 Tailscale 节点标签；Tier 1 保持无标签                                             |
| `allowed_users`     | string\[] | 代理/文件 HTTP 允许身份（邮箱或 `tag:xxx`）；TCP 拒绝非空值                                          |
| `funnel`            | bool      | 启用 Tailscale Funnel（仅代理，公共暴露）                                                     |
| `public_ack`        | bool      | `funnel` 为 true 时必需的公共暴露确认                                                        |
| `funnel_expires_at` | string    | Funnel 必填：RFC3339 截止时间；保留旧持久 never，但拒绝新的公开 never；缺失时以 `funnel_expiry_required` 拒绝 |
| `no_auto_provision` | bool      | 禁用该服务的 Funnel policy 自动配置                                                         |
| `file`              | string    | 文件服务 `path` 内可选的单个文件名                                                             |
| `control_url`       | string    | 服务级控制服务器覆盖                                                                        |
| `created_at`        | string    | ISO 8601 创建时间戳                                                                    |
| `people_scoped`     | bool      | 私有 HTTP/文件应用需要人员授权或明确的旧 allow 规则；移除授权后标记保留                                        |
| `health`            | object    | 后端路径/状态/正文断言、超时和间隔，见[健康检查](https://tslink.md/zh/docs/health-and-alerts.md)        |
| `request_limits`    | object    | 上传大小、header/body-read/idle 窗口，不是请求限流                                              |
| `preserve_host`     | bool      | Proxy 转发 canonical 外部 Host，Origin 保持不变                                            |

## Roadmap / Experimental 配置

`middleware`、`domain`、`acme_email` registry 键已移除，旧键即使为空也会以 `unknown_config_key` 拒绝。严格的修改命令拒绝这些条目；daemon 跳过无效服务条目并继续运行健康服务。热重载使公开 Funnel 条目无效时会关闭其公开 listener。Docker discovery 和 Prometheus instrumentation 尚未实现。详情请参阅[实验性与路线图](https://tslink.md/zh/docs/experimental-roadmap.md#roadmap-%E9%85%8D%E7%BD%AE%E5%AD%97%E6%AE%B5)。

## 凭证存储

默认 Tier 1 通过浏览器登录，无需存储 API 凭据。有存储凭据的运行模式支持以下两种类型：

| 凭证              | 前缀               | 行为                                                     |
| --------------- | ---------------- | ------------------------------------------------------ |
| **API 访问令牌**    | `tskey-api-*`    | 会定期过期。当前最完整支持 Tailscale REST 标签/设备操作和认证材料派生。           |
| **OAuth 客户端密钥** | `tskey-client-*` | 不会周期性过期。由 tsnet 直接用于认证，但当前 REST 标签/设备自动化路径更窄。无人值守前请验证。 |

### 存储优先级

凭证按以下顺序存储和解析：

1. **系统钥匙串**（首选） — macOS Keychain、Linux secret service、Windows Credential Manager。钥匙串服务名为 `"tslink"`。
2. **有条件的文件回退** — `~/.config/tslink/apikey` 或 `~/.config/tslink/clientsecret`，权限 `0600`。仅 macOS/Linux 在证明旧钥匙串值不存在或已删除后允许；不可达或不确定时拒绝，Windows 禁用。

### 旧版支持

为了向后兼容，TSLink 还会检查 `~/.config/tslink/authkey`。此文件仅在不存在 API 密钥或 OAuth 客户端密钥时使用。不推荐用于新安装。

### 凭证迁移

如果存在文件存储的 API 访问令牌且系统钥匙串可用，`tslink serve` 会将该 API token 迁移到钥匙串，并在钥匙串写入成功后删除文件。OAuth client-secret 回退文件不会由 `serve` 自动迁移；如需走正常的钥匙串优先路径，请把 secret 通过 stdin 传给 `tslink login --client-secret-stdin`。

### 认证密钥派生

使用 API 访问令牌（`tskey-api-*`）时，TSLink 会在每个服务启动时派生新的认证材料。该请求按该服务配置的标签、临时节点设置和服务级描述限定范围。认证密钥不会持久化到磁盘。

## 热重载

TSLink 使用 [fsnotify](https://github.com/fsnotify/fsnotify) 监控注册表文件的变化。当文件被修改时：

1. 监控器检测到文件系统事件。
2. 获取文件锁（mutex）以防止并发写入导致的竞态条件。
3. 加载并验证新的注册表。
4. 将新状态与运行状态进行差异比较。
5. 新增的服务被启动；移除的服务被停止。
6. 现有未更改的服务继续运行，不受影响。

### 需要节点重启

以下字段的更改会通过热重载重新创建受影响的服务 runtime，短暂中断该服务：

* 服务 `type`（proxy、file、tcp）
* 服务 `target`、`path` 或选定的 `file`
* 服务 `port`
* 服务 `tags`、`allowed_users`、`ephemeral`、`funnel`、`public_ack` 或 `no_auto_provision`
* 实际生效的 `control_url`

Runtime 重建与注册状态重置不同。修改标签保留 Tier 1 注册；有存储凭据时实际节点标签变化、临时节点设置变化或实际控制服务器变化可能重置节点身份。daemon 跳过无效服务条目，其他健康条目继续运行；热重载使公开 Funnel 条目无效时会关闭其 listener，不会保留公开暴露。

## 状态目录

TSLink 将运行时状态存储在 `~/.config/tslink/`：

| 路径              | 用途                                |
| --------------- | --------------------------------- |
| `config.json`   | 全局设置（控制服务器 URL 等）                 |
| `registry.json` | 服务定义                              |
| `tslink.pid`    | 守护进程 ID 跟踪                        |
| `apikey`        | API 密钥文件（macOS/Linux 有条件回退）       |
| `clientsecret`  | OAuth 客户端密钥文件（macOS/Linux 有条件回退）  |
| `authkey`       | 旧版认证密钥（向后兼容）                      |
| `nodes/`        | 每个服务的 tsnet 状态（WireGuard 密钥、节点状态） |
| `logs/`         | 守护进程和访问日志                         |

状态目录在首次使用时自动创建。`nodes/` 目录由嵌入的 tsnet 库管理。Roadmap/experimental 自定义域名 ACME 工作不属于已交付的状态目录契约。

## 重置状态

重置状态前先移除受管理的自动启动。macOS 上仅停止 LaunchAgent 会被 `KeepAlive` 重新启动；`uninstall` 失败时必须终止重置，包括无法检查 launchd 域的情况，不要用 `--force` 绕过该证明。停止任何剩余的手动网关，确认进程已停止且没有自动启动/重启注册后，才清除凭据和状态。

以下 Bash 示例重置默认 macOS/Linux 目录，用 Python 3 验证状态 JSON。它在子 shell 中运行，任何命令失败、状态字段缺失/格式错误、网关仍运行或监督仍有效时都会终止。自定义配置目录须使用匹配的服务注册与状态路径；此示例拒绝不同的 `TSLINK_CONFIG_DIR`。重置期间不要启动另一网关。

```bash
(
  set -euo pipefail
  [ -z "${TSLINK_CONFIG_DIR:-}" ] || [ "$TSLINK_CONFIG_DIR" = "$HOME/.config/tslink" ]

  # 1. 移除受管理的启动注册，再停止任何剩余的手动网关
  tslink uninstall
  tslink stop

  # 2. 必须确认网关停止、监督无效后才能继续
  tslink status --json | python3 -c '
import json, sys
result = json.load(sys.stdin)
data = result.get("data", {})
supervision = data.get("supervision", {})
if not (result.get("ok") is True and data.get("daemon_running") is False
        and all(supervision.get(key) is False
                for key in ("installed", "autostart", "restart_on_exit"))):
    sys.exit("Reset halted: gateway or supervision is active or unverified")
'

  # 3. 清除凭据，再移除本地注册表、节点状态和日志
  tslink logout
  rm -rf -- "$HOME/.config/tslink"
)
```

Windows 上也必须先完成 `uninstall` → `stop` → `status --json` 的前提，才能登出或删除本地状态；`stop` 使用进程终止而非正常关闭。以上 Bash/Python 示例面向 macOS/Linux。

成功重置后，重新分享并批准节点；存储凭据的 `tslink login` 仍是可选项。**验证并清理远程设备。** 本地重置不会自动删除 tailnet 中的远程节点。打开 Tailscale 管理控制台，检查并手动删除任何遗留的 TSLink 设备。

`tslink logout` 会清除凭据但保留目录；最后的删除会额外移除注册表和节点状态。仅执行 `rm -rf` 不能清除钥匙串凭据，也不能证明远程已清理。

## 访问管理设置

`mcp.bindings` 增加 viewer/app-operator/people-manager/owner principal，明确 apps 或 viewer inventory，operator/manager 正的 max\_duration，以及可选的固定 binding 期限。旧 mcp.allow 保留 owner 权限，重复 principal fail closed。修改 MCP 配置后重启，见 [MCP scopes](https://tslink.md/zh/docs/mcp-scopes.md)。

用 `tslink portal enable --owner you@example.com` 设置独立私有 portal；可选 admins 能打开所有私有 HTTP/文件应用，请用 status 的实际 URL。服务 requestable 默认 false，明确披露私有应用名供请求；见 [portal 与请求](https://tslink.md/zh/docs/portal-requests.md)。人员/访客/请求/portal 共用原子 registry，需兼容 TSLink reader，降级前保留备份。

`durations.public_max` 设置 guest/公网上限（默认 7d，至少 1h 的相对值）。新人员/Funnel 默认 24h，改策略不重写已有截止时间，见[期限例外](https://tslink.md/zh/docs/durations.md)。

access\_log 管理 enabled/path\_mode/retention\_days/max\_bytes/queue\_size；服务 access\_log\_path\_mode 覆盖继承路径模式，全局 off 是硬退出。支持 `tslink config set access-log-path-mode prefix` 等 access-log keys，默认与缺口见[访问历史](https://tslink.md/zh/docs/access-history.md)。全局变更需重启，应用路径设置热重载。
