Protect node metadata & verify binaries

Block the cloud metadata endpoint; checksum platform binaries.

Advanced10 min · lesson 5 of 24

On a cloud node, the instance metadata service at 169.254.169.254 answers over plain HTTP with no authentication — and on many setups it will hand out the node’s cloud IAM credentials to anything that can reach it. From inside a compromised pod that is a straight line to the node’s role and, from there, into the cloud account: read S3 buckets, assume other roles, spin up infrastructure. It is one of the most common and highest-impact cluster-to-cloud escalation paths, and closing it is a NetworkPolicy one-liner.

CLUSTER-TO-CLOUD ESCALATION VIA METADATA
1CompromisedpodRCE or stolen foothold…2Reach169.254.169.254Metadata service: plain…3Read node IAMcreds/iam/security-credentials…4Pivot intocloud accountAssume roles, read S3,…
An egress policy that permits the world but excepts 169.254.169.254/32 breaks the chain at step two.

The fix is an egress policy that allows everything except the metadata IP. Because NetworkPolicy has no explicit deny, you express “not the metadata address” with an ipBlock that permits the world and carves out 169.254.169.254/32 as an exception. Apply it to workload namespaces; the workloads that legitimately need cloud credentials should get them through a proper mechanism (IRSA / workload identity), not by scraping the node.

deny-metadata.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata: { name: deny-cloud-metadata, namespace: payments }
spec:
podSelector: {}
policyTypes: [Egress]
egress:
- to:
- ipBlock:
cidr: 0.0.0.0/0
except:
- 169.254.169.254/32 # the metadata endpoint — blocked
ports: [] # (add your DNS + app egress rules alongside)
terminal
# from a pod, before the policy: the node’s cloud role leaks
$ kubectl exec -it web -- curl -s \
http://169.254.169.254/latest/meta-data/iam/security-credentials/
node-instance-role # <- an attacker now enumerates and assumes this
# after the policy: the request times out
$ kubectl exec -it web -- curl -m 3 http://169.254.169.254/ ; echo exit=$?
exit=28

Verify the platform binaries

A tampered kubelet, kubectl, or kubeadm is a full compromise wearing a trusted name, so before you install or trust a downloaded binary, check its SHA-256 against the checksum the release publishes over TLS. It is a two-line habit that catches a corrupted download, a mirror that was poisoned, or a man-in-the-middle swap on an unencrypted link.

terminal
# download the binary and its published checksum, then verify
$ curl -LO "https://dl.k8s.io/release/v1.30.2/bin/linux/amd64/kubectl"
$ curl -LO "https://dl.k8s.io/release/v1.30.2/bin/linux/amd64/kubectl.sha256"
$ echo "$(cat kubectl.sha256) kubectl" | sha256sum --check
kubectl: OK # any other output = do not run it
Metadata equals credentials
The metadata endpoint is not a curiosity — on a default cloud node it is the key to the account. Blocking egress to 169.254.169.254 from workloads closes an entire class of escalation for one short policy, and it is a frequent exam and audit item precisely because so many clusters leave it open.