Cilium Network Policy: CiliumNetworkPolicy

CiliumNetworkPolicy (CNP) is the most commonly used policy type in Cilium.

It is namespace-scoped, meaning the policy applies only within the namespace where it is created.

This is the policy most teams use for real-world application security because it enables zero-trust controls at Layer 3, Layer 4, and Layer 7.

If Kubernetes NetworkPolicy is a basic firewall, CiliumNetworkPolicy is the full application-aware policy engine.

What discussed and showed here is similar with CiliumClusterwideNetworkPolicy, the only difference it the policy is cluster wide. Read more on that topic and how to combine these policies.

Table of Contents

Policies by Use Case

Layer 3, Layer 4, and Layer 7 Filtering

Cilium can enforce policy across multiple layers of the network stack.

  • Layer 3 - source/destination identity (who can talk)
  • Layer 4 - ports and protocols (how they talk)
  • Layer 7 - application semantics like HTTP methods, paths, and gRPC

Example: Allow only HTTP GET /health

 1apiVersion: cilium.io/v2
 2kind: CiliumNetworkPolicy
 3metadata:
 4  name: allow-healthcheck-only
 5  namespace: backend
 6spec:
 7  endpointSelector:
 8    matchLabels:
 9      app: api
10
11  ingress:
12    - fromEndpoints:
13        - matchLabels:
14            k8s:io.kubernetes.pod.namespace: frontend
15            app: frontend
16      toPorts:
17        - ports:
18            - port: "3000"
19              protocol: TCP
20          rules:
21            http:
22              - method: "GET"
23                path: "/health"

Ingress and Egress Controls

Cilium supports both inbound and outbound traffic policy.

  • ingress controls who can reach a workload
  • egress controls where a workload can go

Example: Allow ingress from frontend

 1apiVersion: cilium.io/v2
 2kind: CiliumNetworkPolicy
 3metadata:
 4  name: allow-frontend-ingress
 5  namespace: backend
 6spec:
 7  endpointSelector:
 8    matchLabels:
 9      app: api
10
11  ingress:
12    - fromEndpoints:
13        - matchLabels:
14            k8s:io.kubernetes.pod.namespace: frontend

Example: Allow egress to external API

 1apiVersion: cilium.io/v2
 2kind: CiliumNetworkPolicy
 3metadata:
 4  name: allow-external-api
 5  namespace: demo
 6spec:
 7  endpointSelector:
 8    matchLabels:
 9      app: demo
10
11  egress:
12    - toFQDNs:
13        - matchName: api.marktaguiad.dev

Pod and Namespace Selectors

Cilium can also select workloads using pod labels and namespace identity.

Example: Allow frontend namespace to reach backend pods

 1apiVersion: cilium.io/v2
 2kind: CiliumNetworkPolicy
 3metadata:
 4  name: frontend-to-backend
 5  namespace: backend
 6spec:
 7  endpointSelector:
 8    matchLabels:
 9      app: backend
10
11  ingress:
12    - fromEndpoints:
13        - matchLabels:
14            k8s:io.kubernetes.pod.namespace: frontend
15            app: frontend

CIDR-Based Rules

Cilium can allow or deny traffic using raw IP ranges.

Useful for:

  • external services
  • legacy systems
  • VMs outside Kubernetes
  • blocking known IP ranges

Example: Allow access from other subnets

 1apiVersion: cilium.io/v2
 2kind: CiliumNetworkPolicy
 3metadata:
 4  name: allow-other-subnet
 5  namespace: demo
 6spec:
 7  endpointSelector: {}
 8
 9  ingress:
10    - fromCIDR:
11        - 10.10.0.0/16

Built-in Entities

Cilium includes built-in identity groups called entities.

Common ones include:

  • world - traffic outside the cluster
  • cluster - anything inside the cluster
  • host - the local node
  • remote-node - other Kubernetes nodes
  • kube-apiserver - Kubernetes API server

Example: Allow only cluster-internal traffic

 1apiVersion: cilium.io/v2
 2kind: CiliumNetworkPolicy
 3metadata:
 4  name: internal-only
 5  namespace: apps
 6spec:
 7  endpointSelector: {}
 8
 9  ingress:
10    - fromEntities:
11        - cluster

Example: Allow Traffic to Kubernetes API Server

 1apiVersion: cilium.io/v2
 2kind: CiliumNetworkPolicy
 3metadata:
 4  name: allow-kube-api
 5  namespace: demo
 6spec:
 7  endpointSelector: {}
 8
 9  egress:
10    - toEntities:
11        - kube-apiserver

DNS-Aware Filtering

Cilium can inspect DNS traffic and allow only specific DNS queries.

This is useful when workloads should resolve only approved domains.

Example: Allow DNS lookups only for api.marktaguiad.dev

 1apiVersion: cilium.io/v2
 2kind: CiliumNetworkPolicy
 3metadata:
 4  name: allow-specific-dns-query
 5  namespace: frontend
 6spec:
 7  endpointSelector:
 8    matchLabels:
 9      app: frontend
10
11  egress:
12    - toEndpoints:
13        - matchLabels:
14            k8s:io.kubernetes.pod.namespace: kube-system
15            k8s:k8s-app: kube-dns
16      toPorts:
17        - ports:
18            - port: "53"
19              protocol: UDP
20          rules:
21            dns:
22              - matchName: api.marktaguiad.dev

FQDN-Based Egress

Cilium can enforce egress policy using domain names.

Example: Allow outbound HTTPS to marktaguaid.dev only

 1apiVersion: cilium.io/v2
 2kind: CiliumNetworkPolicy
 3metadata:
 4  name: allow-stripe
 5  namespace: backend
 6spec:
 7  endpointSelector:
 8    matchLabels:
 9      app: backend
