Recopila registros de Keycloak
En este documento, se explica cómo configurar Keycloak para enviar registros a Google Security Operations con webhooks.
Keycloak es una solución de administración de identidades y accesos (IAM) de código abierto que proporciona capacidades de inicio de sesión único (SSO), federación de usuarios, intermediación de identidades y acceso con redes sociales. Admite los protocolos OpenID Connect, OAuth 2.0 y SAML 2.0, y hace un seguimiento de los eventos del usuario (acceso, cierre de sesión, registro, cambios de contraseña) y los eventos del administrador (operaciones de administración de usuarios, clientes, dominios y roles) para la auditoría de seguridad.
Antes de comenzar
Asegúrate de cumplir con los siguientes requisitos previos:
- Una instancia de Google SecOps
- Una instancia de Keycloak en ejecución (se recomienda la versión 20 o posterior)
- Acceso de administrador a la Consola de administración de Keycloak
- Acceso al sistema de archivos o al contenedor del servidor de Keycloak para implementar extensiones
- Acceso a la Google Cloud Console (para la creación de claves de API)
Crea un feed de webhook en Google SecOps
Crea el feed
- Ve a Configuración de SIEM > Feeds.
- Haz clic en Agregar feed nuevo.
- En la siguiente página, haz clic en Configurar un solo feed.
- En el campo Nombre del feed, ingresa un nombre para el feed (por ejemplo,
Keycloak Events). - Selecciona Webhook como el Tipo de origen.
- Selecciona Keycloak como el Tipo de registro.
- Haz clic en Siguiente.
- Especifica valores para los siguientes parámetros de entrada:
- Delimitador de división (opcional): Ingresa
\npara dividir eventos de varias líneas (cada POST de webhook contiene un solo evento, por lo que este campo se puede dejar vacío). - Espacio de nombres del recurso: Es el espacio de nombres del recurso.
- Etiquetas de transferencia: Es la etiqueta que se aplicará a los eventos de este feed.
- Delimitador de división (opcional): Ingresa
- Haz clic en Siguiente.
- Revisa la nueva configuración del feed en la pantalla Finalizar y, luego, haz clic en Enviar.
Genera y guarda la clave secreta
Después de crear el feed, debes generar una clave secreta para la autenticación:
- En la página de detalles del feed, haz clic en Generar clave secreta.
- Un diálogo muestra la clave secreta.
- Copia y guarda la clave secreta de forma segura.
Importante: La clave secreta solo se muestra una vez y no se puede recuperar más adelante. Si la pierdes, deberás generar una nueva.
Obtén la URL del extremo del feed
- Ve a la pestaña Detalles del feed.
- En la sección Endpoint Information, copia la URL del extremo del feed.
El formato de la URL es el siguiente:
https://malachiteingestion-pa.googleapis.com/v2/unstructuredlogentries:batchCreateo
https://<REGION>-malachiteingestion-pa.googleapis.com/v2/unstructuredlogentries:batchCreateGuarda esta URL para los próximos pasos.
Haz clic en Listo.
Crea una clave de API de Google Cloud
Chronicle requiere una clave de API para la autenticación. Crea una clave de API restringida en la Google Cloud Console.
Crea la clave de API
- Ve a la página Credenciales de la consola de Google Cloud.
- Selecciona tu proyecto (el proyecto asociado con tu instancia de Chronicle).
- Haz clic en Crear credenciales > Clave de API.
- Se creará una clave de API y se mostrará en un diálogo.
- Haz clic en Editar clave de API para restringir la clave.
Restringe la clave de API
- En la página de configuración de la clave de API, haz lo siguiente:
- Nombre: Ingresa un nombre descriptivo (por ejemplo,
Chronicle Webhook API Key).
- Nombre: Ingresa un nombre descriptivo (por ejemplo,
- En Restricciones de API, haz lo siguiente:
- Selecciona Restringir clave.
- En el menú desplegable Seleccionar APIs, busca y selecciona Google SecOps API (o Chronicle API).
- Haz clic en Guardar.
- Copia el valor de la clave de API del campo Clave de API en la parte superior de la página.
- Guarda la clave de API de forma segura.
Habilita el almacenamiento de eventos en Keycloak
Antes de configurar la extensión de webhook, habilita el almacenamiento de eventos en Keycloak para que se generen eventos y estén disponibles para el reenvío.
Habilita los eventos de usuario
- Accede a la Consola del administrador de Keycloak.
- Selecciona el dominio que deseas supervisar en el menú desplegable de dominios que se encuentra en la esquina superior izquierda.
- Ve a Configuración del dominio > Eventos.
- Selecciona la pestaña secundaria Configuración de eventos de usuario.
- Habilita el botón de activación Guardar eventos.
- Establece el período de vencimiento (se recomienda un mínimo de 7 días).
- Haz clic en Guardar.
Habilita los eventos de administrador
- En la misma pestaña Eventos, selecciona la subpestaña Configuración de eventos de administrador.
- Habilita el botón de activación Guardar eventos.
- Habilita el botón de activación Incluir representación para capturar todos los detalles de los objetos modificados.
- Establece el período de vencimiento (se recomienda un mínimo de 7 días).
- Haz clic en Guardar.
Instala la extensión del objeto de escucha de eventos de webhook
Keycloak no incluye un objeto de escucha de eventos de webhook nativo. Instala la extensión keycloak-events de Phase Two (p2-inc) para habilitar la entrega de webhooks.
Descarga e implementa la extensión
Descarga el archivo JAR de la versión más reciente desde la página de versiones de keycloak-events en Maven Central o compílalo desde la fuente:
git clone https://github.com/p2-inc/keycloak-events.git cd keycloak-events mvn clean installCopia el archivo JAR grueso resultante en el directorio
providersde Keycloak:cp target/keycloak-events-*.jar /opt/keycloak/providers/Vuelve a compilar y reiniciar Keycloak:
/opt/keycloak/bin/kc.sh build /opt/keycloak/bin/kc.sh start
Habilita el objeto de escucha de eventos de webhook
- Accede a la Consola del administrador de Keycloak.
- Selecciona el dominio objetivo en el menú desplegable.
- Ve a Configuración del dominio > Eventos.
- En el menú desplegable Event listeners, selecciona ext-event-webhook.
- Haz clic en Guardar.
Configura el webhook de Keycloak
Construye la URL del webhook
Combina la URL del extremo de Chronicle y la clave de API:
<ENDPOINT_URL>?key=<API_KEY>Ejemplo:
https://malachiteingestion-pa.googleapis.com/v2/unstructuredlogentries:batchCreate?key=AIzaSyD...
Crea una suscripción a webhook a través de la API de REST de Keycloak
La extensión keycloak-events proporciona extremos de REST para administrar suscripciones a webhooks. Usa la API de REST de administrador de Keycloak para crear un webhook.
Paso 1: Obtén un token de acceso
Solicita un token de acceso a Keycloak con una cuenta de administrador:
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')
Reemplaza lo siguiente:
<KEYCLOAK_HOST>: El nombre de host y el puerto de tu servidor Keycloak (por ejemplo,keycloak.example.com:8443)<ADMIN_USERNAME>: Tu nombre de usuario de administrador de Keycloak<ADMIN_PASSWORD>: Tu contraseña de administrador de Keycloak
Paso 2: Crea el webhook
Envía una solicitud POST para crear la suscripción al webhook del dominio objetivo:
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": ["*"] }'
Reemplaza lo siguiente:
<KEYCLOAK_HOST>: Es el nombre de host de tu servidor Keycloak.<REALM_NAME>: Es el nombre del dominio que se supervisará (por ejemplo,masteromy-realm).<ENDPOINT_URL>: Es la URL del extremo del feed de Chronicle que copiaste antes.<API_KEY>: La clave de API de Google Cloud que creaste antes<SECRET_KEY>: Es la clave secreta del webhook de Chronicle que se generó anteriormente.<WEBHOOK_HMAC_SECRET>: Es una cadena secreta arbitraria para la firma HMAC de cargas útiles de webhook (por ejemplo,mySecretKey123).
Paso 3: Verifica el webhook
Para confirmar que se creó el webhook, enumera todos los webhooks del dominio:
curl -sS -X GET "https://<KEYCLOAK_HOST>/realms/<REALM_NAME>/webhooks" \ -H "Authorization: Bearer ${TOKEN}" \ -H "Accept: application/json"
La respuesta devuelve una lista de objetos de webhook. Verifica que tu webhook aparezca con "enabled": "true" y la URL correcta.
Tipos de eventos de webhook
El campo eventTypes acepta un array de expresiones para filtrar los eventos que se envían:
*: Envía todos los eventos (recomendado para la integración de SIEM).access.*: Envía todos los eventos de accesoadmin.*: Envía todos los eventos de administrador.admin.USER-*: Envía todos los eventos de administrador relacionados con los usuarios.admin-USER-CREATE: Envía solo eventos de administrador de creación de usuarios
Formato de la carga útil del webhook
El webhook envía eventos como solicitudes POST de HTTP con cargas útiles de JSON. Ejemplo de carga útil de evento del usuario:
{ "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" } }
Comportamiento de reintentos de webhook
La extensión usa la retirada exponencial automática para los reintentos cuando se recibe una respuesta que no es 2xx:
| Parámetro | Valor predeterminado | Descripción |
|---|---|---|
| backoffInitialInterval | 500 ms | Intervalo de reintento inicial |
| backoffMaxElapsedTime | 900,000 ms (15 min) | Tiempo total máximo de reintentos |
| backoffMaxInterval | 180,000 ms (3 min) | Intervalo máximo entre reintentos |
| backoffMultiplier | 5 | Multiplicador para cada intervalo de reintento |
| backoffRandomizationFactor | 0.5 | Factor de aleatorización para la fluctuación |
Referencia de métodos de autenticación
Los feeds de webhook de Chronicle admiten varios métodos de autenticación. Elige el método que admite tu proveedor.
Método 1: Encabezados personalizados (recomendado)
Si tu proveedor admite encabezados HTTP personalizados, usa este método para mejorar la seguridad.
Formato de la solicitud:
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" }
Ventajas:
- La clave y el secreto de la API no son visibles en la URL
- Más seguro (los encabezados no se registran en los registros de acceso al servidor web)
- Método preferido cuando el proveedor lo admite
Método 2: Parámetros de consulta
Si tu proveedor no admite encabezados personalizados, agrega credenciales a la URL.
Formato de URL:
<ENDPOINT_URL>?key=<API_KEY>&secret=<SECRET_KEY>Ejemplo:
https://malachiteingestion-pa.googleapis.com/v2/unstructuredlogentries:batchCreate?key=AIzaSyD...&secret=abcd1234...Formato de la solicitud:
POST <ENDPOINT_URL>?key=<API_KEY>&secret=<SECRET_KEY> HTTP/1.1 Content-Type: application/json { "event": "data", "timestamp": "2025-01-15T10:30:00Z" }
Desventajas:
- Credenciales visibles en la URL
- Es posible que se registren en los registros de acceso del servidor web
- Menos seguro que los encabezados
Método 3: Híbrido (URL + encabezado)
Algunas configuraciones usan la clave de API en la URL y la clave secreta en el encabezado.
Formato de la solicitud:
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" }
Nombres de encabezados de autenticación
Chronicle acepta los siguientes nombres de encabezado para la autenticación:
Para la clave de API:
x-goog-chronicle-auth(recomendada)X-Goog-Chronicle-Auth(no distingue mayúsculas de minúsculas)
Para la clave secreta, haz lo siguiente:
x-chronicle-auth(recomendada)X-Chronicle-Auth(no distingue mayúsculas de minúsculas)
Límites y prácticas recomendadas para los Webhooks
Límites de solicitudes
| Límite | Valor |
|---|---|
| Tamaño máximo de la solicitud | 4 MB |
| QPS máx. (consultas por segundo) | 15,000 |
| Tiempo de espera de la solicitud | 30 segundos |
| Comportamiento de reintento | Automática con retirada exponencial |
Tabla de asignación de UDM
| Campo de registro | Asignación de UDM | Lógica |
|---|---|---|
| payload.client_id | additional.fields | Se combinó con los campos creados a partir de payload.client_id y payload.realm_id. |
| payload.realm_id | additional.fields | |
| source_timestamp | metadata.event_timestamp | Se analizó con el filtro de fecha con los patrones ISO8601 y aaaa-MM-dd'T'HH:mm:ss.SSSZ. |
| payload.ip_address | metadata.event_type | Se establece en "STATUS_UPDATE" si payload.ip_address no está vacío; de lo contrario, se establece en "USER_UNCATEGORIZED" si uuid no está vacío o en "GENERIC_EVENT". |
| uuid | metadata.event_type | |
| payload.type | metadata.product_event_type | Valor copiado directamente |
| payload.session_id | network.session_id | Valor copiado directamente |
| payload.ip_address | principal.ip | Valor copiado directamente |
| source_metadata.schema | principal.resource.attribute.labels | Se fusionó con las etiquetas creadas a partir de source_metadata.schema, source_metadata.table, source_metadata.is_deleted (convertido a cadena), source_metadata.change_type, source_metadata.tx_id y 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 | Valor copiado directamente |
| objeto | security_result.detection_fields | Se combinó con las etiquetas creadas a partir de object, read_method y payload.id. |
| read_method | security_result.detection_fields | |
| payload.id | security_result.detection_fields | |
| redirect_uri | target.url | Valor copiado directamente |
| nombre de usuario | target.user.userid | Valor copiado directamente |
| metadata.product_name | metadata.product_name | Se establece en "KEYCLOAK". |
| metadata.vendor_name | metadata.vendor_name | Se establece en "KEYCLOAK". |
username" from "details_json |
target.user.userid |
Se asignó desde el registro de cambios |
redirect_uri" from "details_json |
target.url |
Se asignó desde el registro de cambios |
realm_id" and "client_id |
additional.fields |
Se asignó desde el registro de cambios |
Registro de cambios
Consulta el registro de cambios de este analizador
¿Necesitas más ayuda? Obtén respuestas de miembros de la comunidad y profesionales de Google SecOps.