Aller au contenu

TP : Construire une mini-CNAPP locale avec Trivy, Syft, Grype et Kubescape

Prérequis

Environnement technique

  • Docker installé et fonctionnel.
  • Accès à un terminal Bash ou Zsh.
  • Accès aux registres Docker Hub et GHCR.
  • jq installé pour lire, filtrer et consolider les rapports JSON.
  • Kubescape installé localement.
  • Accès Internet pour télécharger les images, les bases de vulnérabilités et les frameworks Kubescape.
  • Environnement recommandé sur Windows : WSL2 + Ubuntu + Docker Desktop.

Installer jq

Sur Debian / Ubuntu :

Fenêtre de terminal
sudo apt update
sudo apt install -y jq

Sur macOS avec Homebrew :

Fenêtre de terminal
brew install jq

Installer Kubescape

Sur macOS avec Homebrew :

Fenêtre de terminal
brew install kubescape

Sur Linux :

Fenêtre de terminal
curl -s https://raw.githubusercontent.com/kubescape/kubescape/master/install.sh | /bin/bash

Vérifier l’installation :

Fenêtre de terminal
kubescape version

Compatibilité Windows

Ce TP est conçu pour être exécuté dans un terminal Bash.

Sur Windows, l’environnement recommandé est :

Windows + WSL2 + Ubuntu + Docker Desktop + jq + Kubescape

Depuis Ubuntu WSL2, vérifier que Docker est accessible :

Fenêtre de terminal
docker --version
docker run --rm hello-world

L’exécution directe en PowerShell n’est pas recommandée, car plusieurs commandes utilisent une syntaxe Bash :

  • heredocs <<'EOF'
  • variable $PWD
  • blocs { echo ... }
  • pipes et redirections
  • chemins Linux pour les volumes Docker

Outils utilisés

Les outils utilisés via Docker sont :

Fenêtre de terminal
aquasec/trivy:0.71.0
ghcr.io/anchore/syft:v1.45.1
ghcr.io/anchore/grype:v0.114.0

Kubescape est utilisé localement avec la commande :

Fenêtre de terminal
kubescape

Image utilisée

Fenêtre de terminal
python:3.4-alpine

Cette image est volontairement obsolète. Python 3.4 est en fin de vie et ne doit pas être utilisé en production.

Objectifs

À la fin de ce TP, l’apprenant doit être capable de :

  • Comprendre la logique d’une mini-CNAPP locale.
  • Scanner une image de conteneur avec Trivy.
  • Générer un SBOM avec Syft.
  • Scanner une image avec Grype.
  • Scanner un SBOM avec Grype.
  • Scanner des manifests Kubernetes avec Trivy.
  • Scanner des manifests Kubernetes avec Kubescape.
  • Produire des rapports JSON exploitables.
  • Consolider plusieurs résultats dans un rapport unique.
  • Comprendre la complémentarité entre scan image, SBOM, vulnérabilités, configuration Kubernetes et posture sécurité.
  • Interpréter un code de retour dans un contexte CI/CD.

Commandes

1. Créer l’arborescence du TP

Fenêtre de terminal
mkdir -p mini-cnapp-locale
cd mini-cnapp-locale
mkdir -p k8s
mkdir -p reports
mkdir -p .trivy-cache

2. Télécharger l’image vulnérable

Fenêtre de terminal
docker pull python:3.4-alpine

3. Télécharger les images des outils

Fenêtre de terminal
docker pull aquasec/trivy:0.71.0
docker pull ghcr.io/anchore/syft:v1.45.1
docker pull ghcr.io/anchore/grype:v0.114.0

4. Vérifier les versions des outils Docker

Fenêtre de terminal
docker run --rm aquasec/trivy:0.71.0 --version
docker run --rm ghcr.io/anchore/syft:v1.45.1 version
docker run --rm ghcr.io/anchore/grype:v0.114.0 version

5. Vérifier la version de Kubescape

Fenêtre de terminal
kubescape version

6. Créer un manifeste Kubernetes volontairement vulnérable

Fenêtre de terminal
cat > k8s/pod-vulnerable.yaml <<'EOF'
apiVersion: v1
kind: Pod
metadata:
name: vulnerable-pod
labels:
app: vulnerable-app
spec:
hostPID: true
containers:
- name: nginx
image: nginx:1.25
ports:
- containerPort: 80
securityContext:
privileged: true
runAsUser: 0
EOF

7. Scanner l’image locale avec Trivy

Fenêtre de terminal
docker run --rm \
-v /var/run/docker.sock:/var/run/docker.sock \
-v "$PWD/.trivy-cache:/root/.cache/" \
-v "$PWD:/workdir" \
aquasec/trivy:0.71.0 \
image --format json --output /workdir/reports/trivy-image.json python:3.4-alpine
Fenêtre de terminal
echo "Code de retour Trivy image : $?"

8. Lire la synthèse des vulnérabilités Trivy

