Aller au contenu

La CKS, la sécurité avant tout !

·17 mins
certification kubernetes kubectl docker container cks sécurité falco trivy bom nginx istio cilium
Romain Boulanger
Auteur
Romain Boulanger
Architecte Infra/Cloud avec une touche de DevSecOps
Sommaire

Mises à jour de l’article :

29 juillet 2025 : Mise à jour du contenu de l’article, notamment sur les domaines, suite à l’évolution de la certification en octobre 2024.

26 septembre 2023 : Restructuration de l’article avec l’ajout de nouvelles catégories présentes dans l’examen.

La CKS, c’est quoi ??
#

La Certified Kubernetes Security Specialist ou CKS est la troisième certification sur Kubernetes proposée par la Linux Foundation, elle a pour but d’évaluer vos connaissances sur la configuration d’un cluster et l’ensemble des bonnes pratiques associées, aussi bien au niveau de l’infrastructure que sur les objets Kubernetes, avec plusieurs grands thèmes très fortement liés à la sécurité.

Cette certification est pour moi la plus dure des trois, de par la multitude des domaines couverts, l’ensemble des outils annexes à Kubernetes à manipuler, mais aussi par le temps restreint pour terminer les différents exercices proposés.

De mon côté, que ce soit pour mon premier passage ou lors de mes re-certifications, j’ai pu terminer l’ensemble des exercices (15-17 questions) sans forcément avoir le temps de tous les tester. Une fois les questions terminées, il ne me restait environ qu’une dizaine de minutes. Coriace non ?

Pour réussir l’examen, vous devez avoir un taux de réussite de 67% sur un panel de 15 à 20 questions, c’est un point de pourcentage de plus que pour la CKA ou CKAD.

Pour ceux qui ont l’habitude de se tester sur killer.sh, j’ai trouvé la certification assez proche du niveau de ce simulateur en termes d’exigence. Même si killer.sh creuse beaucoup plus certaines parties, je vous recommanderai d’avoir un “high score” pour passer l’examen sereinement.

Les connaissances requises
#

Comme les certifications précédentes, plusieurs domaines sont évalués avec un poids associé qui représente la part de ce domaine dans l’examen.

  • Cluster setup (15%) : Mettre en place des NetworkPolicy ou CiliumNetworkPolicy, utiliser kube-bench pour sécuriser l’ensemble des composants d’un cluster, savoir créer des Ingress avec les spécificités de l’Ingress NGINX ;

  • Cluster Hardening (15%) : Modifier l’accès à l’API, mettre en place le RBAC au niveau cluster et utilisateurs, gestion des services accounts ainsi que la mise à jour du cluster sans oublier la mise à jour du cluster avec kubeadm ;

  • System Hardening (10%) : Utilisation de Seccomp ou AppArmor en utilisant des profils pour restreindre l’exécution d’un conteneur ainsi que tout ce qui est relatif au bloc securityContext ;

  • Minimize Microservice Vulnerabilities (20%) : Gérer les Secrets et les monter au sein d’un Pod, mettre en place une RuntimeClass avec gVisor et utiliser Istio ou Cilium pour chiffrer les connexions entre Pods ;

  • Supply Chain Security (20%) : Connaître les bonnes pratiques à mettre en oeuvre au sein d’un Dockerfile, mettre en place un ImagePolicyWebhook pour la vérification des images, scanner ces dernières afin de repérer les dernières failles critiques ou de générer un SBOM avec bom ou trivy ;

  • Monitoring, Logging and Runtime Security (20%) : Savoir identifier un processus qui s’exécute dans un conteneur, rendre un conteneur immuable, savoir utiliser Falco pour détecter les comportements suspects et implémenter de nouvelles macros ou règles, enfin, définir une Audit Policy pour logger les événements.

La difficulté de cette certification réside aussi dans le fait qu’il y a beaucoup d’outils tiers à connaître et à savoir manipuler.

Par exemple, vous aurez besoin de trivy pour analyser vos images et savoir ou non si celles-ci contiennent des vulnérabilités (CVE).

Lors de l’examen vous avez le droit à la documentation officielle des outils concernés, néanmoins, une maîtrise basique de l’outil est recommandée pour ne pas perdre de temps…

