Skip to main content

Configurer l’analyse d’images Docker avec GitHub Actions

feature docker secrets

19 mai 2023

0 minutes de lecture

Aujourd’hui, le produit final de la plupart des dépôts Git est une image Docker, ensuite utilisée dans un déploiement Kubernetes. La sécurité étant un sujet brûlant, et à juste titre, il est essentiel d’analyser les images Docker que vous créez dans votre CI.

Dans cet article, je vais utiliser GitHub Actions pour créer des images Docker, puis les analyser afin d’y détecter des failles de sécurité. L’image Docker créée dans la CI est également envoyée vers le registre Docker de GitHub.

Tasse de café avec un latte art, sous le texte « Analyse d’images Docker en CI avec GITHUB »

Créer un workflow GitHub Actions

Le workflow CI que nous allons créer se compose des étapes suivantes :

  • Tester

  • Créer l’image

  • Analyser l’image

L’image créée lors de la deuxième étape est ensuite envoyée vers le registre Docker de GitHub, puis récupérée à nouveau lors de la troisième étape.

Créer le job de test

Si votre dépôt GitHub ne dispose d’aucune configuration CI, nous allons créer un fichier à l’emplacement `.github/workflows/ci.yaml` avec le contenu suivant :

    name: ci
    on:
      push:
        branches:
          - master
      pull_request:

    jobs:
      test:
        runs-on: ubuntu-latest

        strategy:
          matrix:
            node-version: [ 18.x ]

        steps:
          - name: Checkout 🛎️
            uses: actions/checkout@v3

          - name: Setup Node environment 🧱: Node.js ${{ matrix.node-version }}
            uses: actions/setup-node@v3
            with:
              node-version: ${{ matrix.node-version }}

          - name: Install and test 🪲
            run: |
              npm ci
              npm test

L’attribut `on` définit les événements qui déclenchent l’exécution de ce workflow. Dans notre cas, tout push sur la branche `master` et chaque pull request déclenchent le workflow CI.

Les étapes du job sont les suivantes :

  • Checkout 🔔 : récupère le code du dépôt

  • Configurer l’environnement Node 🧱 : installe l’environnement Node.js 18

  • Installer et tester 🪲 : installe les dépendances et exécute les tests

Ici, le job de test sert simplement d’exemple d’exécution des tests unitaires d’un projet Node.js. N’hésitez pas à l’adapter au langage de programmation et à la structure de votre projet.

Un job de test n’est pas indispensable pour créer l’image Docker, mais c’est une bonne pratique que j’ai choisi de suivre.

Créer le job de build Docker

Pour créer le job de build Docker, je commencerais par définir deux variables d’environnement au début du fichier, sous `on` :

  on:
      ...

    env:
      DOCKER_IMAGE_TAG: ${{ github.ref == 'refs/heads/master' && 'prod-' || 'dev-' }}${{ github.sha }}
      GITHUB_REGISTRY: ghcr.io
      GITHUB_REPOSITORY: ${{ github.repository }}

Créons maintenant le job `build_docker` :

    jobs:
      test:
        ...

      build_image:
        permissions:
          id-token: write
          contents: read
          packages: write
        runs-on: ubuntu-latest
        needs: [ test ]

        steps:
          - name: Checkout 🛎️
            uses: actions/checkout@v2

          - name: Log in to the Container registry 📦
            uses: docker/login-action@v2
            with:
              registry: ${{ env.GITHUB_REGISTRY }}
              username: ${{ github.actor }}
              password: ${{ secrets.GITHUB_TOKEN }}

          - name: Build and push Docker image 🐳
            uses: docker/build-push-action@v3
            with:
              push: true
              tags: |
                ${{ env.GITHUB_REGISTRY }}/${{ env.GITHUB_REPOSITORY }}:${{ env.DOCKER_IMAGE_TAG }}

Examinons le code. La section des autorisations est nécessaire, car nous voulons envoyer des images Docker vers le registre de GitHub.

Le job s’exécute sur la dernière version d’Ubuntu et nécessite que le job de test soit terminé. Ainsi, nous ne créons des images Docker que pour du code fonctionnel. Si vous avez ignoré le job de test, supprimez la ligne `needs`.

