Lab : Configuration du Workload Identity OIDC pour GitHub Actions avec AWS

Aperçu

Si vos workflows GitHub Actions s’authentifient auprès d’AWS en utilisant AWS_ACCESS_KEY_ID et AWS_SECRET_ACCESS_KEY stockés comme secrets de dépôt, vous avez un sérieux problème de sécurité. Ces credentials à longue durée de vie n’expirent jamais d’eux-mêmes, peuvent être exfiltrés par n’importe quelle étape du workflow (y compris les actions tierces), et donnent aux attaquants un accès persistant à votre compte AWS en cas de compromission.

La fédération OpenID Connect (OIDC) élimine entièrement ce risque. Au lieu de stocker des credentials AWS statiques dans GitHub, votre workflow demande un jeton OIDC à courte durée de vie auprès du fournisseur d’identité de GitHub. AWS valide ce jeton, vérifie les claims (dépôt, branche, environnement), et émet des credentials temporaires qui expirent en quelques minutes. Aucun secret n’est stocké nulle part — la relation de confiance est établie entre le fournisseur OIDC de GitHub et votre rôle IAM AWS.

Dans ce lab pratique, vous allez :

  • Mettre en place la configuration de base non sécurisée (clés AWS statiques) afin de comprendre ce que vous remplacez
  • Créer un fournisseur d’identité OIDC dans AWS qui fait confiance à GitHub Actions
  • Créer un rôle IAM avec une politique de confiance limitée à votre dépôt et branche spécifiques
  • Mettre à jour votre workflow GitHub Actions pour utiliser l’authentification OIDC
  • Implémenter des contrôles d’accès basés sur les branches et les environnements
  • Auditer les événements d’authentification OIDC dans CloudTrail
  • Supprimer les anciens credentials statiques pour finaliser la migration

À la fin de ce lab, votre pipeline CI/CD n’aura aucun credential AWS stocké, et chaque événement d’authentification sera traçable jusqu’à un dépôt, une branche, un commit et une exécution de workflow spécifiques.

Prérequis

Avant de commencer ce lab, assurez-vous de disposer des éléments suivants :

  • Compte AWS avec un accès administratif IAM (capacité à créer des fournisseurs d’identité, des rôles et des politiques)
  • Compte GitHub avec un dépôt que vous contrôlez (le niveau gratuit est suffisant)
  • AWS CLI v2 installé et configuré localement (aws --version doit retourner 2.x)
  • Terraform v1.5+ (optionnel mais recommandé — ce lab fournit les instructions Console et Terraform)
  • Compréhension de base des rôles IAM, des politiques de confiance et de la syntaxe des workflows GitHub Actions

Durée estimée : 60 à 90 minutes

Configuration de l’environnement : la base non sécurisée

Avant d’implémenter OIDC, établissons le modèle non sécurisé que vous allez remplacer. Cela rend l’amélioration de la sécurité concrète et vous donne un workflow fonctionnel à migrer.

Étape 1 : Créer un dépôt de test

Créez un nouveau dépôt GitHub appelé oidc-lab. Initialisez-le avec un README et clonez-le localement :

gh repo create oidc-lab --public --clone
cd oidc-lab

Étape 2 : Stocker les credentials AWS comme secrets GitHub (la méthode non sécurisée)

Si vous avez un utilisateur IAM existant avec un accès programmatique, stockez ses credentials comme secrets GitHub :

gh secret set AWS_ACCESS_KEY_ID --body "AKIAIOSFODNN7EXAMPLE"
gh secret set AWS_SECRET_ACCESS_KEY --body "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"

Pourquoi c’est dangereux :

  • Pas d’expiration : Ces credentials sont valides jusqu’à rotation ou suppression manuelle. En cas de fuite, un attaquant a un accès indéfini.
  • Exposition large : Chaque exécution de workflow, chaque fork (si public), et chaque action tierce dans votre workflow peut lire ces secrets.
  • Pas de piste d’audit granulaire : CloudTrail montre l’utilisateur IAM, mais pas quel dépôt, quelle branche ou quel workflow a déclenché l’appel API.
  • Charge de rotation : Vous devez manuellement faire la rotation de ces clés et mettre à jour les secrets GitHub — un processus souvent négligé.

