Contain the blast radius: keep pods away from the control plane

Contain the blast radius: keep pods away from the control plane

Every lesson so far secured traffic between apps. But the most damaging thing a compromised pod can do is reach the control plane - and the crown jewel there is etcd, which holds the entire cluster's API state. Anything that can talk to etcd can read or rewrite everything. No application pod has any reason to. This lesson locks etcd down to its one legitimate client with an ordinary GlobalNetworkPolicy.

What you'll learn

The policy

apiVersion: projectcalico.org/v3
kind: GlobalNetworkPolicy
metadata:
  name: etcd-allow-apiserver-only
spec:
  selector: component == 'etcd'
  types:
    - Ingress
  ingress:
    - action: Allow
      source:
        selector: component == 'kube-apiserver'

etcd's only legitimate client is the kube-apiserver; the scheduler, controller-manager and add-ons all go through the apiserver, never straight to the datastore.

What to observe

Allowed

Denied

The trap is scoping the allow too broadly. source.selector: all(), or allowing the whole tier == 'control-plane', turns those deny cells green and hands the datastore to far more than needs it. Allow exactly the one client.

Beyond pods: HostEndpoints

This GNP protects a control-plane pod. To stop a pod from reaching a node's own interface (kubelet, SSH, host-network services), you make that interface a policy target with a HostEndpoint - it then appears in the matrix as a host/<name> row, and a GlobalNetworkPolicy can firewall it just like a pod. That's the deepest layer of blast-radius containment; locking etcd to the apiserver, as here, is the highest-value first step.

Recap

The infrastructure layer deserves policy too: lock etcd to the kube-apiserver and a single pod compromise can't become a cluster takeover. That secured a control-plane pod - next we go one layer deeper and firewall the nodes themselves with HostEndpoints and a custom Tier.