Skip to content
SRE & DevOpsHow-To Published Updated 5 min readViews unavailable

How to Set Up Kubernetes NetworkPolicies

Deploy Kubernetes NetworkPolicies with verified CNI enforcement, default-deny baselines, explicit DNS access, namespace labels, and negative tests.

By default, Kubernetes networking allows every pod to communicate with every other pod in the cluster, regardless of namespace — a NetworkPolicy is how you restrict that to only the traffic you actually intend to allow.

Step 1: confirm your cluster’s CNI plugin actually supports NetworkPolicy

kubectl get pods -n kube-system

The Kubernetes API can accept a NetworkPolicy even when the networking implementation does not enforce it. Check the managed-service or CNI documentation for the installed version and then prove enforcement with a denied connection. Standard API support does not imply support for every vendor extension.

Step 2: start by understanding the default-allow baseline

Before writing any policies, understand what you’re changing: with zero NetworkPolicies applied, every pod can reach every other pod on any port. Once a policy selects a pod, that pod becomes isolated for each direction named by the policy; only traffic allowed by applicable rules passes.

Policies are additive: an allowed connection may be permitted by any policy selecting the source for egress and any policy selecting the destination for ingress. They do not contain ordered firewall rules, and one policy cannot override an allow from another with a deny. Both sides must permit a connection when both are isolated.

Step 3: create a default-deny-all policy for a namespace

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-all
  namespace: myapp
spec:
  podSelector: {}
  policyTypes: [Ingress, Egress]

An empty podSelector: {} matches every pod in the namespace — combined with no rules under Ingress/Egress, this denies all traffic by default, a common, deliberate starting point before adding specific allow rules on top.

Step 4: allow specific ingress traffic between two applications

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-frontend-to-backend
  namespace: myapp
spec:
  podSelector:
    matchLabels: {app: backend}
  ingress:
    - from:
        - podSelector:
            matchLabels: {app: frontend}
      ports:
        - port: 8080

This allows pods labeled app: frontend to reach pods labeled app: backend on port 8080 specifically — everything else remains blocked by the default-deny policy from Step 3.

This source podSelector refers only to pods in the policy’s namespace. To require a source in another namespace with a particular label, place namespaceSelector and podSelector in the same from item. Separate list items mean namespace OR pod and can create a much broader allow.

Step 5: allow traffic from a specific namespace

ingress:
  - from:
      - namespaceSelector:
          matchLabels:
            kubernetes.io/metadata.name: monitoring

Useful for allowing a monitoring namespace’s Prometheus instance to scrape metrics endpoints across other namespaces, without opening those pods up to arbitrary traffic from everywhere else.

Kubernetes applies the immutable kubernetes.io/metadata.name label to namespaces, avoiding reliance on a hand-maintained name label. Inspect the actual namespace labels and add a pod selector if only the Prometheus workload—not every pod there—should be allowed.

Step 6: allow necessary egress traffic explicitly

egress:
  - to:
      - namespaceSelector:
          matchLabels:
            kubernetes.io/metadata.name: kube-system
        podSelector:
          matchLabels: {k8s-app: kube-dns}
    ports:
      - protocol: UDP
        port: 53
      - protocol: TCP
        port: 53

Don’t forget DNS, including TCP fallback. DNS labels and placement vary by distribution, so inspect them instead of copying k8s-app: kube-dns blindly. Behavior for Service virtual IPs, nodes, and rewritten addresses can vary depending on where the network implementation performs address translation.

Step 7: test the policy actually behaves as intended

kubectl run test-allowed --image=TEST_IMAGE_BY_DIGEST --rm -it \
  --labels="app=frontend" -- wget -T 3 -qO- http://backend:8080/health
kubectl run test-denied --image=TEST_IMAGE_BY_DIGEST --rm -it \
  --labels="app=other" -- wget -T 3 -qO- http://backend:8080/health

Confirm the allowed path succeeds and an unrelated pod without the matching label is actually blocked — testing both the positive and negative case, not just that the intended traffic works.

Use a pinned diagnostic image and explicit timeouts. Test ingress and egress independently, from the correct namespaces, over every required protocol, and repeat after CNI upgrades. A timeout can also mean broken DNS or a missing application, so verify the target before isolation and inspect CNI flow logs where available.

Step 8: apply policies incrementally, namespace by namespace

Rolling out default-deny policies across an entire cluster all at once, without first testing in one namespace, risks breaking legitimate traffic patterns you didn’t realize existed — applying incrementally and monitoring for unexpected connection failures is a meaningfully safer rollout path.

Inventory dependencies from application documentation and observed flows, but do not automatically make every observed connection permanent. Include DNS, identity, telemetry, and required external APIs. NetworkPolicy is generally layer 3/4 control; it does not authenticate a caller or authorize an HTTP path. Pair it with workload identity, TLS, and application authorization.

Maintain policy as application code

Store policies beside the workload and review label changes with policy changes. Run positive and negative connectivity tests in CI or pre-production. Alert on policy-controller errors and pods missing required labels. Define an audited emergency procedure that restores a narrowly scoped path without deleting all namespace protection.

Why NetworkPolicy’s default-allow starting point catches people off guard

Most engineers coming from traditional network security assume “deny by default, explicitly allow what’s needed” is how Kubernetes networking already works out of the box — it isn’t, until you introduce NetworkPolicies yourself. Understanding that the default is wide open, and that policies are additive restrictions layered on top rather than a pre-existing restrictive baseline, is the conceptual model that makes the rest of NetworkPolicy configuration make sense.

Related:

Sources:

Comments