# ELI5 (Explain Like I'm 5) — SSH Proxy Transparent ## Le concept en 1 phrase **Vous vous connectez à une "porte d'entrée", qui vous envoie automatiquement à une vraie machine derrière, sans que vous fassiez quoi que ce soit.** --- ## Analogie du restaurant ``` VOUS (Client Windows) ↓ "Je veux manger!" HÔTE (Gateway) ↓ "On a 2 cuisines (dest1, dest2), je t'envoie à une au hasard" CUISINE 1 ou 2 (Destination) ↓ "Voilà ton plat!" VOUS (Client Windows) ↑ "Super! Je savais pas que j'allais à la cuisine 2!" ``` --- ## Les 3 conteneurs ### 1. GATEWAY (La porte d'entrée) - Écoute SSH sur le port 2222 - Votre clé SSH (`lab_rsa`) y est connue - Dès qu'une commande arrive, elle dit: "Je te la proxifie vers une destination" - Utilise sa propre clé (`gateway_rsa`) pour se connecter aux destinations ### 2. DEST1 & DEST2 (Les vraies machines) - Reçoivent les connexions de la gateway - Acceptent la clé de la gateway (`gateway_rsa.pub`) - Exécutent les commandes normalement - Renvoient les résultats --- ## Les 3 clés SSH ### Clé 1: `lab_rsa` (Windows → Gateway) ``` C:\Users\user\.ssh\lab_rsa (clé privée) ↓ prouve que c'est toi La gateway connait la clé publique ``` ### Clé 2: `gateway_rsa` (Gateway → Destinations) ``` Gateway a la clé privée ↓ s'authentifie auprès de dest1/dest2 Les destinations connaissent la clé publique ``` --- ## Les 6 fichiers importants ### 1. **docker-compose.yaml** ``` "J'ai 3 conteneurs: - gateway (port 2222 → 22) - dest1 (172.30.0.11) - dest2 (172.30.0.12) Tous dans un réseau privé" ``` ### 2. **gateway/Dockerfile** ``` "Build gateway: 1. Compile sshproxy (outil proxy) 2. Installe sshd (daemon SSH) 3. Ajoute compte testuser 4. Copie clés SSH 5. Configure sshd" ``` ### 3. **gateway/sshd_config** ← CRUCIAL ``` "Toute connexion SSH lancera d'abord: ForceCommand /usr/sbin/sshproxy-wrapper" ↓ C'est ça qui rend tout transparent! ``` ### 4. **gateway/sshproxy.yaml** ← CRUCIAL ``` "Destinations: [172.30.0.11, 172.30.0.12] Choix: aléatoire (50% chance chaque) Commande SSH: ssh -tt -i /path/to/gateway_rsa user@dest -tt = alloue un pseudo-terminal (pour shell interactif)" ``` ### 5. **gateway/sshproxy-wrapper.sh** ``` "Script simple: Si commande: lance sshproxy Si shell: lance sshproxy (= exactement pareil, sshproxy gère)" ``` ### 6. **dest/sshd_config** ``` "SSH normal. Pas de ForceCommand. Accepte juste la clé gateway_rsa.pub" ``` --- ## Flux d'une connexion (version simple) ``` 1. Vous: ssh -p 2222 localhost 'hostname' 2. Gateway sshd dit: "C'est quelle clé? lab_rsa.pub? Oui je la connais!" 3. Gateway sshd exécute: /usr/sbin/sshproxy-wrapper 4. Wrapper dit: "T'a un commande? Oui. OK j'appelle sshproxy" 5. sshproxy dit: "Je dois envoyer ça où? Destinations: [dest1, dest2] Je tire au dé... dest2! 🎲" 6. sshproxy exécute: ssh -tt -i gateway_rsa testuser@dest2 'hostname' 7. dest2 sshd dit: "C'est quelle clé? gateway_rsa? Oui je la connais!" 8. dest2 exécute: hostname 9. dest2 retourne: dest2 10. Vous voyez: dest2 Exit status 0 ✓ ``` --- ## Points clés à comprendre ### ✅ Pourquoi ça fonctionne | Point | Raison | |-------|--------| | ForceCommand | Intercepte CHAQUE SSH → la proxifie | | -tt dans config | Permet les shells interactifs sur destination | | 2 clés SSH | Première couche (client→gateway), deuxième couche (gateway→dest) | | Destinations fixes | IPs statiques (172.30.0.x) permettent à sshproxy de les cibler | | Random selection | Chaque connexion choisit aléatoirement → charge balancée | ### ❌ Pièges évités | Piège | Solution | |-------|----------| | Permission denied sur gateway_rsa | `chown testuser /etc/sshproxy/gateway_rsa` | | Shell interactif qui freeze | `-tt` flag dans sshproxy.yaml | | "Cannot contact etcd" error | `etcd: endpoints: []` dans config (mode stateless OK) | | Exit status 255 | ↑ Les deux erreurs précédentes | --- ## Configuration minimale: checklist ``` ☐ docker-compose.yaml — 3 conteneurs + réseau ☐ gateway/Dockerfile — compile sshproxy + installe sshd ☐ gateway/sshd_config — ForceCommand /usr/sbin/sshproxy-wrapper ☐ gateway/sshproxy.yaml — destinations + "-tt" flag ☐ gateway/sshproxy-wrapper.sh — lance sshproxy ☐ dest/Dockerfile — installe sshd uniquement ☐ dest/sshd_config — SSH normal ☐ keys/lab_rsa — clé privée Windows (utilisateur garde) ☐ keys/lab_rsa.pub — publique dans gateway ☐ keys/gateway_rsa — privée gateway (dans image) ☐ keys/gateway_rsa.pub — publique dans destinations ``` --- ## Les 3 "magies" principales ### Magie 1: ForceCommand ``` Ligne du sshd_config: ForceCommand /usr/sbin/sshproxy-wrapper Effet: ✓ Toute connexion SSH exécute d'abord ce wrapper ✓ C'est ce qui rend le proxy transparent ``` ### Magie 2: -tt flag ``` Dans sshproxy.yaml: ssh: args: - "-tt" Effet: ✓ Force allocation PTY sur destination ✓ Permet d'avoir un shell interactif ✓ Sans ça: freeze ou exit 255 ``` ### Magie 3: 2 niveaux de clés ``` Level 1 (Client→Gateway): lab_rsa ↓ (vous vous authentifiez) Level 2 (Gateway→Dest): gateway_rsa ↓ (gateway s'authentifie) Destination ↓ (exécute commande) Résultat ``` --- ## Commande test minimale ```bash # Lance tout docker compose up -d # Teste ssh -p 2222 -i keys/lab_rsa testuser@localhost 'hostname' # Devrait afficher dest1 ou dest2 aléatoirement # Exit status 0 # Et vu que c'est random, si tu testes 5 fois: for i in {1..5}; do ssh -p 2222 -i keys/lab_rsa testuser@localhost hostname; done # Tu verras un mélange de dest1 et dest2 ``` --- ## Pourquoi "transparent"? ``` Vous avez l'impression de faire: ssh dest1 'commande' En réalité vous faites: ssh gateway 'commande' Et la gateway redonne la main à la destination de façon invisible. Vous ne voyez PAS l'intermédiaire → transparent! ``` --- ## Prochaines étapes (optionnel) - **etcd**: Ajouter persistence (sessions sauvegardées) - **OpenLDAP**: Centralize users (au lieu de comptes locaux) - **Audit**: Logger chaque commande exécutée - **ACL**: Restricter qui peut aller où - **Healthchecks**: Vérifier que les destinations sont vivantes