在 Google BeyondCorp 的架构成熟之后,一个最直接的问题是:怎么让”每个请求都经过认证和授权”而不需要修改每个应用? 答案就是身份感知代理(Identity-Aware Proxy,IAP)——一个部署在应用前面的反向代理,承担所有认证和授权的逻辑,向后端传递已验证的用户身份。
前置阅读:NIST SP 800-207 架构拆解、BeyondCorp 六篇论文全景。
一、核心思想:把认证从应用中剥离
传统模型下,每个应用自己处理认证——检查 Cookie/Session、重定向到 SSO、处理回调。这个模型的累积成本很高:公司内有 50 个内部 Web 应用,就有 50 套认证逻辑(其中 45 套有 bug)。
IAP 把这个模型倒过来:
sequenceDiagram
participant User as 用户浏览器
participant IAP as IAP (反向代理)
participant IdP as IdP (OIDC)
participant App as 后端应用
User->>IAP: GET /internal-tool
IAP->>IAP: 检查 Session Cookie
alt 无有效 Session
IAP->>User: 302 重定向到 IdP
User->>IdP: OIDC 登录 (可能含 MFA)
IdP->>User: 302 携带 Authorization Code
User->>IAP: GET /callback?code=xxx
IAP->>IdP: 用 code 换 token
IdP->>IAP: id_token + access_token
IAP->>IAP: 创建 Session, 签发 Cookie
end
IAP->>IAP: 验证 Session, 查询授权策略
alt 授权通过
IAP->>App: 转发请求 (注入身份 Header)
App->>IAP: 响应
IAP->>User: 200 OK
else 授权拒绝
IAP->>User: 403 Forbidden
end
这个模型的核心价值:应用代码里不再有认证逻辑。 应用只信任 IAP 注入的经过加密签名的身份信息。SSO 的集成、MFA 的启用、授权策略的变更,全部在 IAP 层完成,不需要应用发版。
二、Google IAP 的请求管线
Google IAP 是 Google Cloud 的内置服务,运行在 GCP 的全球负载均衡层——在请求到达后端之前就完成拦截。以下是它的完整请求管线。
2.1 OAuth2 认证阶段
当用户第一次访问受 IAP 保护的应用时:
- IAP 检查
Cookie(
GCP_IAAP_AUTH_TOKEN)。 - 如果没有或已过期,重定向到 Google
Accounts(
accounts.google.com)执行 OAuth2 流程。 - 用户通过 Google 身份认证(包括 MFA,如果启用了)。
- Google Accounts 将 Authorization Code 通过浏览器重定向传回 IAP。
- IAP 用
code向 Google Token Endpoint 换取id_token和access_token。 - IAP 签发加密的 Session Cookie,设置在前端。
2.2 授权检查阶段
认证通过后,IAP 还执行一次 IAM 权限检查:
- 用户需要拥有
roles/iap.httpsResourceAccessor角色(针对目标资源)。 - 这个角色可以通过 IAM 绑定到用户、组或服务账号。
IAP 的授权模型是 IAM-based 的——简单但粗粒度。你可以在 IAM 中说”张三能访问这个应用”,但不能说”张三只能在周一到周五的 9-18 点访问”。
2.3 JWT 断言:IAP 向后端传递身份的核心机制
IAP 向后端应用注入 HTTP Header。关键 Header:
| Header | 签名 | 用途 |
|---|---|---|
X-Goog-IAP-JWT-Assertion |
ES256 签名 | 唯一可信的身份源 |
X-Goog-Authenticated-User-Email |
无签名 | 显示/日志用途 |
X-Goog-Authenticated-User-ID |
无签名 | 显示/日志用途 |
安全原则:后端应用绝对不能信任无签名的 Header。
X-Goog-Authenticated-User-Email在 IAP 被绕过时可以被攻击者随意伪造。唯一可信的是X-Goog-IAP-JWT-Assertion中的 ES256 签名 JWT。
JWT 验证的关键参数:
| 参数 | 值 | 用途 |
|---|---|---|
| 签名算法 | ES256 (ECDSA P-256 + SHA-256) | Google 选择的签名方案 |
iss |
https://cloud.google.com/iap |
签发者验证 |
aud |
项目/后端特定格式 | 防止跨服务 JWT 重放 |
| JWKS URL | https://www.gstatic.com/iap/verify/public_key-jwk |
公钥获取 |
| 时钟偏差容忍 | ±30 秒 | exp/iat 的检查容差 |
2.4 后端的 JWT 验证实现
后端应用不能只读 Header——必须验证 JWT 签名。最小正确的实现(Go 示例):
import "google.golang.org/api/idtoken"
func authenticate(r *http.Request) (string, error) {
iapJWT := r.Header.Get("X-Goog-IAP-JWT-Assertion")
if iapJWT == "" {
return "", fmt.Errorf("missing IAP assertion")
}
payload, err := idtoken.Validate(r.Context(), iapJWT, expectedAudience)
if err != nil {
return "", fmt.Errorf("JWT validation failed: %w", err)
}
email, ok := payload.Claims["email"].(string)
if !ok {
return "", fmt.Errorf("no email claim")
}
return email, nil
}这个验证的三个关键步骤: 1. 从 Google 的 JWKS 端点获取
ES256 公钥(有内存缓存,每小时后台刷新) 2. 验证
iss =
https://cloud.google.com/iap、aud
= 预配置值、exp 未过期 3. 提取
email 作为用户身份
2.5 认证逃逸(Auth Bypass)
一个被低估的安全风险:IAP 允许配置某些路径不需要认证(如
/health、/public/*)。当应用本身的后端路由和
IAP 的路径白名单之间存在差异时,攻击者可以利用这种差异绕过
IAP:
IAP 配置: /internal/* → 需要认证
应用路由: /internal/admin/debug → 不需要鉴权(开发者留下的调试端点)
结果: IAP 对 /internal/admin/debug 放行(因为它匹配 /internal/*),
但应用本身对这个路径没有认证——任何人都能访问。
防御措施:后端应用在 IAP 注入的 JWT 断言缺失时拒绝服务任何非公开路径。不要把”有 IAP”等同于”应用不需要做授权检查”。
三、Pomerium:开源 IAP 的完整实现
Pomerium 是基于 Envoy 构建的开源身份感知代理(Apache 2.0 许可),实现了一个功能上比 Google IAP 更灵活的替代方案。
3.1 四组件架构
Pomerium 由四个逻辑服务组成:
flowchart TD
User["用户"] --> Proxy["Proxy Service<br/>(Envoy-based, 所有流量的入口)"]
Proxy --> AuthN["Authentication Service<br/>(OAuth2/OIDC 流程)"]
Proxy --> AuthZ["Authorization Service<br/>(每请求策略评估)"]
Proxy --> Databroker["Databroker Service<br/>(状态存储, Session/Claims/Token)"]
AuthN --> Databroker
AuthZ --> Databroker
AuthN --> IdP["IdP<br/>(Okta/Azure AD/Google/Keycloak/...)"]
Proxy --> Upstream["上游应用"]
四个组件可以合并为单进程运行(开发环境),也可以分开部署(生产环境)。
3.2 与 Google IAP 的关键差异
| 维度 | Google IAP | Pomerium |
|---|---|---|
| 部署范围 | GCP only | 任何环境(云/本地/混合) |
| IdP 支持 | 主要 Google Identity | 任何 OIDC 兼容 IdP |
| 授权模型 | IAM 角色(粗粒度) | 策略即代码(细粒度 context-aware) |
| 协议支持 | HTTP + TCP | HTTP、TCP、gRPC、SSH、MCP |
| 持续验证 | 每次请求 | 每次请求重新评估 |
| 流量隐私 | Google 可解密你的流量 | 自托管,数据不外泄 |
| 多集群 | 依赖 VPN 隧道 | 原生多集群 |
授权策略的粒度差异是最关键的。Google IAP
的授权是”谁能访问这个应用”——Pomerium
的授权可以做到”张三只能在 Trusted 设备上、从中国 IP、在 9-18
点之间、访问 /api/orders 的 GET 方法”:
policy:
- from: https://internal.example.com
to: https://orders-api.internal
allowed_users:
- zhangsan@example.com
allowed_idp_claims:
groups:
- engineering
allow_any_authenticated_user: false3.3 Envoy 代理的性能特征
Pomerium 基于 Envoy,这意味着它的数据平面继承了 Envoy 的性能特征——每秒百万级请求的处理能力、丰富的 HTTP 协议支持(HTTP/1.1、HTTP/2、gRPC),以及与非 HTTP 协议的兼容。但也意味着运维复杂度——Envoy 的配置文件和故障排查比 oauth2-proxy 复杂得多。
四、oauth2-proxy:简单的代价
oauth2-proxy 是最轻量的 IAP 实现——它是一个 Go 编写的单进程反向代理,从最初的 Google Auth Proxy 演化而来。它的设计哲学是”只做认证,不做授权”。
| 维度 | oauth2-proxy | Pomerium |
|---|---|---|
| 授权模型 | 二元(登录/未登录) | 策略即代码(context-aware) |
| 持续验证 | 否(Session 持续到过期) | 每请求重新评估 |
| API 支持 | Web only | 全部 HTTP API |
| 部署复杂度 | 极低(单二进制) | 中(四个服务组件) |
oauth2-proxy 适合简单的场景——一个内部 Wiki 或 Dashboard 需要登录控制。但一旦你需要”市场部的人不能访问财务 Dashboard”或者”在手机上不能访问生产环境的 API”,oauth2-proxy 就不够了。它的本质是”认证代理”而非”身份感知代理”——区别在于它不问”你是谁”之后就问”你能做什么”。
五、IAP 的工程坑点
5.1 Header Injection 的信任问题
所有 IAP 实现都面临同一个问题:后端怎么信任 IAP 注入的 Header?
如果 IAP 和后端之间的连接不是 mTLS 加密的,攻击者可以绕过 IAP 直接向后端发送伪造的 Header。防御方法:
- 方案 A(推荐):后端设置在非公开网络上(Kubernetes 的 ClusterIP、AWS 的 VPC),防火墙上只允许 IAP 的 IP 访问。加上 IAP 和后端之间的 mTLS。后端验证 IAP 签发的 JWT,不信任裸 Header。
- 方案 B:后端同时验证 HTTP Header
的签名(如 Google IAP 的
X-Goog-IAP-JWT-Assertion)——即使网络层防护失效,加密签名也防伪。 - 方案 C(不推荐):只依赖网络隔离 + 信任裸 Header。
5.2 长连接的 Session 续期
WebSocket、gRPC streaming、SSE(Server-Sent Events)等长连接在 IAP 后面有一个棘手问题:一次连接建立时通过了认证和授权,但 30 分钟后用户的角色变了(HR 系统显示该员工已离职),IAP 不能强行关闭这个已经建立的 WebSocket 连接。
当前的实践是折中:IAP
验证每个新连接,但不插手已建立的连接。长连接的应用需要自己在应用层做周期性权限检查(如每
5 分钟验证一次 JWT 的 iat
是否仍然在允许的窗口内)。
5.3 性能与延迟
IAP 在请求路径上引入了额外的处理——OAuth2 token 验证、Session 查找、策略评估。这些操作的延迟取决于 IAP 的实现方式和部署位置:
- Google IAP:在 GCP 全球负载均衡层,延迟增量通常在 10-20ms。
- Pomerium:部署在企业自己的基础设施上,延迟取决于部署位置——在同一个数据中心可以做到 < 5ms。
- oauth2-proxy:最轻量,延迟增量 < 1ms(纯内存 Session 缓存)。
在生产中,10-20ms 的 IAP 延迟对大多数 Web 应用来说是微不足道的(远小于后端处理的延迟),但对高频 API 调用来说需要纳入设计考量。
六、选型建议
需求评估流程:
1. 你只需要"登录控制"(一个内部 Dashboard 不要让外部访问)?
→ oauth2-proxy 足够。
2. 你需要"谁 + 在什么设备/时间/位置 + 能访问什么 + 什么操作"?
→ Pomerium 或类似的开源 IAP。
3. 你的所有工作负载都在 GCP,且你接受 Google IAP 的 IAM 授权模型?
→ Google IAP。
4. 你需要多云/混合云,且不信任任何厂商持有你的流量?
→ Pomerium(自托管)。
5. 你的团队 < 20 人,想尽可能少运维基础设施?
→ Cloudflare Access(agentless ZTNA,下一篇文章讨论)。
下一篇:设备姿态与远程证明,拆解 TPM 2.0 远程证明协议和设备信任分数的计算。
参考资料
- Google Cloud. Identity-Aware Proxy Documentation. https://cloud.google.com/iap/docs
- Pomerium. Architecture Overview. https://www.pomerium.com/docs/internals/architecture
- oauth2-proxy. GitHub Repository. https://github.com/oauth2-proxy/oauth2-proxy
- Escobedo, V. et al. (2016). “BeyondCorp: The Access Proxy”. ;login:, Vol. 42, No. 4.
同主题继续阅读
把当前热点继续串成多页阅读,而不是停在单篇消费。
【身份与访问控制工程】IAM 全景:为什么这是高价值赛道
从 2020 年 SolarWinds 到 2024 年 Okta 支持系统泄露,身份基础设施的安全失败反复证明一件事:IAM 不是 IT 支撑系统,而是安全架构的承重墙。本文建立现代 IAM 的全景地图——从认证协议、令牌体系、权限模型到身份治理与平台选型,给出 5 个贯穿全系列的核心问题。
【身份与访问控制工程】JWT、JWS、JWE、JWKS 一次讲透
JWT 不等于 JWS。JWS 是签名格式,JWE 是加密格式,JWK 是密钥表示,JWKS 是密钥集合——这四个规范共同构成了 JOSE(JSON Object Signing and Encryption)技术族。本文从 JOSE 体系全景出发,逐层拆解 JWT 的三段式结构、JWS 的签名算法选择(从 HS256 到 EdDSA 的选择逻辑)、JWE 的密钥加密与内容加密双层模型、JWKS 的密钥轮换与缓存策略。
【身份与访问控制工程】Session、Refresh Token 与吊销体系
JWT 的无状态设计带来了可扩展性,但让令牌吊销变成了系统性问题——签出去的 JWT 在到期之前全是活令牌。Refresh Token Rotation、Token Introspection、基于事件的吊销通知、撤销列表——这些机制构成了身份系统的'紧急刹车',各自的成本、延迟和覆盖范围完全不同。本文拆解四种吊销机制的工程权衡。
【身份与访问控制工程】服务身份:mTLS、SPIFFE/SPIRE 与 Workload Identity
前 9 篇讨论的都是'人'的身份——用户怎么登录、怎么验证。但微服务世界中,80% 的 API 调用是服务之间的。服务身份(Workload Identity)是整个 IAM 体系的另一半:mTLS 解决'传输层你是谁',SPIFFE/SPIRE 解决'在平台层你是谁且怎么证明',JWT Profile for OAuth 解决'我怎么拿到一个服务身份的 Token'。本文从这三条线拆解服务身份的工程实现。