Aller au contenu
Conteneurs & Orchestration medium
🔐 Alerte sécurité — Incident supply chain Trivy : lire mon analyse de l'attaque

Cheatsheet CKS : commandes sécurité Kubernetes

11 min de lecture

logo kubernetes

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.

BesoinCommande
Bloquer tout trafic entrantk apply -f deny-all-ingress.yaml
Créer un Role pour lire les podsk create role pod-reader --verb=get,list --resource=pods
Tester les permissions d’un SAk auth can-i get pods --as=system:serviceaccount:ns:sa
Appliquer PSA restrictedk label ns <ns> pod-security.kubernetes.io/enforce=restricted
Scanner une imagetrivy image --severity CRITICAL,HIGH nginx:1.25
Audit CIS Benchmarkkube-bench run --targets master
Voir les audit logscat /var/log/kubernetes/audit.log | jq 'select(.verb=="create")'
Identifier les alertes Falcotail -f /var/log/falco/falco.log
Vérifier le chiffrement etcdcat /etc/kubernetes/manifests/kube-apiserver.yaml | grep encryption
Obtenir le digest d’une imageskopeo inspect docker://nginx:1.25 | jq -r '.Digest'
Fenêtre de terminal
# Alias fondamentaux
alias k=kubectl
alias kn='kubectl config set-context --current --namespace'
export do="--dry-run=client -o yaml"
# Autocomplétion
source <(kubectl completion bash)
complete -F __start_kubectl k
# Vérifier le contexte
kubectl config current-context

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: deny-all-ingress
namespace: production
spec:
podSelector: {}
policyTypes:
- Ingress

But : Bloquer tout trafic entrant vers les Pods du namespace. Piège : podSelector: {} = tous les Pods. Sans règle ingress, tout est bloqué.

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: deny-all-egress
namespace: production
spec:
podSelector: {}
policyTypes:
- Egress

Piège : Bloque aussi le DNS ! Ajoutez une règle autorisant le port 53 vers CoreDNS si nécessaire.

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-frontend-to-backend
namespace: production
spec:
podSelector:
matchLabels:
app: backend
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
app: frontend
ports:
- protocol: TCP
port: 8080
Fenêtre de terminal
kube-bench run --targets master
kube-bench run --targets node
kube-bench run --json > cis-results.json

