azure-ad-sso について
azure-ad-ssoは、Kubernetes上のアプリケーションにAzure AD(Entra ID)のOAuth2/OIDCシングルサインオンとグループ単位のアクセス制御を設定するスキルです。アプリ別の設定例、Key Vaultによるシークレット管理、トラブルシューティングを扱います。
主な機能
- 01Azure ADアプリ登録のガイド
- 02OAuth2/OIDCとSSOの設定
- 03グループ単位のアクセス制限
- 04CSI経由のKey Vaultシークレット管理
- 05アプリ設定とエラーの調査
活用シーン
- 01Kubernetes上のアプリへのSSO追加
- 02Azure ADグループによるアクセス制限
- 03Key Vaultを使ったアプリシークレットの保存
- 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.mdazure-ad-sso-guide.mdtroubleshooting.md詳細
Azure AD SSO 統合スキル
概要
このスキルでは、Azure AD(Entra ID)の OAuth2/OIDC シングルサインオンを Kubernetes クラスターにデプロイされたアプリケーションに実装する方法を、Azure AD グループによるアクセス制限も含めて詳しく説明します。
クイックリファレンス
対応アプリケーション
| アプリケーション | プロバイダー | リダイレクト URI のパターン | グループ同期 |
|---|---|---|---|
| DefectDojo | azuread-tenant-oauth2 | /complete/azuread-tenant-oauth2/ | はい |
| Grafana | azuread | /login/azuread | はい |
| ArgoCD | microsoft(Dex) | /api/dex/callback | はい |
| Harbor | oidc | /c/oidc/callback | はい |
| SonarQube | saml または oidc | /oauth2/callback/saml | はい |
| OAuth2 Proxy | azure | /oauth2/callback | はい |
| Keycloak | oidc | /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 | Reply URL が一致しません | 末尾のスラッシュを含め、リダイレクト URI が完全に一致していることを確認します |
| AADSTS50105 | ユーザーが割り当てられていません | エンタープライズアプリの割り当てにユーザーまたはグループを追加します |
| AADSTS700016 | アプリが見つかりません | クライアント ID とテナント ID を確認します |
| AADSTS7000218 | シークレットの有効期限が切れています | Key Vault のシークレットをローテーションし、Pod を再起動します |
| AADSTS90102 | redirect_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
セキュリティのベストプラクティス
- シークレットをハードコードしない - 必ず Key Vault と CSI Driver を使用します
- マネージド ID を使用する - サービスプリンシパルの認証情報は避けます
- グループでアクセスを制限する -
appRoleAssignmentRequired=trueを有効にします - シークレットをローテーションする - 有効期限前にリマインダーを設定します
- HTTPS のみを使用する - すべてのリダイレクト URI で HTTPS を使用する必要があります
- シングルテナントを使用する - 内部アプリでマルチテナントを使用しないでください
- 監査ログを有効にする - Azure AD のサインインログを有効にします
環境リファレンス
| 環境 | Key Vault | マネージド ID | テナント ID |
|---|---|---|---|
| example-app-dev | kv-example-dev-hlg | <WORKLOAD_IDENTITY_CLIENT_ID> | <TENANT_ID> |
| example-app-hub | kv-example-default | <WORKLOAD_IDENTITY_CLIENT_ID> | <TENANT_ID> |
| example-app-prd | kv-example-prd | <WORKLOAD_IDENTITY_CLIENT_ID> | <TENANT_ID> |
詳細リファレンス
実装例の詳細については、以下を参照してください。
- references/azure-ad-sso-guide.md - マニフェストを含む完全なガイド
- references/app-configs.md - アプリケーション固有の設定
- references/troubleshooting.md - 詳細なトラブルシューティングガイド
注意点
- グループクレームの既定値は名前ではなく objectId です —
optionalClaims: groups: SecurityGroupを有効にし、マニフェストでトークンバージョンを 2 に設定してください。そうしないと、名前で照合するアプリでは何も表示されません。 - トークンのスコープ:
https://graph.microsoft.com/.defaultスコープで付与されるのは、管理者の同意を得た権限のみです。委任トークンのUser.Read.Allも、初回使用時に管理者の同意が必要です。 - OIDC リダイレクト URI は完全一致が必要です — 末尾のスラッシュが一致しないと、ネットワークの問題のように見える曖昧なエラーとともに AADSTS50011 が返されます。
- グループクレームは 200 グループで超過します:200 を超えるグループに属するユーザーには、グループクレームがまったく含まれません(超過)。メンバーシップの照会には Graph クエリを使用してください。
- アプリトークンの有効期間ポリシーはアプリごとに適用されます — 既定では 1 時間です。2 時間目に達する長時間のバッチジョブでは、401 を受けても黙って失敗します。すべての呼び出しを、401 の場合にトークンを更新する処理でラップしてください。
コメント
Markdown 対応