Quick Reference — Architecture du proxy SSH transparent
📐 Architecture Visuelle
┌─────────────────────────────────────────────────────────────────┐
│ WINDOWS CLIENT (ssh -p 2222 -i lab_rsa testuser@localhost) │
└────────────────────────┬────────────────────────────────────────┘
│
│ Port 2222 (remappé vers 172.30.0.10:22)
│ Clé: lab_rsa
↓
┌─────────────────────────────────────────────────────────────────┐
│ GATEWAY (172.30.0.10:22) │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ SSH Daemon (sshd) │ │
│ │ - Accepte clé lab_rsa.pub │ │
│ │ - Exécute: ForceCommand /usr/sbin/sshproxy-wrapper │ │
│ └──────────────────────────────────────────────────────────┘ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ sshproxy-wrapper.sh │ │
│ │ - Détecte commande vs shell │ │
│ │ - Lance: /usr/sbin/sshproxy │ │
│ └──────────────────────────────────────────────────────────┘ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ sshproxy (daemon compilé en Go) │ │
│ │ - Lit config: sshproxy.yaml │ │
│ │ - Choisit destination: random(dest1, dest2) │ │
│ │ - Exécute rebond SSH avec clé gateway_rsa │ │
│ └──────────────────────────────────────────────────────────┘ │
└────────────────┬────────────────────────────────────────────────┘
│
┌────────┴────────┐
│ random() │
↓ 50% ↓ 50%
┌───────────────────┐ ┌───────────────────┐
│ DEST1 │ │ DEST2 │
│ 172.30.0.11:22 │ │ 172.30.0.12:22 │
│ │ │ │
│ SSH Daemon │ │ SSH Daemon │
│ - Accepte │ │ - Accepte │
│ gateway_rsa.pub │ │ gateway_rsa.pub │
│ - Shell normal │ │ - Shell normal │
└───────────────────┘ └───────────────────┘
🔑 Authentification à 2 niveaux
AUTHENTIFICATION NIVEAU 1: Windows → Gateway
┌─────────────────────────────────┐
│ Client Windows │
│ Clé privée: lab_rsa │
│ Clé publique: lab_rsa.pub │
└────────────┬────────────────────┘
│
↓
┌─────────────────────────────────┐
│ Gateway /home/testuser/.ssh/ │
│ authorized_keys (contient) │
│ → lab_rsa.pub │
└─────────────────────────────────┘
✓ Connexion Windows→Gateway autorisée
AUTHENTIFICATION NIVEAU 2: Gateway → Destinations
┌─────────────────────────────────┐
│ Gateway /etc/sshproxy/ │
│ Clé privée: gateway_rsa │
│ Clé publique: gateway_rsa.pub │
└────────────┬────────────────────┘
│
↓ (utilisée par sshproxy)
┌─────────────────────────────────┐
│ Dest1/Dest2 /home/testuser/.ssh/│
│ authorized_keys (contient) │
│ → gateway_rsa.pub │
└─────────────────────────────────┘
✓ Connexion Gateway→Destinations autorisée
📝 Fichiers clés et leurs rôles
INFRASTRUCTURE
- docker-compose.yaml: Définit 3 conteneurs + réseau privé
GATEWAY
- gateway/Dockerfile:
- Build sshproxy v2.1.0 en Go
- Installe sshd
- Configure comptes + permissions
- gateway/sshd_config:
- ForceCommand /usr/sbin/sshproxy-wrapper ← Clé!
- Auth par clé uniquement
- gateway/sshproxy.yaml:
- Destinations: 172.30.0.11, 172.30.0.12
- Selection: random
- SSH args: -tt (PTY) crucial
- gateway/sshproxy-wrapper.sh:
- Détecte shell interactif vs commande
- Exécute sshproxy dans les deux cas
DESTINATIONS
- dest/Dockerfile:
- Installe sshd uniquement
- Pas de sshproxy (pas besoin)
- dest/sshd_config:
- SSH normal (pas de ForceCommand)
- Auth par clé (gateway_rsa.pub)
AUTHENTIFICATION
- keys/lab_rsa: Privée Windows (utilisateur garde secret)
- keys/lab_rsa.pub: Publique → authorized_keys gateway
- keys/gateway_rsa: Privée gateway (compilée dans image)
- keys/gateway_rsa.pub: Publique → authorized_keys dest1/dest2
🔄 Processus d'une connexion
CLIENT WINDOWS
│
├─ ssh -p 2222 -i lab_rsa testuser@localhost 'hostname'
│ (ou sans 'hostname' pour shell interactif)
│
└─→ GATEWAY sshd (port 2222)
│
├─ Vérifie: lab_rsa.pub en authorized_keys? ✓
├─ Lance: /usr/sbin/sshproxy-wrapper
│
└─→ SSHPROXY-WRAPPER
│
├─ Détecte: $SSH_ORIGINAL_COMMAND fournie?
│ (oui/non, peu importe → fait pareil)
│
└─→ /usr/sbin/sshproxy (daemon Go)
│
├─ Lit: /etc/sshproxy/sshproxy.yaml
├─ Choisit: random → 172.30.0.11 ou 172.30.0.12
│
└─→ Exécute: ssh -tt -i /etc/sshproxy/gateway_rsa testuser@DESTINATION 'hostname'
│
├─ Vérifie: gateway_rsa en authorized_keys? ✓
│
└─→ DESTINATION sshd (normal)
│
└─ Exécute: 'hostname'
Retourne résultat à client
⚙️ Configuration minimale requise
Clés SSH (pré-générées)
# Générer si manquantes:
ssh-keygen -t ed25519 -f keys/lab_rsa -N "" # Windows→Gateway
ssh-keygen -t ed25519 -f keys/gateway_rsa -N "" # Gateway→Dest
Structure minimale
.
├── docker-compose.yaml
├── keys/
│ ├── lab_rsa # ← Windows garde privé
│ ├── lab_rsa.pub # ← Copié dans gateway
│ ├── gateway_rsa # ← Compilation dans image
│ └── gateway_rsa.pub # ← Copié dans destinations
├── gateway/
│ ├── Dockerfile
│ ├── sshd_config # ← ForceCommand crucial
│ ├── sshproxy.yaml # ← -tt crucial
│ └── sshproxy-wrapper.sh
└── dest/
├── Dockerfile
└── sshd_config
🚀 Commandes de test
# Lancer l'infrastructure
docker compose up -d
# Test 1: Commande simple
ssh -p 2222 -i keys/lab_rsa testuser@localhost 'hostname'
# Résultat: dest1 ou dest2 (random)
# Test 2: Commande avec sortie
ssh -p 2222 -i keys/lab_rsa testuser@localhost 'whoami; pwd; id'
# Résultat: testuser, /home/testuser, uid=1000...
# Test 3: Shell interactif (piped)
echo "hostname" | ssh -p 2222 -i keys/lab_rsa testuser@localhost
# Résultat: testuser@dest1:~$ hostname ↵ dest1
# Test 4: Vérifier round-robin
for i in {1..5}; do ssh -p 2222 -i keys/lab_rsa testuser@localhost hostname; done
# Résultat: dest2, dest1, dest2, dest1, dest2 (alternance aléatoire)
# Test 5: Voir les logs sshproxy
docker exec sshproxy-gateway tail -f /tmp/sshproxy-testuser.log
# Vérifier structure réseau
docker network inspect sshproxy_net
# Arrêter
docker compose down -v
🎯 Ce qui rend le proxy transparent
| Élément |
Raison |
| ForceCommand |
Intercepte SSH → proxifie |
| sshproxy wrapper |
Gère shell et commandes uniformément |
| -tt flag |
PTY allocation pour interactivité |
| gateway_rsa |
Identité gateway pour rebond auto |
| Random route_select |
Répartition charge automatique |
| IPs Docker fixes |
Destinations prévisibles pour sshproxy |
🔍 Cas d'usage
✅ Fonctionne
ssh gateway 'ls -la' # Commandes
ssh gateway 'for i in 1..3; do date; done' # Scripts
ssh gateway # Shell interactif
ssh gateway 'bash' # Lancer shell explicite
❌ Ne fonctionne pas (intentionnel)
ssh gateway -L 3306:dest:3306 # Port forwarding (AllowTcpForwarding no)
ssh gateway -X # X11 forwarding (X11Forwarding no)
📊 Statistiques architecture
- Gateway: 1 conteneur (proxy + orchestration)
- Destinations: 2 conteneurs (SSH normal)
- Réseau: Bridge Docker privé (172.30.0.0/24)
- Port exposé: 2222 (remappé vers port 22 gateway)
- Auth: 2 niveaux (client→gateway + gateway→dest)
- Sélection: Random (50/50 dest1 vs dest2)