Maximize Page
Tech & DevOps HubEspace Tech & DevOps: Explorez le monde du Dev, du Cloud et des outils DevOps à travers nos articles et discussions Explore the world of development, the cloud and DevOps tools

Trivy: Installation failed et vulnérabilité sur l'image Docker

Date de l'article:06-04-2026
Docker CI/CD Github-Actions
Nous allons, dans cet article, traiter deux incidents DevSecOps liés à Trivy: La première est une défaillance surevenue à l'installation de l'action suite à une mise à jour dependabot. La seconde est la détection de vulnérabilités critiques dans une image Docker distroless utilisée en production.

Nous allons, dans cet article, traiter deux incidents DevSecOps liés à Trivy: La première est une défaillance surevenue à l'installation de l'action, suite à une mise à jour dependabot. La seconde est la détection de vulnérabilités critiques dans une image Docker distroless utilisée en production.
Les deux situations sont bloquantes pour le build.

Contexte
Le projet utilise l'action officielle aquasecurity/trivy-action dans tous les workflows:
  • [develop] et [staging]: Scan des dépendances du filesystem
  • [main]: Scan de l'image Docker sur la branche
  • Blocage du pipeline en cas de vulnérabilités: sévérité HIGH ou CRITICAL

Les deux issues ont été rencontrées le même jour donc, ici l'une résolution servira à la suivante.

Plusieurs solutions peuvent être envisagées selon le contexte et, plusieurs ajustements sont parfois nécessaires pour conserver un pipeline stable tout en maintenant le niveau de sécurité élevé.

2 problèmes rencontrés
1 À la suite d'une mise à jour des actions par Dependabot
- L'installation de Trivy ne s'exécutait plus correctement dans les différents environnements - Voir la solution trouvée

2 Des vulnérabilités ont été détectées dans l'image Docker (distroless)
      Cette situation a eu deux conséquences principales :
  • Blocage systématique du pipeline en production
  • Comportement instable selon les environnements, avec des effets de bord sur le cache et l'installation
Voir le détail de l'analyse et son fix
Update: Ce process à été automatisé (19/05)

BUILD FAILED 02/04/2026 Trivy - Installation Failed with exit code 1

Diagnostic de l'erreur
#output
bash ./trivy/contrib/install.sh -b ... v0.65.0
...
found version: 0.65.0
Error: Process completed with exit code 1
Cause identifiée

Il s'agit d'une limitation du mécanisme d'installation de Trivy utilisé par l'action. L'action clone le dépôt, exécute contrib/install.sh et télécharge le binaire.

Certaines versions de Trivy, comme v0.65.0, provoquent l'échec de ce script d'installation. Le téléchargement se termine correctement, la version est bien détectée, mais le script install.sh échoue ensuite.

Attention
Il est important de noter que l'organisation GitHub d'Aqua Security a récemment fait l'objet d'incidents de sécurité (mars 2026).

Il est recommandé de vérifier les sources officielles sur aquasec.com pour obtenir les dernières informations sur la sécurité de leurs dépôts

A la date du 03/04, les versions 0.69.2 et 0.69.3 étaient confirmées sûres. - avd.aquasec.com/misconfig/dockerfile advisories GHSA-69fq-xp46-6x23
À la date du 05/05, la version 0.69 n'est plus listée dans le repo, elle à été remplacée par le version 0.70

FIX Installation manuelle de Trivy

À la suite de plusieurs itérations sur le pipeline CI/CD, la configuration de Trivy a été revue de manière significative.
L'objectif était d'obtenir une installation plus fiable, plus prévisible et plus facile à maintenir.

Cache Trivy (main)
- name: Cache Trivy DB
  uses: actions/cache@v5.0
  with:
    path: ~/.cache/trivy
    key: trivy-db-${{ runner.os }}-${{ github.run_number }}
    restore-keys: |
      trivy-db-${{ runner.os }}-
