OIDC GitLab vers AWS : la trust policy et le job CI
Comment on a branché GitLab CI sur AWS via OIDC, avec une trust policy serrée, un seul rôle pour tous les projets et la limite ACLSizePerRole qu'on a frôlée.
- CI/CD
- Auth
- Operations
Le sujet peut vite devenir verbeux parce qu’il mélange IAM, CI et sécurité. Cet article fait l’inverse : montrer le chemin minimal qui marche, puis dire où il casse — y compris la limite AWS qu’on n’avait pas vue venir.
Le pattern en cinq lignes
Le cœur du montage :
- un provider OIDC GitLab enregistré dans IAM
- une trust policy avec
StringLikesurgitlab.com:sub - un
audfixé àsts.amazonaws.com assume-role-with-web-identitydans le job CI- un seul rôle partagé entre plusieurs projets, avec sessions courtes (1h)
Le lecteur doit pouvoir reconstruire le montage sans lire un guide AWS de 40 pages.
La trust policy Terraform
# cicd.tf
locals {
gitlab_build_subjects = [
"project_path:keyson/cercly/*:ref_type:branch:ref:main",
"project_path:keyson/cercly/*:ref_type:branch:ref:develop",
"project_path:keyson/cercly/*:ref_type:tag:ref:*",
]
}
data "aws_iam_policy_document" "gitlab_build_assume_role" {
statement {
effect = "Allow"
actions = ["sts:AssumeRoleWithWebIdentity"]
principals {
type = "Federated"
identifiers = [aws_iam_openid_connect_provider.gitlab.arn]
}
condition {
test = "StringEquals"
variable = "gitlab.com:aud"
values = ["sts.amazonaws.com"]
}
condition {
test = "StringLike"
variable = "gitlab.com:sub"
values = local.gitlab_build_subjects
}
}
}
Le StringLike avec wildcard * permet de couvrir tous les projets sous keyson/cercly/ sans lister chaque repo.
Le job CI
# templates/docker-build.yml
.assume_aws_role: &assume_aws_role
- >
export $(printf "AWS_ACCESS_KEY_ID=%s AWS_SECRET_ACCESS_KEY=%s AWS_SESSION_TOKEN=%s"
$(aws sts assume-role-with-web-identity
--role-arn "$AWS_BUILD_ROLE_ARN"
--role-session-name "gitlab-build-${CI_PROJECT_NAME}-${CI_PIPELINE_ID}"
--web-identity-token "$CI_JOB_JWT_V2"
--duration-seconds 3600
--query "Credentials.[AccessKeyId,SecretAccessKey,SessionToken]"
--output text))
Pas de credentials statiques. Le token OIDC est fourni par GitLab à chaque job via $CI_JOB_JWT_V2, échangé contre des credentials AWS temporaires valables 1h.
La limite qu’on n’avait pas vue venir
On avait initialement listé chaque projet explicitement dans la trust policy :
gitlab_build_subjects = [
"project_path:keyson/cercly/apis/operator-api:ref_type:branch:ref:main",
"project_path:keyson/cercly/apis/platform-api:ref_type:branch:ref:main",
"project_path:keyson/cercly/bff/member-bff:ref_type:branch:ref:main",
# ... 8 autres projets
]
En ajoutant portal et mobile, le deploy CI a échoué avec :
LimitExceeded: Cannot exceed quota for ACLSizePerRole: 2048
AWS limite la taille totale des trust policies à 2048 caractères par rôle. Avec des chemins de projet longs multipliés par 3 branches autorisées (main, develop, tags), on atteignait la limite avec 5-6 projets.
La solution : remplacer l’énumération par un wildcard sur le namespace :
gitlab_build_subjects = [
"project_path:keyson/cercly/*:ref_type:branch:ref:main",
"project_path:keyson/cercly/*:ref_type:branch:ref:develop",
"project_path:keyson/cercly/*:ref_type:tag:ref:*",
]
Trois lignes au lieu de trente. Tous les projets actuels et futurs sous keyson/cercly/ sont couverts. Le wildcard * dans StringLike est supporté nativement par IAM.
Le seul garde-fou perdu : un projet extérieur au namespace keyson/cercly/ ne peut plus se glisser par erreur. Mais c’est le même namespace GitLab — si quelqu’un a accès au namespace, il a accès aux projets.
La checklist opérationnelle
Pour ajouter un nouveau projet :
- Créer le projet GitLab sous
keyson/cercly/ - Créer le repo ECR dans Terraform (
var.ecr_services) - Appliquer Terraform — le repo ECR et les droits sont créés
- Pousser une première image — ça marche immédiatement
Plus besoin de toucher à la trust policy pour chaque nouveau projet.
Avec du recul
Une bonne intégration OIDC vaut surtout par sa lisibilité quand on ajoute un projet de plus. La version par énumération avait l’air précise et sécurisée. Elle était surtout fragile : chaque nouveau projet était une occasion d’oublier une ligne, de dépasser la limite, ou de devoir planifier un Terraform apply avant le premier push.
Le wildcard est moins explicite. Il est plus robuste.