Advanced Kubernetes Security with Cilium Network Policies

avinash gawadeavinash gawade
6 min read

Kubernetes Network Policies are essential for securing pod-to-pod and pod-to-external communication. However, traditional Kubernetes NetworkPolicies come with several limitations that make them insufficient for complex, production-grade environments.

This is where Cilium Network Policy (CNP) and Cilium Clusterwide Network Policy (CCNP) come in, offering more expressive, feature-rich, and secure policies at L3 (IP), L4 (Port), and L7 (Application) layers.


πŸ”’ Limitations of Traditional Kubernetes Network Policies

Before exploring Cilium, let’s understand where Kubernetes’ built-in policies fall short:

  1. No Explicit Deny Rules

    • Policies are allow-list only.

    • You can’t explicitly deny certain traffic while allowing everything else.

  2. Limited Scope

    • Policies only apply to pods by labels and namespaces.

    • No ability to directly target nodes, host traffic, or localhost access.

  3. Lack of Advanced Features

    • No complex querying for combining multiple selectors.

    • No network reachability analysis to simulate/validate policy impacts.

    • No visualization or monitoring for blocked/allowed connections.

  4. No TLS or Application-Layer (L7) Policies

    • Limited to IP + port (L3/L4) only.

    • Can’t enforce rules based on HTTP methods, paths, DNS names, TLS identities, etc.

  5. No Global Default Policies

    • You can’t create a single cluster-wide default policy.

    • Need to manually define in each namespace.

  6. No Gateway Enforcement

    • Cannot force traffic to flow through a common egress gateway.

These limitations make securing large, dynamic Kubernetes environments challenging.


🌐 Enter Cilium Network Policy (CNP)

Cilium extends Kubernetes networking and security with eBPF-powered policies that go far beyond the default NetworkPolicy model.

With CiliumNetworkPolicy and CiliumClusterwideNetworkPolicy, you get:
βœ… Explicit Deny Rules
βœ… Cluster-wide policies
βœ… L3, L4, and L7 filtering
βœ… Entity-based policies (host, world, kube-apiserver, etc.)
βœ… DNS-based egress control
βœ… Service-based policies
βœ… Fine-grained observability & monitoring


πŸ“‘ Examples of Cilium Network Policies

Let’s break it down with YAML examples across different layers:

πŸ”Ή Layer 3 (IP/Endpoint-Based Policies)

Ingress Policy – Allow only frontend β†’ backend

πŸ‘‰ Only pods with role=frontend can talk to pods with role=backend in dev namespace.

apiVersion: "cilium.io/v2"
kind: CiliumNetworkPolicy
metadata:
  name: "l3-rule"
  namespace: dev
spec:
  endpointSelector:
    matchLabels:
      role: backend
  ingress:
  - fromEndpoints:
    - matchLabels:
        role: frontend

Ingress Policy – Allow from all pods

πŸ‘‰ Any pod in the namespace can access backend pods.

apiVersion: "cilium.io/v2"
kind: CiliumNetworkPolicy
metadata:
  name: "l3-rule"
  namespace: dev
spec:
  endpointSelector:
    matchLabels:
      role: backend
  ingress:
  - fromEndpoints:
    - {}

Ingress Policy – Block from everywhere

πŸ‘‰ Block all ingress traffic to backend pods.

apiVersion: "cilium.io/v2"
kind: CiliumNetworkPolicy
metadata:
  name: "l3-rule"
  namespace: dev
spec:
  endpointSelector:
    matchLabels:
      role: backend
  ingress:
    - {}

Egress Policy – Allow frontend β†’ backend

πŸ‘‰ Frontend pods can only talk to backend pods.

apiVersion: "cilium.io/v2"
kind: CiliumNetworkPolicy
metadata:
  name: "l3-rule"
  namespace: dev
spec:
  endpointSelector:
    matchLabels:
      role: frontend
  egress:
  - toEndpoints:
    - matchLabels:
        role: backend