Inutile de préciser aussi que les domaines couverts par la CKAD et CKA vous seront toujours utiles dans l’ensemble des exercices.

L’interface de l’examen
#

Depuis mon premier passage en 2021, l’interface de l’examen a bien changée. En effet, l’examen officiel se déroule dans un environnement de bureau à distance sur une machine virtuelle Ubuntu avec comme interface graphique XFCE, c’est le cas aussi de la plateforme killer.sh dont je vous parlerai plus tard.

Cette nouvelle interface d’examen est un peu déroutante pour les utilisateurs macOS et Windows, en effet, les copier-coller sur le Terminal se font avec les touches CTRL+MAJ C et CTRL+MAJ V. De plus, j’ai remarqué qu’il y a un peu de latence au niveau des actions réalisées avec la souris ou le clavier.

Ce guide utilisateur permet de répertorier toutes les informations à connaître sur cette interface baptisée ExmanUI. À consulter obligatoirement pour éviter les mauvaises surprises !

Bonne nouvelle cependant, l’alias k pour kubectl et la complétion des commandes sont déjà configurés tout comme vim qui bénéficie qu’une configuration par défaut pour les tabulations.

Ce que j’ai utilisé au niveau des ressources
#

Si vous souhaitez passer cette certification, je ne peux que vous conseiller ce cours très complet sur la plateforme Udemy de Zeal Vora: Certified Kubernetes Security Specialist 2025. Il vous aidera à balayer l’ensemble des chapitres de l’examen avec la possibilité de reproduire les exercices sur un cluster Kubeadm à déployer chez soi.

Ce cours est bien sûr payant, pour ceux qui ne souhaitent pas passer à la caisse, le cours de Kim Wüstkamp (le créateur de killer.sh et killercoda.com) est disponible sur Youtube. Néanmoins, ce dernier n’a pas été mis à jour récemment et ne bénéficie pas des dernières évolutions de la certification.

Une fois que vous avez terminé la partie théorique, il est recommandé de s’entraîner dans un premier temps sur killercoda afin de résoudre les exercices le plus rapidement possible, n’hésitez pas à utiliser le Playground pour pratiquer sur des parties qui ne sont pas couvertes par cette plateforme, c’est le cas de la mise en place du chiffrement de Pod à Pod avec Cilium ou Istio par exemple.

Une fois l’ensemble des scénarios maîtrisés, vous pouvez vous lancer sur le simulateur killer.sh dans le but d’évaluer votre niveau, sachant que vous avez deux tentatives réunissant les mêmes exercices, et que chaque essai donne le droit à une session de 36 heures sur le simulateur.

Une fois vos deux tentatives expirées, vous pouvez toujours consulter les énoncés et les corrections associés pour reproduire les exercices sur votre cluster Kubernetes dans l’objectif d’être totalement prêt.

N’hésitez pas à faire et refaire les exercices, ainsi que les questions bonus pour couvrir un maximum de domaines. Comme dit plus haut, l’examen final est une vraie course de rapidité, il vous faudra savoir résoudre les exercices sans perdre votre temps !

Les domaines à connaître
#

Le but de cette partie est de lister l’ensemble des commandes et concepts à avoir en tête ainsi que la documentation à consulter pour gagner en rapidité lors de l’examen final.

N’hésitez pas non plus à lire les articles sur la CKAD et CKA qui contiennent aussi plein de petites astuces qui ne seront pas reprises ici.

Les domaines tels que la mise à jour d’un cluster, la gestion des rôles et permissions (RBAC) et la création et l’analyse des Secrets ne seront pas listés ici.

Avant toute chose, il est parfois demandé de modifier des fichiers présents sur les machines. N’oubliez pas de faire une copie de ce dernier en cas de mauvaise manip’ !

Cas pour le kube-apiserver :

cd /etc/kubernetes/manifests/
cp kube-apiserver.yaml ~/kube-apiserver.yaml.bak

Ingress avec TLS
#

La CKA vous a appris à manipuler un objet Ingress, la CKS va plus loin avec la mise en place d’un certificat pour ce dernier.

