# How to Implement Sophisticated Network Policies in Kubernetes for Least-Privilege Communication

> Master Kubernetes network policies for least-privilege communication. Learn to implement default deny and whitelist traffic using labels, CIDRs, and OPA Gatekeeper.

- Repository: [Arie Bregman/devops-exercises](https://github.com/bregman-arie/devops-exercises)
- Tags: how-to-guide
- Published: 2026-02-28

---

**Implement a default-deny baseline in every namespace, then explicitly whitelist required pod-to-pod, cross-namespace, and external flows using label selectors and CIDR blocks, validating configurations with OPA Gatekeeper.**

Kubernetes NetworkPolicies provide a firewall-like mechanism to control pod traffic, but achieving true least-privilege requires a systematic approach beyond basic rules. According to the `bregman-arie/devops-exercises` repository, particularly in [`topics/kubernetes/README.md`](https://github.com/bregman-arie/devops-exercises/blob/main/topics/kubernetes/README.md) around line 2697, implementing sophisticated network policies involves layering default-deny baselines with granular allow rules for both internal and external communication paths.

## Establishing a Default-Deny Security Baseline

By default, Kubernetes pods accept traffic from any source. To implement a **zero-trust** posture, you must first establish a **default-deny** policy that selects all pods in a namespace and blocks both ingress and egress traffic. This forces every subsequent policy to explicitly define permitted communication paths.

Create a global deny-all policy in each namespace to establish your secure baseline:

```yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: deny-all
  namespace: prod
spec:
  podSelector: {}
  policyTypes:
  - Ingress
  - Egress

```

Because NetworkPolicies are namespace-scoped, apply this pattern to every namespace requiring isolation. This single policy flips the default behavior from allow-all to deny-all, ensuring that only explicitly defined traffic flows are permitted.

## Implementing Intra-Namespace Service-to-Service Rules

After establishing the deny-all baseline, whitelist specific service-to-service communication using **pod selectors** and labels. Consistent labeling (e.g., `app=frontend`, `app=backend`) is critical, as NetworkPolicies rely on these selectors to identify traffic endpoints.

Allow front-end pods to communicate with back-end pods on port 8080:

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

```

This approach enforces least-privilege within the namespace by ensuring the back-end only accepts connections from explicitly labeled front-end instances on the required port.

## Enforcing Cross-Namespace Least-Privilege Communication

Traffic between namespaces is blocked by default once you implement deny-all policies. To enable **cross-namespace communication**, you must create complementary policies on both sides: an egress rule in the source namespace and an ingress rule in the target namespace.

First, create the ingress policy in the target namespace (`backend-ns`) allowing traffic from the source namespace:

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

```

Then, create the corresponding egress policy in the source namespace (`frontend-ns`):

```yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-to-backend-ns
  namespace: frontend-ns
spec:
  podSelector:
    matchLabels:
      app: frontend
  policyTypes:
  - Egress
  egress:
  - to:
    - namespaceSelector:
        matchLabels:
          name: backend-ns
      podSelector:
        matchLabels:
          app: backend
    ports:
    - protocol: TCP
      port: 8080

```

This bidirectional enforcement ensures that **namespace isolation** remains intact while permitting only the specific pod-to-pod flows required for application functionality.

## Controlling External Service Access

To implement least-privilege for external communication, use **egress rules** with `ipBlock` selectors to whitelist specific CIDR ranges or DNS-resolved IP blocks. This prevents pods from accessing unauthorized internet endpoints while permitting necessary third-party API calls.

Allow back-end pods to call an external API on HTTPS:

```yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: egress-allow-external-api
  namespace: prod
spec:
  podSelector:
    matchLabels:
      app: backend
  policyTypes:
  - Egress
  egress:
  - to:
    - ipBlock:
        cidr: 203.0.113.0/24
    ports:
    - protocol: TCP
      port: 443

```

Replace the CIDR with the actual IP range of your external service. For DNS-based external services, you must specify the resolved IP ranges, as NetworkPolicies operate at layer 3/4 and do not perform DNS resolution.

## Validating Policies with OPA Gatekeeper

To prevent configuration drift and enforce organizational standards, validate NetworkPolicy configurations using **OPA Gatekeeper**. As noted in the Policy Testing section of [`topics/kubernetes/README.md`](https://github.com/bregman-arie/devops-exercises/blob/main/topics/kubernetes/README.md) around line 2567, Gatekeeper constraints can require specific policy patterns or deny namespaces lacking proper network isolation.

Deploy a constraint to ensure every namespace contains at least one NetworkPolicy:

```yaml
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sRequiredNetworkPolicy
metadata:
  name: require-np-per-namespace
spec:
  enforcementAction: deny
  match:
    kinds:
    - apiGroups: [""] 
      kinds: ["Namespace"]

```

This validation layer ensures that developers cannot bypass the default-deny requirement, maintaining the least-privilege posture across the cluster.

## Monitoring and Observability

Verify that only permitted flows are occurring using **Falco** or **Calico** audit logs. These tools detect and alert on policy violations, providing visibility into attempted unauthorized connections. For testing, use `kubectl exec` to attempt connections between pods or employ specialized tools like `npdiag` to validate policy behavior before production deployment.

## Summary

- **Start with default-deny** policies in every namespace to establish a secure baseline where no traffic is allowed by default.
- **Label pods consistently** to enable precise selector-based rules for service-to-service communication.
- **Require bidirectional rules** for cross-namespace traffic, implementing both egress policies in source namespaces and ingress policies in target namespaces.
- **Whitelist external access** using specific CIDR blocks in egress rules to limit internet exposure.
- **Validate configurations** with OPA Gatekeeper constraints to prevent misconfiguration drift and ensure compliance with security standards.

## Frequently Asked Questions

### What is the default traffic behavior in Kubernetes without NetworkPolicies?

Without NetworkPolicies, Kubernetes pods accept traffic from any source both internally and externally. This permissive default allows unrestricted communication between all pods in the cluster and external networks, which violates least-privilege principles. The first step in securing the cluster is implementing a default-deny NetworkPolicy that blocks all ingress and egress traffic until explicitly allowed.

### How do you allow traffic between two specific namespaces?

You must create complementary rules on both sides of the connection. In the target namespace, create an ingress policy using `namespaceSelector` and `podSelector` to identify the source. In the source namespace, create an egress policy using matching selectors to identify the destination. Both policies must reference the specific ports and protocols required, and both namespaces must have the `name` label properly set for the `namespaceSelector` to match.

### Can NetworkPolicies block traffic to external services?

Yes, by implementing default-deny egress policies and only whitelisting specific external destinations using `ipBlock` with CIDR notation. Once a pod is selected by any NetworkPolicy defining egress rules, it can only communicate with explicitly allowed destinations. This effectively blocks all external internet access except for the specific IP ranges and ports defined in your egress whitelist.

### How do you validate NetworkPolicy configurations before deployment?

Use OPA Gatekeeper to enforce policy constraints that require specific NetworkPolicy patterns, such as mandatory default-deny rules in every namespace. As documented in [`topics/kubernetes/README.md`](https://github.com/bregman-arie/devops-exercises/blob/main/topics/kubernetes/README.md) at line 2567, Gatekeeper validates resources against rego policies during admission, preventing non-compliant configurations from reaching the cluster. Additionally, test policies using `kubectl exec` to verify connectivity or tools like Calico's policy preview features to simulate rule behavior.