KCSA — Network Policies
Kubernetes Network Model — The Default
By default, Kubernetes uses a flat network where every pod can communicate with every other pod across all namespaces. There are no firewalls between pods.
This means:
- Pod A in
namespace-acan reach Pod B innamespace-b - Any compromised pod can scan and reach every other pod in the cluster
- There is no namespace-level network isolation out of the box
This is a significant security risk. Network Policies exist to fix this.
What Are Network Policies?
A NetworkPolicy is a Kubernetes resource that acts as a firewall for pod-to-pod traffic. It controls:
- Ingress — which traffic is allowed into a pod
- Egress — which traffic is allowed out of a pod
Network Policies are namespace-scoped and use label selectors to target pods.
Key Characteristics
| Aspect | Detail |
|---|---|
| Scope | Namespace-scoped |
| Target selection | Label selectors on pods |
| Default behavior | No NetworkPolicy = all traffic allowed |
| Policy behavior | NetworkPolicy on a pod = deny all except what’s explicitly allowed |
| Additive | Multiple policies are unioned (OR logic between policies) |
| Requires CNI | Only works with CNI plugins that support NetworkPolicy |
Ingress vs Egress
| Direction | Controls | Example |
|---|---|---|
| Ingress | Traffic coming into the selected pods | “Allow traffic from frontend pods to backend pods on port 8080” |
| Egress | Traffic going out of the selected pods | “Allow backend pods to reach the database on port 5432” |
Ingress Egress
[Source Pod] ──────────────► [Target Pod] ──────────────► [Destination Pod]
(into target) (out of target)
NetworkPolicy Spec Structure
Every NetworkPolicy has four key parts:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: my-policy
namespace: default
spec:
podSelector: # 1. Which pods does this policy apply to?
matchLabels:
app: backend
policyTypes: # 2. Which directions are controlled?
- Ingress
- Egress
ingress: # 3. What ingress traffic is allowed?
- from:
- podSelector:
matchLabels:
app: frontend
ports:
- port: 8080
egress: # 4. What egress traffic is allowed?
- to:
- podSelector:
matchLabels:
app: database
ports:
- port: 5432
Understanding podSelector
podSelector: {}— selects all pods in the namespacepodSelector: { matchLabels: { app: web } }— selects only pods withapp: weblabel- The
podSelectordefines the target pods the policy applies to
Understanding policyTypes
- If
policyTypesincludesIngress— the pod’s incoming traffic is restricted - If
policyTypesincludesEgress— the pod’s outgoing traffic is restricted - If a type is listed but no rules are specified — all traffic of that type is denied
Essential YAML Examples
Default Deny All Ingress
The most important starting point for network security:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-ingress
namespace: production
spec:
podSelector: {} # Applies to ALL pods in the namespace
policyTypes:
- Ingress # No ingress rules = deny all incoming traffic
This policy:
- Applies to every pod in the
productionnamespace - Blocks all incoming traffic (no
ingressrules = deny all) - Does not affect egress (outgoing traffic is still unrestricted)
Default Deny All Egress
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-egress
namespace: production
spec:
podSelector: {} # Applies to ALL pods in the namespace
policyTypes:
- Egress # No egress rules = deny all outgoing traffic
Warning: Denying all egress also blocks DNS resolution (port 53). You typically need to allow DNS egress alongside a default deny.
Default Deny All Traffic (Both Directions)
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-all
namespace: production
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress
Allow DNS Egress (Common Companion to Default Deny)
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-dns
namespace: production
spec:
podSelector: {}
policyTypes:
- Egress
egress:
- to:
- namespaceSelector: {} # Any namespace
ports:
- port: 53
protocol: UDP
- port: 53
protocol: TCP
Allow Ingress from Specific Namespace
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-frontend-ingress
namespace: backend-ns
spec:
podSelector:
matchLabels:
app: backend
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector:
matchLabels:
name: frontend-ns
podSelector:
matchLabels:
app: frontend
ports:
- port: 8080
protocol: TCP
Allow Ingress on Specific Port
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-port-80
namespace: web-ns
spec:
podSelector:
matchLabels:
app: web
policyTypes:
- Ingress
ingress:
- ports:
- port: 80
protocol: TCP
This allows traffic on port 80 from any source (no from selector means all sources are allowed on this port).
Network Policy Selectors
NetworkPolicy uses three types of selectors to identify traffic sources and destinations:
| Selector | Description | Example |
|---|---|---|
podSelector |
Selects pods by label within the same namespace | matchLabels: { app: frontend } |
namespaceSelector |
Selects pods in namespaces matching the labels | matchLabels: { env: production } |
ipBlock |
Selects traffic from/to specific IP ranges (CIDR) | cidr: 10.0.0.0/24 |
ipBlock Example
ingress:
- from:
- ipBlock:
cidr: 10.0.0.0/8
except:
- 10.0.1.0/24 # Exclude this subnet
AND vs OR Logic — Critical Exam Topic!
This is one of the most commonly tested concepts on the KCSA exam. The behavior depends on how selectors are structured in the YAML.
OR Logic (Separate from Entries)
Each - under from: is a separate rule. Traffic matching any one of them is allowed.
ingress:
- from:
- podSelector: # Rule 1: pods with app=frontend
matchLabels:
app: frontend
- namespaceSelector: # Rule 2: pods in namespaces with env=staging
matchLabels:
env: staging
This means: Allow traffic from frontend pods OR from any pod in staging namespaces.
AND Logic (Same from Entry)
When podSelector and namespaceSelector are in the same entry (no - before podSelector), they combine as AND.
ingress:
- from:
- namespaceSelector: # Both selectors in the SAME entry
matchLabels:
env: staging
podSelector: # No "-" before podSelector = AND
matchLabels:
app: frontend
This means: Allow traffic from pods with app=frontend that are also in namespaces with env=staging. Both conditions must be true.
Side-by-Side Comparison
# OR logic — two separate entries (note the "-" before each)
ingress:
- from:
- podSelector: # Entry 1
matchLabels:
app: frontend
- namespaceSelector: # Entry 2
matchLabels:
env: staging
# Allows: frontend pods (any namespace) OR any pod in staging namespace
# AND logic — single entry (no "-" before podSelector)
ingress:
- from:
- namespaceSelector: # Single entry
matchLabels:
env: staging
podSelector: # Same entry, no "-"
matchLabels:
app: frontend
# Allows: frontend pods ONLY in staging namespace
Exam Tip: Pay close attention to the YAML indentation and the - character. This single character determines whether the logic is AND or OR.
CNI Plugins and NetworkPolicy Support
NetworkPolicy requires a CNI plugin that supports it. Not all CNI plugins do!
| CNI Plugin | NetworkPolicy Support | Notes |
|---|---|---|
| Calico | ✅ Full support | Most popular for NetworkPolicy |
| Cilium | ✅ Full support + L7 | eBPF-based, advanced features |
| Weave Net | ✅ Full support | Simple to set up |
| Antrea | ✅ Full support | VMware, built for Kubernetes |
| Flannel | ❌ No support | Overlay-only, no NetworkPolicy |
| kubenet | ❌ No support | Basic, no NetworkPolicy |
Critical: If your CNI plugin doesn’t support NetworkPolicy, creating NetworkPolicy resources will have no effect. The resources will exist in the API but will not be enforced. There will be no error or warning.
Limitations of NetworkPolicy
The built-in Kubernetes NetworkPolicy has several limitations:
| Limitation | Detail | Solution |
|---|---|---|
| No deny rules | You can only allow traffic, not explicitly deny it | Use default-deny + allow rules |
| No logging | Violations are not logged | Use Calico or Cilium for logging |
| No Layer 7 (L7) | Cannot filter by HTTP path, method, or headers | Use Cilium or a service mesh (Istio) |
| No cluster-wide policies | Each policy is namespace-scoped | Use Calico GlobalNetworkPolicy or Cilium ClusterwideNetworkPolicy |
| No FQDN rules | Cannot use domain names (e.g., api.example.com) | Use Cilium FQDN policies |
| Pod-level only | Cannot apply to nodes directly | Use host-level firewalls |
Network Policy Best Practices
-
Start with default deny — apply a default-deny-all policy to every namespace, then open up only what’s needed.
-
Always allow DNS — when using default deny egress, remember to allow UDP/TCP port 53.
-
Use namespace labels — label your namespaces (e.g.,
env: production) so you can usenamespaceSelectorin policies. -
Verify your CNI — confirm your CNI plugin supports NetworkPolicy before relying on it.
-
Test policies — use tools like
kubectl runwithwgetorcurlto verify connectivity. -
Document policies — use annotations to explain why each policy exists.
Key Takeaways for the Exam
-
Default Kubernetes network: All pods can reach all pods. There are no restrictions.
-
NetworkPolicy acts as a firewall. Once a policy selects a pod, all non-allowed traffic of that type (ingress/egress) is denied.
-
Policies are additive — multiple policies on the same pod are unioned (OR between policies).
-
AND vs OR logic: Same
fromentry = AND. Separatefromentries = OR. Watch the YAML-character. -
CNI requirement: Flannel does not support NetworkPolicy. Calico, Cilium, and Weave do.
-
Default deny all ingress =
podSelector: {}+policyTypes: [Ingress]with noingressrules. -
Limitations: No deny rules, no logging, no L7 filtering, no cluster-wide scope in the standard API.
-
NetworkPolicy resources will exist but have no effect if the CNI doesn’t support them.
Next: Resource Limits & DoS Prevention — protecting your cluster from resource exhaustion attacks.