Egress Policy – Allow frontend to all pods

πŸ‘‰ Frontend can reach any pod in the namespace.

apiVersion: "cilium.io/v2"
kind: CiliumNetworkPolicy
metadata:
  name: "l3-rule"
  namespace: dev
spec:
  endpointSelector:
    matchLabels:
      role: frontend
  egress:
  - toEndpoints:
    - {}

Egress Policy – Block frontend to anywhere

πŸ‘‰ Block, frontend pods can connect anywhere.

apiVersion: "cilium.io/v2"
kind: CiliumNetworkPolicy
metadata:
  name: "l3-rule"
  namespace: dev
spec:
  endpointSelector:
    matchLabels:
      role: frontend
  egress:
    - {}

πŸ”Ή Service-Based Policies

πŸ‘‰ Frontend pods in dev namespace:

  • Can access myservice service in dev.

  • Can access any service in another-namespace with label env=staging.

apiVersion: "cilium.io/v2"
kind: CiliumNetworkPolicy
metadata:
  name: "l3-rule"
  namespace: dev
spec:
  endpointSelector:
    matchLabels:
      role: frontend
  egress:
  - toServices:
    - k8sService:
        serviceName: myservice
        namespace: dev
    - k8sServiceSelector:
        selector:
          matchLabels:
            env: staging
        namespace: another-namespace

πŸ”Ή CIDR-Based Policies

πŸ‘‰ myService pods can connect to:

  • A single IP 20.1.1.1.

  • Any IP in 10.0.0.0/8 except 10.96.0.0/12.

apiVersion: "cilium.io/v2"
kind: CiliumNetworkPolicy
metadata:
  name: "cidr-rule"
spec:
  endpointSelector:
    matchLabels:
      app: myService
  egress:
  - toCIDR:
    - 20.1.1.1/32
  - toCIDRSet:
    - cidr: 10.0.0.0/8
      except:
      - 10.96.0.0/12

πŸ”Ή Layer 4 (Port & Protocol-Based)

Port Range Rule

πŸ‘‰ myService pods can egress to any TCP port between 80–444.

apiVersion: "cilium.io/v2"
kind: CiliumNetworkPolicy
metadata:
  name: "l4-port-range-rule"
spec:
  endpointSelector:
    matchLabels:
      app: myService
  egress:
    - toPorts:
      - ports:
        - port: "80"
          endPort: 444
          protocol: TCP

Ingress with Port Restriction

πŸ‘‰ Frontend β†’ Backend allowed only on port 80/TCP.

apiVersion: "cilium.io/v2"
kind: CiliumNetworkPolicy
metadata:
  name: "l4-rule"
spec:
  endpointSelector:
    matchLabels:
      role: backend
  ingress:
  - fromEndpoints:
    - matchLabels:
        role: frontend
    toPorts:
    - ports:
      - port: "80"
        protocol: TCP

CIDR + Port Rule

πŸ‘‰ Crawlers can connect to 192.0.2.0/24 on port 80/TCP only.

apiVersion: "cilium.io/v2"
kind: CiliumNetworkPolicy
metadata:
  name: "cidr-l4-rule"
spec:
  endpointSelector:
    matchLabels:
      role: crawler
  egress:
  - toCIDR:
    - 192.0.2.0/24
    toPorts:
    - ports:
      - port: "80"
        protocol: TCP

πŸ”Ή Layer 7 (Application-Aware Policies)

HTTP Policy

πŸ‘‰ Only allows HTTP GET requests to /public on port 80 from pods labeled env=prod.

apiVersion: "cilium.io/v2"
kind: CiliumNetworkPolicy
metadata:
  name: "rule1"
spec:
  description: "Allow HTTP GET /public from env=prod to app=service"
  endpointSelector:
    matchLabels:
      app: service
  ingress:
  - fromEndpoints:
    - matchLabels:
        env: prod
    toPorts:
    - ports:
      - port: "80"
        protocol: TCP
      rules:
        http:
        - method: "GET"
          path: "/public"