La documentation donne un aperçu des étapes à faire :

  • Au sein du namespace de l’Ingress, créez un Secret au format TLS : kubectl -n [namespace] create secrets tls [nom du secret] --cert=./[fichier .crt] --key=./[fichier .key].
  • Ajouter le bloc tls dans l’Ingress :
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ingress-tls
spec:
  tls:
  - hosts:
      - [host] # Doit correspondre au host au sein des "rules"
    secretName: [nom du secret]
  rules:
  [...]

Une fois la configuration appliquée, cURL est votre allié ! Il permettra d’afficher les certificats lors de l’exécution de la commande pour vérifier que c’est bien votre secret qui est utilisé lors de la requête.

curl -v https://[nom de domaine]

CertificateSigningRequest
#

Pour générer un certificat qui permettra à un utilisateur de s’authentifier à travers le kube-apiserver, il y a plusieurs étapes à suivre.

Ce lien récapitule l’ensemble des étapes à réaliser :

  • Création d’une clé privée : openssl genrsa -out myuser.key 3072
  • Génération d’un CSR : openssl req -new -key myuser.key -out myuser.csr -subj "/CN=myuser"
  • Création de l’objet au sein du cluster :
cat <<EOF | kubectl apply -f -
apiVersion: certificates.k8s.io/v1
kind: CertificateSigningRequest
metadata:
  name: myuser
spec:
  request: # À compléter avec la commande `cat myuser.csr | base64 -w0`
  signerName: kubernetes.io/kube-apiserver-client
  expirationSeconds: 86400
  usages:
  - client auth
EOF

Dans la documentation, on vous recommande d’utiliser la commande cat myuser.csr | base64 | tr -d "\n" pour générer le contenu en base64 de request. Je trouve personnellement que la commande cat myuser.csr | base64 -w0 est un peu plus simple à retenir mais surtout plus rapide.

  • Approuver le CSR : kubectl certificate approve myuser
  • Récupération du certificat : kubectl get csr myuser -o jsonpath='{.status.certificate}'| base64 -d > myuser.crt

NetworkPolicy
#

Pour ce qui est des NetworkPolicy, je vous conseille de vous baser sur le squelette de la documentation qui permet de résoudre les différents cas d’usage (ipBlock, namespaceSelector, podSelector).

Généralement, il vous sera demandé de créer une règle qui interdit les flux aussi bien ingress que egress, rien de plus simple, la documentation fournit un exemple déjà tout préparé.

Enfin, pour tester qu’une connexion est bien bloquée vers une IP ou un domaine donné, vous pouvez utiliser cette ligne de commande :

kubectl exec pod -- curl -m 1 [IP ou nom de domaine]

Le -m ou --connect-timeout permet de définir le temps maximum avant que la requête tombe en timeout, ce qui évite d’attendre trop longtemps (surtout quand le temps est compté !).

CiliumNetworkPolicy
#

Nouveau domaine, les CiliumNetworkPolicy font maintenant parties du contenu de l’examen.

Tout comme avec les NetworkPolicy, ils vous sera demandé de restreindre les flux réseaux potentiellement sur plusieurs niveaux du modèle OSI avec différentes possibilités :

La documentation joue un rôle crucial pour récupérer et adapter les exemples en fonction de vos besoins.

De plus, il ne faut pas oublier la possibilité d’interdire des accès spécifiques grâce aux blocs ingressDeny ou egressDeny :

apiVersion: "cilium.io/v2"
kind: CiliumNetworkPolicy
metadata:
  name: "l3-rule-deny"
spec:
  endpointSelector:
    matchLabels:
      role: backend
  ingressDeny:
  - fromEndpoints:
    - matchLabels:
        role: frontend

Cette CiliumNetworkPolicy empêche les Pods avec le label role: frontend d’atteindre les Pods avec le label role: frontend.

CIS Kubernetes Benchmark avec kube-bench
#

Si vous devez durcir la configuration d’un cluster, il vous sera demandé d’utiliser la commande kube-bench qui vous fera des recommandations du CIS Kubernetes Benchmark sur les manipulations à effectuer sur différents composants afin de garantir un niveau de sécurité optimal.

Il est possible que l’on vous demande de résoudre quelques règles sur le controlplane, etcd ou un worker spécifique avec le numéro de la règle (1.1.2), la description de celle-ci ou le paramètre particulier à modifier (par exemple: Ensure that the –profiling argument is set to false).

