Transformer une Debian minimale en proxy SSH transparent qui:
lab_rsagateway_rsa172.30.0.0/24docker-compose.yamlC'est quoi: Fichier d'orchestration Docker
Fait quoi:
Crée 3 services:
- gateway (Dockerfile: gateway/Dockerfile)
- dest1 (Dockerfile: dest/Dockerfile)
- dest2 (Dockerfile: dest/Dockerfile)
Les relie par un réseau privé: 172.30.0.0/24
Expose port 2222 (gateway SSH)
Pourquoi c'est important: Sans ce fichier, les conteneurs ne peuvent pas communiquer
gateway/DockerfileC'est quoi: Fichier de construction de l'image gateway
Fait quoi:
Stage 1 (Builder):
FROM golang:1.24
Clone sshproxy v2.1.0 depuis GitHub
Compile les binaires sshproxy en Go
Stage 2 (Final):
FROM debian:bookworm-slim
Copie binaires compilés
Installe sshd
Crée compte testuser
Copie clé gateway_rsa
CRUCIAL: chown testuser /etc/sshproxy/gateway_rsa ← Sinon: Permission denied!
Copie authorized_keys (lab_rsa.pub)
Copie sshd_config
Copie sshproxy.yaml
Copie sshproxy-wrapper.sh
Pourquoi c'est important:
gateway/sshd_configC'est quoi: Configuration du daemon SSH gateway
Fait quoi:
PasswordAuthentication no ← Clé uniquement
PubkeyAuthentication yes
PermitRootLogin no ← Pas de root
AllowTcpForwarding no ← Pas de tunneling
X11Forwarding no
ForceCommand /usr/sbin/sshproxy-wrapper ← ⭐⭐⭐ LIGNE MAGIQUE ⭐⭐⭐
Toute connexion SSH lancera d'abord: /usr/sbin/sshproxy-wrapper
Pourquoi c'est important:
ForceCommand est ce qui rend le proxy TRANSPARENT
Chaque SSH est interceptée et proxifiée
gateway/sshproxy.yamlC'est quoi: Configuration du proxy sshproxy
Fait quoi:
Destinations:
- 172.30.0.11:22 (dest1)
- 172.30.0.12:22 (dest2)
Sélection: random ← Chaque connexion choisit aléatoirement
Commande SSH pour rebond:
ssh -v -tt -i /etc/sshproxy/gateway_rsa \
-o StrictHostKeyChecking=no \
-o UserKnownHostsFile=/dev/null \
testuser@DESTINATION
-tt ← ⭐⭐⭐ CRUCIAL POUR SHELL INTERACTIF ⭐⭐⭐
Pourquoi c'est important:
-tt alloue un pseudo-terminal → shell interactif possible-tt: commandes OK, mais shell freezegateway/sshproxy-wrapper.shC'est quoi: Script shell wrapper
Fait quoi:
if [ -z "$SSH_ORIGINAL_COMMAND" ]; then
# Pas de commande (shell interactif)
exec /usr/sbin/sshproxy
else
# Commande fournie
exec /usr/sbin/sshproxy
fi
Pourquoi c'est important:
ForceCommand du sshddest/DockerfileC'est quoi: Fichier de construction des images dest1 et dest2
Fait quoi:
FROM debian:bookworm-slim
RUN apt-get install openssh-server ← Juste sshd
Crée testuser
Copie gateway_rsa.pub dans authorized_keys
Copie dest/sshd_config
Lance sshd
Pourquoi c'est important:
dest/sshd_configC'est quoi: Configuration SSH des destinations
Fait quoi:
Même config que gateway SAUF:
PAS DE ForceCommand ← Clé différence!
SSH fonctionne normalement
Pourquoi c'est important: Les destinations sont des vraies machines SSH Pas d'interception (pas de proxy)
keys/lab_rsa + keys/lab_rsa.pubC'est quoi: Paire de clés SSH Windows → Gateway
Fait quoi:
lab_rsa (privée):
Fichier: C:\Users\user\.ssh\lab_rsa
Usage: ssh -i lab_rsa -p 2222 localhost
lab_rsa.pub (publique):
Copié dans: gateway:/home/testuser/.ssh/authorized_keys
Signature: c'est une connexion autorisée
Génération:
ssh-keygen -t ed25519 -f keys/lab_rsa -N ""
Pourquoi c'est important:
keys/gateway_rsa + keys/gateway_rsa.pubC'est quoi: Paire de clés SSH Gateway → Destinations
Fait quoi:
gateway_rsa (privée):
Compilée dans: /etc/sshproxy/gateway_rsa
Usage: sshproxy l'utilise pour rebond auto
gateway_rsa.pub (publique):
Copié dans: dest1/dest2:/home/testuser/.ssh/authorized_keys
Signature: c'est la gateway qui demande d'entrer
Génération:
ssh-keygen -t ed25519 -f keys/gateway_rsa -N ""
Pourquoi c'est important:
Step 1: Vous lancez
ssh -p 2222 -i keys/lab_rsa testuser@localhost
Step 2: Docker reçoit sur port 2222
Remap vers 172.30.0.10:22 (gateway sshd)
Step 3: Gateway sshd accepte
Lit: /home/testuser/.ssh/authorized_keys
Cherche: lab_rsa.pub
✓ Trouve et valide
Step 4: sshd lance ForceCommand
Exécute: /usr/sbin/sshproxy-wrapper
Step 5: Wrapper détecte
SSH_ORIGINAL_COMMAND = "hostname" (si vous aviez tapé)
Lance: /usr/sbin/sshproxy
Step 6: sshproxy lit config
Lit: /etc/sshproxy/sshproxy.yaml
Voit: destinations = [172.30.0.11, 172.30.0.12]
Choisit: random → 172.30.0.12 (dest2)
Step 7: sshproxy exécute rebond
ssh -tt -i /etc/sshproxy/gateway_rsa testuser@172.30.0.12
Step 8: dest2 sshd accepte
Lit: /home/testuser/.ssh/authorized_keys
Cherche: gateway_rsa.pub
✓ Trouve et valide
Step 9: dest2 exécute shell/commande
Résultat retourné via gateway vers vous
Step 10: Vous voyez
testuser@dest2:~$
(ou résultat de la commande)
Fichier: gateway/sshd_config
Ligne: ForceCommand /usr/sbin/sshproxy-wrapper
Effet: TOUTE connexion SSH est interceptée et proxifiée
Fichier: gateway/sshproxy.yaml
Args: "-tt"
Effet: Alloue PTY sur destination → shell interactif possible
Sans ça: exit 255 ou freeze
Layer 1: lab_rsa (client→gateway)
Layer 2: gateway_rsa (gateway→dest)
Effet: Authentification multi-niveaux
# 1. Générer clés (si pas fait)
ssh-keygen -t ed25519 -f keys/lab_rsa -N ""
ssh-keygen -t ed25519 -f keys/gateway_rsa -N ""
# 2. Lancer infra
docker compose up -d
# 3. Tester
ssh -p 2222 -i keys/lab_rsa testuser@localhost 'hostname'
# Devrait afficher: dest1 ou dest2
# 4. Tester shell interactif
echo "hostname" | ssh -p 2222 -i keys/lab_rsa testuser@localhost
# Devrait afficher: testuser@dest1:~$ hostname ↵ dest1
# 5. Vérifier round-robin
for i in {1..5}; do \
ssh -p 2222 -i keys/lab_rsa testuser@localhost hostname; \
done
# Mélange de dest1 et dest2
| Concept | Explication |
|---|---|
| ForceCommand | Intercepte chaque SSH sur la gateway |
| -tt flag | Permet l'interactivité sur destination |
| 2 clés SSH | 2 authentifications (client→gateway, gateway→dest) |
| IPs statiques | sshproxy doit savoir où proxifier |
| Random route_select | Répartition charge automatique |
| Wrapper.sh | Future extensibilité (audit, ACL) |
| Multi-stage Docker | Image finale petite et efficace |
| Piège | Cause | Solution |
|---|---|---|
| Permission denied | gateway_rsa propriété root | chown testuser /etc/sshproxy/gateway_rsa |
| Exit status 255 | PTY non alloué | -tt dans sshproxy.yaml |
| Shell freeze | Pas de wrapper | sshproxy-wrapper.sh |
| IPs qui changent | Network recréé | Subnet fixe + ipv4_address |
DOCUMENTATION_COMPLETE.md (17 KB)
QUICK_REFERENCE.md (10 KB)
ELI5_EXPLICATION.md (6 KB)
RESOLUTION_RAPPORT.md (4 KB)
INDEX_FICHIERS.md (9 KB)
✅ Vous pouvez faire:
ssh -p 2222 testuser@localhost # Shell interactif
ssh -p 2222 testuser@localhost 'hostname' # Commandes
ssh -p 2222 testuser@localhost 'ls -la' # Tout ce que SSH permet
✅ Transparence:
Vous atterrissez directement sur dest1 ou dest2
Sans voir le saut par la gateway
Authentification multi-niveaux cachée
✅ Round-robin:
Chaque connexion choisit aléatoirement dest1 ou dest2
Répartition charge automatique