CVE Watch
CybersécuritéArticle6 min de lecture

HashiCorp Vault + Guacamole - stocker les secrets SSH/RDP sans jamais les écrire en clair

MB
Massioudath Bankole
22 mars 2026 · 28 vues

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é

Partager cet article

Commentaires (0)

Sois le premier à commenter !

Articles similaires