# Network Policies and Pod-to-Pod Communication Security

> Chapter 16 of the Courseiva CNCF-CKAD curriculum — https://courseiva.com/learn/cncf-ckad/network-policies-and-security

**Official objective:** 6.3 — Define and apply Network Policies to control traffic between pods

## Introduction

A Kubernetes cluster is a busy apartment building where pods are apartments and traffic is visitors moving between them. Without rules, any pod can talk to any other pod, which is a security nightmare for applications that handle sensitive data. Network Policies let you lock down which pods are allowed to communicate, giving you control over internal traffic — a critical skill for the CKAD exam and for building secure systems.

## The 4-Door Sedan Party Invitation Analogy

You are hosting a party in a 4-door sedan. You send out invitations to 5 friends: each invitation specifies exactly who can enter which door. For example, Alice can only enter the front-left door, Bob can only enter the front-right door, and Charlie can only enter the back-left door. But you also decide that no one from the same street can enter through the back-right door. 


When a friend arrives, you check their name against the invitation list for that specific door. If they have permission, they can step inside. If they don't, the door stays locked. You also have a rule that anyone who is not on any invitation list is turned away at the gate, regardless of which door they try. 


This is exactly how Kubernetes Network Policies work. Each door is a pod. Each friend is another pod trying to send traffic to that pod. The invitation list is the Network Policy — it defines which pods (by their labels or namespaces) are allowed to send traffic to the target pod's ports. Just as you control who enters which door, a Network Policy controls traffic from specific source pods to a specific destination pod's port. If no Network Policy exists for a pod, all traffic is allowed (like a party with no door security). But once you apply a policy, only traffic matching the rules is permitted; everything else is blocked (like turning away uninvited guests).

## Core explanation

Network Policies are rules that control traffic flow between pods in a Kubernetes cluster. Think of them as a firewall for internal pod communication. By default, Kubernetes allows all pod-to-pod traffic, meaning any pod can send a network request to any other pod, regardless of what application they run. This is like leaving every apartment door unlocked in a building. Network Policies change this by explicitly allowing or denying traffic based on conditions like pod labels or namespace names.


Every Network Policy consists of three key parts: a pod selector, ingress rules, and egress rules. The pod selector picks the set of pods that the policy applies to, using labels like "app: frontend" or "tier: backend". Ingress rules define which sources can send traffic to those pods, and egress rules define which destinations those pods can send traffic to. Each rule can specify one or more ports (like 80 for web traffic or 5432 for databases) and one or more sources (for ingress) or destinations (for egress). Sources and destinations are identified using podSelectors (matching pods within the same namespace) or namespaceSelectors (matching all pods in a namespace) or IP blocks (like 10.0.0.0/16).


When you apply a Network Policy to a pod, Kubernetes enforces it at the network layer using a plug-in like Calico, Cilium, or Weave. If no Network Policy applies to a pod, all traffic is allowed. But once even one Network Policy selects that pod, only traffic matching those policy rules is allowed, and everything else is denied — this is known as the "whitelist" or "default-deny" model. This is a crucial detail: a single policy can inadvertently block all traffic if its rules are too restrictive.


Why does this exist? Because in real applications, you need to isolate services. For example, a frontend web server should only talk to an API backend, and the API backend should only talk to a database. Without Network Policies, a compromised frontend pod could directly attack the database. Network Policies enforce microsegmentation, ensuring each component can only communicate with authorised partners.


Network Policies replace the need for complex, per-host firewall rules that require manual configuration on every node. They are cluster-native, meaning they are declared alongside other Kubernetes resources like Deployments and Services. They work at Layer 3 (IP) and Layer 4 (TCP/UDP) of the OSI model — they cannot filter based on application-layer data like HTTP methods or URLs. For that, you need additional tools like service meshes or API gateways.


The podselector within a Network Policy can use multiple labels to create precise targeting. For instance, you can select only pods labelled both "app: web" and "env: prod". The namespaceSelector can match a whole namespace, like choosing all pods in the "database-ns" namespace. The ipBlock allows you to specify external CIDR ranges, which is useful when your pods need to talk to external services outside the cluster.