Étape 3 : Créer le workflow de base non sécurisé

Créez .github/workflows/deploy.yml avec des credentials statiques :

name: Deploy (Insecure - Static Keys)

on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Configure AWS Credentials (INSECURE)
        uses: aws-actions/configure-aws-credentials@v4
        with:
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          aws-region: us-east-1

      - name: Verify Identity
        run: aws sts get-caller-identity

      - name: List S3 Buckets
        run: aws s3 ls

Committez et poussez ce workflow. Confirmez qu’il s’exécute avec succès — c’est la base que vous allez remplacer par OIDC.

Exercice 1 : Créer le fournisseur d’identité OIDC dans AWS

La première étape de la fédération OIDC consiste à indiquer à AWS de faire confiance au fournisseur d’identité de GitHub. C’est une configuration unique par compte AWS.

Option A : Console AWS

  1. Ouvrez la Console IAMFournisseurs d’identitéAjouter un fournisseur
  2. Sélectionnez OpenID Connect
  3. Pour URL du fournisseur, entrez : https://token.actions.githubusercontent.com
  4. Cliquez sur Obtenir l’empreinte (AWS récupère et valide le certificat TLS)
  5. Pour Audience, entrez : sts.amazonaws.com
  6. Cliquez sur Ajouter le fournisseur

Option B : AWS CLI

# Obtenir l'empreinte du fournisseur OIDC de GitHub
# Depuis 2024, l'empreinte de GitHub est gérée par AWS et auto-vérifiée
# Vous pouvez la récupérer avec :
THUMBPRINT=$(openssl s_client -servername token.actions.githubusercontent.com \
  -showcerts -connect token.actions.githubusercontent.com:443 < /dev/null 2>/dev/null \
  | openssl x509 -fingerprint -noout \
  | cut -d'=' -f2 \
  | tr -d ':' \
  | tr '[:upper:]' '[:lower:]')

aws iam create-open-id-connect-provider \
  --url https://token.actions.githubusercontent.com \
  --client-id-list sts.amazonaws.com \
  --thumbprint-list "$THUMBPRINT"

Option C : Terraform

resource "aws_iam_openid_connect_provider" "github" {
  url             = "https://token.actions.githubusercontent.com"
  client_id_list  = ["sts.amazonaws.com"]
  thumbprint_list = ["6938fd4d98bab03faadb97b34396831e3780aea1"]

  tags = {
    Name        = "GitHub Actions OIDC"
    Environment = "shared"
    ManagedBy   = "terraform"
  }
}

Note : AWS vérifie désormais automatiquement l’empreinte du fournisseur OIDC de GitHub. La valeur de l’empreinte dans la ressource Terraform est requise par l’API mais AWS validera la chaîne de certificats quelle que soit la valeur fournie.

Vérification

Confirmez que le fournisseur a été créé :

aws iam list-open-id-connect-providers

Vous devriez voir un ARN comme :

arn:aws:iam::123456789012:oidc-provider/token.actions.githubusercontent.com

Exercice 2 : Créer le rôle IAM avec la politique de confiance

Créez maintenant un rôle IAM que GitHub Actions peut assumer via OIDC. La politique de confiance est la partie la plus critique — elle détermine exactement quels dépôts, branches et environnements peuvent assumer ce rôle.

Étape 1 : Créer la politique de confiance

Enregistrez ce qui suit dans trust-policy.json. Remplacez 123456789012 par votre ID de compte AWS, et myorg/myrepo par votre organisation et dépôt GitHub :

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Federated": "arn:aws:iam::123456789012:oidc-provider/token.actions.githubusercontent.com"
      },
      "Action": "sts:AssumeRoleWithWebIdentity",
      "Condition": {
        "StringEquals": {
          "token.actions.githubusercontent.com:aud": "sts.amazonaws.com"
        },
        "StringLike": {
          "token.actions.githubusercontent.com:sub": "repo:myorg/myrepo:ref:refs/heads/main"
        }
      }
    }
  ]
}

