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.
Deux barrières entre un commit et le cluster
- 01git push
- 02Trivy + Safety
- 03Registre
- 04API Kubernetes
- 05Décision Gatekeeper
Ce qui est refusé
| CONTRÔLE | COUCHE | ACTION |
|---|---|---|
| Image avec une CVE CRITICAL ou HIGH | CI · Trivy | BLOQUÉ |
| Dépendance Python vulnérable | CI · Safety | BLOQUÉ |
| Image d’un registre non approuvé | Admission · Rego | REFUSÉ |
| Image avec le tag latest | Admission · Rego | REFUSÉ |
| Pod exécuté en root | Admission · Rego | REFUSÉ |
| Système de fichiers en écriture, capabilities en trop | Manifest | DURCI |
Directement depuis le code
Extraits du vrai dépôt et des livrables, raccourcis pour la lecture.
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.
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: falseLa 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.
Résultats
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.