mcpField
A
azure-ad-sso

azure-ad-sso

github.com/julianobarbosa安全14123 次安装2 天前更新
代码仓库

关于 azure-ad-sso

azure-ad-sso 是用于在 Kubernetes 上为应用配置 Azure AD(Entra ID)OAuth2/OIDC 单点登录及组级访问控制的实施 Skill。它提供应用配置指引、通过 Key Vault 管理密钥的方案,以及故障排查资料。

主要功能

  1. 01Azure AD 应用注册指引
  2. 02OAuth2/OIDC 与 SSO 配置
  3. 03按组限制访问
  4. 04通过 CSI 管理 Key Vault 密钥
  5. 05应用配置与错误排查

适用场景

  1. 01为 Kubernetes 上的应用添加 SSO
  2. 02限制 Azure AD 组对应用的访问
  3. 03通过 Key Vault 存储应用密钥
  4. 04诊断重定向或组同步故障

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/julianobarbosa/claude-code-skills --skill azure-ad-sso

安装后在 Claude Code 中输入/azure-ad-sso确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

references/
app-configs.md
azure-ad-sso-guide.md
troubleshooting.md

详细介绍

Azure AD SSO 集成技能

概述

本技能全面介绍如何为部署在 Kubernetes 集群上的应用实现 Azure AD(Entra ID)OAuth2/OIDC 单点登录,包括通过 Azure AD 组限制访问。

快速参考

支持的应用
应用提供方重定向 URI 模式组同步
DefectDojoazuread-tenant-oauth2/complete/azuread-tenant-oauth2/是
Grafanaazuread/login/azuread是
ArgoCDmicrosoft (Dex)/api/dex/callback是
Harboroidc/c/oidc/callback是
SonarQubesaml 或 oidc/oauth2/callback/saml是
OAuth2 Proxyazure/oauth2/callback是
Keycloakoidc/realms/{realm}/broker/azure/endpoint是
身份验证流程选择
┌─────────────────────────────────────────────────────────────────┐
│                    Access Control Decision                       │
├─────────────────────────────────────────────────────────────────┤
│                                                                  │
│  Q: Who should access this application?                         │
│                                                                  │
│  ├─ Everyone in tenant ──► appRoleAssignmentRequired=false      │
│  │                                                               │
│  └─ Specific groups ────► appRoleAssignmentRequired=true        │
│                           + Assign groups to Enterprise App     │
│                                                                  │
└─────────────────────────────────────────────────────────────────┘

实施工作流

阶段 1:Azure AD 应用注册
# 1. Create App Registration
APP_NAME="<application>-<environment>"
REDIRECT_URI="https://<app-domain>/complete/<provider>/"

APP_ID=$(az ad app create \
  --display-name "$APP_NAME" \
  --sign-in-audience "AzureADMyOrg" \
  --web-redirect-uris "$REDIRECT_URI" \
  --query appId -o tsv)

echo "Application (client) ID: $APP_ID"

# 2. Get Tenant ID
TENANT_ID=$(az account show --query tenantId -o tsv)
echo "Directory (tenant) ID: $TENANT_ID"

# 3. Create Client Secret
SECRET=$(az ad app credential reset \
  --id $APP_ID \
  --append \
  --years 1 \
  --query password -o tsv)

echo "Client Secret: $SECRET"  # Save immediately!
阶段 2:启用组声明
# Enable security group claims in tokens
az ad app update --id $APP_ID --set groupMembershipClaims=SecurityGroup

# Add Group.Read.All permission (delegated)
az ad app permission add \
  --id $APP_ID \
  --api 00000003-0000-0000-c000-000000000000 \
  --api-permissions 5f8c59db-677d-491f-a6b8-5f174b11ec1d=Scope

# Grant admin consent
az ad app permission admin-consent --id $APP_ID
阶段 3:按组限制访问(关键)
# Get Service Principal object ID
SP_ID=$(az ad sp list --filter "appId eq '$APP_ID'" --query "[0].id" -o tsv)

# Enable user assignment requirement
az ad sp update --id $SP_ID --set appRoleAssignmentRequired=true

# Get the group ID to restrict access
GROUP_ID=$(az ad group show --group "G-Usuarios-<App>-Admin" --query id -o tsv)

