The Certified Kubernetes Security Specialist (CKS) exam, offered by the Linux Foundation, validates your ability to secure Kubernetes clusters in production environments. This credential demonstrates expertise in implementing security best practices across container orchestration platforms. Whether you're a DevOps engineer, security professional, or platform architect, the CKS certification proves you can design and maintain secure Kubernetes infrastructure. This page provides a focused study roadmap covering the exam's core domains and practical preparation strategies to help you pass confidently.
Use this topic map to guide your study for the Linux Foundation CKS (Certified Kubernetes Security Specialist) within the Kubernetes Security Specialist path.
The CKS exam combines multiple question styles to assess both theoretical knowledge and hands-on security implementation skills. Questions progress in difficulty and require you to apply concepts to realistic cluster scenarios.
Expect progressive difficulty that mirrors the complexity of securing production Kubernetes environments.
An effective study plan breaks the six domains into weekly milestones while balancing theory with hands-on practice. Allocate 4-6 weeks for thorough preparation, dedicating time to both learning and reinforcement through realistic scenarios.
Explore other Linux Foundation certifications: view all Linux Foundation exams.
Strengthen your preparation with up-to-date resources from validexamdumps.com. These materials align to CKS and cover practical scenarios with clear explanations.
Visit the exam page to download the PDF, Online Practice Test, or get a bundle discount for both formats: Certified Kubernetes Security Specialist.
Cluster Hardening, System Hardening, and Minimize Microservice Vulnerabilities demand the most practical experience because they involve direct configuration of security controls. Spend significant time in live Kubernetes environments applying RBAC rules, creating network policies, scanning images, and hardening kernel parameters. Hands-on repetition builds the muscle memory and troubleshooting skills the exam tests.
In practice, these domains form a security lifecycle: Cluster Setup establishes the foundation with secure communication and authentication, Cluster Hardening and System Hardening reduce the attack surface, Minimize Microservice Vulnerabilities protects running applications, Supply Chain Security ensures image integrity, and Monitoring, Logging and Runtime Security enables detection and response. Understanding these connections helps you see security as an integrated system rather than isolated topics.
Many candidates underestimate the importance of understanding why a security control matters, not just how to implement it. Others spend too little time on Supply Chain Security and Monitoring, Logging and Runtime Security, which are increasingly weighted. Rushing through hands-on labs without reading error messages and reviewing configurations carefully also costs points. Take time to verify each configuration change and understand its security impact.
Use your final week for targeted review and full-length practice exams rather than learning new topics. Identify your weakest domains from earlier practice tests and drill those areas with focused question sets. Complete at least two full-length timed exams to build pacing confidence and identify any remaining gaps. Review explanations carefully, especially for questions you answered incorrectly or guessed on.
Yes, the Linux Foundation recommends at least one year of hands-on Kubernetes experience and suggests passing the Certified Kubernetes Administrator (CKA) exam first. CKS builds on core Kubernetes knowledge and assumes familiarity with cluster operations, deployments, and networking. If you're new to Kubernetes, invest time in foundational labs and consider pursuing CKA certification before CKS.
SIMULATION
Fix all issues via configuration and restart the affected components to ensure the new setting takes effect.
Fix all of the following violations that were found against theAPI server:-
a. Ensure the --authorization-mode argument includes RBAC
b. Ensure the --authorization-mode argument includes Node
c. Ensure that the --profiling argument is set to false
Fix all of the following violations that were found against theKubelet:-
a. Ensure the --anonymous-auth argument is set to false.
b. Ensure that the --authorization-mode argument is set to Webhook.
Fix all of the following violations that were found against theETCD:-
a. Ensure that the --auto-tls argument is not set to true
Hint: Take the use of Tool Kube-Bench
API server:
Ensure the --authorization-mode argument includes RBAC
Turn on Role Based Access Control.
Role Based Access Control (RBAC) allows fine-grained control over the operations that different entities can perform on different objects in the cluster. It is recommended to use the RBAC authorization mode.
Fix - Buildtime
Kubernetes
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: null
labels:
component: kube-apiserver
tier: control-plane
name: kube-apiserver
namespace: kube-system
spec:
containers:
- command:
+ - kube-apiserver
+ - --authorization-mode=RBAC,Node
image: gcr.io/google_containers/kube-apiserver-amd64:v1.6.0
livenessProbe:
failureThreshold: 8
httpGet:
host: 127.0.0.1
path: /healthz
port: 6443
scheme: HTTPS
initialDelaySeconds: 15
timeoutSeconds: 15
name: kube-apiserver-should-pass
resources:
requests:
cpu: 250m
volumeMounts:
- mountPath: /etc/kubernetes/
name: k8s
readOnly: true
- mountPath: /etc/ssl/certs
name: certs
- mountPath: /etc/pki
name: pki
hostNetwork: true
volumes:
- hostPath:
path: /etc/kubernetes
name: k8s
- hostPath:
path: /etc/ssl/certs
name: certs
- hostPath:
path: /etc/pki
name: pki
Ensure the --authorization-mode argument includes Node
Remediation:Edit the API server pod specification file/etc/kubernetes/manifests/kube-apiserver.yamlon the master node and set the--authorization-modeparameter to a value that includesNode.
--authorization-mode=Node,RBAC
Audit:
/bin/ps -ef | grep kube-apiserver | grep -v grep
Expected result:
'Node,RBAC' has 'Node'
Ensure that the --profiling argument is set to false
Remediation:Edit the API server pod specification file/etc/kubernetes/manifests/kube-apiserver.yamlon the master node and set the below parameter.
--profiling=false
Audit:
/bin/ps -ef | grep kube-apiserver | grep -v grep
Expected result:
'false' is equal to 'false'
Fix all of the following violations that were found against theKubelet:-
Ensure the --anonymous-auth argument is set to false.
Remediation:If using a Kubelet config file, edit the file to set authentication:anonymous: enabled tofalse. If using executable arguments, edit the kubelet service file/etc/systemd/system/kubelet.service.d/10-kubeadm.confon each worker node and set the below parameter inKUBELET_SYSTEM_PODS_ARGSvariable.
--anonymous-auth=false
Based on your system, restart the kubelet service. For example:
systemctl daemon-reload
systemctl restart kubelet.service
Audit:
/bin/ps -fC kubelet
Audit Config:
/bin/cat /var/lib/kubelet/config.yaml
Expected result:
'false' is equal to 'false'
2) Ensure that the --authorization-mode argument is set to Webhook.
Audit
docker inspect kubelet | jq -e '.[0].Args[] | match('--authorization-mode=Webhook').string'
Returned Value:--authorization-mode=Webhook
Fix all of the following violations that were found against theETCD:-
a. Ensure that the --auto-tls argument is not set to true
Do not use self-signed certificates for TLS. etcd is a highly-available key value store used by Kubernetes deployments for persistent storage of all of its REST API objects. These objects are sensitive in nature and should not be available to unauthenticated clients. You should enable the client authentication via valid certificates to secure the access to the etcd service.
Fix - Buildtime
Kubernetes
apiVersion: v1
kind: Pod
metadata:
annotations:
scheduler.alpha.kubernetes.io/critical-pod: ''
creationTimestamp: null
labels:
component: etcd
tier: control-plane
name: etcd
namespace: kube-system
spec:
containers:
- command:
+ - etcd
+ - --auto-tls=true
image: k8s.gcr.io/etcd-amd64:3.2.18
imagePullPolicy: IfNotPresent
livenessProbe:
exec:
command:
- /bin/sh
- -ec
- ETCDCTL_API=3 etcdctl --endpoints=https://[192.168.22.9]:2379 --cacert=/etc/kubernetes/pki/etcd/ca.crt
--cert=/etc/kubernetes/pki/etcd/healthcheck-client.crt --key=/etc/kubernetes/pki/etcd/healthcheck-client.key
get foo
failureThreshold: 8
initialDelaySeconds: 15
timeoutSeconds: 15
name: etcd-should-fail
resources: {}
volumeMounts:
- mountPath: /var/lib/etcd
name: etcd-data
- mountPath: /etc/kubernetes/pki/etcd
name: etcd-certs
hostNetwork: true
priorityClassName: system-cluster-critical
volumes:
- hostPath:
path: /var/lib/etcd
type: DirectoryOrCreate
name: etcd-data
- hostPath:
path: /etc/kubernetes/pki/etcd
type: DirectoryOrCreate
name: etcd-certs
status: {}







