
- 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.
- Cilium Network Policy docs: https://docs.cilium.io/en/stable/policy/
- netshoot image: https://hub.docker.com/r/nicolaka/netshoot
- Kubernetes docs: https://kubernetes.io/docs/
- Check namespaces:
- List all pods (excluding kube-system) to view IPs and nodes:
- Check labels in the dev namespace:
- 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.
- Important: selectors in a CiliumNetworkPolicy are namespace-scoped by default (selectors without explicit namespace qualifiers match endpoints in the same namespace as the policy).
- From app2 in dev -> should be allowed (telnet returns “Connection refused”).
- From app3 in dev -> should be blocked (telnet will hang).
- 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.
- app2 in prod -> allowed.
- app2 in dev -> still allowed due to original matchLabels (if preserved).
- app3 in dev -> still blocked.
- Use matchExpressions when you need to match a label key against multiple values.
- 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.
- An empty endpoint selector () in fromEndpoints or toEndpoints matches all endpoints in the same namespace as the CiliumNetworkPolicy.
- If you select endpoints and provide an empty ingress list, ingress is denied to those endpoints (default deny).
- Egress uses toEndpoints and follows the same matching patterns as ingress (namespace-qualified labels, matchExpressions, empty selector).
- Examples below show common egress controls.
- Provide an empty egress list to deny all egress from the selected endpoints:
- Use toCIDR to specify CIDR entries directly.
- Use toCIDRSet to specify CIDRs with an except list.
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/