Aller au contenu
mouaad.sek:~$
Tous les dossiers~/work/cf-02$
CF-02DevSecOpsSTATUS: CONTENU

Sécurité de la supply chain sur Kubernetes

Une image vulnérable n’atteint jamais le registre, et un pod non conforme n’atteint jamais le cluster. Deux barrières : une dans la CI, une à l’admission.

RÔLE
Seul, lab personnel
QUAND
M1, 2026
STACK
GitLab CI, Docker, Trivy, Safety, Kubernetes (kind), OPA Gatekeeper, Rego
01 / CONTEXTE

Le problème

La plupart des attaques sur les conteneurs ne commencent pas en production. Elles commencent par une image de base vulnérable, une dépendance obsolète, ou un pod qui tourne en root parce que personne n’a dit non.

L’objectif : rendre le chemin non sécurisé impossible par défaut, dans le pipeline comme dans le cluster, sans compter sur la mémoire des développeurs.

02 / APPROCHE

Deux barrières entre un commit et le cluster

  1. 01git push
  2. 02Trivy + Safety
  3. 03Registre
  4. 04API Kubernetes
  5. 05Décision Gatekeeper
03 / CE QUI EST REFUSÉ

Ce qui est refusé

CONTRÔLECOUCHEACTION
Image avec une CVE CRITICAL ou HIGHCI · TrivyBLOQUÉ
Dépendance Python vulnérableCI · SafetyBLOQUÉ
Image d’un registre non approuvéAdmission · RegoREFUSÉ
Image avec le tag latestAdmission · RegoREFUSÉ
Pod exécuté en rootAdmission · RegoREFUSÉ
Système de fichiers en écriture, capabilities en tropManifestDURCI
04 / PREUVES

Directement depuis le code

Extraits du vrai dépôt et des livrables, raccourcis pour la lecture.

gatekeeper/templates/no-latest-template.yamlyaml
apiVersion: templates.gatekeeper.sh/v1
kind: ConstraintTemplate
metadata:
  name: k8snolatestimage
spec:
  crd:
    spec:
      names:
        kind: K8sNoLatestImage
  targets:
    - target: admission.k8s.gatekeeper.sh
      rego: |
        package k8snolatestimage
        violation[{"msg": msg}] {
          container := input.review.object.spec.containers[_]
          endswith(container.image, ":latest")
          msg := sprintf("BLOQUE : image '%v' utilise le tag latest.", [container.image])
        }
        violation[{"msg": msg}] {
          container := input.review.object.spec.containers[_]
          not contains(container.image, ":")
          msg := sprintf("BLOQUE : image '%v' n'a pas de tag.", [container.image])
        }

Politique d’admission en Rego : tout pod qui utilise le tag latest, ou aucun tag, est refusé par l’API Kubernetes.

.gitlab-ci.ymlyaml
variables:
  IMAGE_TAG: "$CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA"
  TRIVY_SEVERITY: "CRITICAL,HIGH"

stages: [build, scan, push]

trivy-image-scan:
  stage: scan
  script:
    - trivy image --input image.tar --severity "$TRIVY_SEVERITY" --exit-code 1 --format table
    - trivy image --input image.tar --format json --output trivy-report.json
  allow_failure: false

safety-scan:
  stage: scan
  script:
    - pip install safety
    - safety check -r app/requirements.txt --full-report --output text
  allow_failure: false

La barrière CI : Trivy sort en code 1 sur toute vulnérabilité CRITICAL ou HIGH, donc l’étape de push ne s’exécute jamais.

05 / RÉSULTATS

Résultats

2barrières, CI et admission
3politiques Gatekeeper écrites en Rego
0image poussée quand un scan échoue
06 / RETOUR D’EXPÉRIENCE

Ce que je ferais autrement

Gatekeeper vérifie d’où vient une image, pas si mon pipeline l’a vraiment construite. Prochaine étape : signer les images avec Cosign dans la CI, vérifier les signatures à l’admission, et générer un SBOM à chaque build.

DOSSIER SUIVANT · CF-03JobPilot
Esc

↑↓ naviguer · Entrée ouvrir · Échap fermer