场景

你把 Keycloak 部署到了生产环境,用户正常登录、应用正常运行。然后发生了三件事之一:

  1. 安全团队问你要「最近 90 天的所有管理员操作日志」
  2. 合规审计要求证明「所有登录失败有记录、所有权限变更有追溯」
  3. 运维发现数据库越来越大,怀疑是 Keycloak 的事件表在膨胀

你打开 Keycloak Admin Console,事件设置看起来很简单——但哪些事件必须记、怎么导出到 SIEM、日志保留多久、怎么和等保 2.0 / SOC 2 要求对齐,这些才是真正的难点。

适用 / 不适用

适用不适用
需要 IAM 审计日志用于等保 2.0 / SOC 2 / ISO 27001 合规仅做功能验证的开发环境
Keycloak 22+(Quarkus 发行版)Keycloak < 22(事件配置方式不同)
已有 ELK / Loki / Splunk 等日志平台没有集中式日志平台(本文也会讲怎么用文件日志起步)
Kubernetes / Docker / 裸机部署不需要合规审计的单机实验

最小配置

1. 启用登录事件与管理员事件

Keycloak 的事件系统分两层:登录事件(用户行为:登录、注册、登出、Token 刷新等)和管理员事件(Admin REST API 调用:修改用户、创建 Client、变更 Realm 配置等)。

CLI 方式(推荐):

kc.sh start \
  --spi-events-store=jpa \
  --spi-events-listener-jboss-logging-success-level=info \
  --spi-events-listener-jboss-logging-error-level=warn \
  --events-db-max-age-seconds=7776000

环境变量方式(Kubernetes / Docker):

env:
  - name: KC_SPI_EVENTS_STORE
    value: "jpa"
  - name: KC_SPI_EVENTS_LISTENER_JBOSS_LOGGING_SUCCESS_LEVEL
    value: "info"
  - name: KC_SPI_EVENTS_LISTENER_JBOSS_LOGGING_ERROR_LEVEL
    value: "warn"
  - name: KC_EVENTS_DB_MAX_AGE_SECONDS
    value: "7776000"  # 90 天

关键参数说明:

参数说明推荐值
spi-events-store事件存储后端jpa(存数据库,支持查询)
spi-events-listener-jboss-logging-success-level成功事件日志级别info(生产环境不记录 debug)
spi-events-listener-jboss-logging-error-level失败事件日志级别warn(登录失败必须记录)
events-db-max-age-seconds事件在数据库中的保留时间7776000(90 天,配合等保最低要求)

⚠️ events-db-max-age-seconds 只对数据库存储生效(jpa 模式)。如果改成 none,这个参数无效且所有事件都不会持久化。

2. 在 Admin Console 中可视化配置

登录 Keycloak Admin Console → Realm Settings → Events:

  • Save Events: ON
  • Save Admin Events: ON
  • Expiration: 事件保留天数(与 CLI 参数等效)
  • Event Listeners: jboss-logging(同时输出到应用日志)

你还可以配置 Event Types 细粒度选择要记录的事件:

LOGIN, LOGIN_ERROR, LOGOUT, REGISTER, REGISTER_ERROR,
CLIENT_LOGIN, CLIENT_LOGIN_ERROR, CODE_TO_TOKEN, CODE_TO_TOKEN_ERROR,
REFRESH_TOKEN, REFRESH_TOKEN_ERROR, TOKEN_EXCHANGE, TOKEN_EXCHANGE_ERROR,
IDENTITY_PROVIDER_LOGIN, IDENTITY_PROVIDER_LOGIN_ERROR,
FEDERATED_IDENTITY_LINK, REMOVE_FEDERATED_IDENTITY,
UPDATE_EMAIL, UPDATE_PROFILE, SEND_RESET_PASSWORD,
UPDATE_PASSWORD, UPDATE_PASSWORD_ERROR

生产环境不要全勾上——TOKEN_EXCHANGEREFRESH_TOKEN 在微服务高频调用时会产生海量事件,可以考虑只用 *_ERROR 变体。

3. 管理员事件详细级别

管理员事件的详细级别决定了日志中是否包含请求的 representation(变更前后的完整 JSON 数据)。这在排查「谁改了某个配置」时至关重要:

# 记录完整的变更前后数据(推荐生产开启)
--spi-admin-event-detail=UPDATE,CREATE,DELETE

可选值:OFF(只记录操作类型)、REPRESENTATION(记录变更数据)、UPDATE,CREATE,DELETE(按操作类型选择)。

验证

1. 检查事件是否在记录

登录 Keycloak Admin Console → Events → Login Events:

  • 尝试登录(成功和故意失败各一次)
  • 列表中应出现 LOGINLOGIN_ERROR 事件
  • 点击事件详情,确认能看到 IP、客户端、User Agent

2. 检查管理员事件

Admin Console → Events → Admin Events:

  • 修改一个用户的邮箱
  • 列表中应出现 UPDATE 事件,Resource Type 为 USER
  • 点开详情,如果开启了 REPRESENTATION 级别,会显示变更前后的 JSON

3. 从日志文件确认

# 搜索登录事件
grep "type=LOGIN" /opt/keycloak/data/log/keycloak.log | tail -5

# 搜索管理员操作
grep "operationType=UPDATE" /opt/keycloak/data/log/keycloak.log | tail -3

4. 导出事件到外部系统

Keycloak 原生不支持直接写入 Syslog/ELK——需要借助日志采集器:

Filebeat 配置(Keycloak → Elasticsearch):

filebeat.inputs:
  - type: log
    enabled: true
    paths:
      - /opt/keycloak/data/log/keycloak.log
    multiline.pattern: '^\d{4}-\d{2}-\d{2}'
    multiline.negate: true
    multiline.match: after
    fields:
      app: keycloak
      env: production
    fields_under_root: true

