Two of the six CKS domains — Cluster Setup (15%) and Cluster Hardening (15%) — are about locking down the cluster itself before you ever worry about the workloads running on it. Together they’re 30% of the exam, the single largest block of points after you combine them, and they’re the domains most likely to involve editing control-plane component flags, writing RBAC rules under time pressure, and applying NetworkPolicies that actually block traffic. Unlike supply-chain or runtime topics, these tasks live in the cluster’s foundation: the API server, etcd, the kubelet, the network, and the permission model.
This guide is a practitioner’s walkthrough of that foundation, mapped to the CKS objectives. Every section includes commands or manifests you can run on a real cluster, because the CKS is entirely hands-on — you won’t get multiple-choice credit for knowing the flag; you have to set it and prove it works. If you want the exam logistics and full blueprint first, read the CKS exam guide for 2026 and the CKS exam topics breakdown.
The Attack Surface You’re Defending
Before the fixes, picture what you’re protecting. A Kubernetes control plane exposes several high-value targets:
| Component | Why it matters | Primary risk if misconfigured |
|---|---|---|
| kube-apiserver | The front door to the entire cluster | Anonymous or overly broad access = full cluster compromise |
| etcd | Stores all cluster state, including Secrets | Unencrypted/unauthenticated etcd leaks every Secret |
| kubelet | Runs on every node, can exec into pods | An open kubelet API = remote code execution on nodes |
| RBAC | Decides who can do what | Wildcard/cluster-admin bindings = privilege escalation |
| Pod network | East-west traffic between pods | No NetworkPolicy = flat, fully reachable network |
The Cluster Setup and Cluster Hardening domains exist to close each of these gaps. Let’s go component by component.
Benchmark First: CIS Benchmarks with kube-bench
The CKS objective “use CIS benchmark to review the security configuration of Kubernetes components” maps directly to a tool: kube-bench. The CIS Kubernetes Benchmark is a published set of hardening recommendations, and kube-bench automates checking your cluster against them.
Run it as a job or a one-off pod. The most common approach on the exam is to run it against a specific target:
# Check the control-plane components (apiserver, controller-manager, scheduler, etcd)
kube-bench run --targets master
# Check the worker node components (kubelet, kube-proxy)
kube-bench run --targets node
# Or run everything and filter to failures
kube-bench run --check 1.2.1
Each result is [PASS], [FAIL], or [WARN], with a remediation block telling you exactly which flag or file permission to change. A typical CKS task gives you a failing check ID and asks you to remediate it. The fix almost always means editing a static pod manifest in /etc/kubernetes/manifests/ (for control-plane components) or the kubelet config on a node, then waiting for the component to restart.
Exam tip: When you edit a static pod manifest like
/etc/kubernetes/manifests/kube-apiserver.yaml, the kubelet restarts the pod automatically — but it can take 30–60 seconds and the API server may briefly become unavailable. Make the edit, thenwatch crictl psor retrykubectl get pods -n kube-systemuntil it’s back.
Securing the kube-apiserver
The API server is the highest-value target, and several CIS checks focus on its flags. Edit /etc/kubernetes/manifests/kube-apiserver.yaml and ensure the dangerous defaults are closed:
spec:
containers:
- command:
- kube-apiserver
# Reject unauthenticated requests
- --anonymous-auth=false
# Use RBAC (and Node) authorization, never AlwaysAllow
- --authorization-mode=Node,RBAC
# Restrict what kubelets can modify
- --enable-admission-plugins=NodeRestriction
# Encrypt Secrets at rest in etcd
- --encryption-provider-config=/etc/kubernetes/enc/enc.yaml
# Send audit events to a log
- --audit-log-path=/var/log/kubernetes/audit/audit.log
- --audit-policy-file=/etc/kubernetes/audit/policy.yaml
Key points the exam tests:
--anonymous-auth=falsestops the API server from accepting unauthenticated requests as thesystem:anonymoususer.--authorization-modemust not beAlwaysAllow. RBAC (usually with Node) is the secure setting.NodeRestrictionadmission plugin limits each kubelet to modifying only its own Node and the pods bound to it — a key defense against a compromised node escalating.- The legacy insecure port (
--insecure-port=0) is removed in modern Kubernetes, but older CIS checks still reference disabling it.
Encrypting Secrets at Rest in etcd
By default, Secrets are stored in etcd base64-encoded, not encrypted — anyone who reads the etcd data store can decode every Secret. The fix is an EncryptionConfiguration referenced by --encryption-provider-config:
apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
- resources:
- secrets
providers:
- aescbc:
keys:
- name: key1
secret: <base64-encoded-32-byte-key>
- identity: {} # fallback so existing data stays readable
After enabling it, re-encrypt existing Secrets so they’re written back encrypted:
kubectl get secrets --all-namespaces -o json | kubectl replace -f -
Also protect etcd itself with client and peer TLS (--cert-file, --key-file, --peer-*, --client-cert-auth=true) so only the API server can talk to it.
Hardening the Kubelet
Every worker node runs a kubelet, and an unprotected kubelet API is a direct path to running commands inside your pods. The objective “protect node metadata and endpoints” and the CIS node checks point here. Edit the kubelet config (commonly /var/lib/kubelet/config.yaml) and set:
apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
authentication:
anonymous:
enabled: false # no anonymous access to the kubelet API
webhook:
enabled: true
authorization:
mode: Webhook # not AlwaysAllow — delegate authz to the API server
readOnlyPort: 0 # disable the unauthenticated read-only port (10255)
Then restart the kubelet:
systemctl restart kubelet
systemctl status kubelet
The three things to remember: anonymous auth off, authorization mode Webhook (not AlwaysAllow), and the read-only port disabled. These close the most common kubelet exposures the CKS tests.
RBAC: Least Privilege in Practice
The Cluster Hardening objective “use Role Based Access Controls to minimize exposure” is one of the most reliably tested topics. The CKS wants to see you create tightly scoped Roles and avoid the lazy cluster-admin-for-everyone pattern.
The core distinction: a Role grants permissions within a single namespace; a ClusterRole grants them cluster-wide. Bind them with a RoleBinding (namespace-scoped) or ClusterRoleBinding (cluster-wide). A least-privilege example — a service account that can only read pods in one namespace:
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: web
name: pod-reader
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "list", "watch"] # never use ["*"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: read-pods
namespace: web
subjects:
- kind: ServiceAccount
name: app-sa
namespace: web
roleRef:
kind: Role
name: pod-reader
apiGroup: rbac.authorization.k8s.io
Audit any RBAC setup with kubectl auth can-i, which is your single most useful command in this domain:
# Can this service account delete pods? (should be 'no')
kubectl auth can-i delete pods \
--as=system:serviceaccount:web:app-sa -n web
# List everything a subject is allowed to do
kubectl auth can-i --list \
--as=system:serviceaccount:web:app-sa -n web
Hard rules the exam rewards: never use verbs: ["*"] or resources: ["*"] unless explicitly required, avoid binding to the built-in cluster-admin ClusterRole, and prefer Roles over ClusterRoles when a namespace scope is enough.
Locking Down Service Accounts
Closely tied to RBAC is the objective “exercise caution in using service accounts.” By default, Kubernetes mounts a token for the namespace’s default service account into every pod — and if that token has more permissions than it needs, it’s an escalation path. Two defenses:
1. Disable automatic token mounting where it isn’t needed — on the service account or the pod:
apiVersion: v1
kind: ServiceAccount
metadata:
name: app-sa
namespace: web
automountServiceAccountToken: false
# Or per-pod, which overrides the SA setting
apiVersion: v1
kind: Pod
metadata:
name: no-token-pod
spec:
serviceAccountName: app-sa
automountServiceAccountToken: false
2. Don’t rely on the default service account. Create a dedicated, minimally-permissioned service account per workload, and ensure the default SA has no meaningful RoleBindings. If a pod doesn’t need to call the Kubernetes API at all, it shouldn’t have a token mounted.
Default-Deny NetworkPolicies
The Cluster Setup objective “use network security policies to restrict cluster level access” maps to NetworkPolicy. Out of the box, all pods can talk to all other pods — a flat network. The secure baseline is a default-deny policy per namespace, then explicitly allow only the traffic you need.
A default-deny for all ingress and egress in a namespace:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-all
namespace: web
spec:
podSelector: {} # selects every pod in the namespace
policyTypes:
- Ingress
- Egress
With nothing in the ingress/egress lists, everything is denied. Then add a policy that allows, say, the frontend to reach the backend on port 8080:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-frontend-to-backend
namespace: web
spec:
podSelector:
matchLabels:
app: backend
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
app: frontend
ports:
- protocol: TCP
port: 8080
Critical exam detail: NetworkPolicies are enforced by the CNI plugin, not by Kubernetes core. They only take effect if your cluster uses a CNI that supports them (Calico, Cilium, Weave, etc.). On the exam cluster the CNI supports them, but always verify a deny actually blocks traffic by
kubectl exec-ing a test pod and curling the target.
Protecting Node Metadata Endpoints
A subtle but tested objective: cloud instances expose a metadata endpoint at 169.254.169.254, which can hand out IAM credentials. A compromised pod that reaches it can steal node-level cloud permissions. Block pod egress to that address with a NetworkPolicy:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: deny-metadata-access
namespace: web
spec:
podSelector: {}
policyTypes:
- Egress
egress:
- to:
- ipBlock:
cidr: 0.0.0.0/0
except:
- 169.254.169.254/32 # block the metadata IP
Ingress with TLS and Restricting API Access
Two more Cluster Setup items round out the domain:
- Set up Ingress with TLS. Terminate TLS at the Ingress by referencing a TLS Secret. Create the Secret with
kubectl create secret tls my-tls --cert=tls.crt --key=tls.key, then reference it in the Ingressspec.tlsblock so traffic to your service is encrypted in transit. - Restrict access to the Kubernetes API. Defense in depth combines three layers: authentication (no anonymous, valid certs/tokens), authorization (least-privilege RBAC), and network controls (firewall rules and NetworkPolicies limiting who can even reach the API server endpoint).
Keep the Cluster Patched
Finally, “upgrade Kubernetes to avoid vulnerabilities.” Running an out-of-date control plane means running known CVEs. The standard flow with kubeadm:
# On the control plane
kubeadm upgrade plan
kubeadm upgrade apply v1.31.x
# Then upgrade kubelet and kubectl on each node, draining first
kubectl drain <node> --ignore-daemonsets
# ... apt/yum upgrade kubelet kubectl, then:
kubectl uncordon <node>
Always verify platform binaries (checksum/signature) before deploying them — another explicit CKS objective tied to trusting what you run.
Cluster Hardening Cheat Sheet
| Task | Where | Key setting |
|---|---|---|
| Benchmark the cluster | kube-bench | run --targets master/node |
| Reject anonymous API access | apiserver manifest | --anonymous-auth=false |
| Use RBAC authz | apiserver manifest | --authorization-mode=Node,RBAC |
| Encrypt Secrets at rest | EncryptionConfiguration | --encryption-provider-config |
| Harden kubelet | kubelet config | anonymous off, mode Webhook, readOnlyPort: 0 |
| Least-privilege access | Role + RoleBinding | no * verbs/resources |
| Disable token automount | ServiceAccount/Pod | automountServiceAccountToken: false |
| Default-deny network | NetworkPolicy | empty podSelector, both policyTypes |
| Block metadata endpoint | NetworkPolicy egress | except: 169.254.169.254/32 |
Practice on a Real Cluster Before Exam Day
Cluster setup and hardening are muscle-memory skills. Reading that you should set --anonymous-auth=false is easy; editing a live kube-apiserver.yaml manifest, waiting for the static pod to restart, and confirming the API recovers — without panicking when kubectl briefly stops responding — is a different skill that only repetition builds.
Sailor.sh’s Certified Kubernetes Security Specialist (CKS) Mock Exam Bundle runs on a real Kubernetes cluster with exam-style performance tasks that mirror the format and difficulty of the actual CKS, including the cluster-setup and hardening scenarios covered here. Practicing kube-bench remediation, RBAC scoping, and NetworkPolicy enforcement under a ticking clock is the most reliable way to build that speed. Pair the hands-on work with a structured CKS study plan, then extend into the adjacent domains with the CKS supply chain security guide and the CKS runtime security and Falco guide. For a broader checklist, the Kubernetes security best practices for CKS post ties the domains together.
Frequently Asked Questions
What is kube-bench and how is it used on the CKS exam?
kube-bench is an open-source tool that checks whether a cluster’s configuration matches the CIS Kubernetes Benchmark. You run it against the control plane (--targets master) or worker nodes (--targets node), and it returns PASS/FAIL/WARN results with remediation steps. On the CKS, a task typically gives you a failing check and asks you to fix the underlying flag or file permission, usually in a static pod manifest or the kubelet config.
How do I secure the kube-apiserver for CKS?
Edit /etc/kubernetes/manifests/kube-apiserver.yaml and ensure --anonymous-auth=false, --authorization-mode=Node,RBAC (never AlwaysAllow), the NodeRestriction admission plugin is enabled, Secrets are encrypted at rest via --encryption-provider-config, and audit logging is on. The kubelet restarts the static pod automatically after you save the file.
What is a default-deny NetworkPolicy?
A default-deny NetworkPolicy selects every pod in a namespace (podSelector: {}) and declares Ingress and/or Egress as policy types with no allow rules, which blocks all matching traffic. You then add explicit allow policies for the connections you actually need. Remember that NetworkPolicies are enforced by the CNI plugin (Calico, Cilium, etc.), not by Kubernetes core, so they have no effect unless the CNI supports them.
How do I apply least-privilege RBAC in Kubernetes?
Create Roles (namespace-scoped) or ClusterRoles (cluster-wide) that list only the specific apiGroups, resources, and verbs a subject needs — never wildcards — and bind them with RoleBindings or ClusterRoleBindings. Verify the result with kubectl auth can-i <verb> <resource> --as=<subject> and kubectl auth can-i --list --as=<subject>. Avoid binding workloads to the built-in cluster-admin ClusterRole.
Why should I disable automountServiceAccountToken?
By default Kubernetes mounts a service account token into every pod, even pods that never call the Kubernetes API. If that token’s service account has broad permissions, a compromised pod can use it to escalate. Setting automountServiceAccountToken: false on the service account or pod removes that token, shrinking the attack surface — a frequent CKS hardening task.
How much of the CKS exam is cluster setup and hardening?
Cluster Setup is 15% and Cluster Hardening is 15%, so together they account for 30% of the CKS — the largest combined block alongside the runtime, supply-chain, and microservice domains. See the CKS exam topics breakdown for the full weighting across all six domains.
Conclusion
Cluster setup and hardening are where Kubernetes security starts: a locked-down API server, an encrypted etcd, a kubelet that rejects anonymous calls, RBAC that grants the minimum, and a network that denies by default. These tasks are 30% of the CKS and, more importantly, they’re the controls that contain everything else — even a perfectly built workload is exposed on a cluster with AlwaysAllow authorization or a flat network.
The only way to make these fixes fast and reliable is to do them repeatedly on a real cluster until the steps are automatic. Work through the CKS mock exams, remediate every kube-bench finding by hand, and verify each control actually blocks what it should. Do that, and the largest combined domain on the CKS becomes the part you finish first. For the full study path, start with the CKS exam guide for 2026.