Avantages
  • pipeline plus stable et plus prévisible
  • réduction des effets de bord liés aux actions tierces
  • meilleure reproductibilité dans une logique DevSecOps

Cette approche permet de mieux contrôler le cache, la version installée et les options utilisées. Trivy n'a plus qu'à vérifier les nouvelles failles publiées depuis le dernier scan


1. Installation de Trivy (toute branches)

La configuration suit la méthode officielle recommandée pour installer la version stable de Trivy, via le dépôt APT d'Aqua Security sur une distribution Debian/Ubuntu.

- name: Install Trivy (official repo)
  run: |
    sudo apt-get update
    sudo apt-get install -y wget gnupg lsb-release

    wget -qO - https://get.trivy.dev/deb/public.key | \
      gpg --dearmor | sudo tee /usr/share/keyrings/trivy.gpg > /dev/null

    echo "deb [signed-by=/usr/share/keyrings/trivy.gpg] https://get.trivy.dev/deb generic main" | \
      sudo tee /etc/apt/sources.list.d/trivy.list

    sudo apt-get update
    sudo apt-get install -y trivy
Utiliser le dépôt officiel via apt permet de contrôler l'origine du binaire et d'éviter de dépendre d'une action tierce qui pourrait être compromise ou injecter du code malveillant.

2. Vérification de la version (logs)

UPDATE au 05/05/26; la version 0.69 installée après l'incident, à été retirée du dépot, à ce jour la version 0.70 est disponible:

      Install Trivy (official repo)
Preparing to unpack .../trivy_0.70.0_amd64.deb ...
Unpacking trivy (0.70.0) ...
Setting up trivy (0.70.0) ...

affiche la version dans les logs →
3. Fixer la version
Ajoutez, après sudo apt-get update
TRIVY_VERSION="0.70.0"

if apt-cache madison trivy | grep -q "$TRIVY_VERSION"; then
  sudo apt-get install -y trivy=$TRIVY_VERSION
else
  echo "[WARNING] Version not found → fallback to latest"
  sudo apt-get install -y trivy
fi

trivy --version

Exécution des scans (FS)
ci-develop - ci-staging:
- name: Run Trivy scan
  run: |
    trivy fs . \
      --severity HIGH,CRITICAL \
      --format table \
      --exit-code 0    

cd-prod:
- name: Run Trivy scan
  run: |
    trivy fs . \
      --format table \
      --exit-code 1 \
      --severity HIGH,CRITICAL \
      --no-progress

--exit-code 0 affiche toujours l'intégralité du tableau des vulnérabilités dans les logs mais ne bloque pas le build.
Cela nécessite une consultation manuelle: en cliquant sur l'étape du scan depuis l'interface de la CI et lire les logs.

--exit-code 1 bloque le build à la moindre vulnérabilités correspondant aux critères (HIGH/CRITICAL)
--no-progress désactive la barre de progression interactive dans la console. C'est une option essentielle en CI/CD

Conclusion

L'installation de trivy en manuel résouds tout les conflits de sha que j'ai eu ces derniers jours, sha updaté par dependabot > fix avec version stable > remplacement de cette version par l'installation manuelle. Seule cette installation s'est déroulée sans problème.

Dans un premier temps j'avais testé sans fixer de version pour savoir laquelle, d'apres eux, allait être téléchargée. Ensuite j'ai regardé les logs et fixé la version


BUILD FAILED 02/04/2026 - Scan Trivy : vulnérabilité détectée sur l'image Docker

UPDATE 05/05/2026
Après une nouvelle mise à jour Dependabot, de nouvelles vulnérabilités se sont ajoutées, ... j'ai donc décidé de faire la migration vers une image Debian:13, ces vulnérabilités ne sont donc plus présentes. Le fichier .trivyignore.yaml a été vidé.

