Skip to content

NetworkPolicy in practice

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.

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 list Ingress, Egress, or both. If you only list Ingress, 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/v1
kind: NetworkPolicy
metadata:
name: allow-frontend-to-backend
namespace: <your-tenant>-prod
spec:
podSelector:
matchLabels:
app: backend
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
app: frontend
ports:
- protocol: TCP
port: 8080

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/v1
kind: NetworkPolicy
metadata:
name: allow-egress-external-api
namespace: <your-tenant>-prod
spec:
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: 443

Cross-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: deployer

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

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:

Terminal window
kubectl get networkpolicy -n <your-tenant>-prod
kubectl describe networkpolicy <policy-name> -n <your-tenant>-prod

Check the PodSelector field in the describe output. If it shows <none>, the selector matched nothing.

Step 2 — Verify pod labels match the selector:

Terminal window
kubectl get pods -n <your-tenant>-prod --show-labels

Compare 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:

Terminal window
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.

MistakeSymptomFix
Egress rule without DNS allowPods lose all name resolutionAdd explicit DNS egress to kube-dns
AND vs OR selector indentationRule is wider or narrower than intendedPut namespaceSelector and podSelector under the same list element for AND
Policy in the wrong namespaceTraffic still blockedPolicy must be in the namespace of the destination pod
Label typo in selectorSelector matches zero podsCompare kubectl get pods --show-labels against the policy