Comprendre les champs de la politique de confiance

  • Principal.Federated — L’ARN du fournisseur OIDC GitHub que vous avez créé dans l’exercice 1. Cela indique à AWS quel fournisseur d’identité approuver.
  • Action: sts:AssumeRoleWithWebIdentity — L’action STS spécifique pour la fédération OIDC. Elle est différente de sts:AssumeRole (utilisée pour les rôles inter-comptes) ou sts:AssumeRoleWithSAML (utilisée pour la fédération SAML).
  • Condition.StringEquals.aud — Valide le claim d’audience dans le jeton OIDC. Cela doit être sts.amazonaws.com pour correspondre à ce que l’action configure-aws-credentials envoie.
  • Condition.StringLike.sub — Valide le claim de sujet. C’est le contrôle de sécurité le plus important. Le sujet contient le dépôt, le type de ref et la valeur de ref. L’utilisation de StringLike avec des caractères génériques permet une correspondance flexible, tandis que StringEquals exige une correspondance exacte.

Étape 2 : Créer le rôle IAM

aws iam create-role \
  --role-name github-actions-deploy \
  --assume-role-policy-document file://trust-policy.json \
  --description "Role for GitHub Actions OIDC deployment" \
  --max-session-duration 3600

Étape 3 : Attacher une politique IAM minimale

Suivez le principe du moindre privilège. Pour ce lab, attachez une politique qui accorde un accès en lecture seule à un bucket S3 spécifique :

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "s3:GetObject",
        "s3:ListBucket"
      ],
      "Resource": [
        "arn:aws:s3:::my-deployment-bucket",
        "arn:aws:s3:::my-deployment-bucket/*"
      ]
    }
  ]
}
# Enregistrez ce qui précède dans permissions-policy.json, puis :
aws iam put-role-policy \
  --role-name github-actions-deploy \
  --policy-name S3ReadAccess \
  --policy-document file://permissions-policy.json

Équivalent Terraform

data "aws_iam_policy_document" "github_actions_trust" {
  statement {
    effect  = "Allow"
    actions = ["sts:AssumeRoleWithWebIdentity"]

    principals {
      type        = "Federated"
      identifiers = [aws_iam_openid_connect_provider.github.arn]
    }

    condition {
      test     = "StringEquals"
      variable = "token.actions.githubusercontent.com:aud"
      values   = ["sts.amazonaws.com"]
    }

    condition {
      test     = "StringLike"
      variable = "token.actions.githubusercontent.com:sub"
      values   = ["repo:myorg/myrepo:ref:refs/heads/main"]
    }
  }
}

resource "aws_iam_role" "github_actions_deploy" {
  name                 = "github-actions-deploy"
  assume_role_policy   = data.aws_iam_policy_document.github_actions_trust.json
  max_session_duration = 3600

  tags = {
    Name      = "GitHub Actions Deploy"
    ManagedBy = "terraform"
  }
}

resource "aws_iam_role_policy" "s3_read" {
  name = "S3ReadAccess"
  role = aws_iam_role.github_actions_deploy.id

  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Effect = "Allow"
        Action = [
          "s3:GetObject",
          "s3:ListBucket"
        ]
        Resource = [
          "arn:aws:s3:::my-deployment-bucket",
          "arn:aws:s3:::my-deployment-bucket/*"
        ]
      }
    ]
  })
}

Vérification

# Confirmez que le rôle existe et possède la bonne politique de confiance
aws iam get-role --role-name github-actions-deploy \
  --query 'Role.AssumeRolePolicyDocument' --output json

Exercice 3 : Mettre à jour le workflow GitHub Actions

Remplacez maintenant le workflow avec credentials statiques par l’authentification OIDC. C’est l’étape principale de la migration.

Étape 1 : Mettre à jour le workflow

Remplacez le contenu de .github/workflows/deploy.yml par ce qui suit :

name: Deploy (Secure - OIDC)

on:
  push:
    branches: [main]

