管理服务
如何添加、移除和管理代理、文件和 TCP 服务
概述
TSLink 中的"服务"是一个命名条目,将 Tailscale 网络上的主机名映射到本地目标。TSLink 支持三种服务类型:
| 类型 | 标志 | 用途 |
|---|---|---|
| 代理 | --proxy | 反向代理到 Web 服务器 |
| 文件 | --dir | 通过 HTTPS 提供目录服务 |
| TCP | --tcp | 原始 TCP 转发(数据库、自定义协议) |
每个服务使用独立 tsnet 节点,拥有自己的 tailnet 身份和主机名,可由 tailnet 策略按服务控制访问。代理和文件服务使用带 TLS 证书的 HTTPS,例如 https://<名称>.<tailnet>.ts.net;原始 TCP 使用主机名和端口,例如 <名称>.<tailnet>.ts.net:5432,没有 TSLink HTTP/TLS 监听器。请读取实际运行时端点,不要自行拼接主机名。
代理服务
使用 --proxy 通过反向代理暴露本地 Web 服务:
tslink add <名称> --proxy <host:port>示例:
# 暴露 Next.js 开发服务器
tslink add frontend --proxy localhost:3000
# 暴露运行在自定义端口的 API
tslink add api --proxy 127.0.0.1:8080
# 简写形式 — 省略 host 表示 localhost
tslink add myapp --proxy :3000代理身份传递是 best-effort:TSLink 先移除客户端提交的 X-Tailscale-* 头,只有 Tailscale WhoIs 解析出用户资料时才添加身份头。未配置 --allow 时,查询失败仍可能转发请求且不带身份头;配置 --allow 后,缺少身份或未获授权的请求会在到达后端前被拒绝。
只有在受控的 TSLink 到后端路径中、防止直接绕过代理时,才能信任身份头。应用若要求用户身份,就应拒绝缺失值,并保留所需认证与授权。文件服务不注入代理身份头。头字段、后端 Host 重写及转发头重建见反向代理。
后端错误: 非超时的上游失败返回 502 Bad Gateway,超时返回 504 Gateway Timeout。错误正文中的 “Service unavailable” 不表示 HTTP 状态为 503。
文件服务
使用 --dir 通过 HTTPS 共享目录:
tslink add docs --dir ~/Documents/shared目录以只读方式通过内置文件浏览器提供服务,并启用目录列表。支持绝对路径和相对路径;相对路径在注册时解析。
目录在该服务主机名的根路径提供;用 tslink url docs --wait 获取实际地址。已配置的 --allow 和 HTTP 访问日志可以使用 WhoIs,但文件处理器不注入代理身份头。
TCP 服务
使用 --tcp 暴露原始 TCP 连接(数据库、Redis、自定义协议):
# 暴露 PostgreSQL 数据库
tslink add mydb --tcp localhost:5432
# 暴露 Redis
tslink add cache --tcp localhost:6379TCP 服务执行双向字节转发,并正确处理 TCP 半关闭 — 任何基于 TCP 的协议都可以使用。CLI 会从 --tcp host:port 记录目标端口,tailnet listener 使用该端口。如果手工编辑 registry 且省略 port,runtime 会回退到 443。
TCP 服务不经过 HTTP middleware、身份头或 HTTP --allow。CLI 注册及 registry 验证均拒绝 TCP 非空 allowed_users / --allow。用 tailnet 策略和目标服务自身认证保护 TCP。
Tailnet 流量由 WireGuard 加密,但 TSLink 不为原始 TCP 添加应用 TLS。认证及需要的应用/后端 TLS,应在目标服务和客户端中配置。
常见用途:
- 数据库: PostgreSQL、MySQL、MongoDB
- 缓存: Redis、Memcached
- SSH 隧道: 通过 tailnet 转发 SSH 连接
注册并就绪后,从 tailnet 策略允许的设备访问 tslink url <name> --wait 返回的实际主机名和端口。以下客户端示例使用主机名占位值:
# PostgreSQL
psql -h <mydb-实际返回的主机名> -p 5432
# SSH
ssh -p 22 <已注册-SSH-服务返回的主机名>高级标志
这些标志可以与任何服务类型组合使用(除非另有说明):
临时节点
为临时服务请求临时节点:
tslink add devserver --proxy :8080 --ephemeralTailscale 控制面在不活跃后清理节点;短暂断连不证明设备已移除。本地服务注册保留至显式移除,见临时节点行为。
ACL 标签
有存储凭据时配置服务的节点标签,前提是已有标签所有权,或显式授权普通 ACL 配置:
tslink add api --proxy :8080 --tags tag:api,tag:prod即使命令将标签存进 registry,默认无存储凭据(Tier 1)节点仍归用户所有且无标签。保存标签不会改变远端策略。API-token 模式按服务实际标签派生认证材料;client-secret 操作更窄。普通 tag-owner ensure 要求在 login / serve 上显式传 --manage-acl,并拥有足够策略权限。默认标签、Tier 1 保留注册状态及独立的已确认 Funnel 配置流程见标签配置。
访问控制
将代理/文件 HTTP 服务限制为 Tailscale 登录邮箱或设备标签:
tslink add internal --proxy :9090 --allow user@example.com,tag:admin配置 --allow 后,缺少或不匹配的 WhoIs 身份会在到达代理后端或文件内容前收到 403 Forbidden。解析按源 IP 缓存 60 秒,不是每次请求都重新查询控制面。文件服务通过 WhoIs 授权/记录日志,不注入身份头;保留应用自身所需授权。
这是 HTTP 请求过滤,不适用于原始 TCP 服务。
人员授权为私有 HTTP/文件添加逐请求 WhoIs 检查与期限。已管理账户的人员授权优先于旧 allow,撤销保留拒绝记录。仅旧 allow 检查及身份头使用上面的缓存行为;人员授权不缓存。见分享给指定的人。
Tailscale Funnel
通过 Tailscale Funnel 将代理服务公开暴露到互联网:
tslink add public-site --proxy localhost:3000 --funnel --public此处的开放 Funnel 发布仅适用于代理服务。它必须显式传 --public 确认,会让服务 URL 公开可访问,客户端无需安装 Tailscale,且不受 TSLink 身份执行保护。允许节点公开服务不等于授权互联网访客;所需访客认证与授权应由后端应用负责。
自定义域名(Roadmap / experimental)
自定义域名 TLS 和 ACME 尚未实现。--domain / --acme-email 标志及 domain / acme_email registry 字段已移除;旧 registry 键即使为空也会以 unknown_config_key 拒绝。已交付流程请使用默认 <service>.<tailnet>.ts.net 主机名。
服务级控制服务器
覆盖全局控制服务器(适用于混合使用 Tailscale 和 Headscale 节点的场景):
tslink add headscale-app --proxy localhost:3000 --control-url https://headscale.example.com或直接在 registry.json 中设置:
{
"name": "headscale-app",
"type": "proxy",
"target": "http://localhost:3000",
"control_url": "https://headscale.example.com"
}配置优先级: 服务级标志/配置 > serve 的 CLI 标志 > 全局配置(tslink config)> Tailscale 默认值。
中间件(Roadmap / experimental)
可配置 rate limiting、Basic Auth、IP allow list 和 CORS 尚不可用,已移除的 middleware 键即使为空也会在 registry 加载时以 unknown_config_key 拒绝,见实验性与路线图。它不是运行中分享的保护层。
Docker 自动发现(Roadmap / experimental)
Docker discovery 尚未实现,当前 TSLink 没有 discovery 包或可用标签。该功能接入并完成集成测试前,请用 tslink add 显式注册服务。
命名约定
服务名称是 URL 的一部分:
https://<服务名>.<tailnet名>.ts.net规则:
- 仅限小写字母、数字和连字符
- 必须以字母或数字开头和结尾
- 在 TSLink 实例中必须唯一
好的名称示例: webapp、api-v2、staging-server、docs
列出服务
查看所有已注册的服务:
tslink list显示每个条目的服务名称、类型、目标和已配置的标志。
移除服务
按名称移除服务:
tslink remove frontend本地移除会删除 registry 条目;运行中的网关通过热重载检测到变化后停止服务。远端 tailnet 清理只有在 TSLink 能证明匹配设备的精确所有权时才会尝试;否则匹配候选会报告为 protected,并跳过清理。本地成功不证明远端设备已删除。
热重载
TSLink 使用 fsnotify 监控 registry.json 的变化。当你在网关运行时添加或移除服务:
- 注册表文件自动更新。
- 文件监控器检测到变化。
- 新服务完成入网、审批并就绪后开始接收流量;移除的服务停止。
这意味着你可以在不中断其他活动服务的情况下管理服务。
通过热重载生效:
- 添加新服务(
tslink add) - 移除服务(
tslink remove) - runtime 能检测到的结构性变化,例如 target/path/port、标签、临时节点模式或服务级控制服务器 URL 更新,会重启受影响节点
Runtime 重启不同于注册状态重置:标签修改会保留 Tier 1 用户注册。字段及身份契约见热重载。
需要重启网关:
- 修改全局配置(如
control-url) - 更新认证凭证或切换凭证模式(
tslink login) - 修改旧版
authkey文件
这些变更请使用按平台恢复运行的重启流程。受管理安装需先确认停机,再通过对应管理器恢复运行;手动 stop / serve 仅适用于未登记自启动的环境。
启动时,TSLink 还会尝试对上次运行遗留的过期 tailnet 节点做所有权安全的清理。缺少精确所有权证明的候选会报告为 protected/skipped。
多服务
注册任意数量的服务。每个服务获得自己的主机名:
tslink add app --proxy localhost:3000
tslink add api --proxy localhost:8080
tslink add docs --dir ~/docs
tslink add mydb --tcp localhost:5432默认 add 已会确保后台网关运行。检查每次结果,如返回 needs_login,打开它的 auth_url 并完成 tailnet 要求的注册/审批。随后对每个服务运行 tslink url <name> --wait,并检查 tslink status --json。同一网关会同时运行所有已就绪服务;默认 add 后不要再执行 serve。
明确选择手动运行时,注册使用 --no-daemon-install,且只有在没有网关或受管理安装时才启动 serve。该流程及确认停机的前提见守护进程模式。
访客与私有管理流程
上面的 Funnel 注册是开放发布。访客链接则从私有 HTTP proxy 应用开始,明确启用有强制 gate 和可选 PIN 的公网 Funnel。链接分别到期,私有人员/allow 检查保持独立。新公开期限至少 1h,默认 24h,默认上限 7d,拒绝 never。期限说明主人上限与明确 regrant。
每台主机的私有 portal 与请求帮助符合条件的 tailnet 成员发现或申请私有 HTTP/文件应用。访问历史检查保留的访问,不保证完整记录。