calvinanglo/iam-lab
GitHub: calvinanglo/iam-lab
这是一个基于 Docker 和 Terraform 构建的生产级身份与访问管理(IAM)实验平台,完整模拟了金融机构的 SSO 联合、RBAC 权限控制、MFA 认证及 SIEM 审计流程。
Stars: 0 | Forks: 0
# IAM 实验室 — 生产级身份与访问管理
一个完全可用的企业级 IAM 平台,运行 12 个容器,具备 SSO 联合、LDAP 目录服务、RBAC 生命周期自动化、SIEM 集成以及基础设施即代码功能。该平台旨在模拟一级金融机构的身份架构。
## 实时堆栈截图
## 目录
1. [架构概览](#architecture-overview)
2. [演示内容](#what-this-demonstrates)
3. [快速开始](#quick-start)
4. [实现详解](#implementation-walkthrough)
- [步骤 1:网络架构与 TLS 终止](#step-1-network-architecture--tls-termination)
- [步骤 2:目录服务 (OpenLDAP)](#step-2-directory-services-openldap)
- [步骤 3:身份提供商 (Keycloak 26.2)](#step-3-identity-provider-keycloak-262)
- [步骤 4:SSO 联合 (OIDC + SAML)](#step-4-sso-federation-oidc--saml)
- [步骤 5:RBAC — LDAP 到令牌的角色链](#step-5-rbac--ldap-to-token-role-chain)
- [步骤 6:安全加固](#step-6-security-hardening)
- [步骤 7:可观测性与 SIEM](#step-7-observability--siem)
- [步骤 8:IGA 生命周期自动化](#step-8-iga-lifecycle-automation)
- [步骤 9:基础设施即代码 (Terraform)](#step-9-infrastructure-as-code-terraform)
- [步骤 10:CI/CD 与测试](#step-10-cicd--testing)
5. [事件处理手册](#incident-playbooks)
6. [关键设计决策](#key-design-decisions)
7. [仓库结构](#repository-structure)
## 架构概览
```
┌──────────────────────────────────────────────┐
│ BROWSER / CLIENT │
└───────────────────┬──────────────────────────┘
│ HTTPS (TLS 1.3 only)
┌───────────────────▼──────────────────────────┐
│ Nginx Reverse Proxy │
│ HSTS preload │ CSP │ Rate-limit │ OCSP │
└──┬────────┬─────────┬──────────┬────────────┘
│ │ │ │
┌──────────▼──┐ ┌──▼────┐ ┌──▼──────┐ ┌▼───────┐
│ Keycloak 26 │ │Grafana│ │Nextcloud│ │ Gitea │
│ :8443 IdP │ │ :3443 │ │ :8444 │ │ :3444 │
│ OIDC │ SAML │ │ OIDC │ │ SAML │ │ OIDC │
└──────┬──────┘ └───────┘ └─────────┘ └────────┘
│
┌────────────┴──────────────────────────────┐
│ iam-backend (internal network) │
│ ┌──────────────┐ ┌──────────────┐ │
│ │ PostgreSQL │ │ OpenLDAP │ │
│ │ :5432 │ │ :389 │ │
│ │ (KC store) │ │ (user dir) │ │
│ └──────────────┘ └──────────────┘ │
└───────────────────────────────────────────┘
┌───────────────────────────────────────────┐
│ Observability Pipeline │
│ Loki :3100 ◄── Promtail (all 12 svcs) │
│ Grafana IAM Dashboard (13 panels) │
└───────────────────────────────────────────┘
┌───────────────────────────────────────────┐
│ SIEM Integration │
│ KC Events ──► Forwarder ──► Receiver │
│ (30s poll) (gunicorn) │
└───────────────────────────────────────────┘
```
**网络分段:** `iam-backend` 是一个仅限内部的 Docker 网络 — PostgreSQL 和 OpenLDAP 对主机零端口暴露。所有外部流量均通过 Nginx TLS 终止点进入。
## 演示内容
| 领域 | 实现方式 |
|---|---|
| **企业级 SSO** | Keycloak IdP,支持 OIDC (Grafana, Gitea) 和 SAML 2.0 (Nextcloud) — 三个服务提供商,两种协议 |
| **目录服务** | 通过 `group-ldap-mapper` 与 Keycloak 联合的 OpenLDAP;OU 层级结构映射组织架构图 |
| **MFA 与升级认证** | 通过 Required Actions 强制执行 TOTP;ACR 级别 `silver`/`gold`;Grafana 要求 LoA 2 重新质询 |
| **无密码 FIDO2** | WebAuthn 浏览器流程;通过 `webauthn-register-passwordless` 注册通行密;ES256/RS256 |
| **RBAC + ABAC** | 5 个从 LDAP 组联合的 realm 角色;通过令牌声明上的 JMESPath 进行 Grafana 角色映射 |
| **IGA 自动化** | 入职/转岗/离职 + 访问认证 + 针对 Keycloak Admin API 的 JIT 特权访问 |
| **基础设施即代码** | Terraform (`mrparkers/keycloak` provider) 以声明方式管理 realm、客户端、角色和 LDAP |
| **安全加固** | 仅限 TLS 1.3、HSTS 预加载、CSP、PKCE S256、暴力破解防护、密码策略、OCSP 装订 |
| **可观测性** | 跨所有服务的 Loki + Promtail 日志聚合;Grafana IAM 运营仪表板(13 个面板) |
| **SIEM 集成** | Keycloak 事件存储 → 轮询转发器 → gunicorn WSGI 接收器;标准化的 JSON 审计跟踪 |
| **CI/CD** | GitHub Actions:配置验证、Trivy CVE 扫描、堆栈冒烟测试、集成测试 |
| **事件响应** | 5 个符合 ITIL 标准的手册(P1-P3),涵盖凭证填充、MFA 降级、SSO 中断 |
## 快速开始
### 前置条件
- Docker Desktop(推荐 WSL2 后端)
- `make`(可通过 Git Bash 或 WSL 获取)
- 添加到 `C:\Windows\System32\drivers\etc\hosts`:
```
127.0.0.1 keycloak.iam-lab.local
127.0.0.1 grafana.iam-lab.local
127.0.0.1 nextcloud.iam-lab.local
127.0.0.1 gitea.iam-lab.local
```
### 启动堆栈
```
make setup # Generate TLS certs + create .env from template
# 使用你的机密信息(管理员密码、DB 凭证)编辑 .env
make up # Start all 12 containers
make health # Verify: expect 7 passed, 0 failed
```
所有内容在首次启动时自动引导 — Keycloak realm 会导入所有预先配置的客户端、角色和 LDAP 联合。OpenLDAP 从 LDIF 加载 4 个用户角色、5 个组和 2 个服务账户。Grafana 配置 IAM 运营仪表板和 Loki 数据源。SIEM 转发器立即开始轮询 Keycloak 事件。
### 服务 URL
| 服务 | URL | 身份验证 |
|---|---|---|
| Keycloak Admin | `https://keycloak.iam-lab.local:8443` | `admin` / 见 `.env` |
| Grafana | `https://grafana.iam-lab.local:3443` | 通过 Keycloak SSO (OIDC) |
| Nextcloud | `https://nextcloud.iam-lab.local:8444` | 通过 Keycloak SSO (SAML) |
| Gitea | `https://gitea.iam-lab.local:3444` | 通过 Keycloak SSO (OIDC) |
### Makefile 操作
```
make help # Show all 18 available targets
make up # Start the full stack
make down # Stop stack (preserves data volumes)
make clean # Full teardown including volumes
make health # 7-assertion production readiness check
make logs # Tail all container logs
make backup # Backup PostgreSQL + LDAP data
make restore # Restore from backup with validation
make test # Run 20+ integration tests against live stack
make lint # Flake8 lint all Python scripts
make validate # Validate all config files (compose, nginx, realm JSON)
make certify # Run access certification report
make tf-plan # Plan Terraform changes
make tf-apply # Apply Terraform changes
```
## 实现详解
本节将详细介绍每个组件的设计、配置和集成方式。这里的每一个决策都映射到真实的企业级 IAM 部署模式。
### 步骤 1:网络架构与 TLS 终止
基础是一个分段的 Docker 网络,在边缘进行 TLS 1.3 终止。
**网络设计:**
```
# docker-compose.yml
networks:
iam-frontend:
driver: bridge
iam-backend:
driver: bridge
internal: true # No host access — DB and LDAP are isolated
```
PostgreSQL 和 OpenLDAP 仅连接到 `iam-backend`。它们没有绑定到主机的端口,无法从 Docker 网络外部访问。这模拟了生产环境如何在防火墙后隔离目录服务和数据库。
**TLS 配置 (Nginx):**
```
# nginx/nginx.conf — 仅限 TLS 1.3,不回退到 1.2
ssl_protocols TLSv1.3;
ssl_prefer_server_ciphers off; # TLS 1.3 cipher negotiation is client-driven
ssl_stapling on; # OCSP stapling for cert validation
ssl_stapling_verify on;
```
每个服务都有自己的基于 SNI 的 `server` 块,并配置了完整的安全头栈:
| 头 | 值 | 用途 |
|---|---|---|
| `Strict-Transport-Security` | `max-age=63072000; includeSubDomains; preload` | 符合 HSTS 预加载列表条件 |
| `Content-Security-Policy` | `default-src 'self'; frame-ancestors 'self'` | 防止 XSS 和点击劫持 |
| `Permissions-Policy` | `camera=(), microphone=(), geolocation=()` | 禁用不必要的浏览器 API |
| `X-Content-Type-Options` | `nosniff` | 防止 MIME 类型嗅探 |
| `Cross-Origin-Opener-Policy` | `same-origin` | 跨源隔离 |
**速率限制** 分别保护登录端点和 API:
```
limit_req_zone $binary_remote_addr zone=login:10m rate=10r/s; # Login: 10 req/s
limit_req_zone $binary_remote_addr zone=api:10m rate=30r/s; # API: 30 req/s
```
### 步骤 2:目录服务 (OpenLDAP)
LDAP 目录提供身份数据的权威来源。Keycloak 从其进行联合,而不是直接管理用户 — 这是企业 IAM 在实际工作中的运作方式。
**目录结构:**
```
dc=rbclab,dc=local
├── ou=users
│ ├── uid=jsmith (John Smith — Trading)
│ ├── uid=alee (Alice Lee — Risk)
│ ├── uid=mchen (Michael Chen — Compliance)
│ └── uid=bpatel (Bina Patel — IT Support)
├── ou=groups
│ ├── cn=traders
│ ├── cn=risk-analysts
│ ├── cn=compliance-admins
│ ├── cn=helpdesk
│ └── cn=iam-admins
└── ou=service-accounts
├── cn=readonly (Keycloak bind account — read-only)
└── cn=siem-svc (SIEM forwarder service account)
```
每个用户都是一个完整的 `inetOrgPerson`,包含部门、职位、电子邮件以及通过 `memberOf` 的组成员身份。只读服务账户是 Keycloak 用于绑定 LDAP 的账户 — 它不能修改目录数据,从而强制执行最小权限原则。
**LDIF 自动加载:**
```
# docker-compose.yml
openldap:
image: osixia/openldap:1.5.0
command: --copy-service # Required for 1.5.0 template bug
volumes:
- ./ldap/init-ldap.ldif:/container/service/slapd/assets/config/bootstrap/ldif/custom/init.ldif
environment:
LDAP_ORGANISATION: "RBC Lab"
LDAP_DOMAIN: "rbclab.local"
LDAP_TLS: "false" # TLS handled at Nginx layer
```
`--copy-service` 标志解决了 osixia/openldap 1.5.0 的一个已知启动问题,即模板文件(`replication-disable.ldif`、`root-password-change.ldif`)在预期路径中缺失。LDAP 容器上禁用了 TLS,因为它位于 Nginx TLS 终止点之后的内部网络上 — 加密仅限内部的流量只会增加延迟而无安全收益。
### 步骤 3:身份提供商 (Keycloak 26.2)
Keycloak 以生产模式运行,并在首次启动时自动导入 realm。
**生产配置:**
```
# docker-compose.yml
keycloak:
image: quay.io/keycloak/keycloak:26.2
command: >
start
--hostname=keycloak.iam-lab.local
--hostname-port=8443
--hostname-strict=false
--proxy-headers=xforwarded
--http-enabled=true
--import-realm
--health-enabled=true
```
关键点:
- `--proxy-headers=xforwarded` 告诉 Keycloak 它位于反向代理之后(Nginx 处理 TLS)
- `--import-realm` 在首次启动时自动导入 `enterprise-realm-export.json`
- `--health-enabled=true` 在管理端口 (9000) 暴露健康检查端点 — 这与 KC 26.2 中的应用程序端口 (8080) 是分开的
- 健康检查针对端口 9000,而不是 8080,因为 KC 26.2 将健康端点移至了专用的管理接口
**Realm 设置 (`enterprise`):**
| 设置 | 值 | 原因 |
|---|---|---|
| 暴力破解防护 | 5 次失败 → 锁定,60秒递增,最大 900秒 | 防止凭证填充 |
| 密码策略 | 12+ 字符,大写+小写+数字+特殊字符,历史记录 5 条 | 符合 NIST 800-63B 指南 |
| PKCE 强制执行 | 所有 OIDC 客户端均使用 S256 | OAuth 2.1 最佳实践 |
| 令牌生命周期 | 访问令牌:5分钟,SSO 空闲:30分钟,SSO 最大:10小时 | 最小化令牌暴露窗口 |
| 事件 | 认证 + 管理事件,保留 30 天 | 用于 SIEM 的审计跟踪 |
| WebAuthn | ES256/RS256,rpId `keycloak.iam-lab.local` | FIDO2 通行密支持 |
### 步骤 4:SSO 联合 (OIDC + SAML)
三个服务提供商展示了两种主要的 SSO 协议:
**Grafana — 使用 PKCE S256 的 OIDC:**
```
# Keycloak 客户端配置
Client ID: grafana
Protocol: OpenID Connect (confidential)
PKCE: S256 enforced
Redirect URI: https://grafana.iam-lab.local:3443/*
```
Grafana 通过 Keycloak 的 OIDC 授权码流程对用户进行身份验证。ID 令牌中的 `roles` 声明通过 JMESPath 映射到 Grafana 组织角色 — 交易员获得 Viewer,合规管理员获得 Editor,iam-admin 获得 Admin。
**Gitea — 使用 PKCE S256 的 OIDC:**
```
Client ID: gitea
Protocol: OpenID Connect (confidential)
PKCE: S256 enforced
Redirect URI: https://gitea.iam-lab.local:3444/user/oauth2/keycloak/callback
```
**Nextcloud — SAML 2.0:**
```
Client ID: https://nextcloud.iam-lab.local:8444/apps/user_saml/saml/metadata
Protocol: SAML 2.0
ACS URL: https://nextcloud.iam-lab.local:8444/apps/user_saml/saml/acs
```
Nextcloud 使用 SAML 是为了展示协议多样性 — 真正的企业级 IdP 需要同时支持 OIDC 和 SAML,因为遗留应用通常只支持 SAML。
**SSO 登录流程:**
```
User visits Grafana → Redirect to Keycloak login → Authenticate (password + TOTP)
→ Authorization code issued → Grafana exchanges code for tokens (with PKCE S256)
→ ID token contains roles[] claim → Grafana maps role to org permissions → Session established
```
### 步骤 5:RBAC — LDAP 到令牌的角色链
这是 RBAC 实现的核心。角色从 LDAP 流经 Keycloak 进入应用程序令牌:
```
LDAP Group (ou=groups) ──► Keycloak Group ──► Realm Role ──► Token Claim (roles[])
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
cn=traders ──► traders ──► trader ──► "trader"
cn=risk-analysts ──► risk-analysts ──► risk-analyst ──► "risk-analyst"
cn=compliance-admins ──► compliance-admins──► compliance-admin ──► "compliance-admin"
cn=helpdesk ──► helpdesk ──► helpdesk ──► "helpdesk"
cn=iam-admins ──► iam-admins ──► iam-admin ──► "iam-admin"
```
**这在 Keycloak 中如何工作:**
1. **LDAP 用户联合** 将 Keycloak 连接到 OpenLDAP 作为只读源 (`ldap://openldap:389`)
. **`group-ldap-mapper`** 在用户登录时自动将 LDAP 组同步到 Keycloak 组
3. **组到角色映射** 根据组成员身份分配 realm 角色
4. **协议映射器** 将 `roles` 声明注入到 OIDC 令牌和 SAML 断言中
5. **服务提供商** (Grafana, Gitea) 读取 `roles` 声明以确定授权
这意味着,当用户在 LDAP 中被添加到 `cn=traders` 时,他们会自动获得 `trader` realm 角色以及 Grafana 和 Gitea 中的正确权限 — 无需手动配置 Keycloak。当他们离开 LDAP 中的组时,访问权限将在下次同步时被撤销。
**用户角色及其角色分配:**
| 用户 | 部门 | LDAP 组 | Realm 角色 | Grafana 访问权限 |
|---|---|---|---|---|
| `jsmith` | 交易部 | `cn=traders` | `trader` | Viewer |
| `alee` | 风险部 | `cn=risk-analysts` | `risk-analyst` | Viewer |
| `mchen` | 合规部 | `cn=compliance-admins` | `compliance-admin` | Editor |
| `bpatel` | IT 支持 | `cn=helpdesk` | `helpdesk` | Viewer |
### 步骤 6:安全加固
堆栈的每一层都应用了安全控制:
**身份验证控制:**
| 控制 | 配置 | 基本原理 |
|---|---|---|
| 暴力破解防护 | 5 次失败 → 渐进式锁定 (60秒 → 最大 900秒) | 防止凭证填充攻击 |
| 密码策略 | 12+ 字符,复杂性规则,历史记录 5 条 | 符合 NIST SP 800-63B 标准 |
| PKCE S256 | 在所有 OIDC 客户端上强制执行 | 防御授权码拦截 (OAuth 2.1) |
| MFA 注册 | CONFIGURE_TOTP 作为 Required Action | 强制在首次登录时设置 TOTP |
| WebAuthn | ES256/RS256 认证,绑定源 | 防钓鱼身份验证 |
| 会话管理 | 访问令牌:5分钟,SSO 空闲:30分钟,SSO 最大:10小时 | 限制暴露窗口 |
**传输控制:**
| 控制 | 配置 |
|---|---|
| 仅限 TLS 1.3 | `ssl_protocols TLSv1.3` — 无 TLS 1.2 回退 |
| HSTS 预加载 | `max-age=63072000; includeSubDomains; preload` |
| OCSP 装订 | 启用以进行实时证书验证 |
| 关闭服务器令牌 | `server_tokens off` — 不泄露 Nginx 版本 |
**网络控制:**
| 控制 | 配置 |
|---|---|
| 内部网络 | `iam-backend: internal: true` — DB 和 LDAP 无法从主机访问 |
| 速率限制 | 登录:10r/s burst=20,API:30r/s burst=50 |
| 资源限制 | 通过 `deploy.resources.limits` 对所有容器设置内存和 CPU 上限 |
| 密钥卫生 | `.env`、`*.key`、`*.tfvars` 已从 git 中排除 |
### 步骤 7:可观测性与 SIEM
**日志聚合管道:**
```
All 12 containers ──► Promtail (Docker SD) ──► Loki ──► Grafana Dashboard
```
Promtail 使用 Docker 套接字服务发现来抓取每个容器,而不仅仅是 Keycloak。这是有意为之 — 安全事件可能源自任何服务。Grafana IAM 运营仪表板通过代码自动配置(无需手动设置),包含 13 个面板:
| 面板 | 类型 | 显示内容 |
|---|---|---|
| 登录成功/失败/MFA | 统计 | 带有颜色阈值的当前计数 |
| 随时间变化的认证事件 | 时间序列 | 30 秒刷新,按事件类型细分 |
| 登录失败率 | 仪表盘 | 暴力破解检测指标 |
| 实时事件流 | 表格 | 实时 Keycloak 事件日志 |
| SIEM 严重性细分 | 饼图 | HIGH/MEDIUM/LOW/INFO 分布 |
**SIEM 集成(拉取模型):**
```
Keycloak Event Store ──► siem-forwarder (30s poll) ──► siem-receiver (gunicorn WSGI)
```
SIEM 转发器每 30 秒轮询一次 Keycloak 的 Admin REST API 以获取新的身份验证和管理事件。它在 `/state/.siem_state.json` 中维护状态,以避免在重启时重新处理事件。接收器将事件标准化为结构化的 JSON 审计跟踪:
| 事件类型 | 严重性 | 可告警 |
|---|---|---|
| `LOGIN_ERROR` | HIGH | 是 |
| `REMOVE_TOTP` | HIGH | 是 |
| `ADMIN_DELETE_*` | HIGH | 是 |
| `UPDATE_PASSWORD` | MEDIUM | 否 |
| `LOGIN` | INFO | 否 |
| `LOGOUT` | LOW | 否 |
选择拉取模型而不是 Webhook 推送,是因为 Keycloak 的事件监听器 SPI 需要部署自定义 JAR,而 REST API 方法适用于标准 Keycloak 镜像,并且转发器重启时不会丢失事件(有状态检查点)。
### 步骤 8:IGA 生命周期自动化
两个 CLI 工具针对 Keycloak Admin API 自动化身份治理:
**入职 / 转岗 / 离职 (`iam_lifecycle.py`):**
```
# 入职新交易员
python3 scripts/iam_lifecycle.py joiner \
--username tchen --email tchen@rbclab.local --role trader
# 移交给风险团队
python3 scripts/iam_lifecycle.py mover \
--username tchen --old-role trader --new-role risk-analyst
# 离职(禁用 + 撤销所有会话 + 移除角色)
python3 scripts/iam_lifecycle.py leaver --username tchen
# 季度访问认证 — 标记角色超过 90 天未变更的用户
python3 scripts/iam_lifecycle.py certify --days 90
```
离职操作不仅禁用账户 — 它还会撤销所有活动会话,移除所有角色分配,并记录操作以供审计。certify 命令会生成过期角色分配的报告,用于访问审查。
**即时特权访问 (`jit_access.py`):**
```
# 授予临时 iam-admin 角色(2 小时,需附带事故工单)
python3 scripts/jit_access.py elevate \
--username bpatel --role iam-admin --duration 120 --reason "P1 INC0001234"
# 列出活跃的 JIT 授权
python3 scripts/jit_access.py list
# 自动撤销过期的授权(专为 cron 设计:*/5 * * * *)
python3 scripts/jit_access.py expire
# 手动撤销
python3 scripts/jit_access.py revoke --username bpatel --role iam-admin
```
JIT 访问为紧急情况实现了最小权限原则。服务台用户可以临时提升为 `iam-admin`,但需要强制提供事件工单参考、自动过期和完整的审计日志记录。
### 步骤 9:基础设施即代码 (Terraform)
`terraform/` 目录使用 [`mrparkers/keycloak`](https://registry.terraform.io/providers/mrparkers/keycloak/latest) provider 以声明方式管理 Keycloak realm:
```
cd terraform
cp secrets.tfvars.example secrets.tfvars # Fill in KC admin credentials
terraform init
terraform plan -var-file="secrets.tfvars"
terraform apply -var-file="secrets.tfvars"
```
**Terraform 管理的内容:**
| 资源 | 数量 | 详情 |
|---|---|---|
| `keycloak_realm` | 1 | 包含所有安全设置的企业 realm |
| `keycloak_role` | 5 | trader, risk-analyst, compliance-admin, helpdesk, iam-admin |
| `keycloak_group` | 5 | 附带角色映射 |
| `keycloak_ldap_user_federation` | 1 | 包含所有映射器的完整 LDAP 配置 |
| `keycloak_openid_client` | 2 | Grafana 和 Gitea (OIDC + PKCE) |
| `keycloak_saml_client` | 1 | Nextcloud (SAML 2.0) |
**为什么同时使用 Terraform 和 JSON realm 导出?** JSON 导出在首次 `docker compose up` 时引导 realm — 这是“第 0 天”的配置。Terraform 以声明方式管理持续变更,支持 IdP 管理的 GitOps 工作流。这种双重方法正是生产级 IAM 团队大规模管理 Keycloak 的方式:realm 导出用于初始配置,Terraform 用于变更管理。
### 步骤 10:CI/CD 与测试
**GitHub Actions 流水线(4 个作业):**
| 作业 | 验证内容 |
|---|---|
| **validate** | Docker Compose 语法、Nginx 配置、Grafana 仪表板 JSON、realm 导出 schema、Promtail YAML、密钥泄露检查 |
| **python-lint** | 对所有 Python 文件进行 Flake8 + 语法检查,对所有 Shell 脚本进行 Bash 语法检查 |
| **trivy-scan** | 对 8 个容器镜像进行 CVE 扫描,并将 SARIF 上传至 GitHub Security 选项卡 |
| **stack-smoke** | 完整堆栈启动、OIDC 发现端点验证、管理员令牌获取、realm 自动导入检查 |
**集成测试(针对实时堆栈的 20+ 项断言):**
```
export KC_ADMIN_PASS=
make test
```
测试端到端验证整个身份链:
| 测试 | 证明内容 |
|---|---|
| OIDC 发现 | Keycloak 发布有效的 `.well-known/openid-configuration` |
| 管理员令牌 | 服务账户身份验证正常工作 |
| Realm 配置 | Enterprise realm 存在,且显示名称和设置正确 |
| 暴力破解 | `bruteForceProtected: true`,`maxLoginFailures: 5` |
| 密码策略 | 12 字符最小长度及复杂性要求 |
| Realm 角色 | 所有 5 个自定义角色均存在 (trader, risk-analyst 等) |
| OIDC 客户端 | Grafana 和 Gitea 客户端存在,且强制执行 PKCE S256 |
| LDAP 同步 | 所有 4 个用户角色均从 LDAP 同步 (jsmith, alee, mchen, bpatel) |
| 组映射 | LDAP 组正确映射到 Keycloak 组 |
| 角色链 | 组具有正确的 realm 角色分配 |
| SIEM 健康状况 | 接收器端点响应,事件正在被摄取 |
| SP 健康状况 | Grafana、Gitea 和 Nextcloud 可访问 |
## 事件处理手册
五项符合 ITIL 标准的事件响应程序,涵盖最常见的 IAM 故障场景:
| 手册 | 优先级 | 场景 | 关键操作 |
|---|---|---|---|
| [P1: 大规模登录失败](docs/incident-playbooks/P1-mass-login-failures.md) | P1 — 严重 | 凭证填充 / 账户锁定潮 | 阻断源 IP,检查暴力破解日志,批量解锁 |
| [P1: 未授权的管理员操作](docs/incident-playbooks/P1-unauthorized-admin-action.md) | P1 — 严重 | 检测到权限提升 | 撤销会话,审计管理员事件,检查 JIT 授权 |
| [P2: MFA 降级](docs/incident-playbooks/P2-mfa-degradation.md) | P2 — 高 | TOTP 故障激增 / 认证器应用中断 | 检查 OTP 时钟同步,经批准后临时绕过 |
| [P2: SSO 中断](docs/incident-playbooks/P2-sso-outage.md) | P2 — 高 | Keycloak IdP 无法访问 | 健康检查、数据库连接、故障转移程序 |
| [P3: 锁定潮](docs/incident-playbooks/P3-lockout-wave.md) | P3 — 中等 | 针对部分用户的有针对性锁定 | 识别模式,选择性解锁,调整阈值 |
每本手册均包含检测标准、升级路径、补救步骤和事后审查检查清单。
## 关键设计决策
**为什么选择 Keycloak + OpenLDAP 而不是托管 IdP?**
这展示了对 OIDC、SAML 和 LDAP 协议的深入理解,而不是 SaaS 控制台配置。企业环境通常运行混合身份堆栈,其中 IdP 从现有目录进行联合。这反映了一级金融机构的架构。
**为什么在 JSON realm 导出之外还要使用 Terraform?**
JSON 导出在首次 `docker compose up` 时引导 realm。Terraform 以声明方式管理持续的配置变更,支持 IdP 管理的 GitOps 工作流 — 这是大规模生产中使用的模式。这种双重方法是有意的,反映了真实的 IAM 运营。
**为什么在机密客户端上使用 PKCE S256?**
这是 OAuth 2.1 草案规范的最佳实践。即使是机密客户端也能从 PKCE 中受益,作为防御授权码拦截的纵深防御。这现在是必需项,而非可选项。
**为什么使用拉取模型的 SIEM 而不是 Webhooks?**
Keycloak 的事件监听器 SPI 需要构建和部署自定义 JAR。REST API 轮询方法适用于标准 Keycloak 镜像,转发器重启时不会丢失事件(有状态检查点),并且在运维上更易于维护。
**为什么使用 Required Actions 来实现 MFA,而不是自定义身份验证流程?**
Keycloak 26 改变了 `ConditionalUserConfiguredAuthenticator` 在复制的浏览器流程中的评估方式 — 它在建立用户身份之前运行,从而破坏了自定义流程中的条件 OTP。Required Actions 是 KC 26 中强制 MFA 注册的正确模式,并且在版本升级之间能可靠工作。
**为什么 Promtail 抓取所有容器,而不仅仅是 Keycloak?**
安全事件可能源自堆栈中的任何服务。将日志收集限制在 Keycloak、LDAP 和 Nginx 意味着 Grafana 认证失败、Gitea 登录事件和 SIEM 接收器警报会从可观测性管道中丢弃。全面的抓取是安全基线。
**为什么 SIEM 接收器使用 gunicorn 而不是 Flask 的开发服务器?**
Flask 的内置服务器是单线程的,不适合生产工作负载。Gunicorn 提供了适当的工作进程管理、优雅重启和并发请求处理 — 即使在实验室环境中,运行生产级基础设施也体现了正确的运维思维。
## 项目
每个实现领域的详细文档:
| # | 项目 | 技术 | 文档 |
|---|---|---|---|
| P1 | SSO 联合 | OIDC, SAML 2.0, LDAP, group-ldap-mapper | [P1-SSO-Federation.md](docs/projects/P1-SSO-Federation.md) |
| P2 | MFA 与升级认证 | TOTP Required Actions, ACR/LoA 级别, gold/silver 层级 | [P2-MFA-StepUp.md](docs/projects/P2-MFA-StepUp.md) |
| P3 | 无密码 FIDO2 | WebAuthn, passkeys, 自定义浏览器流程, origin-binding | [P3-Passwordless-FIDO2.md](docs/projects/P3-Passwordless-FIDO2.md) |
| P4 | RBAC, ABAC 与生命周期 | Realm 角色, JMESPath ABAC, 入职/转岗/离职, JIT PAM | [P4-RBAC-Lifecycle.md](docs/projects/P4-RBAC-Lifecycle.md) |
| P5 | 监控与事件响应 | Loki, Grafana, SIEM, 访问认证, 手册 | [P5-Monitoring-Incident-Audit.md](docs/projects/P5-Monitoring-Incident-Audit.md) |
## 仓库结构
```
iam-lab/
├── docker-compose.yml # 12-container production stack (auto-bootstrap)
├── docker-compose.override.yml.example # Dev mode overrides (debug logging, hot reload)
├── .env.example # Documented environment variable template
├── Makefile # 18 ops targets: setup, up, down, health, test, tf-*
├── .github/workflows/ci.yml # CI: validate + lint + Trivy CVE scan + smoke test
│
├── nginx/
│ └── nginx.conf # TLS 1.3, HSTS, CSP, rate-limiting, OCSP stapling
├── certs/ # Generated TLS certs (keys gitignored)
│
├── keycloak/
│ └── enterprise-realm-export.json # Full realm config (auto-imported on first boot)
│
├── ldap/
│ ├── init-ldap.ldif # 4 users + 5 groups + 2 service accounts
│ └── bootstrap.sh # LDAP password hashing helper
│
├── grafana/
│ ├── provisioning/ # Loki datasource + dashboard provider configs
│ └── dashboards/iam-operations.json # 13-panel IAM operations dashboard
│
├── monitoring/
│ └── promtail-config.yml # Docker SD scraping all containers
│
├── siem-receiver/
│ ├── app.py # Flask SIEM event normalizer
│ └── Dockerfile # gunicorn WSGI production build
│
├── siem-forwarder/
│ ├── siem_forwarder.py # Keycloak event store poller
│ └── Dockerfile # Multi-stage production build
│
├── scripts/
│ ├── iam_lifecycle.py # Joiner/Mover/Leaver/Certify CLI
│ ├── jit_access.py # JIT privileged access management
│ ├── healthcheck.sh # 7-assertion production readiness check
│ ├── backup.sh # PostgreSQL + LDAP backup with verification
│ └── restore.sh # Restore with validation
│
├── terraform/
│ ├── main.tf # Realm, roles, clients, LDAP (mrparkers/keycloak)
│ ├── variables.tf # Parameterized configuration
│ ├── outputs.tf # Terraform outputs
│ └── providers.tf # Provider + version constraints
│
├── tests/
│ ├── test_integration.py # 20+ assertions against live stack
│ ├── conftest.py # Pytest fixtures (KC token, OIDC discovery)
│ └── requirements.txt # Test dependencies
│
└── docs/
├── projects/P1-P5*.md # Detailed project specifications
├── incident-playbooks/ # 5 ITIL-aligned response procedures
├── runbooks/ # Keycloak realm setup procedures
└── security/trivy-scan-report.md # CVE scan results and remediation plan
```
Keycloak 管理控制台 — Enterprise IAM Realm
RBAC Realm 角色 — 从 LDAP 组映射的 5 个自定义角色
SSO 客户端 — Grafana (OIDC)、Gitea (OIDC)、Nextcloud (SAML)
Grafana OIDC 客户端 — 重定向 URI 与 PKCE S256 配置
LDAP 用户联合 — OpenLDAP 目录集成
联合用户 — LDAP 同步的用户目录
暴力破解检测 — 5 次失败后渐进式锁定
Grafana — 使用“通过 Keycloak SSO 登录”的 OIDC SSO
IAM 运营仪表板 — 实时身份验证事件监控
 Loki 日志聚合 — 来自所有 12 个容器的集中式日志管道
Gitea — 具备 OIDC SSO 的自托管 Git 服务
标签:Docker, ECS, FIDO2, Gitea, Grafana, Homelab, IAM, JSONLines, Keycloak, MFA, Nextcloud, NIDS, OIDC, OpenLDAP, PB级数据处理, PKCE, RBAC, SAML, SSO, Terraform, 企业级架构, 单点登录, 基于角色的访问控制, 多因素认证, 安全信息与事件管理, 安全助手, 安全运维, 安全防御评估, 家庭实验室, 容器化, 搜索引擎爬取, 暴力破解防护, 构建工具, 测试用例, 用户生命周期管理, 目录服务, 网络安全, 联邦认证, 请求拦截, 身份认证与访问管理, 逆向工具, 隐私保护, 零信任