authentik 身份平台:从 Flow 策略到 Outpost、高可用与退出
一套内部系统接入 authentik 后,所有员工都能看到应用入口,未加入授权组的人甚至也能完成登录。管理员检查了 Provider 的 client ID 和回调地址,都没有发现异常;问题出在 Application 没有 binding 时默认允许访问。身份协议成功只证明“这个人是谁”,并不自动证明“这个人可以进入这个应用”,授权策略必须有正向与拒绝两条证据。
另一个现场发生在扩容之后:server 副本健康,OIDC 登录却间歇性超时,worker 任务持续积压,Outpost 与核心版本也不一致。authentik 把交互请求、后台任务、协议 Provider、策略 Flow 和边缘 Outpost 组合成平台;server/worker 无状态不等于整个平台无状态,PostgreSQL、上传文件、密钥、入口和 Outpost 连接共同决定服务是否真的可用。
从 Application 与 Provider 分清展示和协议
authentik 是身份提供方与访问平台,可提供 OIDC、SAML、LDAP、代理等接入方式,并用 Flow、Stage 和 Policy 组织认证、注册、恢复与授权。它适合需要统一身份入口、灵活策略、外部身份源和身份感知代理的团队;若项目只需要在测试中签发固定 JWT,完整平台的数据库、worker 和升级成本并不划算。
Application 是用户看到和被授权访问的应用对象,保存名称、slug、启动地址、分组和访问 binding;Provider 实现 OIDC、SAML、Proxy、LDAP 等协议。两者通常一对一关联,但角色并不相同:改 Application 图标不会改变 issuer,改 OAuth2/OIDC Provider 的 signing key、redirect URI 或 issuer mode 却会直接改变令牌验证。官方的Application 模型说明可用于核对二者关联。
Flow 是一条用户交互流程,Stage 是密码、身份识别、MFA、用户写入等步骤,Policy 判断某个 Stage、Flow 或 Application binding 是否执行/允许。Source 把 LDAP、OAuth、SAML 等外部身份带入平台;Outpost 在靠近目标应用的位置运行 Proxy、LDAP、RADIUS 等能力。用户、组、角色、服务账号、token、证书与事件则形成访问生命周期和审计状态。
用官方 Compose 建立可恢复起点
authentik 采用发布系列与 patch 版本,部署时应从安装入口确认目标版本并固定镜像与 Compose/Helm 资产。latest 不是可靠升级通道。下面使用官方版本化 Compose 文件,适合测试和小规模环境;下载后应审查 diff,而不是长期维护一份脱离上游的复制模板。
mkdir authentik-lab && cd authentik-lab
export AUTHENTIK_RELEASE='<PINNED_RELEASE>'
curl -fsSLo compose.yml \
"https://goauthentik.io/version/${AUTHENTIK_RELEASE}/lifecycle/container/compose.yml"
printf 'PG_PASS=%s\n' "$(openssl rand -base64 36 | tr -d '\n')" > .env
printf 'AUTHENTIK_SECRET_KEY=%s\n' "$(openssl rand -base64 60 | tr -d '\n')" >> .env
printf 'AUTHENTIK_TAG=%s\n' "$AUTHENTIK_RELEASE" >> .env
chmod 600 .env
docker compose -f compose.yml config --services
docker compose -f compose.yml pull
docker compose -f compose.yml up -d<PINNED_RELEASE> 替换为发布页上的精确 patch,例如目标 2026.5 发布系列中的受控版本;首次使用这种日期形态时要把它理解为产品版本标识,不是部署日期。首次初始化通过 http://localhost:9000/if/flow/initial-setup/ 设置 akadmin 密码,URL 末尾斜杠不可省略。.env、数据库转储和初始凭据不得提交到 Git。
官方 Compose 可能为了 managed Outpost 挂载 Docker socket。它等同于高权限宿主控制接口;不需要自动管理容器 Outpost 时移除挂载,需要时使用受限 socket proxy 并限制 worker 网络与权限。Compose 的定位、端口和更新方式见Docker Compose 安装指南。
Kubernetes 上使用官方 Helm chart,生产应连接外部高可用 PostgreSQL;chart 内置数据库只适合演示/测试。server、worker 分别设置副本、requests/limits、readiness 和网络策略,Outpost 另行考虑调度与连通。Helm 值和依赖随版本变化,应从Kubernetes 安装指南生成受审查的 values。
第一组正反实验:健康与 OIDC 元数据要同时成立
创建 OAuth2/OIDC Provider,client ID 设为 inventory-web,redirect URI 精确登记 http://localhost:5173/callback,选择非对称 Signing Key;再创建 slug 为 inventory 的 Application 并关联 Provider。随后执行:
export AK_BASE_URL=http://localhost:9000
export AK_APP_SLUG=inventory
curl -fsS "$AK_BASE_URL/-/health/live/"
curl -fsS "$AK_BASE_URL/-/health/ready/"
docker compose -f compose.yml exec worker ak healthcheck
curl -fsS \
"$AK_BASE_URL/application/o/$AK_APP_SLUG/.well-known/openid-configuration" \
| jq '{issuer, authorization_endpoint, token_endpoint, jwks_uri}'
curl -fsS "$AK_BASE_URL/application/o/$AK_APP_SLUG/jwks/" \
| jq '{key_count:(.keys|length), kids:[.keys[].kid]}'正向结果是 live/ready 返回成功、worker healthcheck 通过、discovery 的 issuer 与对外地址一致、JWKS 至少有一把带 kid 的公钥。live 只证明进程存活,ready 与 worker 检查覆盖 PostgreSQL,OIDC 元数据再证明 Provider/Application 路径可读;三者缺一都不能宣布登录链可用。
反向实验访问不存在的 slug,并确认它不会返回另一个 Application 的元数据;再把本地预期 issuer 与 discovery 实际值比较:
test "$(curl -sS -o /dev/null -w '%{http_code}' \
"$AK_BASE_URL/application/o/not-an-application/.well-known/openid-configuration")" = "404"
EXPECTED_ISSUER="$AK_BASE_URL/application/o/$AK_APP_SLUG/"
ACTUAL_ISSUER=$(curl -fsS \
"$AK_BASE_URL/application/o/$AK_APP_SLUG/.well-known/openid-configuration" \
| jq -r .issuer)
test "$ACTUAL_ISSUER" = "$EXPECTED_ISSUER"
printf 'issuer=%s\n' "$ACTUAL_ISSUER"若第二个断言失败,先核对 Provider issuer mode、反向代理 Host/Forwarded 头和外部 URL。资源服务必须校验 discovery 返回的 issuer,不能因为 JWKS 可达就接受另一个签发者。
Flow、Stage 与 Policy 如何产生访问结果
认证 Flow 决定用户如何证明身份,授权 Flow 决定已识别用户能否继续进入 Provider。Stage 按顺序收集或改变状态,Policy 绑定在 Stage/Flow/Application 上并产生通过、拒绝或不匹配结果。策略表达式可以读取用户、组、请求、设备和上下文,但越复杂越需要稳定输入、单元化命名和拒绝测试,否则界面中的顺序调整就可能改变安全语义。
Application binding 是应用级访问门。没有 binding 时默认允许所有用户,因此“空配置”不是最小权限。可以把 inventory-users 组绑定为允许,再用不属于该组的测试用户证明拒绝;若还需要例外,应建立显式 policy,而不是把管理员组全局放行。应用可见性、启动入口可见与 Provider 最终授权也要分别验证,不能用“看不到磁贴”代替后端拒绝。
Source 负责外部用户进入和属性映射。LDAP 同步、OAuth/SAML 首次登录、用户名冲突和组映射都需要确定权威字段与合并策略。外部目录禁用账号后,还要确认同步任务执行、authentik 用户状态改变、现有会话/token 被撤销,以及下游应用不再接受旧身份。worker 任务失败可能让目录已变更而平台状态未变更,所以离职核销必须读取任务与事件证据。
项目接入从 Discovery 和 Claim 合同开始
OIDC 客户端应使用 Application 对应的 discovery URL,不从 URL 外形猜测 token endpoint。浏览器 public client 使用 Authorization Code + PKCE,不保存 secret;服务端 confidential client 的 secret 进入秘密管理器。资源服务读取 issuer/JWKS,显式校验 audience、有效期和所需 claim,再把 authentik group/entitlement 映射为本地权限。
spring:
security:
oauth2:
resourceserver:
jwt:
issuer-uri: ${OIDC_ISSUER_URI:http://localhost:9000/application/o/inventory/}前端只保留公开参数:
VITE_OIDC_AUTHORITY=http://localhost:9000/application/o/inventory/
VITE_OIDC_CLIENT_ID=inventory-web
VITE_OIDC_REDIRECT_URI=http://localhost:5173/callbackProperty Mapping 决定哪些用户属性进入 token。应为每个 claim 指定业务含义、数据 owner 和消费方,避免把用户对象、全量组和敏感属性打包进每个令牌。集成测试至少固定 iss、aud、sub、exp、签名 kid 和一个业务权限 claim,并分别验证有权限与无权限用户。
不支持 OIDC/SAML 的旧应用可以使用 Proxy Provider 与 Outpost。Outpost 在请求前完成认证,再向 upstream 注入受控 header;应用必须只信任来自 Outpost 的网络路径,并清除客户端伪造的同名 header。embedded、managed 和 manual Outpost 的升级、token、网络和故障域不同,选型依据应是部署边界与控制权限,而不是“哪个更省一步”。
第二组正反实验:显式允许必须伴随显式拒绝
在管理界面创建 inventory-users 组,把测试用户 allowed-user 加入组,把 denied-user 保持在组外;为 Application 创建只允许该组的 binding。下面生成 PKCE 授权 URL,两位用户分别在隔离浏览器会话中打开同一地址:
VERIFIER=$(openssl rand -base64 64 | tr -d '=+/' | cut -c1-64)
CHALLENGE=$(printf '%s' "$VERIFIER" | openssl dgst -binary -sha256 \
| openssl base64 -A | tr '+/' '-_' | tr -d '=')
STATE=$(openssl rand -hex 16)
printf '%s\n' \
"$AK_BASE_URL/application/o/authorize/?client_id=inventory-web&response_type=code&scope=openid%20profile&redirect_uri=http%3A%2F%2Flocalhost%3A5173%2Fcallback&code_challenge_method=S256&code_challenge=$CHALLENGE&state=$STATE"正向会话使用 allowed-user,应完成 Flow 并回到精确 callback,返回的 state 必须等于本地值。反向会话使用 denied-user,应停在拒绝结果,不能生成可兑换授权码。随后在 Events 中按 Application、用户稳定标识和请求关联信息查到允许/拒绝结论,但不要记录授权码、Cookie 或 token。
再做配置反例:临时移除 Application binding。若 denied-user 立刻可以继续,便证明“没有 binding 默认允许”这一关键语义。恢复 binding 后重跑拒绝会话,并撤销两位实验用户的会话。管理方式和 binding 行为见Manage Applications。
最后检查 metrics 边界。server、worker 与 Outpost 的 9300 指标端口默认没有认证,应只允许观测网络访问:
# 观测网络内应成功
curl -fsS http://authentik-server.monitoring.svc:9300/metrics >/dev/null
# 公网或普通业务网段应超时或拒绝;成功即为暴露缺陷
curl --connect-timeout 3 http://login.example.com:9300/metrics
unset VERIFIER CHALLENGE STATE故障证据沿请求、任务和 Outpost 分层
登录失败先看浏览器到 Application/Provider:issuer、redirect URI、client ID、Flow、Stage、Policy、binding 与 event。协议元数据正常但授权被拒绝,通常进入策略与用户上下文;拿到 token 后资源服务 401,则比较 iss、aud、kid、有效期和 claim。不要把所有错误都归因于“用户密码不对”。
server ready 失败时看 PostgreSQL 连接、迁移与连接池;worker healthcheck 失败时看任务进程、数据库和队列状态。Source 同步、邮件、清理和异步事件依赖 worker,server 页面可打开并不能证明后台生命周期继续运行。PostgreSQL 正常而任务持续堆积时,再检查 worker 并发、慢任务、锁和版本一致性。
Outpost 故障单独检查核心 API/WebSocket 连通、service account/token、版本、Provider 分配、upstream DNS/TLS 和 header 信任。managed Outpost 自动更新失败与 manual Outpost 镜像未更新会留下不同证据。核心与 Outpost 版本不一致时,先恢复版本一致性,再判断协议配置。
事件和日志只保留排障所需的对象 ID、Application、Provider、Flow/Policy 结论、状态码、关联 ID 和时间顺序。AUTHENTIK_SECRET_KEY、session Cookie、API/Outpost token、OAuth secret、数据库 URL、完整用户属性与 ak dump_config 输出不得进入日志、工单或 CI artifact。
HA 与容量围绕 PostgreSQL 重新计算
当前架构中的 server 和 worker 可横向扩展,但 PostgreSQL 是配置、会话、缓存协调和后台任务协调的核心状态。较新的发布系列已移除 Redis,旧教程中的 Redis 拓扑不能继续照搬;升级跨过该变化后,PostgreSQL 连接和负载也要重新基线化。产品架构可从官方架构说明核对。
单站 HA 通常包含多 server、多 worker、外部高可用 PostgreSQL、共享或对象化上传存储、负载均衡和按故障域部署的 Outpost。read replica、PgBouncer/Pgpool 可以优化连接,但 transaction pooling 需要关闭 server-side cursor,并让连接寿命与 pooler 回收策略匹配。数据库故障切换后必须验证 ready、worker、登录、token、Source 同步和 Outpost,而不是只看 TCP 端口。
容量基线至少包括交互登录并发、活跃会话、用户/组/策略数量、Source 同步批次、Flow 执行、worker 队列年龄、PostgreSQL 连接与慢查询、事件保留、上传文件、Outpost 数量和指标基数。扩容 server 不能修复数据库连接耗尽,扩容 worker 也可能放大数据库压力。阈值应由连续压测、业务 SLO 和恢复演练决定。
active-passive 可共享同一数据库并切换入口,但 schema 变更会限制不同版本双跑。跨地域部署还要计算数据库一致性、对象存储、Outpost 回连和 DNS/TLS 切换,不应把无状态副本误写成任意多地域 active-active。更完整的边界见HA 指南。
权限、凭证与数据边界
超级管理员只用于少数平台 owner;日常管理拆成用户/组管理员、Application/Provider owner、Flow/Policy 维护者和只读审计者。Kubernetes 中普通主体不应读取 authentik Secret、修改 server/worker Deployment 或取得 Outpost token。服务账号按集成独立创建,授权到必要对象,不共享全局 API token。
AUTHENTIK_SECRET_KEY、管理员/recovery 凭据、OAuth secret、SAML/OIDC 私钥、Source bind 密码、邮件密码、PostgreSQL/S3 凭据、session Cookie、API token 与 Outpost token都属于秘密。Docker socket、kubeconfig 和 ServiceAccount token同样是高风险凭据。只记录 Secret 名称、版本、指纹、owner 和轮换状态,不记录值。
PostgreSQL dump、WAL、/data 上传文件、/certs、自定义模板、Blueprint 和 CSV 报告可能含用户属性、内部域名或秘密引用。备份应加密、限制读取、设置保留和销毁证明;Blueprint 是配置自动化输入,不是完整备份,也不应成为明文 secret 容器。多租户需求要单独审查版本、许可和隔离限制,不能仅靠 Application 分组宣称强租户隔离。
成本、升级、恢复与退出核销
authentik 的长期成本包括 PostgreSQL HA 与备份、server/worker 资源、对象存储、入口和证书、邮件/MFA、Outpost 分布、日志指标、Flow/Policy 维护、Source 同步、升级测试和恢复值班。复杂 Flow 能减少应用定制,也会把关键业务规则集中到身份平台;策略 owner、评审和回归测试是必须计入的工程成本。
恢复可用实例时,先恢复 PostgreSQL,再恢复 /data 或 S3、文件型证书、自定义模板和 Blueprint,注入匹配的 Secret,最后启动同版本 server、worker 与 Outpost。只恢复 Blueprint 或上传目录都不完整。备份介质必须在数据库主机之外,并定期在隔离环境验证用户、组、Flow、Application/Provider、Source、登录、拒绝和 Outpost,参见备份恢复指南。
升级不支持简单降级,也不应跨发布系列跳跃。先把当前系列升级到最新 patch,再依次经过中间系列;每一步读取 release notes、备份 PostgreSQL、保存 Compose/values 与静态资产清单,并保持核心与所有 Outpost 版本一致。PostgreSQL major 升级是独立维护动作。失败恢复要使用升级前数据库、匹配应用版本和匹配 Outpost,不能让旧镜像连接已迁移的新 schema,具体门槛见升级指南。
迁移到新身份平台时,先为测试 Application 建立双信任,稳定 sub 或业务主体映射,逐个迁移 redirect URI、claim、组/角色和会话策略。观察旧新 issuer 的签发与拒绝证据后,撤销旧 Provider secret、Source 凭据、Outpost token 与 API token。退出核销还要删除 DNS/TLS、入口路由、Docker socket/Kubernetes 权限、数据库与对象存储副本,并证明下游应用已不再信任旧 issuer 或代理 header。
个人实验可执行 docker compose -f compose.yml down -v --remove-orphans,确认 Compose project 无共享数据后再删除目录。共享环境按对象回滚:恢复 Application binding/Provider/Flow 的上一受审版本,撤销新增测试用户与会话,轮换暴露凭据,必要时恢复 PostgreSQL与静态资产,然后重跑健康、discovery、允许、拒绝、Source 和 Outpost 验证。