permissions:
  id-token: write   # Requis pour la demande de jeton OIDC
  contents: read     # Requis pour actions/checkout

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Configure AWS Credentials via OIDC
        uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: arn:aws:iam::123456789012:role/github-actions-deploy
          role-session-name: github-actions-${{ github.run_id }}
          aws-region: us-east-1

      - name: Verify Identity
        run: |
          aws sts get-caller-identity
          echo "Successfully authenticated via OIDC!"

      - name: List S3 Bucket Contents
        run: aws s3 ls s3://my-deployment-bucket/

Changements clés expliqués

  • permissions: id-token: write — C’est obligatoire. Cela accorde au workflow la permission de demander un jeton OIDC auprès du fournisseur d’identité de GitHub. Sans cela, l’action configure-aws-credentials ne peut pas obtenir de jeton.
  • permissions: contents: read — Lorsque vous définissez une permission explicite, toutes les autres permissions sont par défaut à none. Vous devez explicitement accorder contents: read pour que actions/checkout fonctionne.
  • role-to-assume — L’ARN du rôle IAM créé dans l’exercice 2. L’action appellera sts:AssumeRoleWithWebIdentity en utilisant le jeton OIDC.
  • role-session-name — Un nom de session descriptif qui apparaît dans CloudTrail. Inclure le github.run_id rend chaque session traçable jusqu’à une exécution de workflow spécifique.
  • Pas de aws-access-key-id ni aws-secret-access-key — Ces champs sont complètement supprimés. L’action détecte que role-to-assume est défini sans credentials statiques et utilise automatiquement OIDC.

Étape 2 : Pousser et vérifier

git add .github/workflows/deploy.yml
git commit -m "Migrate to OIDC authentication"
git push origin main

Naviguez vers l’onglet Actions dans votre dépôt GitHub. Vous devriez voir le workflow en cours d’exécution. Dans l’étape « Verify Identity », la sortie ressemblera à :

{
    "UserId": "AROA3XFRBF23ZCEXAMPLE:github-actions-9876543210",
    "Account": "123456789012",
    "Arn": "arn:aws:sts::123456789012:assumed-role/github-actions-deploy/github-actions-9876543210"
}

Remarquez que l’ARN montre assumed-role/github-actions-deploy — cela confirme que le workflow s’authentifie via le rôle OIDC, et non via un utilisateur IAM.

Exercice 4 : Restreindre par branche, environnement et tag

Le claim sub de la politique de confiance est votre mécanisme principal de contrôle d’accès. Le claim de sujet de GitHub Actions suit un format prévisible qui encode le dépôt, le type de déclencheur et la ref.

Modèles courants de claims de sujet

Déclencheur Format du claim de sujet
Push vers une branche repo:OWNER/REPO:ref:refs/heads/BRANCH
Pull request repo:OWNER/REPO:pull_request
Environnement repo:OWNER/REPO:environment:ENV_NAME
Push de tag repo:OWNER/REPO:ref:refs/tags/TAG

Restriction à la branche main uniquement

C’est la configuration de l’exercice 2. Seuls les workflows déclenchés par un push vers main peuvent assumer le rôle :

"StringLike": {
  "token.actions.githubusercontent.com:sub": "repo:myorg/myrepo:ref:refs/heads/main"
}

Restriction à un environnement spécifique

Les environnements GitHub fournissent une couche supplémentaire de contrôle d’accès. Lorsqu’un job spécifie un environment, le claim de sujet change pour inclure le nom de l’environnement :

"StringLike": {
  "token.actions.githubusercontent.com:sub": "repo:myorg/myrepo:environment:production"
}

Restriction aux releases taggées

Autoriser uniquement les releases taggées (par exemple, v1.0.0, v2.3.1) à assumer le rôle :

"StringLike": {
  "token.actions.githubusercontent.com:sub": "repo:myorg/myrepo:ref:refs/tags/v*"
}

Tester les restrictions

Créez une branche de fonctionnalité et poussez une exécution de workflow :

git checkout -b feature/test-oidc-restriction
git commit --allow-empty -m "Test OIDC restriction"
git push origin feature/test-oidc-restriction

