Connector · Docker
在容器中运行 Connector,无需入站端口
官方镜像 ghcr.io/termalinapp/termalin-connector 把 Termalin Connector 运行在网络中任意一台机器上的单个容器里——NAS、家庭实验室、路由器、Kubernetes 集群或 ECS 服务。它主动向外连接 Termalin,访问该网络内的服务器、内网站点和远程桌面,无需开放任何入站端口。约 4 MB,支持 amd64/arm64,以 uid 10001 运行。
amd64 / arm64 · 无需入站端口 · Pro(或 14 天试用)
01 / 为什么用容器
网络中一个容器,什么都不用开放
Connector 让 Termalin 无需入站端口、也不在机器上留长期密钥就能访问私有机器。有些机器与其安装二进制文件和服务,不如直接作为容器添加——还有些机器只能运行容器。这个镜像正适合它们:
- ▸NAS 或家庭实验室。局域网里的 Synology、Unraid 或 Docker 主机——访问这台机器自身的 SSH,以及网络中任何其他服务器、站点或桌面。
- ▸路由器。MikroTik RouterOS 7 能运行容器;一个容器就能为路由器及其后面的一切承载 Connector。
- ▸Kubernetes。一个单副本的 Deployment,通过内部地址访问集群内的服务。
- ▸AWS ECS / VPC。一个仅有出站 HTTPS 的 Fargate 或 EC2 任务——没有负载均衡器,没有入站规则——即可访问私有实例、内网站点和远程桌面。
02 / 镜像
四兆字节,没有 shell,没有 SSH 服务器
镜像为 ghcr.io/termalinapp/termalin-connector:<version>(以及 :latest),支持 linux/amd64 和 linux/arm64。
- ▸约 4 MB。只有静态编译的 Connector、CA 证书,别无其他——没有 shell、没有包管理器、没有 SSH 服务器。它以 uid 10001 运行,并把配置保存在
/data卷中。 - ▸公开构建。在公开的
termalinapp/termalin仓库中,仅使用 termal.in 上带签名的发布二进制文件构建——每个文件在进入镜像前都会对照发布清单及其签名进行校验。 - ▸armv7 尚未构建(32 位 ARM)。目前仅支持 amd64 和 arm64。
03 / 快速上手
一条 docker run 接入一个容器
在网页控制台里获取接入令牌(Connectors → 添加),然后:
docker run -d --name termalin --restart unless-stopped \ -v termalin:/data \ -e TERMALIN_TOKEN=<token> \ -e TERMALIN_TARGET=192.168.1.10:22 \ -e TERMALIN_USER=deploy \ ghcr.io/termalinapp/termalin-connector
各个变量:
| 变量 | 作用 |
|---|---|
TERMALIN_TOKEN | 接入令牌;仅在首次启动、卷中尚无配置时使用。 |
TERMALIN_TARGET | 终端、命令和文件所连接的 SSH 服务器的 host:port(Docker 宿主机,或另一台机器)。在容器内部,127.0.0.1 指的是容器自身,而容器里没有 SSH 服务器。 |
TERMALIN_USER | 该 SSH 服务器上的登录用户(默认:账户的默认用户)。 |
TERMALIN_NAME | 在网页控制台中显示的名称(默认:容器的主机名——通过 --hostname 或此变量设置)。 |
TERMALIN_SERVER | 默认为 https://termal.in。 |
首次启动:/data 中没有配置、且设置了令牌时,容器会完成接入,打印下面的登录授权行并建立连接。之后的启动只是连接——令牌可以留在环境变量中,它不会被再次发送。既没有配置也没有令牌时,它会提示并以状态码 1 退出。docker stop 会立即停止它。要重新接入,删除卷后用新令牌启动。
04 / 登录授权行
在目标主机上信任该证书
容器里的 Connector 从不接触目标主机。接入时,它会打印那一行——让 Termalin 为此账户签发的短期证书得以登录——docker exec termalin termalin-agent check 也会再次打印它:
cert-authority,principals="termalin-<account id>" ssh-ed25519 AAAA… termalin-connector
把它放进目标主机上登录用户的 ~/.ssh/authorized_keys(一行,文件权限 600,~/.ssh 为 700)。在你以 root 管理的服务器上,也可以改为在其 sshd_config 中添加 TrustedUserCAKeys 和 AuthorizedPrincipalsFile——check 两种都会打印。没有这一行,通过这个 Connector 打开的终端和命令会被目标主机拒绝;内网站点和远程桌面则不需要它。check 还会报告目标主机的 SSH 服务器是否响应(sshd=up);登录状态显示为 ca=unknown,因为在容器内部无法读取目标主机上的文件。
05 / 无需 SSH 服务器
内网站点和远程桌面,任何地方都不需要 sshd
内网站点和远程桌面(RDP、VNC)不走 SSH:后端请求 Connector 打开一个到站点或桌面自身 host:port 的连接,Connector 直接连到那里。所以 VPC、ECS 服务或家庭网络里的一个容器,完全不用任何 SSH 服务器就能发布内网站点和远程桌面。数据库页面配合数据库密码也以同样方式工作,连到数据库的端口。
- ▸仅限私有地址。发起连接的目标必须位于私有网络——RFC 1918、环回地址、链路本地地址、CGNAT
100.64/10(例如 Tailscale)、IPv6 ULA。Connector 不是通向互联网的中转,而云元数据地址(169.254.169.254之类)始终会被拒绝。 - ▸需要目标主机上有 SSH 服务器:终端、
ssh_exec及其他命令工具、通过 SFTP 传输文件、免密码的数据库工具、指标和备份。设置TERMALIN_TARGET并在那里添加登录授权行。
06 / docker compose
同样的配置,写成 compose 文件
services:
termalin:
image: ghcr.io/termalinapp/termalin-connector:latest
restart: unless-stopped
hostname: office-nas
environment:
TERMALIN_TOKEN: ${TERMALIN_TOKEN}
TERMALIN_TARGET: 192.168.1.10:22
TERMALIN_USER: deploy
volumes:
- termalin:/data
volumes:
termalin: 要访问 Docker 宿主机本身,使用它的局域网地址,或者在 Docker Desktop 上使用 host.docker.internal:22(在 Linux 上需添加 extra_hosts: ["host.docker.internal:host-gateway"])。
07 / NAS
Synology 和 Unraid
- ▸Synology(Container Manager):在 Registry 中添加
ghcr.io/termalinapp/termalin-connector,用上面的变量创建一个容器,为/data挂载一个卷,并开启“自动重启”。要使用终端,请在 Control Panel → Terminal & SNMP 中启用 SSH,并设置TERMALIN_TARGET=<NAS 地址>:22。 - ▸Unraid:Docker → Add Container,仓库填
ghcr.io/termalinapp/termalin-connector,添加一个路径/data→/mnt/user/appdata/termalin,变量作为容器变量填入。 - ▸绑定挂载的文件夹必须对 uid 10001 可写(
chown 10001:10001 /mnt/…/termalin);命名卷则由 Docker 替你设置好。
08 / MikroTik RouterOS 7
直接在路由器上运行
目前支持 arm64 和 x86 路由器(尚无 armv7 构建)。启用容器软件包(container package)和设备模式(device-mode),然后:
/interface/veth add name=veth-termalin address=172.17.0.2/24 gateway=172.17.0.1
/interface/bridge/port add bridge=containers interface=veth-termalin
/container/envs add name=termalin key=TERMALIN_TOKEN value=<token>
/container/envs add name=termalin key=TERMALIN_TARGET value=192.168.88.1:22
/container/mounts add name=termalin-data src=/usb1/termalin dst=/data
/container add remote-image=ghcr.io/termalinapp/termalin-connector:latest \
interface=veth-termalin envlist=termalin mounts=termalin-data \
root-dir=/usb1/termalin-root start-on-boot=yes logging=yes 这里假设有一个名为 containers、地址为 172.17.0.1/24 并做了出站 NAT 的网桥,正如 MikroTik 自己的容器指南所述。目标可指向路由器自身的 SSH(/ip/service enable ssh),或其后面的任意一台主机。
09 / Kubernetes
一个副本、一个 Secret、一个很小的 PVC
apiVersion: v1
kind: Secret
metadata: { name: termalin }
stringData: { token: "<token>" }
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata: { name: termalin-data }
spec:
accessModes: [ReadWriteOnce]
resources: { requests: { storage: 16Mi } }
---
apiVersion: apps/v1
kind: Deployment
metadata: { name: termalin-connector }
spec:
replicas: 1
strategy: { type: Recreate }
selector: { matchLabels: { app: termalin-connector } }
template:
metadata: { labels: { app: termalin-connector } }
spec:
securityContext: { runAsUser: 10001, runAsGroup: 10001, fsGroup: 10001, runAsNonRoot: true }
containers:
- name: connector
image: ghcr.io/termalinapp/termalin-connector:0.2.3
env:
- { name: TERMALIN_TOKEN, valueFrom: { secretKeyRef: { name: termalin, key: token } } }
- { name: TERMALIN_TARGET, value: "10.0.12.7:22" }
- { name: TERMALIN_NAME, value: "k8s-prod" }
securityContext: { readOnlyRootFilesystem: true, allowPrivilegeEscalation: false, capabilities: { drop: [ALL] } }
resources: { requests: { cpu: 10m, memory: 16Mi }, limits: { memory: 64Mi } }
volumeMounts: [{ name: data, mountPath: /data }]
volumes:
- { name: data, persistentVolumeClaim: { claimName: termalin-data } } 集群内部的服务,用它们的集群地址以网页链接的形式访问——http://grafana.monitoring.svc:3000 会经集群 DNS 解析到一个私有地址。请每个接入保持一个副本:同一个卷上的两个 Pod 等于把同一个 Connector 跑了两遍。
10 / AWS ECS
一个 Fargate 或 EC2 任务,仅出站
一个期望任务数为 1 的服务,使用上面的镜像,把来自 Secrets Manager 的令牌作为 TERMALIN_TOKEN,并在 /data 挂载一个 EFS 卷(属主为 10001——一个 uid/gid 均为 10001 的 EFS 访问点)。它所需要的只是到 termal.in 的出站 HTTPS——没有负载均衡器,没有入站规则。目标主机的安全组必须允许该任务从所用端口(22、3389、各站点的端口)进入。ECS 任务元数据地址和其他所有元数据地址一样会被拒绝。
11 / 更新与限制
拉取新标签;容器做不到的事
- ▸更新。容器从不自行更新——拉取一个新标签并重新创建它(
docker compose pull && docker compose up -d,或在 Deployment 中换一个新的镜像标签)。卷会保留接入状态。网页控制台不会为它显示“立即更新”。 - ▸SSH 功能需要一个目标主机。终端、命令、文件、免密码数据库、指标和备份都需要目标主机上有 SSH 服务器,并在那里添加登录授权行;镜像本身不带任何 SSH 服务器。
- ▸每个 Connector 只对应一个目标 SSH 服务器(
TERMALIN_TARGET);内网站点和远程桌面则可以指向任意私有地址。 - ▸armv7(32 位 ARM)尚未构建。
常见问题
镜像支持哪些架构?有多大?
一个支持 linux/amd64 和 linux/arm64 的多架构镜像,约 4 MB:只有静态编译的 Connector、CA 证书,别无其他——没有 shell、没有包管理器、没有 SSH 服务器。它以 uid 10001 运行,并把配置保存在 /data 卷中。armv7(32 位 ARM)尚未构建。
镜像从何而来?
它在公开的 termalinapp/termalin 仓库中公开构建,仅使用 termal.in 上发布的带签名的发布二进制文件——每个文件在进入镜像前都会对照发布清单及其签名进行校验。
容器里或容器旁边需要一个 SSH 服务器吗?
内网站点、远程桌面(RDP/VNC)和数据库页面不需要——它们通过 Connector 直接连到目标自身的 host:port,所以 VPC 或家庭网络里的一个容器可以在任何地方都没有 sshd 的情况下发布它们。而终端、命令、SFTP 文件、免密码的数据库工具、指标和备份确实需要目标主机(TERMALIN_TARGET)上有一个 SSH 服务器,并在那里添加登录授权行——镜像本身不带。
如何更新容器?
容器从不自行更新。拉取一个新标签并重新创建它——docker compose pull && docker compose up -d,或在 Deployment 中换一个新的镜像标签。/data 卷会保留接入状态,所以你无需再次接入。网页控制台不会为容器显示“立即更新”。
它收费吗?需要哪个套餐?
Connector 是托管功能,所以需要一个 Pro 或 14 天试用的 Termalin 账户。镜像可免费拉取;你添加的 Connector 数量没有上限。
一个容器,访问整个网络
一个令牌,一条 docker run,无需入站端口。在 NAS、路由器、Kubernetes 或 ECS 上。