04_CONCEPT_EXPLIQUE_SIMPLEMENT.md 6.2 KB

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

# 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