Connector · Docker
Ejecuta el Connector en un contenedor — sin puerto de entrada.
La imagen oficial ghcr.io/termalinapp/termalin-connector ejecuta el Termalin Connector en un solo contenedor en cualquier máquina de una red — un NAS, un laboratorio casero, un router, un clúster de Kubernetes, un servicio de ECS. Marca hacia fuera a Termalin y llega a los servidores, sitios internos y escritorios de esa red, sin ningún puerto de entrada abierto. Unos 4 MB, amd64/arm64, corre como uid 10001.
amd64 / arm64 · sin puerto de entrada · Pro (o los 14 días de prueba)
01 / por qué un contenedor
Un contenedor en la red, nada que abrir.
Un Connector permite a Termalin llegar a un equipo privado sin puerto de entrada y sin ninguna clave de larga duración en él. Algunas máquinas son más fáciles de añadir como contenedor que instalándoles un binario y un servicio — y otras solo pueden ejecutar contenedores. La imagen encaja con ellas:
- ▸Un NAS o laboratorio casero. Synology, Unraid o un host Docker en tu LAN — llega al propio SSH del equipo y a cualquier otro servidor, sitio o escritorio de la red.
- ▸Un router. MikroTik RouterOS 7 ejecuta contenedores; uno alberga el Connector para el router y todo lo que hay tras él.
- ▸Kubernetes. Un Deployment de una sola réplica llega a los servicios del clúster por su dirección interna.
- ▸AWS ECS / una VPC. Una tarea de Fargate o EC2 con solo HTTPS saliente — sin balanceador de carga, sin regla de entrada — llega a instancias privadas, sitios internos y escritorios.
02 / la imagen
Cuatro megabytes, sin shell, sin servidor SSH.
La imagen es ghcr.io/termalinapp/termalin-connector:<version> (y :latest), para linux/amd64 y linux/arm64.
- ▸Unos 4 MB. El Connector estático, los certificados de CA y nada más — sin shell, sin gestor de paquetes, sin servidor SSH. Corre como uid 10001 y guarda su configuración en el volumen
/data. - ▸Compilada a la vista. En el repositorio público
termalinapp/termalin, solo a partir de los binarios de versión firmados en termal.in — cada uno comprobado contra el manifiesto de la versión y su firma antes de entrar. - ▸armv7 aún no se compila (ARM de 32 bits). Por ahora solo amd64 y arm64.
03 / inicio rápido
Da de alta un contenedor con un solo docker run.
Consigue un token de alta en el panel web (Añadir → Connector), y luego:
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
Las variables:
| Variable | Qué hace |
|---|---|
TERMALIN_TOKEN | Token de alta; se usa solo en el primer arranque, cuando el volumen aún no tiene configuración. |
TERMALIN_TARGET | host:port del servidor SSH al que van los terminales, los comandos y los archivos (el host de Docker, u otra máquina). Dentro del contenedor, 127.0.0.1 es el propio contenedor, que no tiene servidor SSH. |
TERMALIN_USER | Usuario de inicio de sesión en ese servidor SSH (por defecto: el predeterminado de la cuenta). |
TERMALIN_NAME | Nombre en el panel web (por defecto: el hostname del contenedor — fíjalo con --hostname o con esto). |
TERMALIN_SERVER | Por defecto https://termal.in. |
Primer arranque: sin configuración en /data y con un token definido, el contenedor se da de alta, imprime la línea de acceso de abajo y conecta. Los arranques posteriores solo conectan — el token puede quedarse en el entorno, no se vuelve a enviar. Sin configuración y sin token lo indica y sale con código 1. docker stop lo detiene de inmediato. Para darlo de alta otra vez, elimina el volumen y arranca con un token nuevo.
04 / la línea de acceso
Confía en el certificado en el objetivo.
El Connector del contenedor nunca toca el objetivo. Al darse de alta imprime la línea que permite iniciar sesión a los certificados de corta duración de Termalin de esta cuenta — y docker exec termalin termalin-agent check la imprime de nuevo:
cert-authority,principals="termalin-<account id>" ssh-ed25519 AAAA… termalin-connector
Ponla en ~/.ssh/authorized_keys del usuario de inicio de sesión en el objetivo (una línea, el archivo en modo 600, ~/.ssh en 700). En un servidor que gestionas como root puedes añadir en su lugar TrustedUserCAKeys y AuthorizedPrincipalsFile a su sshd_config — check imprime ambos. Sin esto, el objetivo rechaza los terminales y comandos a través de este Connector; los enlaces web y los escritorios no la necesitan. check también informa de si el servidor SSH del objetivo responde (sshd=up); el estado de acceso es ca=unknown, porque los archivos del objetivo no pueden leerse desde dentro del contenedor.
05 / sin necesidad de servidor SSH
Sitios internos y escritorios, sin ningún sshd.
Los sitios internos y los escritorios remotos (RDP, VNC) no pasan por SSH: el backend le pide al Connector que abra una conexión al host:port propio del sitio o del escritorio, y el Connector conecta ahí directamente. Así, un contenedor en una VPC, un servicio de ECS o una red doméstica puede publicar sitios internos y escritorios sin ningún servidor SSH. La página de bases de datos con una contraseña de base de datos funciona igual, hacia el puerto de la base de datos.
- ▸Solo direcciones privadas. Los objetivos a los que marca deben estar en la red privada — RFC 1918, loopback, link-local, CGNAT
100.64/10(como Tailscale), ULA de IPv6. El Connector no es un relé hacia internet, y las direcciones de metadatos de la nube (169.254.169.254y similares) se rechazan siempre. - ▸Necesita un servidor SSH en el objetivo: el terminal,
ssh_execy las demás herramientas de comandos, los archivos por SFTP, las herramientas de base de datos sin contraseña, la monitorización y las copias de seguridad. DefineTERMALIN_TARGETy añade ahí la línea de acceso.
06 / docker compose
Lo mismo, como archivo 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: Para llegar al propio host de Docker, usa su dirección de LAN, o en Docker Desktop host.docker.internal:22 (en Linux añade extra_hosts: ["host.docker.internal:host-gateway"]).
07 / NAS
Synology y Unraid.
- ▸Synology (Container Manager): Registry → añade
ghcr.io/termalinapp/termalin-connector, crea un contenedor con las variables de arriba, un volumen para/datay «auto restart» activado. Activa SSH en Control Panel → Terminal & SNMP para el terminal, y defineTERMALIN_TARGET=<dirección del NAS>:22. - ▸Unraid: Docker → Add Container, repositorio
ghcr.io/termalinapp/termalin-connector, una ruta/data→/mnt/user/appdata/termalin, las variables como variables de contenedor. - ▸Una carpeta montada por bind debe poder escribirla uid 10001 (
chown 10001:10001 /mnt/…/termalin); un volumen con nombre lo prepara Docker por ti.
08 / MikroTik RouterOS 7
En el propio router.
Routers arm64 y x86 por ahora (aún no hay compilación armv7). Activa el paquete container y el device-mode, y luego:
/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 Eso da por hecho un bridge containers con 172.17.0.1/24 y NAT de salida, como en la propia guía de contenedores de MikroTik. Apunta al SSH del propio router (/ip/service enable ssh) o a cualquier host tras él.
09 / Kubernetes
Una réplica, un Secret, un PVC diminuto.
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 } } A los servicios internos se llega por su dirección de clúster en un enlace web — http://grafana.monitoring.svc:3000 se resuelve por el DNS del clúster a una dirección privada. Mantén una réplica por alta: dos pods sobre el mismo volumen serían el mismo Connector dos veces.
10 / AWS ECS
Una tarea de Fargate o EC2, solo saliente.
Un servicio con desired count 1, la imagen de arriba, el token de Secrets Manager como TERMALIN_TOKEN y un volumen de EFS en /data (propiedad de 10001 — un punto de acceso de EFS con uid/gid 10001). Solo necesita HTTPS saliente a termal.in — sin balanceador de carga, sin regla de entrada. Los security groups de los objetivos deben dejar entrar a la tarea por los puertos que se usen (22, 3389, los puertos de los sitios). La dirección de metadatos de la tarea de ECS se rechaza como cualquier dirección de metadatos.
11 / actualizaciones y límites
Baja una etiqueta nueva; lo que un contenedor no puede hacer.
- ▸Actualizaciones. El contenedor nunca se actualiza solo — baja una etiqueta nueva y recréalo (
docker compose pull && docker compose up -d, o una nueva etiqueta de imagen en el Deployment). El volumen conserva el alta. El panel web no muestra «Actualizar ahora» para él. - ▸Las funciones SSH necesitan un objetivo. El terminal, los comandos, los archivos, las bases de datos sin contraseña, la monitorización y las copias de seguridad necesitan un servidor SSH en el objetivo y la línea de acceso allí; la imagen no tiene ninguno propio.
- ▸Un servidor SSH objetivo por Connector (
TERMALIN_TARGET); los enlaces web y los escritorios pueden indicar cualquier dirección privada. - ▸armv7 (ARM de 32 bits) aún no se compila.
Preguntas
¿Qué arquitecturas admite y cuánto ocupa la imagen?
Una imagen multiarquitectura para linux/amd64 y linux/arm64, de unos 4 MB: el Connector estático, los certificados de CA y nada más — sin shell, sin gestor de paquetes, sin servidor SSH. Corre como uid 10001 y guarda su configuración en el volumen /data. armv7 (ARM de 32 bits) aún no se compila.
¿De dónde sale la imagen?
Se compila a la vista, en el repositorio público termalinapp/termalin, solo a partir de los binarios de versión firmados publicados en termal.in — cada uno comprobado contra el manifiesto de la versión y su firma antes de entrar en la imagen.
¿Necesito un servidor SSH dentro o junto al contenedor?
No para los sitios internos, los escritorios remotos (RDP/VNC) ni la página de bases de datos — esos llegan directamente al host:port propio del objetivo a través del Connector, así que un contenedor en una VPC o en una red doméstica puede publicarlos sin ningún sshd. Un terminal, los comandos, los archivos por SFTP, las herramientas de base de datos sin contraseña, la monitorización y las copias de seguridad sí necesitan un servidor SSH en el objetivo (TERMALIN_TARGET) y la línea de acceso añadida allí — la imagen en sí no tiene ninguno.
¿Cómo actualizo el contenedor?
El contenedor nunca se actualiza solo. Baja una etiqueta nueva y recréalo — docker compose pull && docker compose up -d, o una nueva etiqueta de imagen en el Deployment. El volumen /data conserva el alta, así que no vuelves a darlo de alta. El panel web no muestra «Actualizar ahora» para un contenedor.
¿Cuánto cuesta y qué plan necesito?
El Connector es una función alojada, así que necesita una cuenta de Termalin en Pro o los 14 días de prueba. Bajar la imagen es gratis; no hay límite en cuántos Connectors añades.
Llega a toda la red desde un solo contenedor.
Un token, un docker run, sin puerto de entrada. En un NAS, un router, Kubernetes o ECS.
Pro (o los 14 días de prueba) · Sitios internos · Escritorios remotos