Collecter les journaux Keycloak
Ce document explique comment configurer Keycloak pour qu'il transmette les journaux à Google Security Operations à l'aide de webhooks.
Keycloak est une solution de gestion de l'authentification et des accès (IAM) Open Source qui fournit des fonctionnalités d'authentification unique (SSO), de fédération d'utilisateurs, de courtage d'identité et de connexion via les réseaux sociaux. Il est compatible avec les protocoles OpenID Connect, OAuth 2.0 et SAML 2.0, et suit les événements utilisateur (connexion, déconnexion, inscription, modification du mot de passe) et les événements d'administration (opérations de gestion des utilisateurs, des clients, des domaines et des rôles) pour l'audit de sécurité.
Avant de commencer
Assurez-vous de remplir les conditions préalables suivantes :
- Une instance Google SecOps
- Une instance Keycloak en cours d'exécution (version 20 ou ultérieure recommandée)
- Accès administrateur à la console d'administration Keycloak
- Accès au système de fichiers ou au conteneur du serveur Keycloak pour déployer des extensions
- Accès à la Google Cloud Console (pour créer une clé API)
Créer un flux de webhook dans Google SecOps
Créer le flux
- Accédez à Paramètres > Gestion des données > Flux. Si vous utilisez une plate-forme SIEM uniquement, accédez à Paramètres > Flux.
- Cliquez sur Ajouter un flux d'actualités.
- Sur la page suivante, cliquez sur Configurer un seul flux.
- Dans le champ Nom du flux, saisissez un nom pour le flux (par exemple,
Keycloak Events). - Sélectionnez Webhook comme type de source.
- Sélectionnez Keycloak comme Type de journal.
- Cliquez sur Suivant.
- Spécifiez les valeurs des paramètres d'entrée suivants :
- Délimiteur de fractionnement (facultatif) : saisissez
\npour fractionner les événements multilignes (chaque requête POST de webhook contient un seul événement, vous pouvez donc laisser ce champ vide). - Espace de noms de l'élément : espace de noms de l'élément
- Étiquettes d'ingestion : étiquette à appliquer aux événements de ce flux.
- Délimiteur de fractionnement (facultatif) : saisissez
- Cliquez sur Suivant.
- Vérifiez la configuration de votre nouveau flux d'actualités sur l'écran Finaliser, puis cliquez sur Envoyer.
Générer et enregistrer une clé secrète
Après avoir créé le flux, vous devez générer une clé secrète pour l'authentification :
- Sur la page d'informations du flux, cliquez sur Générer une clé secrète.
- Une boîte de dialogue affiche la clé secrète.
- Copiez et enregistrez la clé secrète de manière sécurisée.
Important : La clé secrète ne s'affiche qu'une seule fois et ne peut pas être récupérée ultérieurement. Si vous la perdez, vous devrez en générer une nouvelle.
Obtenir l'URL du point de terminaison du flux
- Accédez à l'onglet Détails du flux.
- Dans la section Informations sur le point de terminaison, copiez l'URL du point de terminaison du flux.
Le format d'URL est le suivant :
https://{region}-chronicle.googleapis.com/v1/{parent}/logs:importOù
{region}est la région de votre instance Google SecOps (par exemple,us,europeouasia-southeast1) et{parent}est le chemin d'accès à la ressource au formatprojects/<PROJECT_ID>/locations/<LOCATION>/instances/<INSTANCE_ID>/logTypes/<LOG_TYPE>.Enregistrez cette URL pour les étapes suivantes.
Cliquez sur OK.
Créer une clé API Google Cloud
Chronicle nécessite une clé API pour l'authentification. Créez une clé API restreinte dans la Google Cloud Console.
Créer la clé API
- Accédez à la page Identifiants de la console Google Cloud.
- Sélectionnez votre projet (celui associé à votre instance Chronicle).
- Cliquez sur Créer des identifiants > Clé API.
- Une clé API est créée et s'affiche dans une boîte de dialogue.
- Cliquez sur Modifier la clé API pour restreindre la clé.
Restreindre la clé API
- Sur la page des paramètres de la clé API :
- Nom : saisissez un nom descriptif (par exemple,
Chronicle Webhook API Key).
- Nom : saisissez un nom descriptif (par exemple,
- Sous Restrictions relatives aux API :
- Sélectionnez Restreindre la clé.
- Dans le menu déroulant Sélectionner des API, recherchez et sélectionnez API Google SecOps (ou API Chronicle).
- Cliquez sur Enregistrer.
- Copiez la valeur de la clé API dans le champ Clé API en haut de la page.
- Enregistrez la clé API de manière sécurisée.
Activer le stockage des événements dans Keycloak
Avant de configurer l'extension de webhook, activez le stockage des événements dans Keycloak afin que les événements soient générés et disponibles pour le transfert.
Activer les événements utilisateur
- Connectez-vous à la console d'administration Keycloak.
- Sélectionnez le domaine que vous souhaitez surveiller dans le menu déroulant des domaines en haut à gauche.
- Accédez à Realm Settings > Events (Paramètres du domaine > Événements).
- Sélectionnez le sous-onglet Paramètres des événements utilisateur.
- Activez l'option Enregistrer les événements.
- Définissez la période d'expiration (minimum recommandé : 7 jours).
- Cliquez sur Enregistrer.
Activer les événements d'administration
- Dans le même onglet Événements, sélectionnez le sous-onglet Paramètres des événements d'administration.
- Activez l'option Enregistrer les événements.
- Activez l'option Inclure la représentation pour capturer tous les détails des objets modifiés.
- Définissez la période d'expiration (minimum recommandé : 7 jours).
- Cliquez sur Enregistrer.
Installer l'extension d'écouteur d'événements de webhook
Keycloak n'inclut pas d'écouteur d'événements de webhook natif. Installez l'extension keycloak-events de Phase Two (p2-inc) pour activer la diffusion des webhooks.
Télécharger et déployer l'extension
Téléchargez le fichier JAR de la dernière version depuis la page des versions de keycloak-events sur Maven Central ou compilez-le à partir de la source :
git clone https://github.com/p2-inc/keycloak-events.git cd keycloak-events mvn clean installCopiez le fichier JAR fat obtenu dans le répertoire
providersde Keycloak :cp target/keycloak-events-*.jar /opt/keycloak/providers/Recompilez et redémarrez Keycloak :
/opt/keycloak/bin/kc.sh build /opt/keycloak/bin/kc.sh start
Activer l'écouteur d'événements de webhook
- Connectez-vous à la console d'administration Keycloak.
- Sélectionnez le domaine cible dans le menu déroulant des domaines.
- Accédez à Realm Settings > Events (Paramètres du domaine > Événements).
- Dans le menu déroulant Écouteurs d'événements, sélectionnez ext-event-webhook.
- Cliquez sur Enregistrer.
Configurer le webhook Keycloak
Créer l'URL de webhook
Combinez l'URL du point de terminaison Chronicle et la clé API :
<ENDPOINT_URL>?key=<API_KEY>Exemple :
https://{region}-chronicle.googleapis.com/v1/{parent}/logs:import?key=AIzaSyD...
Créer un abonnement webhook via l'API REST Keycloak
L'extension keycloak-events fournit des points de terminaison REST pour gérer les abonnements aux webhooks. Utilisez l'API REST d'administration Keycloak pour créer un webhook.
Étape 1 : Obtenez un jeton d'accès
Demandez un jeton d'accès à Keycloak à l'aide d'un compte administrateur :
TOKEN=$(curl -sS -X POST "https://<KEYCLOAK_HOST>/realms/master/protocol/openid-connect/token" \ -H "Content-Type: application/x-www-form-urlencoded" \ --data-urlencode "grant_type=password" \ --data-urlencode "client_id=admin-cli" \ --data-urlencode "username=<ADMIN_USERNAME>" \ --data-urlencode "password=<ADMIN_PASSWORD>" \ | sed -n 's/.*"access_token":"\([^"]*\)".*/\1/p')
Remplacez les éléments suivants :
<KEYCLOAK_HOST>: nom d'hôte et port de votre serveur Keycloak (par exemple,keycloak.example.com:8443)<ADMIN_USERNAME>: votre nom d'utilisateur administrateur Keycloak<ADMIN_PASSWORD>: votre mot de passe administrateur Keycloak
Étape 2 : Créez le webhook
Envoyez une requête POST pour créer l'abonnement au webhook pour le domaine cible :
curl -sS -X POST "https://<KEYCLOAK_HOST>/realms/<REALM_NAME>/webhooks" \ -H "Authorization: Bearer ${TOKEN}" \ -H "Content-Type: application/json" \ -d '{ "enabled": "true", "url": "<ENDPOINT_URL>?key=<API_KEY>&secret=<SECRET_KEY>", "secret": "<WEBHOOK_HMAC_SECRET>", "eventTypes": ["*"] }'
Remplacez les éléments suivants :
<KEYCLOAK_HOST>: nom d'hôte de votre serveur Keycloak<REALM_NAME>: nom du domaine à surveiller (par exemple,masteroumy-realm)<ENDPOINT_URL>: URL du point de terminaison du flux Chronicle copiée précédemment<API_KEY>: clé API Google Cloud créée précédemment<SECRET_KEY>: clé secrète du webhook Chronicle générée précédemment<WEBHOOK_HMAC_SECRET>: chaîne secrète arbitraire pour la signature HMAC des charges utiles de webhook (par exemple,mySecretKey123)
Étape 3 : Vérifier le webhook
Vérifiez que le webhook a été créé en listant tous les webhooks du domaine :
curl -sS -X GET "https://<KEYCLOAK_HOST>/realms/<REALM_NAME>/webhooks" \ -H "Authorization: Bearer ${TOKEN}" \ -H "Accept: application/json"
La réponse renvoie une liste d'objets webhook. Vérifiez que votre webhook s'affiche avec "enabled": "true" et l'URL correcte.
Types d'événements du webhook
Le champ eventTypes accepte un tableau d'expressions pour filtrer les événements envoyés :
*: envoyer tous les événements (recommandé pour l'intégration SIEM)access.*: envoyer tous les événements d'accèsadmin.*: envoyer tous les événements d'administrationadmin.USER-*: envoyer tous les événements d'administration liés aux utilisateursadmin-USER-CREATE: n'envoyer que les événements d'administration liés à la création d'utilisateurs
Format de charge utile webhook
Le webhook envoie des événements sous forme de requêtes HTTP POST avec des charges utiles JSON. Exemple de charge utile d'événement utilisateur :
{ "id": "987865-1a2b-3c4d-9876-654321abc", "time": 1767799710612, "type": "LOGIN", "realmId": "12345abcde-1a2b-4d3c-9876-abcd456", "clientId": "account-console", "userId": "abcd456-1234-5678-abc9-987gfed654", "sessionId": "efghij-9876-abcd-456-11223344", "ipAddress": "203.0.113.45", "details": { "auth_method": "openid-connect", "auth_type": "code", "redirect_uri": "https://app.example.com/callback", "consent": "no_consent_required", "username": "jdoe" } }
Comportement des nouvelles tentatives de webhook
L'extension utilise un intervalle exponentiel entre les tentatives automatique lorsqu'une réponse autre que 2xx est reçue :
| Paramètre | Valeur par défaut | Description |
|---|---|---|
| backoffInitialInterval | 500 ms | Intervalle initial avant une nouvelle tentative |
| backoffMaxElapsedTime | 900 000 ms (15 min) | Temps total maximal de nouvelle tentative |
| backoffMaxInterval | 180 000 ms (3 min) | Intervalle maximal entre les nouvelles tentatives |
| backoffMultiplier | 5 | Multiplicateur pour chaque intervalle entre les tentatives |
| backoffRandomizationFactor | 0,5 | Facteur de randomisation pour la gigue |
Référence des méthodes d'authentification
Les flux de webhook Chronicle sont compatibles avec plusieurs méthodes d'authentification. Choisissez la méthode compatible avec votre fournisseur.
Méthode 1 : En-têtes personnalisés (recommandé)
Si votre fournisseur accepte les en-têtes HTTP personnalisés, utilisez cette méthode pour une meilleure sécurité.
Format de la requête :
POST <ENDPOINT_URL> HTTP/1.1 Content-Type: application/json x-goog-chronicle-auth: <API_KEY> x-chronicle-auth: <SECRET_KEY> { "event": "data", "timestamp": "2025-01-15T10:30:00Z" }
Avantages :
- La clé API et le secret ne sont pas visibles dans l'URL
- Plus sécurisé (les en-têtes ne sont pas enregistrés dans les journaux d'accès du serveur Web)
- Méthode privilégiée lorsque le fournisseur la prend en charge
Méthode 2 : Paramètres de requête
Si votre fournisseur n'accepte pas les en-têtes personnalisés, ajoutez les identifiants à l'URL.
Format d'URL :
<ENDPOINT_URL>?key=<API_KEY>&secret=<SECRET_KEY>Exemple :
https://{region}-chronicle.googleapis.com/v1/{parent}/logs:import?key=AIzaSyD...&secret=abcd1234...Format de la requête :
POST <ENDPOINT_URL>?key=<API_KEY>&secret=<SECRET_KEY> HTTP/1.1 Content-Type: application/json { "event": "data", "timestamp": "2025-01-15T10:30:00Z" }
Inconvénients :
- Identifiants visibles dans l'URL
- Peut être consigné dans les journaux d'accès du serveur Web
- Moins sécurisé que les en-têtes
Méthode 3 : Hybride (URL + en-tête)
Certaines configurations utilisent une clé API dans l'URL et une clé secrète dans l'en-tête.
Format de la requête :
POST <ENDPOINT_URL>?key=<API_KEY> HTTP/1.1 Content-Type: application/json x-chronicle-auth: <SECRET_KEY> { "event": "data", "timestamp": "2025-01-15T10:30:00Z" }
Noms des en-têtes d'authentification
Chronicle accepte les noms d'en-tête suivants pour l'authentification :
Pour la clé API :
x-goog-chronicle-auth(recommandé)X-Goog-Chronicle-Auth(non sensible à la casse)
Pour la clé secrète :
x-chronicle-auth(recommandé)X-Chronicle-Auth(non sensible à la casse)
Limites et bonnes pratiques concernant les webhooks
Limites de requêtes
| Limite | Valeur |
|---|---|
| Taille maximale de la requête | 4 Mo |
| Nombre maximal de RPS (requêtes par seconde) | 15 000 |
| Délai avant expiration de la requête | 30 seconds |
| Comportement de nouvelle tentative | Automatique avec intervalle exponentiel entre les tentatives |
Table de mappage UDM
| Champ du journal | Mappage UDM | Logique |
|---|---|---|
| payload.client_id | additional.fields | Fusionné avec les champs créés à partir de payload.client_id, payload.realm_id |
| payload.realm_id | additional.fields | |
| source_timestamp | metadata.event_timestamp | Analyse à l'aide d'un filtre de date avec les formats ISO8601 et yyyy-MM-dd'T'HH:mm:ss.SSSZ |
| payload.ip_address | metadata.event_type | Définissez sur "STATUS_UPDATE" si payload.ip_address n'est pas vide, sinon sur "USER_UNCATEGORIZED" si uuid n'est pas vide, sinon sur "GENERIC_EVENT". |
| uuid | metadata.event_type | |
| payload.type | metadata.product_event_type | Valeur copiée directement |
| payload.session_id | network.session_id | Valeur copiée directement |
| payload.ip_address | principal.ip | Valeur copiée directement |
| source_metadata.schema | principal.resource.attribute.labels | Fusionné avec les étiquettes créées à partir de source_metadata.schema, source_metadata.table, source_metadata.is_deleted (converti en chaîne), source_metadata.change_type, source_metadata.tx_id, source_metadata.lsn |
| source_metadata.table | principal.resource.attribute.labels | |
| source_metadata.is_deleted | principal.resource.attribute.labels | |
| source_metadata.change_type | principal.resource.attribute.labels | |
| source_metadata.tx_id | principal.resource.attribute.labels | |
| source_metadata.lsn | principal.resource.attribute.labels | |
| uuid | principal.user.userid | Valeur copiée directement |
| objet | security_result.detection_fields | Fusionné avec les étiquettes créées à partir de l'objet, de read_method et de payload.id |
| read_method | security_result.detection_fields | |
| payload.id | security_result.detection_fields | |
| redirect_uri | target.url | Valeur copiée directement |
| nom d'utilisateur | target.user.userid | Valeur copiée directement |
| metadata.product_name | metadata.product_name | Définissez cette option sur "KEYCLOAK". |
| metadata.vendor_name | metadata.vendor_name | Définissez cette option sur "KEYCLOAK". |
username" from "details_json |
target.user.userid |
Mappé à partir du journal des modifications |
redirect_uri" from "details_json |
target.url |
Mappé à partir du journal des modifications |
realm_id" and "client_id |
additional.fields |
Mappé à partir du journal des modifications |
Journal des modifications
Afficher le journal des modifications de cet analyseur
Vous avez encore besoin d'aide ? Obtenez des réponses de membres de la communauté et de professionnels Google SecOps.