Stop Writing Cilium Network Policies, Instead Start Writing Cilium Cluster-Wide Network Policies

Tired of Copy-Pasting Cilium Network Policies? Try Clusterwide Policies Instead

Imagine: your Kubernetes cluster is alive with activity. Dozens, perhaps hundreds, of namespaces operate independently, each with its unique microservices. One team needs internet access to build AI vectors and download AI models. Another team has stricter security regulations due to sensitive data. How do you keep this organized without getting lost in a spaghetti of Cilium Network Policies?

Often teams struggle with writing an endless number of almost identical network policies. Every time a new namespace is created, the whole routine starts over again. It’s sometimes even included in the Scrum Definition of Done! Surely there has to be an easier way?

There’s a much simpler way. The solution: Cilium Cluster-Wide Network Policies (CCNP’s) combined with smart namespace labeling. This eliminates redundancy and the inconsistencies it causes.

Policy Spaghetti

  • Repetition: Do you see the same internet access rules appearing over and over again? What a waste of time.
  • Maintenance and clarity: Imagine having hundreds of these policies. Making a quick change is difficult. The easiest way is to do this in a central location, preferably in GitOps with, for example, ArgoCD.
  • Error margin: One wrong digit in an IP address or a typo in a port number and the connection won’t work, or even worse, it will be wide open.

An Easily Maintainable Approach: CCNP's and Namespace Labels

Cilium Cluster-wide Network Policies (CCNPs) are powerful because they define policies that apply across your entire cluster, not just a single, small namespace. And the beauty of it is: it is possible to let these CCNP’s react to labels that are applied to your namespaces. Here’s how this works:

➜ ~ kubectl create namespace hello-world
namespace/hello-world created
➜  ~ kubectl run nginx -n hello-world --image docker.io/nginxdemos/hello --image-pull-policy=IfNotPresent --port 80
pod/nginx created
➜  ~ kubectl -n hello-world expose pod nginx --type LoadBalancer
service/nginx exposed
➜  ~ kubectl -n hello-world get svc
NAME    TYPE           CLUSTER-IP     EXTERNAL-IP    PORT(S)        AGE
nginx   LoadBalancer   10.43.67.208   192.168.4.51   80:30715/TCP   61s
➜  ~ curl http://192.168.4.51 | grep "<title>"
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100 12108    0 12108    0     0   955k      0 --:--:-- --:--:-- --:--:--  985k
<title>Hello World</title>
                                                        

Copy code

Deny All Policy

