收集 Keycloak 日志

支持的语言:

本文档介绍了如何配置 Keycloak 以使用 Webhook 将日志推送到 Google Security Operations。

Keycloak 是一种开源身份和访问权限管理 (IAM) 解决方案,可提供单点登录 (SSO)、用户联合、身份代理和社交登录功能。它支持 OpenID Connect、OAuth 2.0 和 SAML 2.0 协议,并跟踪用户事件(登录、退出、注册、密码更改)和管理员事件(用户、客户端、领域和角色管理操作),以进行安全审核。

准备工作

请确保您满足以下前提条件:

  • Google SecOps 实例
  • 正在运行的 Keycloak 实例(建议使用 20 或更高版本)
  • 对 Keycloak 管理控制台的管理员访问权限
  • 访问 Keycloak 服务器文件系统或容器以部署扩展程序
  • 对 Google Cloud Console 的访问权限(用于创建 API 密钥)

在 Google SecOps 中创建 webhook Feed

创建 Feed

  1. 前往 SIEM 设置 > Feed。
  2. 点击添加新 Feed。
  3. 在下一页上,点击配置单个 Feed。
  4. 在 Feed 名称字段中,输入 Feed 的名称(例如 Keycloak Events)。
  5. 选择 Webhook 作为来源类型。
  6. 选择 Keycloak 作为日志类型。
  7. 点击下一步。
  8. 为以下输入参数指定值:
    • 拆分定界符(可选):输入 \n 以拆分多行事件(每个 webhook POST 都包含一个事件,因此可以留空)。
    • 资产命名空间:资产命名空间
    • 注入标签:要应用于此 Feed 中事件的标签
  9. 点击下一步。
  10. 在最终确定界面中查看新的新闻信息流配置,然后点击提交。

生成并保存密钥

创建 Feed 后,您必须生成用于身份验证的密钥:

  1. 在 Feed 详情页面上,点击生成密钥。
  2. 对话框会显示密钥。
  3. 复制并妥善保存密钥。

重要提示:密钥只会显示一次,之后无法检索。如果丢失,您必须生成新的密钥。

获取 Feed 端点网址

  1. 前往该 Feed 的详细信息标签页。
  2. 在端点信息部分,复制 Feed 端点网址。
  3. 网址格式为:

    https://{region}-chronicle.googleapis.com/v1/{parent}/logs:import
    

    其中,{region} 是 Google SecOps 实例所在的区域(例如 us、europe 或 asia-southeast1),{parent} 是资源路径,格式为 projects/<PROJECT_ID>/locations/<LOCATION>/instances/<INSTANCE_ID>/logTypes/<LOG_TYPE>。

  4. 保存此网址以供后续步骤使用。

  5. 点击完成。

创建 Google Cloud API 密钥

Chronicle 需要使用 API 密钥进行身份验证。在 Google Cloud Console 中创建受限 API 密钥。

创建 API 密钥

  1. 前往 Google Cloud 控制台“凭据”页面。
  2. 选择您的项目(与您的 Chronicle 实例关联的项目)。
  3. 点击创建凭据 > API 密钥。
  4. 系统会创建 API 密钥,并在对话框中显示。
  5. 点击修改 API 密钥以限制密钥。

限制 API 密钥

  1. 在 API 密钥设置页面中:
    • 名称:输入描述性名称(例如 Chronicle Webhook API Key)
  2. 在 API 限制下:
    1. 选择限制密钥。
    2. 在选择 API 下拉菜单中,搜索并选择 Google SecOps API(或 Chronicle API)。
  3. 点击保存。
  4. 从页面顶部的 API 密钥字段复制 API 密钥值。
  5. 安全地保存 API 密钥。

在 Keycloak 中启用事件存储

在配置 webhook 扩展程序之前,请在 Keycloak 中启用事件存储,以便生成事件并可供转发。

启用用户事件

  1. 登录 Keycloak 管理控制台。
  2. 从左上角的“大区”下拉菜单中选择要监控的大区。
  3. 前往 Realm Settings> Events。
  4. 选择用户事件设置子标签页。
  5. 启用保存活动切换开关。
  6. 设置失效期限(建议至少 7 天)。
  7. 点击保存。

