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