Hardening

Container Security

Sécurisation des conteneurs Docker et Kubernetes — images, runtime et orchestration

dockerkubernetesconteneurssécuritéscanning

Images Docker sécurisées

Bonnes pratiques Dockerfile

# Utiliser une image minimale
FROM node:22-alpine AS builder
 
# Ne pas tourner en root
RUN addgroup -S app && adduser -S app -G app
 
# Copier uniquement le nécessaire
COPY --chown=app:app package.json pnpm-lock.yaml ./
RUN corepack enable && pnpm install --frozen-lockfile --prod
 
COPY --chown=app:app . .
RUN pnpm build
 
# Image finale minimale
FROM gcr.io/distroless/nodejs22-debian12
COPY --from=builder /app/dist /app
USER 1000
EXPOSE 3000
CMD ["app/server.js"]

Règles d'or

  • Images minimales : Alpine ou Distroless
  • Multi-stage builds : pas d'outils de build en production
  • Pas de root : USER 1000 ou utilisateur non-root
  • COPY spécifique : pas de COPY . . sans .dockerignore
  • Versions fixées : node:22.0.0-alpine pas node:latest

Scan d'images

$trivy image mon-app:latest
Scanner les vulnérabilités d'une image Docker
$trivy image --severity HIGH,CRITICAL mon-app:latest
Uniquement les vulnérabilités hautes et critiques
$docker scout cves mon-app:latest
Docker Scout — analyse de vulnérabilités
$grype mon-app:latest
Grype — scanner open source d'Anchore

Intégration CI

# GitHub Actions
- name: Scan image
  uses: aquasecurity/trivy-action@master
  with:
    image-ref: mon-app:latest
    severity: HIGH,CRITICAL
    exit-code: 1

Runtime Docker sécurisé

Docker daemon

{
  "icc": false,
  "userns-remap": "default",
  "no-new-privileges": true,
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "10m",
    "max-file": "3"
  }
}

Exécution sécurisée

$docker run --read-only --tmpfs /tmp mon-app
Filesystem en lecture seule
$docker run --cap-drop=ALL --cap-add=NET_BIND_SERVICE mon-app
Capabilities minimales
$docker run --security-opt=no-new-privileges mon-app
Empêcher l'escalade de privilèges
$docker run --memory=256m --cpus=0.5 mon-app
Limiter les ressources

Checklist Docker

  • Pas de conteneur en root
  • Capabilities droppées
  • Filesystem read-only quand possible
  • Limites de ressources définies
  • Images scannées dans le pipeline CI
  • Pas de secrets dans les variables d'environnement de docker run
  • Réseau Docker isolé par application

Kubernetes Security

Pod Security Standards

apiVersion: v1
kind: Pod
spec:
  securityContext:
    runAsNonRoot: true
    runAsUser: 1000
    fsGroup: 1000
    seccompProfile:
      type: RuntimeDefault
  containers:
    - name: app
      securityContext:
        allowPrivilegeEscalation: false
        readOnlyRootFilesystem: true
        capabilities:
          drop: ["ALL"]
      resources:
        limits:
          cpu: 500m
          memory: 256Mi
        requests:
          cpu: 100m
          memory: 128Mi

Network Policies

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: deny-all
spec:
  podSelector: {}
  policyTypes:
    - Ingress
    - Egress
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-api-to-db
spec:
  podSelector:
    matchLabels:
      app: db
  ingress:
    - from:
        - podSelector:
            matchLabels:
              app: api
      ports:
        - port: 5432

RBAC

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: pod-reader
rules:
  - apiGroups: [""]
    resources: ["pods"]
    verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: read-pods
subjects:
  - kind: ServiceAccount
    name: monitoring
roleRef:
  kind: Role
  name: pod-reader
  apiGroup: rbac.authorization.k8s.io

Outils K8s Security

OutilUsage
kube-benchAudit CIS Benchmark
FalcoRuntime security monitoring
OPA/GatekeeperPolicy enforcement
kubescapeScan de sécurité complet
$kube-bench run --targets node
Audit CIS Benchmark du node
$kubescape scan framework nsa
Scan NSA/CISA Kubernetes Hardening