What you can get done in 30 minutes
Kubernetes NetworkPolicy files are already a machine-readable model of intent: who can talk to whom, on which ports, and under what selectors. The problem is that YAML is not an operational view. In an incident or a pre-prod review, you need two artifacts fast:
- A zero-trust connectivity graph that makes allowed paths and “default deny gaps” obvious.
- A blast-radius checklist that translates policy intent into verifiable risk questions (and concrete tests).
This workflow is designed for a tight 30-minute window and assumes you already have a set of NetworkPolicy YAMLs checked into git.
Minute 0–5: Inventory and normalize the input
Collect the policies and establish scope
Start by collecting the exact YAML that applies to the environment you’re reviewing (staging vs production often diverges). Pull from the repo or export from the cluster with your preferred tooling, then group policies by namespace.
In this first pass, write down:
- Namespaces covered (and namespaces with no policies).
- Whether your CNI enforces NetworkPolicy (Calico/Cilium/etc.); if enforcement is off, your YAML is documentation, not control.
- Any “catch-all” policies (e.g., broad ingress or egress allows) that will dominate the graph.
Normalize label selectors into an asset list
Connectivity graphs become useful when nodes represent stable “assets,” not ephemeral pods. Convert selectors into a small set of asset identities such as:
- Workloads: app=payments-api, app=web, app=worker
- Dependencies: app=redis, app=postgres
- Infrastructure: kube-dns, ingress controller, observability agents
If the YAML uses inconsistent labels (common in older clusters), treat this as a finding and keep the graph honest: you can only model what selectors actually match.
Minute 5–15: Convert YAML into a zero-trust connectivity graph
Use the policy model: default deny + explicit allow edges
A zero-trust graph is not “everything can talk.” It’s default deny with explicit, minimal edges. In graph terms:
- Each asset identity becomes a node (namespace-aware).
- Each allowed ingress rule becomes a directed edge source → destination with ports/protocol as edge attributes.
- Each allowed egress rule becomes a directed edge source → destination as well.
Important nuance: NetworkPolicy semantics depend on whether a pod is selected by any policy for ingress/egress. If a pod is selected by a policy that defines ingress, then only that ingress is allowed. Same for egress. Your graph must show implicit allows due to missing enforcement (pods not selected for a direction) as a separate category—these are often the largest hidden blast-radius multipliers.
Model “to/from” blocks accurately
When translating YAML:
- podSelector narrows to pods in the same namespace unless paired with namespaceSelector.
- namespaceSelector changes the scope; represent this as an inter-namespace edge.
- ipBlock indicates traffic to/from CIDRs (external services, VPC ranges, on-prem). Represent these as special “external” nodes like CIDR:10.0.0.0/8.
- ports should be captured as edge metadata; don’t collapse everything into “allowed” without ports.
At this stage, don’t chase perfection. You want an accurate first graph you can refine later.
Turn the graph into a shareable diagram
The fastest way to get alignment is to render the model as a diagram you can annotate. If you already have a text representation (nodes and edges), you can paste the relationships into napkin.ai to quickly generate a clean, editable connectivity visual—useful for design reviews and incident retrospectives when “what talks to what” needs to be understood in minutes rather than hours.
Keep the diagram scoped: one namespace per diagram unless you’re specifically validating cross-namespace trust boundaries.
Minute 15–25: Build the blast-radius checklist from the graph
A checklist is only valuable if it’s testable and tied to real failure modes. Use your graph to produce a short list of concrete questions, grouped by the most common blast-radius drivers.
Checklist section 1: Default-deny coverage
- Which workloads have no ingress policy selecting them (ingress effectively open within the cluster or per CNI defaults)?
- Which workloads have no egress policy selecting them (data exfiltration path risk)?
- Are there namespaces with no policies at all?
Checklist section 2: High-privilege pathways
- Is anything allowed to reach kube-system components beyond DNS?
- Do policies allow traffic to node-local services or broad CIDRs via ipBlock?
- Are there broad selectors like podSelector: {} or namespace-wide allows that defeat segmentation?
Checklist section 3: Data stores and shared dependencies
- For each database/cache node, can you list the exact calling workloads and ports?
- Are read/write paths indistinguishable (single port with many callers), or is access segmented by service?
- Are migrations, jobs, and admin pods included in the model (often overlooked)?
Checklist section 4: Ingress and egress choke points
- From the ingress controller, which services are reachable internally?
- For egress, is outbound traffic constrained to known dependencies (APIs, payment providers, telemetry endpoints), or is it “anywhere”?
- Is DNS egress explicitly allowed, and is it limited to kube-dns/CoreDNS pods and port 53/UDP (and 53/TCP if needed)?
Checklist section 5: Operational change safety
- What breaks if a selector label changes (e.g., app=web → app=frontend)?
- Do policies depend on mutable labels like version, build, or helm chart labels?
- Is there a review step that detects widening edges (e.g., adding a namespaceSelector that matches more namespaces)?
If you already run contract tests for external dependencies, adapt the same mindset to network intent: policies are contracts between workloads. The same discipline described in closing the AI builder to production testing gap with contract tests maps cleanly to connectivity guarantees—only here the contract is “this caller can reach that callee on this port.”
Minute 25–30: Validate quickly and capture action items
Spot-check with targeted probes
Pick 3–5 edges from the graph and validate them with simple connectivity checks from representative pods (not from your laptop). Then pick 1–2 non-edges (traffic that should be denied) and verify they fail. This gives you high confidence your model reflects reality and that enforcement is working.
Write down fixes as precise diffs
End the session by turning observations into small, reviewable changes:
- Add missing default-deny policies per namespace/direction.
- Replace broad selectors with specific app labels.
- Constrain ipBlock ranges and document why each CIDR exists.
- Split “shared dependency” policies by caller group if the blast radius is too large.
Finally, store the graph next to the YAML in your repo. Treat the diagram as living documentation: the moment your NetworkPolicy changes, your connectivity graph and checklist should be updated in the same pull request.
