oauth2-proxy 接入 Microsoft Entra ID:OIDC 认证、Group Overage 与 Workload Identity 完整实战指南
【免费下载链接】oauth2-proxyA reverse proxy that provides authentication with Google, Azure, OpenID Connect and many more identity providers.项目地址: https://gitcode.com/GitHub_Trending/oa/oauth2-proxy
本指南基于 oauth2-proxy 仓库中entra-idProvider 的官方文档(docs/versioned_docs/version-7.9.x/configuration/providers/ms_entra_id.md),系统讲解如何让 oauth2-proxy 使用 Microsoft Entra ID(原 Azure AD)作为身份提供方。你将掌握:App Registration 的正确配置方法、groups claim与 200+ 组成员超限(Group Overage)的处理、多租户应用下 issuer 校验的取舍,以及免客户端密钥的 Workload Identity 联邦认证方案,并能在文末示例配置基础上直接落地。
Provider 概览:完全兼容 OIDC 的entra-id
oauth2-proxy 通过provider="entra-id"启用 Microsoft Entra ID 认证。该 Provider 完全兼容 OIDC 协议,因此通用 OIDC 参数(如oidc_issuer_url、client_id、client_secret、scope、insecure_oidc_skip_issuer_verification等)全部生效,在此之上额外提供了多租户白名单与联邦令牌认证两项能力。
从源码结构看,MicrosoftEntraIDProvider直接内嵌了通用OIDCProvider(见 providers/ms_entra_id.go),对外显示名为 "Microsoft Entra ID"(microsoftEntraIDProviderName常量),并在Redeem、RefreshSession、EnrichSession、ValidateSession四个生命周期方法上做了差异化扩展。在 pkg/apis/options/providers.go 中,MicrosoftEntraIDProvider ProviderType = "entra-id"与azure(旧版 Azure AD v1 端点)是两种独立的 Provider 类型,本指南只讨论entra-id。
专属配置项:租户白名单与联邦令牌认证
除通用 OIDC 参数外,entra-idProvider 只有两个专属配置项,原始文档给出的参数表如下:
| Flag | Toml 字段 | 类型 | 说明 | 默认值 |
|---|---|---|---|---|
--entra-id-allowed-tenant | entra_id_allowed_tenants | string | list | 允许的租户列表。多租户应用场景下,收到的令牌由不同 issuer 签发,因此必须关闭 OIDC issuer 校验。未指定时允许所有租户。对单租户应用而言该项是冗余的(常规 ID token 校验已能匹配 issuer)。 | 空 |
--entra-id-federated-token-auth | entra_id_federated_token_auth | boolean | 启用由 Entra Workload Identity 插件投射的联邦令牌进行 OAuth2 客户端认证,替代客户端密钥。 | false |
这两个参数在仓库中的定义位置非常清晰:
- 命令行 Flag 与旧式配置文件(legacy config)字段定义在 pkg/apis/options/legacy_options.go,并注册到 flagSet(见 legacy_options.go);
- Alpha 配置文件(YAML)中对应
microsoftEntraIDConfig.allowedTenants与microsoftEntraIDConfig.federatedTokenAuth两个字段,定义在 pkg/apis/options/providers.go; - 默认值通过
EnsureDefaults()统一补齐:FederatedTokenAuth默认为false(providers.go,默认常量见 providers.go)。
--entra-id-federated-token-auth的默认值为false,这意味着默认情况下仍走传统的client_secret认证路径;只有显式开启后,Redeem才会切换到联邦令牌分支(见下文"Workload Identity"一节)。
配置 App Registration
开始之前,需要先在 Azure 门户中完成三件事:创建 App Registration、设置重定向 URI、生成客户端密钥。所有账户类型均受支持,包括:
- 单租户(Single-tenant);
- 多租户(Multi-tenant);
- 多租户 + Microsoft 个人账户;
- 仅 Microsoft 个人账户。
重定向 URI 必须是 oauth2-proxy 的/oauth2/callback端点,例如https://your-domain.example.com/oauth2/callback。原始文档同时给出了 Terraform 的等价声明方式,可复制到你的 IaC 仓库中使用:
resource "azuread_application" "auth" { display_name = "oauth2-proxy" sign_in_audience = "AzureADMyOrg" # 其他账户类型也支持 web { redirect_uris = [ "https://podinfo.lakis.tech/oauth2/callback", ] } // 不声明任何必需的 API 权限 —— 仅依赖用户同意 } resource "azuread_service_principal" "sp" { client_id = azuread_application.auth.client_id app_role_assignment_required = false } resource "azuread_service_principal_password" "pass" { service_principal_id = azuread_service_principal.sp.id }创建完成后,将client_id与client_secret(即azuread_service_principal_password生成的密码)填入 oauth2-proxy 的client_id/client_secret配置即可。
配置 groups claim 以支持基于组的授权
如果希望使用组做权限控制——例如通过 oauth2-proxy 的allowed_groups配置放行特定组,或在后端服务内基于组进行鉴权——需要在 App Registration 中开启groups claim,使 ID Token 携带组成员信息。
Terraform 声明方式为在azuread_application上增加group_membership_claims:
resource "azuread_application" "auth" { display_name = "oauth2-proxy" sign_in_audience = "AzureADMyOrg" group_membership_claims = [ "SecurityGroup" ] web { redirect_uris = [ "https://podinfo.lakis.tech/oauth2/callback", ] } } resource "azuread_service_principal" "sp" { client_id = azuread_application.auth.client_id app_role_assignment_required = false } resource "azuread_service_principal_password" "pass" { service_principal_id = azuread_service_principal.sp.id }开启后,oauth2-proxy 会把 ID Token 中的组列表写入会话的Groups字段,allowed_groups即可按组 ID(如ac51800c-2679-4ecb-8130-636380a3b491)进行精确匹配。原始文档强调:groups claim 场景下无需额外 scope,openid即可,且该方案在组成员数不超过 200 个时有效。
Scopes 与 claims:区分三种典型场景
关于授权范围(scope)的选择,原始文档明确区分了三种典型场景,这是最容易踩坑的地方:
- 不使用组的单租户/多租户应用:唯一必需的 scope 是
openid。微软官方文档(《Scopes and permissions》中 "The openid scope" 一节)对此有专门说明。 - 使用 groups claim(≤200 个组):开启 groups claim 后,组列表直接出现在签发的 ID Token 中,除
openid外不需要任何额外 scope。 - 超过 200 个组成员(Group Overage):当用户所属组超过 200 个时,Entra ID 不再把完整组列表放进 ID Token,而是通过令牌内的
_claim_names标记"组信息超限"。此时 oauth2-proxy 会尝试调用 Microsoft Graph API 的transitiveMemberOf端点拉取完整列表。该端点要求委托权限(delegated permission)User.Read,此权限可以在用户首次登录时默认获得同意。配置上需要将 scope 设为openid User.Read以请求用户同意。如果没有正确的 scope,200+ 组的用户将只能以 0 个组完成认证,导致allowed_groups全部失效。
此外还有两条边界情况:
- 如果由管理员对
openid和User.Read同时授予管理员同意(Admin Consent),用户首次登录时便不再被询问同意,且 Group Overage 场景下只需openid一个 scope 即可工作。某些租户也可能强制要求管理员同意,可通过 Terraform 的azuread_service_principal_delegated_permission_grant资源授予。 - 对于Microsoft 个人账户,必需的 scope 是
openid profile email。
源码级原理:Overage 的检测与 Graph 拉取
Group Overage 的完整处理链路都实现在 providers/ms_entra_id.go 中,可以借此理解它"何时查 Graph、怎么查":
- 检测超限:
EnrichSession在调用通用 OIDC 逻辑后,通过checkGroupOverage(ms_entra_id.go)解析 ID Token 的_claim_names声明,若其中包含groups键,即判定为 Overage,随后打印entra overage found, reading groups from Graph API并触发 Graph 拉取。 - 分页拉取:
addGraphGroupsToSession(ms_entra_id.go)以https://graph.microsoft.com/v1.0/me/transitiveMemberOf?$select=id&$top=100为入口发起请求,携带Authorization: Bearer <access-token>与ConsistencyLevel: eventual请求头,通过响应的@odata.nextLink字段循环翻页,直到拉完所有页,最后用RemoveDuplicateStr去重后并入会话 Groups。 - 请求失败时优雅降级:若 Graph 调用失败(如缺少
User.Read权限返回 401),代码只记录错误日志并返回空列表,认证本身不会中断——这正对应文档中"无权限时以 0 个组完成认证"的行为描述。
单元测试 providers/ms_entra_id_test.go 通过 mock Graph 服务器验证了该流程:第一个响应返回两组并带@odata.nextLink分页标记,第二个响应返回第三组,最终断言session.Groups同时包含三个组 ID,证明分页逻辑真实生效(测试中mockGraphAPI实现见 ms_entra_id_test.go)。
多租户应用:issuer 校验的正确关闭方式
多租户应用(含个人 Microsoft 账户)的令牌由不同租户的 issuer 签发,oauth2-proxy 需要做两处调整:
oidc_issuer_url=https://login.microsoftonline.com/common/v2.0 insecure_oidc_skip_issuer_verification=trueinsecure_oidc_skip_issuer_verification=true必须开启,原因是它关闭了两项默认校验:
- 启动时对 discovery document 的 issuer 校验(对应 OIDC Discovery 规范 4.3 节的 Provider Configuration Validation):
https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration返回的issuer字段并不等于https://login.microsoftonline.com/common/v2.0,必须跳过该校验才能启动成功。 - ID Token 校验时的
issuerclaim 匹配(对应 OIDC Core 规范 3.1.3.7 节的 IDToken Validation):多租户场景下每个租户签发的 token 其issuer各不相同,无法与配置的固定 issuer 匹配。
值得注意的是,关闭 issuer 校验并不意味着完全裸奔。entra-idProvider 额外做了一层兜底安全校验:它从 ID Token 的iss声明中提取租户 ID,并要求其匹配https://login.microsoftonline.com/{tenant-id}/v2.0模板。这一逻辑实现在getTenantFromToken(ms_entra_id.go),通过正则^https://login\.microsoftonline\.com/([a-zA-Z0-9-]+)/v2\.0$提取租户;随后ValidateSession(ms_entra_id.go)会检查该租户是否在entra_id_allowed_tenants白名单内,不在名单中则拒绝会话并记录日志。
白名单校验同样有对应的单元测试(ms_entra_id_test.go):配置允许租户85d7d600-...后,伪造iss为无效租户的 token 会话被拒绝(valid == false),而合法租户 token 被放行(valid == true)。getTenantFromToken的正则要求租户 ID 只含字母、数字与连字符,因此任何不符合 Entra 租户 ID 格式的 issuer 都会导致校验失败,这是对insecure_oidc_skip_issuer_verification的有力补充。
Workload Identity:免 client_secret 的联邦认证
在 AKS 等 Kubernetes 环境,维护客户端密钥(client secret)既繁琐又存在泄露风险。entra-idProvider 支持通过Workload Identity 联邦令牌完成 OAuth2 客户端认证,从而完全省略client_secret。原始文档列出的前提条件如下:
- 集群具有公开的 OIDC Provider URL:主流云厂商通常一行配置即可开启,例如通过 Terraform 部署的 AKS,设置
oidc_issuer_enabled。 - 集群部署了 Workload Identity 准入 Webhook:AKS 可通过
workload_identity_enabled标志开启;Azure 之外的自建集群可从 Azure Workload Identity 项目的 Helm Chart 安装。 - 在 App Registration 上添加合适的联合凭据(federated credential),Terraform 示例如下:
resource "azuread_application_federated_identity_credential" "fedcred" { application_id = azuread_application.application.id # 你的应用 ID display_name = "federation-cred" description = "Workload identity for oauth2-proxy" audiences = ["api://AzureADTokenExchange"] # 固定值 issuer = "https://cluster-oidc-issuer-url..." subject = "system:serviceaccount:oauth2-proxy-namespace-name:oauth2-proxy-sa-name" # 换成真实的 NS 和 SA 名称 }- Kubernetes ServiceAccount 注解:与 oauth2-proxy Deployment 关联的 ServiceAccount 需要注解
azure.workload.identity/client-id: <app-registration-client-id>。 - Pod 标签:oauth2-proxy 的 Pod 需要打上
azure.workload.identity/use: "true"标签。 - 开启联邦令牌认证:oauth2-proxy 配置
entra_id_federated_token_auth=true。
满足上述条件后,client_secret配置项可以完全省略。
源码级原理:联邦令牌如何取代 client secret
Redeem方法(ms_entra_id.go)根据federatedTokenAuth标志在"标准 OIDC 兑换"与"联邦令牌兑换"之间分流。联邦分支redeemWithFederatedToken(ms_entra_id.go)的实现要点:
- 从环境变量
AZURE_FEDERATED_TOKEN_FILE指定的文件路径读取联邦令牌(该路径由 Workload Identity 项目约定,由运维注入而非用户输入,源码中带有#nosec G703注释); - 构造令牌端点请求时,用
client_assertion参数携带联邦令牌,并声明client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer,同时带上code、redirect_uri、client_id与可选的code_verifier(PKCE),grant_type为authorization_code; - 请求以
application/x-www-form-urlencoded形式 POST 到RedeemURL,响应由fetchToken(ms_entra_id.go)解析为 token 并保留额外字段。
会话刷新同样支持联邦令牌:RefreshSession在联邦模式下走redeemRefreshTokenWithFederatedToken(ms_entra_id.go),以refresh_tokengrant 类型、同样的client_assertion方式换发新令牌,并同步更新会话中的 ID Token、用户信息与组信息。
可直接落地的五种示例配置
原始文档给出的五组配置覆盖了绝大多数实际部署形态,以下逐一整理(同时注意各配置间的差异点):
1. 单租户应用,不使用组(groups claim 未开启)——最简单的形态,官方建议此时甚至可以考虑直接使用通用 OIDC Provider:
provider="entra-id" oidc_issuer_url="https://login.microsoftonline.com/<tenant-id>/v2.0" client_id="<client-id>" client_secret="<client-secret>" scope="openid"2. 单租户应用,组成员 ≤200(groups claim 已开启)——组列表随 ID Token 下发,allowed_groups按组 ID 精确授权:
provider="entra-id" oidc_issuer_url="https://login.microsoftonline.com/<tenant-id>/v2.0" client_id="<client-id>" client_secret="<client-secret>" scope="openid" allowed_groups=["ac51800c-2679-4ecb-8130-636380a3b491"]3. 单租户应用,组成员 >200——必须追加User.Readscope,触发 Graph 拉取完整组列表:
provider="entra-id" oidc_issuer_url="https://login.microsoftonline.com/<tenant-id>/v2.0" client_id="<client-id>" client_secret="<client-secret>" scope="openid User.Read" allowed_groups=["968b4844-d5e7-4e18-a834-59927959369f"]4. 单租户应用,组成员 >200 且启用 Workload Identity——省略client_secret,开启联邦令牌认证:
provider="entra-id" oidc_issuer_url="https://login.microsoftonline.com/<tenant-id>/v2.0" client_id="<client-id>" scope="openid User.Read" allowed_groups=["968b4844-d5e7-4e18-a834-59927959369f"] entra_id_federated_token_auth=true5. 多租户应用(含个人 Microsoft 账户)+ 白名单 + Overage——组合了 common issuer、跳过 issuer 校验、租户白名单与多 scope 的完整形态。其中9188040d-6c67-4c5b-b112-36a304b66dad是 Microsoft 个人账户租户的固定 ID,email_domains="*"用于放行所有邮箱域名:
provider="entra-id" oidc_issuer_url="https://login.microsoftonline.com/common/v2.0" client_id="<client-id>" client_secret="<client-secret>" insecure_oidc_skip_issuer_verification=true scope="openid profile email User.Read" entra_id_allowed_tenants=["9188040d-6c67-4c5b-b112-36a304b66dad","<my-tenant-id>"] # 仅放行 <my-tenant-id> 与个人 MS 账户租户 email_domains="*"需要说明的是,上述配置文件是 TOML 格式(对应旧式配置文件oauth2-proxy.cfg,可参考仓库中的 contrib/oauth2-proxy.cfg.example);若使用新式 Alpha 配置,则对应 YAML 中的microsoftEntraIDConfig.allowedTenants与microsoftEntraIDConfig.federatedTokenAuth(字段定义见 pkg/apis/options/providers.go)。
在 AKS 上与 Kubernetes Dashboard 集成
原始文档还提示了一个高频场景:在 AKS 上使用 Entra ID 认证接入 Kubernetes Dashboard。完整的集成指南(含详细配置示例、RBAC 设置、故障排查与 Workload Identity 配置)位于仓库的 Kubernetes Dashboard 集成指南,其中entra-idProvider 的配置思路与本指南完全一致——单租户 AKS 集群通常采用oidc_issuer_url指向租户专属端点、开启 groups claim 并使用allowed_groups限制 Dashboard 访问者,需要 Workload Identity 时再叠加联邦认证。
小结:选型决策速查
- 只有邮箱/用户级别的访问控制:
scope="openid"足够,甚至可直接改用通用 OIDC Provider; - 需要按组授权且组数不多:开启 groups claim,
scope="openid"+allowed_groups; - 组数可能超过 200:务必加上
User.Readscope,否则超限用户会以 0 组身份登录; - 多租户/个人账户:
common/v2.0issuer +insecure_oidc_skip_issuer_verification=true,并用entra_id_allowed_tenants收紧租户白名单; - Kubernetes 上不想管理密钥:Workload Identity 联邦令牌 +
entra_id_federated_token_auth=true,彻底去掉client_secret。
本文涉及的实现事实均可在仓库源码中验证,相关文件包括:providers/ms_entra_id.go(Provider 核心实现)、providers/ms_entra_id_test.go(租户白名单与 Overage 测试)、pkg/apis/options/providers.go(参数结构定义)以及 pkg/apis/options/legacy_options.go(Flag 与 Toml 字段映射)。
【免费下载链接】oauth2-proxyA reverse proxy that provides authentication with Google, Azure, OpenID Connect and many more identity providers.项目地址: https://gitcode.com/GitHub_Trending/oa/oauth2-proxy
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考