Here is a simple example. You have three pods: frontend-pod with label "app: frontend", api-pod with label "app: api", and db-pod with label "app: db". You want only frontend-pod to talk to api-pod on port 8080, and only api-pod to talk to db-pod on port 5432. You create two Network Policies:

- One for api-pod that allows ingress from pods with label "app: frontend" on port 8080, and egress to pods with label "app: db" on port 5432.
- One for db-pod that allows ingress from pods with label "app: api" on port 5432.


Notice that the api-pod's policy controls both incoming and outgoing traffic. This is typical: you need to think about both directions. A common trap is forgetting to add egress rules, which can block the target pod from making outbound requests, such as DNS queries. By default, DNS runs on port 53 UDP, so to allow pods to resolve domain names, you may need to add egress rules allowing UDP to the cluster DNS service IP or IP block.


In summary, Network Policies give you fine-grained control over pod-to-pod communication. They are essential for security compliance (like PCI-DSS or HIPAA) and for building multi-tier applications. For the CKAD exam, you must master writing and applying these policies in YAML, understanding how selectors combine, and recognising the default-allow behaviour.

## Real-world context

An IT professional at a fintech company needs to secure a three-tier application: a frontend web server, a backend API server, and a PostgreSQL database, all running in the same Kubernetes namespace. The business requirement is strict: the frontend should only talk to the API on port 3000, the API should only talk to the database on port 5432, and the database should accept no inbound traffic from any pod except the API. Additionally, all pods need outbound access to the cluster DNS service for name resolution, but no pod should be able to reach the internet directly — traffic must go through a explicit proxy.


The IT pro starts by checking the existing setup. They run "kubectl get pods --show-labels" to see the pod labels: frontend has "tier: frontend", API has "tier: api", and database has "tier: db". They then write three YAML files.

- First, a NetworkPolicy for the database pod. In the YAML, they set podSelector to matchLabels: tier: db. In ingress, they add a rule that allows from pods with matchLabels: tier: api on port 5432 TCP. They also add an egress rule to allow UDP to port 53 for DNS, because the database needs to resolve hostnames. They apply it with "kubectl apply -f db-policy.yaml".
- Second, a NetworkPolicy for the API pod. The podSelector matches tier: api. Ingress allows from pods with tier: frontend on port 3000. Egress allows to pods with tier: db on port 5432, and to the cluster DNS IP (e.g., 10.96.0.10) on port 53 UDP.
- Third, a NetworkPolicy for the frontend pod. PodSelector matches tier: frontend. Ingress allows nothing (because frontend does not need inbound traffic from other pods). Egress allows to pods with tier: api on port 3000, and to the cluster DNS IP on port 53 UDP.


After applying all three, the IT pro tests by exec-ing into a pod and using curl or nc. They verify that frontend can reach api:3000, api can reach db:5432, but frontend cannot reach db:5432 directly — the connection times out. They also confirm that the database can still resolve DNS by pinging a hostname from within the database container.


This is a typical real-world use case. The IT pro might also need to use namespaceSelectors if the application spans multiple namespaces (e.g., API in one namespace, database in another). For example, they might have a namespace "backend" and another "data". The NetworkPolicy for the database would then use namespaceSelector: matchLabels: name: backend. 


Common tasks include troubleshooting blocked traffic by examining NetworkPolicy objects with "kubectl describe networkpolicy", checking pod annotations, and looking at the CNI plug-in logs. The professional also needs to be aware that a single misconfigured policy can take down the entire application, so they typically deploy changes in stages and monitor connectivity metrics.

## Exam focus

The CKAD exam tests Network Policies in a focused way. Here is exactly what you need to know.


The exam objectives for Network Policies (objective 6.3) require you to "Define and apply Network Policies to control traffic between pods". The typical question type gives you a scenario: a set of pods with specific labels, and you must write a NetworkPolicy YAML that allows only certain traffic. You might be asked to restrict ingress, egress, or both. You may be asked to apply the policy to a namespace, using a YAML file provided or creating one from scratch.


Key concepts they love to test:

