Sécuriser les images Docker avec Dockle
Mise à jour :
La sécurité des conteneurs Docker est devenue un enjeu majeur pour les administrateurs systèmes et les équipes DevOps. Les images non sécurisées peuvent introduire des vulnérabilités critiques dans vos environnements de production. C’est là qu’intervient Dockle, un outil open-source qui m’aide à auditer facilement mes images Docker. Il vérifie leur conformité avec les bonnes pratiques de sécurité, comme celles définies dans les benchmarks CIS Docker. À mon avis, c’est un allié indispensable pour toute démarche DevSecOps.
Qu’est-ce que Dockle ?
Dockle est un outil en ligne de commande spécialement conçu pour analyser la
sécurité des images Docker. À la différence des scanners de vulnérabilités
classiques comme Trivy, Dockle ne
s’intéresse pas uniquement aux paquets logiciels vulnérables. Il se concentre
surtout sur des points de configuration souvent négligés, mais critiques. Par
exemple : est-ce que l’image utilise un compte utilisateur non privilégié ?
Est-ce qu’elle a une instruction HEALTHCHECK
? Y a-t-il des fichiers sensibles
laissés dans l’image finale ?
Voici quelques exemples de contrôles que Dockle peut effectuer :
- Présence d’un utilisateur root par défaut
- Absence d’un fichier
.dockerignore
- Utilisation de la commande
latest
dans lesFROM
- Manque d’un HEALTHCHECK
- Permissions trop larges sur certains fichiers
Dockle se base principalement sur les recommandations du CIS Docker Benchmark ↗, ce qui me permet de rester aligné avec des standards reconnus. Son gros avantage ? Il est simple à utiliser, rapide, et idéal pour une intégration dans un pipeline CI/CD.
Installation de Dockle
Installer Dockle est simple, quelle que soit votre plateforme. Je vais vous montrer comment je procède selon le système d’exploitation utilisé. L’outil est disponible sous forme de binaire, ce qui facilite son intégration dans un environnement automatisé.
Sur macOS
Sur mon Mac, j’utilise Homebrew, c’est le plus rapide :
brew install goodwithtech/r/dockle
Homebrew va automatiquement récupérer et installer la dernière version stable de Dockle.
Sur Linux
Pour les distributions Debian ou Ubuntu :
VERSION=$( curl --silent "https://api.github.com/repos/goodwithtech/dockle/releases/latest" | \ grep '"tag_name":' | \ sed -E 's/.*"v([^"]+)".*/\1/')curl -L -o dockle.deb https://github.com/goodwithtech/dockle/releases/download/v${VERSION}/dockle_${VERSION}_Linux-64bit.debsudo dpkg -i dockle.deb && rm dockle.deb
Et pour CentOS, RHEL ou autres distributions RPM-based :
VERSION=$( curl --silent "https://api.github.com/repos/goodwithtech/dockle/releases/latest" | \ grep '"tag_name":' | \ sed -E 's/.*"v([^"]+)".*/\1/')sudo rpm -ivh https://github.com/goodwithtech/dockle/releases/download/v${VERSION}/dockle_${VERSION}_Linux-64bit.rpm
Pour toutes les distributions, vous pouvez suivre les dernières versions ici : github.com/goodwithtech/dockle/releases ↗
Controle de la version
Une fois installé, je vérifie la version de Dockle pour m’assurer que tout est fonctionnel :
dockle --versiondockle version 0.4.15
Utilisation de base de Dockle
Une fois Dockle installé, je peux immédiatement commencer à analyser mes images Docker. L’utilisation est très simple : une seule commande suffit pour lancer un audit.
Lancer une analyse
Pour analyser une image, j’utilise cette commande :
dockle NOM_DE_L_IMAGE
Par exemple, pour analyser l’image officielle de Nginx :
dockle nginx:latest
Dockle va alors inspecter l’image et produire un rapport directement dans le terminal. Ce rapport est structuré par types de bonnes pratiques et classé par niveau de gravité :
- FATAL : erreurs critiques qui nécessitent une action immédiate
- WARN : configurations risquées ou non recommandées
- INFO : informations utiles mais non bloquantes
- PASS : contrôles réussis
Exemples de résultats
Voici le rendu actuelle de Dockle sur l’image Nginx :
FATAL - CIS-DI-0010: Do not store credential in environment variables/files * Suspicious ENV key found : NGINX_GPGKEYS on RUN /bin/sh -c set -x && groupadd --system --gid 101 nginx && useradd --system --gid nginx --no-create-home --home /nonexistent --comment "nginx user" --shell /bin/false --uid 101 nginx && apt-get update && apt-get install --no-install-recommends --no-install-suggests -y gnupg1 ca-certificates && NGINX_GPGKEYS="573BFD6B3D8FBC641079A6ABABF5BD827BD9BF62 8540A6F18833A80E9C1653A42FD21310B49F6B46 9E9BE90EACBCDE69FE9B204CBCDCD8A38D88A2B3"; NGINX_GPGKEY_PATH=/etc/apt/keyrings/nginx-archive-keyring.gpg; export GNUPGHOME="$(mktemp -d)"; found=''; for NGINX_GPGKEY in $NGINX_GPGKEYS; do for server in hkp://keyserver.ubuntu.com:80 pgp.mit.edu ; do echo "Fetching GPG key $NGINX_GPGKEY from $server"; gpg1 --keyserver "$server" --keyserver-options timeout=10 --recv-keys "$NGINX_GPGKEY" && found=yes && break; done; test -z "$found" && echo >&2 "error: failed to fetch GPG key $NGINX_GPGKEY" && exit 1; done; gpg1 --export "$NGINX_GPGKEYS" > "$NGINX_GPGKEY_PATH" ; rm -rf "$GNUPGHOME"; apt-get remove --purge --auto-remove -y gnupg1 && rm -rf /var/lib/apt/lists/* && dpkgArch="$(dpkg --print-architecture)" && nginxPackages=" nginx=${NGINX_VERSION}-${PKG_RELEASE} nginx-module-xslt=${NGINX_VERSION}-${DYNPKG_RELEASE} nginx-module-geoip=${NGINX_VERSION}-${DYNPKG_RELEASE} nginx-module-image-filter=${NGINX_VERSION}-${DYNPKG_RELEASE} nginx-module-njs=${NGINX_VERSION}+${NJS_VERSION}-${NJS_RELEASE} " && case "$dpkgArch" in amd64|arm64) echo "deb [signed-by=$NGINX_GPGKEY_PATH] https://nginx.org/packages/mainline/debian/ bookworm nginx" >> /etc/apt/sources.list.d/nginx.list && apt-get update ;; *) tempDir="$(mktemp -d)" && chmod 777 "$tempDir" && savedAptMark="$(apt-mark showmanual)" && apt-get update && apt-get install --no-install-recommends --no-install-suggests -y curl devscripts equivs git libxml2-utils lsb-release xsltproc && ( cd "$tempDir" && REVISION="${NGINX_VERSION}-${PKG_RELEASE}" && REVISION=${REVISION%~*} && curl -f -L -O https://github.com/nginx/pkg-oss/archive/${REVISION}.tar.gz && PKGOSSCHECKSUM="973690e64fa47e3704e817a3b08205b9e3f8c0cbe31825d9d62a81c11eb3aa186df015f27fdfd48c8799ffc528e38a9168c592ae665e4835c2d28638ec5f7845 *${REVISION}.tar.gz" && if [ "$(openssl sha512 -r ${REVISION}.tar.gz)" = "$PKGOSSCHECKSUM" ]; then echo "pkg-oss tarball checksum verification succeeded!"; else echo "pkg-oss tarball checksum verification failed!"; exit 1; fi && tar xzvf ${REVISION}.tar.gz && cd pkg-oss-${REVISION} && cd debian && for target in base module-geoip module-image-filter module-njs module-xslt; do make rules-$target; mk-build-deps --install --tool="apt-get -o Debug::pkgProblemResolver=yes --no-install-recommends --yes" debuild-$target/nginx-$NGINX_VERSION/debian/control; done && make base module-geoip module-image-filter module-njs module-xslt ) && apt-mark showmanual | xargs apt-mark auto > /dev/null && { [ -z "$savedAptMark" ] || apt-mark manual $savedAptMark; } && ls -lAFh "$tempDir" && ( cd "$tempDir" && dpkg-scanpackages . > Packages ) && grep '^Package: ' "$tempDir/Packages" && echo "deb [ trusted=yes ] file://$tempDir ./" > /etc/apt/sources.list.d/temp.list && apt-get -o Acquire::GzipIndexes=false update ;; esac && apt-get install --no-install-recommends --no-install-suggests -y $nginxPackages gettext-base curl && apt-get remove --purge --auto-remove -y && rm -rf /var/lib/apt/lists/* /etc/apt/sources.list.d/nginx.list && if [ -n "$tempDir" ]; then apt-get purge -y --auto-remove && rm -rf "$tempDir" /etc/apt/sources.list.d/temp.list; fi && ln -sf /dev/stdout /var/log/nginx/access.log && ln -sf /dev/stderr /var/log/nginx/error.log && mkdir /docker-entrypoint.d # buildkit (You can suppress it with --accept-key)WARN - CIS-DI-0001: Create a user for the container * Last user should not be rootWARN - DKL-DI-0006: Avoid latest tag * Avoid 'latest' tagINFO - CIS-DI-0005: Enable Content trust for Docker * export DOCKER_CONTENT_TRUST=1 before docker pull/buildINFO - CIS-DI-0006: Add HEALTHCHECK instruction to the container image * not found HEALTHCHECK statementINFO - CIS-DI-0008: Confirm safety of setuid/setgid files * setuid file: urwxr-xr-x usr/bin/passwd * setuid file: urwxr-xr-x usr/bin/su * setuid file: urwxr-xr-x usr/bin/umount * setuid file: urwxr-xr-x usr/bin/chfn * setgid file: grwxr-xr-x usr/bin/expiry * setuid file: urwxr-xr-x usr/bin/newgrp * setuid file: urwxr-xr-x usr/bin/gpasswd * setgid file: grwxr-xr-x usr/sbin/unix_chkpwd * setgid file: grwxr-xr-x usr/bin/chage * setuid file: urwxr-xr-x usr/bin/chsh * setuid file: urwxr-xr-x usr/bin/mount
Ce qui m’aide beaucoup, c’est que Dockle affiche les recommandations détaillées pour chaque point non conforme. Je peux donc adapter mon Dockerfile rapidement.
Les options utiles de Dockle
Lorsque j’utilise Dockle au quotidien, certaines options me permettent d’adapter l’analyse à mes besoins, surtout en environnement CI/CD. Ces options sont précieuses pour personnaliser le comportement de l’outil et affiner les rapports de sécurité.
Voici celles que j’utilise le plus souvent :
--exit-code
Cette option permet de définir le code de sortie de Dockle si des
problèmes sont détectés. Par défaut, Dockle retourne toujours 0
, ce qui
n’est pas idéal en automatisation.
Par exemple :
dockle --exit-code 1 nginx:latest
Cette commande retournera 1
si une alerte de type WARN
ou FATAL
est
présente. Idéal pour bloquer un pipeline CI/CD en cas de configuration non
sécurisée.
--format
Elle me permet de changer le format de sortie, ce qui est utile si je veux exploiter les résultats dans un outil externe (parsing, alerting, etc.).
raw
(par défaut)json
Exemple en JSON :
dockle --format json nginx:latest
J’intègre souvent ce format dans mes scripts d’analyse post-traitement.
--ignore
Il arrive parfois que certaines règles ne soient pas pertinentes pour mon contexte. Grâce à cette option, je peux ignorer des règles spécifiques :
dockle --ignore CIS-DI-0001,CIS-DI-0006 nginx:latest
Attention à ne pas abuser de cette option. À mon avis, ignorer une règle doit toujours être justifié.
--input-type
Par défaut, Dockle analyse les images locales. Mais je peux aussi lui dire d’utiliser une image à distance, par exemple depuis un registre :
dockle --input-type=registry mon-registre.io/mon-image:tag
--no-color
Si je redirige la sortie dans un fichier ou un autre outil, je désactive les couleurs pour un rendu plus lisible :
dockle --no-color nginx:latest
--timeout
Par défaut, Dockle utilise un timeout de 30 secondes. Pour les grosses images ou les réseaux lents, je peux ajuster cette valeur :
dockle --timeout 60 nginx:latest
Intégration de Dockle dans un pipeline CI/CD
À mon avis, l’un des plus grands avantages de Dockle, c’est sa facilité d’intégration dans un pipeline CI/CD. C’est ce que je recommande toujours : automatiser les vérifications de sécurité dès la construction de l’image, pour éviter les mauvaises surprises plus tard.
Pourquoi intégrer Dockle dans un pipeline ?
L’objectif est simple : empêcher la mise en production d’images non conformes. Grâce à Dockle, je peux valider automatiquement qu’une image respecte les bonnes pratiques avant son déploiement.
Les bénéfices sont clairs :
- Détection précoce des failles de configuration
- Automatisation du contrôle qualité
- Blocage conditionnel si des règles critiques échouent
Exemple avec GitLab CI
Voici un exemple de job dans un fichier .gitlab-ci.yml
:
dockle_scan: stage: test image: docker:latest services: - docker:dind before_script: - apk add --no-cache curl - VERSION=$(curl --silent "https://api.github.com/repos/goodwithtech/dockle/releases/latest" | grep '"tag_name":' | sed -E 's/.*"v([^"]+)".*/\1/') - curl -L -o dockle https://github.com/goodwithtech/dockle/releases/download/v${VERSION}/dockle_${VERSION}_Linux-64bit - chmod +x dockle script: - docker build -t mon-image . - ./dockle --exit-code 1 mon-image
Avec l’option --exit-code 1
, le job échoue si une alerte de type WARN ou
FATAL est détectée. Cela me permet de forcer les correctifs avant de passer
à l’étape suivante.
Le mieux est de construire sa propre image contenant Dockle, pour éviter de télécharger le binaire à chaque fois.
Exemple avec GitHub Actions
Et voici un exemple équivalent pour GitHub Actions :
name: CI Dockle
on: [push]
jobs: dockle: runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v2
- name: Build Docker image run: docker build -t mon-image .
- name: Install Dockle run: | VERSION=$(curl --silent "https://api.github.com/repos/goodwithtech/dockle/releases/latest" | grep '"tag_name":' | sed -E 's/.*"v([^"]+)".*/\1/') curl -L -o dockle https://github.com/goodwithtech/dockle/releases/download/v${VERSION}/dockle_${VERSION}_Linux-64bit chmod +x dockle
- name: Run Dockle run: ./dockle --exit-code 1 mon-image
Bonnes pratiques
Je préfère toujours :
- Exécuter Dockle juste après le build
- Échouer le job sur les erreurs critiques
- Personnaliser la politique de sécurité avec
--ignore
si nécessaire
Intégré dans mon CI/CD, Dockle devient un gardien automatique de mes standards de sécurité. Je peux livrer plus sereinement, tout en respectant les exigences de conformité.
Conclusion
Dockle est, à mon avis, un outil à adopter sans hésitation pour toute personne soucieuse de la sécurité de ses images Docker. Simple à installer, facile à intégrer dans un pipeline CI/CD, il m’offre une analyse fine des configurations souvent négligées. C’est un excellent moyen d’automatiser la détection des erreurs courantes et de garantir un niveau de sécurité minimum avant mise en production.
Mais pour aller encore plus loin, je recommande vivement de le combiner à deux autres outils :
- Hadolint pour analyser la qualité et
les bonnes pratiques de vos
Dockerfile
. - Trivy pour détecter les vulnérabilités dans les paquets logiciels et dépendances embarquées dans les images.
À eux trois, Dockle, Hadolint et Trivy me permettent de construire des images à la fois propres, optimisées et sécurisées. Ce trio est, selon moi, le meilleur combo pour un workflow Docker robuste et conforme aux exigences modernes de la sécurité DevOps.