NetworkPolicy in practice
When you need your own NetworkPolicy
Section titled “When you need your own NetworkPolicy”The four pre-approved rules cover DNS, intra-tenant pod traffic, Traefik ingress, and Prometheus scraping. You need a custom NetworkPolicy when traffic falls outside those four lanes:
- A pod that must reach an external endpoint (API, database, object store) over the internet or a private CIDR.
- A pod that must accept traffic from a source outside your tenant (rare — requires an RCS ticket for the cross-tenant side, plus your own allow rule for the destination).
Because the floor is default-deny, every custom rule is additive — it opens a lane without weakening the existing posture.
Anatomy of a tenant-scoped rule
Section titled “Anatomy of a tenant-scoped rule”A NetworkPolicy lives in the namespace where the destination pod runs. It selects the destination pod(s) via podSelector and then declares which sources (ingress) or destinations (egress) are allowed.
Key fields:
podSelector— selects which pods in this namespace the rule applies to. An empty selector{}means “all pods in this namespace”.ingress— who can send traffic to the selected pods.egress— where the selected pods can send traffic to.policyTypes— must listIngress,Egress, or both. If you only listIngress, the rule does not affect egress at all (and vice versa).
Example: allow ingress from a specific pod in the same namespace
Section titled “Example: allow ingress from a specific pod in the same namespace”This rule lets pods labelled app: backend receive traffic on port 8080 from pods labelled app: frontend, both in the same namespace.
apiVersion: networking.k8s.io/v1kind: NetworkPolicymetadata: name: allow-frontend-to-backend namespace: <your-tenant>-prodspec: podSelector: matchLabels: app: backend policyTypes: - Ingress ingress: - from: - podSelector: matchLabels: app: frontend ports: - protocol: TCP port: 8080Example: allow egress to an external CIDR
Section titled “Example: allow egress to an external CIDR”This rule lets pods labelled app: data-loader reach an external API at 203.0.113.0/24 on port 443. The DNS egress rule is already covered by the pre-approved allow, but if you lock egress down to specific CIDRs you must also re-allow DNS explicitly — otherwise name resolution breaks.
apiVersion: networking.k8s.io/v1kind: NetworkPolicymetadata: name: allow-egress-external-api namespace: <your-tenant>-prodspec: podSelector: matchLabels: app: data-loader policyTypes: - Egress egress: # Allow DNS (required when specifying explicit egress rules) - to: - namespaceSelector: {} podSelector: matchLabels: k8s-app: kube-dns ports: - protocol: UDP port: 53 - protocol: TCP port: 53 # Allow the external API - to: - ipBlock: cidr: 203.0.113.0/24 ports: - protocol: TCP port: 443Cross-namespace traffic within the same tenant
Section titled “Cross-namespace traffic within the same tenant”The pre-approved intra-tenant rule already permits cross-namespace traffic within your tenant. If you need to restrict it further — for example, allowing only a specific pod in namespace <your-tenant>-staging to reach a pod in <your-tenant>-prod — use namespaceSelector alongside podSelector:
ingress: - from: - namespaceSelector: matchLabels: kubernetes.io/metadata.name: <your-tenant>-staging podSelector: matchLabels: app: deployerNote the indentation: namespaceSelector and podSelector under the same - element means both must match (AND logic). If they are separate - elements, either one matching is enough (OR logic). Getting this wrong is the most common source of overly permissive rules.
Debugging dropped traffic
Section titled “Debugging dropped traffic”When traffic is silently dropped, the problem is almost always a NetworkPolicy that does not match the way you think it does.
Step 1 — Verify the policy exists and selects the right pods:
kubectl get networkpolicy -n <your-tenant>-prodkubectl describe networkpolicy <policy-name> -n <your-tenant>-prodCheck the PodSelector field in the describe output. If it shows <none>, the selector matched nothing.
Step 2 — Verify pod labels match the selector:
kubectl get pods -n <your-tenant>-prod --show-labelsCompare the labels on the source and destination pods against what the policy expects. Label typos (e.g. app: front-end vs app: frontend) are the single most common cause of “my policy doesn’t work.”
Step 3 — Check that policyTypes includes the direction you care about:
A policy with policyTypes: [Ingress] does not affect egress at all. If traffic is being dropped on the outbound side, you need Egress in policyTypes and an explicit egress allow.
Step 4 — Test connectivity:
kubectl exec -n <your-tenant>-prod <source-pod> -- curl -v --connect-timeout 5 http://<dest-service>:8080/A connection timeout with no response means the packet was dropped by NetworkPolicy. A connection refused means the packet reached the destination but nothing is listening — that is a Service or application issue, not a NetworkPolicy issue.
Common mistakes
Section titled “Common mistakes”| Mistake | Symptom | Fix |
|---|---|---|
| Egress rule without DNS allow | Pods lose all name resolution | Add explicit DNS egress to kube-dns |
| AND vs OR selector indentation | Rule is wider or narrower than intended | Put namespaceSelector and podSelector under the same list element for AND |
| Policy in the wrong namespace | Traffic still blocked | Policy must be in the namespace of the destination pod |
| Label typo in selector | Selector matches zero pods | Compare kubectl get pods --show-labels against the policy |