Keycloak 审计日志配置与 IAM 合规实践 | IDaaS Book
场景
你把 Keycloak 部署到了生产环境,用户正常登录、应用正常运行。然后发生了三件事之一:
- 安全团队问你要「最近 90 天的所有管理员操作日志」
- 合规审计要求证明「所有登录失败有记录、所有权限变更有追溯」
- 运维发现数据库越来越大,怀疑是 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_EXCHANGE和REFRESH_TOKEN在微服务高频调用时会产生海量事件,可以考虑只用*_ERROR变体。
3. 管理员事件详细级别
管理员事件的详细级别决定了日志中是否包含请求的 representation(变更前后的完整 JSON 数据)。这在排查「谁改了某个配置」时至关重要:
# 记录完整的变更前后数据(推荐生产开启)
--spi-admin-event-detail=UPDATE,CREATE,DELETE可选值:OFF(只记录操作类型)、REPRESENTATION(记录变更数据)、UPDATE,CREATE,DELETE(按操作类型选择)。
验证
1. 检查事件是否在记录
登录 Keycloak Admin Console → Events → Login Events:
- 尝试登录(成功和故意失败各一次)
- 列表中应出现
LOGIN和LOGIN_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 -34. 导出事件到外部系统
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_ENTITY 和 ADMIN_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-logging 和 jpa 满足不了需求(例如直接写入 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 恢复默认值。