- podSelector: how to select pods using matchLabels (exact match) or matchExpressions (set-based). The exam frequently uses simple matchLabels.
- policyTypes: you must specify which directions the policy applies to. If you only include ingress rules, you do not need to set policyTypes, but if you use egress, you must explicitly set policyTypes: ["Ingress", "Egress"] or just ["Egress"]. Omitting this is a common trap — the exam will expect you to include the correct type.
- Default behaviour: remembering that if no NetworkPolicy selects a pod, all traffic is allowed. If any policy selects a pod, only traffic matching rules is allowed, and everything else is denied. This means adding a policy that only allows ingress from one source will deny all other ingress, including from pods that we want to keep talking.
- Combining selectors: a rule can have both podSelector and namespaceSelector, and they must match both (AND logic) unless you use multiple rules. The exam tests whether you understand that within a single rule, multiple fields are ANDed.
- Ports: specifying ports is optional but common. The exam expects you to include the correct port number and protocol (TCP unless specified UDP).
- ipBlock: this is less common but may appear in questions where you need to allow or deny traffic from external IPs, often with except ranges.


Exam traps to watch for:

- Forgetting to add egress rules for DNS: many scenarios require pods to resolve hostnames, but if you only add ingress rules, egress is blocked by default. The question might not mention DNS explicitly, but you need to infer it from the need for pod-to-pod communication.
- Overly restrictive policy: you write a policy that inadvertently blocks the pod from reaching the cluster DNS or the Kubernetes API server. Always consider essential services.
- Wrong namespace selection: if the question says "apply to all pods in namespace x", you must set the podSelector to {} (empty) to select all pods in that namespace.
- Ignoring existing policies: imagine you are asked to add a rule to an existing set of policies. The exam expects you to either edit the existing YAML or create a new policy that complements the existing one without conflicting.


To prepare, make sure you can write a basic NetworkPolicy YAML from memory:

- apiVersion: networking.k8s.io/v1
- kind: NetworkPolicy
- metadata: name, namespace (optional, defaults to current)
- spec: podSelector, policyTypes, ingress (array of rules), egress (array of rules)


Practise with the official Kubernetes documentation open — the exam allows you to browse it. You should be fast at finding the NetworkPolicy page and copying the template. During the exam, you will use a terminal to create YAML files with vi or nano and then apply them with kubectl. Speed is crucial, so get comfortable with the syntax.


In summary, the CKAD exam tests your ability to write precise YAML for pod-to-pod traffic control. They will not ask theory questions; you must produce a working policy in a terminal. Master the podSelector, policyTypes, and the default-deny behaviour, and you will be well-prepared.

## Step by step

1. **Identify the target pods** — First, determine which pods you want to protect or restrict. Use kubectl get pods --show-labels to see their labels. Write down the labels you will use in the podSelector of your NetworkPolicy. This step is crucial because an incorrect selector can either lock down too many pods or leave vulnerabilities open.
2. **Create the policy YAML** — Using a text editor (like vi or nano), create a YAML file. Start with apiVersion: networking.k8s.io/v1, kind: NetworkPolicy, and metadata: name: your-policy-name. In the spec, set podSelector to match the labels from step 1. Decide whether you need ingress, egress, or both, and set policyTypes accordingly. Then write the rules following the Kubernetes documentation structure.
3. **Define ingress rules** — If you want to control inbound traffic, add an ingress array. Each rule can specify from sources using podSelector, namespaceSelector, or ipBlock. For example, to allow traffic from pods with label app: frontend, use: - from: - podSelector: matchLabels: app: frontend. Remember that multiple entries in from are ORed, but multiple selectors within a single from are ANDed. Always specify ports if needed.
4. **Define egress rules** — If you want to control outbound traffic, add an egress array. Similar to ingress, use to with selectors. A common requirement is egress to DNS: - to: - ipBlock: cidr: 10.96.0.0/12 (the cluster DNS IP range) and ports: - protocol: UDP port: 53. Without this, pods cannot resolve domain names.
5. **Apply and verify the policy** — Run kubectl apply -f your-policy.yaml. Then test connectivity by exec-ing into a pod: kubectl exec -it <pod-name> -- /bin/sh, then use curl, nc, or wget to test. Verify allowed connections succeed and denied connections timeout or refuse. If something breaks, check the policy with kubectl describe networkpolicy your-policy-name and look for errors in your selector logic or missing ports.

