TT Lab
Get started
Learn Learning paths Courses

KCSA — Kubernetes Security Associate

CNI, Storage, Client — Risks at the Edge Components

Continue in TT Lab

In one line

NetworkPolicy is enforced by the CNI plugin, so if there is no plugin enforcing it, the policy has no effect at all. hostPath is a passage to the node's credentials and the runtime socket, and a CSI driver holds the secrets of the storage backend. On the client side, a single kubeconfig file is the key to the whole cluster and can also be a file that makes arbitrary commands run.

Why this was needed

If the three components of the previous reading were inside the control plane and the node, these three are at the cluster's boundary. The CNI is between the Pod and the network, storage is between the Pod and the disk, and the client is between the person and the API server. A boundary is a place where the trust levels on the two sides differ, so it is the first line you draw in a threat model. But all three are places where it is easy to misunderstand that "Kubernetes will surely guard it for us." I created a NetworkPolicy so it must be blocked, I used a PVC so it must be isolated, a kubeconfig is just a configuration file — all three are wrong.

How it works

CNI — the plugin enforces the policy

The prerequisites section of the network policies document is clear. Network policies are implemented by the network plugin, you must use a networking solution that supports NetworkPolicy, and if you create a NetworkPolicy resource without a controller that implements it, it has no effect. The API server only stores the object and does not reject it, so a situation where a policy exists but traffic flows arises silently.

You must also know the default state of policy. If a namespace has no policy at all, all traffic into and out of that namespace's Pods is allowed. Isolation is declared for ingress and egress separately, and as the document puts it, "isolation" is not absolute but means "some restriction applies." That is why the security checklist recommends putting a default-deny policy that selects all Pods in each namespace and going with an allowlist approach, and puts whether the CNI in use supports policy as its first item. The CNI plugin itself is loaded by the container runtime, as seen in the previous reading, so write permission on the plugin binary and configuration directory is permission that can disable the node's network policy enforcement. Encryption in transit is not provided by every CNI, and the checklist also notes that without it, a service mesh is the alternative.

Storage — the hostPath passage and CSI secrets

The hostPath section of the volumes document starts with a warning. hostPath carries many security risks, so avoid it if you can, and use a local PersistentVolume instead. The reasons are specific — if you reach the host filesystem, privileged system credentials such as the kubelet's credentials and privileged APIs such as the container runtime socket are exposed and can be used for container escape or attacks on other parts of the cluster. Even if you restrict admission to allow only specific directories, the restriction is valid only if that mount is forced read-only. This is because if you allow any host path as read-write to an untrusted Pod, that Pod's container can flip the mount. The baseline profile of the Pod Security Standards blocking hostPath is the basic defense against this risk.

The secrets on the CSI side are of a different kind. According to the Secrets and Credentials section of the CSI driver documentation, the credentials a driver needs to access the backend are divided into two tiers. Driver-level secrets (such as the backend's service account) are injected directly into the driver Pod at deployment with the standard Secret deployment method, while operation-level and volume-level secrets are allowed by the CSI spec to be carried in each operation request such as CreateVolume, and the administrator creates a Secret and passes its key by writing it in a StorageClass or VolumeSnapshotClass. The sidecar containers' Secret-related RBAC rules are off by default to reduce permissions and are turned on only when needed, and the spec marks sensitive fields with the csi_secret annotation so they are kept out of logs. Translated into a threat model: a subject that can read the service account of the CSI controller Pod or the Secret that a StorageClass references obtains credentials for the entire storage backend.

Client — a kubeconfig is both a key and an executable

The organizing cluster access using kubeconfig files document explains that kubectl by default reads $HOME/.kube/config and that you can specify another file with the KUBECONFIG environment variable or the --kubeconfig flag, and then adds a warning. It says to use only kubeconfig files from trusted sources, and that a specially crafted kubeconfig can lead to malicious code execution or file exposure, so you should inspect an untrusted file first, as you would a shell script.

Why a configuration file becomes code execution is explained in the credential plugins section of the authentication document. client-go, and kubectl and the kubelet that use it, can execute external commands to obtain user credentials (stabilized in 1.22). It is a feature for protocols that client-go does not support directly, such as LDAP, Kerberos, OAuth2, and SAML; client-go uses the token the plugin returns as a bearer token, and on the server side the webhook token authenticator handles the TokenReview. So the command written in the exec entry of a kubeconfig runs with the user's privileges every time you run kubectl. That is why you need to open a kubeconfig you receive and look at which binary the exec block points to.

The nature of the credentials inside a kubeconfig also decides the risk. The authentication mechanisms hardening guide lists the constraints of X.509 client certificates — they cannot be revoked individually so if leaked they can be used until expiry, invalidating one requires reissuing the CA, no issuance record stays in the cluster, the private key cannot be protected with a password so anyone who reads the file can use it, and the group is baked into the certificate's O value and cannot be changed during its lifetime. As for service account tokens, the authentication document says they are "fully valid even when used outside the cluster," so a token put into a pipeline tool must be handled with the same weight as a kubeconfig. The response is exactly as the cluster security documents say — short lifetimes, automatic rotation, and an authentication provider that can control the lifetime of issued tokens.

What it looks like in the field

A NetworkPolicy was deployed, yet everything gets through. This is common in clusters built with kwok or a plugin that does not support policy. An object showing up in kubectl get networkpolicy and traffic being blocked are different things, and whether it is enforced must be confirmed by trying an actual connection.

A kubeconfig shared in a team channel. The client-certificate-data in the file cannot be revoked, so once a leak is confirmed it stays valid until expiry. If you use certificate-based user credentials, keep the lifetime short, and moving human users to an external authentication provider such as OIDC is the direction the hardening guide points to.

What to check in the next quiz

The quiz asks about the behavior when the scheduler's authentication flags are left empty, the default binding of the kube-proxy metrics port, what access to the runtime socket means, the effect of a NetworkPolicy without an enforcing controller, the condition under which a hostPath restriction becomes valid, and why a kubeconfig can be code execution.