启用管理员事件

  1. 在同一事件标签页中,选择管理事件设置子标签页。
  2. 启用保存活动切换开关。
  3. 启用包含表示法切换开关,以捕获已更改对象的完整详细信息。
  4. 设置失效期限(建议至少 7 天)。
  5. 点击保存。

安装 webhook 事件监听器扩展程序

Keycloak 不包含原生 webhook 事件监听器。安装第二阶段 (p2-inc) 的 keycloak-events 扩展程序,以启用网络钩子传送。

下载并部署扩展程序

  1. 从 Maven Central 上的 keycloak-events 版本页面下载最新版本的 JAR,或通过源代码构建:

    git clone https://github.com/p2-inc/keycloak-events.git
    cd keycloak-events
    mvn clean install
    
  2. 将生成的胖 JAR 文件复制到 Keycloak providers 目录中:

    cp target/keycloak-events-*.jar /opt/keycloak/providers/
    
  3. 重新构建并重启 Keycloak:

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

启用 webhook 事件监听器

  1. 登录 Keycloak 管理控制台。
  2. 从领域下拉菜单中选择目标领域。
  3. 前往 Realm Settings> Events。
  4. 在事件监听器下拉菜单中,选择 ext-event-webhook。
  5. 点击保存。

配置 Keycloak 网络钩子

构建网络钩子网址

  • 将 Chronicle 端点网址和 API 密钥组合在一起:

    <ENDPOINT_URL>?key=<API_KEY>
    
  • 示例:

    https://{region}-chronicle.googleapis.com/v1/{parent}/logs:import?key=AIzaSyD...
    

通过 Keycloak REST API 创建 webhook 订阅

keycloak-events 扩展程序提供用于管理 webhook 订阅的 REST 端点。使用 Keycloak Admin REST API 创建 webhook。

第 1 步:获取访问令牌

  • 使用管理员账号向 Keycloak 请求访问令牌:

    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')
    

替换以下内容:

  • <KEYCLOAK_HOST>:您的 Keycloak 服务器主机名和端口(例如 keycloak.example.com:8443)
  • <ADMIN_USERNAME>:您的 Keycloak 管理员用户名
  • <ADMIN_PASSWORD>:Keycloak 管理员密码

第 2 步:创建 webhook

  • 发送 POST 请求,为目标领域创建 webhook 订阅:

    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": ["*"]
      }'
    

替换以下内容:

  • <KEYCLOAK_HOST>:您的 Keycloak 服务器主机名
  • <REALM_NAME>:要监控的 Realm 的名称(例如 master 或 my-realm)
  • <ENDPOINT_URL>:之前复制的 Chronicle Feed 端点网址
  • <API_KEY>:之前创建的 Google Cloud API 密钥
  • <SECRET_KEY>:之前生成的 Chronicle webhook 密钥
  • <WEBHOOK_HMAC_SECRET>:用于对 webhook 载荷进行 HMAC 签名的任意 Secret 字符串(例如 mySecretKey123)

第 3 步:验证 Webhook

  • 通过列出领域的所有网络钩子,确认网络钩子已创建:

    curl -sS -X GET "https://<KEYCLOAK_HOST>/realms/<REALM_NAME>/webhooks" \
      -H "Authorization: Bearer ${TOKEN}" \
      -H "Accept: application/json"
    

响应会返回一个 webhook 对象列表。验证您的 Webhook 是否显示 "enabled": "true" 和正确的网址。

Webhook 事件类型

eventTypes 字段接受一个表达式数组,用于过滤要发送的事件:

  • * - 发送所有事件(建议用于 SIEM 集成)
  • access.* - 发送所有访问事件
  • admin.* - 发送所有管理员事件
  • admin.USER-* - 发送与用户相关的所有管理员事件
  • admin-USER-CREATE - 仅发送用户创建管理员事件

网络钩子载荷格式

  • Webhook 会以 HTTP POST 请求的形式发送事件,并附带 JSON 载荷。用户事件载荷示例:

    {
      "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"
      }
    }
    

