Skip to main content
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.
A presentation slide showing the word "Demo" on the left and a teal graphic on the right that reads "Cilium Network Policy Part 1." A small copyright notice for KodeKloud appears in the bottom-left corner.
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.
Useful links Environment: example deployment Use one Deployment per app (adjust name/namespace/labels accordingly). Example manifest for app1 in dev:
Repeat the same pattern for app2 and app3 in dev, and similarly for prod and staging. Verify cluster namespaces and pods
  • Check namespaces:
  • List all pods (excluding kube-system) to view IPs and nodes:
  • Check labels in the dev namespace:
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
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):
Apply and verify:
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:
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:
Apply and test:
  • app2 in prod -> allowed.
  • app2 in dev -> still allowed due to original matchLabels (if preserved).
  • app3 in dev -> still blocked.
Match multiple namespaces (matchExpressions)
  • Use matchExpressions when you need to match a label key against multiple values.
Example — allow app2 from prod OR staging:
  • After applying, app2 in prod and app2 in staging will be allowed.
  • app2 in dev will be allowed only if you include dev in the values or keep a matchLabels that matches dev.
Allow all endpoints in the policy namespace
  • An empty endpoint selector () in fromEndpoints or toEndpoints matches all endpoints in the same namespace as the CiliumNetworkPolicy.
Example — allow all pods in dev to talk to app1:
Deny all ingress (default deny for selected endpoints)
  • If you select endpoints and provide an empty ingress list, ingress is denied to those endpoints (default deny).
Egress rules (toEndpoints, toCIDR, toCIDRSet)
  • Egress uses toEndpoints and follows the same matching patterns as ingress (namespace-qualified labels, matchExpressions, empty selector).
  • Examples below show common egress controls.
Example — allow app1 in dev to talk only to app2 in dev:
Example — allow egress to app2 in prod namespace:
Allow egress to all endpoints in same namespace:
Deny all egress:
  • Provide an empty egress list to deny all egress from the selected endpoints:
Egress to CIDR ranges
  • Use toCIDR to specify CIDR entries directly.
  • Use toCIDRSet to specify CIDRs with an except list.
Example — allow egress to specific CIDRs:
Example — allow egress to a large CIDR but exclude specific subranges:
Quick reference table — common policy patterns Key reminders and best practices
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/

Watch Video