## Comparisons

### podSelector vs namespaceSelector

**podSelector:**
- Matches pods based on their labels within the same namespace as the policy.
- You can use matchLabels or matchExpressions to specify exact label values.
- Only pods from the same namespace are considered for matching.

**namespaceSelector:**
- Matches all pods in a namespace specified by the namespace's labels.
- You can select a namespace by name or by label (e.g., matchLabels: name: production).
- Allows cross-namespace traffic control by referring to a different namespace.

### Ingress rules vs Egress rules

**Ingress rules:**
- Control incoming traffic to selected pods.
- Use the from field to specify allowed sources.
- If no ingress rules exist but policyTypes includes Ingress, all inbound traffic is denied.

**Egress rules:**
- Control outgoing traffic from selected pods.
- Use the to field to specify allowed destinations.
- If no egress rules exist but policyTypes includes Egress, all outbound traffic is denied.

### Default-allow when no policy exists vs Default-deny once a policy selects the pod

**Default-allow when no policy exists:**
- If no NetworkPolicy selects a pod, all ingress and egress traffic is allowed.
- This is the Kubernetes default network model.
- No configuration is needed for pods to communicate freely.

**Default-deny once a policy selects the pod:**
- As soon as even one NetworkPolicy selects a pod, all traffic not explicitly allowed is denied.
- The policy defines a whitelist of permitted sources/destinations and ports.
- This includes blocking traffic that was previously allowed, like DNS queries.

## Diagram

_Flowchart showing Network Policies controlling traffic between frontend, API, and database pods, with allowed and blocked flows._

```mermaid
flowchart TD
    A[Pod A: frontend] -->|egress allowed| B[Pod B: api]
    B -->|egress allowed| C[Pod C: db]
    A -.->|blocked| C
    D[DNS Service] -->|egress allowed| A
    D -->|egress allowed| B
    D -->|egress allowed| C
    E[Internet] -.->|blocked| A
    E -.->|blocked| B
    E -.->|blocked| C
    F[NetworkPolicy frontend] -->|selects| A
    G[NetworkPolicy api] -->|selects| B
    H[NetworkPolicy db] -->|selects| C
```

## Common misconceptions

- **Misconception:** Network Policies apply to all traffic entering and leaving the pod, including traffic from the pod to the internet. **Reality:** Network Policies only control traffic within the Kubernetes cluster, not traffic to external IPs unless you explicitly use ipBlock to specify external ranges. Outbound traffic to the internet not covered by any egress rule is blocked if any policy selects the pod, but typically you need to add egress rules for DNS and external services. (Beginners assume Network Policies are like a full firewall that handles all network traffic, but Kubernetes does not manage external traffic by default. The CNI plug-in controls what happens outside the cluster.)
- **Misconception:** A NetworkPolicy with no ingress or egress rules allows all traffic by default. **Reality:** A NetworkPolicy always enforces a default-deny behaviour for the directions specified in policyTypes. If you specify "Ingress" in policyTypes but include no ingress rules, all ingress traffic is denied. If you do not specify policyTypes, it defaults to "Ingress", which means all ingress is denied if you have no ingress rules — but egress remains allowed unless you explicitly set policyTypes to include "Egress". (This stems from misunderstanding the default-deny model. People think that an empty rule means 'allow all', but in Kubernetes, an empty rule list means deny all for that direction.)
- **Misconception:** If a pod has no NetworkPolicy applied to it, it cannot receive any traffic. **Reality:** If no NetworkPolicy selects a pod, all traffic to and from that pod is allowed. This is the default behaviour. Only when at least one NetworkPolicy selects the pod does the default-deny start applying. (This confusion arises because people often test with policies applied and assume that creating a policy is mandatory for any communication. But Kubernetes is open by default until you lock it down.)
- **Misconception:** Network Policies can filter based on application-layer details like HTTP methods or URL paths. **Reality:** Network Policies operate at Layer 3 (IP) and Layer 4 (TCP/UDP) of the OSI model. They cannot inspect HTTP headers or URLs. For layer 7 filtering, you need a service mesh (like Istio) or an API gateway. (Many beginners come from network firewall backgrounds where next-generation firewalls can do deep packet inspection. They assume Kubernetes Network Policies offer similar capabilities.)

