HashiCorp Vault + Guacamole - stocker les secrets SSH/RDP sans jamais les écrire en clair
Dans les deux articles précédents, j'ai installé Guacamole et configuré le SSO avec Keycloak. Il reste un problème majeur : les mots de passe SSH et RDP sont stockés en clair dans la base de données Guacamole. Si quelqu'un compromet la base, il a accès à tous les serveurs.
La solution : HashiCorp Vault. Un gestionnaire de secrets open source qui chiffre tout, contrôle les accès et peut même rotation automatiquement les mots de passe.
L'architecture finale devient :
Utilisateur → Keycloak (SSO) → Guacamole → Vault (récupère le secret SSH/RDP) → Serveur cible
Prérequis : Guacamole + Keycloak installés et fonctionnels, Ubuntu Server 22.04.
C'est quoi HashiCorp Vault ?
Vault est un outil de gestion des secrets. Il résout un problème concret : comment stocker et distribuer des secrets (mots de passe, clés API, certificats) de façon sécurisée dans une infrastructure ?
Sans Vault, les secrets traînent partout - dans des fichiers de config, des bases de données, des scripts. Avec Vault :
- Tout est chiffré au repos et en transit
- Chaque accès est loggé et auditable
- Les secrets peuvent être révoqués à tout moment
- La rotation automatique est possible
Étape 1 : Installer Vault
Ajouter le dépôt HashiCorp
wget -O - https://apt.releases.hashicorp.com/gpg | sudo gpg \
--dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg
echo "deb [arch=$(dpkg --print-architecture) \
signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] \
https://apt.releases.hashicorp.com $(lsb_release -cs) main" | \
sudo tee /etc/apt/sources.list.d/hashicorp.list
sudo apt update && sudo apt install vault
vault version
# Vault v1.21.x
Étape 2 : Démarrer Vault en mode développement
Pour un lab ou des tests, le mode développement est suffisant. Ne jamais utiliser ce mode en production - les données sont en mémoire et perdues au redémarrage.
vault server -dev -dev-listen-address="0.0.0.0:8200"
Vault affiche des informations importantes - note-les immédiatement :
Unseal Key: AbCdEfGh...
Root Token: hvs.XXXXXXX...
Conseil : sauvegarde ces deux valeurs dans un fichier sécurisé :
nano vault.txt
Configurer l'environnement
Dans un second terminal :
export VAULT_ADDR='http://127.0.0.1:8200'
vault status
Tu devrais voir Sealed: false - Vault est prêt.
Se connecter avec le Root Token
vault login
# Entre ton Root Token quand demandé
Étape 3 : Créer un moteur de secrets pour Guacamole
Vault organise les secrets dans des moteurs de secrets (secrets engines). On va créer un moteur KV (Key-Value) dédié à Guacamole :
vault secrets enable -path=guacamole kv-v2
kv-v2 c'est la version 2 du moteur Key-Value - elle supporte le versionnement des secrets. Si tu changes un mot de passe, l'ancienne version reste accessible pour audit.
Stocker un secret SSH
vault kv put guacamole/ssh/mon-serveur \
hostname="192.168.X.X" \
port="22" \
username="ubuntu" \
password="YOUR_SSH_PASSWORD"
Lire un secret
vault kv get guacamole/ssh/mon-serveur
====== Secret Path ======
guacamole/data/ssh/mon-serveur
======= Metadata =======
Key Value
created_time 2026-03-05T15:22:19Z
version 1
====== Data ======
Key Value
hostname 192.168.X.X
password YOUR_SSH_PASSWORD
port 22
username ubuntu
Étape 4 : Créer des connexions Guacamole via l'API Python
Maintenant qu'on a les secrets dans Vault, on va créer les connexions dans Guacamole en récupérant les credentials depuis Vault - jamais en les écrivant en dur dans le script.
#!/usr/bin/env python3
"""
Création d'une connexion SSH dans Guacamole
avec les credentials récupérés depuis HashiCorp Vault.
"""
import requests
import json
import logging
# ── Configuration ──────────────────────────────────────────
VAULT_ADDR = "http://127.0.0.1:8200"
VAULT_TOKEN = "YOUR_VAULT_ROOT_TOKEN"
SECRET_PATH = "guacamole/data/ssh/mon-serveur"
GUACAMOLE_URL = "http://YOUR_SERVER_IP:8080/guacamole"
GUACAMOLE_ADMIN = "guacadmin"
GUACAMOLE_PASS = "YOUR_GUACADMIN_PASSWORD"
GUACAMOLE_DS = "mysql"
TARGET_USER = "user@example.com"
CONNECTION_NAME = "Mon Serveur SSH"
logging.basicConfig(level=logging.INFO,
format="%(asctime)s [%(levelname)s] %(message)s")
log = logging.getLogger(__name__)
# ── Récupérer le secret depuis Vault ───────────────────────
def get_secret_from_vault():
"""Récupère les credentials SSH stockés dans Vault."""
url = f"{VAULT_ADDR}/v1/{SECRET_PATH}"
headers = {"X-Vault-Token": VAULT_TOKEN}
r = requests.get(url, headers=headers)
if r.status_code == 200:
data = r.json().get("data", {}).get("data", {})
log.info("Secret récupéré depuis Vault.")
return data
log.error(f"Erreur Vault: {r.status_code} - {r.text}")
return None
# ── Obtenir un token Guacamole ──────────────────────────────
def get_guacamole_token():
"""Obtenir un token d'authentification Guacamole."""
r = requests.post(
f"{GUACAMOLE_URL}/api/tokens",
data={"username": GUACAMOLE_ADMIN, "password": GUACAMOLE_PASS},
headers={"Content-Type": "application/x-www-form-urlencoded"}
)
if r.status_code == 200:
return r.json().get("authToken")
log.error(f"Erreur token Guacamole: {r.status_code}")
return None
# ── Créer la connexion SSH dans Guacamole ──────────────────
def create_ssh_connection(token, secret):
"""Crée une connexion SSH dans Guacamole avec les credentials Vault."""
r = requests.post(
f"{GUACAMOLE_URL}/api/session/data/{GUACAMOLE_DS}/connections",
headers={"Guacamole-Token": token, "Content-Type": "application/json"},
json={
"parentIdentifier": "ROOT",
"name": CONNECTION_NAME,
"protocol": "ssh",
"parameters": {
"hostname": secret.get("hostname"),
"port": secret.get("port", "22"),
"username": secret.get("username"),
"password": secret.get("password"),
"recording-path": "${HISTORY_PATH}/${HISTORY_UUID}",
"create-recording-path": "true",
"recording-include-keys": "true"
},
"attributes": {
"max-connections": "5",
"max-connections-per-user": "1"
}
}
)
if r.status_code == 200:
connection_id = r.json().get("identifier")
log.info(f"Connexion SSH créée (ID: {connection_id})")
return connection_id
log.error(f"Erreur création connexion: {r.status_code} - {r.text}")
return None
# ── Assigner la connexion à un utilisateur ─────────────────
def assign_connection_to_user(token, connection_id, username):
"""Donne accès à la connexion pour un utilisateur spécifique."""
r = requests.patch(
f"{GUACAMOLE_URL}/api/session/data/{GUACAMOLE_DS}/users/{username}/permissions",
headers={"Guacamole-Token": token, "Content-Type": "application/json"},
json=[{"op": "add", "path": f"/connectionPermissions/{connection_id}", "value": "READ"}]
)
if r.status_code == 204:
log.info(f"Connexion assignée à {username}")
return True
log.error(f"Erreur assignation: {r.status_code} - {r.text}")
return False
# ── Main ───────────────────────────────────────────────────
def main():
secret = get_secret_from_vault()
if not secret:
return
token = get_guacamole_token()
if not token:
return
connection_id = create_ssh_connection(token, secret)
if not connection_id:
return
assign_connection_to_user(token, connection_id, TARGET_USER)
log.info("Terminé !")
if __name__ == "__main__":
main()
Ce script fait quelque chose d'important : les credentials SSH ne sont jamais écrits en dur. Ils sont récupérés depuis Vault au moment de l'exécution, puis passés à Guacamole. Si tu changes le mot de passe dans Vault, il suffit de relancer le script.
Ce que j'ai appris avec Vault
Vault m'a appris à penser différemment la gestion des secrets. Avant, j'aurais mis le mot de passe SSH directement dans un fichier de config ou dans la base Guacamole. Avec Vault :
- Je sais exactement qui a accès à quel secret
- Je peux révoquer un accès en une commande
- Chaque lecture de secret est loguée - indispensable pour un audit de sécurité
- En production, je peux configurer une rotation automatique des mots de passe SSH
Pour la production : ne jamais utiliser le mode
vault server -dev. Configure Vault avec un vrai backend de stockage (Consul, PostgreSQL) et active le Auto-unseal avec AWS KMS ou Azure Key Vault.
Architecture finale
Voici ce qu'on a construit sur ces trois articles :
Utilisateur
│
▼
Keycloak (SSO) ──── authentifie ───→ Guacamole
│
▼
HashiCorp Vault
(récupère les credentials)
│
▼
Serveur cible (SSH/RDP)
(session enregistrée)
- ✅ Guacamole installé avec enregistrement de sessions
- ✅ SSO via Keycloak (OpenID Connect)
- ✅ Synchronisation automatique des utilisateurs
- ✅ Secrets SSH/RDP stockés dans Vault
- ⏳ SSL/HTTPS (prochain article)
Tags : HashiCorp Vault, Guacamole, Secrets Management, DevSecOps, Admin Linux, Sécurité