Pour cela, vous pouvez exécuter, dans le cas du controlplane :

kube-bench run --targets master | grep "profiling" -A10
# Récupérer le numéro de la règle
kube-bench run --targets master --check [numéro de la règle]

kube-bench va charger les règles du controplane, le grep va récupérer le contenu de règle et le -A10 va afficher les dix prochaines lignes qui suivent le résultat du grep. Cela permet de récupérer le numéro de la règle associée pour avoir l’ensemble des informations, avec surtout la solution pour obtenir le fameux PASS qui permettra de venir à bout de l’exercice.

AppArmor
#

La documentation Kubernetes contient l’ensemble des informations nécessaires à la mise en place d’un profil AppArmor sur un Pod. Néanmoins, quelques commandes pratiques :

# Récupérer le nom du profil en consultant le fichier
cat [fichier du profil]

# Déplacer le profil sur le worker
scp [fichier du profil] node01:

# Se connecter sur le worker
ssh node01

# Charger le profil
apparmor_parser [fichier du profil]

# Vérifier que le profil est bien pris en compte
aa-status | grep [nom du profil]

Et voici le bloc securityContext à mettre au niveau du Pod :

securityContext:
  appArmorProfile:
    type: Localhost
    localhostProfile: [nom du profil]

Seccomp
#

Pour Seccomp, là aussi, la documentation permet d’avoir un aperçu afin de configurer un Pod.

Tout comme le appArmorProfile, le seccompProfile se place dans un securityContext, comme ceci :

apiVersion: v1
kind: Pod
metadata:
  name: audit-pod
  labels:
    app: audit-pod
spec:
  securityContext:
    seccompProfile:
      type: Localhost
      localhostProfile: [nom du profil]

Pour terminer, avant de créer votre Pod avec sa configuration Seccomp associée, il est nécessaire d’installer le profil dans le chemin /var/lib/kubelet/seccomp/profiles/ au sein de chaque nœud du cluster.

RuntimeClass
#

Pour créer une classe d’exécution spécifique, comme c’est généralement demandé pour gVisor qui permet d’isoler et de limiter les appels système, basez-vous sur ce guide.

Dans l’ordre des choses, vous devrez :

  • Installer gVisor sur les nœuds du cluster
  • Créer un objet RuntimeClass comme ceci :
kind: RuntimeClass
metadata:
  name: gvisor 
handler: runsc
  • Ajouter le champ runtimeClassName: gvisor au sein de votre Pod.

Pour tester que gVisor est correctement utilisé au sein de votre Pod, vous pouvez utiliser la commande dmseg :

$ kubectl exec sec -- dmesg
[   0.000000] Starting gVisor...
[   0.447244] Verifying that no non-zero bytes made their way into /dev/zero...
[   0.675412] Synthesizing system calls...
[   0.837421] Preparing for the zombie uprising...
[   0.893032] Waiting for children...
[   1.087694] Constructing home...
[   1.318251] Reticulating splines...
[   1.750988] Recruiting cron-ies...
[   1.921829] Singleplexing /dev/ptmx...
[   2.298970] Feeding the init monster...
[   2.382319] Moving files to filing cabinet...
[   2.513948] Ready!

Le message Starting gVisor… indique que le conteneur est exécuté avec runsc.

Lister les vulnérabilités des images avec Trivy
#

Trivy est un outil open source qui permet de scanner les images et de fournir un tableau avec les vulnérabilités (CVE) relevées tout en affichant pour chacune, leur criticité.

On vous demandera, lors de l’examen, de regarder si une ou plusieurs images contiennent ou non certaines vulnérabilités avec un numéro spécifique (CVE-*). Pour réaliser cette opération, celle ligne de commande peut être utile :

trivy image <mon image> | grep [CVE-*]
# ou
trivy i <mon image> | grep [CVE-*] # plus rapide

Générer le SBOM des images avec Bom
#

Le SBOM pour Software Bill of Materials est un inventaire détaillé et complet de tous les composants présent au sein d’un système ou, dans le cas de la certification, une image de conteneur.

Bom est l’outil officiel de la CNCF pour réaliser cette tâche et obtenir un fichier contenant l’ensemble de ces informations.