如果使用 Kubernetes,推荐用 kubectl logs + Fluent Bit / Loki + Promtail 而非 Filebeat。

Syslog 输出(通过 rsyslog):

# 在 keycloak.conf 中启用 Syslog handler(Keycloak 26.x)
# 注意:需要先配置 JBoss Logging Syslog handler
# 推荐方案:用 Filebeat 读日志文件再转发,避免直接 Syslog 的格式解析问题

等保 2.0 / IAM 合规要点

Keycloak 的事件日志可以覆盖等保 2.0(GB/T 22239-2019)中与 IAM 相关的以下控制点:

等保要求Keycloak 对应能力配置要点
安全审计(8.1.4.5)登录事件 + 管理员事件开启 LOGIN, LOGIN_ERROR, 及所有 Admin Event
审计记录保护数据库存储 + 外部日志平台events-db-max-age-seconds ≥ 90 天,日志平台开启只读归档
身份鉴别(8.1.4.2)登录失败记录 + 账户锁定策略LOGIN_ERROR + Brute Force Detection
访问控制(8.1.4.3)管理员事件 + 权限变更追溯Admin Event Detail = UPDATE,CREATE,DELETE
通信保密性TLS 1.2+,事件日志不包含 Token日志不输出 access_token / refresh_token(默认行为)

详细等保对照见 IAM 等保 2.0 合规对照表

常见错误

现象原因解决
Admin Console 的 Events 列表为空未开启事件保存(Save Events = OFF)或保留了 events-db-max-age-seconds 已过期检查 Realm Settings → Events → Save Events = ON
日志里看不到事件,但数据库里有只配置了 jpa store,没配置 jboss-logging listener增加 --spi-events-listener-jboss-logging-success-level=info
数据库 EVENT_ENTITY 表过大(几 GB)事件保留时间太长或高流量下未过滤高频事件缩短 events-db-max-age-seconds,关闭 REFRESH_TOKEN 等高频事件
管理员事件不显示变更内容admin-event-detail 未设置为 REPRESENTATION--spi-admin-event-detail=UPDATE,CREATE,DELETE
合规审计要求「谁在什么时候改了哪个 Realm 的什么字段」,但日志缺字段没开管理员事件或 Detail 级别不够确认 Admin Events = ON,Detail ≥ 含 UPDATE

数据库维护

Keycloak 通过定时任务自动清理过期事件(events-db-max-age-seconds)。但高流量场景下,即使开启自动清理,EVENT_ENTITYADMIN_EVENT_ENTITY 表仍可能在两次清理之间膨胀到几十 GB。

手动查询事件表大小:

SELECT
  relname AS table_name,
  pg_size_pretty(pg_total_relation_size(relid)) AS total_size
FROM pg_catalog.pg_statio_user_tables
WHERE relname IN ('event_entity', 'admin_event_entity')
ORDER BY pg_total_relation_size(relid) DESC;

手动触发清理(紧急情况):

-- 删除 90 天前的登录事件
DELETE FROM event_entity
WHERE event_time < NOW() - INTERVAL '90 days';

-- 删除 90 天前的管理员事件
DELETE FROM admin_event_entity
WHERE admin_event_time < NOW() - INTERVAL '90 days';

-- 回收空间
VACUUM FULL event_entity;
VACUUM FULL admin_event_entity;

⚠️ VACUUM FULL 会锁表,必须在维护窗口执行。日常维护优先调整 events-db-max-age-seconds 让内置清理任务接管。

进阶:自定义事件监听器(SPI)

如果内置的 jboss-loggingjpa 满足不了需求(例如直接写入 Kafka、发送告警到 PagerDuty),Keycloak 提供了 EventListenerProvider SPI 接口。

public class AuditEventListener implements EventListenerProvider {
    @Override
    public void onEvent(Event event) {
        // 将登录失败 > 5 次/分钟的事件发送到告警系统
        if (event.getType() == EventType.LOGIN_ERROR) {
            alertService.send("Suspicious login: " + event.getUserId());
        }
    }

    @Override
    public void onEvent(AdminEvent event, boolean includeRepresentation) {
        // 所有管理员删除操作即时告警
        if (event.getOperationType() == OperationType.DELETE) {
            alertService.send("Admin DELETE: " + event.getResourceType());
        }
    }

    @Override
    public void close() {}
}

自定义 SPI 需要打包成 JAR 放入 providers/ 目录并通过 kc.sh build 注册。Keycloak 26.x 的 SPI 开发流程与 24/25 兼容,详细参考 Keycloak SPI 文档

生产检查清单

  • Save Events = ON,Save Admin Events = ON
  • events-db-max-age-seconds 设置为 ≥ 90 天(等保最低要求)
  • 高频事件(REFRESH_TOKEN, TOKEN_EXCHANGE)已评估是否需要关闭
  • Admin Event Detail 包含 UPDATE,CREATE,DELETE
  • 事件日志通过 Filebeat / Fluent Bit 导出到集中式日志平台
  • 日志平台配置了只读归档,防止合规审计时被篡改
  • 确认 EVENT_ENTITY 表未持续膨胀(每周检查一次大小)
  • 数据库备份包含事件表(事件和用户数据一起备份)
  • IAM 合规团队能通过日志平台查询「最近 90 天所有管理员操作」

回滚方式

# 关掉所有事件存储(不推荐,仅紧急排错时临时使用)
kc.sh start --spi-events-store=none

恢复默认配置:在 keycloak.conf 中移除自定义事件参数后重启,或者通过 Admin Console → Realm Settings → Events 恢复默认值。

参考