10
11  egress:
12    - toFQDNs:
13        - matchName: marktaguiad.dev
14      toPorts:
15        - ports:
16            - port: "443"
17              protocol: TCP

Explicit Deny Policies

Unlike native Kubernetes NetworkPolicy, Cilium supports explicit deny rules.

These are useful for hard blocks even when broader allow rules exist.

Example: Deny all ingress and egress traffic

1apiVersion: cilium.io/v2
2kind: CiliumNetworkPolicy
3metadata:
4  name: default-deny
5  namespace: demo
6spec:
7  endpointSelector: {}
8  ingress: []
9  egress: []

Example

Check this repo for full example.

Repo: mcbtaguiad/cilium-demo

Deploy

1cd  cilium-demo/networkpolicy
2kubectl apply -k demo/environments/demo

This will create four deployments.

  • frontend /app
  • backend /api
  • monitor /status
  • redis
1NAMESPACE     NAME                                       READY   STATUS    RESTARTS   AGE
2backend       backend-759b4ffd4-8ghcq                    1/1     Running   0          4h19m
3database      redis-7849668f57-8rckb                     1/1     Running   0          4h19m
4frontend      frontend-6945d865bc-l7hsh                  1/1     Running   0          4h19m
5monitor       monitor-566cf69cf9-vvndd                   1/1     Running   0          4h19m

A gateway is created and will attach to LoadBalancer IP 192.168.254.230.

1NAMESPACE     NAME                            TYPE           CLUSTER-IP       EXTERNAL-IP       PORT(S)                  AGE
2gateway       cilium-gateway-cilium-gateway   LoadBalancer   10.108.196.155   192.168.254.230   80:32207/TCP             4h19m

Frontend Policy

Policy limits frontend to be reachable only through the Cilium Gateway on port 80.

Allowed:

  • client → Gateway → frontend

Blocked:

  • pod → frontend
  • direct cluster access → frontend
  • frontend → anywhere
 1apiVersion: cilium.io/v2
 2kind: CiliumNetworkPolicy
 3metadata:
 4  name: default-deny
 5spec:
 6  endpointSelector: {}
 7  ingress: []
 8  egress: []
 9---
10apiVersion: cilium.io/v2
11kind: CiliumNetworkPolicy
12metadata:
13  name: frontend-only-gateway
14spec:
15  endpointSelector:
16    matchLabels:
17      app: frontend
18
19  ingress:
20    - fromEntities:
21        - ingress
22
23      toPorts:
24        - ports:
25            - port: "80"
26              protocol: TCP

Backend Policy

Backend is reachable only through the Gateway, only on port 3000, and only for the listed API endpoints.

Allowed:

  • Gateway → backend /api/* (only listed routes/methods)

Blocked:

  • pod → backend
  • direct cluster access → backend
  • any unlisted API path or HTTP method
  • backend → anywhere (egress still denied)
 1apiVersion: cilium.io/v2
 2kind: CiliumNetworkPolicy
 3metadata:
 4  name: default-deny
 5spec:
 6  endpointSelector: {}
 7  ingress: []
 8  egress: []
 9---
10apiVersion: cilium.io/v2
11kind: CiliumNetworkPolicy
12metadata:
13  name: allow-gateway-to-backend
14  namespace: backend
15spec:
16  endpointSelector:
17    matchLabels:
18      app: backend
19
20  ingress:
21    - fromEntities:
22        - ingress
23
24      toPorts:
25        - ports:
26            - port: "3000"
27              protocol: TCP
28          rules:
29            http:
30              - method: "GET"
31                path: "/api"
32              - method: "POST"
33                path: "/api/register"
34              - method: "POST"
35                path: "/api/login"
36              - method: "GET"
37                path: "/api/profile"
38              - method: "GET"
39                path: "/api/version"
40              - method: "GET"
41                path: "/api/users"
42              - method: "PUT"
43                path: "/api/users"
44              - method: "DELETE"
45                path: "/api/users"

Monitor Policy

Monitor is reachable only through the Gateway on port 8000.

Allowed:

  • client → Gateway → monitor Blocked:
  • pod → monitor
  • direct cluster access → monitor
  • monitor → anywhere (egress still denied)
 1apiVersion: cilium.io/v2
 2kind: CiliumNetworkPolicy
 3metadata:
 4  name: default-deny
 5spec:
 6  endpointSelector: {}
 7  ingress: []
 8  egress: []
 9---
10apiVersion: cilium.io/v2
11kind: CiliumNetworkPolicy
12metadata:
13  name: monitor-only-gateway
14spec:
15  endpointSelector:
16    matchLabels:
17      app: monitor
18
19  ingress:
20    - fromEntities:
21        - ingress
22
23      toPorts:
24        - ports:
25            - port: "8000"
26              protocol: TCP

Database Policy

Redis accepts connections only from backend pods.

Allowed:

  • backend → redis Blocked:
  • frontend → redis
  • monitor → redis
  • direct cluster access → redis
  • any other pod → redis
 1apiVersion: cilium.io/v2
 2kind: CiliumNetworkPolicy
 3metadata:
 4  name: default-deny
 5spec:
 6  endpointSelector: {}
 7  ingress: []
 8  egress: []
 9---
10apiVersion: cilium.io/v2
11kind: CiliumNetworkPolicy
12metadata:
13  name: backend-to-database
14spec:
15  endpointSelector:
16    matchLabels:
17      app: redis
18
19  ingress:
20    - fromEndpoints:
21        - matchLabels:
22            k8s:io.kubernetes.pod.namespace: backend
23            app: backend