Fenêtre de terminal
jq '[.Results[]?.Vulnerabilities[]? | .Severity] | sort | group_by(.) | map({severity: .[0], count: length})' reports/trivy-image.json

9. Afficher les principales vulnérabilités Trivy

Fenêtre de terminal
jq -r '.Results[]?.Vulnerabilities[]? | [.VulnerabilityID, .Severity, .PkgName, .InstalledVersion, (.FixedVersion // "non renseigné")] | @tsv' reports/trivy-image.json | head -20

10. Générer un SBOM Syft au format Syft JSON

Fenêtre de terminal
docker run --rm \
-v "$PWD:/workdir" \
ghcr.io/anchore/syft:v1.45.1 \
registry:python:3.4-alpine \
-o syft-json=/workdir/reports/syft-sbom.json
Fenêtre de terminal
echo "Code de retour Syft JSON : $?"

11. Générer un SBOM Syft au format CycloneDX JSON

Fenêtre de terminal
docker run --rm \
-v "$PWD:/workdir" \
ghcr.io/anchore/syft:v1.45.1 \
registry:python:3.4-alpine \
-o cyclonedx-json=/workdir/reports/syft-cyclonedx.json
Fenêtre de terminal
echo "Code de retour Syft CycloneDX : $?"

12. Vérifier les SBOM générés

Fenêtre de terminal
ls -lh reports/syft-sbom.json reports/syft-cyclonedx.json

13. Lire une synthèse du SBOM Syft

Fenêtre de terminal
jq '{schema: .schema.version, artifacts: (.artifacts | length)}' reports/syft-sbom.json

14. Lire une synthèse du SBOM CycloneDX

Fenêtre de terminal
jq '{bomFormat, specVersion, components: (.components | length)}' reports/syft-cyclonedx.json

15. Scanner l’image avec Grype depuis le registre

Fenêtre de terminal
docker run --rm \
-v "$PWD:/workdir" \
ghcr.io/anchore/grype:v0.114.0 \
registry:python:3.4-alpine \
-o json=/workdir/reports/grype-image.json
Fenêtre de terminal
echo "Code de retour Grype image : $?"

16. Lire la synthèse des vulnérabilités Grype sur l’image

Fenêtre de terminal
jq '[.matches[]?.vulnerability.severity] | sort | group_by(.) | map({severity: .[0], count: length})' reports/grype-image.json

17. Afficher les principales vulnérabilités Grype sur l’image

Fenêtre de terminal
jq -r '.matches[]? | [.vulnerability.id, .vulnerability.severity, .artifact.name, .artifact.version, (.vulnerability.fix.versions[0] // "non renseigné")] | @tsv' reports/grype-image.json | head -20

18. Scanner le SBOM Syft avec Grype

Fenêtre de terminal
docker run --rm \
-v "$PWD:/workdir" \
ghcr.io/anchore/grype:v0.114.0 \
sbom:/workdir/reports/syft-sbom.json \
-o json=/workdir/reports/grype-sbom.json
Fenêtre de terminal
echo "Code de retour Grype SBOM : $?"

19. Lire la synthèse des vulnérabilités Grype sur le SBOM

Fenêtre de terminal
jq '[.matches[]?.vulnerability.severity] | sort | group_by(.) | map({severity: .[0], count: length})' reports/grype-sbom.json

20. Afficher les principales vulnérabilités Grype sur le SBOM

Fenêtre de terminal
jq -r '.matches[]? | [.vulnerability.id, .vulnerability.severity, .artifact.name, .artifact.version, (.vulnerability.fix.versions[0] // "non renseigné")] | @tsv' reports/grype-sbom.json | head -20

21. Scanner les manifests Kubernetes avec Trivy

Fenêtre de terminal
docker run --rm \
-v "$PWD/.trivy-cache:/root/.cache/" \
-v "$PWD:/workdir" \
aquasec/trivy:0.71.0 \
config --format json --output /workdir/reports/trivy-config-k8s.json /workdir/k8s
Fenêtre de terminal
echo "Code de retour Trivy config Kubernetes : $?"

22. Lire la synthèse des mauvaises configurations Trivy

Fenêtre de terminal
jq '[.Results[]?.Misconfigurations[]? | .Severity] | sort | group_by(.) | map({severity: .[0], count: length})' reports/trivy-config-k8s.json

23. Afficher les principales mauvaises configurations Trivy

Fenêtre de terminal
jq -r '.Results[]?.Misconfigurations[]? | [.ID, .Severity, .Title, (.CauseMetadata.Resource? // "non renseigné")] | @tsv' reports/trivy-config-k8s.json | head -20

24. Scanner les manifests Kubernetes locaux avec Kubescape

Fenêtre de terminal
kubescape scan ./k8s/ \
--format json \
--format-version v2 \
--output reports/kubescape-k8s.json
Fenêtre de terminal
echo "Code de retour Kubescape : $?"

25. Vérifier que le rapport Kubescape a bien été généré

Fenêtre de terminal
ls -lh reports/kubescape-k8s.json

