
Cheatsheet sécurité CKS. Examen 100% pratique orienté sécurisation de clusters Kubernetes. Cette page couvre les commandes essentielles, organisées par domaine d'examen.
Tableau "besoin → commande"
Section intitulée « Tableau "besoin → commande" »Ce tableau est le point d'entrée à consulter pendant l'épreuve : vous partez de l'énoncé, vous y trouvez la commande, les sections suivantes en donnent la variante complète. Le k des exemples est l'alias de kubectl défini juste après. Toutes les commandes supposent que vous avez déjà basculé sur le bon cluster.
| Besoin | Commande |
|---|---|
| Bloquer tout trafic entrant | k apply -f deny-all-ingress.yaml |
| Créer un Role pour lire les pods | k create role pod-reader --verb=get,list --resource=pods |
| Tester les permissions d'un SA | k auth can-i get pods --as=system:serviceaccount:ns:sa |
| Appliquer PSA restricted | k label ns <ns> pod-security.kubernetes.io/enforce=restricted |
| Scanner une image | trivy image --severity CRITICAL,HIGH nginx:1.25 |
| Audit CIS Benchmark | kube-bench run --targets master |
| Voir les audit logs | cat /var/log/kubernetes/audit.log | jq 'select(.verb=="create")' |
| Identifier les alertes Falco | tail -f /var/log/falco/falco.log |
| Vérifier le chiffrement etcd | cat /etc/kubernetes/manifests/kube-apiserver.yaml | grep encryption |
| Obtenir le digest d'une image | skopeo inspect docker://nginx:1.25 | jq -r '.Digest' |
Bootstrap de session
Section intitulée « Bootstrap de session »Ces quatre lignes se tapent en trente secondes au début de l'examen et se rentabilisent sur chaque question. La variable $do abrège --dry-run=client -o yaml, la combinaison qui génère un manifeste sans rien créer : c'est le raccourci le plus rentable de l'épreuve, puisque presque toutes les ressources de sécurité se rédigent en YAML. L'autocomplétion doit être rattachée explicitement à l'alias, sinon elle ne fonctionne que sur kubectl.
# Alias fondamentauxalias k=kubectlalias kn='kubectl config set-context --current --namespace'export do="--dry-run=client -o yaml"
# Autocomplétionsource <(kubectl completion bash)complete -F __start_kubectl k
# Vérifier le contextekubectl config current-contextCluster Setup (15%)
Section intitulée « Cluster Setup (15%) »Ce domaine porte sur la configuration initiale du cluster : isolation réseau, conformité aux référentiels et exposition en TLS. Les NetworkPolicy y occupent la place principale, avec une logique constante à l'examen : refuser tout par défaut, puis rouvrir précisément ce dont l'application a besoin. Attention, une NetworkPolicy n'a d'effet que si le plugin réseau du cluster les implémente.
NetworkPolicy deny-all Ingress
Section intitulée « NetworkPolicy deny-all Ingress »Ce manifeste sélectionne tous les Pods du namespace et ne déclare aucune règle entrante, ce qui coupe tout trafic reçu.
apiVersion: networking.k8s.io/v1kind: NetworkPolicymetadata: name: deny-all-ingress namespace: productionspec: podSelector: {} policyTypes: - IngressBut : Bloquer tout trafic entrant vers les Pods du namespace.
Piège : podSelector: {} = tous les Pods. Sans règle ingress, tout est bloqué.
NetworkPolicy deny-all Egress
Section intitulée « NetworkPolicy deny-all Egress »Même principe dans l'autre sens : les Pods ne peuvent plus initier aucune connexion sortante, y compris vers les services internes du cluster.
apiVersion: networking.k8s.io/v1kind: NetworkPolicymetadata: name: deny-all-egress namespace: productionspec: podSelector: {} policyTypes: - EgressPiège : Bloque aussi le DNS ! Ajoutez une règle autorisant le port 53 vers CoreDNS si nécessaire.
NetworkPolicy autoriser un flux précis
Section intitulée « NetworkPolicy autoriser un flux précis »Une fois le refus global posé, chaque flux légitime se rouvre par une politique dédiée : ici, seuls les Pods portant le label app: frontend atteignent le port 8080 du backend.
apiVersion: networking.k8s.io/v1kind: NetworkPolicymetadata: name: allow-frontend-to-backend namespace: productionspec: podSelector: matchLabels: app: backend policyTypes: - Ingress ingress: - from: - podSelector: matchLabels: app: frontend ports: - protocol: TCP port: 8080kube-bench, Audit CIS
Section intitulée « kube-bench, Audit CIS »kube-bench exécute les contrôles du CIS Benchmark sur un nœud et classe chaque test en PASS, FAIL ou WARN. La cible s'indique explicitement, les contrôles du control plane et ceux des nœuds de travail étant différents.
kube-bench run --targets masterkube-bench run --targets nodekube-bench run --json > cis-results.jsonBut : Vérifier la conformité CIS Benchmark.
Piège : Les résultats FAIL nécessitent souvent d'éditer /etc/kubernetes/manifests/*.yaml.
TLS Ingress, Secret
Section intitulée « TLS Ingress, Secret »Un Secret de type tls attend exactement deux clés, tls.crt et tls.key : c'est le format qu'exigent les Ingress pour terminer le chiffrement.
openssl req -x509 -nodes -days 365 -newkey rsa:2048 \ -keyout tls.key -out tls.crt -subj "/CN=app.example.com"
kubectl create secret tls app-tls --cert=tls.crt --key=tls.key -n productionCluster Hardening (15%)
Section intitulée « Cluster Hardening (15%) »Ce domaine traite du durcissement de l'accès à l'API Kubernetes : qui peut faire quoi, avec quelle identité, et ce que les Pods eux-mêmes sont autorisés à demander. Le RBAC en constitue l'essentiel, complété par la maîtrise des ServiceAccounts et par la vérification des paramètres de démarrage de l'API server. La règle qui guide toutes les réponses est le moindre privilège.
RBAC, Créer un Role
Section intitulée « RBAC, Créer un Role »Un Role accorde des verbes sur des ressources dans un seul namespace ; sa contrepartie ClusterRole porte les mêmes droits à l'échelle du cluster et couvre les ressources sans namespace comme les nœuds.
k create role pod-reader --verb=get,list,watch --resource=pods -n productionk create clusterrole node-viewer --verb=get,list --resource=nodesBut : Limiter les permissions au strict nécessaire.
Piège : Role = namespaced, ClusterRole = cluster-scoped.
RBAC, Lier à un ServiceAccount
Section intitulée « RBAC, Lier à un ServiceAccount »Un rôle ne produit aucun effet tant qu'aucune liaison ne le rattache à une identité : c'est le RoleBinding qui active réellement les permissions.
k create rolebinding app-pod-reader \ --role=pod-reader \ --serviceaccount=production:app-sa \ -n productionFormat SA : namespace:nom
RBAC, Tester les permissions
Section intitulée « RBAC, Tester les permissions »L'option --as fait évaluer la requête par l'API server sous une autre identité, sans avoir à récupérer le moindre token : c'est la façon la plus rapide de prouver qu'une correction RBAC a bien pris effet.
k auth can-i create pods -n production --as=system:serviceaccount:production:app-sak auth can-i --list --as=system:serviceaccount:production:app-sa -n productionBut : Vérifier qu'un SA n'a pas trop de droits.
Piège : Toujours tester avec --as= avant de valider.
Identifier les permissions excessives
Section intitulée « Identifier les permissions excessives »Le rôle cluster-admin donne tous les droits sur tout : lister ses bénéficiaires est le premier audit à mener sur un cluster inconnu.
# Qui a cluster-admin ?kubectl get clusterrolebindings -o json | \ jq '.items[] | select(.roleRef.name=="cluster-admin") | {name: .metadata.name, subjects: .subjects}'ServiceAccount, Désactiver automount
Section intitulée « ServiceAccount, Désactiver automount »Kubernetes monte par défaut un jeton d'accès à l'API dans chaque Pod : une application qui n'appelle pas l'API n'en a pas besoin, et ce jeton devient une clé offerte à qui obtient un shell dans le conteneur.
apiVersion: v1kind: Podmetadata: name: secure-podspec: serviceAccountName: restricted-sa automountServiceAccountToken: false containers: - name: app image: nginx:1.25@sha256:a484819eb60211f5299034ac80f6a681b06f89e65866ce91f356ed7c72af059cBut : Empêcher le Pod d'appeler l'API Kubernetes. Piège : Par défaut, le token est monté automatiquement.
API Server, Vérifier les flags
Section intitulée « API Server, Vérifier les flags »L'API server tourne en Pod statique : sa configuration se lit et se corrige directement dans le manifeste du nœud, et le kubelet le redémarre dès que le fichier change.
cat /etc/kubernetes/manifests/kube-apiserver.yaml | grep -E "anonymous-auth|authorization-mode|admission-plugins"Flags sécurisés :
--anonymous-auth=false--authorization-mode=Node,RBAC--enable-admission-plugins=NodeRestriction
System Hardening (10%)
Section intitulée « System Hardening (10%) »Ce domaine descend au niveau du noyau : il s'agit de restreindre ce qu'un conteneur peut demander au système d'exploitation. Seccomp filtre les appels système, AppArmor restreint l'accès aux fichiers et aux capacités, le securityContext rassemble les garde-fous Kubernetes. Ces trois mécanismes se cumulent et n'ont aucune valeur par défaut protectrice : rien n'est appliqué tant que vous ne le déclarez pas.
Seccomp, RuntimeDefault
Section intitulée « Seccomp, RuntimeDefault »Le profil RuntimeDefault applique la liste d'appels système bloqués fournie par le moteur de conteneurs, ce qui couvre l'essentiel du risque sans avoir à écrire un profil sur mesure.
apiVersion: v1kind: Podmetadata: name: seccomp-podspec: securityContext: seccompProfile: type: RuntimeDefault containers: - name: app image: nginx:1.25@sha256:a484819eb60211f5299034ac80f6a681b06f89e65866ce91f356ed7c72af059cBut : Filtrer les syscalls dangereux (ptrace, mount, etc.). Piège : Sans Seccomp, le conteneur peut exécuter n'importe quel syscall.
AppArmor, Annotation
Section intitulée « AppArmor, Annotation »Le profil doit être déjà chargé sur le nœud avant la création du Pod, sinon celui-ci reste bloqué. Depuis Kubernetes 1.30, le champ appArmorProfile du securityContext remplace cette annotation, encore acceptée mais dépréciée.
apiVersion: v1kind: Podmetadata: name: apparmor-pod annotations: container.apparmor.security.beta.kubernetes.io/app: localhost/k8s-nginxspec: containers: - name: app image: nginx:1.25@sha256:a484819eb60211f5299034ac80f6a681b06f89e65866ce91f356ed7c72af059cFormat : container.apparmor.security.beta.kubernetes.io/<container-name>: localhost/<profile-name>
# Vérifier les profils chargésaa-statuscat /sys/kernel/security/apparmor/profilesSecurityContext, Pod durci complet
Section intitulée « SecurityContext, Pod durci complet »Ce manifeste réunit tout ce qu'attend le niveau restricted de Pod Security Admission : retenez-le tel quel, il sert de base à la plupart des questions de durcissement. Notez que le securityContext existe à deux étages, celui du Pod et celui du conteneur, et que les champs ne sont pas interchangeables entre les deux.
apiVersion: v1kind: Podmetadata: name: hardened-podspec: securityContext: runAsNonRoot: true runAsUser: 1000 seccompProfile: type: RuntimeDefault containers: - name: app image: nginx:1.25@sha256:a484819eb60211f5299034ac80f6a681b06f89e65866ce91f356ed7c72af059c securityContext: allowPrivilegeEscalation: false readOnlyRootFilesystem: true capabilities: drop: - ALL volumeMounts: - name: tmp mountPath: /tmp volumes: - name: tmp emptyDir: {}Champs à mémoriser :
runAsNonRoot: trueallowPrivilegeEscalation: falsereadOnlyRootFilesystem: truecapabilities.drop: ["ALL"]
Identifier les Pods à risque
Section intitulée « Identifier les Pods à risque »Un conteneur privileged dispose de presque tous les droits du nœud : c'est le premier motif à rechercher quand un énoncé demande de repérer les Pods dangereux d'un cluster.
# Pods privilégiéskubectl get pods -A -o json | jq '.items[] | select(.spec.containers[].securityContext.privileged == true) | .metadata.namespace + "/" + .metadata.name'
# Pods rootkubectl get pods -A -o json | jq '.items[] | select(.spec.securityContext.runAsNonRoot != true) | .metadata.namespace + "/" + .metadata.name'Minimize Vulnerabilities (20%)
Section intitulée « Minimize Vulnerabilities (20%) »Ce domaine consiste à empêcher qu'une configuration dangereuse entre dans le cluster, plutôt qu'à la corriger après coup. Pod Security Admission est le mécanisme natif qui refuse les Pods non conformes à l'admission, OPA Gatekeeper couvre les règles que PSA ne sait pas exprimer, et le chiffrement d'etcd protège les Secrets stockés. Ces contrôles s'appliquent au moment de la création : ils ne modifient pas les ressources déjà en place.
Pod Security Admission, Appliquer restricted
Section intitulée « Pod Security Admission, Appliquer restricted »PSA se pilote uniquement par des labels de namespace : aucune ressource supplémentaire à créer, et l'étiquette de version fige le référentiel appliqué.
kubectl label namespace production \ pod-security.kubernetes.io/enforce=restricted \ pod-security.kubernetes.io/enforce-version=latestNiveaux : privileged (aucune restriction) < baseline < restricted (strict).
Modes : enforce (bloque), warn (avertit), audit (log).
Tester un Pod non conforme
Section intitulée « Tester un Pod non conforme »Le --dry-run=server soumet la demande à l'API server et déclenche donc réellement les contrôles d'admission, contrairement au --dry-run=client qui s'arrête sur votre poste.
kubectl run test --image=nginx -n production --dry-run=serverSortie si enforce=restricted : Error from server (Forbidden): violates PodSecurity "restricted:latest"
Secrets, Chiffrement etcd
Section intitulée « Secrets, Chiffrement etcd »Le chiffrement au repos s'active par un fichier EncryptionConfiguration référencé dans les paramètres de l'API server ; sans lui, quiconque lit la base etcd lit vos Secrets.
# Vérifier si configurécat /etc/kubernetes/manifests/kube-apiserver.yaml | grep encryptioncat /etc/kubernetes/enc/enc.yamlPiège : Les Secrets sont encodés base64 par défaut, pas chiffrés !
OPA Gatekeeper, Contraintes
Section intitulée « OPA Gatekeeper, Contraintes »Gatekeeper fonctionne en deux temps : un ConstraintTemplate définit la règle en langage Rego, une Constraint l'applique à un périmètre donné. Ce sont ces contraintes que la commande ci-dessous liste.
kubectl get constraintskubectl describe constraint no-latest-tagBut : Politiques d'admission personnalisées (bloquer tag latest, etc.).
Supply Chain Security (20%)
Section intitulée « Supply Chain Security (20%) »Ce domaine porte sur ce que vous déployez, avant même que cela tourne : d'où vient l'image, quelles vulnérabilités elle contient, et comment garantir que celle qui démarre est bien celle que vous avez auditée. Trivy répond à la deuxième question, le digest à la troisième. Les questions d'examen combinent presque toujours un scan et une correction de manifeste.
Trivy, Scanner une image
Section intitulée « Trivy, Scanner une image »Le scan lit les paquets installés dans l'image et les compare aux bases de vulnérabilités publiques ; filtrer sur les sévérités hautes évite de se noyer dans des centaines de résultats sans portée pratique.
trivy image nginx:1.25trivy image --severity CRITICAL,HIGH nginx:1.25trivy image --ignore-unfixed nginx:1.25trivy image --format json -o results.json nginx:1.25But : Détecter les CVE avant déploiement.
Piège : --ignore-unfixed = montre uniquement les CVE avec patch disponible.
Trivy, Scanner le cluster
Section intitulée « Trivy, Scanner le cluster »Le mode k8s interroge l'API pour découvrir les images en cours d'exécution et les analyse toutes, ce qui donne une vue d'ensemble sans lister les Pods à la main.
trivy k8s --report summary clusterScanner toutes les images du cluster
Section intitulée « Scanner toutes les images du cluster »Cette boucle est la solution de repli quand trivy k8s n'est pas disponible : le sort -u évite de scanner dix fois la même image partagée par plusieurs Pods.
for img in $(kubectl get pods -A -o jsonpath='{.items[*].spec.containers[*].image}' | tr ' ' '\n' | sort -u); do echo "=== $img ===" trivy image --severity CRITICAL --quiet "$img"doneImage digest, Obtenir le hash
Section intitulée « Image digest, Obtenir le hash »Un tag peut être réattribué à tout moment à un contenu différent ; le digest est l'empreinte du contenu lui-même et désigne donc toujours la même image.
skopeo inspect docker://nginx:1.25 | jq -r '.Digest'Usage dans un Pod :
image: nginx@sha256:6db391d1c0cfb30588ba0bf72ea999404f2764febf0f1f196acd5867ac7efa7eBut : Image immuable, protège contre le tag poisoning.
Identifier les Pods sans digest
Section intitulée « Identifier les Pods sans digest »L'expression régulière retient les images dont la référence se termine par un tag sans @sha256, autrement dit celles dont le contenu peut changer sous vos pieds.
kubectl get pods -A -o json | jq '.items[] | select(.spec.containers[].image | test(":[^@]+$")) | .metadata.namespace + "/" + .metadata.name'Runtime Security (20%)
Section intitulée « Runtime Security (20%) »Ce dernier domaine porte sur ce qui se passe une fois les conteneurs démarrés : tracer les appels à l'API, détecter les comportements anormaux et réduire la surface d'attaque à l'exécution. Les audit logs répondent à la question « qui a fait quoi », Falco à la question « que se passe-t-il d'anormal maintenant ». Les deux se lisent depuis le nœud, pas depuis kubectl.
Audit logs, Analyser
Section intitulée « Audit logs, Analyser »Chaque ligne du journal d'audit est un objet JSON complet : jq est donc l'outil de lecture, et le filtre porte selon les cas sur le verbe, l'utilisateur ou le code de réponse.
# Créations de Secretscat /var/log/kubernetes/audit.log | jq 'select(.verb=="create" and .objectRef.resource=="secrets")'
# Actions d'un utilisateurcat /var/log/kubernetes/audit.log | jq 'select(.user.username=="suspect")'
# Échecscat /var/log/kubernetes/audit.log | jq 'select(.responseStatus.code >= 400)'But : Investigation post-incident.
Créer une policy d'audit
Section intitulée « Créer une policy d'audit »Les règles sont évaluées dans l'ordre et la première qui correspond fixe le niveau retenu : l'ordre des entrées fait donc partie de la réponse attendue, une exclusion placée après une règle plus large n'a aucun effet.
apiVersion: audit.k8s.io/v1kind: Policyrules:- level: Metadata resources: - group: "" resources: ["secrets"]- level: RequestResponse resources: - group: "" resources: ["pods"]- level: None users: ["system:kube-proxy"]Niveaux : None < Metadata < Request < RequestResponse
Falco, Lire les alertes
Section intitulée « Falco, Lire les alertes »Falco surveille les appels système du nœud et déclenche une alerte dès qu'un comportement correspond à l'une de ses règles ; il rapporte, il ne bloque pas.
tail -f /var/log/falco/falco.loggrep "Critical\|Error\|Warning" /var/log/falco/falco.logsystemctl status falcoBut : Détection runtime (shell dans conteneur, accès fichiers sensibles, etc.).
Immuabilité, Filesystem lecture seule
Section intitulée « Immuabilité, Filesystem lecture seule »Un système de fichiers en lecture seule empêche l'installation d'un outil ou la modification d'un binaire après une intrusion ; il faut en contrepartie ouvrir explicitement les répertoires temporaires dont l'application a besoin.
securityContext: readOnlyRootFilesystem: truevolumeMounts:- name: tmp mountPath: /tmpvolumes:- name: tmp emptyDir: {}But : L'attaquant ne peut pas modifier les binaires.
Piège : L'application doit écrire quelque part → ajouter des volumes emptyDir.
Commandes à connaître par cœur
Section intitulée « Commandes à connaître par cœur »Ces commandes doivent sortir sans réflexion : chaque hésitation coûte des minutes sur une épreuve de deux heures. Elles sont volontairement écrites en forme impérative, sans passer par un fichier YAML, car c'est la manière la plus rapide de créer une ressource simple. Les valeurs entre chevrons sont à remplacer par celles de l'énoncé.
# NetworkPolicyk get netpol -Ak apply -f deny-all.yaml
# RBACk create role <name> --verb=get,list --resource=podsk create rolebinding <name> --role=<role> --serviceaccount=<ns>:<sa>k auth can-i <verb> <resource> --as=system:serviceaccount:<ns>:<sa>
# PSAk label ns <ns> pod-security.kubernetes.io/enforce=restricted
# Scantrivy image --severity CRITICAL,HIGH <image>kube-bench run --targets master
# Audit/Falcocat /var/log/kubernetes/audit.log | jq '...'tail -f /var/log/falco/falco.log
# AppArmor/Seccompaa-statusls /var/lib/kubelet/seccomp/À retrouver vite dans la doc
Section intitulée « À retrouver vite dans la doc »Ne perdez pas de temps à mémoriser, utilisez kubectl explain :
- Syntaxe complète NetworkPolicy (
k explain networkpolicy.spec) - Champs securityContext (
k explain pod.spec.securityContext) - Format EncryptionConfiguration etcd
- Options ImagePolicyWebhook
- Règles Falco personnalisées
- Policy Gatekeeper ConstraintTemplate (syntaxe Rego)
Dépannage rapide
Section intitulée « Dépannage rapide »Ces six situations reviennent en salle d'examen et ont toutes la même cause profonde : une configuration acceptée par l'API mais sans effet réel. La commande indiquée sert à le confirmer en quelques secondes, avant de repartir sur une fausse piste.
| Problème | Commande |
|---|---|
| Pod rejeté par PSA | k run test --dry-run=server |
| NetworkPolicy inactive | k describe netpol <name> |
| Audit logs vides | grep audit /etc/kubernetes/manifests/kube-apiserver.yaml |
| Falco silencieux | systemctl status falco |
| API Server down | journalctl -u kubelet + vérifier YAML |
| kube-bench FAIL | kube-bench run --targets master |
À retenir
Section intitulée « À retenir »- NetworkPolicies : deny-all d'abord, whitelist ensuite
- RBAC : moindre privilège, jamais cluster-admin sauf pour admins
- ServiceAccounts :
automountServiceAccountToken: falsepar défaut - SecurityContext :
drop ALL,readOnlyRootFilesystem: true,runAsNonRoot: true - PSA :
restrictedsur les namespaces de production - Secrets : chiffrement etcd obligatoire, RBAC strict
- Trivy : scanner CRITICAL/HIGH avant déploiement
- Digests : utiliser
@sha256:...pour images immuables - Audit logs : tracer les actions pour investigation
- Falco : surveiller les alertes runtime en continu
Pour aller plus loin
Section intitulée « Pour aller plus loin »- Sécuriser : durcissement, supply chain, détection : Élargir au-delà de Kubernetes ce que la CKS vous a fait travailler.
- Examens et quiz : Mesurer vos acquis sur l'ensemble des domaines du site.