Si votre politique de confiance restreint à refs/heads/main, le workflow sur la branche de fonctionnalité échouera avec :

Error: Could not assume role with OIDC: Not authorized to perform
sts:AssumeRoleWithWebIdentity

AccessDenied: Not authorized to perform sts:AssumeRoleWithWebIdentity

Maintenant, poussez le même changement vers main :

git checkout main
git merge feature/test-oidc-restriction
git push origin main

Le workflow sur main réussit. Cela démontre le contrôle d’accès basé sur les claims — la politique de confiance AWS évalue les claims intégrés dans le jeton OIDC pour prendre des décisions d’autorisation. Aucun credential n’est impliqué ; le jeton lui-même porte le contexte d’autorisation.

Exercice 5 : Rôles par environnement

Les déploiements en production doivent avoir des contrôles plus stricts que le staging. Dans cet exercice, vous créez des rôles IAM séparés pour chaque environnement, avec des politiques de confiance et des permissions différentes.

Étape 1 : Créer les environnements GitHub

Dans votre dépôt, allez dans ParamètresEnvironnements :

  1. Créez un environnement staging (sans règles de protection)
  2. Créez un environnement production avec :
    • Réviseurs requis : Ajoutez au moins un membre de l’équipe
    • Branches de déploiement : Restreindre à main uniquement

Étape 2 : Créer le rôle IAM de staging

Le rôle de staging fait confiance à toutes les branches du dépôt :

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Federated": "arn:aws:iam::123456789012:oidc-provider/token.actions.githubusercontent.com"
      },
      "Action": "sts:AssumeRoleWithWebIdentity",
      "Condition": {
        "StringEquals": {
          "token.actions.githubusercontent.com:aud": "sts.amazonaws.com"
        },
        "StringLike": {
          "token.actions.githubusercontent.com:sub": "repo:myorg/myrepo:*"
        }
      }
    }
  ]
}
aws iam create-role \
  --role-name github-actions-staging \
  --assume-role-policy-document file://staging-trust-policy.json \
  --description "Staging deployment role - all branches"

Étape 3 : Créer le rôle IAM de production

Le rôle de production fait confiance uniquement à l’environnement production sur la branche main :

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Federated": "arn:aws:iam::123456789012:oidc-provider/token.actions.githubusercontent.com"
      },
      "Action": "sts:AssumeRoleWithWebIdentity",
      "Condition": {
        "StringEquals": {
          "token.actions.githubusercontent.com:aud": "sts.amazonaws.com",
          "token.actions.githubusercontent.com:sub": "repo:myorg/myrepo:environment:production"
        }
      }
    }
  ]
}
aws iam create-role \
  --role-name github-actions-production \
  --assume-role-policy-document file://production-trust-policy.json \
  --description "Production deployment role - main branch + production environment only"

Étape 4 : Workflow multi-environnement

Créez un workflow qui déploie automatiquement en staging et en production avec approbation manuelle :

name: Deploy Multi-Environment

on:
  push:
    branches: [main]

permissions:
  id-token: write
  contents: read

jobs:
  deploy-staging:
    runs-on: ubuntu-latest
    environment: staging
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Configure AWS Credentials (Staging)
        uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: arn:aws:iam::123456789012:role/github-actions-staging
          role-session-name: staging-${{ github.run_id }}
          aws-region: us-east-1

      - name: Deploy to Staging
        run: |
          aws sts get-caller-identity
          echo "Deploying to staging environment..."
          # Your staging deployment commands here

  deploy-production:
    runs-on: ubuntu-latest
    needs: deploy-staging
    environment: production
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Configure AWS Credentials (Production)
        uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: arn:aws:iam::123456789012:role/github-actions-production
          role-session-name: production-${{ github.run_id }}
          aws-region: us-east-1

      - name: Deploy to Production
        run: |
          aws sts get-caller-identity
          echo "Deploying to production environment..."
          # Your production deployment commands here

