SecLab
SSRF & calling external APIswalkthroughDifficulty 4/529 min

Deny-by-default egress at the network layer

Objective: After this lesson you can write an egress NetworkPolicy for a workload and name three things it does not stop.

V13API7:2023NSA/CISA GuideNetworkPolicyssrf
Step 1 of 5 · read6 min

The layer that needs nobody to remember anything

The previous lesson gave a correct application-layer fix. It shares one weakness with every fix at that layer: it only protects where somebody calls it. A new HttpClient in another service, a library making its own calls, a background job, or the payload from m6-challenge — none of them pass through your ConnectCallback.

This is the same relationship as the WHERE clause and RLS in module 2, and this lesson is SSRF's RLS: a lower layer, needing nobody to remember anything, applying to every connection regardless of who opens it.

LayerProtectsDoes not protect
Allowlist + ConnectCallbackWherever somebody calls itAnother HttpClient, a library, a background job, a build-time payload
Egress NetworkPolicyEvery connection leaving the podSee the final step — there are three routes

The mechanism: Kubernetes NetworkPolicy defaults to allow all, and the most important thing to understand is how that default is inverted: the moment a pod is selected by any policy with policyTypes: [Egress], that pod switches to denying all egress except what is explicitly permitted. There is no "deny" rule — only "select these pods and permit the following".

Two traps that make a policy look right while doing nothing:

(1) The CNI does not enforce NetworkPolicy. This is the worst trap because it is completely silent: kubectl apply succeeds, kubectl describe shows the policy in full, and nothing is enforced. Flannel does not support NetworkPolicy; Calico, Cilium and the cloud providers' CNIs do. This is the same failure mode as RLS on an owner-accessed table in rls-second-layer.

(2) Blocking egress and forgetting DNS. DNS is egress. Block all egress and the pod resolves no hostname, so every call fails in a hard-to-diagnose way — and somebody will widen the policy to to: [{}] "to make it work". Permitting UDP/TCP 53 to kube-system is a mandatory line.

View path

Comments

Join the discussion
Sign up to comment

Commenting needs an account with at least one completed lesson. That condition is what keeps this thread worth reading: every point belongs to someone who can be asked back, and reputation accrues over time.

Sign upSign in

You can still read every comment below without an account. Signing in brings you back to this exact spot, not to the top of the page.

Loading comments…