CIS Benchmark Docker : exigences clés pour images conteneurisées conformes

Comment rendre vos images Docker conformes au CIS : durcissement du Dockerfile, provenance/SBOM, scans CVE, runtime durci et checklist opérationnelle pour PrestaShop.

Schéma de flux de données entre des conteneurs Docker et des dispositifs de sécurité avec des écrans affichant du code.

Table des matières :

  1. Périmètre réel du CIS Benchmark Docker : ce que vous sécurisez exactement
  2. Exigences côté image : Dockerfile durci, dépendances minimales, builds reproductibles
  3. Exigences runtime : moindre privilège, isolation kernel, réseau et filesystem
  4. Scanner, prouver et bloquer : rendre la conformité CIS “enforceable” en CI/CD
  5. Cas PrestaShop : rendre une image PHP/Nginx “CIS-friendly” sans tuer la performance
  6. Check-list opérationnelle : exigences clés CIS à valider avant de pousser une image en production

Le CIS Benchmark Docker n’est pas un “label magique” : c’est une grille de contrôle pragmatique pour réduire la surface d’attaque autour de Docker (daemon, hôte, registries, construction et exécution). Si votre objectif est d’avoir des images conteneurisées conformes, vous devez traiter deux couches en même temps : la supply chain (build, dépendances, registry, SBOM) et le runtime (droits, isolation, syscalls, réseau, filesystem). En 2026, ignorer l’une des deux couches revient à “sécuriser une archive tar” sans sécuriser le processus qui la produit ni l’environnement qui l’exécute.

Dans cet article, les exemples sont contextualisés pour Docker Engine 27.x, Docker Compose v2.30+, images Linux (Debian 12 slim), et des stacks e‑commerce typiques (PHP‑FPM/Nginx/Redis/MariaDB). Adaptez si vous êtes en rootless, en Kubernetes managé ou avec des contraintes de plateforme.

Un point important (souvent mal compris en audit) : le CIS Benchmark aide à démontrer une démarche de durcissement et de réduction du risque, mais il ne remplace ni vos obligations réglementaires (ex. RGPD, art. 32 sur la sécurité du traitement), ni vos exigences contractuelles (ex. PCI DSS si vous traitez des paiements). Il sert plutôt de baseline technique vérifiable, utile pour structurer des preuves (rapports, tickets d’écarts, contrôles CI/CD).

Périmètre réel du CIS Benchmark Docker : ce que vous sécurisez exactement

Le CIS Benchmark Docker couvre historiquement surtout la configuration de Docker Engine (dockerd), la séparation des responsabilités (accès au socket Docker, logs, audit), et une série de recommandations d’exécution (pas de --privileged, pas de montages sensibles, etc.). Pour les images, le benchmark n’est pas un guide “Dockerfile style”, mais il impose implicitement des exigences (non‑root, moindre privilège, gestion des secrets, provenance, hygiène des packages) parce que ces points conditionnent votre capacité à respecter les contrôles runtime.

CIS rappelle sur son site que « CIS Benchmarks are consensus-based best practice security configuration guides » (Center for Internet Security, CIS Benchmarks). Le mot important est consensus-based : ce n’est ni un standard légal, ni une preuve de sécurité, ni une garantie de non‑compromission. C’est une baseline. Pour du e‑commerce (PrestaShop, API, ERP), vous aurez toujours des écarts justifiés (ex. observabilité, debug, compatibilité extensions PHP).

Le benchmark est généralement structuré en niveaux (souvent Level 1 = durcissement “raisonnable” avec impact limité, Level 2 = durcissement plus strict, impact fonctionnel possible). En pratique : Level 2 va vite casser des workflows (ex. besoins de CAP_NET_BIND_SERVICE, montages write, sidecars de debug). Le bon pattern : mettre Level 1 en “enforced” dans la CI/CD, et Level 2 en “audit” avec exceptions tracées (tickets, RFC interne, justification de risque).

Pour clarifier le périmètre “réel” côté Docker, pensez en 3 cercles (et c’est souvent là que les audits échouent) :

  • Le daemon/hôte : qui peut parler au socket Docker (/var/run/docker.sock) et avec quels droits ? Le groupe docker donne de facto un accès root sur l’hôte. Un contrôle CIS “green” côté conteneur ne compensera pas un socket exposé à une application.
  • Le registre : qui peut pousser/tirer ? Avez-vous une politique de tags (immuabilité), du contrôle d’accès, et des scans à l’entrée ? Sans ça, vous “validez” une image en CI mais quelqu’un peut pousser :latest après coup.
  • L’image elle-même : peut-elle tourner sous contraintes (non-root, read_only, drop capabilities) sans forcer l’opérateur à tout ouvrir ?