网络钩子重试行为

当收到非 2xx 响应时,该扩展程序会使用自动指数退避算法进行重试:

参数 默认值 说明
backoffInitialInterval 500 毫秒 初始重试间隔
backoffMaxElapsedTime 900000 毫秒(15 分钟) 总重试时间上限
backoffMaxInterval 180000 毫秒(3 分钟) 重试之间的最长间隔时间
backoffMultiplier 5 每个重试间隔的乘数
backoffRandomizationFactor 0.5 抖动的随机化因素

身份验证方法参考

Chronicle Webhook Feed 支持多种身份验证方法。选择供应商支持的方法。

如果您的供应商支持自定义 HTTP 标头,请使用此方法来提高安全性。

  • 请求格式:

    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"
    }
    

优点:

  • API 密钥和 Secret 在网址中不可见
  • 更安全(标头不会记录在 Web 服务器访问日志中)
  • 供应商支持的首选方法

方法 2:查询参数

如果您的供应商不支持自定义标头,请将凭据附加到网址。

  • 网址格式:

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

    https://{region}-chronicle.googleapis.com/v1/{parent}/logs:import?key=AIzaSyD...&secret=abcd1234...
    
  • 请求格式:

    POST <ENDPOINT_URL>?key=<API_KEY>&secret=<SECRET_KEY> HTTP/1.1
    Content-Type: application/json
    
    {
            "event": "data",
            "timestamp": "2025-01-15T10:30:00Z"
    }
    

缺点:

  • 网址中可见的凭据
  • 可能会记录在 Web 服务器访问日志中
  • 安全性不如标头

方法 3:混合(网址 + 标头)

某些配置在网址中使用 API 密钥,在标头中使用密钥。

  • 请求格式:

    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"
    }
    

身份验证标头名称

Chronicle 接受以下标头名称进行身份验证:

对于 API 密钥:

  • x-goog-chronicle-auth(推荐)
  • X-Goog-Chronicle-Auth(不区分大小写)

对于密钥:

  • x-chronicle-auth(推荐)
  • X-Chronicle-Auth(不区分大小写)

Webhook 限制和最佳实践

请求限制

限制 值
请求大小上限 4 MB
最大 QPS(每秒查询次数) 15000
请求超时 30 秒
重试行为 自动(采用指数退避算法)

UDM 映射表

日志字段 UDM 映射 逻辑
payload.client_id additional.fields 已与通过 payload.client_id、payload.realm_id 创建的字段合并
payload.realm_id additional.fields
source_timestamp metadata.event_timestamp 使用日期过滤条件(采用 ISO8601 和 yyyy-MM-dd'T'HH:mm:ss.SSSZ 格式)进行解析
payload.ip_address metadata.event_type 如果 payload.ip_address 不为空,则设置为“STATUS_UPDATE”;否则,如果 uuid 不为空,则设置为“USER_UNCATEGORIZED”;否则,设置为“GENERIC_EVENT”
uuid metadata.event_type
payload.type metadata.product_event_type 直接复制值
payload.session_id network.session_id 直接复制值
payload.ip_address principal.ip 直接复制的值
source_metadata.schema principal.resource.attribute.labels 与根据 source_metadata.schema、source_metadata.table、source_metadata.is_deleted(转换为字符串)、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 直接复制值
对象 security_result.detection_fields 与根据 object、read_method、payload.id 创建的标签合并
read_method security_result.detection_fields
payload.id security_result.detection_fields
redirect_uri target.url 直接复制的值
用户名 target.user.userid 直接复制的值
metadata.product_name metadata.product_name 设置为“KEYCLOAK”
metadata.vendor_name metadata.vendor_name 设置为“KEYCLOAK”
username" from "details_json target.user.userid 从更新日志映射
redirect_uri" from "details_json target.url 从更新日志映射
realm_id" and "client_id additional.fields 从更新日志映射

更新日志

查看相应解析器的更新日志

需要更多帮助?获得社区成员和 Google SecOps 专业人士的解答。