Lorsque ce workflow s’exécute :

  1. Le job staging s’exécute immédiatement, assume github-actions-staging, et déploie
  2. Le job production attend que le staging soit terminé, puis se met en pause pour approbation manuelle
  3. Un réviseur requis approuve le déploiement dans l’interface GitHub
  4. Le job de production s’exécute, assume github-actions-production, et déploie

La politique de confiance du rôle de production utilise StringEquals (et non StringLike) avec le sujet exact repo:myorg/myrepo:environment:production. Cela signifie que seuls les jobs qui déclarent environment: production peuvent assumer le rôle de production — et les environnements GitHub imposent que seuls les déploiements depuis la branche main avec approbation d’un réviseur peuvent utiliser cet environnement.

Terraform pour les deux rôles

locals {
  github_oidc_arn = aws_iam_openid_connect_provider.github.arn
  github_repo     = "myorg/myrepo"
}

# Rôle staging - fait confiance à toutes les branches
resource "aws_iam_role" "github_actions_staging" {
  name = "github-actions-staging"

  assume_role_policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Effect = "Allow"
        Principal = {
          Federated = local.github_oidc_arn
        }
        Action = "sts:AssumeRoleWithWebIdentity"
        Condition = {
          StringEquals = {
            "token.actions.githubusercontent.com:aud" = "sts.amazonaws.com"
          }
          StringLike = {
            "token.actions.githubusercontent.com:sub" = "repo:${local.github_repo}:*"
          }
        }
      }
    ]
  })
}

# Rôle production - fait confiance uniquement à l'environnement production
resource "aws_iam_role" "github_actions_production" {
  name = "github-actions-production"

  assume_role_policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Effect = "Allow"
        Principal = {
          Federated = local.github_oidc_arn
        }
        Action = "sts:AssumeRoleWithWebIdentity"
        Condition = {
          StringEquals = {
            "token.actions.githubusercontent.com:aud" = "sts.amazonaws.com"
            "token.actions.githubusercontent.com:sub" = "repo:${local.github_repo}:environment:production"
          }
        }
      }
    ]
  })
}

Exercice 6 : Vérifier et auditer

L’authentification OIDC crée des pistes d’audit détaillées dans AWS CloudTrail. Chaque appel AssumeRoleWithWebIdentity est journalisé avec l’ensemble complet des claims OIDC de GitHub.

Étape 1 : Interroger CloudTrail pour les événements OIDC

aws cloudtrail lookup-events \
  --lookup-attributes AttributeKey=EventName,AttributeValue=AssumeRoleWithWebIdentity \
  --max-results 10 \
  --query 'Events[].{Time:EventTime,Username:Username,Resources:Resources}' \
  --output table

Étape 2 : Examiner l’événement CloudTrail

Un événement AssumeRoleWithWebIdentity typique dans CloudTrail contient les champs clés suivants :

{
  "eventName": "AssumeRoleWithWebIdentity",
  "eventSource": "sts.amazonaws.com",
  "requestParameters": {
    "roleArn": "arn:aws:iam::123456789012:role/github-actions-deploy",
    "roleSessionName": "github-actions-9876543210"
  },
  "requestID": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "responseElements": {
    "credentials": {
      "accessKeyId": "ASIAEXAMPLE...",
      "expiration": "Mar 23, 2026 2:00:00 PM"
    },
    "subjectFromWebIdentityToken": "repo:myorg/myrepo:ref:refs/heads/main",
    "provider": "arn:aws:iam::123456789012:oidc-provider/token.actions.githubusercontent.com"
  },
  "additionalEventData": {
    "WebIdFederationData": {
      "federatedProvider": "arn:aws:iam::123456789012:oidc-provider/token.actions.githubusercontent.com",
      "attributes": {
        "sub": "repo:myorg/myrepo:ref:refs/heads/main",
        "aud": "sts.amazonaws.com",
        "iss": "https://token.actions.githubusercontent.com",
        "repository": "myorg/myrepo",
        "ref": "refs/heads/main",
        "sha": "abc123def456...",
        "actor": "github-username",
        "workflow": "Deploy (Secure - OIDC)",
        "run_id": "9876543210"
      }
    }
  }
}

