Votre plateforme tourne sur AKS, mais une partie du SI vit chez AWS — et un secret dont votre application a besoin est dans AWS Secrets Manager. Le réflexe courant : créer un utilisateur IAM, générer une access key, la stocker dans un Secret Kubernetes. C’est précisément la clé statique, éternelle et copiable qu’un bon design de sécurité cherche à éliminer.
La bonne réponse s’appelle IRSA (IAM Roles for Service Accounts) — et contrairement à une idée reçue, elle n’est pas réservée à EKS. Tout cluster Kubernetes exposant un issuer OIDC public peut être fédéré avec AWS IAM. Un cluster AKS avec --enable-oidc-issuer remplit la condition.
Cet article déroule le montage complet — issu de notre démo publique eso-aks-demo, où External Secrets Operator (ESO) lit trois coffres depuis le même cluster — et surtout les quatre pièges que nous avons réellement rencontrés en le mettant en place.
Le concept : AssumeRoleWithWebIdentity
Le mécanisme tient en une phrase : le pod présente le token OIDC de son ServiceAccount (signé par le cluster) à AWS STS, qui le vérifie auprès de l’issuer et rend des credentials temporaires pour un rôle IAM.
SA eso-aws-sa ──token OIDC──▶ STS AssumeRoleWithWebIdentity ──▶ credentials temporaires
▲ │ vérifie iss / sub / aud
signé par l'issuer AKS ▼
rôle IAM (lecture d'UN secret)
Aucune clé n’est créée, aucune n’expire mal, aucune ne fuit : la confiance repose sur la signature du cluster et une trust policy chirurgicale. C’est le miroir exact de Workload Identity côté Azure.
1. Déclarer l’issuer AKS comme OIDC provider IAM
OIDC=$(az aks show -g $RG -n $AKS --query oidcIssuerProfile.issuerUrl -o tsv)
aws iam create-open-id-connect-provider \
--url "$OIDC" \
--client-id-list sts.amazonaws.com \
--thumbprint-list "<sha1 du certificat de l'issuer>"
client-id-list sts.amazonaws.com: l’audience que STS attendra dans les tokens ;- le thumbprint est toujours exigé par l’API, mais AWS ne le vérifie plus pour les autorités de certification connues — sa valeur doit simplement être bien formée.
2. Le rôle IAM : une trust policy chirurgicale
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": { "Federated": "arn:aws:iam::<account>:oidc-provider/<issuer-sans-https>" },
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": { "StringEquals": {
"<issuer-sans-https>:sub": "system:serviceaccount:eso-demo:eso-aws-sa",
"<issuer-sans-https>:aud": "sts.amazonaws.com"
} }
}]
}
Trois verrous : seul ce cluster (Federated), seul ce ServiceAccount (sub), seule cette audience (aud). On attache ensuite au rôle une policy de lecture limitée au seul secret concerné — pas un secretsmanager:* sur * :
{
"Effect": "Allow",
"Action": ["secretsmanager:GetSecretValue", "secretsmanager:DescribeSecret"],
"Resource": "arn:aws:secretsmanager:eu-west-1:<account>:secret:demo/pg-app-aws*"
}
3. Côté cluster : le SA annoté et le SecretStore ESO
apiVersion: v1
kind: ServiceAccount
metadata:
name: eso-aws-sa
namespace: eso-demo
annotations:
eks.amazonaws.com/role-arn: "arn:aws:iam::<account>:role/eso-demo-aws"
---
apiVersion: external-secrets.io/v1
kind: SecretStore
metadata:
name: aws-store
namespace: eso-demo
spec:
provider:
aws:
service: SecretsManager
region: eu-west-1
auth:
jwt:
serviceAccountRef:
name: eso-aws-sa
Oui, l’annotation s’appelle bien
eks.amazonaws.com/role-arn— même hors EKS. C’est la convention IRSA, et c’est elle qu’ESO lit pour savoir quel rôle assumer.
Les quatre pièges (vécus, pas théoriques)
1. Le slash final de l’issuer compte. L’issuer AKS se termine par / (https://<region>.oic.prod-aks.azure.com/<tenant>/<uuid>/). IAM exige un match exact entre l’ARN du provider, les clés de condition de la trust policy et le claim iss du token. Retirer le slash « pour faire propre » produit un AccessDenied sans autre explication. Gardez l’URL telle quelle, partout.
2. Ne déclarez pas d’audiences dans le SecretStore. ESO demande déjà un token avec l’audience sts.amazonaws.com par défaut. En ajouter une explicitement crée un token multi-audiences, que STS rejette : InvalidIdentityToken: Token audience contains more than one audience while authorized party is not present.
3. L’ARN du rôle va dans l’annotation du SA, pas dans spec.provider.aws.role. Ce champ-là déclenche un chaînage (AssumeRole par-dessus l’identité web) — s’il pointe le même rôle, l’appel échoue puisque la trust policy n’autorise que la fédération. Symptôme : an IAM role must be associated with service account.
4. La propagation IAM n’est pas instantanée. Après un update-assume-role-policy, comptez jusqu’à une minute d’AccessDenied transitoire. Ne « corrigez » pas une configuration déjà juste : re-testez d’abord.
Vérifier à la main, sans ESO
Le test qui isole la fédération de tout le reste :
TOKEN=$(kubectl -n eso-demo create token eso-aws-sa --audience sts.amazonaws.com)
aws sts assume-role-with-web-identity \
--role-arn arn:aws:iam::<account>:role/eso-demo-aws \
--role-session-name test \
--web-identity-token "$TOKEN"
Si cet appel rend des credentials, la fédération est bonne — tout problème restant est côté ESO ou RBAC. Pour inspecter les claims (iss, sub, aud) du token : décodez sa deuxième partie base64.
Ce qu’il faut retenir
| Brique | Rôle |
|---|---|
| OIDC provider IAM | AWS fait confiance à l’issuer du cluster AKS |
Trust policy sub + aud |
seul le bon SA, avec la bonne audience |
| Policy sur le rôle | lecture d’un secret, pas d’un service |
Annotation eks.amazonaws.com/role-arn |
le câblage IRSA, lu par ESO |
AssumeRoleWithWebIdentity |
credentials temporaires, zéro clé statique |
Le tout est scripté et idempotent dans la démo eso-aks-demo (mise run aws-setup) — avec le pendant Azure dans Workload Identity sur AKS. Deux clouds, un cluster, zéro credential en dur.
Pour situer ce chantier dans une trajectoire plateforme plus large : Kubernetes pour l’entreprise et Migration cloud AWS.