Collecter les journaux Keycloak

Compatible avec :

Ce document explique comment configurer Keycloak pour envoyer des journaux à Google Security Operations à l'aide de webhooks.

Keycloak est une solution Open Source de gestion des identités et des accès (IAM, Identity and Access Management) qui offre des fonctionnalités d'authentification unique (SSO, Single Sign-On), de fédération des 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 de mot de passe) et les événements d'administration (opérations de gestion des utilisateurs, des clients, des royaumes 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

  1. Accédez à Paramètres SIEM> Flux.
  2. Cliquez sur Add New Feed (Ajouter un flux).
  3. Sur la page suivante, cliquez sur Configurer un seul flux.
  4. Dans le champ Nom du flux, saisissez un nom pour le flux (par exemple, Keycloak Events).
  5. Sélectionnez Webhook comme type de source.
  6. Sélectionnez Keycloak comme type de journal.
  7. Cliquez sur Suivant.
  8. Spécifiez les valeurs des paramètres d'entrée suivants :
    • Délimiteur de fractionnement (facultatif) : saisissez \n pour fractionner les événements multilignes (chaque 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
    • Libellés d'ingestion : libellé à appliquer aux événements de ce flux
  9. Cliquez sur Suivant.
  10. Vérifiez la configuration de votre nouveau flux 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 :

  1. Sur la page d'informations sur le flux, cliquez sur Générer une clé secrète.
  2. Une boîte de dialogue affiche la clé secrète.
  3. 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

  1. Accédez à l'onglet Détails du flux.
  2. Dans la section Endpoint Information (Informations sur le point de terminaison), copiez l'URL du point de terminaison du flux.
  3. Le format d'URL est le suivant :

    https://malachiteingestion-pa.googleapis.com/v2/unstructuredlogentries:batchCreate
    

    ou

    https://<REGION>-malachiteingestion-pa.googleapis.com/v2/unstructuredlogentries:batchCreate
    
  4. Enregistrez cette URL pour les étapes suivantes.

  5. 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

  1. Accédez à la page Identifiants de la console Google Cloud.
  2. Sélectionnez votre projet (celui associé à votre instance Chronicle).
  3. Cliquez sur Créer des identifiants> Clé API.
  4. Une clé API est créée et affichée dans une boîte de dialogue.
  5. Cliquez sur Modifier la clé API pour la restreindre.

Restreindre la clé API

  1. Sur la page des paramètres Clé API :
    • Nom : saisissez un nom descriptif (par exemple, Chronicle Webhook API Key).
  2. Sous Restrictions relatives aux API :
    1. Sélectionnez Restreindre la clé.
    2. Dans le menu déroulant Sélectionner des API, recherchez et sélectionnez API Google SecOps (ou API Chronicle).
  3. Cliquez sur Enregistrer.
  4. Copiez la valeur de la clé API depuis le champ Clé API en haut de la page.
  5. 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

  1. Connectez-vous à la console d'administration Keycloak.
  2. Sélectionnez le domaine que vous souhaitez surveiller dans le menu déroulant en haut à gauche.
  3. Accédez à Paramètres du domaine > Événements.
  4. Sélectionnez le sous-onglet Paramètres des événements utilisateur.
  5. Activez l'option Enregistrer des événements.
  6. Définissez la période d'expiration (minimum recommandé : 7 jours).
  7. Cliquez sur Enregistrer.

Activer les événements d'administration

  1. Dans l'onglet Événements, sélectionnez le sous-onglet Paramètres des événements d'administration.
  2. Activez l'option Enregistrer des événements.
  3. Activez l'option Inclure la représentation pour capturer tous les détails des objets modifiés.
  4. Définissez la période d'expiration (minimum recommandé : 7 jours).
  5. Cliquez sur Enregistrer.

Installer l'extension d'écouteur d'événements webhook

Keycloak n'inclut pas d'écouteur d'événements webhook natif. Installez l'extension keycloak-events depuis Phase Two (p2-inc) pour activer la diffusion des webhooks.

Télécharger et déployer l'extension

  1. Téléchargez le dernier fichier JAR 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 install
    
  2. Copiez le fichier JAR complet obtenu dans le répertoire providers de Keycloak :

    cp target/keycloak-events-*.jar /opt/keycloak/providers/
    
  3. Recompilez et redémarrez Keycloak :

    /opt/keycloak/bin/kc.sh build
    /opt/keycloak/bin/kc.sh start
    

Activer l'écouteur d'événements webhook

  1. Connectez-vous à la console d'administration Keycloak.
  2. Sélectionnez le royaume cible dans le menu déroulant des royaumes.
  3. Accédez à Paramètres du domaine > Événements.
  4. Dans le menu déroulant Écouteurs d'événements, sélectionnez ext-event-webhook.
  5. 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://malachiteingestion-pa.googleapis.com/v2/unstructuredlogentries:batchCreate?key=AIzaSyD...
    

Créer un abonnement au webhook via l'API REST Keycloak

L'extension keycloak-events fournit des points de terminaison REST pour gérer les abonnements aux webhook. Utilisez l'API REST d'administration Keycloak pour créer un webhook.

Étape 1 : Obtenir 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> : 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, master ou my-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 : Validez le webhook

  • Vérifiez que le webhook a été créé en listant tous les webhooks pour le 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 de webhook. Vérifiez que votre webhook s'affiche avec "enabled": "true" et l'URL correcte.

Types d'événements de webhook

Le champ eventTypes accepte un tableau d'expressions permettant de filtrer les événements à envoyer :

  • * : envoie tous les événements (recommandé pour l'intégration SIEM)
  • access.* : envoie tous les événements d'accès
  • admin.* : envoie tous les événements d'administration.
  • admin.USER-* : envoie tous les événements d'administration liés aux utilisateurs.
  • admin-USER-CREATE : n'envoyer que les événements d'administration de création d'utilisateur

Format de la charge utile du 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 de nouvelle tentative du webhook

L'extension utilise un intervalle exponentiel entre les tentatives automatiques 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) Durée totale maximale des nouvelles tentatives
backoffMaxInterval 180 000 ms (3 min) Intervalle maximal entre les nouvelles tentatives
backoffMultiplier 5 Multiplicateur pour chaque intervalle de nouvelle tentative
backoffRandomizationFactor 0,5 Facteur de randomisation pour le jitter

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 acceptée par votre fournisseur.

Si votre fournisseur accepte les en-têtes HTTP personnalisés, utilisez cette méthode pour une meilleure sécurité.

  • Format de la demande :

    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 :

  • Clé API et code secret non visibles dans l'URL
  • Plus sécurisé (les en-têtes ne sont pas enregistrés dans les journaux d'accès au 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 de l'URL :

    <ENDPOINT_URL>?key=<API_KEY>&secret=<SECRET_KEY>
    
  • Exemple :

    https://malachiteingestion-pa.googleapis.com/v2/unstructuredlogentries:batchCreate?key=AIzaSyD...&secret=abcd1234...
    
  • Format de la demande :

    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 au 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 demande :

    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
RPS (requêtes par seconde) max. 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 de journal Mappage UDM Logique
payload.client_id additional.fields Fusionné avec les champs créés à partir de payload.client_id et payload.realm_id
payload.realm_id additional.fields
source_timestamp metadata.event_timestamp Analysé à l'aide du filtre de date avec les modèles ISO8601 et yyyy-MM-dd'T'HH:mm:ss.SSSZ
payload.ip_address metadata.event_type Défini sur "STATUS_UPDATE" si payload.ip_address n'est pas vide, sur "USER_UNCATEGORIZED" si uuid n'est pas vide, ou 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 libellés créés à 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 libellés créés à partir de l'objet, read_method et 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-le sur "KEYCLOAK".
metadata.vendor_name metadata.vendor_name Définissez-le 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 pour ce parseur

Vous avez encore besoin d'aide ? Obtenez des réponses de membres de la communauté et de professionnels Google SecOps.