But : Vérifier la conformité CIS Benchmark. Piège : Les résultats FAIL nécessitent souvent d’éditer /etc/kubernetes/manifests/*.yaml.

Fenêtre de terminal
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 production

Fenêtre de terminal
k create role pod-reader --verb=get,list,watch --resource=pods -n production
k create clusterrole node-viewer --verb=get,list --resource=nodes

But : Limiter les permissions au strict nécessaire. Piège : Role = namespaced, ClusterRole = cluster-scoped.

Fenêtre de terminal
k create rolebinding app-pod-reader \
--role=pod-reader \
--serviceaccount=production:app-sa \
-n production

Format SA : namespace:nom

Fenêtre de terminal
k auth can-i create pods -n production --as=system:serviceaccount:production:app-sa
k auth can-i --list --as=system:serviceaccount:production:app-sa -n production

But : Vérifier qu’un SA n’a pas trop de droits. Piège : Toujours tester avec --as= avant de valider.

Fenêtre de terminal
# Qui a cluster-admin ?
kubectl get clusterrolebindings -o json | \
jq '.items[] | select(.roleRef.name=="cluster-admin") | {name: .metadata.name, subjects: .subjects}'
apiVersion: v1
kind: Pod
metadata:
name: secure-pod
spec:
serviceAccountName: restricted-sa
automountServiceAccountToken: false
containers:
- name: app
image: nginx:1.25

But : Empêcher le Pod d’appeler l’API Kubernetes. Piège : Par défaut, le token est monté automatiquement.

Fenêtre de terminal
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

apiVersion: v1
kind: Pod
metadata:
name: seccomp-pod
spec:
securityContext:
seccompProfile:
type: RuntimeDefault
containers:
- name: app
image: nginx:1.25

But : Filtrer les syscalls dangereux (ptrace, mount, etc.). Piège : Sans Seccomp, le conteneur peut exécuter n’importe quel syscall.

apiVersion: v1
kind: Pod
metadata:
name: apparmor-pod
annotations:
container.apparmor.security.beta.kubernetes.io/app: localhost/k8s-nginx
spec:
containers:
- name: app
image: nginx:1.25

Format : container.apparmor.security.beta.kubernetes.io/<container-name>: localhost/<profile-name>

Fenêtre de terminal
# Vérifier les profils chargés
aa-status
cat /sys/kernel/security/apparmor/profiles
apiVersion: v1
kind: Pod
metadata:
name: hardened-pod
spec:
securityContext:
runAsNonRoot: true
runAsUser: 1000
seccompProfile:
type: RuntimeDefault
containers:
- name: app
image: nginx:1.25
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop:
- ALL
volumeMounts:
- name: tmp
mountPath: /tmp
volumes:
- name: tmp
emptyDir: {}

Champs à mémoriser :

  • runAsNonRoot: true
  • allowPrivilegeEscalation: false
  • readOnlyRootFilesystem: true
  • capabilities.drop: ["ALL"]
Fenêtre de terminal
# Pods privilégiés
kubectl get pods -A -o json | jq '.items[] | select(.spec.containers[].securityContext.privileged == true) | .metadata.namespace + "/" + .metadata.name'
# Pods root
kubectl get pods -A -o json | jq '.items[] | select(.spec.securityContext.runAsNonRoot != true) | .metadata.namespace + "/" + .metadata.name'

Fenêtre de terminal
kubectl label namespace production \
pod-security.kubernetes.io/enforce=restricted \
pod-security.kubernetes.io/enforce-version=latest

Niveaux : privileged (aucune restriction) < baseline < restricted (strict). Modes : enforce (bloque), warn (avertit), audit (log).

Fenêtre de terminal
kubectl run test --image=nginx -n production --dry-run=server

Sortie si enforce=restricted : Error from server (Forbidden): violates PodSecurity "restricted:latest"

Fenêtre de terminal
# Vérifier si configuré
cat /etc/kubernetes/manifests/kube-apiserver.yaml | grep encryption
cat /etc/kubernetes/enc/enc.yaml

Piège : Les Secrets sont encodés base64 par défaut, pas chiffrés !

Fenêtre de terminal
kubectl get constraints
kubectl describe constraint no-latest-tag

But : Politiques d’admission personnalisées (bloquer tag latest, etc.).


Fenêtre de terminal
trivy image nginx:1.25
trivy image --severity CRITICAL,HIGH nginx:1.25
trivy image --ignore-unfixed nginx:1.25
trivy image --format json -o results.json nginx:1.25

But : Détecter les CVE avant déploiement. Piège : --ignore-unfixed = montre uniquement les CVE avec patch disponible.

Fenêtre de terminal
trivy k8s --report summary cluster
Fenêtre de terminal
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"
done
Fenêtre de terminal
skopeo inspect docker://nginx:1.25 | jq -r '.Digest'

Usage dans un Pod :

image: nginx@sha256:6db391d1c0cfb30588ba0bf72ea999404f2764febf0f1f196acd5867ac7efa7e

But : Image immuable, protège contre le tag poisoning.

Fenêtre de terminal
kubectl get pods -A -o json | jq '.items[] | select(.spec.containers[].image | test(":[^@]+$")) | .metadata.namespace + "/" + .metadata.name'

Fenêtre de terminal
# Créations de Secrets
cat /var/log/kubernetes/audit.log | jq 'select(.verb=="create" and .objectRef.resource=="secrets")'
# Actions d'un utilisateur
cat /var/log/kubernetes/audit.log | jq 'select(.user.username=="suspect")'
# Échecs
cat /var/log/kubernetes/audit.log | jq 'select(.responseStatus.code >= 400)'

But : Investigation post-incident.

/etc/kubernetes/audit-policy.yaml
apiVersion: audit.k8s.io/v1
kind: Policy
rules:
- level: Metadata
resources:
- group: ""
resources: ["secrets"]
- level: RequestResponse
resources:
- group: ""
resources: ["pods"]
- level: None
users: ["system:kube-proxy"]

Niveaux : None < Metadata < Request < RequestResponse

Fenêtre de terminal
tail -f /var/log/falco/falco.log
grep "Critical\|Error\|Warning" /var/log/falco/falco.log
systemctl status falco

But : Détection runtime (shell dans conteneur, accès fichiers sensibles, etc.).

securityContext:
readOnlyRootFilesystem: true
volumeMounts:
- name: tmp
mountPath: /tmp
volumes:
- name: tmp
emptyDir: {}

But : L’attaquant ne peut pas modifier les binaires. Piège : L’application doit écrire quelque part → ajouter des volumes emptyDir.


Fenêtre de terminal
# NetworkPolicy
k get netpol -A
k apply -f deny-all.yaml
# RBAC
k create role <name> --verb=get,list --resource=pods
k create rolebinding <name> --role=<role> --serviceaccount=<ns>:<sa>
k auth can-i <verb> <resource> --as=system:serviceaccount:<ns>:<sa>
# PSA
k label ns <ns> pod-security.kubernetes.io/enforce=restricted
# Scan
trivy image --severity CRITICAL,HIGH <image>
kube-bench run --targets master
# Audit/Falco
cat /var/log/kubernetes/audit.log | jq '...'
tail -f /var/log/falco/falco.log
# AppArmor/Seccomp
aa-status
ls /var/lib/kubelet/seccomp/

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)

ProblèmeCommande
Pod rejeté par PSAk run test --dry-run=server
NetworkPolicy inactivek describe netpol <name>
Audit logs videsgrep audit /etc/kubernetes/manifests/kube-apiserver.yaml
Falco silencieuxsystemctl status falco
API Server downjournalctl -u kubelet + vérifier YAML
kube-bench FAILkube-bench run --targets master

  1. NetworkPolicies : deny-all d’abord, whitelist ensuite
  2. RBAC : moindre privilège, jamais cluster-admin sauf pour admins
  3. ServiceAccounts : automountServiceAccountToken: false par défaut
  4. SecurityContext : drop ALL, readOnlyRootFilesystem: true, runAsNonRoot: true
  5. PSA : restricted sur les namespaces de production
  6. Secrets : chiffrement etcd obligatoire, RBAC strict
  7. Trivy : scanner CRITICAL/HIGH avant déploiement
  8. Digests : utiliser @sha256:... pour images immuables
  9. Audit logs : tracer les actions pour investigation
  10. Falco : surveiller les alertes runtime en continu

Ce site vous est utile ?

Sachez que moins de 1% des lecteurs soutiennent ce site.

Je maintiens +700 guides gratuits, sans pub ni tracing. Aujourd'hui, ce site ne couvre même pas mes frais d'hébergement, d'électricité, de matériel, de logiciels, mais surtout de cafés.

Un soutien régulier, même symbolique, m'aide à garder ces ressources gratuites et à continuer de produire des guides de qualité. Merci pour votre appui.

Abonnez-vous et suivez mon actualité DevSecOps sur LinkedIn