SIMULATION
use the Trivy to scan the following images,
1. amazonlinux:1
2. k8s.gcr.io/kube-controller-manager:v1.18.6
Look for images with HIGH or CRITICAL severity vulnerabilities and store the output of the same in /opt/trivy-vulnerable.txt
SIMULATION
Documentation
Deployment, Pod Security Admission, Pod Security Standards
You must connect to the correct host . Failure to do so may result in a zero score.
[candidate@base] $ ssh cks000036
Context
For compliance, all user namespaces enforce the restricted Pod Security Standard .
Task
The confidential namespace contains a Deployment that is not compliant with the restricted Pod Security Standard . Thus, its Pods can not be scheduled.
Modify the Deployment to be compliant and verify that the Pods are running.
The Deployment's manifest file can be found at /home/candidate/nginx-unprivileged.yaml.
1) Connect to the correct host
ssh cks000036
sudo -i
export KUBECONFIG=/etc/kubernetes/admin.conf
2) Confirm the failing Pods + see the PSA error (fast)
kubectl -n confidential get deploy
kubectl -n confidential get pods
kubectl -n confidential describe deploy <deployment-name> | sed -n '/Events/,$p'
(You'll usually see ''violates PodSecurity 'restricted' ...'' with the exact missing fields.)
3) Edit the provided manifest
vi /home/candidate/nginx-unprivileged.yaml
You must ensure the Pod template becomes compliant. Add/ensure the following exact blocks:
4) Add Pod-level securityContext (under spec.template.spec)
Find:
spec:
template:
spec:
Add this block under it (or merge if securityContext: already exists):
securityContext:
runAsNonRoot: true
runAsUser: 65535
seccompProfile:
type: RuntimeDefault
5) Add Container-level securityContext (under the nginx container)
Find:
containers:
- name: ...
image: ...
Under that container, add (or adjust) this exact block:
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop:
- ALL
If there are multiple containers, apply the same container securityContext to each one.
Save and exit:
:wq
6) Apply the manifest to the confidential namespace
kubectl -n confidential apply -f /home/candidate/nginx-unprivileged.yaml
Wait rollout:
kubectl -n confidential rollout status deployment/<deployment-name>
If you don't know the deployment name from the file, list:
kubectl -n confidential get deploy
7) Verify Pods are running
kubectl -n confidential get pods -o wide
If still failing, show the exact PSA violation (this tells you what else to fix):
kubectl -n confidential describe pod
Quick ''if it still fails'' fixes (common restricted blockers)
Open the manifest again and ensure these are NOT set (or are removed/false):
hostNetwork: true
hostPID: true
hostIPC: true
any hostPort:
privileged: true
capabilities.add:
seccompProfile: Unconfined
runAsUser: 0 or runAsNonRoot: false
Then re-apply.
Minimal compliant result (what the grader expects)
Your Pod template should include:
seccompProfile: RuntimeDefault
runAsNonRoot: true (and a non-root UID like 65535)
container: allowPrivilegeEscalation: false
container: capabilities.drop: [ALL]
container: readOnlyRootFilesystem: true
SIMULATION
Context
For testing purposes, the kubeadm provisioned cluster 's API server
was configured to allow unauthenticated and unauthorized access.
Task
First, secure the cluster 's API server configuring it as follows:
. Forbid anonymous authentication
. Use authorization mode Node,RBAC
. Use admission controller NodeRestriction
The cluster uses the Docker Engine as its container runtime . If needed, use the docker command to troubleshoot running containers.
kubectl is configured to use unauthenticated and unauthorized access. You do not have to change it, but be aware that kubectl will stop working once you have secured the cluster .
You can use the cluster 's original kubectl configuration file located at etc/kubernetes/admin.conf to access the secured cluster.
Next, to clean up, remove the ClusterRoleBinding
system:anonymous.
1) SSH to control-plane node
ssh cks000002
sudo -i
2) Edit API Server static pod manifest
API server in kubeadm runs as a static pod.
vi /etc/kubernetes/manifests/kube-apiserver.yaml
3) Apply required API Server security settings
3.1 Forbid anonymous authentication
Find command: section and ensure this line exists:
- --anonymous-auth=false
3.2 Use authorization mode Node,RBAC
Ensure exactly this line exists (and no AlwaysAllow):
- --authorization-mode=Node,RBAC
Remove if present:
- --authorization-mode=AlwaysAllow
3.3 Enable admission controller NodeRestriction
Find --enable-admission-plugins and ensure NodeRestriction is included.
Correct example:
- --enable-admission-plugins=NodeRestriction
If other plugins already exist, append NodeRestriction, e.g.:
- --enable-admission-plugins=NamespaceLifecycle,ServiceAccount,NodeRestriction
4) Save file and let kubelet restart API server
Just save and exit (:wq)
Kubelet will automatically restart the API server pod.
5) Switch kubectl to secured config
Current kubectl will stop working after API server hardening.
export KUBECONFIG=/etc/kubernetes/admin.conf
Verify access:
kubectl get nodes
6) Remove insecure ClusterRoleBinding
Delete system:anonymous binding:
kubectl delete clusterrolebinding system:anonymous
Verify removal:
kubectl get clusterrolebinding | grep anonymous
(no output = correct)
7) Quick validation (optional but fast)
API server flags check:
grep -n 'anonymous-auth' /etc/kubernetes/manifests/kube-apiserver.yaml
grep -n 'authorization-mode' /etc/kubernetes/manifests/kube-apiserver.yaml
grep -n 'NodeRestriction' /etc/kubernetes/manifests/kube-apiserver.yaml
SIMULATION
Context
You must fully integrate a container image scanner into the kubeadm provisioned cluster.
Task
Given an incomplete configuration located at /etc/kubernetes/bouncer and a functional container image scanner
with an HTTPS endpoint at https://smooth-yak.local/review, perform the following tasks to implement a validating admission controller.
First, re-configure the API server to enable all admission plugin(s) to support the provided AdmissionConfiguration.
Next, re-configure the ImagePolicyWebhook configuration to deny images on backend failure.
Next, complete the backend configuration to point to the container image scanner's endpoint at https://smooth-yak.local/review.
Finally, to test the configuration, deploy the test resource defined in /home/candidate/vulnerable.yaml which is using an image that should be denied.
You may delete and re-create the resource as often as needed.
The container image scanner's log file is located at /var/log/nginx/access_log.
Below is the CKS exam style ''do-this-exactly'' runbook for Q3. It includes the minimal discovery commands (so you don't guess filenames), then the exact lines/blocks to set.
QUESTION 3 -- ImagePolicyWebhook (Validating Admission) -- Exam Steps
0) SSH + root
ssh cks000002
sudo -i
1) Identify the provided config files (no guessing)
ls -la /etc/kubernetes/bouncer
You are looking for files typically named like:
admission_configuration.yaml (AdmissionConfiguration)
imagepolicywebhook.yaml (ImagePolicyWebhookConfiguration) OR the ImagePolicyWebhook config embedded inside the AdmissionConfiguration
kubeconfig (webhook kubeconfig)
If unsure which is which, quick peek:
grep -R 'ImagePolicyWebhook' -n /etc/kubernetes/bouncer
grep -R 'AdmissionConfiguration' -n /etc/kubernetes/bouncer
grep -R 'kubeconfig' -n /etc/kubernetes/bouncer
PART A --- Reconfigure API Server to enable required admission plugin(s)
2) Edit API server static pod manifest
vi /etc/kubernetes/manifests/kube-apiserver.yaml
2.1 Enable the admission plugin ImagePolicyWebhook
Find the line starting with:
- --enable-admission-plugins=
Ensure ImagePolicyWebhook is included in that comma list.
Example (your list may differ; just add ImagePolicyWebhook):
- --enable-admission-plugins=NodeRestriction,ImagePolicyWebhook
If the flag does not exist, add one line under command::
- --enable-admission-plugins=ImagePolicyWebhook
2.2 Point API server to the provided AdmissionConfiguration
In the same file, ensure this flag exists (use the file in /etc/kubernetes/bouncer that contains AdmissionConfiguration):
- --admission-control-config-file=/etc/kubernetes/bouncer/admission_configuration.yaml
If your file is named differently, use the real filename you found in step 1, but keep the flag name exactly --admission-control-config-file.
Save/exit:
:wq
Static pod will restart automatically (kubelet watches the manifest).
Optional quick watch:
docker ps | grep kube-apiserver
# or:
crictl ps | grep kube-apiserver
PART B --- Configure ImagePolicyWebhook to deny images on backend failure
3) Edit the ImagePolicyWebhook config
One of these is true on your cluster:
Option 1 (most common in these tasks): ImagePolicyWebhook config is a standalone file
Edit the file in /etc/kubernetes/bouncer that contains kind: ImagePolicyWebhookConfiguration:
grep -R 'kind: ImagePolicyWebhookConfiguration' -n /etc/kubernetes/bouncer
vi /etc/kubernetes/bouncer/<THE_FILE_YOU_FOUND>.yaml
Set (or ensure) exactly:
defaultAllow: false
Option 2: ImagePolicyWebhook config is embedded inside AdmissionConfiguration
Edit the AdmissionConfiguration file:
vi /etc/kubernetes/bouncer/admission_configuration.yaml
Find the plugin section for ImagePolicyWebhook and ensure the config includes:
defaultAllow: false
Save/exit:
:wq
PART C --- Point backend configuration to https://smooth-yak.local/review
4) Edit the webhook kubeconfig to use the scanner endpoint
Find the kubeconfig file referenced by the ImagePolicyWebhook config.
Search for kubeConfigFile:
grep -R 'kubeConfigFile' -n /etc/kubernetes/bouncer
Open that kubeconfig path (example name below; yours may differ):
vi /etc/kubernetes/bouncer/kubeconfig
In kubeconfig, set the cluster server exactly:
clusters:
- cluster:
server: https://smooth-yak.local/review
Save/exit:
:wq
PART D --- Restart effect (make sure API server picks up config)
Because you already edited /etc/kubernetes/manifests/kube-apiserver.yaml, the API server restarted.
To be safe (and fast), force a restart by ''touching'' the manifest (no content change needed):
touch /etc/kubernetes/manifests/kube-apiserver.yaml
PART E --- Test: apply vulnerable workload and confirm it is denied
5) Use admin kubeconfig (because old kubectl config may break)
export KUBECONFIG=/etc/kubernetes/admin.conf
kubectl get nodes
6) Deploy the test resource (should be DENIED)
kubectl apply -f /home/candidate/vulnerable.yaml
Expected: admission error/denied message.
If it already exists:
kubectl delete -f /home/candidate/vulnerable.yaml
kubectl apply -f /home/candidate/vulnerable.yaml
PART F --- Verify the scanner was called (log check)
7) Check scanner access log
tail -n 50 /var/log/nginx/access_log
You should see requests hitting /review.
Quick ''what to check if it doesn't deny''
Run these in order:
Confirm API server flags:
grep -n 'enable-admission-plugins' /etc/kubernetes/manifests/kube-apiserver.yaml
grep -n 'admission-control-config-file' /etc/kubernetes/manifests/kube-apiserver.yaml
Confirm deny-on-failure:
grep -R 'defaultAllow' -n /etc/kubernetes/bouncer
Must show:
defaultAllow: false
Confirm endpoint:
grep -R 'server: https://smooth-yak.local/review' -n /etc/kubernetes/bouncer
API server logs (docker runtime):
docker ps | grep kube-apiserver
docker logs $(docker ps -q --filter name=kube-apiserver) --tail 80
If you paste the output of:
ls - /etc/kubernetes/bouncer
grep -R 'kind: AdmissionConfiguration' -n /etc/kubernetes/bouncer
grep -R 'ImagePolicyWebhook' -n /etc/kubernetes/bouncer