DOCKER · SÉCURITÉ · DEVOPS
Conteneurs non-root, hardening du Dockerfile, scan de vulnerabilites avec Trivy : un guide operationnel pour renforcer la securite de vos images Docker en production.
📋 Au programme
Pourquoi securiser ses images Docker
Docker simplifie le deploiement, mais une image mal configuree est une porte ouverte pour les attaquants. Selon les rapports de securite, les conteneurs compromises representent une part croissante des incidents : images de base obsoletes, processus root a l’interieur du conteneur, secrets en clair dans le Dockerfile…
La docker container security repose sur trois piliers :
- Moindre privilege : ne jamais executer en root quand c’est evitable
- Surface d’attaque reduite : minimiser les packages, utiliser des images de base legeres
- Detection continue : scanner regulierement les vulnerabilites
A retenir
La securite des conteneurs ne remplace pas celle de l’hote. Un noyau Linux a jour, un firewall actif et une segmentation reseau restent indispensables.
Si vous debutez avec la construction d’images securisees, notre article sur la creation d’une image Docker PHP 8.4 non-root avec Composer et Node 24 est un excellent point de depart concret.
Executer un conteneur en utilisateur non-root
Par defaut, Docker execute les processus en tant que root a l’interieur du conteneur. Si un attaquant reussit a exploiter une vulnerabilite dans votre application, il obtient les privileges root dans le conteneur, ce qui augmente considerablement l’impact de l’attaque.
Creer un utilisateur dedie dans le Dockerfile
FROM python:3.12-slim AS base
# Installer les dependances systeme
RUN apt-get update \
&& apt-get install -y --no-install-recommends curl \
&& rm -rf /var/lib/apt/lists/*
# Creer un utilisateur non-root
RUN groupadd -r appuser && useradd -r -g appuser -d /app -s /sbin/nologin appuser
# Creer le repertoire et donner les droits
WORKDIR /app
COPY --chown=appuser:appuser requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY --chown=appuser:appuser . .
# Basculer vers l'utilisateur non-root
USER appuser
EXPOSE 8000
CMD ["gunicorn", "--bind", "0.0.0.0:8000", "app:app"]
Forcer le non-root dans Docker Compose
services:
web:
image: mon-app:latest
user: "1000:1000"
read_only: true
tmpfs:
- /tmp
- /run
cap_drop:
- ALL
security_opt:
- no-new-privileges:true
Astuce
Utilisez user: "1000:1000" dans docker-compose.yml pour forcer un utilisateur non-root meme si l’image ne le specifie pas. C’est un filet de securite supplementaire.
Hardening du Dockerfile : les bonnes pratiques
Les dockerfile best practices vont au-dela du simple USER non-root. Voici les regles essentielles pour un Dockerfile securise.
Utiliser une image de base officielle et legeres
Privilegiez les variantes -slim ou -alpine des images officielles. Moins de packages signifie moins de vulnerabilites.
# Bien : image slim
FROM node:22-slim
# Mieux si compatible : image alpine
FROM node:22-alpine
# A eviter : image complete avec outils inutiles
FROM node:22
Piner les versions des images de base
# Eviter : tag flottant, la version change
FROM python:3.12
# Preferer : version pinee
FROM python:3.12.3-slim
Attention
Les tags flottants comme latest ou python:3.12 peuvent pointer vers une image differente a chaque build. Cela rend les deploiements non reproductibles et peut introduire des vulnerabilites inattendues.
Multi-stage build pour reduire la surface d’attaque
# Stage 1 : build
FROM node:22-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
RUN npm run build
# Stage 2 : production (image finale minimaliste)
FROM node:22-alpine AS production
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
WORKDIR /app
COPY --from=builder --chown=appuser:appgroup /app/dist ./dist
COPY --from=builder --chown=appuser:appgroup /app/node_modules ./node_modules
COPY --from=builder --chown=appuser:appgroup /app/package.json ./
USER appuser
EXPOSE 3000
CMD ["node", "dist/server.js"]
Avec le multi-stage build, l’image finale ne contient ni les outils de compilation, ni les sources, ni les devDependencies. Seul le necessaire pour l’execution est present.
Ne jamais copier de secrets dans l’image
# INTERDIT : secrets en dur dans le Dockerfile
ENV DB_PASSWORD="monMotDePasse123"
COPY .env /app/.env
# CORRECT : passer les secrets via l'environnement ou Docker secrets
# docker-compose.yml :
# environment:
# - DB_PASSWORD_FILE=/run/secrets/db_password
Critique
Un secret dans un Dockerfile reste dans l’image, meme si vous le supprimez dans un layer ulterieur. Les layers Docker sont immuables. Utilisez --secret de BuildKit ou les Docker secrets pour injecter les donnees sensibles au runtime.
Ajouter un HEALTHCHECK
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
CMD curl -f http://localhost:8000/health || exit 1
Docker Compose : configuration securisee
Au-dela du Dockerfile, la configuration de l’orchestration joue un role cle dans la docker security. Voici un template docker-compose.yml securise :
services:
app:
image: mon-app:1.0.0
user: "1000:1000"
read_only: true
tmpfs:
- /tmp:noexec,nosuid,size=100m
cap_drop:
- ALL
cap_add:
- NET_BIND_SERVICE
security_opt:
- no-new-privileges:true
- seccomp:seccomp-profile.json
ports:
- "127.0.0.1:8000:8000"
environment:
- NODE_ENV=production
deploy:
resources:
limits:
memory: 512M
cpus: "1.0"
reservations:
memory: 128M
Explication des options :
read_only: true: le filesystem est en lecture seule, empeche toute ecriture non autoriseecap_drop: ALL: supprime toutes les capabilities Linux,cap_addn’ajoute que le strict necessaireno-new-privileges:true: empeche l’escalade de privileges via setuid127.0.0.1:8000:8000: n’ecoute que sur localhost, utilise un reverse proxy (Nginx/Traefik) pour l’exposition publiquedeploy.resources.limits: empeche un conteneur de consommer toutes les ressources de l’hote
Scanner les vulnerabilites avec Trivy
Trivy est un scanner de vulnerabilites open source developpe par Aqua Security. Il analyse les images Docker, les filesystems, les repositories Git et les configurations Kubernetes (IaC). C’est l’outil de reference pour le docker security scan.
Installer Trivy
# Debian/Ubuntu
sudo apt-get install wget apt-transport-https gnupg lsb-release
wget -qO - https://aquasecurity.github.io/trivy-repo/deb/public.key | sudo gpg --dearmor -o /usr/share/keyrings/trivy.gpg
echo "deb [signed-by=/usr/share/keyrings/trivy.gpg] https://aquasecurity.github.io/trivy-repo/deb $(lsb_release -sc) main" | sudo tee /etc/apt/sources.list.d/trivy.list
sudo apt-get update && sudo apt-get install trivy
# Ou via le script d'installation rapide
curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh -s -- -b /usr/local/bin
Scanner une image Docker
# Scan basique
trivy image mon-app:latest
# Scan avec sortie en format table
trivy image --format table mon-app:latest
# Scan avec seuil de severite (bloquer si CRITICAL ou HIGH)
trivy image --exit-code 1 --severity CRITICAL,HIGH mon-app:latest
# Scan en format JSON pour l'integration CI/CD
trivy image --format json --output trivy-report.json mon-app:latest
# Scanner une image depuis un registry prive
trivy image registry.linux-man.fr/mon-app:1.0.0
A savoir
Trivy utilise la base de donnees NVD (National Vulnerability Database) mise a jour quotidiennement. Pour les environnements hors-ligne, vous pouvez telecharger la DB et l’utiliser localement.
Exemple de resultat Trivy
mon-app:latest (debian 12.5)
Total: 42 (UNKNOWN: 0, LOW: 12, MEDIUM: 18, HIGH: 10, CRITICAL: 2)
┌──────────────┬───────────────┬──────────┬────────┬───────────────────┬───────────────────────┐
│ Library │ Vulnerability │ Severity │ Status │ Installed Version │ Fixed Version │
├──────────────┼───────────────┼──────────┼────────┼───────────────────┼───────────────────────┤
│ openssl │ CVE-2024-XXXX │ CRITICAL │ fixed │ 3.0.11-1 │ 3.0.13-1 │
│ libcurl4 │ CVE-2024-XXXX │ HIGH │ fixed │ 7.88.1-10 │ 7.88.1-10+deb12u5 │
└──────────────┴───────────────┴──────────┴────────┴───────────────────┴───────────────────────┘
Scanner le Dockerfile lui-meme (misconfiguration)
trivy config ./Dockerfile
Ce scan detecte les mauvaises pratiques comme l’utilisation de ADD au lieu de COPY, l’absence de HEALTHCHECK, ou l’utilisation de USER root.
Integrer le scan dans un pipeline CI/CD
Un scan manuel ne suffit pas. Le docker hardening doit etre automatise dans votre pipeline. Voici un exemple d’integration avec GitLab CI :
# .gitlab-ci.yml
stages:
- build
- scan
- deploy
build:image:
stage: build
image: docker:24
services:
- docker:24-dind
script:
- docker build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA .
- docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
scan:trivy:
stage: scan
image: aquasec/trivy:latest
script:
- trivy image --exit-code 1 --severity CRITICAL,HIGH $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
allow_failure: false
deploy:production:
stage: deploy
image: docker:24
services:
- docker:24-dind
script:
- docker pull $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
- docker tag $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA $CI_REGISTRY_IMAGE:production
- docker push $CI_REGISTRY_IMAGE:production
only:
- main
Pour une approche plus complete de la publication d’images dans un registry prive, consultez notre guide sur la publication d’images Docker dans un registry GitLab avec CI/CD.
Astuce
Configurez Trivy pour bloquer le deploiement si des vulnerabilites CRITICAL sont detectees. Utilisez --exit-code 1 --severity CRITICAL pour echouer le job de scan.
Recapitulatif des bonnes pratiques
| Categorie | Action | Priorite |
|---|---|---|
| Image de base | Utiliser une image officielle, pinee et legere (-slim ou -alpine) | Critique |
| Utilisateur | Creer un user non-root et utiliser USER dans le Dockerfile | Critique |
| Secrets | Jamais de secrets en dur, utiliser BuildKit –secret ou Docker secrets | Critique |
| Multi-stage | Separer le build et le runtime pour reduire la taille de l’image | Haute |
| Capabilities | cap_drop: ALL + cap_add du strict necessaire | Haute |
| Filesystem | read_only: true avec tmpfs pour les repertoires d’ecriture | Haute |
| Scan | Trivy en CI/CD avec seuil CRITICAL/HIGH | Haute |
| Reseaux | Binder sur 127.0.0.1, reverse proxy devant | Moyenne |
| Ressources | Limiter CPU et memoire avec deploy.resources | Moyenne |
| HEALTHCHECK | Ajouter un HEALTHCHECK au Dockerfile | Moyenne |
FAQ
▶ Est-il obligatoire d’utiliser un utilisateur non-root dans Docker ?
▶ Trivy est-il gratuit pour un usage en production ?
▶ Quelle difference entre une image slim et alpine ?
▶ Comment scanner une image Docker sans la telecharger ?
trivy image registry.example.com/image:tag. Il ne telecharge que les metadonnees et les couches necessaires a l’analyse, pas l’image complete.
▶ Les conteneurs non-root sont-ils vraiment plus securises ?
no-new-privileges et cap_drop: ALL, les conteneurs non-root offrent une defense solide.
{
« @context »: « https://schema.org »,
« @type »: « FAQPage »,
« mainEntity »: [
{
« @type »: « Question »,
« name »: « Est-il obligatoire d’utiliser un utilisateur non-root dans Docker ? »,
« acceptedAnswer »: {
« @type »: « Answer »,
« text »: « Ce n’est pas obligatoire mais fortement recommande. Executer en root dans un conteneur augmente le risque d’escalade de privileges si une vulnerabilite est exploitee. »
}
},
{
« @type »: « Question »,
« name »: « Trivy est-il gratuit pour un usage en production ? »,
« acceptedAnswer »: {
« @type »: « Answer »,
« text »: « Oui, Trivy est open source (licence Apache 2.0) et entierement gratuit, y compris en production. »
}
},
{
« @type »: « Question »,
« name »: « Quelle difference entre une image slim et alpine ? »,
« acceptedAnswer »: {
« @type »: « Answer »,
« text »: « Une image slim est une version reduite de l’image complete (meme distribution Linux, packages minimaux). Une image alpine utilise Alpine Linux, une distribution tres legere (environ 5 Mo). »
}
},
{
« @type »: « Question »,
« name »: « Comment scanner une image Docker sans la telecharger ? »,
« acceptedAnswer »: {
« @type »: « Answer »,
« text »: « Trivy peut scanner une image directement depuis un registry avec la commande trivy image registry.example.com/image:tag. »
}
},
{
« @type »: « Question »,
« name »: « Les conteneurs non-root sont-ils vraiment plus securises ? »,
« acceptedAnswer »: {
« @type »: « Answer »,
« text »: « Oui, ils reduisent significativement l’impact d’une compromission. Un attaquant dans un conteneur non-root ne peut pas modifier les fichiers systeme ni utiliser des ports privilegies. »
}
}
]
}
Besoin d’un audit securite de vos conteneurs Docker ?
Nos experts DevOps analysent vos images, identifient les vulnerabilites et mettent en place un pipeline CI/CD securise.

