三端配置文件示例
三端配置文件示例
TunnelMesh 三端共用同一配置模型,但每个进程只使用与自身职责相关的配置段。配置优先级为:命令行参数 > 环境变量 > 配置文件 > 默认值。YAML 不展开 ${VAR},密码、Token 和密钥应通过环境变量或 Secret Manager 注入。
Server 只监听 server.http_addr 一个地址,管理 API、Web 后台、/ws/agent、/ws/client、/ws/webssh/<session-id>、/ws/tcp 和动态 HTTP 路由共用它;是否在同一个监听器上启用 TLS 由 tls.enabled 决定。server.https_addr、server.agent_ws_addr、server.client_ws_addr 仍会被加载但不会建立额外监听器,属于历史遗留键,示例中不再出现。
Server:单机 SQLite
适合单节点部署。若 systemd 使用 tunnelmesh 用户运行,需保证 /var/lib/tunnelmesh 对该用户可写。
mode: local
storage:
driver: sqlite
auto_init: true
sqlite:
path: /var/lib/tunnelmesh/tunnelmesh.db
registry:
type: database
server:
# 非特权端口,建议由 Nginx/Caddy 对外终止 HTTPS。
http_addr: 127.0.0.1:8080
# 动态托管域名后缀;生产环境替换为实际 wildcard 域名。
dynamic_suffix: apps.example.com
tcp_bridge:
enabled: true
path: /ws/tcp
max_bytes: 65536
webssh:
enabled: true
ticket_ttl: 30s
session_ttl: 8h
max_active_sessions_per_user: 5
open_timeout: 10s
idle_timeout: 5m
max_message_bytes: 65536
stream:
max_concurrent_opens: 256
# 存量水位:同一 Agent 此刻保有的活跃流上限,0 = 不限。
max_active_per_agent: 1024
max_pending_opens: 1024
initial_window: 262144
window_update_threshold: 131072
max_frame_payload: 32768
authorization_cache:
enabled: true
local_positive_ttl: 5s
negative_ttl: 3s
revision_poll_interval: 2s
max_stale_on_poll_error: 5s
max_entries: 100000
# 管理监听器的请求边界:一个端口同时承载后台页面、/api/v1 与已升级的
# WebSocket,所以这里只约束请求头、请求体与 keep-alive 空闲,绝不设连接级
# read/write 超时。取值依据与和 Nginx 的关系见
# configuration.md「管理入口的超时、上限与保留策略」。
http:
read_header_timeout: 10s
idle_timeout: 120s
max_header_bytes: 1048576
body_timeout: 30s
# 0 = 不限制。Nginx 在前时容量由 limit_conn 决定;Server 直接暴露公网时
# 必须设置与进程容量匹配的正数,超限新连接立即关闭而不排队。
max_connections: 0
agents:
# 单个 Agent 身份在本节点可保有的物理 WebSocket 数量。
max_connections_per_agent: 64
metrics:
# 空 = /metrics 匿名可抓取(与历史行为一致)。二选一:反向代理按来源网段
# 放行,或用 TUNNELMESH_SERVER_METRICS_TOKEN 注入不少于 16 字符的 Bearer。
# 该字段不会出现在 config dump 输出里。
token: ""
audit:
# 0 = 永久保留。审计日志是证据,删除必须是显式决策;正数才会启动清理器,
# 每小时按 1000 行一批删除,避免首次清理历史大表时长时间锁表。
retention_days: 0
# 内嵌 VPN 网关(WireGuard)。数据面在 -tags vpn 构建里:带 tag 且 enabled: true
# 时进程真的监听 listen 指定的公网 UDP 端口并承载隧道;不带 tag 时只有管理面
# (签发、列出、修改、轮换、吊销、审计),config:reveal 返回 409 vpn_node_disabled。
# 不带 tag 却配 enabled: true 会启动失败并提示 rebuild with -tags vpn,不会静默不工作。
# 全部键的取值范围见 configuration.md 的「内嵌 VPN 网关」一节,
# 放行、密钥注入与 IP 池规划见 deployment/vpn-gateway.md。
vpn:
enabled: false
# 独立的公网 UDP 端口,不经反向代理,需在防火墙单独放行并单独限流。
listen: 0.0.0.0:51820
# 下发给用户的 Endpoint 主机名,只写主机名不写端口,端口取自 listen。
endpoint_host: gw-1.mesh.example.com
# 集群里每个节点必须配置相同的 ip_pool 与 node_subnet_size,
# 节点各自从池中租约一个 /24 子网(vpn_ip_leases,epoch fencing)。
ip_pool: 10.64.0.0/16
node_subnet_size: 24
# 1500 - WireGuard 的 72 字节开销。
mtu: 1420
# 下面三个 0 表示不限。
max_peers: 0
max_flows_per_peer: 128
max_flows_total: 0
packet_rate_per_peer: 0
connect_timeout: 10s
idle_timeout: 120s
shutdown_timeout: 15s
# 节点级上限而非承诺:peer 还需单独开启,且出口 Agent 要协商到该能力,
# 否则签发返回 409 vpn_agent_capability_missing。
icmp_enabled: true
icmp_timeout: 5s
icmp_max_concurrent: 64
# 节点自身的 WireGuard 私钥不是配置项,只由 TUNNELMESH_VPN_NODE_PRIVATE_KEY 注入。
security:
allowed_hosts:
- tunnel.example.com
allowed_origins:
- https://tunnel.example.com
# Deprecated: 仅在存量 Agent 尚未换发 service token 的迁移窗口临时开启。
allow_legacy_connection_tokens: false
# 后台“发行管理”页展示的仓库,格式 owner/name,无命令行参数。
downloads:
github_repository: nnworld/TunnelMesh
tls:
enabled: false
min_version: "1.2"
Server:集群 MySQL 明文 Relay
node.id 可省略。run 首次启动时会生成稳定 ID,复用 /var/lib/tunnelmesh/node-id,并把最终值回写到 YAML 的 node.id。需要提前初始化时,以有权限修改配置文件的用户执行:
tunnelmesh-server --config /etc/tunnelmesh/server.yaml init-node-id
systemd 单元会在服务用户运行前以 root 执行一次初始化,因此配置文件可以保持只读。
受控内网可以运行明文 relay:endpoint 留空时自动使用本机可用 IP 和 listen 端口;ca/cert/key/server_name 全部省略。明文模式仍要求 server-node token,并继续校验节点状态和 epoch,但不提供传输加密或证书级节点身份,只适合受控内网。
# /etc/tunnelmesh/server.yaml
mode: cluster
storage:
driver: mysql
auto_init: true
mysql:
dsn: ""
tls: false
registry:
type: database
node:
id: ""
server:
http_addr: 127.0.0.1:8080
dynamic_suffix: apps.example.com
relay:
enabled: true
listen: 0.0.0.0:9443
# 可省略;自动推导结果不回写 YAML。多网卡环境建议显式配置。
endpoint: ""
# 由 TUNNELMESH_SERVER_RELAY_NODE_TOKEN 注入。
node_token: ""
# 管理监听器的请求边界:一个端口同时承载后台页面、/api/v1 与已升级的
# WebSocket,所以这里只约束请求头、请求体与 keep-alive 空闲,绝不设连接级
# read/write 超时。取值依据与和 Nginx 的关系见
# configuration.md「管理入口的超时、上限与保留策略」。
http:
read_header_timeout: 10s
idle_timeout: 120s
max_header_bytes: 1048576
body_timeout: 30s
# 0 = 不限制。Nginx 在前时容量由 limit_conn 决定;Server 直接暴露公网时
# 必须设置与进程容量匹配的正数,超限新连接立即关闭而不排队。
max_connections: 0
agents:
# 单个 Agent 身份在本节点可保有的物理 WebSocket 数量。
max_connections_per_agent: 64
metrics:
# 空 = /metrics 匿名可抓取(与历史行为一致)。二选一:反向代理按来源网段
# 放行,或用 TUNNELMESH_SERVER_METRICS_TOKEN 注入不少于 16 字符的 Bearer。
# 该字段不会出现在 config dump 输出里。
token: ""
audit:
# 0 = 永久保留。审计日志是证据,删除必须是显式决策;正数才会启动清理器,
# 每小时按 1000 行一批删除,避免首次清理历史大表时长时间锁表。
retention_days: 0
# 内嵌 VPN 网关(WireGuard)。数据面在 -tags vpn 构建里:带 tag 且 enabled: true
# 时进程真的监听 listen 指定的公网 UDP 端口并承载隧道;不带 tag 时只有管理面
# (签发、列出、修改、轮换、吊销、审计),config:reveal 返回 409 vpn_node_disabled。
# 不带 tag 却配 enabled: true 会启动失败并提示 rebuild with -tags vpn,不会静默不工作。
# 全部键的取值范围见 configuration.md 的「内嵌 VPN 网关」一节,
# 放行、密钥注入与 IP 池规划见 deployment/vpn-gateway.md。
vpn:
enabled: false
# 独立的公网 UDP 端口,不经反向代理,需在防火墙单独放行并单独限流。
listen: 0.0.0.0:51820
# 下发给用户的 Endpoint 主机名,只写主机名不写端口,端口取自 listen。
endpoint_host: gw-1.mesh.example.com
# 集群里每个节点必须配置相同的 ip_pool 与 node_subnet_size,
# 节点各自从池中租约一个 /24 子网(vpn_ip_leases,epoch fencing)。
ip_pool: 10.64.0.0/16
node_subnet_size: 24
# 1500 - WireGuard 的 72 字节开销。
mtu: 1420
# 下面三个 0 表示不限。
max_peers: 0
max_flows_per_peer: 128
max_flows_total: 0
packet_rate_per_peer: 0
connect_timeout: 10s
idle_timeout: 120s
shutdown_timeout: 15s
# 节点级上限而非承诺:peer 还需单独开启,且出口 Agent 要协商到该能力,
# 否则签发返回 409 vpn_agent_capability_missing。
icmp_enabled: true
icmp_timeout: 5s
icmp_max_concurrent: 64
# 节点自身的 WireGuard 私钥不是配置项,只由 TUNNELMESH_VPN_NODE_PRIVATE_KEY 注入。
Server:集群 MySQL mTLS Relay
以下模板面向“Nginx 终止公网 HTTPS、Server 监听本机 HTTP、relay 使用独立 mTLS”的生产部署。MySQL DSN 和 server-node token 不写入 YAML,由环境变量注入。证书生成、安装、校验和轮换步骤见 Relay mTLS 证书生成与配置。
# /etc/tunnelmesh/server.yaml
mode: cluster
storage:
driver: mysql
# 首次启动自动初始化空库,或按 schema_meta.version 执行增量迁移。
auto_init: true
mysql:
# 由 TUNNELMESH_STORAGE_MYSQL_DSN 注入;不要把密码提交到配置文件。
dsn: ""
# MySQL 5.6 不支持 TLS 时保持 false,并确保数据库端口仅在内网可达。
tls: false
registry:
# 集群默认使用 MySQL lease;不需要额外配置 endpoints。
type: database
# 可省略。systemd 会在非特权启动前执行 init-node-id;
# 生成后会回写为 node.id: server-<32位小写十六进制>。
# relay 证书 SAN 必须包含最终 node.id。
node:
id: ""
server:
# 仅监听本机,由 Nginx/Caddy 转发;Agent/Client WebSocket 也走该监听器。
http_addr: 127.0.0.1:8080
# 只有来自 Nginx 的 X-Forwarded-For 才可信,用于登录限流桶和审计 IP。
# Server 直接对公网暴露时必须留空,否则客户端可伪造该头自选限流桶。
trusted_proxies:
- 127.0.0.1/32
- ::1/128
# 必须与 DNS、证书和 Nginx server_name 的动态域名后缀一致,不要写 *。
dynamic_suffix: apps.example.com
tcp_bridge:
enabled: true
path: /ws/tcp
max_bytes: 65536
webssh:
enabled: true
ticket_ttl: 30s
session_ttl: 8h
max_active_sessions_per_user: 5
open_timeout: 10s
idle_timeout: 5m
max_message_bytes: 65536
# Server 节点间 relay。每个节点使用独立证书和私钥;
# 所有节点使用同一 relay CA,并复用同一个 fleet token。
relay:
enabled: true
# 本节点 relay gRPC 监听地址。
listen: 0.0.0.0:9443
# 其他 Server 节点可访问的本节点地址;不要填 127.0.0.1。
endpoint: server-1.internal.example.com:9443
ca: /etc/tunnelmesh/certs/relay-ca.pem
cert: /etc/tunnelmesh/certs/server-1-relay.pem
key: /etc/tunnelmesh/certs/server-1-relay-key.pem
# 出站 relay 连接校验对端证书使用的 TLS ServerName。
# 对端证书需要同时包含该名称和该节点最终 node.id SAN。
server_name: relay.internal.example.com
# 由 TUNNELMESH_SERVER_RELAY_NODE_TOKEN 注入;留空会导致 check-config 失败。
node_token: ""
# MySQL 集群可用较长正向缓存;共享 revision 保证权限变更快速失效。
stream:
max_concurrent_opens: 256
# 存量水位:同一 Agent 此刻保有的活跃流上限,0 = 不限。
max_active_per_agent: 1024
max_pending_opens: 1024
initial_window: 262144
window_update_threshold: 131072
max_frame_payload: 32768
authorization_cache:
enabled: true
cluster_positive_ttl: 5m
negative_ttl: 3s
revision_poll_interval: 2s
max_stale_on_poll_error: 5s
max_entries: 100000
# 管理监听器的请求边界:一个端口同时承载后台页面、/api/v1 与已升级的
# WebSocket,所以这里只约束请求头、请求体与 keep-alive 空闲,绝不设连接级
# read/write 超时。取值依据与和 Nginx 的关系见
# configuration.md「管理入口的超时、上限与保留策略」。
http:
read_header_timeout: 10s
idle_timeout: 120s
max_header_bytes: 1048576
body_timeout: 30s
# 0 = 不限制。Nginx 在前时容量由 limit_conn 决定;Server 直接暴露公网时
# 必须设置与进程容量匹配的正数,超限新连接立即关闭而不排队。
max_connections: 0
agents:
# 单个 Agent 身份在本节点可保有的物理 WebSocket 数量。
max_connections_per_agent: 64
metrics:
# 空 = /metrics 匿名可抓取(与历史行为一致)。二选一:反向代理按来源网段
# 放行,或用 TUNNELMESH_SERVER_METRICS_TOKEN 注入不少于 16 字符的 Bearer。
# 该字段不会出现在 config dump 输出里。
token: ""
audit:
# 0 = 永久保留。审计日志是证据,删除必须是显式决策;正数才会启动清理器,
# 每小时按 1000 行一批删除,避免首次清理历史大表时长时间锁表。
retention_days: 0
# 内嵌 VPN 网关(WireGuard)。数据面在 -tags vpn 构建里:带 tag 且 enabled: true
# 时进程真的监听 listen 指定的公网 UDP 端口并承载隧道;不带 tag 时只有管理面
# (签发、列出、修改、轮换、吊销、审计),config:reveal 返回 409 vpn_node_disabled。
# 不带 tag 却配 enabled: true 会启动失败并提示 rebuild with -tags vpn,不会静默不工作。
# 全部键的取值范围见 configuration.md 的「内嵌 VPN 网关」一节,
# 放行、密钥注入与 IP 池规划见 deployment/vpn-gateway.md。
vpn:
enabled: false
# 独立的公网 UDP 端口,不经反向代理,需在防火墙单独放行并单独限流。
listen: 0.0.0.0:51820
# 下发给用户的 Endpoint 主机名,只写主机名不写端口,端口取自 listen。
endpoint_host: gw-1.mesh.example.com
# 集群里每个节点必须配置相同的 ip_pool 与 node_subnet_size,
# 节点各自从池中租约一个 /24 子网(vpn_ip_leases,epoch fencing)。
ip_pool: 10.64.0.0/16
node_subnet_size: 24
# 1500 - WireGuard 的 72 字节开销。
mtu: 1420
# 下面三个 0 表示不限。
max_peers: 0
max_flows_per_peer: 128
max_flows_total: 0
packet_rate_per_peer: 0
connect_timeout: 10s
idle_timeout: 120s
shutdown_timeout: 15s
# 节点级上限而非承诺:peer 还需单独开启,且出口 Agent 要协商到该能力,
# 否则签发返回 409 vpn_agent_capability_missing。
icmp_enabled: true
icmp_timeout: 5s
icmp_max_concurrent: 64
# 节点自身的 WireGuard 私钥不是配置项,只由 TUNNELMESH_VPN_NODE_PRIVATE_KEY 注入。
security:
# 管理 API 和 WebSocket Host 白名单;动态 HTTP 路由由路由表匹配。
allowed_hosts:
- tunnel.example.com
# Agent/Client 连接 wss://tunnel.example.com 时发送该 Origin。
# 启用 WebSSH/SFTP 时还必须包含管理后台的精确 Origin。
allowed_origins:
- https://tunnel.example.com
# 管理台身份认证。这里只写“进程级默认值与旋钮”:
# session_token_ttl / device_trust.enabled / device_trust.bypass_mfa 只在
# auth_settings 行首次创建时作为种子写入,之后数据库行是权威来源,
# 改这里不会覆盖管理员在后台做出的决定。OIDC 提供商的 issuer、
# client id、client secret 和 role mapping 全部存在数据库,不在配置文件中。
auth:
# 管理台 token 有效期;0 表示沿用升级前的不过期行为。
session_token_ttl: 12h
login_throttle:
max_attempts: 10
window: 5m
block: 10m
mfa:
# 会写进 otpauth:// 标签,不能包含冒号。
issuer: TunnelMesh
digits: 6
period: 30s
skew: 1
challenge_ttl: 5m
max_attempts: 5
recovery_codes: 10
device_trust:
enabled: true
cookie_name: tm_device
cookie_secure: true
cookie_same_site: lax
# 关掉它表示“每次登录都要二次验证”,但仍保留设备清单与撤销能力。
bypass_mfa: true
oidc:
# 关闭后登录页不再列出 SSO 按钮,GET /api/v1/auth/oidc/providers 返回 404。
public_providers: true
http_timeout: 10s
state_ttl: 10m
login_ticket_ttl: 60s
jwks_cache_ttl: 1h
max_discovery_body_bytes: 1048576
# 后台“发行管理”页展示的仓库,格式 owner/name。
# 内网镜像可改为 mirror-owner/TunnelMesh,或用
# TUNNELMESH_DOWNLOADS_GITHUB_REPOSITORY 覆盖(该键没有命令行参数)。
downloads:
github_repository: nnworld/TunnelMesh
# 公网 TLS 已由 Nginx 终止,这里保持关闭。
# 若 Server 直接暴露 HTTPS,需同时提供 cert_file 和 key_file。
tls:
enabled: false
cert_file: ""
key_file: ""
min_version: "1.2"
配套的 systemd 环境文件示例:
TUNNELMESH_STORAGE_MYSQL_DSN='db_user:db_password@tcp(mysql.internal.example.com:3306)/tunnelmesh?parseTime=true'
TUNNELMESH_STORAGE_MYSQL_TLS=false
TUNNELMESH_SERVER_RELAY_NODE_TOKEN='replace-with-server-node-service-token'
# 管理台 token reveal、SSH 凭据自动认证,以及 SSO/MFA 必需:
# OIDC client_secret、TOTP 共享密钥和 auth_challenges payload 都用它加密。
# 长度为 16/24/32 字节,base64 或 hex;集群所有 Server 节点必须完全一致。
# 未配置时 Server 仍可启动,但 MFA 绑定和 OIDC 提供商创建返回
# 503 secret_storage_unavailable,不会退化成明文存储。
TUNNELMESH_TOKEN_ENCRYPTION_KEY="${TUNNELMESH_TOKEN_ENCRYPTION_KEY:?inject from your secret manager}"
# 可选;轮换密钥时随之更换。未设置时 key id 固定为 default。
TUNNELMESH_TOKEN_ENCRYPTION_KEY_ID="${TUNNELMESH_TOKEN_ENCRYPTION_KEY_ID:-default}"
# 多 Server 集群所有节点必须一致,用于跨节点 traceroute 签名。
TUNNELMESH_TRACE_SIGNING_KEY="${TUNNELMESH_TRACE_SIGNING_KEY:?inject from your secret manager}"
MySQL 支持 TLS 时改为:
storage:
driver: mysql
auto_init: true
mysql:
dsn: ""
tls: true
并在 DSN 中追加 tls=true:
TUNNELMESH_STORAGE_MYSQL_DSN='db_user:db_password@tcp(db.example.com:3306)/tunnelmesh?parseTime=true&tls=true'
在集群上启用 SSO 与 MFA
配置文件只负责种子。真正打开这两项能力是对数据库的两次管理 API 调用,任何 Server 节点都可以执行, 集群立即一致生效。以下命令中的 secret 一律来自环境变量,示例里只有占位符。
# 1) 打开全局两步验证策略(mfaMode: disabled / optional / required)
curl -sS -X PUT "https://tunnel.example.com/api/v1/auth/policy" \
-H "Authorization: Bearer ${ADMIN_TOKEN}" \
-H 'Idempotency-Key: auth-policy-enable-mfa-001' \
-H 'Content-Type: application/json' \
-d '{
"mfaMode": "required",
"deviceTrustEnabled": true,
"deviceTrustTtlSeconds": 2592000,
"allowTrustedDeviceBypass": true,
"maxTrustedDevices": 10,
"sessionTokenTtlSeconds": 43200
}'
# 2) 注册 OIDC 提供商。redirectUri 必须精确等于
# https://<对外域名>/api/v1/auth/oidc/<name>/callback,
# 且落在 security.allowed_origins / allowed_hosts 推导出的 base 之内。
curl -sS -X POST "https://tunnel.example.com/api/v1/sso/providers" \
-H "Authorization: Bearer ${ADMIN_TOKEN}" \
-H 'Idempotency-Key: sso-provider-okta-001' \
-H 'Content-Type: application/json' \
-d '{
"name": "okta",
"displayName": "Okta",
"issuer": "https://example.okta.com",
"clientId": "'"${OIDC_CLIENT_ID}"'",
"clientSecret": "'"${OIDC_CLIENT_SECRET}"'",
"scopes": ["openid", "profile", "email", "groups"],
"redirectUri": "https://tunnel.example.com/api/v1/auth/oidc/okta/callback",
"idTokenAlgs": ["RS256"],
"usernameClaim": "preferred_username",
"roleMappings": [
{"claim": "groups", "value": "tunnelmesh-admins", "role": "admin"}
],
"defaultRole": "user",
"authoritativeRoles": true,
"autoCreateUsers": true,
"fetchUserinfo": false,
"publicListed": true,
"enabled": true
}'
# 3) 在不影响用户的前提下验证 issuer discovery 与 JWKS 可达性。
# PROVIDER_ID 取第 2 步响应 data.id(不透明 ID,不是 name)。
PROVIDER_ID='<上一步返回的 data.id>'
curl -sS -X POST "https://tunnel.example.com/api/v1/sso/providers/${PROVIDER_ID}/test" \
-H "Authorization: Bearer ${ADMIN_TOKEN}" \
-H 'Idempotency-Key: sso-provider-okta-test-001'
三条命令里的 ADMIN_TOKEN、OIDC_CLIENT_ID、OIDC_CLIENT_SECRET 都必须先在当前 shell 里从
Secret Manager 导出;-d 用单引号包裹,所以凡是需要展开的字段都写成 "'"${VAR}"'" 形式,
直接写 "${VAR}" 会把字面量 ${VAR} 发出去。
字段取值范围、role mapping 语义和 data.error 排障表见
单点登录与两步验证;键含义见
配置说明。
Agent
Agent 主动连接 Server 的 /ws/agent,无需开放入站端口。agent.id 必须在设备生命周期内稳定,并与后台创建的 Agent 资源和 agent 类型 service token 绑定。
mode: local
agent:
server_url: wss://tunnel.example.com/ws/agent
id: agent-devbox
# 可省略;首次运行会生成并持久化稳定 instance ID。
# Linux 打包部署默认保存到 /var/lib/tunnelmesh-agent/agent-instance-id。
instance_id: agent-devbox-host-a
# 默认保持单连接。扩容配置见 operations/connection-pool.md。
connections:
min: 1
max: 1
streams:
max_concurrent_dials: 32
# Agent 自己的活跃流水位(纵深防御第二层),0 = 不限。
max_active: 1024
max_pending_dials: 128
connect_timeout: 5s
open_timeout: 8s
inbound_buffer_bytes: 262144
# VPN 网关的 ICMP echo 出口。开启前主机必须先执行一次:
# sysctl -w net.ipv4.ping_group_range='0 2147483647'
# 否则 socket 打不开、能力不通告,Server 会拒签 ICMP peer,
# 但 Agent 仍会继续服务 TCP/UDP/HTTP 隧道。
icmp_enabled: false
icmp_bind_address: 0.0.0.0
icmp_timeout: 5s
icmp_max_concurrent: 64
# token 由 TUNNELMESH_AGENT_TOKEN 注入。
metadata:
- name: device_id
source: file
path: /etc/machine-id
- name: region
source: env
key: TUNNELMESH_REGION
环境文件示例:
TUNNELMESH_AGENT_TOKEN='replace-with-agent-service-token'
TUNNELMESH_REGION='shanghai'
Client
Client 通过 /ws/client 建立会话。client.tunnels 可同时配置多个本地入口;执行 run 时,本地 listener 先启动,再按 agent_id 建立独立 WebSocket 连接池。同一 Agent 的 stream 复用该 Agent 的 WebSocket,断线时只重连 WebSocket,不重建本地 listener。临时转发仍可用 forward/proxy 命令。
mode: local
client:
server_url: wss://tunnel.example.com/ws/client
# 可省略;首次 run 生成小写 client-<32hex> 并持久化。
# Linux 默认 /var/lib/tunnelmesh-client/client-instance-id,
# macOS 默认 ~/Library/Application Support/TunnelMesh/client-instance-id,
# Windows 默认 %ProgramData%\TunnelMesh\client-instance-id。
instance_id: client-0123456789abcdef0123456789abcdef
instance_id_path: /var/lib/tunnelmesh-client/client-instance-id
connections:
min: 1
max: 4
high_watermark: 16
low_watermark: 2
evaluation_interval: 10s
cooldown: 30s
stream:
open_timeout: 8s
inbound_buffer_bytes: 262144
remote_validation:
positive_ttl: 15s
negative_ttl: 2s
timeout: 3s
max_entries: 10000
metadata:
- name: device_id
source: file
path: /etc/machine-id
- name: region
source: env
key: TUNNELMESH_CLIENT_REGION
# token 由 TUNNELMESH_CLIENT_TOKEN 注入。
tunnels:
- name: postgres
protocol: tcp
listen: 127.0.0.1:15432
agent_id: agent-db
target_host: db.internal
target_port: 5432
- name: dns
protocol: udp
listen: 127.0.0.1:15353
agent_id: agent-net
target_host: 10.0.0.53
target_port: 53
- name: internal-web
protocol: http
listen: 127.0.0.1:18080
agent_id: agent-web
target_host: 127.0.0.1
target_port: 8080
- name: socks-a
protocol: socks5
listen: 127.0.0.1:10866
agent_id: agent-a
- name: socks-b
protocol: socks5
listen: 127.0.0.1:10867
agent_id: agent-b
环境文件示例:
TUNNELMESH_CLIENT_TOKEN='replace-with-client-service-token'
TUNNELMESH_CLIENT_REGION='cn-north'
client.metadata 最多 32 项,单项 4 KiB,总 payload 32 KiB。名称只能使用字母、数字、.、_、-,且不能包含 password、passphrase、token、secret、private key、api key、credential、authorization、cookie 或 DSN 语义。字段值会在启动时读取一次并随 metadata 帧上报;读取失败只记录为该字段的 metadata 错误,不会泄露文件路径或值。
SOCKS5 本地入口
SOCKS5 可写入 client.tunnels,随 run 命令启动。默认监听 loopback 且不启用本地认证:
client:
tunnels:
- name: socks-a
protocol: socks5
listen: 127.0.0.1:10866
agent_id: agent-a
- name: socks-b
protocol: socks5
listen: 127.0.0.1:10867
agent_id: agent-b
tunnelmesh-client --config /etc/tunnelmesh/client.yaml run
需要临时启动单个入口时,也可使用命令行:
tunnelmesh-client forward socks5 \
--listen 127.0.0.1:1080 \
--agent agent-devbox
如需监听非 loopback 地址,配置文件必须同时设置 allow_remote: true 和 auth_mode: password,并从环境变量注入本地入口凭据。命令行模式对应 --allow-remote 和 --auth password:
client:
tunnels:
- name: remote-socks
protocol: socks5
listen: 0.0.0.0:1080
agent_id: agent-devbox
allow_remote: true
auth_mode: password
auth_url: http://auth.internal/validate
TUNNELMESH_SOCKS5_USERNAME='alice' \
TUNNELMESH_SOCKS5_PASSWORD='local-ingress-secret' \
tunnelmesh-client forward socks5 \
--listen 0.0.0.0:1080 \
--agent agent-devbox \
--allow-remote \
--auth password
这两个环境变量只用于本地 SOCKS5 入口认证,不用于 Server 或 Agent 的 service token。
如需启用远程校验,配置文件可设置 auth_url;临时命令可额外指定 --auth-url:
tunnelmesh-client forward socks5 \
--listen 127.0.0.1:1080 \
--agent agent-devbox \
--auth-url http://auth.internal/validate
标准 HTTP 代理
标准 HTTP 代理当前作为临时 forward http-proxy 命令提供,不写入 client.tunnels 配置。默认监听 loopback 且不启用本地认证:
tunnelmesh-client forward http-proxy \
--listen 127.0.0.1:8080 \
--agent agent-devbox
如需监听非 loopback 地址,必须同时开启 --allow-remote 和 --auth basic,并从环境变量注入本地代理凭据:
TUNNELMESH_HTTP_PROXY_USERNAME='alice' \
TUNNELMESH_HTTP_PROXY_PASSWORD='local-proxy-secret' \
tunnelmesh-client forward http-proxy \
--listen 0.0.0.0:8080 \
--agent agent-devbox \
--allow-remote \
--auth basic
这两个环境变量只用于本地 HTTP 代理认证,不用于 Server 或 Agent 的 service token。
如需启用远程校验,可额外指定 --auth-url:
tunnelmesh-client forward http-proxy \
--listen 127.0.0.1:8080 \
--agent agent-devbox \
--auth-url http://auth.internal/validate
Client 全协议配置示例
连接池参数是全局配置,作用于每个逻辑 Agent:多个 Agent 至少各有一条 WebSocket,同一 Agent 可按负载扩容到 max。high_watermark 表示单条 WebSocket 的活跃 stream 数,达到后扩容;low_watermark 表示缩容阈值。完整示例见 Client 配置示例。
检查与文件权限
tunnelmesh-server --config /etc/tunnelmesh/server.yaml check-config
tunnelmesh-agent --config /etc/tunnelmesh/agent.yaml check-config
tunnelmesh-client --config /etc/tunnelmesh/client.yaml login
配置和环境文件只应允许 root 与对应服务组读取:
chmod 640 /etc/tunnelmesh/*.yaml /etc/tunnelmesh/*.env
check-config 只验证配置结构,不测试数据库、证书或 WebSocket 的实际连通性。Server 的依赖状态应通过 /health/ready 检查。