DNS Restriction

πŸ‘‰ test-app pods can resolve only google.com and its subdomains.

apiVersion: "cilium.io/v2"
kind: CiliumNetworkPolicy
metadata:
  name: "to-fqdn"
spec:
  endpointSelector:
    matchLabels:
      app: test-app
  egress:
    - toEndpoints:
      - matchLabels:
          "k8s:io.kubernetes.pod.namespace": kube-system
          "k8s:k8s-app": kube-dns
      toPorts:
        - ports:
           - port: "53"
             protocol: ANY
          rules:
            dns:
              - matchName: "google.com"
              - matchPattern: "*.google.com"

Combined DNS + FQDN Rules

πŸ‘‰ Allows DNS resolution + HTTP access to specific domains and patterns.

apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
  name: "tofqdn-dns-visibility"
spec:
  endpointSelector:
    matchLabels:
      any:org: alliance
  egress:
  - toEndpoints:
    - matchLabels:
       "k8s:io.kubernetes.pod.namespace": kube-system
       "k8s:k8s-app": kube-dns
    toPorts:
      - ports:
         - port: "53"
           protocol: ANY
        rules:
          dns:
            - matchName: "cilium.io"
            - matchPattern: "*.cilium.io"
            - matchPattern: "*.api.cilium.io"

  - toFQDNs:
      - matchName: "cilium.io"
      - matchName: "sub.cilium.io"
      - matchName: "service1.api.cilium.io"
      - matchPattern: "special*service.api.cilium.io"
    toPorts:
      - ports:
         - port: "80"
           protocol: TCP

πŸ”Ή Entity-Based Policies

Pod β†’ Kube-API

πŸ‘‰ dev pods can talk to kube-apiserver entity (secure API access).

apiVersion: "cilium.io/v2"
kind: CiliumNetworkPolicy
metadata:
  name: "dev-to-kube-apiserver"
spec:
  endpointSelector:
    matchLabels:
      env: dev
  egress:
    - toEntities:
      - kube-apiserver

Ingress from Host & Nodes

πŸ‘‰ Allows traffic from host and cluster nodes into dev pods.

apiVersion: "cilium.io/v2"
kind: CiliumNetworkPolicy
metadata:
  name: "to-dev-from-nodes-in-cluster"
spec:
  endpointSelector:
    matchLabels:
      env: dev
  ingress:
    - fromEntities:
      - host
      - remote-node

World Entity

πŸ‘‰ Public pods can be accessed from outside cluster (internet).

apiVersion: "cilium.io/v2"
kind: CiliumNetworkPolicy
metadata:
  name: "from-world-to-role-public"
spec:
  endpointSelector:
    matchLabels:
      role: public
  ingress:
    - fromEntities:
      - world

πŸ”Ή Deny & Clusterwide Policies

Clusterwide Deny

πŸ‘‰ Denies all external (world) traffic clusterwide.
πŸ‘‰ Deny rules always override allow rules.

apiVersion: "cilium.io/v2"
kind: CiliumClusterwideNetworkPolicy
metadata:
  name: "external-lockdown"
spec:
  endpointSelector: {}
  ingressDeny:
  - fromEntities:
    - "world"
  ingress:
  - fromEntities:
    - "all"

πŸ† Key Takeaways

  • Cilium policies enhance Kubernetes NetworkPolicy with:

    • βœ… Deny rules

    • βœ… Cluster-wide enforcement

    • βœ… L7 security (HTTP, DNS, Kafka, gRPC)

    • βœ… DNS/FQDN support

    • βœ… Entity-based targeting (host, world, kube-apiserver)

  • You can start with namespace-level CNPs and gradually move to clusterwide CCNPs for stronger defaults.

0
Subscribe to my newsletter

Read articles from avinash gawade directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

avinash gawade
avinash gawade