Emulateur Terminal Web Local ttyd
Emulateur Terminal Web Local ttyd
ttyd est un émulateur de terminal Web et un shell interactif qui permet aux utilisateurs d’accéder à une interface de ligne de commande (CLI) Linux à partir d’un navigateur Web. Il utilise le protocole WebSocket pour assurer une connexion en temps réel entre le navigateur et le terminal Linux.
Cet outil léger et facile à utiliser peut être utilisé pour accéder à un système Linux distant ou pour fournir un accès à distance à un système Linux local. Il prend en charge plusieurs clients à la fois, permettant à plusieurs utilisateurs de se connecter à la même session. L’un des avantages de ttyd est qu’il ne nécessite l’installation d’aucun logiciel supplémentaire côté client, car il s’exécute entièrement dans un navigateur Web. Cela en fait un outil pratique pour l’administration et le dépannage du système à distance.
Les autres fonctionnalités et avantages associés à ttyd sont :
- Prise en charge SSL basée sur OpenSSL / Mbed TLS
- Prise en charge du transfert de fichiers ZMODEM (lrzsz)/ trzsz
- Terminal complet avec prise en charge CJK et IME
- Construit sur libuv et WebGL2 pour la vitesse
- Prise en charge de la sortie d’image Sixel (img2sixel / lsix)
- Exécutez n’importe quelle commande personnalisée avec des options
- Prise en charge de l’authentification de base et de nombreuses autres options personnalisées
- Logiciel Open Source pouvant être installé sur les systèmes Linux, FreeBSD/OpenBSD, OpenWrt, macOS et Windows. Il est également disponible sous forme d’outil de ligne de commande ou de conteneur Docker.
Proxmox (ThinkCentre M700)
Se connecter sur le serveur Proxmox (ThinkCentre M700) via SSH
1
ssh tin@192.168.0.216 -p 55216 -i /home/yann/.ssh/m700-ed25519
Créer le fichier install-terminal.sh et copier le contenu ci-joint
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
#!/usr/bin/env bash
# Terminal web strictement local : ttyd + tmux derrière nginx (HTTPS, mot de passe, restriction au LAN)
# À lancer en root dans le conteneur Debian 13 dédié.
#
# Variables modifiables à l'appel, par exemple :
# BIND_IP=192.168.0.206 WEB_USER=yannig bash install-terminal.sh
set -euo pipefail
# pct exec peut fournir un PATH réduit : on le fixe explicitement
export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
FQDN="${FQDN:-terminal.home.arpa}"
LAN_CIDR="${LAN_CIDR:-192.168.0.0/24}"
BIND_IP="${BIND_IP:-$(hostname -I | awk '{print $1}')}"
TERM_USER="${TERM_USER:-termuser}" # utilisateur Unix qui exécute le shell et les ssh
WEB_USER="${WEB_USER:-admin}" # identifiant de l'authentification web
TTYD_PORT="${TTYD_PORT:-7681}"
[[ $EUID -eq 0 ]] || { echo "À lancer en root." >&2; exit 1; }
[[ -n "$BIND_IP" ]] || { echo "Impossible de déterminer BIND_IP." >&2; exit 1; }
if [[ -z "${WEB_PASS:-}" ]]; then
read -rsp "Mot de passe web pour ${WEB_USER} : " WEB_PASS; echo
read -rsp "Confirmer : " WEB_PASS2; echo
[[ "$WEB_PASS" == "$WEB_PASS2" ]] || { echo "Les mots de passe diffèrent." >&2; exit 1; }
fi
[[ -n "$WEB_PASS" ]] || { echo "Mot de passe vide refusé." >&2; exit 1; }
echo "==> Paquets"
export DEBIAN_FRONTEND=noninteractive
apt-get update -qq
apt-get install -y -qq tmux nginx openssl openssh-client apache2-utils curl ca-certificates
echo "==> ttyd"
if apt-cache show ttyd >/dev/null 2>&1; then
apt-get install -y -qq ttyd
else
# ttyd absent des dépôts Debian 13 : binaire officiel (GitHub), vérifié par somme SHA256
TTYD_VERSION="${TTYD_VERSION:-1.7.7}"
case "$(uname -m)" in
x86_64) TTYD_ASSET="ttyd.x86_64" ;;
aarch64) TTYD_ASSET="ttyd.aarch64" ;;
*) echo "Architecture non gérée : $(uname -m)" >&2; exit 1 ;;
esac
TTYD_URL="https://github.com/tsl0922/ttyd/releases/download/${TTYD_VERSION}"
tmpd="$(mktemp -d)"
curl -fsSL -o "$tmpd/$TTYD_ASSET" "$TTYD_URL/$TTYD_ASSET"
curl -fsSL -o "$tmpd/SHA256SUMS" "$TTYD_URL/SHA256SUMS"
( cd "$tmpd" && grep " ${TTYD_ASSET}\$" SHA256SUMS | sha256sum -c - )
install -m 755 "$tmpd/$TTYD_ASSET" /usr/local/bin/ttyd
rm -rf "$tmpd"
fi
TTYD_BIN="$(command -v ttyd || true)"
[[ -x "$TTYD_BIN" ]] || { echo "ttyd introuvable après installation." >&2; exit 1; }
echo "ttyd : $TTYD_BIN"
echo "==> Utilisateur ${TERM_USER}"
if ! id "$TERM_USER" &>/dev/null; then
useradd -m -s /bin/bash "$TERM_USER"
fi
HOME_DIR="$(getent passwd "$TERM_USER" | cut -d: -f6)"
install -d -m 700 -o "$TERM_USER" -g "$TERM_USER" "$HOME_DIR/.ssh"
install -d -m 755 -o "$TERM_USER" -g "$TERM_USER" "$HOME_DIR/scripts"
if [[ ! -f "$HOME_DIR/.ssh/id_ed25519" ]]; then
sudo_run() { runuser -u "$TERM_USER" -- "$@"; }
sudo_run ssh-keygen -t ed25519 -N "" -C "terminal-web@${FQDN}" -f "$HOME_DIR/.ssh/id_ed25519" -q
fi
if [[ ! -f "$HOME_DIR/.tmux.conf" ]]; then
cat > "$HOME_DIR/.tmux.conf" <<'EOF'
set -g history-limit 50000
set -g mouse on
set -g default-terminal "tmux-256color"
set -g status-style "bg=colour236,fg=colour250"
EOF
chown "$TERM_USER:$TERM_USER" "$HOME_DIR/.tmux.conf"
fi
echo "==> Service ttyd (écoute sur loopback uniquement)"
cat > /etc/systemd/system/ttyd.service <<EOF
[Unit]
Description=ttyd - terminal web (tmux)
After=network.target
[Service]
User=${TERM_USER}
WorkingDirectory=${HOME_DIR}
ExecStart=${TTYD_BIN} -i lo -p ${TTYD_PORT} -W -O tmux new-session -A -s scripts
Restart=on-failure
NoNewPrivileges=yes
PrivateTmp=yes
ProtectSystem=strict
ReadWritePaths=${HOME_DIR}
[Install]
WantedBy=multi-user.target
EOF
echo "==> Certificat TLS"
if [[ ! -f /etc/ssl/certs/terminal.crt ]]; then
openssl req -x509 -newkey ec -pkeyopt ec_paramgen_curve:prime256v1 -nodes -days 825 \
-keyout /etc/ssl/private/terminal.key -out /etc/ssl/certs/terminal.crt \
-subj "/CN=${FQDN}" \
-addext "subjectAltName=DNS:${FQDN},IP:${BIND_IP}" 2>/dev/null
chmod 600 /etc/ssl/private/terminal.key
fi
echo "==> Authentification web"
printf '%s' "$WEB_PASS" | htpasswd -iBc /etc/nginx/.htpasswd_terminal "$WEB_USER" >/dev/null
chown root:www-data /etc/nginx/.htpasswd_terminal
chmod 640 /etc/nginx/.htpasswd_terminal
echo "==> Vhost nginx"
rm -f /etc/nginx/sites-enabled/default
cat > /etc/nginx/sites-available/terminal <<EOF
server {
listen ${BIND_IP}:443 ssl;
server_name ${FQDN};
ssl_certificate /etc/ssl/certs/terminal.crt;
ssl_certificate_key /etc/ssl/private/terminal.key;
ssl_protocols TLSv1.2 TLSv1.3;
# Strictement local : réseau ${LAN_CIDR} ET mot de passe
satisfy all;
allow ${LAN_CIDR};
deny all;
auth_basic "Terminal";
auth_basic_user_file /etc/nginx/.htpasswd_terminal;
add_header X-Frame-Options DENY always;
add_header X-Content-Type-Options nosniff always;
location / {
proxy_pass http://127.0.0.1:${TTYD_PORT};
proxy_http_version 1.1;
proxy_set_header Upgrade \$http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host \$host;
proxy_read_timeout 1d;
}
}
EOF
ln -sf /etc/nginx/sites-available/terminal /etc/nginx/sites-enabled/terminal
nginx -t
echo "==> Démarrage"
systemctl daemon-reload
systemctl enable --now ttyd
systemctl enable nginx
systemctl restart nginx
cat <<EOF
=====================================================================
Terminal prêt : https://${FQDN}/ (identifiant : ${WEB_USER})
1) Certificat à importer dans ton navigateur / système :
/etc/ssl/certs/terminal.crt
(depuis le nœud Proxmox : pct pull <ID> /etc/ssl/certs/terminal.crt ./terminal.crt)
2) Clé publique à ajouter dans authorized_keys des serveurs distants :
$(cat "$HOME_DIR/.ssh/id_ed25519.pub")
3) DNS : ${FQDN} doit pointer vers ${BIND_IP}
=====================================================================
EOF
Le rendre exécutable
1
chmod +x install-terminal.ssh
Le script
install-terminal.shest prêt.
Conteneur LXC Debian 13
LXC en ligne de commande, depuis le shell du nœud Proxmox
1
2
3
pveam update
pveam available --section system | grep debian-13
pveam download local debian-13-standard_13.6-1_amd64.tar.zst # nom exact vu juste au-dessus
Pour ton usage, un conteneur LXC suffit largement : il est plus léger qu’une VM (environ 256 à 512 Mo de RAM, 4 Go de disque) et démarre en quelques secondes. Prends une VM seulement si tu veux l’isolation maximale, puisque cette machine contiendra tes clés SSH vers tous tes serveurs. Dans les deux cas, ce sera séparé de ton serveur public, ce qui était le but.
1
2
3
4
5
6
7
8
9
10
11
pct create 210 local:vztmpl/debian-13-standard_13.6-1_amd64.tar.zst \
--hostname terminal \
--unprivileged 1 \
--cores 1 --memory 512 --swap 256 \
--rootfs local-lvm:4 \
--net0 name=eth0,bridge=vmbr0,ip=192.168.0.206/24,gw=192.168.0.254 \
--nameserver 192.168.0.254 \
--features nesting=1 \
--onboot 1
pct start 210
pct enter 210
À adapter :
210: un ID librelocal-lvm: le nom de ton stockage (visible avecpvesm status, probablement lié à ton LVMtiny-vg)192.168.0.206: une IP libre de ton /24, de préférence réservée en dehors de la plage DHCPgw/nameserver: l’IP de ton routeur ou de ton DNS local192.168.0.254
Si tu préfères l’interface web : Create CT, décoche « Privileged container », choisis le template Debian 13, 1 cœur, 512 Mo, 4 Go de disque, IP statique.
Une fois que le conteneur est démarré et qu’il a un accès réseau. Vérifie ces trois points avant de lancer le script :
1
2
3
pct status 210 # doit afficher : status: running
pct exec 210 -- ip -4 addr show eth0 # l'IP 192.168.0.206 doit apparaître
pct exec 210 -- apt-get update # doit réussir (le script a besoin d'Internet pour installer les paquets)
Si apt-get update échoue, vérifie la passerelle (gw) et le DNS (--nameserver) que tu as donnés à la création du conteneur.
1. En cas d’erreur - Libérer l’ID 210 et vérifier le template (sur le nœud Proxmox)
1
2
pct stop 210 2>/dev/null; pct destroy 210 --purge
pveam list local | grep debian-13
Si pveam list local n’affiche pas debian-13-standard_13.6-1_amd64.tar.zst, le nom du template est différent chez toi : recopie celui qui est affiché dans ta commande.
2. Créer et démarrer
Lance la commande telle quelle avec les bons paramètres
1
2
3
4
5
6
7
8
9
10
11
12
pct create 210 local:vztmpl/debian-13-standard_13.6-1_amd64.tar.zst \
--hostname terminal \
--unprivileged 1 \
--cores 1 --memory 512 --swap 256 \
--rootfs local-lvm:4 \
--net0 name=eth0,bridge=vmbr0,ip=192.168.0.206/24,gw=192.168.0.254 \
--nameserver 192.168.0.254 \
--features nesting=1 \
--onboot 1
pct start 210
# Instruction suivante pour entrer dans conteneur
#pct enter 210
Résultat création
1
2
3
4
5
6
7
Detected container architecture: amd64
Creating SSH host key 'ssh_host_ed25519_key' - this may take some time ...
done: SHA256:uOzYgWwvC2/xTdhU+N1dGm4UU4fKHZXVC2tUO8M0fPQ root@terminal
Creating SSH host key 'ssh_host_rsa_key' - this may take some time ...
done: SHA256:31tn/rJPsYVOpuWJ1NQaNTa12P8eD8AMJjp3uz0oo84 root@terminal
Creating SSH host key 'ssh_host_ecdsa_key' - this may take some time ...
done: SHA256:JmQYlD8UuUcX4rxMYgSNmQp92saL6whte7yQJ8nQXds root@terminal
puis avant d’entrer dans le conteneur teste son réseau depuis le nœud :
1
2
3
pct exec 210 -- ping -c2 192.168.0.254
pct exec 210 -- ping -c2 1.1.1.1
pct exec 210 -- apt-get update
Installation terminal.home.arpa
Le fichier install-terminal.sh doit d’abord être copié sur le nœud (avec scp depuis ton poste, par exemple).
pct enter 210 ouvre le shell du conteneur, mais le script se pousse depuis le nœud. Ne fais donc pas pct enter avant, ou fais exit pour revenir au nœud, puis :
1
2
pct push 210 install-terminal.sh /root/install-terminal.sh
pct exec 210 -- env BIND_IP=192.168.0.206 WEB_USER=tin bash /root/install-terminal.sh
Le script install-terminal.sh demande le mot de passe web, installe tout, puis affiche à la fin la clé publique à copier et le chemin du certificat.
Ce qu’il fait :
- installe ttyd, tmux et nginx
- crée l’utilisateur
termuseravec une clé SSH ed25519 dédiée et un dossier~/scripts - lance ttyd sur la boucle locale uniquement, avec ta session tmux persistante
scripts - génère un certificat pour
terminal.home.arpa - configure nginx en HTTPS, avec mot de passe et accès limité à
192.168.0.0/24
À la fin, le script affiche la clé publique à ajouter aux authorized_keys de tes serveurs et le chemin du certificat à importer dans ton navigateur.
1
2
3
4
5
6
7
8
9
10
11
12
=====================================================================
Terminal prêt : https://terminal.home.arpa/ (identifiant : tin)
1) Certificat à importer dans ton navigateur / système :
/etc/ssl/certs/terminal.crt
(depuis le nœud Proxmox : pct pull <ID> /etc/ssl/certs/terminal.crt ./terminal.crt)
2) Clé publique à ajouter dans authorized_keys des serveurs distants :
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIBvZ/7YyRGE+7gCqRg/NqK5hXs60Cd++OTZAL64etxG8 terminal-web@terminal.home.arpa
3) DNS : terminal.home.arpa doit pointer vers 192.168.0.206
=====================================================================
Le script peut être relancé sans problème s’il s’arrête en cours de route : il ne régénère ni les clés ni le certificat s’ils existent déjà.
Cet avertissement
perl: warning: Setting locale failed.est sans gravité. Le conteneur n’a pas la locale que ton shell lui transmet (probablementfr_FR.UTF-8), donc perl se rabat sur la locale par défaut. Ça n’affecte pas l’installation.
Pour le faire disparaître, depuis le nœud :
1
pct exec 210 -- bash -c 'apt-get install -y -qq locales && sed -i "s/^# *fr_FR.UTF-8/fr_FR.UTF-8/" /etc/locale.gen && locale-gen && update-locale LANG=fr_FR.UTF-8'
Si tu préfères ne pas installer de locale, LC_ALL=C devant tes commandes pct exec suffit à le masquer.
Maintenant que le script est allé au bout, il reste ces étapes :
-
Récupérer le certificat et l’importer dans ton navigateur ou ton système :
1
pct pull 210 /etc/ssl/certs/terminal.crt ./terminal.crt
- DNS : fais pointer
terminal.home.arpavers192.168.0.206. Pour un premier test, une ligne192.168.0.206 terminal.home.arpadans le/etc/hostsde ton poste suffit. - Clé publique : ajoute celle qu’affiche le script (ou
cat /home/termuser/.ssh/id_ed25519.pubdans le conteneur) auxauthorized_keysde tes serveurs distants. - Test : ouvre
https://terminal.home.arpa/, connecte-toi avectinet le mot de passe choisi, puis lance unsshvers un de tes serveurs.
Si la page ne s’affiche pas, vérifie que les services tournent :
1
pct exec 210 -- systemctl status ttyd nginx --no-pager
Avec un certificat valide homearpaCA.crt
1
2
3
pct push 210 homearpaCA.crt /usr/local/share/ca-certificates/homearpaCA.crt
# Mettre à jour le magasin de certificats
pct exec 210 -- bash -c 'update-ca-certificates'
Message
1
2
3
4
5
Updating certificates in /etc/ssl/certs...
rehash: warning: skipping ca-certificates.crt, it does not contain exactly one certificate or CRL
1 added, 0 removed; done.
Running hooks in /etc/ca-certificates/update.d...
done.
Vérifier que le certificat a bien été ajouté en vérifiant qu’un .pem a été créé dans /etc/ssl/certs
1
pct exec 210 -- bash -c 'ls /etc/ssl/certs/ | grep -i homearpa' # Renoie homearpaCA.pem
Installer les outils réseau
1
pct exec 210 -- bash -c 'apt install dnsutils -y'
Tester la résolution
1
pct exec 210 -- bash -c 'dig @192.168.0.205 terminal.home.arpa'
J’ai une erreur car le serveur DNS est 192.168.0.254, il faut le remplacer par 192.168.0.205 (technitium) puis redémarrage
1
2
3
4
5
pct exec 210 -- bash -c 'cat > /etc/resolv.conf << EOF
search tailscale.xoyize.xyz
nameserver 192.168.0.205
EOF'
pct exec 210 -- bash -c 'systemctl reboot'
Tester la résolution
1
pct exec 210 -- bash -c 'dig @192.168.0.205 terminal.home.arpa'
NOERROR, la résolution est OK
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
; <<>> DiG 9.20.29-1~deb13u1-Debian <<>> terminal.home.arpa
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 41805
;; flags: qr aa rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1232
;; QUESTION SECTION:
;terminal.home.arpa. IN A
;; ANSWER SECTION:
terminal.home.arpa. 3600 IN A 192.168.0.205
;; Query time: 1 msec
;; SERVER: 192.168.0.205#53(192.168.0.205) (UDP)
;; WHEN: Wed Oct 21 19:46:40 UTC 2026
;; MSG SIZE rcvd: 63
On supprime la ligne 192.168.0.206 terminal.home.arpa dans le /etc/hosts du poste PC1
On ajoute dans le serveur DNS Technitium une zone A 192.168.0.206 terminal.home.arpa
On exporte le wilcard home.arpa
1
2
3
pct exec 210 -- bash -c 'mkdir -p /etc/ssl/home.arpa/'
pct push 210 /sharenfs/proxmox/partage/wildcard.home.arpa.crt /etc/ssl/home.arpa/wildcard.home.arpa.crt
pct push 210 /sharenfs/proxmox/partage/wildcard.home.arpa.key /etc/ssl/home.arpa/wildcard.home.arpa.key
Passer en mode terminal dans le conteneur
pct enter 210
Modifier la configuration nginx du terminal /etc/nginx/sites-enabled/terminal
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
server {
listen 192.168.0.206:443 ssl;
http2 on;
server_name terminal.home.arpa;
# ssl_certificate /etc/ssl/certs/terminal.crt;
# ssl_certificate_key /etc/ssl/private/terminal.key;
#
ssl_certificate /etc/ssl/home.arpa/wildcard.home.arpa.crt;
ssl_certificate_key /etc/ssl/home.arpa/wildcard.home.arpa.key;
ssl_protocols TLSv1.2 TLSv1.3;
# Strictement local : réseau 192.168.0.0/24 ET mot de passe
satisfy all;
allow 192.168.0.0/24;
deny all;
auth_basic "Terminal";
auth_basic_user_file /etc/nginx/.htpasswd_terminal;
add_header X-Frame-Options DENY always;
add_header X-Content-Type-Options nosniff always;
location / {
proxy_pass http://127.0.0.1:7681;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_read_timeout 1d;
}
}
Le site https://terminal.home.arpa est accessible sans message d’erreur
SSH + Clés
A-Sur le poste appelant
Générer un jeu de clé
1
ssh-keygen -t ed25519 -o -a 100 -f ~/.ssh/terminal-210
Envoyer la clé publique sur le container lxc
1
ssh-copy-id -i ~/.ssh/terminal-210.pub termuser@192.168.0.206
B-Sur le distant
En mode root
1
2
3
4
echo 'Port 55206
PasswordAuthentication no' | tee /etc/ssh/sshd_config.d/10-user.conf
# Redémarrer sshd
systemctl restart sshd
C-Sur le poste appelant
Lancer la connexion SSH avec clé
1
ssh -p 55206 -i ~/.ssh/terminal-210 termuser@192.168.0.206
Modifications pas prises en compte ?
1
2
3
4
5
6
root@terminal:/# cat /etc/ssh/sshd_config.d/10-user.conf
Port 55206
PasswordAuthentication no
root@terminal:/# systemctl restart sshd
root@terminal:/# ss -lntp | grep ssh
LISTEN 0 4096 *:22 *:* users:(("sshd",pid=992,fd=3),("systemd",pid=1,fd=45))
Le résultat indique que systemd garde le port 22 ouvert : ss montre systemd parmi les processus qui écoutent. Modifier sshd_config ne suffit donc pas si SSH est lancé par ssh.socket.
Dans le conteneur, configure le socket systemd pour écouter sur 55206 :
1
2
3
4
5
6
7
8
9
10
11
12
mkdir -p /etc/systemd/system/ssh.socket.d
cat > /etc/systemd/system/ssh.socket.d/listen.conf <<'EOF'
[Socket]
ListenStream=
ListenStream=55206
EOF
systemctl daemon-reload
systemctl stop ssh.service
systemctl restart ssh.socket
systemctl status ssh.socket --no-pager
Vérifie ensuite :
1
ss -lntp | grep -E 'ssh|55206'
Tu devrais voir *:55206
1
LISTEN 0 4096 *:55206 *:* users:(("systemd",pid=1,fd=46))
Vérifie aussi la configuration effective de sshd :
1
sshd -T | grep '^port '
Si elle affiche encore port 22, cherche une autre directive Port qui surcharge le fichier :
1
grep -Rni '^[[:space:]]*Port' /etc/ssh/sshd_config /etc/ssh/sshd_config.d/
Garde ta session actuelle ouverte et teste une nouvelle connexion avant de la fermer :
1
ssh -p 55206 utilisateur@IP_DU_CONTENEUR
Si tu as activé le pare-feu Proxmox ou celui du conteneur, autorise aussi le trafic TCP entrant sur 55206.
Utiliser csv existant
deux scripts qui adaptent ton inventaire CSV au terminal web
Poste PC1
Ton CSV se réutilise tel quel dans le conteneur, avec une différence : au lieu de tes ~20 clés, le terminal web utilise une seule clé dédiée (celle que le script d’installation a créée). Tes clés habituelles ne quittent donc pas ton poste. J’ai préparé deux scripts, testés sur un CSV d’exemple (la simulation et la génération de la config fonctionnent, mais je n’ai évidemment pas pu les essayer sur tes serveurs).
Sur ton poste : déposer la clé du terminal sur tes serveurs (deploy-key.sh)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
#!/usr/bin/env bash
# À lancer sur TON POSTE (qui a déjà accès à tous les serveurs avec ses clés habituelles).
# Ajoute la clé publique du terminal web dans ~/.ssh/authorized_keys de chaque serveur du CSV.
#
# Par défaut : SIMULATION (n'écrit rien). Ajouter --apply pour exécuter.
#
# Usage :
# PUB=./terminal.pub ./deploy-key.sh # simulation, tous les serveurs
# PUB=./terminal.pub ./deploy-key.sh --apply cwwk proxtin # seulement ces alias
#
# Variables :
# CSV chemin du CSV (défaut : ~/terminalweb.csv)
# PUB clé publique à déposer (obligatoire)
# FROM_LAN IP du conteneur (ex. 192.168.0.206) : limite la clé à cette IP,
# uniquement pour les serveurs en 192.168.0.x
set -euo pipefail
CSV="${CSV:-$HOME/.ssh/terminalweb.csv}"
PUB="${PUB:-$HOME/.ssh/terminal.pub}"
FROM_LAN="${FROM_LAN:-192.168.0.206}"
APPLY=0
TARGETS=()
for a in "$@"; do
case "$a" in
--apply) APPLY=1 ;;
*) TARGETS+=("$a") ;;
esac
done
[[ -f "$CSV" ]] || { echo "CSV introuvable : $CSV" >&2; exit 1; }
[[ -f "$PUB" ]] || { echo "Clé publique introuvable : $PUB" >&2; exit 1; }
PUBLINE="$(head -n1 "$PUB")"
[[ "$PUBLINE" == ssh-ed25519\ * ]] || { echo "Format de clé inattendu dans $PUB" >&2; exit 1; }
KEYBODY="$(awk '{print $2}' <<<"$PUBLINE")"
want() {
[[ ${#TARGETS[@]} -eq 0 ]] && return 0
local t
for t in "${TARGETS[@]}"; do [[ "$t" == "$1" ]] && return 0; done
return 1
}
ok=0; ko=0; skip=0
while IFS=';' read -r name user host port key _local _dist _ping _opt ipv _rest; do
[[ -n "${name// }" ]] || continue
want "$name" || continue
if [[ -z "$key" ]]; then
echo "[$name] pas de clé dans le CSV : ignoré"
skip=$((skip+1)); continue
fi
opts="no-agent-forwarding,no-X11-forwarding,no-port-forwarding"
if [[ -n "$FROM_LAN" && "$host" == 192.168.0.* ]]; then
opts="from=\"$FROM_LAN\",$opts"
fi
line="$opts $PUBLINE"
remote="umask 077; mkdir -p ~/.ssh; touch ~/.ssh/authorized_keys; grep -qF -- '$KEYBODY' ~/.ssh/authorized_keys || echo '$line' >> ~/.ssh/authorized_keys"
famopt=(); [[ "$ipv" == 4 ]] && famopt=(-4); [[ "$ipv" == 6 ]] && famopt=(-6)
if [[ $APPLY -eq 0 ]]; then
echo "[$name] (simulation) ssh ${famopt[*]} -p $port -i ~/.ssh/$key $user@$host"
continue
fi
if ssh "${famopt[@]}" -p "$port" -i "$HOME/.ssh/$key" -o IdentitiesOnly=yes \
-o BatchMode=yes -o ConnectTimeout=10 "$user@$host" "$remote"; then
echo "[$name] OK"; ok=$((ok+1))
else
echo "[$name] ÉCHEC"; ko=$((ko+1))
fi
done < <(tail -n +2 "$CSV" | tr -d '\r')
if [[ $APPLY -eq 1 ]]; then
echo "Terminé : $ok OK, $ko échec(s), $skip ignoré(s)"
else
echo "Simulation terminée. Relancer avec --apply pour déposer la clé."
fi
Il lit ton CSV et utilise les clés et ports habituels pour ajouter la clé publique du terminal dans authorized_keys. Par défaut c’est une simulation qui n’écrit rien.
1
2
3
4
5
pct pull 210 /home/termuser/.ssh/id_ed25519.pub ./terminal.pub # sur le nœud Proxmox, puis copie-le sur ton poste
PUB=./terminal.pub CSV=~/terminalweb.csv ./deploy-key.sh # simulation
PUB=./terminal.pub CSV=~/terminalweb.csv ./deploy-key.sh --apply cwwk # un seul serveur pour tester
PUB=./terminal.pub CSV=~/terminalweb.csv ./deploy-key.sh --apply # tous
La clé est déposée avec no-port-forwarding,no-agent-forwarding,no-X11-forwarding. Avec FROM_LAN=192.168.0.206 devant la commande, elle est en plus limitée à l’IP du conteneur, mais seulement pour les serveurs en 192.168.0.x : pour les VPS distants, la connexion arrive depuis ton IP publique, donc cette restriction ne s’y applique pas.
Conteneur LXC
Dans le conteneur : générer ~/.ssh/config depuis le CSV (gen-ssh-config.sh)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#!/usr/bin/env bash
# À lancer dans le conteneur terminal, en tant que termuser.
# Génère ~/.ssh/config à partir du CSV des connexions :
# Alias;User;IPouDomain;Port_SSH;SSH_Key;Local;Distant;Ping;Opt;IP4_6
# Toutes les connexions utilisent la clé dédiée du terminal (colonne SSH_Key ignorée).
#
# Usage : gen-ssh-config.sh [terminalweb.csv]
set -euo pipefail
CSV="${1:-$HOME/terminalweb.csv}"
KEY="${KEY:-$HOME/.ssh/id_ed25519}"
OUT="${OUT:-$HOME/.ssh/config}"
[[ -f "$CSV" ]] || { echo "CSV introuvable : $CSV" >&2; exit 1; }
install -d -m 700 "$(dirname "$OUT")"
{
echo "# Généré depuis $CSV le $(date -Is) - ne pas éditer à la main"
echo
tail -n +2 "$CSV" | tr -d '\r' | while IFS=';' read -r name user host port _key _local _dist _ping _opt ipv _rest; do
[[ -n "${name// }" ]] || continue
echo "Host $name"
echo " HostName $host"
echo " User $user"
echo " Port $port"
case "$ipv" in
4) echo " AddressFamily inet" ;;
5) echo " AddressFamily inet6" ;;
esac
echo
done
cat <<EOF
Host *
IdentityFile $KEY
IdentitiesOnly yes
StrictHostKeyChecking accept-new
ServerAliveInterval 30
ConnectTimeout 10
EOF
} > "$OUT.tmp"
chmod 600 "$OUT.tmp"
mv "$OUT.tmp" "$OUT"
# Compatibilité avec l'habitude "sshm alias"
if ! grep -q "alias sshm=" "$HOME/.bashrc" 2>/dev/null; then
echo "alias sshm='ssh'" >> "$HOME/.bashrc"
fi
echo "OK : $(grep -c '^Host ' "$OUT") entrées dans $OUT (dont Host *)"
A exécuter sur proxmox Tiny en mode su
1
2
3
4
5
pct push 210 terminalweb.csv /home/termuser/terminalweb.csv
pct push 210 gen-ssh-config.sh /home/termuser/gen-ssh-config.sh
pct exec 210 -- chown termuser:termuser /home/termuser/terminalweb.csv /home/termuser/gen-ssh-config.sh
pct exec 210 -- runuser -u termuser -- bash /home/termuser/gen-ssh-config.sh
# Fin exécution -> OK : 14 entrées dans /home/termuser/.ssh/config (dont Host *)
Ensuite, dans le terminal web, ssh cwwk ouvre la session avec le bon utilisateur, port et famille d’adresse (IPv4 ou IPv6). L’alias sshm est aussi créé, donc sshm cwwk fonctionne comme sur ton poste. À relancer à chaque modification du CSV.
Points d’attention avec ton inventaire
skrimeest en IPv6 uniquement : le conteneur doit avoir une adresse IPv6 (par exempleip6=autosurnet0si ton réseau en distribue), sinon cette connexion échouera.ds723n’a pas de clé dans le CSV :deploy-key.shl’ignore, il faudra y ajouter la clé à la main.acer,ds723,um890pointent vers82.64.153.201, qui semble être ton IP publique. Depuis ton réseau local, ça ne fonctionne que si ton routeur gère le NAT loopback. Sinon, utilise leur IP locale dans le conteneur.- Première connexion : la config contient
StrictHostKeyChecking accept-new, donc les nouveaux serveurs sont acceptés automatiquement et une clé hôte modifiée reste refusée. - La complétion des alias avec Tab demande
apt install bash-completiondans le conteneur.
Accès conteneur LXC en ligne de commande
Identifiant et mot de passe du terminal web
Le login (par exemple tin) et le mot de passe que tu as choisis dans le script ne servent qu’à ouvrir la page : c’est l’authentification nginx. Ce n’est pas un compte du système. Une fois sur la page, tu es directement dans un shell sous termuser, sans autre saisie, et termuser n’a pas de mot de passe ni de droits root.
Console d’administration du conteneur (root)
- Depuis le nœud Proxmox :
pct enter 210. Aucun mot de passe n’est demandé, puisque tu es déjà root sur le nœud. C’est le moyen le plus simple. - Depuis l’interface web de Proxmox (CT 210, onglet Console) : un mot de passe root est demandé. Comme tu as créé le conteneur sans
--password, root n’en a pas, donc la connexion échouera. Tu peux en définir un depuis le nœud :
1
pct exec 210 -- passwd root
Prends un mot de passe long et unique, puisque ce compte contrôle tout le conteneur, y compris la clé SSH du terminal. Si tu n’utilises que pct enter, tu peux laisser root sans mot de passe, ce qui est même plus sûr.
Il n’y a pas de serveur SSH dans le conteneur, et je te conseille de ne pas en installer : l’administration passe par le nœud, et le terminal web reste le seul point d’entrée côté réseau.
Depuis le shell root du conteneur, tu passes en termuser sans mot de passe :
1
su - termuser
Le tiret charge son environnement complet (son HOME, son .bashrc, etc.). exit te ramène en root.
Autres variantes :
1
2
3
4
5
# Une seule commande en tant que termuser
runuser -l termuser -c 'ssh cwwk uptime'
# Depuis le nœud Proxmox, directement en termuser, sans passer par root dans le conteneur
pct exec 210 -- runuser -l termuser
Retrouver la session tmux du terminal web
La session scripts ouverte par la page web appartient à termuser. Pour la rejoindre depuis la console, en tant que termuser :
1
tmux attach -t scripts
Tu verras alors la même session que dans le navigateur, et vous pouvez écrire dedans à deux (pour la quitter sans la fermer : Ctrl+b puis d). Si tu fais cette commande en root, tmux ne trouvera rien : chaque utilisateur a ses propres sessions.
Pour éditer un fichier de termuser depuis root (par exemple connexions.csv), pense à remettre les droits ensuite, sinon termuser ne pourra plus le modifier :
1
chown termuser:termuser /home/termuser/connexions.csv
Utiliser tmux
Oui, c’est même le cas d’usage idéal. Le terminal web ouvre simplement ta session tmux scripts, donc tout ce que tu fais dans tmux fonctionne, et la session continue de tourner si tu fermes l’onglet ou si ton navigateur plante. Tu la retrouves à la prochaine ouverture de la page.
Raccourcis de base (préfixe Ctrl+b, puis la touche)
| Action | Touches |
|---|---|
| Nouvelle fenêtre | Ctrl+b c |
| Fenêtre suivante / précédente | Ctrl+b n / p |
| Aller à la fenêtre n° 3 | Ctrl+b 3 |
| Lister les fenêtres | Ctrl+b w |
| Renommer la fenêtre | Ctrl+b , |
| Couper en deux (haut/bas) | Ctrl+b " |
| Couper en deux (gauche/droite) | Ctrl+b % |
| Changer de volet | Ctrl+b + flèches |
| Fermer fenêtre ou volet | exit ou Ctrl+b & |
Avec la souris activée dans la config, tu peux aussi cliquer sur un volet pour le sélectionner et faire glisser les séparations.
Ouvrir une fenêtre par serveur
Dans le terminal web, ajoute cette fonction à ~/.bashrc :
1
2
3
4
cat >> ~/.bashrc <<'EOF'
sshw() { for h in "$@"; do tmux new-window -n "$h" "ssh $h; exec bash"; done; }
EOF
source ~/.bashrc
Ensuite sshw cwwk proxtin alpine01 ouvre trois fenêtres nommées, chacune connectée à son serveur. Le exec bash garde la fenêtre ouverte si la connexion tombe.
Lancer la même commande sur plusieurs serveurs
Ouvre un volet par serveur, puis active la synchronisation : Ctrl+b puis : et tape setw synchronize-panes on. Tout ce que tu tapes va dans tous les volets en même temps. Désactive avec setw synchronize-panes off. C’est pratique pour tes vérifications, mais fais attention aux commandes qui modifient quelque chose.
Deux détails propres au navigateur
- Copier-coller : avec la souris activée dans tmux, une sélection à la souris va dans le tampon de tmux, pas dans le presse-papiers du navigateur. Maintiens
Shiftpendant la sélection pour utiliser la sélection du navigateur. - Plusieurs onglets du navigateur : ils s’attachent tous à la même session tmux et affichent la même fenêtre active. Pour que chaque onglet puisse naviguer indépendamment, remplace dans
/etc/systemd/system/ttyd.servicela fin de la commandetmux new-session -A -s scriptspartmux new-session -t scripts, puissystemctl daemon-reload && systemctl restart ttyd. Chaque onglet crée alors sa propre session, groupée avecscripts, qui partage les mêmes fenêtres mais garde sa fenêtre active. Les sessions groupées se ferment avec l’onglet, alors que les fenêtres restent dansscripts. Pour quescriptsexiste toujours, lance-la une fois en arrière-plan avectmux new-session -d -s scripts.
Script tmux-grille.sh
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
#!/bin/bash
# Grille 3x3 de sessions SSH dans tmux, lancée par ttyd (terminal web).
# Contrairement à la version du poste, la session n'est PAS détruite à chaque ouverture
# de la page : si elle existe déjà, on s'y rattache simplement (les ssh restent actifs).
#
# Reconstruire la grille : tmux kill-session -t YannSessionD puis recharger la page
# (ou : tmux-grille.sh --rebuild)
#
# Disposition (numéro de volet) :
# | 0 | 3 | 6 |
# | 1 | 4 | 7 |
# | 2 | 5 | 8 |
S="${SESSION:-YannSessionD}"
SSH_CMD="${SSH_CMD:-ssh}" # pour un test à blanc : SSH_CMD=echo
W="$S:YannTermD"
# Alias (voir connexions.csv), dans l'ordre des volets 0 à 8
HOSTS=(cwwk yannir yannig xoyaz skrime yiak sxb proxmox proxtin)
if [[ "${1:-}" == "--rebuild" ]]; then
tmux kill-session -t "$S" 2>/dev/null
fi
if ! tmux has-session -t "$S" 2>/dev/null; then
tmux new-session -d -s "$S" -n YannTermD -x 240 -y 60
# 3 colonnes
tmux split-window -h -l 33% -t "$W.0"
tmux split-window -h -l 50% -t "$W.0"
# 3 lignes dans chaque colonne
tmux split-window -v -l 33% -t "$W.0"
tmux split-window -v -l 50% -t "$W.0"
tmux split-window -v -l 33% -t "$W.3"
tmux split-window -v -l 50% -t "$W.3"
tmux split-window -v -l 33% -t "$W.6"
tmux split-window -v -l 50% -t "$W.6"
# Nom de l'alias affiché sur le bord de chaque volet
tmux set-option -w -t "$W" pane-border-status top
tmux set-option -w -t "$W" pane-border-format ' #{@host} '
for i in "${!HOSTS[@]}"; do
tmux set-option -p -t "$W.$i" @host "${HOSTS[$i]}"
tmux send-keys -t "$W.$i" "$SSH_CMD ${HOSTS[$i]}" C-m
done
tmux select-pane -t "$W.0"
fi
exec tmux attach-session -t "$S"
Oui, il s’adapte facilement. J’ai écrit tmux-grille.sh, qui reprend ta grille 3x3 (une seule fenêtre, neuf volets) avec les mêmes serveurs dans le même ordre. Je n’ai pas pu lancer tmux ici : la syntaxe est vérifiée et j’ai suivi à la main la numérotation des volets, qui correspond à ton schéma, mais fais un test à blanc avant (voir plus bas).
Ce qui change par rapport à ton script
- Pas de destruction de la session à chaque lancement. Ton script la supprime puis la recrée, ce qui, avec le terminal web, rouvrirait les 9 connexions SSH à chaque rechargement de page. Ici, si la session existe, on s’y rattache simplement. Pour reconstruire la grille :
tmux-grille.sh --rebuild, outmux kill-session -t YannSessionDpuis recharger la page. - Bash au lieu de
sh: ton[[ ... ]]ne marche pas avecshsur Debian (c’estdash). - Cibles explicites (
-t YannSessionD:YannTermD.N) au lieu deselectp, ce qui évite les erreurs si une autre session tmux existe. - Nom de l’alias affiché en haut de chaque volet, pratique quand les prompts se ressemblent.
ssh aliasau lieu desshm alias, qui ne dépend pas de l’alias du.bashrc(c’est équivalent).
Installation
D’abord le fichier et un test à blanc (depuis le nœud, avec tmux-grille.sh copié dessus) :
1
2
3
pct push 210 tmux-grille.sh /home/termuser/tmux-grille.sh
pct exec 210 -- chown termuser:termuser /home/termuser/tmux-grille.sh
pct exec 210 -- chmod 755 /home/termuser/tmux-grille.sh
Puis tu peux tester sans connexion SSH depuis la console du conteneur :
1
pct exec 210 -- runuser -l termuser -c 'SSH_CMD=echo SESSION=TestGrille /home/termuser/tmux-grille.sh'
Il faut un vrai terminal pour l’attache :
utilise plutôt
1
pct enter 210
puis
1
su - termuser
puis
1
SSH_CMD=echo SESSION=TestGrille ./tmux-grille.sh
Tu dois voir les neuf noms d’alias apparaître dans la bonne disposition
Ctrl+b d pour sortir
puis
1
tmux kill-session -t TestGrille # pour nettoyer.
Ensuite, fais lancer le script par ttyd à la place de la session simple. En root dans le conteneur (pct enter 210) :
1
2
sed -i 's|^ExecStart=.*|ExecStart=/usr/local/bin/ttyd -i lo -p 7681 -W -O -t fontSize=13 /home/termuser/tmux-grille.sh|' /etc/systemd/system/ttyd.service
systemctl daemon-reload && systemctl restart ttyd
ttyd n’est pas installé par apt
Si ton ttyd est installé par apt, remplace /usr/local/bin/ttyd par /usr/bin/ttyd (grep ExecStart /etc/systemd/system/ttyd.service pour vérifier avant). L’option fontSize=13 réduit la police pour que neuf volets tiennent à l’écran : ajuste selon la taille de ton navigateur.
À savoir
- Au premier chargement, le script lance les neuf
sshen même temps : les nouveaux serveurs sont acceptés automatiquement (accept-new), mais un serveur dont la clé du terminal n’a pas été déposée affichera une demande de mot de passe dans son volet. - Le volet
skrimedemande une connectivité IPv6 dans le conteneur, comme évoqué plus haut. - Si tu ouvres la page dans deux onglets, tous deux affichent la même grille et partagent la même fenêtre active (c’est le comportement normal d’un
tmux attachpartagé). - Pour retrouver un shell libre en plus de la grille, ouvre une nouvelle fenêtre tmux avec
Ctrl+bc
Remplacer Ctrl+b par Ctrl+space
Ni la touche Fn ni la touche Windows ne sont utilisables. Fn est gérée par le clavier lui-même (le navigateur ne la voit jamais), et la touche Windows (Super) est interceptée par ton bureau KDE avant d’atteindre Firefox. Il reste les combinaisons avec Ctrl et les touches F1 à F12.
Préfixe Ctrl+Espace
Firefox ne s’en sert pas. Sur un bureau KDE, il peut parfois entrer en conflit avec le changement de méthode de saisie, mais c’est rare. Dans le terminal web (ou su - termuser) :
1
2
3
4
5
6
cat >> ~/.tmux.conf <<'EOT'
set -g prefix C-Space
unbind C-b
bind C-Space send-prefix
EOT
tmux source-file ~/.tmux.conf
Ensuite : Ctrl+Espace, relâche, puis c pour une nouvelle fenêtre, 0 pour revenir à la grille, n / p pour passer à la suivante / précédente.
Si Ctrl+Espace ne répond pas, utilise Ctrl+a, le préfixe classique de GNU Screen. Remplace alors C-Space par C-a dans les trois lignes. Pour envoyer un vrai Ctrl+a au shell (début de ligne), tape Ctrl+a deux fois. Évite Ctrl+q, Ctrl+w, Ctrl+t ou Ctrl+n, que Firefox garde pour lui, et Alt+1 à Alt+9, qui changent d’onglet Firefox sous Linux.
Raccourcis directs avec les touches F (sans préfixe)
Pour les trois actions les plus courantes, tu peux te passer du préfixe. Ajoute ces lignes au même fichier :
1
2
3
bind -n F2 new-window
bind -n F8 previous-window
bind -n F9 next-window
Puis tmux source-file ~/.tmux.conf. Je choisis F2, F8 et F9 parce que Firefox ne s’en sert pas, contrairement à F3, F5, F6, F7, F10, F11 et F12. Tu perds en contrepartie ces trois touches dans les applications que tu lances dans tmux (par exemple dans htop).
À la souris
La souris est activée dans tmux : un clic sur le nom d’une fenêtre dans la barre du bas la sélectionne. Avec tmux 3.x, un clic droit sur cette barre ouvre normalement un menu qui permet aussi de créer une fenêtre.
Si tu avais déjà ajouté des lignes prefix C-a à ~/.tmux.conf lors d’un essai précédent, supprime-les avec nano ~/.tmux.conf pour éviter les doublons.
Compris : ttyd ne lancera plus la grille au démarrage, il ouvrira une session tmux normale. La grille devient une commande que tu lances quand tu en as besoin, et que tu peux fermer. Comme la page web est occupée par la grille, fais les premières étapes depuis le nœud Proxmox.
1. Arrêter la grille et rétablir le démarrage normal
1
2
pct exec 210 -- runuser -l termuser -c 'tmux kill-session -t YannSessionD'
pct exec 210 -- grep ExecStart /etc/systemd/system/ttyd.service
Note le chemin du binaire ttyd affiché par le grep (/usr/local/bin/ttyd ou /usr/bin/ttyd), puis :
1
2
3
pct exec 210 -- sed -i 's|^ExecStart=.*|ExecStart=/usr/local/bin/ttyd -i lo -p 7681 -W -O -t fontSize=14 tmux new-session -A -s scripts|' /etc/systemd/system/ttyd.service
pct exec 210 -- systemctl daemon-reload
pct exec 210 -- systemctl restart ttyd
Remplace /usr/local/bin/ttyd par le chemin que tu as relevé si besoin. En rechargeant la page, tu obtiens un terminal tmux vide (session scripts).
2. La grille à la demande
J’ai réécrit tmux-grille.sh : lancé depuis une fenêtre tmux, il ouvre une nouvelle fenêtre tmux nommée Grille, avec les neuf volets en 3x3 égaux, à côté de tes autres fenêtres. S’il existe déjà, il y retourne.
Je n’ai pas pu tester tmux ici : essaie-le chez toi avec SSH_CMD=echo avant de lancer les vraies connexions. Pousse-le dans le conteneur (même méthode que précédemment) et ajoute deux alias dans ~/.bashrc de termuser :
1
2
3
4
5
cat >> ~/.bashrc <<'EOT'
alias grille='~/tmux-grille.sh'
alias grille-stop='~/tmux-grille.sh --close'
EOT
source ~/.bashrc
Utilisation, dans le terminal web :
grilleouvre la grillegrille-stopla ferme avec ses neuf connexions SSH. Lance-le depuis une autre fenêtre tmux que la grille, par exemple une fenêtre ouverte avecF2(voir ci-dessous)- tu peux aussi la fermer depuis la grille elle-même, avec
F4(voir ci-dessous)
3. Raccourcis sans préfixe (comme le préfixe pose problème)
1
2
3
4
5
6
7
cat >> ~/.tmux.conf <<'EOT'
bind -n F2 new-window
bind -n F4 confirm-before -p "Fermer la fenêtre ? (y/n)" kill-window
bind -n F8 previous-window
bind -n F9 next-window
EOT
tmux source-file ~/.tmux.conf
Résumé des commandes
grilleouvre la grillegrille-stopla ferme avec ses neuf connexions SSH. Lance-le depuis une autre fenêtre tmux que la grille, par exemple une fenêtre ouverte avecF2(voir ci-dessous)- tu peux aussi la fermer depuis la grille elle-même, avec
F4(voir ci-dessous) F2ouvre une fenêtre videF8Fenêtre précédenteF9Fenêtre suivanteF4ferme la fenêtre courante après confirmation.(supprimer les trois lignes du préfixeCtrl+Espacede~/.tmux.confpour revenir àCtrl+b)
Dans un terminal classique (TUI)
- dialog / whiptail : menus, boutons Oui/Non, listes de choix, en shell pur. Pratique pour un petit lanceur de scripts.
- fzf : menu de sélection fuzzy, très rapide à mettre en place (
printf '%s\n' "sauvegarde" "mise à jour" | fzf), puis uncasesur le choix. - gum (Charm) : boutons, menus, confirmations avec un rendu moderne, utilisable directement dans un script bash.
- Textual (Python) ou Bubble Tea (Go) : si tu veux une vraie interface avec boutons cliquables à la souris, plusieurs panneaux, etc.
- tmux / Midnight Commander / ranger : les raccourcis ou la barre de boutons (F1-F10 dans mc) servent aussi de « boutons » configurables.
Dans une fenêtre terminal graphique
- Konsole (KDE) : possibilité d’ajouter des profils, des actions et une barre d’outils personnalisée, mais pas de vrais boutons de commande intégrés.
- Tilix, Terminator : pas de boutons non plus, mais des raccourcis et des profils.
- Zenity / yad / kdialog : tu lances une petite fenêtre avec des boutons depuis un script, qui exécute ensuite la commande.
Dans un terminal web
Si c’est pour ton terminal web local : une page HTML avec des boutons qui envoient une commande à ttyd, ou qui appellent un petit backend lançant le script en SSH, fonctionne bien. Avec ttyd, on peut aussi passer des arguments par URL pour lancer un script précis.
Cas simple pour démarrer
1
2
3
4
5
6
7
8
9
10
11
#!/bin/bash
choix=$(whiptail --title "Actions" --menu "Que faire ?" 15 50 4 \
1 "Mise à jour" \
2 "Sauvegarde" \
3 "Redémarrer le service" 3>&1 1>&2 2>&3)
case $choix in
1) sudo apt update && sudo apt upgrade ;;
2) ./sauvegarde.sh ;;
3) sudo systemctl restart monservice ;;
esac