Back to Blog

Kubernetes Cluster Setup & Hardening for the CKS Exam: RBAC, CIS Benchmarks, API Server & Kubelet Security

A hands-on guide to the Cluster Setup and Cluster Hardening domains of the CKS exam — running kube-bench against CIS Benchmarks, securing the kube-apiserver and kubelet, least-privilege RBAC, default-deny NetworkPolicies, and protecting node metadata, with copy-paste kubectl and YAML examples.

By Sailor Team , June 18, 2026

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:

ComponentWhy it mattersPrimary risk if misconfigured
kube-apiserverThe front door to the entire clusterAnonymous or overly broad access = full cluster compromise
etcdStores all cluster state, including SecretsUnencrypted/unauthenticated etcd leaks every Secret
kubeletRuns on every node, can exec into podsAn open kubelet API = remote code execution on nodes
RBACDecides who can do whatWildcard/cluster-admin bindings = privilege escalation
Pod networkEast-west traffic between podsNo 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, then watch crictl ps or retry kubectl get pods -n kube-system until 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=false stops the API server from accepting unauthenticated requests as the system:anonymous user.
  • --authorization-mode must not be AlwaysAllow. RBAC (usually with Node) is the secure setting.
  • NodeRestriction admission 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 Ingress spec.tls block 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

TaskWhereKey setting
Benchmark the clusterkube-benchrun --targets master/node
Reject anonymous API accessapiserver manifest--anonymous-auth=false
Use RBAC authzapiserver manifest--authorization-mode=Node,RBAC
Encrypt Secrets at restEncryptionConfiguration--encryption-provider-config
Harden kubeletkubelet configanonymous off, mode Webhook, readOnlyPort: 0
Least-privilege accessRole + RoleBindingno * verbs/resources
Disable token automountServiceAccount/PodautomountServiceAccountToken: false
Default-deny networkNetworkPolicyempty podSelector, both policyTypes
Block metadata endpointNetworkPolicy egressexcept: 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.

Limited Time Offer: Get 80% off all Mock Exam Bundles | Sale ends in 7 days. Start learning today.

Claim Now