La supply chain logicielle couvre le code, les dépendances, les images, les artefacts (binaires, modèles) et les données. Chaque maillon peut être compromis: dépendance piégée, image non signée, modèle altéré, dataset d’origine douteuse. L’objectif n’est pas de tout bloquer, mais d’instaurer des contrôles simples, systématiques et rapides, avec des preuves (SBOM, signatures, attestations) vérifiables en CI/CD et à l’exécution.
prérequis
- un registre d’images privé avec règles d’accès et rétention.
- un proxy de paquets (npm, PyPI, crates, etc.) avec mise en cache et blocage d’auteurs malveillants.
- des outils de SBOM et de scan (ex: syft, grype) et un coffre pour les clés.
- un outil de signature et d’attestation (ex: cosign, in-toto) et des politiques d’admission.
- un espace de métadonnées pour artefacts data/ML (hashes, provenance, licences).
- un runbook d’incident et une procédure de rotation de secrets.
aperçu rapide
- inventaire: générez une SBOM pour chaque build, conservez-la avec l’artefact.
- provenance: signez images et binaires, attestez le build (qui, quoi, quand).
- vérification: bloquez en entrée ce qui n’est pas signé ou non conforme.
- scans: analysez les dépendances et suivez les CVE résiduelles et leurs exceptions.
- artefacts ML et data: stockez les hashes et la provenance, vérifiez avant usage.
- opérations: rotation de secrets, nettoyage des artefacts obsolètes, journal des écarts corrigés.
carte mentale: maillons à couvrir
tutoriel pas-à-pas
étape 1: inventorier et tracer (SBOM)
Générez une SBOM pour chaque build et joignez-la à l’artefact. Conservez un identifiant de build (commit, tag, digest).
# générer une SBOM et la stocker
syft packages:dir . > sbom.json
# scanner la SBOM pour CVE connues
grype -q sbom:sbom.json
artifact_meta:
artifact_id: "svc-payments_1.8.3_linux_amd64"
git_commit: "a1b2c3d"
sbom_uri: "s3://artifacts/svc-payments/1.8.3/sbom.json"
built_at: "2024-12-08T10:15:00Z"
étape 2: signer et attester (provenance)
Signez l’image et produisez une attestation de provenance (qui a construit, à partir de quoi).
# exemple: signature d'image OCI
cosign sign --key cosign.key registry.data.pm/prod/svc:1.8.3
# attestation de provenance (in-toto/cosign attest)
cosign attest --key cosign.key \
--predicate provenance.json \
--type slsaprovenance \
registry.data.pm/prod/svc:1.8.3
{
"predicateType": "slsaprovenance",
"predicate": {
"builder": {"id": "ci.data.pm/github-actions"},
"buildType": "container/image",
"invocation": {"parameters": {"ref": "a1b2c3d"}},
"materials": [{"uri": "git+https://git.data.pm/svc.git", "digest": {"sha1": "a1b2c3d"}}]
}
}
étape 3: vérifier à l’entrée (politiques)
Bloquez le déploiement d’images non signées, de bases non approuvées, ou d’artefacts sans SBOM.
# politique d'admission (pseudo-Kubernetes) bloquant images non signées
policy:
name: "require-signed-images"
match: ["Deployments","Jobs","CronJobs"]
rules:
- field: "spec.template.spec.containers[].image"
require: "cosign_signed"
allow_base_images: ["registry.data.pm/base/ubuntu:22.04","registry.data.pm/base/distroless:latest"]
- field: "metadata.annotations['sbom_uri']"
require: "present"
check: "reachable"
étape 4: scanner dépendances et gérer les exceptions
Scannez à chaque PR et build. Documentez toute exception avec une date d’expiration et un correctif prévu.
grype --fail-on high --only-fixed sbom:sbom.json || echo "CVE à traiter"
vuln_exception:
cve: "CVE-2023-12345"
package: "openssl"
affected_version: "1.1.1x"
reason: "non exploitable dans notre contexte (TLS offload en amont)"
expires_at: "2024-12-31"
mitigation: "maj 3.x planifiée"
étape 5: sécuriser artefacts ML et données
Calculez et stockez les empreintes des modèles et des datasets, vérifiez-les en CI et au chargement.
# empreinte d'un modèle
shasum -a 256 models/cls-v1.bin > models/cls-v1.bin.sha256
# vérification côté pipeline
test "$(cut -d' ' -f1 models/cls-v1.bin.sha256)" = \
"$(shasum -a 256 models/cls-v1.bin | cut -d' ' -f1)" || { echo "hash mismatch"; exit 1; }
-- registre d'artefacts data/ml
CREATE TABLE IF NOT EXISTS supply.artifacts (
id TEXT PRIMARY KEY,
kind TEXT CHECK (kind IN ('model','dataset','binary','image')),
uri TEXT NOT NULL,
sha256 TEXT NOT NULL,
built_at TIMESTAMP,
source TEXT,
license TEXT
);
-- contrôle avant usage
SELECT 1
FROM supply.artifacts
WHERE id = 'model_cls_v1'
AND sha256 = '3a9f...d7';
étape 6: chaîne CI de bout en bout
modèles de contrôles
hook de démarrage: vérification signature et SBOM
#!/usr/bin/env bash
set -euo pipefail
IMAGE="${IMAGE:?}"
SBOM_URI="${SBOM_URI:?}"
cosign verify "$IMAGE" >/dev/null
curl -fsSL "$SBOM_URI" | jq -e '.artifacts | length > 0' >/dev/null
echo "Image signée et SBOM présente"
pipeline de vérification de datasets
-- n'autoriser que des sources connues et sous licence acceptable
WITH allowed AS (
SELECT uri FROM supply.artifacts WHERE kind = 'dataset' AND license IN ('cc-by-4.0','internal')
)
SELECT d.request_id, d.uri
FROM data.load_requests d
LEFT JOIN allowed a ON a.uri = d.uri
WHERE a.uri IS NULL;
opérations
- rotation des secrets: clés, tokens, mots de passe; journaliser les rotations et tester l’accès.
- suppression des artefacts obsolètes: politique de rétention par âge et par statut (superseded).
- journal des violations corrigées: relier CVE, correctifs, versions, dates.
retention_policy:
images:
keep_last: 5
max_age_days: 90
models:
keep_last: 3
archive_to: "s3://artifact-archive/"
secrets_rotation:
frequency_days: 30
owners: ["@secops","@platform"]
violations_log:
entry:
id: "v-2024-12-08-001"
kind: "unsigned_image"
component: "svc-payments:1.8.2"
fix: "signé et redéployé 1.8.3"
exemples complets
cas 1: build reproductible minimal
# pinner les versions et variables d'environnement
export SOURCE_DATE_EPOCH=1700000000
pip install -r requirements.txt --require-hashes
python -m build --sdist --wheel
sha256sum dist/* > dist/SHA256SUMS
explications: aligner les dépendances et l’environnement réduit les diffs binaires et facilite la vérification.
cas 2: politique d’import Python verrouillée
# requirements.lock (extrait pip-tools)
cryptography==41.0.3 \
--hash=sha256:... \
--hash=sha256:...
requests==2.31.0 \
--hash=sha256:... \
--hash=sha256:...
explications: exiger des hashes pour chaque package empêche les substitutions silencieuses.
cas 3: contrôle d’image de base autorisée
allowed_bases:
- "registry.data.pm/base/ubuntu:22.04"
- "registry.data.pm/base/distroless:latest"
deny_on_mismatch: true
explications: limiter les images de base réduit la surface d’attaque et accélère les correctifs.
erreurs courantes et solutions
- dépendances non tracées → surprises en production → SBOM obligatoire et lockfiles versionnés.
- images non signées → chaîne brisée → signature systématique et politique d’admission stricte.
- exceptions éternelles → dette sécuritaire → expiration datée et revue hebdomadaire.
- modèles et datasets sans hash → altérations invisibles → empreintes stockées et vérifiées.
- artefacts éternels → coûts et risques → rétention et nettoyage automatisés.
- clés dans le code → fuite → coffre, rotation, variables d’environnement minimales.
faq
-
Pourquoi une SBOM sur chaque build ? Pour savoir précisément ce qui embarque dans vos artefacts et accélérer la gestion de vulnérabilités ciblées.
-
La signature ne suffit-elle pas ? Non. La signature garantit l’authenticité; l’attestation de provenance renseigne le contexte de build; la SBOM décrit le contenu.
-
Comment gérer les CVE impossibles à corriger immédiatement ? Documentez une exception avec raison, périmètre, date d’expiration et mitigation; suivez-les jusqu’à résolution.
-
Faut-il signer aussi les modèles ML et les datasets ? A minima, stockez et vérifiez les hashes; si possible, signez les manifestes d’artefacts (modèle + métadonnées + SBOM).
-
Comment éviter de ralentir les équipes ? Automatisez dans la CI, utilisez des politiques claires et rapides, et ciblez en priorité les contrôles bloquants à fort impact.