Raccogliere i log di Keycloak
Questo documento spiega come configurare Keycloak per inviare i log a Google Security Operations utilizzando i webhook.
Keycloak è una soluzione open source per la gestione di identità e accessi (IAM) che offre funzionalità di Single Sign-On (SSO), federazione degli utenti, intermediazione di identità e accesso con account social. Supporta i protocolli OpenID Connect, OAuth 2.0 e SAML 2.0 e tiene traccia degli eventi utente (accesso, disconnessione, registrazione, modifiche della password) e degli eventi amministrativi (operazioni di gestione di utenti, client, realm e ruoli) per il controllo della sicurezza.
Prima di iniziare
Assicurati di soddisfare i seguenti prerequisiti:
- Un'istanza Google SecOps
- Un'istanza Keycloak in esecuzione (è consigliata la versione 20 o successive)
- Accesso amministratore alla console di amministrazione Keycloak
- Accesso al file system o al container del server Keycloak per il deployment delle estensioni
- Accesso a Google Cloud Console (per la creazione della chiave API)
Crea un feed webhook in Google SecOps
Creare il feed
- Vai a Impostazioni SIEM > Feed.
- Fai clic su Aggiungi nuovo feed.
- Nella pagina successiva, fai clic su Configura un singolo feed.
- Nel campo Nome feed, inserisci un nome per il feed (ad esempio,
Keycloak Events). - Seleziona Webhook come Tipo di origine.
- Seleziona Keycloak come Tipo di log.
- Fai clic su Avanti.
- Specifica i valori per i seguenti parametri di input:
- Delimitatore di divisione (facoltativo): inserisci
\nper dividere gli eventi su più righe (ogni POST webhook contiene un singolo evento, quindi questo campo può essere lasciato vuoto). - Spazio dei nomi dell'asset: lo spazio dei nomi dell'asset
- Etichette di importazione: l'etichetta da applicare agli eventi di questo feed
- Delimitatore di divisione (facoltativo): inserisci
- Fai clic su Avanti.
- Controlla la nuova configurazione del feed nella schermata Finalizza e poi fai clic su Invia.
Genera e salva la chiave segreta
Dopo aver creato il feed, devi generare una chiave segreta per l'autenticazione:
- Nella pagina dei dettagli del feed, fai clic su Genera chiave segreta.
- Una finestra di dialogo mostra la chiave segreta.
- Copia e salva la chiave segreta in modo sicuro.
Importante: la chiave segreta viene visualizzata una sola volta e non può essere recuperata in un secondo momento. Se la perdi, devi generare una nuova chiave segreta.
Recuperare l'URL dell'endpoint del feed
- Vai alla scheda Dettagli del feed.
- Nella sezione Endpoint Information (Informazioni sull'endpoint), copia l'URL dell'endpoint del feed.
Il formato dell'URL è:
https://malachiteingestion-pa.googleapis.com/v2/unstructuredlogentries:batchCreateo
https://<REGION>-malachiteingestion-pa.googleapis.com/v2/unstructuredlogentries:batchCreateSalva questo URL per i passaggi successivi.
Fai clic su Fine.
Creare una chiave API Google Cloud
Chronicle richiede una chiave API per l'autenticazione. Crea una chiave API con limitazioni nella Google Cloud Console.
Creare la chiave API
- Vai alla pagina Credenziali della console Google Cloud.
- Seleziona il tuo progetto (quello associato alla tua istanza di Chronicle).
- Fai clic su Crea credenziali > Chiave API.
- Viene creata una chiave API e visualizzata in una finestra di dialogo.
- Fai clic su Modifica chiave API per limitare la chiave.
Limitare la chiave API
- Nella pagina delle impostazioni Chiave API:
- Nome: inserisci un nome descrittivo (ad esempio,
Chronicle Webhook API Key)
- Nome: inserisci un nome descrittivo (ad esempio,
- In Limitazioni API:
- Seleziona Limita chiave.
- Nel menu a discesa Seleziona API, cerca e seleziona API Google SecOps (o API Chronicle).
- Fai clic su Salva.
- Copia il valore della chiave API dal campo Chiave API nella parte superiore della pagina.
- Salva la chiave API in modo sicuro.
Abilita l'archiviazione degli eventi in Keycloak
Prima di configurare l'estensione webhook, attiva l'archiviazione degli eventi in Keycloak in modo che gli eventi vengano generati e siano disponibili per l'inoltro.
Abilitare gli eventi utente
- Accedi alla console di amministrazione di Keycloak.
- Seleziona il regno che vuoi monitorare dal menu a discesa nell'angolo in alto a sinistra.
- Vai a Impostazioni del regno > Eventi.
- Seleziona la sottoscheda Impostazioni eventi utente.
- Attiva il pulsante di attivazione/disattivazione Salva eventi.
- Imposta il periodo di Scadenza (minimo consigliato: 7 giorni).
- Fai clic su Salva.
Attivare gli eventi amministrativi
- Nella stessa scheda Eventi, seleziona la scheda secondaria Impostazioni eventi amministrativi.
- Attiva il pulsante di attivazione/disattivazione Salva eventi.
- Attiva il pulsante di attivazione/disattivazione Includi rappresentazione per acquisire tutti i dettagli degli oggetti modificati.
- Imposta il periodo di Scadenza (minimo consigliato: 7 giorni).
- Fai clic su Salva.
Installare l'estensione del listener di eventi webhook
Keycloak non include un listener di eventi webhook nativo. Installa l'estensione keycloak-events di Phase Two (p2-inc) per attivare la distribuzione dei webhook.
Scaricare ed eseguire il deployment dell'estensione
Scarica l'ultimo JAR della release dalla pagina delle release di keycloak-events su Maven Central o compila dal codice sorgente:
git clone https://github.com/p2-inc/keycloak-events.git cd keycloak-events mvn clean installCopia il file JAR risultante nella directory
providersdi Keycloak:cp target/keycloak-events-*.jar /opt/keycloak/providers/Ricompila e riavvia Keycloak:
/opt/keycloak/bin/kc.sh build /opt/keycloak/bin/kc.sh start
Attiva il listener di eventi webhook
- Accedi alla console di amministrazione di Keycloak.
- Seleziona il regno di destinazione dal menu a discesa.
- Vai a Impostazioni del regno > Eventi.
- Nel menu a discesa Listener di eventi, seleziona ext-event-webhook.
- Fai clic su Salva.
Configura il webhook Keycloak
Costruisci l'URL webhook
Combina l'URL dell'endpoint Chronicle e la chiave API:
<ENDPOINT_URL>?key=<API_KEY>Esempio:
https://malachiteingestion-pa.googleapis.com/v2/unstructuredlogentries:batchCreate?key=AIzaSyD...
Crea un abbonamento webhook tramite l'API REST di Keycloak
L'estensione keycloak-events fornisce endpoint REST per la gestione delle iscrizioni webhook. Utilizza l'API Keycloak Admin REST per creare un webhook.
Passaggio 1: ottieni un token di accesso
Richiedi un token di accesso da Keycloak utilizzando un account amministratore:
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')
Sostituisci quanto segue:
<KEYCLOAK_HOST>: l'hostname e la porta del server Keycloak (ad esempio,keycloak.example.com:8443)<ADMIN_USERNAME>: il tuo nome utente amministratore Keycloak<ADMIN_PASSWORD>: la password amministratore di Keycloak
Passaggio 2: crea il webhook
Invia una richiesta POST per creare l'iscrizione al webhook per il realm di destinazione:
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": ["*"] }'
Sostituisci quanto segue:
<KEYCLOAK_HOST>: il nome host del server Keycloak<REALM_NAME>: il nome del realm da monitorare (ad esempio,masteromy-realm)<ENDPOINT_URL>: l'URL dell'endpoint del feed di Chronicle copiato in precedenza<API_KEY>: la chiave API Google Cloud creata in precedenza<SECRET_KEY>: la chiave segreta del webhook di Chronicle generata in precedenza<WEBHOOK_HMAC_SECRET>: una stringa segreta arbitraria per la firma HMAC dei payload webhook (ad esempio,mySecretKey123)
Passaggio 3: verifica il webhook
Verifica che il webhook sia stato creato elencando tutti i webhook per il realm:
curl -sS -X GET "https://<KEYCLOAK_HOST>/realms/<REALM_NAME>/webhooks" \ -H "Authorization: Bearer ${TOKEN}" \ -H "Accept: application/json"
La risposta restituisce un elenco di oggetti webhook. Verifica che il webhook venga visualizzato con "enabled": "true" e l'URL corretto.
Tipi di evento webhook
Il campo eventTypes accetta un array di espressioni per filtrare gli eventi inviati:
*: invia tutti gli eventi (opzione consigliata per l'integrazione SIEM)access.*: invia tutti gli eventi di accessoadmin.*: invia tutti gli eventi amministrativiadmin.USER-*: invia tutti gli eventi amministrativi relativi agli utentiadmin-USER-CREATE: invia solo eventi amministrativi di creazione utente
Formato payload webhook
Il webhook invia gli eventi come richieste POST HTTP con payload JSON. Esempio di payload dell'evento utente:
{ "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" } }
Comportamento di ripetizione del webhook
L'estensione utilizza il backoff esponenziale automatico per i nuovi tentativi quando viene ricevuta una risposta non 2xx:
| Parametro | Valore predefinito | Descrizione |
|---|---|---|
| backoffInitialInterval | 500 ms | Intervallo iniziale tra tentativi |
| backoffMaxElapsedTime | 900.000 ms (15 minuti) | Tempo totale massimo per i nuovi tentativi |
| backoffMaxInterval | 180.000 ms (3 minuti) | Intervallo massimo tra i tentativi |
| backoffMultiplier | 5 | Moltiplicatore per ogni intervallo di nuovi tentativi |
| backoffRandomizationFactor | 0,5 | Fattore di randomizzazione per il jitter |
Riferimento ai metodi di autenticazione
I feed webhook di Chronicle supportano più metodi di autenticazione. Scegli il metodo supportato dal tuo fornitore.
Metodo 1: intestazioni personalizzate (consigliato)
Se il tuo fornitore supporta le intestazioni HTTP personalizzate, utilizza questo metodo per una maggiore sicurezza.
Formato della richiesta:
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" }
Vantaggi:
- Chiave API e secret non visibili nell'URL
- Più sicuro (le intestazioni non vengono registrate nei log di accesso del server web)
- Metodo preferito quando il fornitore lo supporta
Metodo 2: parametri di query
Se il fornitore non supporta le intestazioni personalizzate, aggiungi le credenziali all'URL.
Formato dell'URL:
<ENDPOINT_URL>?key=<API_KEY>&secret=<SECRET_KEY>Esempio:
https://malachiteingestion-pa.googleapis.com/v2/unstructuredlogentries:batchCreate?key=AIzaSyD...&secret=abcd1234...Formato della richiesta:
POST <ENDPOINT_URL>?key=<API_KEY>&secret=<SECRET_KEY> HTTP/1.1 Content-Type: application/json { "event": "data", "timestamp": "2025-01-15T10:30:00Z" }
Svantaggi:
- Credenziali visibili nell'URL
- Potrebbe essere registrato nei log di accesso del server web
- Meno sicuri degli header
Metodo 3: ibrido (URL + intestazione)
Alcune configurazioni utilizzano la chiave API nell'URL e la chiave segreta nell'intestazione.
Formato della richiesta:
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" }
Nomi delle intestazioni di autenticazione
Chronicle accetta i seguenti nomi di intestazione per l'autenticazione:
Per la chiave API:
x-goog-chronicle-auth(consigliata)X-Goog-Chronicle-Auth(senza distinzione tra maiuscole e minuscole)
Per la chiave segreta:
x-chronicle-auth(consigliata)X-Chronicle-Auth(senza distinzione tra maiuscole e minuscole)
Limiti e best practice per i webhook
Limiti per le richieste
| Limite | Valore |
|---|---|
| Dimensioni massime della richiesta | 4 MB |
| QPS max (query al secondo) | 15.000 |
| Timeout richieste | 30 secondi |
| Comportamento di ripetizione | Automatico con backoff esponenziale |
Tabella di mappatura UDM
| Campo log | Mappatura UDM | Logica |
|---|---|---|
| payload.client_id | additional.fields | Unito ai campi creati da payload.client_id, payload.realm_id |
| payload.realm_id | additional.fields | |
| source_timestamp | metadata.event_timestamp | Analizzato utilizzando il filtro per data con i pattern ISO8601 e aaaa-MM-gg'T'HH:mm:ss.SSSZ |
| payload.ip_address | metadata.event_type | Impostato su "STATUS_UPDATE" se payload.ip_address non è vuoto, altrimenti "USER_UNCATEGORIZED" se uuid non è vuoto, altrimenti "GENERIC_EVENT" |
| uuid | metadata.event_type | |
| payload.type | metadata.product_event_type | Valore copiato direttamente |
| payload.session_id | network.session_id | Valore copiato direttamente |
| payload.ip_address | principal.ip | Valore copiato direttamente |
| source_metadata.schema | principal.resource.attribute.labels | Unito alle etichette create da source_metadata.schema, source_metadata.table, source_metadata.is_deleted (convertito in stringa), 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 | Valore copiato direttamente |
| oggetto | security_result.detection_fields | Unito alle etichette create da oggetto, read_method, payload.id |
| read_method | security_result.detection_fields | |
| payload.id | security_result.detection_fields | |
| redirect_uri | target.url | Valore copiato direttamente |
| nome utente | target.user.userid | Valore copiato direttamente |
| metadata.product_name | metadata.product_name | Impostato su "KEYCLOAK" |
| metadata.vendor_name | metadata.vendor_name | Impostato su "KEYCLOAK" |
username" from "details_json |
target.user.userid |
Mappato dal log delle modifiche |
redirect_uri" from "details_json |
target.url |
Mappato dal log delle modifiche |
realm_id" and "client_id |
additional.fields |
Mappato dal log delle modifiche |
Log delle modifiche
Visualizza il log delle modifiche per questo parser
Hai bisogno di ulteriore assistenza? Ricevi risposte dai membri della community e dai professionisti di Google SecOps.