Mini-scenario concret (fréquent en e-commerce) : une équipe ajoute un conteneur “admin tools” dans Compose, monte le socket Docker pour lancer des docker exec depuis un dashboard. Résultat : un SSRF ou une RCE dans ce conteneur devient un contrôle total de l’hôte Docker (et donc de tous les secrets/volumes). Même si vos images “app” sont CIS-friendly, l’architecture globale ne l’est plus.

Exigences côté image : Dockerfile durci, dépendances minimales, builds reproductibles

Pour des images conformes, la première source de non‑conformité, c’est… votre Dockerfile. Docker documente des règles très simples mais systématiquement violées dans les projets legacy. Deux titres de la doc officielle résument 80% du travail : « Use multi-stage builds » et « Don’t install unnecessary packages » (Docker Docs – Best practices for writing Dockerfiles). Multi-stage réduit l’empreinte (outils de build absents à l’exécution), et éviter les packages inutiles réduit la surface CVE + le risque d’outils détournés (curl/wget/ssh inclus “par confort”).

Sur un socle Debian slim, une règle opérationnelle : un apt-get install sans pinning + sans nettoyage = dette de sécurité. Concrètement, vous voulez :

  • apt-get update et apt-get install dans la même couche (sinon cache incohérent),
  • --no-install-recommends,
  • nettoyage (rm -rf /var/lib/apt/lists/*) pour limiter taille et artefacts,
  • idéalement pinning (versions) ou, plus réaliste, mise à jour régulière du base image + scan CVE en CI.

Ajoutez aussi des exigences “hygiène” qui simplifient les audits et les preuves :

  • Labels OCI (provenance, version, commit) : facilitez la traçabilité en cas d’incident.
  • Aucun secret au build : pas de token Git, pas de .npmrc, pas de auth.json. Si vous devez accéder à un registre privé, utilisez des mécanismes de secrets de build (BuildKit) et vérifiez que rien ne fuit dans les layers.
  • Réduction des interpréteurs inutiles : garder bash, python, gcc dans une image runtime crée des surfaces d’escalade. Si votre app n’en a pas besoin, supprimez-les (ou mieux : ne les installez pas).

Autre exigence clé : la reproductibilité et la provenance. En 2026, “je build sur mon laptop” est un anti‑pattern. Visez :

  • base image pinée au digest (FROM debian@sha256:...) pour éviter les surprises,
  • génération d’une SBOM (CycloneDX/SPDX) et scan (Trivy/Grype),
  • signature / attestations de build (cosign, attestations SLSA) si votre contexte le justifie.

Sur PrestaShop, c’est particulièrement critique si vous embarquez des dépendances natives (gd, intl, zip) et des outils Node pour assets : la chaîne d’approvisionnement devient un vrai vecteur. Une bonne pratique “terrain” est de distinguer :

  • image “build” (node, composer, outils, compilateurs),
  • image “runtime” (php-fpm + libs strictement nécessaires),
  • et de n’embarquer dans runtime que des artefacts construits (vendor, assets) sans l’écosystème complet.

Exemple minimaliste (PHP‑FPM “production”) :

# syntax=docker/dockerfile:1.7
FROM php:8.3-fpm-bookworm AS runtime

# 1) Dépendances strictement nécessaires
RUN apt-get update \
 && apt-get install -y --no-install-recommends \
    libicu72 libzip4 libpng16-16 libjpeg62-turbo libfreetype6 \
 && rm -rf /var/lib/apt/lists/*

# 2) User non-root (évite les conteneurs root par défaut)
RUN useradd -u 10001 -m -s /usr/sbin/nologin app

# 3) Répertoires write explicitement (sinon vous finissez en chmod 777)
RUN mkdir -p /var/www/html/var /var/www/html/img \
 && chown -R app:app /var/www/html

USER app
WORKDIR /var/www/html

# 4) Healthcheck applicatif (à adapter)
HEALTHCHECK --interval=30s --timeout=3s CMD php -v || exit 1

Ce Dockerfile ne “fait” pas PrestaShop (pas de code, pas de modules, pas de conf PHP), mais il illustre les points CIS qui reviennent toujours : pas d’outils inutiles, pas de root, permissions maîtrisées, healthcheck.

Pour aller au-delà du “minimum”, une checklist rapide côté image (utile en revue de merge request) :

  • L’image a-t-elle un USER non-root effectif (et pas seulement un user créé mais jamais utilisé) ?
  • Les répertoires qui doivent être RW sont-ils explicitement listés, et les autres implicitement RO ?
  • Avez-vous une politique claire sur les tags (ex. pas de latest en prod ; tags immuables) ?
  • Les logs applicatifs sont-ils envoyés sur stdout/stderr (évite d’ouvrir des chemins RW juste “pour les logs”) ?

Enfin, si vous cherchez une référence complémentaire (plus “guide” que benchmark), NIST SP 800-190 reste une lecture utile pour cadrer les risques “images vs registries vs orchestrateur” : NIST SP 800-190

Exigences runtime : moindre privilège, isolation kernel, réseau et filesystem

CIS insiste (directement ou indirectement) sur une règle : un conteneur ne doit jamais obtenir plus de privilèges que nécessaire. Dans Docker, ça se traduit par l’interdiction de --privileged, la réduction des capabilities Linux, l’usage de profils seccomp/AppArmor/SELinux, et l’activation de garde‑fous comme no-new-privileges. Sur le papier c’est “simple”, en réalité le cœur de Docker laisse trop de latitude et les équipes “ouvrent tout” pour dépanner.

Pour des images conformes, vous devez donc les rendre compatibles avec un runtime durci. Exemple typique : une image qui écrit partout dans /var/www/html va forcer l’opérateur à monter le code en RW ou à mettre des permissions laxistes. À l’inverse, une image qui n’écrit que dans 2-3 emplacements permet un filesystem root en lecture seule et des montages ciblés. Le bon compromis pour PrestaShop :

  • code applicatif en RO,
  • var/, img/ (et éventuellement download/) en volumes RW,
  • tmpfs pour /tmp si possible.

Côté Compose, les options qui font une différence immédiate (sans Kubernetes) :

services:
  php:
    image: monregistry/prestashop-php:9.2
    read_only: true
    tmpfs:
      - /tmp:rw,noexec,nosuid,size=128m
    security_opt:
      - no-new-privileges:true
    cap_drop:
      - ALL
    # Ajouter uniquement si nécessaire (ex: bind port <1024 côté Nginx)
    # cap_add:
    #   - NET_BIND_SERVICE
    volumes:
      - prestashop_var:/var/www/html/var
      - prestashop_img:/var/www/html/img

Attention : read_only: true révèle vite les écritures implicites (sessions, cache, logs). Ce n’est pas un défaut : c’est un test de qualité. Concrètement, si votre PHP écrit ses sessions sur disque par défaut, vous avez trois options “CIS-friendly” :

  • basculer les sessions vers Redis,
  • monter un volume dédié (limité) pour les sessions,
  • ou utiliser /tmp en tmpfs si c’est acceptable (en assumant la volatilité).

Même logique pour Nginx : un conteneur Nginx “propre” doit pouvoir fonctionner sans écrire dans le filesystem root (pid, cache, temp). Si vous voyez que Nginx exige des écritures dans /var/cache/nginx ou /var/run, mappez des emplacements ciblés ou basculez vers tmpfs.

Table de correspondance rapide (utile pour cadrer une revue CIS runtime) :

Objectif de durcissement Risque réduit Option Docker/Compose typique Impact probable
User non-root Escalade “facile” après RCE USER app (Dockerfile), user: (Compose) Permissions/paths à ajuster
Pas de nouveaux privilèges Contournement setuid / exec security_opt: [no-new-privileges:true] Généralement faible
Drop capabilities Abus de privilèges kernel cap_drop: [ALL] + cap_add minimal Peut casser des cas rares
Root FS en lecture seule Persistences / modifications inattendues read_only: true + volumes ciblés Révèle dettes d’écriture
Temp en mémoire Exécution/écriture dans /tmp tmpfs: /tmp:rw,noexec,nosuid Peut casser certains installateurs
Segmentation réseau Mouvement latéral entre services réseaux séparés, pas d’exposition DB Nécessite design réseau

Pour orchestrer proprement en prod, vous pouvez vous appuyer sur les principes abordés dans Docker Compose : orchestrer des services multi-conteneurs en production et, si vous migrez vers un cluster, sur Migration PrestaShop vers Kubernetes : checklist GitOps et pièges courants.

Scanner, prouver et bloquer : rendre la conformité CIS “enforceable” en CI/CD

Une conformité CIS “déclarative” ne vaut rien sans automatisation. L’outil historique est Docker Bench for Security (script de checks alignés sur CIS). Il est utile pour auditer des hôtes Docker (daemon + runtime), mais insuffisant seul pour valider une image : il ne remplacera ni un scanner CVE, ni une policy sur le Dockerfile, ni un contrôle de secrets.

Le pipeline CI/CD typique “images conformes” en 2026 ressemble à ça : 1) lint Dockerfile (Hadolint), 2) build reproductible (BuildKit), 3) scan vulnérabilités OS + libs (Trivy ou Grype), 4) génération SBOM (Syft), 5) policy-as-code (OPA Conftest) sur metadata, labels, user, ports, etc., 6) push registry, signature (cosign) et attestation.

Ce workflow se recolle très bien aux pratiques de durcissement supply chain détaillées dans PrestaShop : sécuriser APIs et modules avec SBOM, WAF et CI/CD.

Exemple (pseudo) étape CI, à adapter à GitLab/GitHub :

# Build
DOCKER_BUILDKIT=1 docker build -t app:${CI_COMMIT_SHA} .

# Refuser si l'image tourne en root
test "$(docker image inspect app:${CI_COMMIT_SHA} --format '{{.Config.User}}')" != "" 

# Scan CVE (bloquant sur HIGH/CRITICAL)
trivy image --severity HIGH,CRITICAL --exit-code 1 app:${CI_COMMIT_SHA}

# SBOM
syft app:${CI_COMMIT_SHA} -o cyclonedx-json > sbom.json

Le point à ne pas rater : définir des seuils. “0 CVE” est irréaliste dès que vous embarquez glibc/openssl. Fixez plutôt :

  • blocage sur CRITICAL non corrigées,
  • blocage sur secrets détectés,
  • blocage sur absence de SBOM,
  • blocage sur user root.

Ensuite, vous améliorez par itération (exception list expirante, SLA de remédiation). Pour l’audit runtime (daemon), exécutez docker-bench-security sur les nœuds, et exportez le résultat vers votre SIEM / stack logs.

Deux compléments pratiques qui rendent la conformité opérable (et pas seulement “bloquante”) :

  • Conserver les artefacts de preuve : rapport Trivy/Grype, SBOM, digest image, et idéalement la commande de build (ou attestation). En audit, vous gagnerez du temps si vous pouvez prouver “ce qui a été déployé” à une date donnée.
  • Gérer les exceptions comme du code : une exception CVE (ex. vulnérabilité dans une lib transitive) devrait avoir une justification, une date d’expiration, et un propriétaire. Sans expiration, l’exception devient permanente.

Si vous utilisez OPA/Conftest, une règle simple et très efficace est d’empêcher des images “à risques” avant même le scan CVE (gain de temps) : refuser les images sans USER, refuser les tags flottants, refuser l’absence de labels de traçabilité (commit, repo).

Cas PrestaShop : rendre une image PHP/Nginx “CIS-friendly” sans tuer la performance

PrestaShop a deux contraintes qui entrent frontalement en collision avec le durcissement : les écritures disque (cache, images, logs) et la performance (OPcache, cache applicatif, sessions). Si vous bloquez l’écriture “par principe”, vous allez soit casser la boutique, soit réintroduire des RW globaux. La bonne approche est de rendre explicites les zones write, puis d’optimiser le cache “hors conteneur” (Redis/Varnish/CDN) pour limiter les écritures et le CPU.

Mini-scenario réaliste : un module PrestaShop écrit des fichiers temporaires dans un chemin non standard (ex. sous le répertoire du module). En conteneur durci (read_only), le module casse et l’équipe “résout” en passant tout /var/www/html en RW. C’est exactement l’effet inverse recherché. La démarche la plus propre est : 1) identifier le chemin exact (logs applicatifs, strace en staging si besoin), 2) corriger le module ou reconfigurer le chemin vers var/ ou img/, 3) monter uniquement ce chemin en RW si correction impossible (et documenter l’écart).

Sur le plan perf, l’erreur classique est de confondre conformité et ralentissement. En réalité, une image plus petite, avec moins de packages, se déploie plus vite et réduit la variabilité. Pour la partie cache, vous avez intérêt à externaliser au maximum : OPcache correctement dimensionné, Redis pour sessions/cache, reverse-proxy si besoin. Pour une stratégie complète et actuelle, voir Cache PrestaShop : stratégie 2026 OPcache, Redis, Varnish et CDN (et, si vous mettez un CDN devant, CDN 2026 : critères techniques pour choisir le meilleur fournisseur).

Un autre point concret “CIS-friendly” côté Nginx (souvent négligé) : évitez d’exposer des endpoints de statut non protégés (ex. /nginx_status) et isolez-les sur un réseau interne (ou par ACL IP) si vous en avez besoin pour l’observabilité. Beaucoup d’équipes ouvrent ces endpoints “temporairement” puis les laissent en prod.

Côté sécurité “pure”, le point le plus fragile dans une stack PrestaShop conteneurisée reste souvent la base de données (privilèges, TLS, exposition réseau). Une image Docker “conforme CIS” n’empêche pas une fuite si votre MariaDB est exposée en clair ou si l’utilisateur SQL est trop permissif. Recoupez avec Sécurité MySQL : pare-feu, TLS, privilèges minimaux et audit. Et pour tester l’expo web côté applicatif, vous pouvez compléter par du scan boîte noire (staging) via Wapiti : scanner open source de vulnérabilites web en boîte noire.

Enfin, si vous devez produire des environnements de test proches de la prod, ne sacrifiez pas la conformité en staging. Un staging “root, 777, debug tools” finit régulièrement promu en prod par accident. Basez-vous sur un setup contrôlé et reproductible, comme décrit dans Staging PrestaShop : créer un environnement Docker fidèle à la production.

Check-list opérationnelle : exigences clés CIS à valider avant de pousser une image en production

Sur la partie image (build), la check-list minimale pour viser une conformité CIS “pragmatique” : base image maintenue (et trackée), multi-stage quand il y a compilation, pas de secrets dans l’image (ni ENV, ni .npmrc, ni auth.json), génération SBOM, scan CVE bloquant, utilisateur non-root, et répertoires write explicitement gérés (pas de chmod -R 777). Si vous avez des modules PrestaShop qui écrivent dans des chemins non standard, corrigez-les : c’est un vrai signal de dette technique.

Sur la partie runtime (exécution), les contrôles qui comptent : pas de conteneur privilégié, pas de montages sensibles (/var/run/docker.sock, /proc, /sys) sauf exception documentée, no-new-privileges, cap_drop: [ALL] + capabilities minimales, filesystem root en RO si possible, réseau segmenté (au moins séparation front/back/db), limits CPU/RAM pour éviter le DoS interne. Pour la dimension “hôte Docker”, ne faites pas l’impasse sur l’audit CIS daemon (dockerd), sinon votre “image conforme” tourne sur un moteur permissif.

Pour rendre cette check-list utilisable en pratique (revue avant release), voici une version “contrôle final” en 2 minutes :

  • Traçabilité
  • Image pinée par digest en prod (ou tag immuable) ; digest documenté dans la release.
  • Labels (version, commit, repo) présents.
  • Build
  • SBOM générée et archivée.
  • Scan CVE exécuté ; exceptions justifiées et expirables.
  • Aucun secret détecté (scan secrets + revue Dockerfile).
  • Exécution
  • Non-root effectif ; pas de --privileged.
  • no-new-privileges activé ; cap_drop: ALL (puis cap_add au cas par cas).
  • Root filesystem RO + volumes RW explicitement limités.
  • Services non nécessaires non exposés ; DB non publiée sur l’interface publique.
  • Hôte/plateforme
  • docker-bench-security exécuté sur les nœuds ; écarts Level 1 traités en priorité.
  • Accès au socket Docker contrôlé (pas de montage du socket dans des services applicatifs).

Si vous devez vous aligner sur des guides plus stricts (secteur régulé, exigences militaires), le CIS Benchmark est souvent un point de départ, pas un point d’arrivée. Le guide DISA pour images conteneurisées est plus prescriptif sur certains contrôles : DISA : sécuriser les images conteneurisées selon le guide officiel. Dans tous les cas, documentez vos écarts (risk acceptance) et traitez-les comme du code : versionnés, révisés, expirables.

En bref : viser le CIS Benchmark Docker pour vos images conteneurisées conformes, ce n’est pas cocher une liste à la main. C’est déplacer la conformité dans le build, rendre l’image compatible avec un runtime durci, et automatiser la preuve (reports, artefacts, SBOM, scans) dans votre CI/CD. Sans ça, vous aurez au mieux une conformité de façade, au pire un faux sentiment de sécurité.


À lire aussi