# Assign group to the application (only these users can login)
az rest --method POST \
  --uri "https://graph.microsoft.com/v1.0/servicePrincipals/$SP_ID/appRoleAssignments" \
  --headers "Content-Type=application/json" \
  --body "{
    \"principalId\": \"$GROUP_ID\",
    \"principalType\": \"Group\",
    \"appRoleId\": \"00000000-0000-0000-0000-000000000000\",
    \"resourceId\": \"$SP_ID\"
  }"
阶段 4:在 Key Vault 中存储密钥
az keyvault secret set \
  --vault-name "<keyvault-name>" \
  --name "<app>-azuread-client-secret" \
  --value "$SECRET"

密钥管理

SecretProviderClass 模板
apiVersion: secrets-store.csi.x-k8s.io/v1
kind: SecretProviderClass
metadata:
  name: <app>-secrets
  namespace: <namespace>
spec:
  provider: azure
  parameters:
    usePodIdentity: "false"
    useVMManagedIdentity: "true"
    userAssignedIdentityID: "<managed-identity-client-id>"
    keyvaultName: "<keyvault-name>"
    tenantId: "<azure-tenant-id>"
    objects: |
      array:
        - |
          objectName: <app>-azuread-client-secret
          objectType: secret
          objectAlias: AZURE_AD_CLIENT_SECRET
  secretObjects:
    - secretName: <app>-azure-ad
      type: Opaque
      data:
        - objectName: AZURE_AD_CLIENT_SECRET
          key: client-secret
Pod 卷挂载
volumes:
  - name: secrets-store
    csi:
      driver: secrets-store.csi.k8s.io
      readOnly: true
      volumeAttributes:
        secretProviderClass: "<app>-secrets"

volumeMounts:
  - name: secrets-store
    mountPath: "/mnt/secrets-store"
    readOnly: true

应用配置

DefectDojo
# Enable SSO
extraEnv:
  - name: DD_SOCIAL_AUTH_AZUREAD_TENANT_OAUTH2_ENABLED
    value: "True"
  - name: DD_SOCIAL_AUTH_AZUREAD_TENANT_OAUTH2_KEY
    value: "<client-id>"
  - name: DD_SOCIAL_AUTH_AZUREAD_TENANT_OAUTH2_TENANT_ID
    value: "<tenant-id>"
  - name: DD_SOCIAL_AUTH_AZUREAD_TENANT_OAUTH2_SECRET
    valueFrom:
      secretKeyRef:
        name: defectdojo
        key: DD_SOCIAL_AUTH_AZUREAD_TENANT_OAUTH2_SECRET
  # Group sync
  - name: DD_SOCIAL_AUTH_AZUREAD_TENANT_OAUTH2_GET_GROUPS
    value: "True"
  - name: DD_SOCIAL_AUTH_AZUREAD_TENANT_OAUTH2_CLEANUP_GROUPS
    value: "True"
  - name: DD_SOCIAL_AUTH_AZUREAD_TENANT_OAUTH2_GROUPS_FILTER
    value: "^G-Usuarios-DefectDojo-.*"
  # CRITICAL: For apps behind reverse proxy
  - name: DD_SECURE_PROXY_SSL_HEADER
    value: "True"
Grafana
grafana.ini:
  auth.azuread:
    enabled: true
    name: Azure AD
    allow_sign_up: true
    client_id: "<client-id>"
    client_secret: "${GF_AUTH_AZUREAD_CLIENT_SECRET}"
    scopes: openid email profile
    auth_url: https://login.microsoftonline.com/<tenant-id>/oauth2/v2.0/authorize
    token_url: https://login.microsoftonline.com/<tenant-id>/oauth2/v2.0/token
    allowed_groups: "<admin-group-id> <viewer-group-id>"
    role_attribute_path: contains(groups[*], '<admin-group-id>') && 'Admin' || 'Viewer'
ArgoCD(通过 Dex)
configs:
  cm:
    dex.config: |
      connectors:
        - type: microsoft
          id: microsoft
          name: Azure AD
          config:
            clientID: "<client-id>"
            clientSecret: $dex.azure.clientSecret
            tenant: "<tenant-id>"
            redirectURI: https://<argocd-domain>/api/dex/callback
            groups:
              - <admin-group-id>
  rbac:
    policy.csv: |
      g, <admin-group-id>, role:admin