Remarquez la richesse de cette piste d’audit : vous pouvez voir le dépôt exact, la branche, le SHA du commit, l’utilisateur GitHub qui a déclenché le workflow, et le nom du workflow. C’est bien plus détaillé que ce que vous obtenez avec des credentials d’utilisateur IAM statiques.

Étape 3 : Créer une alarme CloudWatch pour les tentatives échouées d’AssumeRole

Les appels AssumeRoleWithWebIdentity échoués peuvent indiquer des tentatives d’accès non autorisées ou des politiques de confiance mal configurées. Configurez un filtre de métriques et une alarme :

# Créer un filtre de métriques CloudWatch Logs pour les AssumeRoleWithWebIdentity échoués
aws logs put-metric-filter \
  --log-group-name CloudTrail/DefaultLogGroup \
  --filter-name FailedOIDCAssumeRole \
  --filter-pattern '{ ($.eventName = "AssumeRoleWithWebIdentity") && ($.errorCode = "AccessDenied") }' \
  --metric-transformations \
    metricName=FailedOIDCAssumeRoleCount,metricNamespace=SecurityMetrics,metricValue=1

# Créer une alarme CloudWatch qui se déclenche quand les échecs dépassent le seuil
aws cloudwatch put-metric-alarm \
  --alarm-name FailedOIDCAssumeRole \
  --alarm-description "Alert on failed OIDC AssumeRoleWithWebIdentity attempts" \
  --metric-name FailedOIDCAssumeRoleCount \
  --namespace SecurityMetrics \
  --statistic Sum \
  --period 300 \
  --threshold 3 \
  --comparison-operator GreaterThanOrEqualToThreshold \
  --evaluation-periods 1 \
  --alarm-actions arn:aws:sns:us-east-1:123456789012:security-alerts \
  --treat-missing-data notBreaching

Étape 4 : Auditer quels dépôts et branches ont accédé au rôle

Utilisez CloudTrail Lake ou Athena pour interroger les modèles d’accès historiques :

# Utiliser CloudTrail lookup pour trouver toutes les authentifications OIDC des dernières 24 heures
aws cloudtrail lookup-events \
  --lookup-attributes AttributeKey=EventName,AttributeValue=AssumeRoleWithWebIdentity \
  --start-time $(date -u -d '24 hours ago' '+%Y-%m-%dT%H:%M:%SZ' 2>/dev/null || date -u -v-24H '+%Y-%m-%dT%H:%M:%SZ') \
  --end-time $(date -u '+%Y-%m-%dT%H:%M:%SZ') \
  --query 'Events[].CloudTrailEvent' \
  --output text | jq -r '.responseElements.subjectFromWebIdentityToken // "N/A"' | sort | uniq -c | sort -rn

Cela vous donne un décompte de fréquence des dépôts et branches qui se sont authentifiés via OIDC — essentiel pour les revues d’accès et les audits de conformité.

Exercice 7 : Supprimer les anciennes clés d’accès

C’est l’exercice le plus important de tout le lab. La migration des credentials statiques vers OIDC est incomplète — et votre posture de sécurité n’est pas améliorée — tant que les anciennes clés d’accès n’ont pas été supprimées. Tant que les deux méthodes d’authentification coexistent, un attaquant peut toujours utiliser les clés statiques.

Étape 1 : Supprimer les secrets GitHub

Supprimez les anciens secrets de votre dépôt GitHub :

gh secret delete AWS_ACCESS_KEY_ID --repo myorg/myrepo
gh secret delete AWS_SECRET_ACCESS_KEY --repo myorg/myrepo

Vérifiez qu’ils ont été supprimés :

gh secret list --repo myorg/myrepo

Vous ne devriez plus voir AWS_ACCESS_KEY_ID ni AWS_SECRET_ACCESS_KEY dans la liste.

Étape 2 : Désactiver les clés d’accès IAM

Avant de supprimer, désactivez d’abord les clés. Cela vous donne une option de retour en arrière si quelque chose ne fonctionne plus :

