Demonstrates creating and testing L3 Cilium network policies to control namespace-scoped ingress and egress, including cross-namespace rules, CIDR egress, and default deny behaviors.
In this lesson we’ll demonstrate how to create and test L3 (IP/label-based) Cilium network policies. Examples cover common scenarios: namespace scoping, allowing/denying ingress and egress, matching multiple namespaces, and CIDR-based egress.
Overview
Goal: Apply an L3 ingress policy to the app1 pod in the dev namespace so only pods with label app=app2 (in specific namespaces) can talk to app1.
Test image: nicolaka/netshoot (contains telnet, curl, and other troubleshooting tools).
Environment: three namespaces (dev, prod, staging), each with app1, app2, app3.
Repeat the same pattern for app2 and app3 in dev, and similarly for prod and staging.Verify cluster namespaces and pods
Check namespaces:
Copy
kubectl get nsNAME STATUS AGEcilium-secrets Active 34mdefault Active 36mdev Active 31mkube-node-lease Active 36mkube-public Active 36mkube-system Active 36mlocal-path-storage Active 36mprod Active 31mstaging Active 32m
List all pods (excluding kube-system) to view IPs and nodes:
kubectl get pod -n dev --show-labelsNAME READY STATUS RESTARTS AGE LABELSapp1-75c78488c4-rzf48 1/1 Running 0 39m app=app1,pod-template-hash=75c78488c4app2-5957957d5b-kgkt6 1/1 Running 0 39m app=app2,pod-template-hash=5957957d5bapp3-87d98dbbb-k6xzk 1/1 Running 0 39m app=app3,pod-template-hash=87d98dbbb
Connectivity testing with telnet
Exec into a pod and use telnet to test TCP connectivity.
Two possible outcomes to interpret:
“Connection refused” — the TCP SYN reached the destination pod (no process listening on that port). This confirms network reachability.
Telnet hangs without response — the packet was likely dropped before reaching the pod (network reachability blocked).
Telnet note: “Connection refused” means the traffic reached the destination pod (but no process is listening on that port). A hanging telnet indicates the traffic was blocked or dropped.
Example telnet test
Copy
# Exec into app1 in devkubectl exec -it app1-75c78488c4-rzf48 -n dev -- bash# From inside the pod:telnet 10.0.2.89 3000# Output:# Telnet to an IP that does not exist -> will hang (CTRL+C to cancel)telnet 10.0.2.112 3000# (hangs, showing that traffic did not get to any pod)
Basic L3 ingress policy: allow only app2 (same namespace)
Important: selectors in a CiliumNetworkPolicy are namespace-scoped by default (selectors without explicit namespace qualifiers match endpoints in the same namespace as the policy).
Example CiliumNetworkPolicy (allow ingress to app1 from app2 in dev):
kubectl apply -f l3-policy.yaml# Output:kubectl get ciliumnetworkpolicy -n dev# NAME AGE VALID# l3-policy 10s True
Testing ingress behavior
From app2 in dev -> should be allowed (telnet returns “Connection refused”).
From app3 in dev -> should be blocked (telnet will hang).
Example:
Copy
# From app2 in dev (allowed)kubectl exec -it app2-5957957d5b-kgkt6 -n dev -- bashtelnet 10.0.2.88 80# From app3 in dev (blocked)kubectl exec -it app3-87d98dbbb-k6xzk -n dev -- bashtelnet 10.0.2.88 80# (hangs; CTRL+C to cancel)
Namespace scoping and cross-namespace rules
By default, label selectors reference the policy namespace.
To allow endpoints from another namespace, use the namespace-qualified label: “k8s:io.kubernetes.pod.namespace”: “<namespace>”.
YAML tip: keys containing colons must be quoted.
Example — allow app=app2 from the prod namespace to reach app1 in dev:
When a CiliumNetworkPolicy selects endpoints, it restricts traffic for those endpoints according to the policy rules. If you intend to keep open communication, do not create overly broad selectors without the desired allow rules (use empty from/to endpoints carefully).
Quick YAML tip: Keys containing special characters (such as colons) must be quoted in YAML. Example: “k8s:io.kubernetes.pod.namespace”: “prod”
Default behavior: without any network policy selecting an endpoint, pods can communicate freely.
Once a CiliumNetworkPolicy selects an endpoint, traffic is restricted according to the policy rules you define.
Use endpoint selectors, matchLabels, and matchExpressions for precise targeting.
To match endpoints in another namespace, use the namespace-qualified label key (quoted).
fromEndpoints / toEndpoints with matches all endpoints in the policy namespace.
To explicitly deny all ingress/egress to selected endpoints, provide an empty ingress/egress list.
Extend these L3/L3-only examples for L4/L7 controls and service-aware policies as needed. For advanced use cases, consult the official Cilium policy documentation: https://docs.cilium.io/en/stable/policy/