KCSA — Kubernetes Security Associate
Mapping the Attack Surface — Where Do They Come In
In one line
Kubernetes has fewer entry points than you might think. The API server, the kubelet, etcd, the container registry, and the workload itself. Most of the rest are paths to one of these five.
Why this was needed
"Let's protect the cluster" is vague because there is no list of what to protect. Attackers move with a list — they sweep for open ports, try anonymous access, and count what they can do with credentials they already have. Defenders need the same list for the symmetry to hold.
How it works
Five entry points
1) kube-apiserver (6443)
It is the cluster's only door and its most valuable target. There are three things to look at here —
whether anonymous access is open (--anonymous-auth), what the authorization mode is (AlwaysAllow is a disaster),
and who holds what credentials.
2) kubelet (10250, 10255)
It is the most often forgotten target. The kubelet has its own API, and it includes running Pods, exec, and logs.
If --anonymous-auth=true or the authorization mode is AlwaysAllow, anyone who can reach the node over the network can run commands in every container on that node. The read-only port (10255) also has no authentication, so Pod specs and environment variables are exposed as they are. That is why readOnlyPort=0 is a standard hardening item.
3) etcd (2379/2380) The entire cluster state is here. If you can read etcd, you can read every Secret, and if you can write to it, you bypass all of the apiserver's authorization checks and own the cluster. In the default configuration, Secrets are stored effectively in plaintext, so a person who obtains an etcd backup file, a snapshot, or a disk image gets the same thing.
4) The container registry and the image supply chain
This is a path where the attacker does not need to connect to the cluster directly, but makes you pull and run that code on your own.
Tags move — myapp:1.4.2 may point to a different image today than yesterday.
If you do not pin by digest, you cannot pin down what you deployed.
5) The workload itself
A Pod already running in the cluster is already on the inside. There are several ways out from here —
calling the API with the mounted ServiceAccount token, accessing the node filesystem through hostPath,
taking over the node with a privileged container, and entering the node's network namespace with hostNetwork.
Surfaces that are often missed
- Automatic ServiceAccount token mounting — it is on by default, so every Pod carries credentials to talk to the API server. If you do not need it, you must turn it off with
automountServiceAccountToken: false. - A state with no audit log — not only can you not block the attack, you cannot even know that it happened. If you do not record denials, the signal of a permission probing attempt does not exist at all.
- Admission webhooks — they are a defense but also a surface. If the webhook dies, depending on
failurePolicy, either all deployments are blocked (Fail) or the policy is bypassed entirely (Ignore). - CI/CD credentials — a pipeline usually has permission to deploy to the cluster. That token is cluster access.
Two properties specific to cloud native
It is dynamic. Pods keep dying and coming up anew, and their IPs change. IP-based firewall rules lose meaning, so policy moves to being based on labels and identity (such as SPIFFE).
Evidence is volatile. A compromised Pod may already be gone. If you do not send logs and audit records out of the cluster in advance, nothing is left to investigate.
What it looks like in the field
Among the failure patterns the author's blog compiled, one is especially valuable from the KCSA point of view — the reality of a Kubernetes Secret.
If you run etcdctl get /registry/secrets/default/app-db | hexdump -C, the key path shows up as plain text,
and one line of kubectl get secret ... -o jsonpath='{.data.password}' | base64 -d produces the password.
Base64 has no key and takes one command to reverse, so it is not encryption.
An RBAC problem is layered on top of this. If you have get secrets permission in a namespace,
you read every Secret in that namespace. In the author's words, "it is very common that edit permission granted to a developer for convenience is effectively permission to view production credentials."
Another misconception is about environment variables. Loading a .env file and then deleting it is useless —
the kernel already holds the values for each process and they can be read through /proc/PID/environ.
A sidecar in the same Pod, someone with access to the node, and a debug container attached with kubectl debug --target=app all see the same thing. The point is that an environment variable is a delivery method, not a storage location.
What to check in the next quiz
This module ends with a quiz. In the next module, we go down component by component, from the apiserver's authentication chain to the kubelet's authorization, and in the labs you write the list of dangerous flags and an EncryptionConfiguration yourself.