Cut One Node Out of a Service's Allowed Callers
Scenario
Namespace ledger holds three Pods, each on a different node:
| Pod | Node | What it is |
|---|---|---|
ledger-api | node-01 | the API |
billing-worker | cplane-01 | a caller |
stale-worker | node-02 | a caller on the node pending re-image |
ledger-api serves two ports. 8080 is the API other services call, 9090 is an admin endpoint
that should not be public. The Service publishes both on the same numbers, so from inside the
namespace:
curl http://ledger-api:8080
curl http://ledger-api:9090
Both answer from anywhere in the cluster right now.
Task
Security has flagged node-02, which is scheduled to be re-imaged. Until that happens, no Pod
running on it may reach the ledger API. Everything else in the cluster carries on unaffected,
billing-worker included.
Create a NetworkPolicy named ledger-api-allowlist in the ledger namespace that:
- Selects every Pod in the
ledgernamespace, so all of them are protected by it. - Admits TCP
8080from Pods across the cluster, with the single exception of Pods running onnode-02, including any created there later. - Admits nothing else: TCP
9090stays unreachable, and no second source is added.
The cluster hands out Pod addresses at start-up and the allocation is not stable between plays, so work out the values this cluster is actually using instead of assuming them.
Leave the Deployment, Service, caller Pods, and namespace labels untouched. Do not cordon, drain,
taint, or relabel any node, and do not evict stale-worker. The namespace must end up with
exactly one NetworkPolicy.
Hint 1
Start from the three source types a rule can carry:
kubectl explain networkpolicy.spec.ingress.from
Two of them describe Pods and namespaces by label. Labels will not do here: the requirement follows the node a Pod happens to run on, and a Pod does not carry its node's identity as a label.
That leaves the third type, which selects by address. Look at the fields it offers, including the one that removes a slice from a range:
kubectl explain networkpolicy.spec.ingress.from.ipBlock
Hint 2
Every node is assigned a Pod range, and Pods scheduled to that node take their addresses from it.
So excluding node-02's range excludes exactly the Pods running on node-02, now and for any Pod
created there later. Both values you need are on the Node objects:
kubectl get nodes -o custom-columns='NODE:.metadata.name,PODCIDR:.spec.podCIDR'
The per-node ranges are slices of one larger cluster-wide range. Admit the larger range and remove
node-02's slice from it.
Hint 3
The rule takes this shape:
spec:
podSelector: <scope: every Pod in the namespace>
policyTypes:
- <direction being controlled>
ingress:
- from:
- <source type>:
<field>: <range to admit>
<field>:
- <range to remove from it>
ports:
- protocol: <...>
port: <...>
Two details decide whether this passes:
- An omitted field is not a restriction. A missing
portslist means every port, which would leave the admin endpoint open. - A
portsentry names a port on the Pods the policy protects, not a Service port, even when the two numbers match.
See: Network Policies