认证与授权 SSOT¶
SSOT Key:
platform.sso核心定义: 定义基于 Authentik (IdP) 的统一身份认证体系,以及应用接入规范。
1. 真理来源 (The Source)¶
原则:身份由 Authentik 管理,应用通过 Forward Auth 或 OIDC 接入。
| 维度 | 物理位置 (SSOT) | 说明 |
|---|---|---|
| Authentik 部署 | platform/10.authentik/compose.yaml |
SSO 服务定义 |
| 部署任务 | platform/10.authentik/deploy.py |
pre-compose / composing / post-compose |
| 共享任务 | platform/10.authentik/shared_tasks.py |
SSO 应用自动化 |
| Token 初始化 | platform/10.authentik/init-token.sh |
Root Token 创建 |
2. Token 层级¶
原则:与 Vault Token 层级平行设计。
| Token | Vault Key | 权限范围 | 用途 |
|---|---|---|---|
| AUTHENTIK_ROOT_TOKEN | secret/platform/<env>/authentik/root_token |
全局管理 | 创建应用、管理组 |
| AUTHENTIK_APP_TOKEN | secret/platform/<env>/<service>/sso_* |
单应用 | 服务 SSO 配置(未来) |
Vault Token Hierarchy Authentik Token Hierarchy
───────────────────── ─────────────────────────
VAULT_ROOT_TOKEN AUTHENTIK_ROOT_TOKEN
├─ vault-policy-X ├─ create-proxy-app
└─ VAULT_APP_TOKEN └─ AUTHENTIK_APP_TOKEN
(per-service) (per-service)
3. 访问控制模型¶
3.1 接入方式¶
| 方式 | 适用场景 | 说明 |
|---|---|---|
| Forward Auth | 静态服务、无法改代码 | Traefik 中间件检查,通过标签配置 |
| OIDC | 原生支持 OIDC 的应用 | 标准 OAuth2 流程 |
3.2 组策略 (Group-Based Access)¶
访问由 Expression Policy 控制,检查组成员身份:
```python
策略表达式¶
return ak_is_group_member(request.user, name='admins') ```
| 组名 | 用途 | 创建方式 |
|---|---|---|
admins |
平台管理员,可访问所有管理应用 | invoke authentik.shared.setup-admin-group |
developers |
开发团队 | 手动创建 |
users |
普通用户 | 手动创建 |
注意:Authentik 没有固定的 admin 组名。组的
is_superuser属性控制 Authentik 后台权限,与应用访问控制无关。
4. 标准操作程序 (Playbooks)¶
SOP-001: 初始化 SSO 系统¶
```bash
1. 创建 Authentik Root Token(需要 Vault Root)¶
export VAULT_ROOT_TOKEN=
2. 设置 admin 组并添加 akadmin¶
invoke authentik.shared.setup-admin-group ```
SOP-002: 新增 Forward Auth 应用(推荐)¶
```bash
使用 CLI 自动创建¶
invoke authentik.shared.create-proxy-app \ --name="Portal" \ --slug="portal" \ --external-host="https://home${ENV_DOMAIN_SUFFIX}.${INTERNAL_DOMAIN}" \ --internal-host="platform-portal${ENV_SUFFIX}" \ --port=8080
多组访问¶
invoke authentik.shared.create-proxy-app \ --name="Dev Tools" \ --slug="devtools" \ --external-host="https://dev.example.com" \ --internal-host="devtools" \ --allowed-groups="admins,developers" ```
Compose 配置(已自动生成,或手动添加):
yaml
labels:
# Forward auth through Authentik
- "traefik.http.middlewares.portal-auth${ENV_DOMAIN_SUFFIX}.forwardauth.address=http://platform-authentik-server${ENV_SUFFIX}:9000/outpost.goauthentik.io/auth/traefik"
- "traefik.http.middlewares.portal-auth${ENV_DOMAIN_SUFFIX}.forwardauth.trustForwardHeader=true"
- "traefik.http.middlewares.portal-auth${ENV_DOMAIN_SUFFIX}.forwardauth.authResponseHeaders=X-authentik-username,X-authentik-groups,X-authentik-email,X-authentik-name,X-authentik-uid"
- "traefik.http.routers.portal${ENV_DOMAIN_SUFFIX}.middlewares=portal-auth${ENV_DOMAIN_SUFFIX}@docker"
SOP-003: 新增 OIDC 应用¶
- 触发条件: 应用原生支持 OIDC
- 步骤:
- 登录 Authentik UI:
https://sso${ENV_DOMAIN_SUFFIX}.${INTERNAL_DOMAIN} - 创建 Provider (OAuth2/OIDC),记录 Client ID/Secret
- 创建 Application 并绑定 Provider
- 将 Client Secret 写入 Vault:
bash vault kv put secret/platform/<env>/<app> \ client_id=... \ client_secret=... - 在应用侧配置 OIDC 回调
- 登录 Authentik UI:
SOP-004: 轮换 Client Secret¶
- 在 Authentik UI 重新生成 Secret
- 更新 Vault 中对应路径
- 触发应用重启加载新配置
5. 访问流程¶
用户访问 https://home${ENV_DOMAIN_SUFFIX}.${INTERNAL_DOMAIN}
│
▼
┌────────────────────┐
│ Traefik │
│ Forward Auth 检查 │
└────────────────────┘
│
▼
┌────────────────────┐ 未登录 ┌────────────────────┐
│ Authentik Server │ ───────────────▶│ 登录页面 │
│ 检查 session │ │ 输入用户名密码 │
└────────────────────┘ └────────────────────┘
│ 已登录 │
▼ │
┌────────────────────┐ │
│ 检查组成员身份 │◀────────────────────────┘
│ ak_is_group_member│
└────────────────────┘
│
┌────┴────┐
│ │
在组 不在组
│ │
▼ ▼
200 OK 403 Forbidden
转发请求 拒绝访问
6. 设计约束 (Dos & Don'ts)¶
✅ 推荐模式 (Whitelist)¶
- Forward Auth 优先:静态服务使用 Forward Auth,无需改代码
- 组策略管理:通过 Authentik Group 管理访问权限
- CLI 自动化:使用
invoke authentik.shared.*任务 - 禁用 Dokploy 域名:SSO 保护的服务设置
subdomain = None,使用 compose.yaml Traefik labels
⛔ 禁止模式 (Blacklist)¶
- 反模式 A: 禁止多层认证叠加(Portal Gate + App OIDC)
- 反模式 B: 禁止共享 Client Secret(每应用独立)
- 反模式 C: 禁止在代码中硬编码 Token
- 反模式 D: 禁止同时使用 Dokploy 域名配置和 compose.yaml Traefik labels(会冲突)
📋 SSO 保护服务配置清单¶
- deploy.py: 设置
subdomain = None(禁用 Dokploy 域名配置) - compose.yaml: 添加 Traefik labels(路由 + forwardauth middleware)
- Cloudflare: 确保泛域名
*.${INTERNAL_DOMAIN}已解析到 VPS - Authentik: 运行
invoke authentik.shared.create-proxy-app - 验证: 确认 Dokploy UI 中该服务的 Domain 字段为空
域名分层:
- 公网域名(用户访问):home${ENV_DOMAIN_SUFFIX}.${INTERNAL_DOMAIN} → Cloudflare → Traefik → ForwardAuth → Portal
- 容器域名(内部通信):platform-portal${ENV_SUFFIX}:8080 ← Traefik ← platform-authentik-server${ENV_SUFFIX}:9000
7. 验证与测试 (The Proof)¶
```bash
1. 检查 Authentik 健康状态¶
invoke authentik.shared.status
2. 列出已配置的应用¶
invoke authentik.shared.list-apps
3. 测试未登录访问(应重定向)¶
curl -I https://home${ENV_DOMAIN_SUFFIX}.${INTERNAL_DOMAIN}
Expected: 302 → sso${ENV_DOMAIN_SUFFIX}.zitian.party¶
4. 测试已登录访问¶
浏览器登录后,检查 Network 面板¶
Expected: 200 OK,响应包含 X-authentik-* headers¶
5. 测试非管理员访问¶
创建普通用户,不加入 admins 组¶
Expected: 403 Forbidden¶
```
8. 故障排查¶
| 问题 | 可能原因 | 解决方案 |
|---|---|---|
| "Token not found in Vault" | Root Token 未创建 | invoke authentik.shared.create-root-token |
| 401 Unauthorized on API | Token 过期或无效 | 重新创建 Root Token |
| 403 Forbidden after login | 用户不在允许的组 | 将用户加入组或检查策略配置 |
| Forward Auth 不生效 | Traefik 配置错误 | 检查 compose labels 和 Traefik logs |
| 重定向循环 | 域名配置不匹配 | 检查 external_host 与实际域名 |