26. Lire les clés principales du rapport Kubescape

Fenêtre de terminal
jq 'keys' reports/kubescape-k8s.json

27. Afficher une synthèse des statuts Kubescape

Fenêtre de terminal
jq -r '.results[]?.controls[]?.status.status? // empty' reports/kubescape-k8s.json | sort | uniq -c

28. Vérifier l’ensemble des rapports générés

Fenêtre de terminal
ls -lh reports

29. Construire un rapport de synthèse mini-CNAPP

Fenêtre de terminal
{
echo "# Rapport mini-CNAPP locale"
echo
echo "## Cartographie des contrôles"
echo
echo "| Domaine | Outil | Rapport |"
echo "|---|---|---|"
echo "| Vulnérabilités image | Trivy | reports/trivy-image.json |"
echo "| SBOM Syft JSON | Syft | reports/syft-sbom.json |"
echo "| SBOM CycloneDX | Syft | reports/syft-cyclonedx.json |"
echo "| Vulnérabilités image | Grype | reports/grype-image.json |"
echo "| Vulnérabilités depuis SBOM | Grype | reports/grype-sbom.json |"
echo "| Misconfigurations Kubernetes | Trivy | reports/trivy-config-k8s.json |"
echo "| Posture Kubernetes | Kubescape | reports/kubescape-k8s.json |"
echo
echo "## Synthèse Trivy image"
echo
echo '```json'
jq '[.Results[]?.Vulnerabilities[]? | .Severity] | sort | group_by(.) | map({severity: .[0], count: length})' reports/trivy-image.json
echo '```'
echo
echo "## Synthèse Syft SBOM"
echo
echo '```json'
jq '{schema: .schema.version, artifacts: (.artifacts | length)}' reports/syft-sbom.json
echo '```'
echo
echo "## Synthèse Grype image"
echo
echo '```json'
jq '[.matches[]?.vulnerability.severity] | sort | group_by(.) | map({severity: .[0], count: length})' reports/grype-image.json
echo '```'
echo
echo "## Synthèse Grype SBOM"
echo
echo '```json'
jq '[.matches[]?.vulnerability.severity] | sort | group_by(.) | map({severity: .[0], count: length})' reports/grype-sbom.json
echo '```'
echo
echo "## Synthèse Trivy configuration Kubernetes"
echo
echo '```json'
jq '[.Results[]?.Misconfigurations[]? | .Severity] | sort | group_by(.) | map({severity: .[0], count: length})' reports/trivy-config-k8s.json
echo '```'
echo
echo "## Synthèse Kubescape"
echo
echo '```text'
jq -r '.results[]?.controls[]?.status.status? // empty' reports/kubescape-k8s.json | sort | uniq -c
echo '```'
} > reports/mini-cnapp-summary.md

30. Afficher le rapport de synthèse

Fenêtre de terminal
cat reports/mini-cnapp-summary.md

31. Rendre le scan Grype bloquant à partir du SBOM

Fenêtre de terminal
docker run --rm \
-v "$PWD:/workdir" \
ghcr.io/anchore/grype:v0.114.0 \
sbom:/workdir/reports/syft-sbom.json \
--fail-on high
Fenêtre de terminal
echo "Code de retour Grype bloquant : $?"

32. Rendre le scan Trivy bloquant sur les vulnérabilités HIGH et CRITICAL

Fenêtre de terminal
docker run --rm \
-v /var/run/docker.sock:/var/run/docker.sock \
-v "$PWD/.trivy-cache:/root/.cache/" \
aquasec/trivy:0.71.0 \
image --severity HIGH,CRITICAL --exit-code 1 python:3.4-alpine
Fenêtre de terminal
echo "Code de retour Trivy bloquant : $?"

Résultat attendu

Fichier produitDescription
reports/trivy-image.jsonRapport Trivy de vulnérabilités sur l’image
reports/syft-sbom.jsonSBOM généré par Syft au format JSON
reports/syft-cyclonedx.jsonSBOM généré par Syft au format CycloneDX
reports/grype-image.jsonRapport Grype de vulnérabilités sur l’image
reports/grype-sbom.jsonRapport Grype de vulnérabilités sur le SBOM Syft
reports/trivy-config-k8s.jsonRapport Trivy de misconfigurations sur les manifests Kubernetes
reports/kubescape-k8s.jsonRapport Kubescape de conformité sur les manifests Kubernetes
reports/mini-cnapp-summary.mdRapport de synthèse centralisant les résultats de tous les outils
ContrôleRésultat attendu
Rapport de synthèsemini-cnapp-summary.md centralise les résultats principaux de chaque outil
Approche CNAPPPlusieurs contrôles regroupés localement simulent une approche CNAPP simplifiée
Gate Grype --fail-on highCode de retour différent de 0 si vulnérabilités high ou critical détectées
Gate Trivy --exit-code 1Code de retour différent de 0 si vulnérabilités HIGH ou CRITICAL détectées