Documentation
¶
Overview ¶
Package network analyzes NetworkPolicy coverage and permissiveness so that unprotected namespaces, uncovered workloads, and overly-broad policies surface as findings.
Content for network-policy findings. Each rule has a builder that takes runtime context (namespace, policy name, workload labels, CIDR) and returns an enriched ruleContent with scope-aware language, an attacker walkthrough, ordered remediation steps, and structured references / MITRE technique citations.
Sources: Kubernetes NetworkPolicy docs, CIS Kubernetes Benchmark 5.3.2, NSA/CISA Kubernetes Hardening Guide v1.2, Calico/Cilium docs, MITRE ATT&CK Containers, Christophe Tafani-Dereeper EKS-IMDS escalation, ahmetb network-policy-recipes.
Cross-namespace NetworkPolicy analysis. Looks at every NetworkPolicy and reports when its peer rules explicitly grant ingress or egress that crosses a namespace boundary involving a sensitive namespace (kube-system, kube-public, kube-node-lease, default). The intent is to surface the soft-tenancy violations that look fine in "kubectl get netpol" — a policy in `team-a` that allows traffic from `kube-system` is structurally suspicious because it pierces the cluster's strongest cheap isolation boundary.
Detection model. For each NetworkPolicy P in namespace `polNS`:
- For each ingress.from peer:
- If peer.namespaceSelector matches a namespace `peerNS` other than `polNS`, the pair is (peerNS -> polNS) ingress.
- If peer.namespaceSelector is empty (`{}`), it matches every namespace, so the pair is (any -> polNS) ingress (we surface this as `*` in the source).
- For each egress.to peer:
- Symmetric: matched namespace becomes the destination, polNS the source.
We emit a finding only when at least one of `polNS` or the peer namespace is sensitive. The shared isSystemNamespace helper in analyzer.go enumerates the system set; we add `default` here because user-created workloads land there when no explicit namespace is set, so any rule that bridges `default` to a tenant namespace deserves a note.
Severity: MEDIUM. Cross-namespace allow rules are sometimes legitimate (a logging sidecar in kube-system scraping app namespaces) but they are almost always worth a second look in a multi-tenant cluster, so we lean toward surfacing rather than suppressing.
IMDS (Instance Metadata Service) egress reachability analysis.
The link-local endpoint 169.254.169.254 (the AWS/Azure/GCP cloud Instance Metadata Service) is the single most attacked egress destination in modern cloud Kubernetes clusters. A pod that can reach it can mint cloud-provider IAM credentials and pivot from container RCE to full cloud-account compromise. Compare blog.christophetd.fr's EKS worker-node IAM walkthrough.
This module asks one question per pod / workload: "can this pod's egress reach 169.254.169.254/32?" We answer it by walking the pod's namespace's NetworkPolicies in egress mode:
If no NetworkPolicy with policyTypes:[Egress] selects the pod, Kubernetes leaves the pod non-isolated for egress (allow-all), so IMDS is reachable. Fire KUBE-NETPOL-IMDS-001 with the "no-egress-policy" reason.
If at least one egress policy selects the pod, compute the union of its allowed peers. If any allowed peer is an ipBlock whose CIDR contains 169.254.169.254 AND whose `except:` list does not carve out the IMDS IP, fire KUBE-NETPOL-IMDS-001 with the "explicit-allow" reason. Common offenders here: ipBlock 0.0.0.0/0 (the universal allow), 169.254.0.0/16 (the entire link-local range), or 169.254.169.254/32 itself.
If every selecting policy either does not include IMDS in its ipBlock reach OR carves it out via `except:`, the pod is fine and no finding fires.
The check is intentionally conservative on the "default-deny saves us" case: if a pod has any egress policy at all whose peers do not include a 169.254-containing ipBlock, we trust the deny-by-default semantic and stay quiet. This avoids drowning operators who have done the right thing.
Severity: HIGH. The SSRF -> cloud-creds chain is one of the highest-impact real-world attack patterns in cloud Kubernetes; score 7.8.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.