Let’s start with a secure baseline. Block all incoming and outgoing traffic by applying a Cilium Network Policy which matches all traffic. It is possible to apply the mechanism below in various ways: per namespace, with a CCNP (Cilium Cluster-Wide Network Policy), or by Enforcement Mode always (https://docs.cilium.io/en/latest/security/policy/intro/).

deny-all.yaml:
---
apiVersion: "cilium.io/v2"
kind: CiliumClusterwideNetworkPolicy
metadata:
  name: "deny-all"
spec:
  description: "Make sure namespace is locked"
  endpointSelector:
    matchExpressions:
    - key: io.kubernetes.pod.namespace
      operator: In
      values:
      - hello-world
  ingress:
  - {}
  egress:
  - {}


➜ ~ kubectl apply -f deny-all.yaml
ciliumclusterwidenetworkpolicy.cilium.io/deny-all created
                                                        

Copy code

List Endpoints and Find Your App

Label the namespace with allow-ingress-port-80=true. This is needed in the Cilium Cluster-Wide Network Policy endpoint selector.

➜  ~ kubectl label namespace hello-world allow-ingress-port-80=true
namespace/hello-world labeled


➜ ~ kubectl -n hello-world describe ciliumendpoint nginx
Name:         nginx
Namespace:    hello-world
Labels:       run=nginx
API Version:  cilium.io/v2
Kind:         CiliumEndpoint
...
Status:
  Encryption:
  External - Identifiers:
    k8s-namespace:          hello-world
    k8s-pod-name:           nginx
    Pod - Name:             hello-world/nginx
  Id:                       47
  Identity:
    Id:  34739
    Labels:
      k8s:io.cilium.k8s.namespace.labels.allow-ingress-port-80=true
      k8s:io.cilium.k8s.namespace.labels.kubernetes.io/metadata.name=hello-world
      k8s:io.cilium.k8s.policy.cluster=default
      k8s:io.cilium.k8s.policy.serviceaccount=default
      k8s:io.kubernetes.pod.namespace=hello-world
      k8s:run=nginx
...
                                                        

Copy code

The most important part of this endpoint is k8s:io.cilium.k8s.namespace.labels.allow-ingress-port-80=true. The Nginx pod is known as a Cilium Endpoint.

The namespace labels, automatically prefixed with io.cilium.k8s.namespace.labels, are attached to the endpoint. It is possible to use these labels in the Network Policy EndpointSelector. 

Create a Cilium Cluster-wide Network Policy

Because of the “deny-all” policy the deployed container is not available:

➜  ~ curl --connect-timeout 5 -I http://192.168.4.51
curl: (28) Failed to connect to 192.168.4.51 port 80 after 5006 ms: Timeout was reached


➜  ~ kubectl -n kube-system exec ds/cilium -c cilium-agent -- cilium monitor --related-to 47
Press Ctrl-C to quit
time="2025-06-26T21:18:31Z" level=info msg="Initializing dissection cache..." subsys=monitor
Policy verdict log: flow 0x0 local EP ID 47, remote ID world, proto 6, ingress, action deny, auth: disabled, match none, 192.168.4.229:51956 -> 10.42.0.251:80 tcp SYN
xx drop (Policy denied) flow 0x0 to endpoint 47, ifindex 4, file bpf_lxc.c:2091, , identity world->34739: 192.168.4.229:51956 -> 10.42.0.251:80 tcp SYN
Policy verdict log: flow 0x0 local EP ID 47, remote ID world, proto 6, ingress, action deny, auth: disabled, match none, 192.168.4.229:51956 -> 10.42.0.251:80 tcp SYN
xx drop (Policy denied) flow 0x0 to endpoint 47, ifindex 4, file bpf_lxc.c:2091, , identity world->34739: 192.168.4.229:51956 -> 10.42.0.251:80 tcp SYN
                                                        

Copy code

Create a Cilium Cluster-Wide Network Policy that only activates if a namespace has a specific label. In this example: allow-ingress-port-80: “true”.

allow-ingress-port-80.yaml:

---
apiVersion: "cilium.io/v2"
kind: CiliumClusterwideNetworkPolicy
metadata:
  name: "allow-ingress-port-80"
spec:
  description: "Clusterwide Network to allow ingress http traffic"
  endpointSelector:
    matchLabels:
      io.cilium.k8s.namespace.labels.allow-ingress-port-80: "true"
  ingress:
  - fromEntities:
    - world
    toPorts:
    - ports:
      - port: "80"
        protocol: TCP


➜ ~ kubectl apply -f allow-ingress-port-80.yaml
ciliumclusterwidenetworkpolicy.cilium.io/allow-ingress-port-80 created
                                                        

Copy code

The Result

The Cilium Cluster-Wide Network Policy gives access to the pod in the namespace. Let’s try it out:

➜  ~ curl http://192.168.4.51 | grep "<title>"
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100 12108    0 12108    0     0  2769k      0 --:--:-- --:--:-- --:--:-- 2956k
<title>Hello World</title>
                                                        

Copy code

cilium-cluster-wide-network-policies

References

And that worked: Cilium Cluster-Wide Network Policies (CCNP’s) combined with smart namespace labeling. Imagine a similar approach could replace your Prometheus, Fluentd or other complex namespace scoped network policies.

References

Share this:
Stay up to date
By signing up for our newsletter you indicate that you have taken note of our privacy statement.

Ready to strengthen your Cloud Security?

Stefan Behlen

Let's talk!


Ready to strengthen your Cloud Security?

* required

By sending this form you indicate that you have taken note of our privacy Statement.
Privacy Overview
This website uses cookies. We use cookies to ensure the proper functioning of our website and services, to analyze how visitors interact with us, and to improve our products and marketing strategies. For more information, please consult our privacy- en cookiebeleid.