Pomerium 路由、策略与身份感知访问手册
Pomerium 进程健康、用户也能完成 OIDC 登录,并不代表某个应用已经受到保护。真正承接访问决策的是 Route:请求先由 Proxy 匹配入口,再由 Authorize 评估策略,Authenticate 负责交互式登录,Databroker 保存会话和声明。没有 Route、存在旧直连地址,或把私有路径误标成公开,都可能让“身份感知入口”只停留在控制面配置里。
它适合需要逐请求授权、统一身份路由和可验证上游身份的场景,能力明显超出简单的登录前置代理。但 Pomerium 也不是通用 API 网关,更不会自动理解应用里的对象权限。架构设计应把路由策略限制在入口可判定的身份与上下文,资源 owner 继续负责业务授权;否则中央策略会迅速变成另一套难以测试的业务代码。
先确定 Pomerium 承担哪一层决策
Pomerium 的价值在于把身份与路由绑定:每个 from 指向 to、redirect 或静态 response,Authorize 根据用户、组和策略上下文决定 allow 或 deny。若应用只需要一次 OIDC 登录并恢复 session,轻量的 oauth2-proxy 可能更省维护成本;若已有网关能够可靠验证令牌并调用外部授权服务,也应先比较现有执行点,而不是为了“零信任”再增加一跳。
Core、Zero 与 Enterprise 不能混为同一产品边界。Core 是可自托管的数据面与基础配置模型;Zero 使用托管控制面;Enterprise 提供自托管控制面和额外企业能力。目录同步、设备姿态、管理界面或高级策略是否可用,要对照目标版本和许可确认,不能把网页截图中的能力默认归给开源 Core。
决策记录至少回答四件事:哪些 DNS 入口由它接管,哪些 claim 可以参与策略,谁拥有每条 Route,上游怎样验证来自 Pomerium 的身份。验收不变量也要提前写清:私有 Route 没有 allow 时拒绝,deny 优先,匿名请求不能触达 upstream,旧旁路失败,伪造 Header 或 assertion 无法提升权限。
固定 Core 与 Kubernetes 发行边界
Pomerium Core 的部署文档给出二进制、系统包和容器入口,Kubernetes 使用官方 Ingress Controller及其 CRD/Kustomize 清单。Core 版本、Ingress Controller 版本与 CRD 契约需要分别固定。采用旧 Helm 博文套用到新控制器,会在 annotation、IngressClass 或全局 Pomerium 对象上产生难以察觉的偏差。
export POMERIUM_IMAGE=pomerium/pomerium:<POMERIUM_VERSION>
docker buildx imagetools inspect "$POMERIUM_IMAGE"
docker pull "$POMERIUM_IMAGE"
docker run --rm "$POMERIUM_IMAGE" pomerium version
# 运行后从容器内部读取默认 loopback 健康监听器。
docker exec pomerium pomerium health -v
docker exec pomerium sh -c 'wget -qO- http://127.0.0.1:28080/readyz'安装证据包括镜像 digest、最终配置来源、Service/Ingress、IngressClass、CRD、ClusterRole、Secret 引用、网络入口和一次真实 Route 登录。健康命令证明组件处于某种运行状态,不证明 IdP client、policy、上游 TLS 或数据库写入正确。Kubernetes 上先渲染并审查 cluster-scoped 对象,明确谁负责 CRD 升级与删除,避免卸载 namespace 后遗留全局权限。
cookie secret、shared secret、signing key、IdP client secret 和 Databroker 数据库凭据用途不同,不能复用。Secret 应由受控存储注入,RBAC 只开放给对应 ServiceAccount。配置、CI 输出、诊断包和审计日志不得包含完整 DSN、Token、cookie 或私钥;需要协作排障时只传递脱敏的 issuer、Route ID、状态码和 correlation ID。
理解四个服务与 Databroker 状态
Proxy 接收请求并匹配 Route,Authenticate 完成 OIDC 交互,Authorize 对每次请求重新计算 policy,Databroker 保存 session、claim、token 及相关状态。它们可以在同一进程运行,也能拆分扩容。拆分不是天然更可靠:每多一个组件,就多一组证书、发现、网络、探针、版本兼容和故障归属,需要有实际容量或隔离理由。
登录成功后,Authenticate 在 Databroker 建立状态,并给浏览器写入引用 session 的 cookie。后续访问由 Proxy 与 Authorize 协作,不应只依赖登录时缓存的一次 allow。若组成员或显式 deny 发生变化,策略传播时限必须可测;高风险应用不能把几个小时的 claim 缓存当成实时撤销。
memory 存储仅适合单副本且重启会丢状态;file 可以跨重启保留,但常规部署仍面向单副本。多副本通常使用 Postgres,数据库高可用、TLS、备份、连接池、只读切换与恢复演练随之成为 Pomerium 可用性的一部分。基于 Raft 的 clustered Databroker 若在目标版本仍标注 experimental,就不能直接当作生产默认;它要求唯一 node ID、独立持久卷、成员治理和实验特性升级方案。
用 Route 和 PPL 表达可测试的授权
每条 Route 至少要有 from,并选择一个明确目标。没有 Route 时进程可以全部 Ready,却不会保护任何业务。Pomerium Policy Language 的核心动作是 allow 与 deny:请求至少命中一个 allow,且不命中任何 deny 才放行;deny 优先。allow_public_unauthenticated_access 会绕过认证授权,只能放在真正公开且独立的 Route,不能拿它解决健康检查路径混写。
authenticate_service_url: https://authenticate.example.com
cookie_secret: ${POMERIUM_COOKIE_SECRET}
shared_secret: ${POMERIUM_SHARED_SECRET}
databroker_storage_type: postgres
databroker_storage_connection_string: ${POMERIUM_DATABASE_URL}
routes:
- from: https://admin.example.com
to: http://admin.admin.svc:8080
pass_identity_headers: true
remove_request_headers:
- x-pomerium-claim-groups
policy:
- allow:
and:
- domain: { is: example.com }
- groups: { has: platform-admins }
- deny:
or:
- email: { is: suspended-user@example.com }示例中的 criteria 是否属于目标 Core、Zero 或 Enterprise 版本,必须根据PPL 与策略文档确认。策略变更不能只做语法检查;至少使用匿名用户、允许用户、普通用户和显式 deny 用户执行相同资源请求,证明缺少 allow 时拒绝、deny 优先、公开例外只覆盖预期 path。
Route 应有 owner、敏感度、上游、域名、证书、policy 测试和回滚版本。不要用一个超宽 groups 条件保护不同风险等级的应用,也不要把应用业务对象 ID 塞入不可审计的正则。能够在入口稳定判断的租户、组织、组或设备条件可放在 PPL;订单、项目、数据行等资源内部权限应留在应用。
验证 JWT Assertion 和身份 Header
pass_identity_headers 默认关闭。启用后,上游可收到 X-Pomerium-Jwt-Assertion 和 claim Header。普通 claim Header 适合便利读取,却不能独自成为授权凭证,因为能绕开代理的请求可能伪造同名字段。应用应验证 assertion 的签名、issuer、audience 和时间语义,并从 Pomerium JWKS 更新 key;audience 绑定当前应用,不能让不同敏感度的上游共享宽泛受众。
入口与任何中间代理先删除客户端自带的 X-Pomerium-*、Authorization 和身份字段,再按明确契约写入需要的值。remove_request_headers 用于移除应用不消费的 groups 或敏感 claim,缩小 Header 体积和泄露面。JWT 验证失败应拒绝请求,而不是退回信任 email Header;JWKS 缓存则要支持 key 轮换和合理的失败策略,避免旧 key 永久有效或短暂网络抖动击穿所有访问。
旁路治理同样重要。删除旧 Ingress、限制 Service 暴露面,用 NetworkPolicy 与安全组只允许 Pomerium Proxy 到达 upstream;从普通 Pod、办公网、旧域名和集群外入口分别直连验证。Pomerium 保护的是经过自身的流量,不会自动关闭遗留 LB、NodePort、hostNetwork 或应用调试端口。
项目接入要同时交付策略与证据
一个项目接入包应包含 Route 配置、IdP client 或共享身份源关系、上游证书策略、JWT 验证方式、NetworkPolicy、DNS 变更、正反用例、owner 与退出步骤。平台团队负责入口与策略执行能力,应用团队负责 assertion 验证和业务授权,安全团队确认 claim 最小化与审计保留;三者边界写入变更单,不能把 403 都推给身份平台。
正向用例覆盖首次登录、回调、session 恢复、允许 policy、上游主体、JWT audience 和副本切换。Databroker 使用 Postgres 时,切换一个 Proxy/Authorize 副本并短暂迁移数据库连接,确认已有会话和新授权按设计工作。证据只保存脱敏 subject、Route ID、policy 结果、状态码和 correlation,不记录完整断言或个人 groups。
export ENTRY=https://admin.example.com
export UPSTREAM=http://admin.admin.svc:8080
# 匿名私有请求只能跳转登录或返回 401/403。
curl -sS -o /dev/null -D - "$ENTRY/private"
# 客户端伪造 claim 与 assertion 不得成为管理员。
curl -sS -o /dev/null -D - \
-H 'X-Pomerium-Claim-Email: admin@example.com' \
-H 'X-Pomerium-Jwt-Assertion: forged' \
"$ENTRY/private"
# 非代理来源不能直接触达受保护上游。
curl -sS -o /dev/null -w '%{http_code}\n' "$UPSTREAM/private"反向用例还包括没有 allow 的 Route、显式 deny 用户、错误 issuer/audience、过期 assertion、伪造 groups、非法 PPL 和旧旁路。期望以不变量表达:deny 永远优先,错误凭证被拒绝,配置错误在 controller event 或日志中可定位,旁路不能返回业务 2xx。这样测试不会因登录页文案变化而失效。
按组件收集故障与容量证据
没有登录跳转先查 DNS、Route 与 Proxy;回调失败查 Authenticate、redirect URI 和 IdP;已登录 403 查 Authorize 的 criteria、claim 与 deny;重启后会话丢失或副本行为不一致查 Databroker、Postgres 和共享 Secret。Pomerium 的健康检查文档区分 startup、ready 与 health,但真实登录、授权和数据库可写仍需合成探针。
docker exec pomerium pomerium health -v
curl -fsS http://127.0.0.1:9090/metrics | grep '^pomerium_build_info'
kubectl describe pomerium/global
kubectl describe ingress -n "$NS" "$INGRESS"
kubectl -n pomerium logs deployment/pomerium --since=15m监控分开观察 Proxy 的请求与连接、Authorize 的每请求策略耗时、Authenticate 的登录峰值与 IdP 延迟、Databroker 的 session 读写和 Postgres 连接。只看总错误率会把 401、403、上游 5xx 和数据库故障混成一类。SLO 要同时约束允许请求成功率、应拒请求的拒绝正确性、策略传播时限和登录可用性。
容量实验加入大 groups、Header 体积、登录风暴、token refresh 峰值、IdP 限流、数据库只读切换和通知积压。长期成本包括 Postgres 高可用、域名证书、控制面许可、审计日志、策略评审、密钥轮换和值班,而非几组 Pod。组件拆分后可以分别扩缩,但也增加网络和运维成本,必须用指标证明拆分收益。
升级、迁移与安全退出
升级遵循官方升级指南,同时核对 Core、Ingress Controller、CRD、PPL、Databroker schema 与许可功能边界。先把 Postgres 备份恢复到隔离环境,验证 Route、deny、assertion/JWKS、session 与真实 IdP 登录。CRD 升级保存旧对象和转换结果;回退二进制不保证状态格式也能回退。
变更可按 Route 灰度:候选入口使用独立域名或小流量权重,保留旧配置、镜像 digest、数据库恢复点和 DNS 回切方案。shared/cookie/signing Secret 的轮换要设计双轨窗口,避免滚动期间不同副本互不信任。若策略语义改变,先固定允许、拒绝和旁路三类用例,再发布新控制面,不能只验证管理界面显示成功。
退出前确定应用转向新代理、原生 OIDC 或受控内网。按依赖顺序移除 Route、DNS/LB、上游 assertion 信任与 NetworkPolicy 例外,再撤销 IdP client、cookie/shared/signing Secret。Databroker/Postgres 先确认是否专用以及审计保留期限,不对共享数据库执行通用清库。Zero 或 Enterprise 控制面对象、许可与账单也要单独核销。
关账证据应证明旧域名停止签发新会话、旧 cookie 与 assertion 无法授权、旧 JWKS 信任已从上游移除、旁路和临时 Ingress 关闭、数据库与备份按责任销毁或归档、日志不再接收新数据。只有 Route、身份、状态、网络、权限和成本同时收口,Pomerium 才算安全退出,而不是留下一个仍能签发身份的孤立代理。
