NetworkPolicy
CKA Domain 3 — Kubernetes NetworkPolicy configuration and management (Ingress, Egress, isolation policies)
Overview
NetworkPolicy is a Kubernetes resource used to control network access rules at the Pod level. It uses label selectors and namespace selectors to define which Pods can communicate with each other.
Important Prerequisite: NetworkPolicy requires CNI network plugin support (such as Calico, Cilium, Weave Net, etc.). If the CNI does not support it, NetworkPolicy rules will not take effect.
Resource Structure
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: my-network-policy
spec:
podSelector: # Selects the Pods this policy applies to
matchLabels:
app: my-app
policyTypes: # Policy types (Ingress / Egress)
- Ingress
- Egress
ingress: # Inbound rules
- from:
- ipBlock:
cidr: 10.0.0.0/16
- namespaceSelector:
matchLabels:
project: my-project
- podSelector:
matchLabels:
role: frontend
ports:
- protocol: TCP
port: 80
egress: # Outbound rules
- to:
- ipBlock:
cidr: 0.0.0.0/0
ports:
- protocol: TCP
port: 443
Ingress Rule Configuration
Controls traffic entering (inbound to) a Pod.
Allow Access from Pods in a Specific Namespace
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-from-namespace
namespace: target-ns
spec:
podSelector:
matchLabels:
app: web
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector:
matchLabels:
name: source-ns # Namespace label
Allow Access from Specific Pod Labels
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-from-pod-label
spec:
podSelector:
matchLabels:
app: backend
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
role: frontend
ports:
- protocol: TCP
port: 8080
Combined Selectors (Matching Both Namespace and Pod Labels)
ingress:
- from:
- namespaceSelector:
matchLabels:
name: dev
podSelector: # Multiple selectors within the same rule are ANDed
matchLabels:
role: frontend
Egress Rule Configuration
Controls traffic leaving (outbound from) a Pod.
Allow Access to Specific External CIDR
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-egress-external
spec:
podSelector:
matchLabels:
app: my-app
policyTypes:
- Egress
egress:
- to:
- ipBlock:
cidr: 10.0.0.0/8
ports:
- protocol: TCP
port: 443
- protocol: TCP
port: 80
Common Policy Patterns
1. Deny-All Ingress
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: deny-all-ingress
spec:
podSelector: {} # Matches all Pods in the namespace
policyTypes:
- Ingress
# ingress list is empty, denying all inbound traffic
2. Deny-All Egress
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: deny-all-egress
spec:
podSelector: {}
policyTypes:
- Egress
# egress list is empty, denying all outbound traffic
3. Allow-All Ingress
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-all-ingress
spec:
podSelector: {}
policyTypes:
- Ingress
ingress:
- {} # Empty rule matches all sources
4. Allow Access from a Specific Namespace
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-from-specific-ns
spec:
podSelector:
matchLabels:
app: api
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector:
matchLabels:
environment: production
ports:
- port: 80
5. IP Range-Based Access Control
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-from-ip-range
spec:
podSelector:
matchLabels:
app: api
policyTypes:
- Ingress
ingress:
- from:
- ipBlock:
cidr: 192.168.1.0/24
except: # Exclude specific IPs
- 192.168.1.5/32
ports:
- protocol: TCP
port: 443
Key Concepts and Notes
| Concept | Description |
|---|---|
podSelector | Selects the Pods the policy applies to; {} matches all Pods in the namespace |
policyTypes | Declares the rule types; must be specified even if ingress/egress is empty |
namespaceSelector | Selects source/destination by namespace label |
ipBlock | Selects source/destination IP ranges by CIDR |
| Multiple selectors within a rule | AND relationship (must match all) |
| Multiple selectors across rules | OR relationship (match any one) |
Testing NetworkPolicy
# Create a test Pod
kubectl run test-pod --image=busybox --rm -it --restart=Never -- sh
# Test connectivity
wget -qO- http://target-svc:80
# View NetworkPolicy
kubectl get networkpolicy
kubectl describe networkpolicy my-policy
🧪 Complete Hands-on Example: Configuring NetworkPolicy for Tenant Isolation
Scenario
Deny all ingress traffic by default, then only allow Pods with specific labels to communicate with each other, simulating a multi-tenant isolation scenario.
Prerequisites
- Cluster uses a CNI plugin that supports NetworkPolicy (Calico, Cilium, etc.)
- kubectl is configured with cluster access
Steps
Step 1: Create Two Namespaces and Deploy Pods
# Create two namespaces to simulate different tenants
kubectl create namespace tenant-a
kubectl create namespace tenant-b
# Expected output: namespace/tenant-a created
# Deploy a labeled Pod in tenant-a
kubectl run pod-a --image=nginx:1.25 --labels="app=web,tenant=a" -n tenant-a
# Expected output: pod/pod-a created
# Deploy two Pods with different roles in tenant-b
kubectl run pod-b1 --image=nginx:1.25 --labels="app=web,tenant=b" -n tenant-b
kubectl run pod-b2 --image=nginx:1.25 --labels="app=api,tenant=b" -n tenant-b
# Expected output: pod/pod-b1 created
# Verify Pod status
kubectl get pods -n tenant-a -o wide
kubectl get pods -n tenant-b -o wide
# Expected output: all Pods in Running state
Step 2: Create Deny-All Ingress NetworkPolicy
cat <<EOF | kubectl apply -f -
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: deny-all-ingress
namespace: tenant-a
spec:
podSelector: {}
policyTypes:
- Ingress
EOF
# Expected output: networkpolicy.networking.k8s.io/deny-all-ingress created
# Also apply the deny policy to tenant-b
cat <<EOF | kubectl apply -f -
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: deny-all-ingress
namespace: tenant-b
spec:
podSelector: {}
policyTypes:
- Ingress
EOF
# Expected output: networkpolicy.networking.k8s.io/deny-all-ingress created
Step 3: Verify Ingress Traffic Is Denied
# Start a test Pod in tenant-b and try to access pod-a in tenant-a
kubectl run test-pod --image=busybox:1.28 -n tenant-b --rm -it --restart=Never -- sh
# Inside the test Pod, run:
wget -qO- --timeout=5 http://pod-a.tenant-a
# Expected output: wget: download timed out (connection denied)
# After exiting the test container, check NetworkPolicies
kubectl get networkpolicy --all-namespaces
# NAMESPACE NAME POD-SELECTOR AGE
# tenant-a deny-all-ingress <none> 1m
# tenant-b deny-all-ingress <none> 1m
Step 4: Create a Policy Allowing Pods with Specific Labels
# Create a policy in tenant-a, only allowing Pods labeled app=web to access
cat <<EOF | kubectl apply -f -
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-web-to-web
namespace: tenant-a
spec:
podSelector:
matchLabels:
app: web
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
app: web
ports:
- protocol: TCP
port: 80
EOF
# Expected output: networkpolicy.networking.k8s.io/allow-web-to-web created
Step 5: Verify Labeled Pods Can Access
# From tenant-b's pod-b1 (label app=web), test access to tenant-a's pod-a (label app=web)
kubectl exec pod-b1 -n tenant-b -- wget -qO- --timeout=5 http://pod-a.tenant-a
# Expected output: nginx welcome page HTML (policy allows because both have app=web label)
# From tenant-b's pod-b2 (label app=api), test access to tenant-a's pod-a
kubectl exec pod-b2 -n tenant-b -- wget -qO- --timeout=5 http://pod-a.tenant-a
# Expected output: wget: download timed out (policy denies because pod-b2 has label app=api, not app=web)
Step 6: Comprehensive Testing with a Diagnostic Pod
# Create a diagnostic Pod to test different scenarios
kubectl run test-pod -n tenant-a --image=nicolaka/netshoot --rm -it --restart=Never -- sh
# Inside the diagnostic Pod, test item by item:
# Test 1: Access pod-a in the same namespace (should succeed)
curl -m 5 http://pod-a.tenant-a
# Expected output: HTML content
# Test 2: Access pod-b1 in tenant-b (may be denied depending on policy configuration)
curl -m 5 http://pod-b1.tenant-b
# Expected output: connection timeout (if tenant-b has not allowed this Pod)
Verification
# View all NetworkPolicies
kubectl get networkpolicy --all-namespaces -o wide
# NAMESPACE NAME POD-SELECTOR AGE
# tenant-a deny-all-ingress <none> 5m
# tenant-a allow-web-to-web app=web 3m
# tenant-b deny-all-ingress <none> 5m
# View specific policy details
kubectl describe networkpolicy allow-web-to-web -n tenant-a
# Confirm the Ingress rule includes podSelector: app=web
# Clean up resources
kubectl delete namespace tenant-a tenant-b
Exam Tips
-
Default deny + whitelist allow is the most common NetworkPolicy pattern
-
podSelector: {}matches all Pods in the namespace -- a key pattern for global policies -
Empty ingress list = deny all; Empty rule
ingress: [{}]= allow all -
Multiple selectors within a rule are AND relationship; across rules they are OR -- a common CKA gotcha
-
NetworkPolicy is a namespace-scoped resource, only affecting Pods in its own namespace
-
When testing, prefer
kubectl run ... -- wgetoverping(some CNIs may block ICMP) -
If busybox's wget is unavailable, switch to
kubectl run ... -- curlor use thenetshootimage -
https://kubernetes.io/docs/concepts/services-networking/network-policies/
-
https://kubernetes.io/docs/tasks/administer-cluster/declare-network-policy/
-
https://kubernetes.io/docs/concepts/services-networking/network-policies/#networkpolicy-resource