Pour cela, quelques commandes sont à avoir en tête :

# Lister les options
bom --help

# Créer un fichier avec le SBOM
bom generate --image=[image:tag] --output=sbon.spdx

# Vérifier la présence d'un paquet sur plusieurs images
for i in httpd:2.4.65 nginx:1.29.0 caddy:2.10.0; do bom generate --image=$i | grep [package]; done

Runtime security avec Falco
#

Lorsque Falco est installé sur la machine et se sera probablement le cas lors de l’examen, il est possible de directement l’exécuter via la commande : falco -U pour accélérer le démarrage.

Il est possible que l’on vous demande d’ajouter une règle, ou d’en modifier une.

Dans le cas où vous devez ajouter des champs sur le message associé à une règle par défaut de Falco, vous pouvez retrouver via ce lien l’ensemble des paramètres. Par exemple, on peut vous demander d’ajouter un timestamp en début de message (%evt.time).

Pour ne pas écraser la configuration de Falco, récupérez la règle à modifier dans le fichier /etc/falco/falco_rules.yaml puis copiez le bloc de code dans le fichier /etc/falco/falco_rules.local.yaml pour ensuite apporter les modifications.

Enfin, si vous souhaitez connaître les règles qui sont déclenchées par l’outil, rien de plus simple : falco -U | grep [Nom de la règle]

EncryptionConfiguration et etcd
#

Pour chiffrer les secrets et autres informations au sein d’etcd, vous aurez besoin d’une EncryptionConfiguration, un exemple est donné ici.

Dans la majorité des cas, on vous demandera de chiffrer les secrets uniquement, c’est pourquoi il est nécessaire d’épurer cette configuration pour la faire ressembler à celle-ci :

apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
  - resources:
      - secrets
    providers:
      - aesgcm:
          keys:
            - name: key1
              secret: c2VjcmV0IGlzIHNlY3VyZQ==
      - identity: {} # Permet de lire les secrets en clair

J’ai inversé au niveau des providers aesgcm et identity pour chiffrer par défaut.

De plus, le paramètre --encryption-provider-config est à ajouter au niveau du kube-apiserver en le faisant pointer sur ce fichier. N’oubliez pas non plus de monter via un volume cette configuration comme indiqué dans l’étape 3 de cette documentation.

Enfin, pour récupérer la valeur d’un secret depuis etcd, vous pouvez utiliser cette commande :

ETCDCTL_API=3 etcdctl --cert [cert] --key [key] --cacert [ca] get secrets /registry/secrets/[namespace]/[nom du secret]

Audit Policy
#

Comme toujours, la documentation Kubernetes donne un très bon exemple de fichier de configuration d’audit, vous pouvez vous en inspirer et l’adapter en fonction de l’énoncé.

N’oubliez pas le paramètre --audit-policy-file avec l’endroit où se trouve votre fichier au niveau du kube-apiserver ainsi que le montage de volume pour votre fichier de configuration. Vous pouvez retrouver l’ensemble des différents paramètres un peu plus loin dans la documentation.

Enfin, redémarrez le kube-apiserver pour que la configuration soit prise en compte en modifiant la place du fichier YAML du Pod statique :

cd /etc/kubernetes/manifests/
mv kube-apiserver.yaml ..
mv ../kube-apiserver.yaml .

ImagePolicyWebhook
#

La mise en place de ImagePolicyWebhook au sein du kube-apiserver est bien expliquée dans cette partie de la documentation en se basant sur le squelette ci-dessous :

apiVersion: apiserver.config.k8s.io/v1
kind: AdmissionConfiguration
plugins:
- name: ImagePolicyWebhook
  configuration:
    imagePolicy:
      kubeConfigFile: <path-to-kubeconfig-file>
      allowTTL: 50
      denyTTL: 50
      retryBackoff: 500
      defaultAllow: true # Mettre à false pour tester la bonne mise en place de l'ImagePolicyWebhook

Comme pour la configuration de l’audit, il est important de vérifier que ce fichier est bien monté au niveau d’un volume du kube-apiserver, mettre le paramètre --admission-control-config-file, et en plus, ajouter ImagePolicyWebhook dans les admission-plugins :

