Lock Down an API Server Open to Anonymous Access
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…
Live lab access
This task description is public. Live practice requires full access unless this is the selected free weekly task. AI Lab Coach is available in single-task labs, including the free weekly task. See this week's free task.
Create an account to continue →
On a phone? Read the task and create your account here, then sign in on a desktop or laptop for the terminal lab. Nothing starts until you choose Start.
The task
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.
*Lab note: this practice node runs containerd rather than the Docker Engine, so use crictl (or kubectl) where you would use docker.*
Original Prepium guide · Written for Kubernetes v1.35 and checked against the official documentation on 6 October 2026. Use a disposable practice cluster. Everything below edits a control plane node, so use a throwaway kubeadm VM, never a shared or production cluster.
Authentication, authorization and admission: the three gates of the API server
Every request to kube-apiserver passes three gates in order. Authentication decides who is calling, authorization decides whether that identity may perform the verb, and admission plugins can still reject or modify the object afterwards. Hardening tasks usually mean one gate was left open, so know which flag controls which gate and how to prove the change from outside.
request ──→ authentication ──→ authorization ──→ admission ──→ etcd
(--anonymous-auth) (--authorization-mode) (--enable-admission-plugins)
no credentials? ──→ user system:anonymous, group system:unauthenticated
1. Inspect before you touch anything
On a kubeadm node the API server is a static Pod defined in /etc/kubernetes/manifests/kube-apiserver.yaml. Read the three flags first, then ask the server what a caller with no credentials can do. Replace the address with your own control plane endpoint (kubeadm listens on port 6443 by default).
sudo grep -E -- '--(anonymous-auth|authorization-mode|enable-admission-plugins)' \
/etc/kubernetes/manifests/kube-apiserver.yaml
APISERVER=https://192.0.2.10:6443
curl -sk "$APISERVER/version"
curl -sk "$APISERVER/api"
If --anonymous-auth is missing, it defaults to true. A request that carries no credentials is then not rejected; it is relabelled as user system:anonymous in group system:unauthenticated and handed to authorization. On a healthy RBAC cluster /version typically answers, because the default system:public-info-viewer role gives unauthenticated callers read access to non-sensitive cluster information. /api should return 403 Forbidden, since the discovery role is bound only to authenticated users. If /api returns the resource list, something is authorizing anonymous callers.
2. Know what each flag really means
- Authentication:
--anonymous-auth=falsestops the relabelling, so credential-less requests get401 Unauthorizedinstead of reaching RBAC. The newerAuthenticationConfigurationfile can instead keep anonymous access for chosen paths only; you cannot set both the file'sanonymoussection and the flag. - Authorization:
--authorization-modeis an ordered list. Each authorizer is asked in turn and the first allow or deny wins.AlwaysAllowanywhere in the list approves whatever the others have no opinion on, and because RBAC never says "deny",AlwaysAllow,RBACbehaves exactly likeAlwaysAllow. TheNodeauthorizer only handles kubelet identities (system:node:<name>insystem:nodes). - Admission:
NodeRestrictionis not in the built-in default set. Once listed in--enable-admission-plugins(comma separated), it limits a kubelet to its own Node object and Pods bound to that node.
Also hunt for RBAC bindings that hand rights to anonymous callers, since a binding to system:anonymous or system:unauthenticated reopens the door even with a correct mode list:
kubectl get clusterrolebindings -o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.subjects[*].name}{"\n"}{end}' \
| grep -E 'system:(anonymous|unauthenticated)'
kubectl auth can-i list secrets -A --as=system:anonymous --as-group=system:unauthenticated
3. Edit the static Pod safely
Copy the manifest outside the manifests directory first (the kubelet tries to run every file it finds there), then edit in place. The kubelet notices the change and recreates the API server; there is nothing to kubectl apply.
sudo cp /etc/kubernetes/manifests/kube-apiserver.yaml /root/apiserver-before.yaml
sudo vi /etc/kubernetes/manifests/kube-apiserver.yaml
watch -n 2 'sudo crictl ps -a | grep kube-apiserver'
Expect the API to be unreachable for up to a minute. If no new container appears, read sudo journalctl -u kubelet for a YAML error, or check the exited container's logs with crictl logs for an unknown flag value.
4. Verify from both sides
Repeat the anonymous curl calls, then confirm an authenticated client still works with kubectl --kubeconfig /etc/kubernetes/admin.conf get nodes. Any kubeconfig that relied on anonymous access now fails, as intended.
Common mistakes
- Saving a backup such as
kube-apiserver.yaml.originside the manifests directory: the kubelet may start the stale copy. - Appending RBAC after
AlwaysAllowand believing the cluster is locked down: the order makes RBAC irrelevant. - Expecting
/versionor health paths to stay public after setting--anonymous-auth=false: every credential-less request now gets 401, so any external check that relied on them needs credentials.
| Example check | Evidence |
|---|---|
| Anonymous callers rejected | curl -sk $APISERVER/api returns 401 Unauthorized |
| No AlwaysAllow in the chain | --authorization-mode value in the running container's command |
| Kubelets constrained | NodeRestriction appears in --enable-admission-plugins |
| Admins unaffected | kubectl --kubeconfig /etc/kubernetes/admin.conf get nodes succeeds |
Cleanup: on a throwaway VM, restore your saved copy into the manifests directory or simply discard the VM. References: Authenticating, Authorization, Admission controllers, Static Pods. Next, try API server audit logging or kubelet authentication.
What this tests
Lock down network access, the kubelet, API authentication, and ingress TLS, and run CIS benchmark checks. On the CKS exam, Cluster Setup tasks are graded purely on what you build in the cluster - not multiple choice - so the only way to get faster is to do them on a real cluster against a clock.
Practice it for real
The public description lets you study the task before signing up. Full access includes this live lab; the selected Task of the Week is free with an account. You solve the task in a real terminal, hit validate, and a programmatic checker scores exactly what you got right and wrong (with partial credit). You can open the solution while practising, then retry and use the failed checks to improve your score.