# Lister les clés d'accès de l'utilisateur IAM
aws iam list-access-keys --user-name github-deploy-user

# Désactiver (pas supprimer) la clé d'accès
aws iam update-access-key \
  --user-name github-deploy-user \
  --access-key-id AKIAIOSFODNN7EXAMPLE \
  --status Inactive

Étape 3 : Vérifier que le pipeline fonctionne toujours

Déclenchez le workflow OIDC et confirmez qu’il se termine avec succès :

git commit --allow-empty -m "Verify OIDC-only authentication"
git push origin main

Vérifiez l’onglet Actions — le workflow devrait réussir en utilisant uniquement OIDC.

Étape 4 : Supprimer définitivement les clés d’accès

Après avoir confirmé que le pipeline fonctionne sans clés statiques (attendez au moins 24 à 48 heures par sécurité), supprimez définitivement les clés :

aws iam delete-access-key \
  --user-name github-deploy-user \
  --access-key-id AKIAIOSFODNN7EXAMPLE

Si l’utilisateur IAM n’était utilisé que pour GitHub Actions, supprimez entièrement l’utilisateur :

aws iam delete-user-policy --user-name github-deploy-user --policy-name DeployPolicy
aws iam delete-user --user-name github-deploy-user

La migration est maintenant terminée. Votre pipeline s’authentifie exclusivement via la fédération OIDC avec zéro credential stocké.

Nettoyage

S’il s’agissait d’un environnement de lab, nettoyez toutes les ressources :

# Supprimer les rôles IAM
aws iam delete-role-policy --role-name github-actions-deploy --policy-name S3ReadAccess
aws iam delete-role --role-name github-actions-deploy

aws iam delete-role-policy --role-name github-actions-staging --policy-name StagingPolicy
aws iam delete-role --role-name github-actions-staging

aws iam delete-role-policy --role-name github-actions-production --policy-name ProductionPolicy
aws iam delete-role --role-name github-actions-production

# Supprimer le fournisseur OIDC
OIDC_ARN=$(aws iam list-open-id-connect-providers \
  --query "OpenIDConnectProviderList[?ends_with(Arn, 'token.actions.githubusercontent.com')].Arn" \
  --output text)
aws iam delete-open-id-connect-provider --open-id-connect-provider-arn "$OIDC_ARN"

# Supprimer le dépôt de test (optionnel)
gh repo delete myorg/oidc-lab --yes

Si vous avez utilisé Terraform, le nettoyage est plus simple :

terraform destroy -auto-approve

Points clés à retenir

  • La fédération OIDC élimine les credentials stockés. Plus de AWS_ACCESS_KEY_ID ni de AWS_SECRET_ACCESS_KEY dans GitHub — la relation de confiance est entre le fournisseur d’identité de GitHub et votre rôle IAM AWS.
  • Les credentials à courte durée de vie réduisent le rayon d’impact. Les jetons OIDC et les credentials de session AWS résultants expirent en quelques minutes, pas en mois. Un jeton compromis est inutilisable après expiration.
  • Les politiques de confiance fournissent un contrôle d’accès basé sur les claims. Le claim sub dans le jeton OIDC encode le dépôt, la branche et l’environnement — utilisez les conditions StringEquals et StringLike pour imposer un accès granulaire.
  • Les rôles par environnement imposent la séparation des responsabilités. Le staging et la production doivent utiliser des rôles IAM différents avec des politiques de confiance et des niveaux de permissions différents.
  • CloudTrail fournit des pistes d’audit complètes. Chaque événement d’authentification OIDC journalise le dépôt, la branche, le commit, l’acteur et le workflow — bien plus riche que l’audit des credentials statiques.
  • La migration est incomplète tant que les anciennes clés ne sont pas supprimées. Exécuter OIDC en parallèle avec des clés statiques n’apporte aucune amélioration de sécurité. Supprimez les anciens credentials après avoir vérifié que OIDC fonctionne.

Prochaines étapes

Continuez à renforcer la posture de sécurité de votre CI/CD avec ces guides connexes :