SSO avec Keycloak et Guacamole via OpenID Connect - installation et configuration complète
Dans l'article précédent, j'ai installé Apache Guacamole 1.6.0 sur Ubuntu Server. Aujourd'hui on va plus loin : on va connecter Guacamole à Keycloak pour que les utilisateurs se connectent avec un compte unique - sans avoir à gérer des comptes séparés dans Guacamole. C'est ce qu'on appelle le SSO (Single Sign-On).
Concrètement, le flux devient :
Utilisateur → Guacamole → Redirigé vers Keycloak → Authentifié → Retour Guacamole
Plus besoin de gérer des mots de passe dans Guacamole. Keycloak centralise tout.
Prérequis : Guacamole 1.6.0 installé et fonctionnel, Ubuntu Server 22.04, accès sudo.
C'est quoi Keycloak ?
Keycloak est un serveur d'identité open source développé par Red Hat. Il gère l'authentification et les autorisations pour tes applications via des protocoles standards comme OpenID Connect (OIDC) et SAML.
Dans notre architecture, Keycloak joue le rôle de fournisseur d'identité (IdP) - c'est lui qui vérifie qui tu es. Guacamole joue le rôle de client - il délègue l'authentification à Keycloak.
Étape 1 : Installer Keycloak sur Ubuntu
Installer Java (prérequis)
Keycloak nécessite Java 21 minimum depuis la version 26 :
sudo apt update
sudo apt install -y openjdk-21-jdk
java -version
# openjdk version "21.x.x"
Télécharger et installer Keycloak
wget https://github.com/keycloak/keycloak/releases/download/26.1.4/keycloak-26.1.4.tar.gz
tar -xzf keycloak-26.1.4.tar.gz
sudo mv keycloak-26.1.4 /opt/keycloak
Créer un utilisateur système dédié
On ne lance jamais un service en root. On crée un utilisateur système sans shell d'accès :
sudo useradd -r -d /opt/keycloak -s /sbin/nologin keycloak
sudo chown -R keycloak:keycloak /opt/keycloak
L'option -r crée un compte système, -s /sbin/nologin empêche toute connexion interactive avec ce compte. C'est une bonne pratique de sécurité.
Étape 2 : Démarrer Keycloak
Mode développement (pour les tests)
Pour un lab ou un environnement de test, le mode dev est suffisant :
sudo -u keycloak /opt/keycloak/bin/kc.sh start-dev --http-port=8180
On utilise le port 8180 car Tomcat/Guacamole occupe déjà le port 8080.
Créer le compte admin initial
Dans un second terminal :
sudo -u keycloak /opt/keycloak/bin/kc.sh bootstrap-admin user --username admin
Bug rencontré : "HTTPS required"
En accédant à http://IP:8180, Keycloak affichait "HTTPS required". C'est une protection par défaut pour les accès depuis des IPs non-locales.
Solution - désactiver temporairement cette contrainte via kcadm.sh :
/opt/keycloak/bin/kcadm.sh config credentials \
--server http://localhost:8180 \
--realm master \
--user admin \
--password YOUR_PASSWORD
/opt/keycloak/bin/kcadm.sh update realms/master \
-s sslRequired=NONE
Rafraîchis la page - tu devrais voir la page de login Keycloak.
Important : En production, configure un vrai certificat SSL et remets
sslRequired=EXTERNALouALL.
Si tu as oublié ton mot de passe admin
sudo rm -rf /opt/keycloak/data/h2
sudo -u keycloak KEYCLOAK_ADMIN=admin KEYCLOAK_ADMIN_PASSWORD=NouveauMotDePasse \
/opt/keycloak/bin/kc.sh start-dev --http-port=8180
Étape 3 : Configurer Keycloak pour Guacamole
Créer un Realm dédié
Un Realm dans Keycloak c'est un espace isolé - ses propres utilisateurs, ses propres clients, ses propres règles. On ne mélange pas les applications dans le realm master.
- Clique sur "Keycloak master" en haut à gauche
- Clique sur "Create realm"
- Realm name :
guacamole - Clique sur Create
Créer le Client OIDC
Dans le realm guacamole :
- Clique sur Clients → Create client
- Client type :
OpenID Connect - Client ID :
guacamole - Clique sur Next
Configuration des capabilities :
- Client authentication : Off (client public, suffisant pour Guacamole)
- Authorization : Off
- Authentication flow : coche Standard flow + Implicit flow
Configuration des URLs (remplace YOUR_SERVER_IP) :
Root URL : http://YOUR_SERVER_IP:8080/guacamole
Home URL : http://YOUR_SERVER_IP:8080/guacamole
Valid redirect URIs : http://YOUR_SERVER_IP:8080/guacamole/*
Valid post logout URIs: http://YOUR_SERVER_IP:8080/guacamole
Web origins : http://YOUR_SERVER_IP:8080
Clique sur Save.
Créer un utilisateur de test
- Clique sur Users → Create new user
- Remplis : Username
testuser_example, Emailuser@example.com - Clique sur Create
- Onglet Credentials → Set password
- Mets un mot de passe, désactive Temporary
- Clique sur Save
Étape 4 : Installer l'extension OpenID dans Guacamole
Télécharger l'extension SSO
wget https://downloads.apache.org/guacamole/1.6.0/binary/guacamole-auth-sso-1.6.0.tar.gz
tar -xzf guacamole-auth-sso-1.6.0.tar.gz
cd guacamole-auth-sso-1.6.0
sudo cp openid/guacamole-auth-sso-openid-1.6.0.jar /etc/guacamole/extensions/
ls -la /etc/guacamole/extensions/
Configurer guacamole.properties
sudo nano /etc/guacamole/guacamole.properties
Ajoute ces lignes à la fin du fichier en remplaçant IP par l'IP de ton serveur :
# OpenID Connect (Keycloak SSO)
openid-authorization-endpoint: http://IP:8180/realms/guacamole/protocol/openid-connect/auth
openid-jwks-endpoint: http://IP:8180/realms/guacamole/protocol/openid-connect/certs
openid-issuer: http://IP:8180/realms/guacamole
openid-client-id: guacamole
openid-redirect-uri: http://IP:8080/guacamole
Redémarre Tomcat :
sudo systemctl restart tomcat9
Étape 5 : Tester le SSO
Ouvre http://IP:8080/guacamole dans ton navigateur. Tu devrais voir deux options de connexion :
- Le formulaire classique Guacamole
- Un lien "Connectez-vous avec OpenID"
Clique sur OpenID → tu es redirigé vers Keycloak → connecte-toi avec testuser_example → tu arrives dans Guacamole.
Bug rencontré : "Implicit flow is disabled"
Erreur : unauthorized_client - Implicit flow is disabled for the client
C'est parce que Guacamole 1.6.0 utilise l'implicit flow par défaut. Il faut l'activer dans Keycloak :
- Clients →
guacamole→ onglet Settings - Descends jusqu'à Authentication flow
- Coche Implicit flow
- Clique sur Save
Reteste - ça devrait fonctionner.
Étape 6 : Créer un compte admin permanent
Le compte admin initial de Keycloak est temporaire. Il faut créer un compte admin permanent dans le realm master.
- Repasse sur le realm master
- Users → Add user
- Remplis username, email, prénom, nom
- Credentials → Set password → Temporary : Off
- Onglet Role mapping → Assign role → filtre "realm roles" → coche
admin→ Assign
Reconnecte-toi avec ce nouveau compte, puis supprime le compte admin temporaire.
Étape 7 : Synchroniser les utilisateurs Keycloak → Guacamole
Un utilisateur authentifié via Keycloak doit aussi exister dans Guacamole pour avoir accès aux connexions. J'ai écrit un script Python qui fait cette synchronisation automatiquement.
#!/usr/bin/env python3
"""
Synchronisation Keycloak -> Guacamole
Récupère les users du realm Keycloak et les crée dans Guacamole si absents.
Cron recommandé : 0 * * * * /usr/bin/python3 /opt/scripts/sync_keycloak_guacamole.py
"""
import requests
import logging
KEYCLOAK_URL = "http://YOUR_SERVER_IP:8180"
KEYCLOAK_REALM = "guacamole"
KEYCLOAK_ADMIN_REALM = "master"
KEYCLOAK_ADMIN_USER = "YOUR_KEYCLOAK_ADMIN_USER"
KEYCLOAK_ADMIN_PASS = "TON_MOT_DE_PASSE"
GUACAMOLE_URL = "http://YOUR_SERVER_IP:8080/guacamole"
GUACAMOLE_ADMIN = "guacadmin"
GUACAMOLE_PASS = "YOUR_GUACADMIN_PASSWORD"
GUACAMOLE_DATASOURCE = "mysql"
logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s")
log = logging.getLogger(__name__)
def get_keycloak_token():
url = f"{KEYCLOAK_URL}/realms/{KEYCLOAK_ADMIN_REALM}/protocol/openid-connect/token"
data = {"client_id": "admin-cli", "username": KEYCLOAK_ADMIN_USER,
"password": KEYCLOAK_ADMIN_PASS, "grant_type": "password"}
r = requests.post(url, data=data)
if r.status_code == 200:
return r.json().get("access_token")
log.error(f"Erreur token Keycloak: {r.status_code}")
return None
def get_keycloak_users(token):
url = f"{KEYCLOAK_URL}/admin/realms/{KEYCLOAK_REALM}/users?max=1000"
r = requests.get(url, headers={"Authorization": f"Bearer {token}"})
return r.json() if r.status_code == 200 else []
def get_guacamole_token():
r = requests.post(f"{GUACAMOLE_URL}/api/tokens",
data={"username": GUACAMOLE_ADMIN, "password": GUACAMOLE_PASS},
headers={"Content-Type": "application/x-www-form-urlencoded"})
return r.json().get("authToken") if r.status_code == 200 else None
def get_guacamole_users(token):
r = requests.get(f"{GUACAMOLE_URL}/api/session/data/{GUACAMOLE_DATASOURCE}/users",
headers={"Guacamole-Token": token})
return list(r.json().keys()) if r.status_code == 200 else []
def create_guacamole_user(token, email):
r = requests.post(
f"{GUACAMOLE_URL}/api/session/data/{GUACAMOLE_DATASOURCE}/users",
headers={"Guacamole-Token": token, "Content-Type": "application/json"},
json={"username": email, "password": "",
"attributes": {"disabled": "", "expired": ""}}
)
if r.status_code == 200:
log.info(f"Utilisateur créé : {email}")
else:
log.error(f"Erreur création {email}: {r.status_code}")
def sync():
kc_token = get_keycloak_token()
guac_token = get_guacamole_token()
if not kc_token or not guac_token:
return
kc_users = get_keycloak_users(kc_token)
guac_users = get_guacamole_users(guac_token)
created = 0
for user in kc_users:
email = user.get("email", "")
if not email:
continue
if email not in guac_users:
create_guacamole_user(guac_token, email)
created += 1
log.info(f"Sync terminée : {created} utilisateur(s) créé(s).")
if __name__ == "__main__":
sync()
Pour l'automatiser toutes les heures :
sudo crontab -e
# Ajoute :
0 * * * * /usr/bin/python3 /opt/scripts/sync_keycloak_guacamole.py >> /var/log/sync_keycloak_guacamole.log 2>&1
Résultat final
Tu as maintenant un bastion Guacamole avec SSO complet :
- Les utilisateurs se connectent via Keycloak - un seul compte pour tout
- Guacamole n'a plus à gérer l'authentification
- La synchronisation automatique crée les comptes Guacamole à partir de Keycloak
Dans le prochain article, on va encore plus loin avec HashiCorp Vault pour stocker les secrets SSH/RDP de façon sécurisée - fini les mots de passe en clair dans Guacamole.
Tags : Guacamole, Keycloak, SSO, OpenID Connect, Admin Linux, DevSecOps, Bastion