Cilium NetworkPolicy: default-deny and DNS-aware rules
Published: 2026-03-27
k3s with Cilium means standard Kubernetes NetworkPolicy works, but CiliumNetworkPolicy gives more: L7 HTTP rules, DNS-based egress, and cluster-wide policies via CiliumClusterwideNetworkPolicy.
Default deny
Start with a namespace-wide deny-all ingress policy. Without it, every pod can reach every other pod across namespaces.
yamlapiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-ingress
namespace: dev
spec:
podSelector: {}
policyTypes:
- Ingress
Apply to every application namespace. podSelector: {} matches all pods. Only ingress is denied — egress is allowed by default (pods can still reach Kubernetes DNS, external services).
For full isolation, add egress deny too:
yamlspec:
podSelector: {}
policyTypes:
- Ingress
- Egress
Then add explicit egress rules for DNS and other needed services.
Allow ingress from APISIX
After default-deny, services need explicit rules allowing traffic from the ingress controller:
yamlapiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-apisix-ingress
namespace: dev
spec:
podSelector:
matchLabels:
expose: "true"
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: ingress-apisix
Services that should be reachable from outside get the expose: "true" label. Others remain isolated.
Allow inter-service communication
Allow a frontend to talk to the backend within the same namespace:
yamlapiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-frontend-to-backend
namespace: dev
spec:
podSelector:
matchLabels:
app: backend
ingress:
- from:
- podSelector:
matchLabels:
app: frontend
ports:
- port: 8080
CiliumNetworkPolicy for DNS-based egress
Standard NetworkPolicy can only select by IP. CiliumNetworkPolicy supports FQDN rules:
yamlapiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
name: allow-external-apis
namespace: dev
spec:
endpointSelector:
matchLabels:
app: backend
egress:
- toFQDNs:
- matchName: "api.some-external-service.com"
toPorts:
- ports:
- port: "443"
protocol: TCP
- toEndpoints:
- matchLabels:
"k8s:io.kubernetes.pod.namespace": kube-system
"k8s:k8s-app": kube-dns
toPorts:
- ports:
- port: "53"
protocol: UDP
rules:
dns:
- matchPattern: "*"
The DNS rule must explicitly allow port 53 to kube-dns — otherwise FQDN matching can't resolve names.
L7 HTTP rules
Restrict which paths a service can receive:
yamlapiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
name: api-http-policy
namespace: dev
spec:
endpointSelector:
matchLabels:
app: api
ingress:
- fromEndpoints:
- matchLabels:
app: frontend
toPorts:
- ports:
- port: "8080"
protocol: TCP
rules:
http:
- method: GET
path: /api/v1/.*
- method: POST
path: /api/v1/orders
L7 rules require Cilium's HTTP proxy (Envoy). At L7, Cilium can log matched requests to Hubble UI.
Cluster-wide policy for Prometheus
Instead of adding a rule to every namespace, use CiliumClusterwideNetworkPolicy:
yamlapiVersion: cilium.io/v2
kind: CiliumClusterwideNetworkPolicy
metadata:
name: allow-prometheus-scrape
spec:
endpointSelector: {}
ingress:
- fromEndpoints:
- matchLabels:
"k8s:io.kubernetes.pod.namespace": observability
app.kubernetes.io/name: prometheus
toPorts:
- ports:
- port: "9090"
protocol: TCP
- port: "9100"
protocol: TCP
- port: "8080"
protocol: TCP
One policy for Prometheus scraping across all namespaces.
Verifying with Hubble
bash# Live dropped flows
hubble observe --namespace dev --verdict DROPPED -f
# Policy verdicts for a specific pod
hubble observe --pod dev/api-6d8b4c5f9-xk4qv -f
# All flows to a specific port
hubble observe --namespace dev --port 5432 -f
Dropped flows show which policy blocks traffic immediately. Much faster than guessing.
Debugging checklist
hubble observe --verdict DROPPED— see what's being blocked- Check namespace labels —
namespaceSelectoruses namespace labels - Check pod labels — wrong label means the policy won't match
- DNS egress must include port 53 to kube-dns before any FQDN rules
- L7 rules require Cilium's Envoy proxy to be enabled