Connector · Docker
Lancez le Connector dans un conteneur — sans port entrant.
L’image officielle ghcr.io/termalinapp/termalin-connector fait tourner le Termalin Connector dans un seul conteneur, sur n’importe quelle machine d’un réseau — un NAS, un home lab, un routeur, un cluster Kubernetes, un service ECS. Il établit une connexion sortante vers Termalin et atteint les serveurs, sites internes et bureaux à distance de ce réseau, sans aucun port entrant ouvert. Environ 4 Mo, amd64/arm64, tourne sous l’uid 10001.
amd64 / arm64 · aucun port entrant · Pro (ou essai de 14 jours)
01 / pourquoi un conteneur
Un conteneur sur le réseau, rien à ouvrir.
Un Connector permet à Termalin de joindre une machine privée sans port entrant et sans clé permanente dessus. Certaines machines sont plus simples à ajouter sous forme de conteneur que d’y installer un binaire et un service — et certaines ne savent faire tourner que des conteneurs. L’image est faite pour elles :
- ▸Un NAS ou un home lab. Synology, Unraid ou un hôte Docker sur votre réseau local — joignez le SSH de la machine elle-même, ainsi que tout autre serveur, site ou bureau du réseau.
- ▸Un routeur. MikroTik RouterOS 7 fait tourner des conteneurs ; l’un d’eux porte le Connector pour le routeur et tout ce qui se trouve derrière.
- ▸Kubernetes. Un Deployment à une seule réplique atteint les services du cluster par leur adresse interne.
- ▸AWS ECS / un VPC. Une tâche Fargate ou EC2 en HTTPS sortant uniquement — sans load balancer, sans règle entrante — atteint les instances privées, les sites internes et les bureaux à distance.
02 / l’image
Quatre mégaoctets, pas de shell, pas de serveur SSH.
L’image est ghcr.io/termalinapp/termalin-connector:<version> (et :latest), pour linux/amd64 et linux/arm64.
- ▸Environ 4 Mo. Le Connector statique, les certificats CA et rien d’autre — pas de shell, pas de gestionnaire de paquets, pas de serveur SSH. Elle tourne sous l’uid 10001 et conserve sa configuration dans le volume
/data. - ▸Construite au grand jour. Dans le dépôt public
termalinapp/termalin, uniquement à partir des binaires de version signés présents sur termal.in — chacun vérifié contre le manifeste de version et sa signature avant d’y entrer. - ▸L’armv7 n’est pas encore compilé (ARM 32 bits). amd64 et arm64 seulement pour l’instant.
03 / démarrage rapide
Inscrivez un conteneur en un seul docker run.
Obtenez un jeton d’inscription dans l’espace web (Connectors → Ajouter), puis :
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
Les variables :
| Variable | Son rôle |
|---|---|
TERMALIN_TOKEN | Jeton d’inscription ; utilisé uniquement au premier démarrage, quand le volume n’a pas encore de configuration. |
TERMALIN_TARGET | host:port du serveur SSH vers lequel vont les terminaux, les commandes et les fichiers (l’hôte Docker, ou une autre machine). À l’intérieur du conteneur, 127.0.0.1 désigne le conteneur lui-même, qui n’a pas de serveur SSH. |
TERMALIN_USER | Utilisateur de connexion sur ce serveur SSH (par défaut : celui par défaut du compte). |
TERMALIN_NAME | Nom dans l’espace web (par défaut : le nom d’hôte du conteneur — définissez --hostname ou cette variable). |
TERMALIN_SERVER | Par défaut https://termal.in. |
Premier démarrage : sans configuration dans /data et avec un jeton défini, le conteneur s’inscrit, affiche la ligne de connexion ci-dessous et se connecte. Les démarrages suivants ne font que se connecter — le jeton peut rester dans l’environnement, il n’est pas renvoyé. Sans configuration et sans jeton, il le signale et se termine avec le code 1. docker stop l’arrête aussitôt. Pour réinscrire, supprimez le volume et démarrez avec un nouveau jeton.
04 / la ligne de connexion
Faites confiance au certificat sur la cible.
Le Connector du conteneur ne touche jamais à la cible. À l’inscription, il affiche la ligne qui permet aux certificats à courte durée de Termalin pour ce compte de se connecter — et docker exec termalin termalin-agent check l’affiche de nouveau :
cert-authority,principals="termalin-<account id>" ssh-ed25519 AAAA… termalin-connector
Placez-la dans le ~/.ssh/authorized_keys de l’utilisateur de connexion sur la cible (une seule ligne, fichier en mode 600, ~/.ssh en 700). Sur un serveur que vous administrez en root, vous pouvez à la place ajouter TrustedUserCAKeys et AuthorizedPrincipalsFile à son sshd_config — check affiche les deux. Sans cela, les terminaux et les commandes passant par ce Connector sont refusés par la cible ; les liens web et les bureaux à distance n’en ont pas besoin. check indique aussi si le serveur SSH de la cible répond (sshd=up) ; l’état de la connexion est ca=unknown, car les fichiers de la cible ne peuvent pas être lus depuis l’intérieur du conteneur.
05 / sans serveur SSH
Sites internes et bureaux à distance, sans aucun sshd nulle part.
Les sites internes et les bureaux à distance (RDP, VNC) ne passent pas par SSH : le backend demande au Connector d’ouvrir une connexion vers le host:port du site ou du bureau lui-même, et le Connector s’y connecte directement. Un conteneur dans un VPC, un service ECS ou un réseau domestique peut donc publier des sites internes et des bureaux à distance sans aucun serveur SSH. La page des bases de données avec un mot de passe de base de données fonctionne de la même manière, vers le port de la base.
- ▸Adresses privées uniquement. Les cibles jointes doivent se trouver sur le réseau privé — RFC 1918, loopback, link-local, CGNAT
100.64/10(comme Tailscale), ULA IPv6. Le Connector n’est pas un relais vers Internet, et les adresses de métadonnées cloud (169.254.169.254et consorts) sont toujours refusées. - ▸Exige un serveur SSH sur la cible : le terminal,
ssh_execet les autres outils de commande, les fichiers par SFTP, les outils de bases de données sans mot de passe, les métriques et les sauvegardes. DéfinissezTERMALIN_TARGETet ajoutez-y la ligne de connexion.
06 / docker compose
La même chose, en fichier 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: Pour joindre l’hôte Docker lui-même, utilisez son adresse sur le réseau local, ou sous Docker Desktop host.docker.internal:22 (sous Linux, ajoutez extra_hosts: ["host.docker.internal:host-gateway"]).
07 / NAS
Synology et Unraid.
- ▸Synology (Container Manager) : Registry → ajoutez
ghcr.io/termalinapp/termalin-connector, créez un conteneur avec les variables ci-dessus, un volume pour/data, « auto restart » activé. Activez le SSH dans Control Panel → Terminal & SNMP pour le terminal, et définissezTERMALIN_TARGET=<NAS address>:22. - ▸Unraid : Docker → Add Container, dépôt
ghcr.io/termalinapp/termalin-connector, un chemin/data→/mnt/user/appdata/termalin, les variables en tant que variables de conteneur. - ▸Un dossier monté en bind doit être accessible en écriture par l’uid 10001 (
chown 10001:10001 /mnt/…/termalin) ; un volume nommé est configuré par Docker pour vous.
08 / MikroTik RouterOS 7
Sur le routeur lui-même.
Routeurs arm64 et x86 pour l’instant (pas encore de build armv7). Activez le paquet container et le device-mode, puis :
/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 Cela suppose un bridge containers avec 172.17.0.1/24 et du NAT sortant, comme dans le propre guide des conteneurs de MikroTik. Visez le SSH du routeur lui-même (/ip/service enable ssh) ou n’importe quel hôte derrière lui.
09 / Kubernetes
Une réplique, un Secret, un petit 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 } } Les services internes se joignent par leur adresse de cluster dans un lien web — http://grafana.monitoring.svc:3000 se résout, via le DNS du cluster, vers une adresse privée. Gardez une seule réplique par inscription : deux pods sur le même volume seraient deux fois le même Connector.
10 / AWS ECS
Une tâche Fargate ou EC2, en sortant uniquement.
Un service avec un desired count de 1, l’image ci-dessus, le jeton tiré de Secrets Manager comme TERMALIN_TOKEN, et un volume EFS sur /data (appartenant à 10001 — un point d’accès EFS avec uid/gid 10001). Du HTTPS sortant vers termal.in lui suffit — sans load balancer, sans règle entrante. Les security groups des cibles doivent laisser entrer la tâche sur les ports utilisés (22, 3389, les ports des sites). L’adresse de métadonnées de la tâche ECS est refusée comme toute adresse de métadonnées.
11 / mises à jour et limites
Récupérez un nouveau tag ; ce qu’un conteneur ne sait pas faire.
- ▸Mises à jour. Le conteneur ne se met jamais à jour tout seul — récupérez un nouveau tag et recréez-le (
docker compose pull && docker compose up -d, ou un nouveau tag d’image dans le Deployment). Le volume conserve l’inscription. L’espace web n’affiche aucun « Mettre à jour maintenant » pour lui. - ▸Les fonctions SSH exigent une cible. Le terminal, les commandes, les fichiers, les bases de données sans mot de passe, les métriques et les sauvegardes ont besoin d’un serveur SSH sur la cible et de la ligne de connexion là-bas ; l’image n’en a aucun en propre.
- ▸Un seul serveur SSH cible par Connector (
TERMALIN_TARGET) ; les liens web et les bureaux à distance peuvent désigner n’importe quelle adresse privée. - ▸L’armv7 (ARM 32 bits) n’est pas encore compilé.
Questions fréquentes
Quelles architectures, et quelle taille fait l’image ?
Une image multi-arch pour linux/amd64 et linux/arm64, environ 4 Mo : le Connector statique, les certificats CA et rien d’autre — pas de shell, pas de gestionnaire de paquets, pas de serveur SSH. Elle tourne sous l’uid 10001 et conserve sa configuration dans le volume /data. L’armv7 (ARM 32 bits) n’est pas encore compilé.
D’où vient l’image ?
Elle est construite au grand jour, dans le dépôt public termalinapp/termalin, uniquement à partir des binaires de version signés publiés sur termal.in — chacun vérifié contre le manifeste de version et sa signature avant d’entrer dans l’image.
Ai-je besoin d’un serveur SSH dans le conteneur ou à côté ?
Pas pour les sites internes, les bureaux à distance (RDP/VNC) ni la page des bases de données — ceux-là vont directement au host:port de la cible via le Connector, si bien qu’un conteneur dans un VPC ou sur un réseau domestique peut les publier sans aucun sshd nulle part. Un terminal, des commandes, des fichiers SFTP, les outils de bases de données sans mot de passe, les métriques et les sauvegardes exigent bien un serveur SSH sur la cible (TERMALIN_TARGET) et la ligne de connexion ajoutée là-bas — l’image elle-même n’en a aucun.
Comment mettre à jour le conteneur ?
Le conteneur ne se met jamais à jour tout seul. Récupérez un nouveau tag et recréez-le — docker compose pull && docker compose up -d, ou un nouveau tag d’image dans le Deployment. Le volume /data conserve l’inscription : vous n’avez donc pas à réinscrire. L’espace web n’affiche aucun « Mettre à jour maintenant » pour un conteneur.
Combien ça coûte, et quel forfait faut-il ?
Le Connector est une fonctionnalité hébergée : il nécessite donc un compte Termalin sur Pro ou l’essai de 14 jours. L’image est gratuite à récupérer ; il n’y a aucune limite au nombre de Connectors que vous ajoutez.
Joignez tout le réseau depuis un seul conteneur.
Un jeton, un docker run, aucun port entrant. Sur un NAS, un routeur, Kubernetes ou ECS.
Pro (ou essai de 14 jours) · Sites internes · Bureaux à distance