## Key takeaways

- By default, all pod-to-pod traffic is allowed in Kubernetes, creating an open network model that must be restricted using Network Policies.
- A Network Policy with no rules for a given direction (ingress or egress) denies all traffic for that direction, implementing a whitelist model.
- To select all pods in a namespace, set podSelector: {} (empty curly braces) in the Network Policy spec.
- Within a single ingress or egress rule, multiple selectors (podSelector, namespaceSelector, ipBlock) are combined with AND logic, meaning all conditions must match.
- Always include an egress rule for UDP port 53 (DNS) when appling egress restrictions, otherwise pods may fail to resolve hostnames.
- The policyTypes field must be explicitly set to ["Ingress", "Egress"] if you want to control both directions; omitting it defaults to just ingress.
- When you apply a Network Policy to a pod, it immediately starts enforcing the rules; existing connections may be terminated abruptly depending on the network plug-in.
- Network Policies are enforced by CNI plug-ins (like Calico, Cilium, Weave), not by Kubernetes itself, so the specific behaviour can vary slightly between providers.

## FAQ

**What happens if I apply a NetworkPolicy with an empty podSelector?**

An empty podSelector {} matches all pods in the namespace. This means the policy applies to every pod in that namespace, effectively creating a default-deny for the entire namespace. Use this carefully as it can lock down all communication.

**Can I have multiple NetworkPolicies for the same pod?**

Yes. Multiple policies can select the same pod. If any policy selects the pod, the union of all rules applies: traffic is allowed if it matches at least one policy's rules. If no matching rule exists in any policy, traffic is denied.

**How do I allow all inbound traffic to a pod except from a specific IP?**

You cannot deny specific sources with a NetworkPolicy; you can only allow specific sources. To block a specific IP, you would need to allow all (using ipBlock with CIDR 0.0.0.0/0) but then that source IP will also be allowed. For deny-based filtering, you need a CNI-specific feature like Calico's GlobalNetworkPolicy or a firewall outside the cluster.

**What is the difference between ingress and egress rules in a NetworkPolicy?**

Ingress rules control incoming traffic to the selected pods (where traffic comes from). Egress rules control outgoing traffic from the selected pods (where traffic goes to). Both use the same selector types but with different keywords: from for ingress, to for egress.

**Do I need to restart pods after applying a NetworkPolicy?**

No. Network Policies are enforced by the CNI plug-in at the network layer, and they take effect immediately for new connections. Existing connections may be terminated or allowed to persist depending on the plug-in implementation (e.g., Calico typically terminates existing connections that violate the new policy).

**How do I delete a NetworkPolicy?**

Run kubectl delete networkpolicy <policy-name>. If the policy was the only one selecting a set of pods, those pods revert to the default-allow behaviour, meaning all traffic is allowed again. If there are other policies selecting the same pods, the restrictions from those policies remain.

## Check your understanding

1. **What is the default behaviour for pod-to-pod traffic in a Kubernetes cluster when no NetworkPolicy exists?**

   Answer: All traffic is allowed between pods by default.

2. **When writing a NetworkPolicy, what does an empty podSelector {} do?**

   Answer: It selects all pods in the namespace where the policy is created.

3. **If you specify policyTypes: ["Ingress"] in a NetworkPolicy but provide no ingress rules, what happens to inbound traffic to the selected pods?**

   Answer: All inbound traffic to those pods is denied because an empty rule list means no traffic is allowed.

4. **How do you allow traffic from pods in a different namespace using a NetworkPolicy?**

   Answer: You use a namespaceSelector inside the ingress rule (e.g., from: - namespaceSelector: matchLabels: name: my-namespace) optionally combined with a podSelector to target specific pods.

5. **Why might a pod lose DNS resolution after applying a NetworkPolicy with egress rules?**

   Answer: Because the NetworkPolicy's default-deny blocks all egress traffic not explicitly allowed, including DNS queries on UDP port 53. You need an egress rule to allow UDP 53 to the cluster DNS IP.

---

Interactive version with quiz and diagrams: https://courseiva.com/learn/cncf-ckad/network-policies-and-security
