Infrastructure
Azure AKS : diagnostiquer Workload Identity avant de réintroduire un secret
Un runbook de production pour isoler un échec Microsoft Entra Workload ID entre pod, ServiceAccount, webhook, token OIDC, identité managée et RBAC, puis valider ou revenir en arrière sans secret client.
Un déploiement AKS se termine correctement, les pods passent Ready, mais l’application ne peut plus lire Key Vault, publier dans Service Bus ou accéder à Storage. Les journaux montrent un échec d’échange de jeton, un 401 ou un 403. La réponse la plus rapide semble être de remonter un secret client dans Kubernetes ou d’élargir le rôle de l’identité managée. Ces deux actions contournent le diagnostic et augmentent durablement la surface de risque.
Le cas d’usage est un workload AKS authentifié avec Microsoft Entra Workload ID. Une modification du chart Helm a renommé le ServiceAccount, déplacé le déploiement dans un autre namespace, changé une annotation ou recréé le cluster. Le runbook doit déterminer si la panne se situe dans la mutation du pod, le token projeté, la relation de fédération, l’identité sélectionnée ou l’autorisation sur la ressource cible. La sortie attendue est une décision : corriger la déclaration, restaurer la release précédente, réparer une fédération bornée ou traiter un vrai refus RBAC.
Figer le périmètre avant de modifier l’identité
Commencez par relier un pod précis à une identité et à une ressource cible. Un 403 seul ne permet pas de distinguer un jeton absent, un échange OIDC refusé et un jeton valide sans permission suffisante.
incident:
cluster: aks-prod-weu
namespace: payments
deployment: settlement-api
pod: settlement-api-7c9f6d8f9b-abcde
service_account: settlement-api
first_failure_utc: 2026-08-15T14:20:00Z
expected_identity:
type: user_assigned_managed_identity
client_id: <expected-client-id>
tenant_id: <expected-tenant-id>
target:
service: Azure Key Vault
resource_id: <exact-resource-id>
operation: secrets/get
recent_changes:
- helm_release
- namespace_or_service_account
- federated_credential
- cluster_oidc_issuer
- role_assignment Conservez le message d’erreur complet, l’heure, le nom du pod et l’identifiant de corrélation du service cible. Vérifiez aussi si tous les pods échouent, seulement une nouvelle révision ou seulement un namespace. Cette matrice réduit immédiatement la recherche.
Prouver que le pod demande Workload Identity
Le ServiceAccount doit porter l’annotation du client attendu, et le template du pod doit porter le label azure.workload.identity/use: "true". Le déploiement doit réellement référencer ce ServiceAccount. Inspectez les objets rendus, pas uniquement les valeurs Helm.
NS=payments
DEPLOY=settlement-api
kubectl -n $NS get deploy $DEPLOY -o jsonpath='{.spec.template.spec.serviceAccountName}{"\n"}{.spec.template.metadata.labels}{"\n"}'
SA=$(kubectl -n $NS get deploy $DEPLOY -o jsonpath='{.spec.template.spec.serviceAccountName}')
kubectl -n $NS get serviceaccount $SA -o yaml
kubectl -n $NS get pods -l app=settlement-api -o custom-columns='NAME:.metadata.name,SA:.spec.serviceAccountName,WI:.metadata.labels.azure.workload.identity/use'
kubectl -n $NS get events --sort-by=.lastTimestamp | tail -n 40 Une annotation correcte sur un ServiceAccount inutilisé ne change rien. De même, modifier l’annotation après le démarrage ne réécrit pas un pod existant : prévoyez un redémarrage contrôlé ou une nouvelle révision, puis vérifiez le pod réellement créé.
Vérifier la mutation et le token projeté
Pour un pod éligible, le webhook injecte les variables Azure et le volume contenant le token de compte de service projeté. L’absence de ces éléments pointe d’abord vers le label, le webhook ou une admission qui n’a pas eu lieu, pas vers RBAC.
NS=payments
POD=settlement-api-7c9f6d8f9b-abcde
kubectl -n $NS get pod $POD -o jsonpath='{range .spec.containers[*]}{.name}{"\n"}{range .env[*]}{.name}={.value}{"\n"}{end}{end}' | grep -E 'AZURE_(CLIENT_ID|TENANT_ID|FEDERATED_TOKEN_FILE)|^settlement'
kubectl -n $NS get pod $POD -o jsonpath='{.spec.volumes}'
kubectl -n $NS exec $POD -- sh -c '
test -n "$AZURE_FEDERATED_TOKEN_FILE" &&
test -r "$AZURE_FEDERATED_TOKEN_FILE" &&
printf "client=%s tenant=%s token_file=readable\n" "$AZURE_CLIENT_ID" "$AZURE_TENANT_ID"
' Ne copiez pas le token brut dans le ticket d’incident. Pour contrôler ses claims, décodez localement uniquement le payload et ne conservez que iss, sub, aud, iat et exp. Le sujet attendu suit la forme system:serviceaccount:<namespace>:<serviceaccount> ; la comparaison doit être exacte, y compris la casse et le slash final de l’issuer.
Comparer les trois claims à la fédération Entra
La relation de confiance est un triplet : issuer du cluster, subject Kubernetes et audience. Une crédential fédérée peut être créée avec succès tout en contenant un mauvais namespace ; l’erreur n’apparaît qu’à l’échange du token.
RG=rg-identities-prod
IDENTITY=mi-settlement-prod
AKS_RG=rg-aks-prod
AKS=aks-prod-weu
NS=payments
SA=settlement-api
ISSUER=$(az aks show -g $AKS_RG -n $AKS --query oidcIssuerProfile.issuerUrl -o tsv)
EXPECTED_SUBJECT="system:serviceaccount:$NS:$SA"
printf 'issuer=%s\nsubject=%s\naudience=api://AzureADTokenExchange\n' "$ISSUER" "$EXPECTED_SUBJECT"
az identity federated-credential list --resource-group $RG --identity-name $IDENTITY --query '[].{name:name,issuer:issuer,subject:subject,audiences:audiences}' -o table Si le cluster a été recréé, son issuer peut avoir changé alors que le nom du cluster est identique. Si le namespace ou le ServiceAccount a été renommé, le subject historique ne correspond plus. L’audience habituelle de la fédération directe est api://AzureADTokenExchange. Ne modifiez pas plusieurs champs pour tester : identifiez l’écart exact et préparez son retour arrière.
Une nouvelle fédération peut demander un court délai de propagation. Ce délai ne justifie pas des retries infinis : utilisez une fenêtre bornée, conservez l’heure de création et arrêtez l’essai si les trois claims ne correspondent pas.
Séparer échange de token et autorisation Azure
Une fois le token Entra obtenu, l’autorisation sur la cible reste une étape distincte. Un échec d’échange OIDC se corrige dans la chaîne de fédération. Un 403 renvoyé par Key Vault, Storage ou Service Bus avec la bonne identité se traite au niveau du rôle, du plan de données ou de la politique du service.
Variables ou token projete absents
Verifier label du pod, ServiceAccount reference et mutation webhook
Token present, echange Entra refuse
Comparer issuer, subject, audience, tenant et client ID
Token Entra obtenu, ressource cible renvoie 403
Prouver principalId, operation et scope RBAC exact
Une seule revision echoue
Comparer manifestes rendus et revenir a la release precedente
Tous les workloads lies au cluster echouent
Verifier issuer OIDC, configuration cluster et changement partage
Une seule ressource cible echoue
Inspecter role, firewall et journaux de cette ressource sans modifier la federation Évitez le rôle Contributor au niveau de la souscription comme test de connectivité. Il masque le diagnostic, ne couvre pas nécessairement le plan de données et laisse un privilège difficile à retirer. Interrogez les attributions de l’identité au scope exact et corrélez avec les journaux de la cible.
Corriger avec une modification réversible
Le correctif doit suivre la preuve. Si Helm a renommé le ServiceAccount, restaurez le nom précédent ou créez délibérément la nouvelle fédération. Si l’issuer appartient à l’ancien cluster, ajoutez la relation correspondant au cluster actif avant de supprimer l’ancienne. Si le client ID est erroné, corrigez l’annotation et recréez les pods de manière progressive.
change:
observed_mismatch: subject
intended_fix: restore_previous_service_account_name
scope: deployment/settlement-api
rollout: one_replica_then_progressive
preconditions:
- current_manifest_exported
- expected_identity_principal_recorded
- target_role_scope_recorded
- previous_helm_revision_available
stop_conditions:
- token_exchange_error_on_canary
- unexpected_client_id
- authorization_failure_on_previously_healthy_target
rollback:
command: helm rollback settlement-api <previous-revision> -n payments
preserve: [pod_events, application_logs, target_audit_logs, rendered_manifests] N’ajoutez pas un secret client « temporaire » dans le même déploiement. Les chaînes de credentials peuvent alors choisir un mécanisme différent selon l’environnement, et un succès ne prouve plus que Workload Identity fonctionne.
Valider puis retirer seulement les objets devenus inutiles
Validez d’abord un pod neuf, puis la révision complète. La preuve minimale comprend les variables injectées, le token projeté lisible, les claims conformes, l’identité Entra attendue, l’opération métier réussie et les journaux de la ressource cible. Testez aussi une opération volontairement interdite pour vérifier que le rôle n’a pas été élargi.
Validation positive
Nouveau pod cree avec le ServiceAccount attendu
Label workload identity present
Variables et token projetes presents
Issuer, subject et audience conformes
Client ID et tenant attendus
Lecture Key Vault ou operation metier reussie
Aucune erreur sur la ressource cible pendant la fenetre
Validation negative
Une operation hors role reste refusee
Aucun secret client ajoute au pod ou au pipeline
Aucun role large ajoute a la souscription
Nettoyage differe
Ancienne federation retiree seulement apres observation
Ancien ServiceAccount retire apres absence de consommateurs
Preuves avant/apres jointes au changement Le rollback le plus sûr restaure la dernière combinaison connue du déploiement, du ServiceAccount et de la fédération. Supprimer immédiatement l’ancien objet de confiance retire cette option et transforme une correction simple en migration irréversible.
Conclusion
Un incident Workload Identity sur AKS se diagnostique comme une chaîne de confiance, pas comme un simple problème de permissions. Le pod doit demander la mutation, recevoir un token projeté, présenter les bons claims à la fédération, sélectionner l’identité attendue puis disposer du rôle minimal sur la cible.
La décision devient alors nette : corriger le manifeste quand l’injection manque, réparer le triplet issuer-subject-audience quand l’échange échoue, traiter RBAC seulement quand l’identité est prouvée, ou revenir à la release précédente si le changement a déplacé la frontière de confiance. Le service revient sans secret longue durée et l’équipe conserve un chemin de validation et de rollback exploitable.