→ Le process est conservé et à été adapté pour la nouvelle image (cas ou l'histoire se repéterait)

UPDATE 19/05/2026
À ce jour, j'ai automatisé le processus de la PR-Watch, celle-ci étant au départ "observatrice & detectrice" de changement, pour devenir un système entièrement automatisé pour la mise à jour de l'image Docker (distroless Debian13:nonroot), je vous en fait part dans l'article suivant.

Contexte: de l'utilisation de l'image (distroless:debian12) avec détection de vulnérabilité (Trivy) FIXED

Le Dockerfile utilisé reste volontairement minimal afin de réduire la surface d'attaque, ce qui correspond à l'approche des images distroless.
Ce type d'image ne contient pas les outils classiques de gestion de paquets, ce qui empêche un simple apt-get upgrade dans le Dockerfile

Cette image embarque des paquets Debian, dont libpng, qui peuvent être signalés comme vulnérables tant que la version corrigée n'est pas disponible dans l'image publiée.

Diagnostic depuis le Scan Trivy dans les Actions
#output Report Summary
┌───────────────────────────────────┬────────┬─────────────────┐
│              Target               │  Type  │ Vulnerabilities │
├───────────────────────────────────┼────────┼─────────────────┤
│ flashcards:49aaf1d (debian 12.13) │ debian │        2        │
├───────────────────────────────────┼────────┼─────────────────┤
│ app/app.jar                       │  jar   │        0        │
└───────────────────────────────────┴────────┴─────────────────┘
┌─────────────┬────────────────┬──────────┬────────┬───────────────────┬──────────────────┬─────────────────────────────────────────────────────────┐
│   Library   │ Vulnerability  │ Severity │ Status │ Installed Version │  Fixed Version   │                            Title                        │
├─────────────┼────────────────┼──────────┼────────┼───────────────────┼──────────────────┼─────────────────────────────────────────────────────────┤
│ libpng16-16 │ CVE-2026-33416 │ HIGH     │ fixed  │ 1.6.39-2+deb12u3  │ 1.6.39-2+deb12u4 │ libpng: arbitrary code execution due to use-after-free  │
│             │                │          │        │                   │                  │ vulnerability                                           │
│             ├────────────────┤          │        │                   │                  ├─────────────────────────────────────────────────────────┤
│             │ CVE-2026-33636 │          │        │                   │                  │ libpng: information disclosure and denial of service    │
│             │                │          │        │                   │                  │ via out-of-bounds read/write                            │
└─────────────┴────────────────┴──────────┴────────┴───────────────────┴──────────────────┴─────────────────────────────────────────────────────────┘

Le statut fixed indique qu'un correctif existe, mais qu'il n'est pas encore intégré dans l'image analysée.

Important : Les vulnérabilités détectées par Trivy dans ce cas ne proviennent pas du code applicatif ni du JAR, mais de l'image de base (distroless → Debian 12). Elles concernent des bibliothèques système (glibc, zlib, etc.) embarquées dans l'image.


Solution envisagée

L'approche retenue consiste à vérifier si une version plus récente de gcr.io/distroless/java17-debian12 intègre le correctif Debian deb12u4.
Si le correctif n'est pas encore disponible, la base actuelle est conservée avec une exception temporaire.

Processus à mettre en place
  1. Script bash qui vérifie si un nouveau digest est disponible pour l'image
  2. Workflow "PR Watch", appelle le script, dispose d'un cron pour se lancer tous les jours. Quand un nouveau digest est trouvé, il envoye une PR:
    • develop: aucun check. PR develop > staging
    • staging: test sur l'image "flashcards:staging" avec "--exit code 0" afin de visualiser les vulnérabilités, sans bloquer le pipeline. si ok: PR staging > main
    • prod: test sur l'image: avec "--exit code 1". Suivant la réponse, le comportement varie:
      • true (nouveau digest) : Scan de l'image avec exit code 1, si ok : je peux updaté l'image réelement et nettoyer les CSV
      • false (digest inchangé): charge .trivignore: le pipeline peut continuer rien n'a changé
Le script est également exécuté à chaque x que le pipeline tourne (update etc)

Étapes de vérification de l'image
  La manière la plus simple pour vérifier si un tag + récent existe;
 a.  Soit consulter le dépôt de l'image et de comparer le digest ou la date de publication avec la version utilisée.
 b.  Soit, directement: tirer l'image, inspecter et comparer le digest obtenu avec celui que l'on utilise dans le pipeline ou, de lancer le scan directement.

Méthode rapide
La liste des tags supportés peut être consultée sur: gcr.io/distroless/java17..
→ Comparer plusieurs variantes si besoin:
docker pull gcr.io/distroless/java17-debian12:nonroot
docker pull gcr.io/distroless/java17-debian12:debug-nonroot

Autres solutions
Solution avec jq: sans pull - sans install
docker manifest inspect "$IMAGE" | jq -r '.config.digest'
Avec crane: sans pull - plus rapide - déterministe
crane digest $IMAGE
Docker Commandes CLI
1. Tirer l'image la plus récente:
IMAGE="gcr.io/distroless/java17-debian12:latest"
docker pull $IMAGE
2. Inspecter le digest local:
docker image inspect "$IMAGE" /
    --format '{{index .RepoDigests 0}}'

Fonctionne uniquement si l'image est déjà locale OU que l'on fait un pull avant

3. Lancer un scan local (trivy):
trivy image $IMAGE

Pour confirmer la présence de deb12u4, il faut soit scanner l'image, soit vérifier le paquet embarqué via Trivy ou un SBOM.


Résultat en local
$ docker pull gcr.io/distroless/java17-debian12:latest
latest: Pulling from distroless/java17-debian12
Digest: sha256:0210d5b77dfbe851916184405ddfc8297e7bc652029f7a485274cb71ea2462bc
Status: Image is up to date for gcr.io/distroless/java17-debian12:latest
gcr.io/distroless/java17-debian12:latest

$ docker image inspect gcr.io/distroless/java17-debian12:latest --format '{{.Id}} {{.RepoDigests}}'
sha256:0210d5b77dfbe851916184405ddfc8297e7bc652029f7a485274cb71ea2462bc [gcr.io/distroless/java17-debian12@sha256:0210d5b77dfbe851916184405ddfc8297e7bc652029f7a485274cb71ea2462bc]

Pour la comparaison de l'étape 3: Comme je n'ai ni "Trivy" ni "crane" en local;
un script bash a été mis en place pour comparer automatiquement le SHA utilisé dans le pipeline avec celui de l'image latest

Méthode Fiabilité
crane parfaite
docker pull + inspect fiable
manifest inspect fiable mais moins pratique pour l'automatisation

J'utiliserai donc: "Docker" en local et "crane" dans le script et en CI (installation)


FIX - Étape 1: Mise en place du script de détection

Retrouver le script dans le repository

Le fichier "check-docker-image-latest.sh"
#!/usr/bin/env bash
set -euo pipefail

IMAGE="gcr.io/distroless/java17-debian12:latest"
STATE_FILE="ci-scripts/.distroless-java17-debian12.digest"

echo "[INFO] Checking crane availability..."

if ! command -v crane >/dev/null 2>&1; then
  echo "[WARNING] crane not found → skipping digest check"
  echo "digest_changed=false" >> "$GITHUB_OUTPUT"
  exit 0
fi

echo "[INFO] Fetching current digest..."
CURRENT_DIGEST=$(crane digest "$IMAGE")

if [[ -f "$STATE_FILE" ]]; then
  PREVIOUS_DIGEST=$(cat "$STATE_FILE")
else
  PREVIOUS_DIGEST=""
fi

echo "[INFO] Current:  $CURRENT_DIGEST"
echo "[INFO] Previous: $PREVIOUS_DIGEST"

if [[ "$CURRENT_DIGEST" == "$PREVIOUS_DIGEST" ]]; then
  echo "[INFO] No update detected"
  echo "digest_changed=false" >> "$GITHUB_OUTPUT"
else
  echo "[INFO] Update detected"
  echo "digest_changed=true" >> "$GITHUB_OUTPUT"
  echo "digest_value=$CURRENT_DIGEST" >> "$GITHUB_OUTPUT"
fi
Description

Script conçu pour optimiser les pipelines en détectant si une image de base (ici une image distroless de Google) a réellement changé

Étape par étape :

Récupération du Digest: On utilise crane pour extraire l'identifiant unique et immuable de l'image, plutôt que de se fier au tag :latest qui peut pointer vers différentes versions

Comparaison d'État: Il compare ce nouveau digest avec celui stocké et versionné (.distroless-java17-debian12.digest)

Sortie GitHub Actions: Définition d'une variable de sortie (digest_changed) via $GITHUB_OUTPUT qui retourne:
Soit false: L'image est identique, On peux sauter les étapes lourdes
Soit true: Une nouvelle version de l'image de base est disponible, il faut mettre à jour!

Le fichier (versionné) contient le digest utilisé:
sha256:0210d5b77dfbe851916184405ddfc8297e7bc652029f7a485274cb71ea2462bc

Confirme que l'image correspond à la dernière version publiée pour ce tag, mais pas nécessairement que les correctifs de sécurité les plus récents y sont intégrés

UPDATE * au 05/05/2026, J'ai migré vers la version "debian13:nonroot" ce qui à résolu les vulnérabilité en cours. Les scripts et pipelines ont été adaptés également et tourne toujours, mais pour la nouvelle image

RV dans le repository pour voir les màj

UPDATE * 05/05/26: à présent le script compare l'image "nonroot" elle-même ainsi que son propre fichier digest:
IMAGE="${1:-gcr.io/distroless/java17-debian13:nonroot}"
STATE_FILE="${2:-ci-scripts/.distroless-java17-debian13.digest}"

FIX - Étape 2: Ajout de correctifs
Le fichier ".trivyignore.yaml"

Agit pleinement pour main où le controle est + strict (exit 1)
En staging on voit toutes les vulnérabilité (exit 0) sans bloquer le build (util pour vérifier, être à jour)

vulnerabilities:
  - id: CVE-2026-33416
    expired_at: 2026-07-15
    statement: Temporary ignore until distroless is rebuilt with deb12u4
  - id: CVE-2026-33636
    expired_at: 2026-07-15
    statement: Temporary ignore until distroless is rebuilt with deb12u4
Gestion des vulnérabilités

La base actuelle est conservée, avec l'ajout de ce fichier. Il contient les exceptions signalées au Scan.
Une date d'expiration est ajoutée afin d'éviter qu'elles soient ignorées de façon permanente.

Cette pratique doit rester exceptionnelle et temporaire. Le correctif doit être apporté via une mise à jour de l'image de base ou un changement d'image mère

Le repository reste la source de vérité en cas de divergence liées soit aux mises à jour applicative, soit à ceux des actions dans le workflows CI/CD
Le tableau de suivi & mise à jour peut être consulté pour savoir ce qui est en cours ou prévu prochainement.

FIX - Étape 3: Automatisation de la détection de nouveau Digest disponibles

L'objectif est de vérifier chaque jour si une nouvelle version de l'image de base Distroless est disponible. Si c'est le cas, le workflow créera automatiquement une branche et une Pull Request est envoyée pour mettre à jour le fichier .digest - Ce workflow garantit que l'application utilise toujours la version la plus sûre de l'image

Le script requiert le versionning du fichier
     ".digest", généré par le script bash
Le fichier ".github/workflows/pr-watch-distroless.yml"
name: Distroless Watch

on:
  workflow_dispatch:
  schedule:
    - cron: '0 6 * * *'

permissions:
  contents: write
  pull-requests: write

jobs:
  watch:
    runs-on: ubuntu-24.04

    steps:
      - name: Checkout

      - name: Setup git
        run: |
          git config user.name "github-actions"
          git config user.email "github-actions@github.com"

      - name: Install crane
        run: |
          ci-scripts/install-crane.sh

      - name: Run digest check
        id: check
        run: |
          chmod +x ci-scripts/check-docker-image-latest.sh
          ci-scripts/check-docker-image-latest.sh

      - name: Create branch with updated digest
        if: steps.check.outputs.digest_changed == 'true'
        id: branch
        env:
          GH_TOKEN: ${{ secrets.GH_PAT }}
        run: |
          EXISTING=$(gh pr list \
              --base main \
              --search "distroless" \
              --json number \
              --jq length)

          if [ "$EXISTING" -gt 0 ]; then
            echo "Existing PR detected, skipping"
            echo "skip_pr=true" >> $GITHUB_OUTPUT
            exit 0
          fi
        
          BRANCH="chore/distroless-update-$(date +%s)"

          git checkout -b $BRANCH
          echo "${{ steps.check.outputs.digest_value }}" > 
                ci-scripts/.distroless-java17-debian12.digest
          git add ci-scripts/.distroless-java17-debian12.digest
          git commit -m "chore: update distroless digest"
          git push origin $BRANCH
          echo "branch=$BRANCH" >> $GITHUB_OUTPUT
      
      - name: Create PR
        if: steps.check.outputs.digest_changed == 'true' 
            && steps.branch.outputs.skip_pr != 'true'
        env:
          GH_TOKEN: ${{ secrets.GH_PAT }}
        run: |
          BRANCH="${{ steps.branch.outputs.branch }}"

          gh pr create \
            --base main \
            --head "$BRANCH" \
            --title "chore: distroless image updated" \
            --body "Automatic update of distroless base image.\
            n\n Digest updated -> possible security fixes available.\
            n\n Actions recommended:\n- Run pipeline\
            n - Verify Trivy results\n- Check if CVEs 
            can be removed from .trivyignore"

Les Déclencheurs (Triggers)

workflow_dispatch: Permet de lancer le scan manuellement depuis l'interface GitHub

schedule: S'exécute automatiquement tous les jours à 06h00 UTC via une tâche cron


Permissions et Environnement
contents: read est requis pour checkout
pull-requests: write permets d'interagir avec les PR

Runner: S'exécute sur une machine virtuelle Ubuntu 24.04


Les étapes clés du Job (Steps)
CI de base:
Checkout code

- name: Setup git
Configure un utilisateur Git temporaire, afin d'effectuer les commits automatisés

- name: install crane
d'abords en curl, ensuite depuis le script bash

- name: Run digest check
Exécute le script qui détectera la mise à jour

Le script compare le "digest" de l'image actuelle avec la dernière version en ligne. Le résultat est stocké dans une variable

- name: Create branch with updated digest

Si une mise à jour est détectée:
  • Il crée une nouvelle branche avec un timestamp
  • Il mets à jour le fichier contenant le digest
  • Il effectue un commit et push la branche sur le dépôt distant
  • Il vérifie via la CLI si une PR "distroless" n'est pas déjà ouverte (évite les doublons)

- name: Create PR
Utilise la CLI GitHub et le jeton secret GH_PAT pour créer la PR

Le message de la PR inclut des recommandations de sécurité :
  • Relancer la pipeline
  • Vérifier les scans de vulnérabilités (Trivy)
  • Nettoyer le fichier .trivyignore si des failles ont été corrigées

Vérification attendues, depuis l'Actions
Watch
succeeded 1 hour ago in 12s
   Run diggest check
  Run chmod +x ci-scripts/check-docker-image-latest.sh
No update: gcr.io/distroless/java17-debian12:latest is unchanged.
Digest: sha256:0210d5b77dfbe851916184405ddfc8297e7bc652029f7a485274cb71ea2462bc

 Create branch with updated digest
 Create PR

Le sha n'ayant pas changé, il ne créera donc pas de nouvelle branche, ni de nouvelle PR


FIX - Étape 4: Modifications des workflows

On commence par tester l'environement [staging] pour voir les logs, être averti de la moindre vulnérabilité, avant de faire tourner le pipeline en production

Retrouver tous les workflows dans le repository


Stratégie adoptée
  • [staging] scan informatif (--exit-code 0)
  • [production] scan bloquant (--exit-code 1)
  • On anticipe les vulnérabilités sans bloquer les environnements
Staging (extrait) - Job concerné: "functional-tests"
1. Installation de "crane"
Pour faire tourner le script "check-docker-image-latest.sh", qui lui ne fait que vérifier. À ajouter après l'étape "Wait for PostgreSQL":
- name: Install crane
  run: |
    curl -sL https://github.com/google/go-containerregistry/releases/latest/download/go-containerregistry_Linux_x86_64.tar.gz \
    | tar -xz crane
    sudo mv crane /usr/local/bin/
2. Pinné la version

→ Une fois la version connue, depuis "releases/latest"
→ On "pin" la version dans le script bash avec:
CRANE_VERSION="v0.21.5"

→ Et ensuite, changer l'url vers
go-containerregistry/releases/download/${CRANE_VERSION}/

3. Modification des workflows pour appeller le script
Remplacer les installations curl par le script d'installation .github/workflows/: ci-staging/pr-watch-distroless/cd-prod
- name: Install crane
  id: install
  run: |
    ci-scripts/install-crane.sh
Astuce GIT Pour éviter d'écrire la permission (chmod) des scripts bash dans tous les runs des workflows, vous pouvez éxecuter la commande:
git update-index --chmod=+x ci-scripts/install-crane.sh
Une fois, avant le commit
UPDATE - 08/05/26: Externalisation du script d'installation
→ à la place de faire appel à "releases/latest", nous utilisons désormais "releases/download/" et nous fixons la version. - install-crane

Les étapes principales:
- name: Check distroless digest
    id: digest
    run: |
      chmod +x ci-scripts/check-docker-image-latest.sh
      ci-scripts/check-docker-image-latest.sh

  - name: Debug FULL outputs
    run: |
      echo "digest_changed = '${{ steps.digest.outputs.digest_changed }}'"
      echo "digest_value   = '${{ steps.digest.outputs.digest_value }}'"

  - name: Build Docker image
  - name: Install Trivy (official repo)
    //...   sudo apt-get install -y trivy

  - name: Scan Docker image (Trivy - conditional)
    run: |
      if [ "${{ steps.digest.outputs.digest_changed }}" = "true" ]; then
        echo "[INFO] New distroless → strict scan"
        trivy image flashcards:staging \
          --severity HIGH,CRITICAL \
          --ignorefile /dev/null \
          --exit-code 0 \
          --format table
      else
        echo "[INFO] Stable distroless → known CVEs list"
        trivy image flashcards:staging \
          --severity HIGH,CRITICAL \
          --ignorefile .trivyignore.yaml \
          --exit-code 0 \
          --format table
      fi

Les étapes du workflow et leurs actions
- name: Check distroless digest
Comparaison du digest de l'image de base avec un précédent digest enregistré
- name: Debug FULL outputs
Confirme que les données ont bien été capturées et sont disponibles pour les étapes suivantes
CI de base:
Build Docker image

Changement dans le pipeline: Steps modifiés:
Install Trivy (official repo) voir

Scan Docker image (Trivy - conditional)

Branchement Logique (if/else du scan):

if: Nouveau Digest Action: Scan strict
else: Digest Stable Action: Scan standard


main (extrait) - Job concerné: "Build, Analyze and Deploy Flashcards App"
steps: 
  - name: Cache Trivy DB
  - name: Install Trivy (official repo)
  - name: Run Trivy scan

  //..après "Build Docker image" 
  - name: Install crane
  - name: Check distroless digest
  - name: Debug FULL outputs

  - name: Trivy Scan Docker Image (new distroless)
    if: steps.digest.outputs.digest_changed == 'true'
    run: |
      trivy image $IMAGE_NAME:$IMAGE_TAG \
        --severity HIGH,CRITICAL \
        --exit-code 1 \
        --ignore-unfixed \
        --ignorefile /dev/null \
        --timeout 10m \
        --cache-dir ~/.cache/trivy \
        --format table \
        --no-progress

  - name: Trivy Scan Docker Image (stable distroless)
    if: steps.digest.outputs.digest_changed == 'false'
    run: |
      trivy image $IMAGE_NAME:$IMAGE_TAG \
        --severity HIGH,CRITICAL \
        --exit-code 1 \
        --ignore-unfixed \
        --ignorefile .trivyignore.yaml \
        --timeout 10m \
        --cache-dir ~/.cache/trivy \
        --format table \
        --no-progress

  - name: Update digest file
    if: success() && steps.digest.outputs.digest_changed == 'true'
    run: echo "${{ steps.digest.outputs.digest_value }}" > 
         ci-scripts/.distroless-java17-debian12.digest
Les étapes du workflow et leurs actions
Voir les installations:
Cache Trivy DB
Install Trivy (official repo)
Run Trivy scan

CI de base:
Build Docker image

Install crane voir l'installation
Check distroless digest
Debug FULL outputs

Branchement Logique (if/else du scan) :
Trivy Scan Docker Image (new distroless)

  Cas A: Nouveau Digest - Action: Scan strict

Trivy Scan Docker Image (stable distroless)

  Cas B: Digest Stable - Action: Scan standard


Update digest file

Mets à jour le digest après la vérification et le status "success" de l'étape précédente " Trivy Scan Docker Image (new distroless)"

La mise à jour de certaines actions CI/CD (dependabot) sont succeptible d'engendrer des modifications dans les workflows, images et/ou le code.
Le repository constitue dès lors, La source de vérité

Conclusion

Cet incident met en évidence une réalité importante en DevSecOps:
La sécurité ne dépend pas uniquement du code applicatif, mais de l'ensemble de la "supply chain" (images, dépendances, outils CI/CD)
La mise en place combinée de Trivy, d'un suivi automatisé du digest et d'une gestion contrôlée des exceptions permet de maintenir un pipeline stable et sécurisé

Bien que certaines des opérations soient encore manuelles (digest à modifier) à ce stade, elles jouent un rôle d'observation pour l'instant. Lorsqu'une mise à jour se présentera, j'adapterai le système pour qu'il soit en concordance avec la stratégie Gitlab Flow (PR vers develop).


UPDATE - 19/05/2026, La "Distroless Watch" - PR-watch-distroless à detecté une mise à jour du Digest (déja pour debian13:nonroot).
Job "Run digest check"
 Run chmod +x ci-scripts/check-docker-image-latest.sh
[INFO] Checking crane availability...
[INFO] Fetching current digest...
[INFO] Current:  sha256:81d09cac6ec47f6a13c61a941557f95079213320f3ddbf9d353de9317669aab5
[INFO] Previous: sha256:b0e67f7fa5649297e655e37b2cd67471d7d38c2f8617790e9d8e8eab78ed6bcb
[INFO] Update detected

Entretemps, à l'étape précédente, j'avais ajouté les étapes du "scan de l'image Docker (Trivy)" en [staging], pour pouvoir tester l'image avant qu'elle n'arrive en production. Ce qui est déja bien, à résolu les vulnérabilités et le système de vérification du digest est lancé tous les jours à 06:00 UTC.

Cependant, l'action de changer le digest restant manuelle, j'avais choisi d'en rester là jusqu'à ce que le digest puisse être mis à jour, ce qui est chose faite à présent, je vais pouvoir automatiser.

Dans le prochain article, je pin l'image du Dockerfile et automatise le système de manière à utiliser (et tester) le nouveau digest. J'allègerai également le workflow de production en supprimant les partie devenue inutiles

Laissez-moi un commentaire

En postant un commentaire anonyme, vous adhérez automatiquement aux conditions d'utilisation du site.