Harbor
externalURL: https://harbor.<domain>
core:
  oidc:
    name: "azure"
    endpoint: "https://login.microsoftonline.com/<tenant-id>/v2.0"
    clientId: "<client-id>"
    clientSecret: "<from-secret>"
    scope: "openid,profile,email"
    groupsClaim: "groups"
    adminGroup: "<admin-group-id>"
    autoOnboard: true

故障排除

错误参考
错误代码描述解决方案
AADSTS50011回复 URL 不匹配核对重定向 URI 是否完全一致,包括末尾斜杠
AADSTS50105用户未分配将用户/组添加到企业应用分配中
AADSTS700016找不到应用检查客户端 ID 和租户 ID
AADSTS7000218密钥已过期在 Key Vault 中轮换密钥,然后重启 Pod
AADSTS90102redirect_uri 无效对于反向代理,检查 DD_SECURE_PROXY_SSL_HEADER=True
AADSTS65001尚未授予同意运行 az ad app permission admin-consent
常见问题
redirect_uri 格式错误(代理后的 Django 应用)

症状: redirect_uri=https,%20https://...

根本原因: DD_SECURE_PROXY_SSL_HEADER 设置错误

修复方法:

- name: DD_SECURE_PROXY_SSL_HEADER
  value: "True"  # NOT "HTTP_X_FORWARDED_PROTO,https"
组未同步
# Verify group claims enabled
az ad app show --id <app-id> --query groupMembershipClaims

# Check API permissions
az ad app permission list --id <app-id>

# Verify group exists and user is member
az ad group member check --group "<group-name>" --member-id "<user-object-id>"
未从 Key Vault 同步密钥
# Check SecretProviderClass
kubectl describe secretproviderclass <name> -n <namespace>

# Check CSI driver pods
kubectl get pods -n kube-system | grep secrets-store

# Check managed identity access
az keyvault show --name <vault> --query properties.accessPolicies
诊断命令
# Test OAuth redirect
curl -sS -k -D - -o /dev/null "https://<app>/login/<provider>/" 2>&1 | grep -i location

# Check environment variables in pod
kubectl exec -n <ns> deploy/<app> -c <container> -- env | grep -i azure

# Decode JWT token (after login, from browser dev tools)
# Use https://jwt.io to decode and verify claims

安全最佳实践

  1. 切勿将密钥硬编码 - 始终使用 Key Vault + CSI Driver
  2. 使用托管身份 - 避免使用服务主体凭据
  3. 按组限制访问 - 启用 appRoleAssignmentRequired=true
  4. 轮换密钥 - 在密钥过期前设置日历提醒
  5. 仅使用 HTTPS - 所有重定向 URI 都必须使用 HTTPS
  6. 单租户 - 内部应用切勿使用多租户
  7. 审计日志记录 - 启用 Azure AD 登录日志

环境参考

环境Key Vault托管身份租户 ID
example-app-devkv-example-dev-hlg<WORKLOAD_IDENTITY_CLIENT_ID><TENANT_ID>
example-app-hubkv-example-default<WORKLOAD_IDENTITY_CLIENT_ID><TENANT_ID>
example-app-prdkv-example-prd<WORKLOAD_IDENTITY_CLIENT_ID><TENANT_ID>

详细参考

完整实施示例请参阅:


注意事项

  • 组声明默认使用 objectId,而非名称——启用 optionalClaims: groups: SecurityGroup 并在清单中将令牌版本设为 2,否则按名称匹配的应用将看不到任何内容。
  • 令牌范围:https://graph.microsoft.com/.default 范围仅授予管理员已同意的权限;委托令牌中的 User.Read.All 在首次使用时仍需管理员同意。
  • OIDC 重定向 URI 必须完全匹配——末尾斜杠不匹配会返回 AADSTS50011,并附带一个模糊的错误,看起来像是网络问题。
  • 组声明在 200 个组时会超额:属于超过 200 个组的用户不会获得任何组声明(超额)。改用 Graph 查询来查找成员资格。
  • 应用令牌生命周期策略按应用设置——默认值为 1 小时。运行超过 1 小时的批处理任务会静默返回 401,除非为每次调用都封装 401 后刷新令牌的逻辑。

评论

支持 Markdown