network

package
v1.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 16, 2026 License: Apache-2.0 Imports: 14 Imported by: 0

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:

  1. 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.

  2. 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.

  3. 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.

Types

type Analyzer

type Analyzer struct{}

Analyzer produces network-policy-focused findings from a snapshot.

func New

func New() *Analyzer

New returns a new network-policy analyzer.

func (*Analyzer) Analyze

func (a *Analyzer) Analyze(_ context.Context, snapshot models.Snapshot) ([]models.Finding, error)

Analyze checks namespace-wide coverage, per-workload policy selection, and loose ingress/egress rules like "any namespace" or 0.0.0.0/0 egress.

func (*Analyzer) Name

func (a *Analyzer) Name() string

Name returns the module identifier used by the engine.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL