← Back to DevBytes

Network Policies in Kubernetes Security Hardening and Best Practices

Understanding Kubernetes Network Policies

Network Policies are the primary mechanism for controlling traffic flow within a Kubernetes cluster at the IP address and port level. They act as a firewall between pods, allowing you to define exactly which network connections are permitted. Without Network Policies, all pods in a cluster can freely communicate with each other—a wide-open default that poses serious security risks. By implementing well-structured Network Policies, you enforce the principle of least privilege at the network layer, drastically reducing the attack surface and limiting the blast radius of a compromised workload.

What is a Network Policy?

A Network Policy is a Kubernetes resource that defines how groups of pods are allowed to communicate with each other and with other network endpoints. It uses labels to select pods and specifies rules for Ingress (incoming traffic) and Egress (outgoing traffic). Each rule can further filter traffic based on source pods, destination pods, namespaces, IP CIDR blocks, protocols, and ports. Crucially, Network Policies are additive—if multiple policies apply to a pod, the union of their rules is allowed, and any traffic not explicitly allowed by at least one policy is denied. This behavior makes them perfect for building a zero-trust network model.

Why Network Policies Matter for Security Hardening

In a default Kubernetes installation, there are no restrictions on pod-to-pod communication. An attacker who gains a foothold in one container can scan, probe, and connect to every other service in the cluster. Network Policies allow you to:

They are an essential layer in a defense-in-depth strategy, complementing RBAC, pod security standards, and container runtime hardening.

How Network Policies Work

Core Concepts: Pod Selectors, Policy Types, and Rules

A Network Policy specification consists of three main parts:

It’s important to understand that policies are enforced by the network plugin (CNI) in your cluster. Not all CNIs support Network Policies—Calico, Cilium, Weave Net, and Antrea do, while vanilla Flannel typically does not. Always verify that your cluster’s CNI provides the necessary implementation.

Creating Your First Network Policy

Prerequisites

Before you can apply a Network Policy, ensure:

A Simple Deny-All Policy

The most secure baseline is to deny all traffic to a pod, then progressively allow only what is required. The following policy selects all pods in a namespace and applies both Ingress and Egress rules with no allowed traffic. Because no rules are defined, all traffic is implicitly denied.

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: deny-all
  namespace: production
spec:
  podSelector: {}   # Selects every pod in the namespace
  policyTypes:
    - Ingress
    - Egress
  # No ingress or egress rules specified → all traffic blocked

Apply it with kubectl apply -f deny-all.yaml. After this, pods in the production namespace cannot send or receive any traffic—even DNS or communication with the API server will be blocked unless explicitly allowed. In practice, you’ll layer more specific policies on top.

Allowing Traffic from Specific Pods

Suppose you have a backend API pod labeled app: backend that should only accept requests from a frontend pod with label app: frontend. The following policy allows inbound traffic on TCP port 8080 from any pod that matches the frontend label, while denying everything else.

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-from-frontend
  namespace: production
spec:
  podSelector:
    matchLabels:
      app: backend
  policyTypes:
    - Ingress
  ingress:
    - from:
        - podSelector:
            matchLabels:
              app: frontend
      ports:
        - protocol: TCP
          port: 8080

Note that this policy only affects pods with app: backend. Other pods in the namespace remain unaffected unless additional policies apply. If a default deny-all policy exists, this rule opens exactly the specified path.

Restricting Traffic to a Namespace

Often you need to allow traffic from an entire namespace, such as a monitoring or logging namespace. Use a namespaceSelector to grant access to all pods within a namespace, optionally filtered by pod labels.

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-from-monitoring-ns
  namespace: production
spec:
  podSelector:
    matchLabels:
      app: metrics-collector
  policyTypes:
    - Ingress
  ingress:
    - from:
        - namespaceSelector:
            matchLabels:
              team: observability
      ports:
        - protocol: TCP
          port: 9100

This allows any pod in a namespace labeled team: observability to connect to the metrics-collector pod on port 9100. You can combine namespaceSelector and podSelector within the same from block to narrow access to specific pods inside that namespace (both conditions must match).

Combining Ingress and Egress Rules

A complete security posture often requires controlling both inbound and outbound traffic. The policy below isolates a database pod: it only allows ingress from a specific backend pod on port 5432, and only allows egress to a specific backup service and to the DNS service for name resolution.

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: database-isolation
  namespace: production
spec:
  podSelector:
    matchLabels:
      app: postgres-db
  policyTypes:
    - Ingress
    - Egress
  ingress:
    - from:
        - podSelector:
            matchLabels:
              app: backend-api
      ports:
        - protocol: TCP
          port: 5432
  egress:
    - to:
        - podSelector:
            matchLabels:
              app: backup-service
      ports:
        - protocol: TCP
          port: 9000
    - to:
        - namespaceSelector: {}
        - podSelector:
            matchLabels:
              k8s-app: kube-dns
      ports:
        - protocol: UDP
          port: 53

Notice the egress rule for DNS: it uses an empty namespaceSelector (all namespaces) combined with the pod selector for kube-dns, allowing the database to resolve names. Without this, the database would be completely isolated and unable to look up service endpoints.

Advanced Network Policy Scenarios

Using IP Block-Based Rules

Sometimes you need to allow or deny traffic based on IP CIDR ranges, for example to permit access from on-premise systems or restrict outbound connections to a specific external service. IP block rules work alongside pod and namespace selectors.

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-from-cidr
  namespace: production
spec:
  podSelector:
    matchLabels:
      app: public-api
  policyTypes:
    - Ingress
  ingress:
    - from:
        - ipBlock:
            cidr: 10.0.0.0/24
            except:
              - 10.0.0.5/32   # optionally exclude a specific IP
      ports:
        - protocol: TCP
          port: 443

ipBlock can also be used in egress rules to limit which external IP ranges a pod can contact, providing an additional layer of egress security.

Multi-Port and Protocol Rules

A single Network Policy can specify multiple ports and protocols for the same set of sources or destinations. This is common for services that expose both HTTP and metrics endpoints.

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-multi-port
  namespace: production
spec:
  podSelector:
    matchLabels:
      app: web-server
  policyTypes:
    - Ingress
  ingress:
    - from:
        - podSelector:
            matchLabels:
              role: load-balancer
      ports:
        - protocol: TCP
          port: 80
        - protocol: TCP
          port: 8080
        - protocol: TCP
          port: 9100

You can also mix protocols like TCP and UDP within the same rule set. For each port entry, specify the protocol and the port number.

Stacking Policies for Complex Models

Kubernetes merges multiple Network Policies that apply to a pod. This allows you to create a base deny-all policy, then layer on per-service, per-team, or per-environment policies. For example, you might have:

The resulting allowed traffic is the union of all rules. This composability is powerful but requires careful planning to avoid accidentally allowing unintended traffic.

Best Practices for Security Hardening

1. Start with a Default Deny-All Policy

For every namespace that contains sensitive workloads, deploy a catch-all Network Policy that denies both ingress and egress traffic. This shifts the cluster from an "allow-all" model to a "deny-all, allow-by-exception" model—the foundation of zero trust. Make sure to also allow DNS egress (as shown earlier) to prevent service disruption.

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

2. Use Meaningful Labels and Consistent Labeling Schemes

Network Policies rely entirely on labels for pod selection. Establish a clear labeling strategy across your organization—common patterns include app, component, tier, team, and environment. Avoid ambiguous labels that could accidentally grant access. Document the label taxonomy and enforce it through admission controllers or CI/CD pipelines.

3. Prefer Pod-Level Selectors Over IP Blocks

Pod selectors are dynamic and automatically adapt to pod restarts and scaling. IP block rules are static and brittle, requiring manual updates when IP ranges change. Use IP blocks only when you must control traffic from or to endpoints outside the cluster (e.g., VPN gateways, external databases). For internal cluster traffic, stick to pod and namespace selectors.

4. Isolate Namespaces with Careful Selectors

When using namespaceSelector, always pair it with a podSelector if you want to restrict traffic to specific pods within that namespace. An empty pod selector alongside a namespace selector will allow traffic from all pods in the target namespace, which might be too broad. Write rules that are as specific as possible.

5. Always Allow Required Infrastructure Services

Common infrastructure dependencies like DNS (kube-dns/CoreDNS), the API server, and monitoring agents need explicit egress rules when default deny is in place. Failing to allow DNS will cause service discovery failures; blocking the API server can break liveness probes and leader election. Include these allowances in a base policy that applies to all pods.

6. Test Policies in a Staging Environment

Network Policies can break application connectivity in subtle ways. Use a staging cluster that mirrors production labels and network topology to test new policies before rolling them out. Tools like kubectl describe networkpolicy and network policy visualizers (e.g., the Cilium UI or Calico’s policy debugger) help validate rule effects.

7. Log and Monitor Network Policy Events

Many CNIs can log denied traffic flows. Enable this logging to detect misconfigured policies or active intrusion attempts. Integrate logs into your SIEM or monitoring stack. For example, Calico provides a Log action in policies and Cilium offers network visibility through Hubble. Monitoring denied connections gives you early warning of malicious activity.

8. Combine Network Policies with Pod Security Standards

Network Policies are one layer. Complement them with Kubernetes Pod Security Standards (or the older PodSecurityPolicy) to restrict host network access, privileged containers, and capabilities. A pod that can bind to host ports or run as root can bypass network policies. Use restricted or baseline pod security profiles in tandem with network isolation.

9. Version Control and Review Policies

Treat Network Policies as code: store them in a Git repository, apply them via CI/CD, and require peer review. This prevents ad-hoc modifications that open up the cluster. Use tools like kube-linter or OPA Gatekeeper to enforce that every namespace has a default deny policy and that all policies follow your labeling standards.

10. Use Policy Tiering (if supported by your CNI)

Advanced CNIs like Calico support policy tiers and priority ordering, letting you enforce global policies that take precedence over namespace-scoped ones. This allows platform teams to mandate certain rules (e.g., “all pods must allow DNS and monitoring”) regardless of namespace-level policies. Explore your CNI’s extensions to build defense-in-depth.

Conclusion

Network Policies are an indispensable tool for hardening Kubernetes clusters. They transform the default flat network into a tightly controlled, segmented environment where every connection must be explicitly authorized. By starting with a deny-all posture, labeling pods consistently, and layering precise ingress and egress rules, you can dramatically reduce the risk of lateral movement and data exfiltration. Combine Network Policies with a supported CNI, continuous testing, logging, and complementary security controls to build a robust, zero-trust networking model inside your cluster. The effort invested in crafting and maintaining these policies pays off in a more resilient, auditable, and secure platform.

— Ad —

Google AdSense will appear here after approval

← Back to all articles