intermediate 25 min read cloud-security Updated: 2025-07-18

A Practical Guide to Kubernetes Network Policies

Master Kubernetes network security by learning how to write, apply, and troubleshoot NetworkPolicy resources to control traffic flow between your pods.

🏷️ Topics Covered

kubernetes network policy guidekubernetes network securityhow to create network policy kuberneteskubernetes default deny policykubernetes ingress policy examplekubernetes egress dns policyallow traffic between namespaces kuberneteskubernetes ipBlocktroubleshooting kubernetes network policiespod network segmentationcalico network policy

What are Kubernetes Network Policies?

By default, Kubernetes has a "flat" network where all pods can communicate freely. This is a significant security risk. A single compromised pod could potentially attack any other service in the cluster. Network Policies are the native Kubernetes resource for controlling traffic flow at the IP address or port level (OSI Layer 3/4), acting as a firewall for your pods.

🎯 Core Concept

Think of Network Policies as the "guardrails" for your cluster's network traffic. They allow you to enforce network segmentation and a zero-trust security model, ensuring pods can only communicate with the services they're explicitly allowed to.

The Building Blocks of Network Policies

📥

Ingress Rules

Define which incoming traffic is allowed to reach a pod. If any ingress rules are specified, only matching traffic is permitted.

📤

Egress Rules

Define what outbound traffic a pod is allowed to initiate. If any egress rules are defined, only matching connections are permitted.

🏷️

Selectors

Policies use label selectors to target groups of pods. This flexible mechanism allows you to manage rules for entire applications at once.

Writing Basic Policies: The Default Deny Model

The most effective security posture is to deny all traffic by default and then explicitly allow only what is necessary. This is the foundation of a zero-trust network.

1. Default Deny All Ingress Traffic

This is the foundational policy for any secure namespace. It selects all pods in its namespace and specifies that no ingress traffic is allowed.

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-ingress
  namespace: my-app
spec:
  podSelector: {}
  policyTypes:
  - Ingress

2. Allowing Ingress from a Specific Namespace

Once you have a default deny, you can layer on policies. This policy allows pods with the label `app: api` to receive traffic from any pod in the namespace that is labeled `name: monitoring`.

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-api-from-monitoring
  namespace: my-app
spec:
  podSelector:
    matchLabels:
      app: api
  policyTypes:
  - Ingress
  ingress:
  - from:
    - namespaceSelector:
        matchLabels:
          name: monitoring

3. Allowing Ingress based on Pod Selector

This is the most common pattern. It allows pods with `app: database` to receive traffic on port 5432 from pods with the label `app: api` within the same namespace.

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-db-from-api
  namespace: my-app
spec:
  podSelector:
    matchLabels:
      app: database
  policyTypes:
  - Ingress
  ingress:
  - from:
    - podSelector:
        matchLabels:
          app: api
    ports:
    - protocol: TCP
      port: 5432

Advanced Ingress & Egress Rules

Real-world applications require more complex rules, such as controlling outbound traffic and combining multiple rulesets.

1. Controlling Egress (A Common Pitfall: DNS)

When you apply egress policies, you must explicitly allow all necessary outbound traffic, including DNS. This policy allows `app: api` pods to send DNS queries and connect to external services on port 443.

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-api-egress
  namespace: my-app
spec:
  podSelector:
    matchLabels:
      app: api
  policyTypes:
  - Egress
  egress:
  # Allow DNS traffic to pods labeled k8s-app: kube-dns
  - to:
    - podSelector:
        matchLabels:
          k8s-app: kube-dns
    ports:
    - port: 53
      protocol: UDP
    - port: 53
      protocol: TCP
  # Allow other external traffic over HTTPS
  - to:
    - ipBlock:
        cidr: 0.0.0.0/0
        except:
        - 10.0.0.0/8 # Exclude internal cluster CIDR
    ports:
    - port: 443
      protocol: TCP

2. Combining Ingress Rules (OR Logic)

You can combine multiple `from` clauses in an ingress rule. Traffic is allowed if it matches *any* of the clauses. This policy allows the `database` pod to receive traffic from the `api` pods OR the `backup-job` pods.

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-db-access-combined
  namespace: my-app
spec:
  podSelector:
    matchLabels:
      app: database
  policyTypes:
  - Ingress
  ingress:
  - from:
    # Rule 1: Allow from 'api' pods
    - podSelector:
        matchLabels:
          app: api
    # Rule 2: Allow from 'backup' pods
    - podSelector:
        matchLabels:
          job: backup
    ports:
    - protocol: TCP
      port: 5432

Best Practices for Policy Management

Start with Deny-All

Always apply a default deny-all policy to every new namespace to ensure a zero-trust foundation.

🏷️

Use Consistent Labels

Establish a clear, consistent labeling strategy for all your applications to make policy management scalable.

📜

Be Specific and Explicit

Avoid overly broad rules. Whenever possible, specify ports and protocols to enforce the principle of least privilege.

👁️

Visualize Your Policies

Use tools like Cilium's Hubble to visualize traffic flows and understand the real-world impact of your policies.

Troubleshooting Common Issues

❌ Pods cannot connect to a Service

Problem: After applying a policy, pods can no longer communicate with a service they previously could.

Solutions:

  • Check Labels: Ensure both the source pod and the destination pod have the correct labels matching the `podSelector` in the policy. A single typo will cause the policy to fail.
  • Check Namespaces: If using a `namespaceSelector`, verify that the source namespace has the required label.
  • Check Egress Rules: The source pod might be missing an egress rule that allows it to initiate the connection.

❌ Pod cannot connect to the internet or resolve DNS

Problem: A pod can't make external API calls or resolve domain names.

Solutions:

  • Missing DNS Egress Rule: You must explicitly allow egress traffic to the `kube-dns` service (usually on port 53 TCP/UDP) from your pod.
  • Missing External Egress Rule: To reach the internet, you need an egress rule with an `ipBlock` of `0.0.0.0/0`. Be sure to exclude internal cluster CIDRs.
  • CNI Not Ready: On cluster startup, sometimes pods come up before the CNI is ready, leading to DNS resolution failures. Ensure your readiness checks are robust.

Getting Started Step-by-Step

1

Verify Your CNI

Ensure your cluster's Container Network Interface (e.g., Calico, Cilium, Weave Net) supports and enforces Network Policies.

2

Apply a Default Deny

Choose a non-critical namespace, apply a `default-deny-ingress` policy, and observe that traffic is blocked.

3

Incrementally Add "Allow" Rules

One by one, add specific "allow" policies for required traffic, testing connectivity at each step.

4

Automate with Policy-as-Code

Store your Network Policy YAML files in a Git repository and apply them as part of your CI/CD process.

🎉 Congratulations!

You now have a solid understanding of how to use Kubernetes Network Policies to secure your cluster. You've learned how to:

  • ✅ Isolate workloads and implement a default-deny posture.
  • ✅ Write specific ingress and egress rules using selectors.
  • ✅ Control traffic based on pods, namespaces, and IP addresses.
  • ✅ Troubleshoot common connectivity issues caused by policies.