Qingular

NetworkPolicy

·CKAk8sPractice

CKA Domain 3 — Kubernetes NetworkPolicy configuration and management (Ingress, Egress, isolation policies)

← Back to CKA Practice Index

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

ConceptDescription
podSelectorSelects the Pods the policy applies to; {} matches all Pods in the namespace
policyTypesDeclares the rule types; must be specified even if ingress/egress is empty
namespaceSelectorSelects source/destination by namespace label
ipBlockSelects source/destination IP ranges by CIDR
Multiple selectors within a ruleAND relationship (must match all)
Multiple selectors across rulesOR 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