Aller au contenu
Conteneurs & Orchestration medium

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 tracking. Un soutien, même symbolique, m'aide à couvrir l'hébergement et à garder ces ressources gratuites. Merci pour votre appui.

Le formulaire ne s'affiche pas ? Ouvrir Ko-fi dans un onglet.

Abonnez-vous et suivez mon actualité DevSecOps sur LinkedIn