Vue d’ensemble
Les manifestes Kubernetes mal configurés sont l’une des principales causes d’incidents de sécurité en production. Un conteneur s’exécutant en tant que root, un tag d’image non épinglé, une limite de ressources manquante ou un réseau hôte exposé peuvent chacun ouvrir la porte à une escalade de privilèges, un épuisement des ressources ou un mouvement latéral à l’intérieur de votre cluster.
Le problème est que ces mauvaises configurations sont invisibles jusqu’au moment du déploiement — ou pire, jusqu’à ce qu’un attaquant les exploite. La solution est de décaler la sécurité vers la gauche (shift left) et de détecter les violations de politiques avant que les manifestes n’atteignent le cluster.
Dans ce lab pratique, vous utiliserez Conftest — un framework de test construit sur le moteur Open Policy Agent (OPA) — pour écrire des politiques Rego qui valident les manifestes Kubernetes. Vous intégrerez ensuite ces vérifications dans GitHub Actions et GitLab CI afin que chaque pull request soit automatiquement analysée pour détecter les violations.
À la fin de ce lab, vous disposerez de :
- Une bibliothèque de politiques Rego réutilisables couvrant les tags d’images, les contextes de sécurité, les limites de ressources et l’accès au niveau de l’hôte.
- Des tests unitaires pour ces politiques utilisant
opa test. - Des pipelines CI/CD fonctionnels qui bloquent les manifestes non sécurisés et fournissent des messages de violation clairs et exploitables.
Prérequis
Avant de commencer, assurez-vous de disposer des outils et connaissances suivants :
- CLI conftest installé — installez avec Homebrew :
brew install conftestVous pouvez également télécharger le binaire depuis la page des releases de Conftest.
- kubectl et un cluster de test (optionnel) — si vous souhaitez vérifier que vos manifestes corrigés se déploient correctement, démarrez un cluster local avec
minikube startoukind create cluster. - Un dépôt de test — créez un nouveau dépôt Git ou utilisez un dépôt existant. Nous construirons tous les fichiers à partir de zéro.
- Connaissances de base en YAML et Kubernetes — vous devez être à l’aise avec la lecture des manifestes Deployment, Service et Pod.
Configuration de l’environnement
Commencez par créer la structure du projet et un ensemble de manifestes Kubernetes intentionnellement non sécurisés. Ceux-ci serviront de fixtures de test tout au long de chaque exercice.
Structure du projet
conftest-k8s-lab/
├── k8s/
│ ├── deployment-latest-tag.yaml
│ ├── deployment-run-as-root.yaml
│ ├── deployment-no-limits.yaml
│ ├── service-loadbalancer.yaml
│ └── pod-host-network.yaml
└── policy/
Créez les répertoires :
mkdir -p conftest-k8s-lab/k8s conftest-k8s-lab/policy
cd conftest-k8s-lab
Manifeste 1 — Deployment avec un tag d’image non épinglé
Créez k8s/deployment-latest-tag.yaml :
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-latest
spec:
replicas: 1
selector:
matchLabels:
app: web-latest
template:
metadata:
labels:
app: web-latest
spec:
containers:
- name: nginx
image: nginx:latest
ports:
- containerPort: 80
Ce manifeste utilise nginx:latest, ce qui signifie que chaque pull pourrait silencieusement introduire un binaire différent dans votre cluster.
Manifeste 2 — Deployment s’exécutant en tant que root
Créez k8s/deployment-run-as-root.yaml :
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-root
spec:
replicas: 1
selector:
matchLabels:
app: web-root
template:
metadata:
labels:
app: web-root
spec:
containers:
- name: nginx
image: nginx:1.25.4
ports:
- containerPort: 80
Aucun securityContext n’est défini, donc le conteneur s’exécute par défaut en tant que root — un vecteur d’escalade de privilèges bien connu.
Manifeste 3 — Deployment sans limites de ressources
Créez k8s/deployment-no-limits.yaml :
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-no-limits
spec:
replicas: 1
selector:
matchLabels:
app: web-no-limits
template:
metadata:
labels:
app: web-no-limits
spec:
containers:
- name: nginx
image: nginx:1.25.4
ports:
- containerPort: 80
Sans limites de CPU et de mémoire, un seul pod défaillant peut affamer l’ensemble du nœud.
Manifeste 4 — Service de type LoadBalancer
Créez k8s/service-loadbalancer.yaml :
apiVersion: v1
kind: Service
metadata:
name: web-lb
spec:
type: LoadBalancer
selector:
app: web
ports:
- port: 80
targetPort: 80
Un service LoadBalancer sans annotations peut exposer des charges de travail sur l’internet public dans les environnements cloud.
Manifeste 5 — Pod avec accès au réseau hôte
Créez k8s/pod-host-network.yaml :
apiVersion: v1
kind: Pod
metadata:
name: debug-pod
spec:
hostNetwork: true
containers:
- name: debug
image: busybox:1.36
command: ["sleep", "3600"]
hostNetwork: true donne au pod un accès complet à la pile réseau du nœud, contournant entièrement les network policies.
Exercice 1 : Écrire votre première politique Rego — Pas de tags latest
Votre première politique refusera toute image de conteneur qui utilise le tag :latest ou omet un tag entièrement (ce qui résout également vers latest).
Étape 1 — Créer la politique
Créez policy/tags.rego :
package main
import future.keywords.in
deny[msg] {
input.kind == "Deployment"
container := input.spec.template.spec.containers[_]
image := container.image
not contains(image, ":")
msg := sprintf("Container '%s' uses image '%s' without a tag. Pin to a specific version.", [container.name, image])
}
deny[msg] {
input.kind == "Deployment"
container := input.spec.template.spec.containers[_]
image := container.image
endswith(image, ":latest")
msg := sprintf("Container '%s' uses the ':latest' tag in image '%s'. Pin to a specific version.", [container.name, image])
}
Étape 2 — Exécuter Conftest sur le manifeste non sécurisé
conftest test k8s/deployment-latest-tag.yaml
Sortie attendue :
FAIL - k8s/deployment-latest-tag.yaml - main - Container 'nginx' uses the ':latest' tag in image 'nginx:latest'. Pin to a specific version.
1 test, 0 passed, 0 warnings, 1 failure
Étape 3 — Corriger le manifeste
Éditez k8s/deployment-latest-tag.yaml et modifiez la ligne de l’image :
image: nginx:1.25.4
Exécutez Conftest à nouveau :
conftest test k8s/deployment-latest-tag.yaml
Sortie attendue :
1 test, 1 passed, 0 warnings, 0 failures
Comprendre la structure Rego
Chaque fichier de politique Rego utilisé par Conftest suit un schéma simple :
package main— Conftest recherche le packagemainpar défaut. Vous pouvez modifier cela avec--namespace.deny[msg]— un ensemble de règles. Si toutes les conditions dans le corps de la règle s’évaluent à vrai, la règle se déclenche et ajoutemsgà l’ensemble des violations.input— représente le document YAML testé. Conftest le parse automatiquement en un objet JSON.sprintf— formate un message d’erreur lisible qui apparaît dans les logs CI.
Exercice 2 : Pas de conteneurs s’exécutant en tant que root
Les conteneurs qui s’exécutent en tant que root peuvent modifier le système de fichiers, installer des paquets et — combinés à un exploit du noyau — s’échapper vers l’hôte. Cette politique applique deux contrôles : runAsNonRoot: true et allowPrivilegeEscalation: false.
Étape 1 — Créer la politique
Créez policy/security_context.rego :
package main
deny[msg] {
input.kind == "Deployment"
container := input.spec.template.spec.containers[_]
not container.securityContext.runAsNonRoot == true
msg := sprintf("Container '%s' must set securityContext.runAsNonRoot to true.", [container.name])
}
deny[msg] {
input.kind == "Deployment"
container := input.spec.template.spec.containers[_]
not container.securityContext.allowPrivilegeEscalation == false
msg := sprintf("Container '%s' must set securityContext.allowPrivilegeEscalation to false.", [container.name])
}
Étape 2 — Tester sur le manifeste non sécurisé
conftest test k8s/deployment-run-as-root.yaml
Sortie attendue :
FAIL - k8s/deployment-run-as-root.yaml - main - Container 'nginx' must set securityContext.runAsNonRoot to true.
FAIL - k8s/deployment-run-as-root.yaml - main - Container 'nginx' must set securityContext.allowPrivilegeEscalation to false.
1 test, 0 passed, 0 warnings, 2 failures
Étape 3 — Corriger le manifeste
Mettez à jour k8s/deployment-run-as-root.yaml pour inclure un contexte de sécurité sur chaque conteneur :
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-root
spec:
replicas: 1
selector:
matchLabels:
app: web-root
template:
metadata:
labels:
app: web-root
spec:
containers:
- name: nginx
image: nginx:1.25.4
ports:
- containerPort: 80
securityContext:
runAsNonRoot: true
allowPrivilegeEscalation: false
Exécutez Conftest à nouveau — les deux règles passent maintenant.
Exercice 3 : Exiger des limites de ressources
Sans limites de ressources, un seul conteneur peut consommer tout le CPU et la mémoire du nœud, provoquant des défaillances en cascade sur des charges de travail non liées. De nombreux référentiels de conformité (SOC 2, CIS Benchmarks) exigent des limites explicites.
Étape 1 — Créer la politique
Créez policy/resources.rego :
package main
deny[msg] {
input.kind == "Deployment"
container := input.spec.template.spec.containers[_]
not container.resources.limits.cpu
msg := sprintf("Container '%s' must define resources.limits.cpu.", [container.name])
}
deny[msg] {
input.kind == "Deployment"
container := input.spec.template.spec.containers[_]
not container.resources.limits.memory
msg := sprintf("Container '%s' must define resources.limits.memory.", [container.name])
}
Étape 2 — Tester sur le manifeste non sécurisé
conftest test k8s/deployment-no-limits.yaml
Sortie attendue :
FAIL - k8s/deployment-no-limits.yaml - main - Container 'nginx' must define resources.limits.cpu.
FAIL - k8s/deployment-no-limits.yaml - main - Container 'nginx' must define resources.limits.memory.
1 test, 0 passed, 0 warnings, 2 failures
Étape 3 — Corriger le manifeste
Ajoutez des limites de ressources à k8s/deployment-no-limits.yaml :
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-no-limits
spec:
replicas: 1
selector:
matchLabels:
app: web-no-limits
template:
metadata:
labels:
app: web-no-limits
spec:
containers:
- name: nginx
image: nginx:1.25.4
ports:
- containerPort: 80
resources:
requests:
cpu: "100m"
memory: "128Mi"
limits:
cpu: "250m"
memory: "256Mi"
Exécutez Conftest à nouveau — les vérifications de CPU et de mémoire passent.
Exercice 4 : Refuser l’accès privilégié à l’hôte
Les pods qui demandent un accès au niveau de l’hôte — hostNetwork, hostPID, hostIPC ou un contexte de sécurité privilégié — s’exécutent effectivement en dehors du sandbox du conteneur. Un pod compromis avec l’un de ces drapeaux peut voir tout le trafic sur le nœud, s’attacher à d’autres processus ou s’échapper entièrement vers l’hôte.
Étape 1 — Créer la politique
Créez policy/host_access.rego :
package main
deny[msg] {
input.kind == "Pod"
input.spec.hostNetwork == true
msg := sprintf("Pod '%s' must not use hostNetwork: true.", [input.metadata.name])
}
deny[msg] {
input.kind == "Pod"
input.spec.hostPID == true
msg := sprintf("Pod '%s' must not use hostPID: true.", [input.metadata.name])
}
deny[msg] {
input.kind == "Pod"
input.spec.hostIPC == true
msg := sprintf("Pod '%s' must not use hostIPC: true.", [input.metadata.name])
}
deny[msg] {
input.kind == "Pod"
container := input.spec.containers[_]
container.securityContext.privileged == true
msg := sprintf("Container '%s' in Pod '%s' must not run in privileged mode.", [container.name, input.metadata.name])
}
deny[msg] {
input.kind == "Deployment"
input.spec.template.spec.hostNetwork == true
msg := sprintf("Deployment '%s' must not use hostNetwork: true.", [input.metadata.name])
}
deny[msg] {
input.kind == "Deployment"
container := input.spec.template.spec.containers[_]
container.securityContext.privileged == true
msg := sprintf("Container '%s' in Deployment '%s' must not run in privileged mode.", [container.name, input.metadata.name])
}
Étape 2 — Tester sur le pod non sécurisé
conftest test k8s/pod-host-network.yaml
Sortie attendue :
FAIL - k8s/pod-host-network.yaml - main - Pod 'debug-pod' must not use hostNetwork: true.
1 test, 0 passed, 0 warnings, 1 failure
Étape 3 — Corriger le manifeste
Supprimez la ligne hostNetwork: true de k8s/pod-host-network.yaml :
apiVersion: v1
kind: Pod
metadata:
name: debug-pod
spec:
containers:
- name: debug
image: busybox:1.36
command: ["sleep", "3600"]
Exécutez Conftest — le pod passe maintenant toutes les vérifications d’accès à l’hôte.
Exercice 5 : Tester les politiques avec opa test
Les politiques sont du code, et le code a besoin de tests. Sans tests, vous ne pouvez pas être sûr qu’une politique détecte ce qu’elle devrait, ni qu’un futur refactoring n’introduise un faux positif qui bloque des déploiements légitimes.
Étape 1 — Créer les cas de test
Créez policy/tags_test.rego :
package main
test_latest_denied {
input := {
"kind": "Deployment",
"spec": {
"template": {
"spec": {
"containers": [
{
"name": "app",
"image": "nginx:latest"
}
]
}
}
}
}
count(deny) > 0
}
test_no_tag_denied {
input := {
"kind": "Deployment",
"spec": {
"template": {
"spec": {
"containers": [
{
"name": "app",
"image": "nginx"
}
]
}
}
}
}
count(deny) > 0
}
test_pinned_allowed {
input := {
"kind": "Deployment",
"spec": {
"template": {
"spec": {
"containers": [
{
"name": "app",
"image": "nginx:1.25.4"
}
]
}
}
}
}
count(deny) == 0
}
Étape 2 — Exécuter les tests
opa test policy/ -v
Sortie attendue :
policy/tags_test.rego:
data.main.test_latest_denied: PASS (1.234ms)
data.main.test_no_tag_denied: PASS (0.567ms)
data.main.test_pinned_allowed: PASS (0.432ms)
--------------------------------------------------------------------------------
PASS: 3/3
Pourquoi tester les politiques est important
Dans un contexte CI/CD, un faux négatif signifie qu’un manifeste non sécurisé passe à travers les mailles du filet, tandis qu’un faux positif bloque un déploiement légitime et érode la confiance des développeurs dans le pipeline. En écrivant des cas de test explicites pour les entrées autorisées et refusées, vous obtenez une suite de régression qui s’exécute en millisecondes et garantit que vos politiques se comportent correctement à mesure que la bibliothèque de règles grandit.
Prenez l’habitude d’ajouter un fichier *_test.rego pour chaque nouveau fichier de politique. Exécutez opa test policy/ -v dans le cadre de votre pipeline CI aux côtés de conftest test.
Exercice 6 : Intégrer Conftest dans GitHub Actions
Avec les politiques écrites et testées, l’étape suivante est de les connecter à votre pipeline CI afin que chaque pull request soit automatiquement validée.
Étape 1 — Créer le workflow
Créez .github/workflows/policy-check.yml :
name: Kubernetes Policy Check
on:
pull_request:
paths:
- "k8s/**"
- "policy/**"
push:
branches: [main]
paths:
- "k8s/**"
- "policy/**"
jobs:
conftest:
name: Validate K8s Manifests
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Install Conftest
run: |
CONFTEST_VERSION="0.56.0"
wget -q "https://github.com/open-policy-agent/conftest/releases/download/v${CONFTEST_VERSION}/conftest_${CONFTEST_VERSION}_Linux_x86_64.tar.gz"
tar xzf "conftest_${CONFTEST_VERSION}_Linux_x86_64.tar.gz"
sudo mv conftest /usr/local/bin/
conftest --version
- name: Install OPA
run: |
OPA_VERSION="v0.68.0"
curl -L -o opa "https://openpolicyagent.org/downloads/${OPA_VERSION}/opa_linux_amd64_static"
chmod +x opa
sudo mv opa /usr/local/bin/
opa version
- name: Run policy unit tests
run: opa test policy/ -v
- name: Run Conftest against all manifests
run: |
echo "Scanning all Kubernetes manifests in k8s/ ..."
FAILED=0
for file in k8s/*.yaml; do
echo ""
echo "--- Testing: $file ---"
if ! conftest test "$file" --policy policy/; then
FAILED=1
fi
done
if [ "$FAILED" -eq 1 ]; then
echo ""
echo "One or more manifests violated policy. Fix the issues above."
exit 1
fi
echo ""
echo "All manifests passed policy checks."
Étape 2 — Observer une PR échouée
Poussez une branche contenant les manifestes non sécurisés originaux. La sortie du pipeline ressemblera à ceci :
--- Testing: k8s/deployment-latest-tag.yaml ---
FAIL - k8s/deployment-latest-tag.yaml - main - Container 'nginx' uses the ':latest' tag in image 'nginx:latest'. Pin to a specific version.
--- Testing: k8s/deployment-run-as-root.yaml ---
FAIL - k8s/deployment-run-as-root.yaml - main - Container 'nginx' must set securityContext.runAsNonRoot to true.
FAIL - k8s/deployment-run-as-root.yaml - main - Container 'nginx' must set securityContext.allowPrivilegeEscalation to false.
--- Testing: k8s/pod-host-network.yaml ---
FAIL - k8s/pod-host-network.yaml - main - Pod 'debug-pod' must not use hostNetwork: true.
One or more manifests violated policy. Fix the issues above.
Error: Process completed with exit code 1.
Le statut de la PR passe au rouge avec des messages de violation clairs qui indiquent au développeur exactement quoi corriger et où.
Étape 3 — Observer une PR réussie
Corrigez tous les manifestes comme montré dans les exercices précédents, poussez à nouveau, et le pipeline passe :
--- Testing: k8s/deployment-latest-tag.yaml ---
1 test, 1 passed, 0 warnings, 0 failures
--- Testing: k8s/deployment-run-as-root.yaml ---
1 test, 1 passed, 0 warnings, 0 failures
--- Testing: k8s/deployment-no-limits.yaml ---
1 test, 1 passed, 0 warnings, 0 failures
All manifests passed policy checks.
Exercice 7 : Intégrer Conftest dans GitLab CI
Si votre équipe utilise GitLab, l’intégration est tout aussi simple. Ajoutez le job suivant à votre .gitlab-ci.yml :
Configuration complète fonctionnelle
stages:
- validate
conftest-policy-check:
stage: validate
image: alpine:3.19
variables:
CONFTEST_VERSION: "0.56.0"
OPA_VERSION: "v0.68.0"
before_script:
- apk add --no-cache curl wget tar
- wget -q "https://github.com/open-policy-agent/conftest/releases/download/v${CONFTEST_VERSION}/conftest_${CONFTEST_VERSION}_Linux_x86_64.tar.gz"
- tar xzf "conftest_${CONFTEST_VERSION}_Linux_x86_64.tar.gz"
- mv conftest /usr/local/bin/
- curl -L -o /usr/local/bin/opa "https://openpolicyagent.org/downloads/${OPA_VERSION}/opa_linux_amd64_static"
- chmod +x /usr/local/bin/opa
script:
- echo "Running policy unit tests..."
- opa test policy/ -v
- echo "Running Conftest against all manifests..."
- |
FAILED=0
for file in k8s/*.yaml; do
echo ""
echo "--- Testing: $file ---"
if ! conftest test "$file" --policy policy/; then
FAILED=1
fi
done
if [ "$FAILED" -eq 1 ]; then
echo ""
echo "One or more manifests violated policy."
exit 1
fi
echo ""
echo "All manifests passed policy checks."
rules:
- changes:
- k8s/**/*
- policy/**/*
when: always
Comportement réussite/échec
Le comportement est identique au workflow GitHub Actions. Lorsque des manifestes non sécurisés sont présents, le job échoue avec des messages de violation. Lorsque tous les manifestes sont conformes, le job passe avec un résumé propre. Le bloc rules garantit que le job ne s’exécute que lorsque les manifestes Kubernetes ou les fichiers de politique changent, minimisant ainsi le temps d’exécution du pipeline.
Avancé : Avertissements vs. refus
Toutes les violations de politique ne doivent pas bloquer un déploiement. Certaines sont des recommandations — des bonnes pratiques que vous souhaitez signaler sans casser le pipeline. Conftest prend en charge cette distinction grâce aux règles warn.
Comment ça fonctionne
deny[msg]— une gate stricte. Si une règle deny se déclenche,conftest testretourne un code de sortie non nul et le pipeline échoue.warn[msg]— un avis consultatif. Le message est affiché mais le code de sortie reste zéro, donc le pipeline passe.conftest test --fail-on-warn— promeut optionnellement tous les avertissements en échecs. Utile lorsque vous souhaitez resserrer progressivement les politiques : commencez avecwarn, et une fois que les équipes ont corrigé les violations existantes, passez àdenyou activez--fail-on-warn.
Créer une politique consultative
Créez policy/recommendations.rego :
package main
warn[msg] {
input.kind == "Service"
input.spec.type == "LoadBalancer"
not input.metadata.annotations
msg := sprintf("Service '%s' is of type LoadBalancer with no annotations. Consider adding cloud-provider-specific annotations for internal load balancers.", [input.metadata.name])
}
warn[msg] {
input.kind == "Deployment"
container := input.spec.template.spec.containers[_]
not container.readinessProbe
msg := sprintf("Container '%s' has no readinessProbe. Add one so Kubernetes can route traffic only to healthy pods.", [container.name])
}
warn[msg] {
input.kind == "Deployment"
container := input.spec.template.spec.containers[_]
not container.livenessProbe
msg := sprintf("Container '%s' has no livenessProbe. Add one so Kubernetes can restart unhealthy pods.", [container.name])
}
Tester la politique consultative
conftest test k8s/service-loadbalancer.yaml
Sortie attendue :
WARN - k8s/service-loadbalancer.yaml - main - Service 'web-lb' is of type LoadBalancer with no annotations. Consider adding cloud-provider-specific annotations for internal load balancers.
1 test, 1 passed, 1 warning, 0 failures
Remarque : le code de sortie est 0 — le pipeline passe toujours. Si vous souhaitez appliquer les avertissements :
conftest test k8s/service-loadbalancer.yaml --fail-on-warn
Maintenant le code de sortie est 1 et le pipeline échouerait.
Ce modèle vous permet de déployer de nouvelles politiques progressivement : introduisez-les comme avertissements, laissez aux équipes le temps de remédier, puis promouvez-les en refus.
Nettoyage
Lorsque vous avez terminé le lab, supprimez les ressources de test :
# Supprimer le répertoire du lab
rm -rf conftest-k8s-lab
# Si vous avez déployé des manifestes corrigés sur un cluster de test
kubectl delete -f k8s/ --ignore-not-found
# Si vous avez créé un cluster kind pour ce lab
kind delete cluster --name conftest-lab
Points clés à retenir
- Décalez la sécurité vers la gauche de manière agressive. Détecter un manifeste mal configuré dans une pull request coûte des ordres de grandeur moins cher que de le découvrir après une brèche.
- Conftest + Rego est un point d’entrée léger vers le policy-as-code. Vous n’avez pas besoin d’un serveur OPA complet ni d’une installation Gatekeeper pour commencer à appliquer des politiques — un simple binaire CLI et quelques fichiers Rego suffisent.
- Testez vos politiques comme du code applicatif. Utilisez
opa testavec des cas de test explicites positifs et négatifs pour prévenir les régressions dans votre bibliothèque de règles. - Utilisez les avertissements pour un déploiement progressif. Commencez les nouvelles politiques comme règles
warn, partagez-les avec l’équipe, et promouvez-les endenyune fois les violations existantes résolues. - Des messages d’erreur exploitables sont essentiels. Utilisez
sprintfdans chaque règle pour indiquer au développeur quel conteneur, quel champ et quoi faire. Des messages génériques « politique violée » érodent la confiance dans les gates CI. - Conservez les politiques dans le même dépôt que les manifestes. Co-localiser
policy/aveck8s/signifie que les changements de politique passent par le même processus de revue que les changements d’infrastructure.
Prochaines étapes
Maintenant que vous disposez d’un pipeline Conftest fonctionnel, continuez à développer votre pratique de policy-as-code :
- Policy as Code pour CI/CD : OPA et Rego — approfondissez le langage Rego, découvrez les imports de données, la gestion des bundles et la journalisation des décisions pour les pistes d’audit.
- Patterns défensifs et atténuations — explorez le paysage plus large du durcissement des pipelines CI/CD, de la gestion des secrets à la signature des artefacts et l’application en runtime.