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 集成以及基础设施即代码功能。该平台旨在模拟一级金融机构的身份架构。 ## 实时堆栈截图
Keycloak 管理控制台 — Enterprise IAM Realm ![Enterprise IAM Realm](https://static.pigsec.cn/wp-content/uploads/repos/2026/04/a93cc56b3d155704.png)
RBAC Realm 角色 — 从 LDAP 组映射的 5 个自定义角色 ![Realm Roles](https://static.pigsec.cn/wp-content/uploads/repos/2026/04/08347b8e2e155705.png)
SSO 客户端 — Grafana (OIDC)、Gitea (OIDC)、Nextcloud (SAML) ![Clients](https://static.pigsec.cn/wp-content/uploads/repos/2026/04/2420381df8155706.png)
Grafana OIDC 客户端 — 重定向 URI 与 PKCE S256 配置 ![Grafana Client Settings](https://static.pigsec.cn/wp-content/uploads/repos/2026/04/986152e0fb155708.png)
LDAP 用户联合 — OpenLDAP 目录集成 ![LDAP Settings](https://static.pigsec.cn/wp-content/uploads/repos/2026/04/05f818d915155709.png)
联合用户 — LDAP 同步的用户目录 ![Users](https://static.pigsec.cn/wp-content/uploads/repos/2026/04/b6523cb0c5155710.png)
暴力破解检测 — 5 次失败后渐进式锁定 ![Brute Force](https://static.pigsec.cn/wp-content/uploads/repos/2026/04/35e0d4dbeb155711.png)
Grafana — 使用“通过 Keycloak SSO 登录”的 OIDC SSO ![Grafana SSO](https://static.pigsec.cn/wp-content/uploads/repos/2026/04/b63b0f764e155712.png)
IAM 运营仪表板 — 实时身份验证事件监控 ![IAM Dashboard](https://static.pigsec.cn/wp-content/uploads/repos/2026/04/95569edac3155713.png) ![IAM Dashboard Events](https://static.pigsec.cn/wp-content/uploads/repos/2026/04/d41e9b7cef155715.png)
Loki 日志聚合 — 来自所有 12 个容器的集中式日志管道 ![Grafana Data Sources](https://static.pigsec.cn/wp-content/uploads/repos/2026/04/b81bef198b155716.png)
Gitea — 具备 OIDC SSO 的自托管 Git 服务 ![Gitea](https://static.pigsec.cn/wp-content/uploads/repos/2026/04/187503a2d7155717.png)
## 目录 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 ```
标签:Docker, ECS, FIDO2, Gitea, Grafana, Homelab, IAM, JSONLines, Keycloak, MFA, Nextcloud, NIDS, OIDC, OpenLDAP, PB级数据处理, PKCE, RBAC, SAML, SSO, Terraform, 企业级架构, 单点登录, 基于角色的访问控制, 多因素认证, 安全信息与事件管理, 安全助手, 安全运维, 安全防御评估, 家庭实验室, 容器化, 搜索引擎爬取, 暴力破解防护, 构建工具, 测试用例, 用户生命周期管理, 目录服务, 网络安全, 联邦认证, 请求拦截, 身份认证与访问管理, 逆向工具, 隐私保护, 零信任