apiVersion: v1
kind: Pod
metadata:
[...]
  name: kube-apiserver
  namespace: kube-system
spec:
  containers:
  - command:
    - kube-apiserver
    - --advertise-address=172.30.1.2
    - --allow-privileged=true
    - --authorization-mode=Node,RBAC
    - --client-ca-file=/etc/kubernetes/pki/ca.crt
    - --enable-admission-plugins=NodeRestriction # ImagePolicyWebhook à ajouter

Comme ceci :

- --enable-admission-plugins=NodeRestriction,ImagePolicyWebhook

N’oubliez pas de redémarrer le kube-apiserver comme expliqué au-dessus dans la partie Audit Policy.

SecurityContext
#

Le SecurityContext ou contexte de sécurité permet de définir le contexte d’exécution d’un Pod ou d’un conteneur au sein d’un Pod. L’objectif est souvent de restreindre des privilèges ou configurer un utilisateur spécifique pour exécuter un conteneur.

Cette documentation permet de regrouper la plupart des cas de figure et d’exposer quelques bouts de code réutilisables.

Point important : S’il y a au sein d’un même Pod, une définition d’un SecurityContext au niveau du Pod mais aussi au niveau conteneur, le SecurityContext du conteneur écrase celui du Pod pour les clés identiques. C’est ce que l’on vous explique ici.

Supprimer les processus malveillants
#

Pour identifier un processus selon un port donné, vous pouvez utiliser la commande netstat.

Celle-ci est bien souvent installée, si ce n’est pas le cas, vous pouvez excécuter cette commande :

apt-get install -y net-tools

Pour repérer un processus en question et l’identifier, rien de plus simple :

netstat -plantu | grep [port]

Cette commande devrait vous retourner quelque chose sous cette forme :

tcp        0      0 0.0.0.0:660            0.0.0.0:*               LISTEN      20987/malware

20987 étant l’identifiant du processus (PID) et malware le nom de l’exécutable.

Dernière chose importante, tuer le processus et supprimer le binaire malveillant :

# Identifier le chemin du binaire
$ ls -l /proc/20987/exe
lrwxrwxrwx 1 root root 0 Sep 23 15:49 /proc/20987/exe -> /usr/local/bin/malware
# Tuer le processus
$ kill -9 20987
# Supprimer le binaire
$ rm /usr/local/bin/malware

Chiffrement de Pod à Pod
#

Dans cette nouvelle section de l’examen arrive deux nouveaux outils : Cilium (qui est déjà utilisé plus haut) mais surtout Istio.

Les deux permettent de faire du chiffrement de Pod à Pod avec des configurations différentes.

Pour Cilium : IPsec Transparent Encryption ou WireGuard Transparent Encryption.

Vous avez la possibilité de suivre la documentation en utilisant soit le CLI ou Helm pour installer et valider le tout !

Pour Istio, il est question du mode Sidecar, le mode Ambient n’est pas au programme.

Attention à ne pas oublier de redéployer les Pods une fois l’annotation en place.

kubectl label namespace [namespace] istio-injection=enabled
kubectl delete po --force --grace-period=0 [pods...]

La CKS, la certification le plus difficile ?
#

Oh oui !

La CKS, comme dit plus haut, est un condensé en termes de difficulté des deux premiers examens (CKAD et CKA), son périmètre est très large et le temps est vraiment très très précieux ! Le fait de manipuler des composants critiques ne rend pas non plus la tâche facile.

Entraînez-vous, essayez d’avoir de bons réflexes avec la documentation pour savoir où chercher, mais surtout, ne paniquez pas ! Si une question se révèle trop difficile ou que vos tests ne fonctionnent pas, mettez-la de côté et essayez d’y revenir un peu plus tard s’il vous reste du temps.

Et comme toujours, bonne chance à celles et ceux qui souhaitent passer cette certification ! :-)

Articles connexes

Comment obtenir la CKA ?
·5 mins
kubernetes certification shell terminal kubectl docker container cka
Réussir la CKAD
·9 mins
kubernetes ckad container docker terminal kubectl certification
Builder une image sans Docker c'est possible !
·6 mins
ci/cd kaniko docker kubernetes build k3s arm container