Passons maintenant aux étapes :

  • Checkout 🔔 : récupère le code du dépôt. Cette étape est nécessaire pour accéder au Dockerfile et à son contexte.

  • Se connecter au registre de conteneurs 📦

  • Créer et envoyer l’image Docker 🐳 : à cette étape, nous utilisons les variables d’environnement définies au début du fichier pour spécifier le tag de l’image Docker.

Créer le job d’analyse

Ajoutons maintenant le job d’analyse de l’image Docker :

    jobs:
      test:
        ...

      build_image:
        ...

      scan_docker_image:
        permissions:
          id-token: read
          contents: read
          packages: read
        runs-on: ubuntu-latest
        needs: [ build_image ]
        steps:
          - name: Checkout 🛎️
            uses: actions/checkout@v2

          - name: Log in to the Container registry 📦
            uses: docker/login-action@v2
            with:
              registry: ${{ env.GITHUB_REGISTRY }}
              username: ${{ github.actor }}
              password: ${{ secrets.GITHUB_TOKEN }}

          - name: Scan Docker image 🐳
            uses: snyk/actions/docker@master
            continue-on-error: true
            with:
              image: ${{ env.GITHUB_REGISTRY }}/${{ env.GITHUB_REPOSITORY }}:${{ env.DOCKER_IMAGE_TAG }}
              args: --file=Dockerfile --severity-threshold=high --sarif-file-output=snyk.sarif
            env:
              SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}

          - name: Upload Snyk report as sarif 📦
            uses: github/codeql-action/upload-sarif@v2
            with:
              sarif_file: snyk.sarif

Cette étape utilise Snyk, le moteur d’analyse de sécurité qui alimente `docker scan`. Pour l’utiliser, vous devez créer un compte gratuit et enregistrer son jeton dans un secret :

  • Inscrivez-vous ici

  • Récupérez le jeton en suivant les instructions ici

  • Ajoutez-le aux secrets CI de votre dépôt GitHub sous le nom `SNYK_TOKEN`, comme expliqué dans l’annexe.

Passons aux étapes :

  • Checkout 🛎️ : le scanner est plus performant s’il peut également accéder au Dockerfile.

  • Se connecter au registre de conteneurs 📦 : pour récupérer les images Docker que nous y avons envoyées précédemment.

  • Analyser l’image Docker 🐳 : ce job analyse l’image Docker et consigne les vulnérabilités dans un fichier nommé `snyk.sarif`. GitHub reconnaît ce format de fichier et peut l’afficher dans la PR — d’où l’étape suivante.

  • Importer le rapport Snyk au format SARIF 📦 : nous importons ici le fichier `sarif` généré à l’étape précédente dans GitHub.

Les vulnérabilités importées dans GitHub s’affichent dans votre PR comme ceci :

Annotation d’analyse du code sur un Dockerfile signalant une vulnérabilité curl de gravité élevée et l’échec d’une vérification.

Conclusion


Dans cet article, nous avons créé un workflow GitHub Actions composé de trois jobs : exécuter les tests, créer l’image Docker, l’envoyer vers le registre GitHub, rechercher les problèmes de sécurité et importer le rapport de vulnérabilités pour que GitHub puisse les interpréter et les afficher dans les PR.

Annexe : ajouter un secret CI sur GitHub

Dans votre dépôt :

  1. Cliquez sur l’onglet Settings.

  2. Dans le panneau de menu à gauche, sous la section Security, cliquez sur Secrets and variables.

  3. Ensuite, dans les nouveaux éléments de menu qui s’affichent, cliquez sur Actions.

  4. En haut à droite, cliquez sur le bouton vert intitulé New repository secret.

  5. Dans la section Name, saisissez le nom souhaité, par exemple `SNYK_TOKEN`.

  6. Dans la section Secret, collez le secret, par exemple le jeton Snyk que vous avez copié depuis le site de Snyk.

  7. Cliquez ensuite sur le bouton vert intitulé Add secret.

Page des paramètres d’un dépôt GitHub affichant les secrets et variables Actions, notamment les secrets du dépôt ACCESS_TOKEN et SNYK_TOKEN.