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シングルサインオンとグループ単位のアクセス制御を設定するスキルです。アプリ別の設定例、Key Vaultによるシークレット管理、トラブルシューティングを扱います。

主な機能

  1. 01Azure ADアプリ登録のガイド
  2. 02OAuth2/OIDCとSSOの設定
  3. 03グループ単位のアクセス制限
  4. 04CSI経由のKey Vaultシークレット管理
  5. 05アプリ設定とエラーの調査

活用シーン

  1. 01Kubernetes上のアプリへのSSO追加
  2. 02Azure ADグループによるアクセス制限
  3. 03Key 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 統合スキル

概要

このスキルでは、Azure AD(Entra ID)の OAuth2/OIDC シングルサインオンを Kubernetes クラスターにデプロイされたアプリケーションに実装する方法を、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

トラブルシューティング

エラーリファレンス
エラーコード説明解決方法
AADSTS50011Reply 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. マネージド ID を使用する - サービスプリンシパルの認証情報は避けます
  3. グループでアクセスを制限する - appRoleAssignmentRequired=true を有効にします
  4. シークレットをローテーションする - 有効期限前にリマインダーを設定します
  5. HTTPS のみを使用する - すべてのリダイレクト URI で HTTPS を使用する必要があります
  6. シングルテナントを使用する - 内部アプリでマルチテナントを使用しないでください
  7. 監査ログを有効にする - Azure AD のサインインログを有効にします

環境リファレンス

環境Key Vaultマネージド IDテナント 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 時間です。2 時間目に達する長時間のバッチジョブでは、401 を受けても黙って失敗します。すべての呼び出しを、401 の場合にトークンを更新する処理でラップしてください。

コメント