Skip to main content
In this lesson we explain how Kubernetes network policies interact with ingress controllers and the Gateway API (for example, Cilium’s Gateway API implementation). There are two distinct enforcement points you must consider when traffic arrives via an ingress or Gateway:
  1. External traffic entering the cluster and reaching the ingress/Gateway service (LoadBalancer or NodePort).
  2. Traffic flowing from the ingress/Gateway proxy to backend pods inside the cluster.
Below are example Cilium policies and guidance for each enforcement point, plus recommended scoping patterns to follow for production deployments.
When designing policies, consider both the traffic source (external clients vs. the ingress/Gateway identity) and the intended targets (ingress controller pods vs. backend application pods). Scoping policies to specific pod labels is safer than using broad selectors like empty endpointSelector or global entities.

Enforcement points at a glance

Enforcement pointWhat to allowTypical Cilium object
Outside → Ingress/GatewayAllow external world to reach ingress controller Service/PodsCiliumClusterwideNetworkPolicy scoped to ingress labels
Ingress/Gateway → Backend podsAllow ingress/Gateway proxy identity to reach backend podsCiliumNetworkPolicy or CiliumClusterwideNetworkPolicy allowing reserved:ingress or ingress pod labels

1) Allow external traffic into the cluster (to your ingress/Gateway)

If you have cluster-wide network restrictions in Cilium, external clients may be prevented from reaching your ingress Service (LoadBalancer/NodePort). A permissive cluster-wide policy that allows traffic from the outside world looks like this:
apiVersion: cilium.io/v2
kind: CiliumClusterwideNetworkPolicy
metadata:
  name: allow-external
spec:
  description: "Allow traffic from the outside world to ingress"
  endpointSelector: {}
  ingress:
  - fromEntities:
    - world
Notes:
  • endpointSelector: matches all endpoints in the cluster — this is very permissive and not recommended for production.
  • Instead, scope the policy to only the ingress/Gateway pods using a label selector (recommended). For example, if your ingress pods use label app: ingress-controller:
apiVersion: cilium.io/v2
kind: CiliumClusterwideNetworkPolicy
metadata:
  name: allow-external-to-ingress
spec:
  description: "Allow external traffic from world to ingress controller pods only"
  endpointSelector:
    matchLabels:
      app: ingress-controller
  ingress:
  - fromEntities:
    - world
This ensures only ingress controller pods receive traffic from the Internet while other pods remain protected by default-deny or narrower policies.

2) Allowing ingress/Gateway to talk to backend pods

Ingress and Gateway API implementations are represented inside Cilium by a reserved identity. Cilium exposes the reserved:ingress identity for traffic originating from the ingress/Gateway proxy. Backend pods must permit traffic from that identity (or from the ingress pod labels) so the proxy can reach application endpoints. A cluster-wide policy that allows egress from the reserved ingress identity into the cluster:
apiVersion: cilium.io/v2
kind: CiliumClusterwideNetworkPolicy
metadata:
  name: allow-ingress-egress
spec:
  description: "Allow all egress traffic from reserved:ingress identity to endpoints in the cluster"
  endpointSelector:
    matchExpressions:
    - key: reserved:ingress
      operator: Exists
  egress:
  - toEntities:
    - cluster
How this works:
  • The endpointSelector selects endpoints that carry the reserved:ingress identity (this identifies the ingress/Gateway proxy within Cilium).
  • The egress rule sends traffic to the entity cluster (all cluster endpoints). For tighter security, replace toEntities: cluster with endpointSelector-based destinations that match only your backend pods.
If your backend pods have their own CiliumNetworkPolicy protecting them, ensure those policies allow traffic from reserved:ingress or from the ingress-controller pod labels. Example allowing only the reserved identity:
apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
  name: allow-from-ingress
  namespace: my-app
spec:
  description: "Allow traffic from ingress/Gateway to backend"
  endpointSelector:
    matchLabels:
      app: my-backend
  ingress:
  - fromEndpoints:
    - matchExpressions:
      - key: reserved:ingress
        operator: Exists
Or allow from the ingress-controller pods using labels:
apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
  name: allow-from-ingress-controller
  namespace: my-app
spec:
  description: "Allow traffic from ingress controller pods to backend"
  endpointSelector:
    matchLabels:
      app: my-backend
  ingress:
  - fromEndpoints:
    - matchLabels:
        app: ingress-controller
Use label-based selectors for least-privilege access. Entity-based allowances like world or cluster are useful for testing, but for production prefer policies that target specific ingress pod labels or backend labels to limit blast radius.

Practical recommendations

  • Prefer CiliumClusterwideNetworkPolicy scoped to ingress labels for allowing external traffic into ingress Services.
  • Use reserved:ingress identity or ingress pod label selectors to permit the proxy to reach backend pods.
  • Avoid global endpointSelector: or broad entity allowances (world, cluster) unless you intentionally want cluster-wide access.
  • Test policies incrementally: first allow connectivity, then tighten selectors to application-specific labels.

Summary

  • Two enforcement points when using ingress/Gateway API: (1) outside → ingress and (2) ingress → backend.
  • Allow external traffic to reach the ingress Service, but scope that access to ingress pods via labels when possible.
  • Allow the reserved ingress identity (reserved:ingress) or ingress pod labels to reach backend pods; ensure backend policies explicitly permit that traffic.
  • Follow least-privilege practices: prefer label-based selectors and narrow rules in production.

Watch Video

Practice Lab