Kubernetes Administration — Level by Level›20 · Ingress & egress gateways

Lesson 20 of 32 · Level 3 — Application delivery

Ingress & egress gateways

Control how traffic enters and leaves the cluster: an ingress gateway with the Gateway API, default-deny egress with NetworkPolicies, and egress gateways that give outbound traffic a fixed, audited exit point, plus a guide for choosing the right tool at each step.

Practitioner → Advanced
Key wordsingress gatewayegress gatewayGateway APIGatewayClassGatewayHTTPRouteegress NetworkPolicydefault deny egressFQDN policiesfixed egress IPIstioCilium
Users internet Partner API api.partner.example allow-lists our IP Kubernetes cluster Ingress gateway Gateway API TLS, routing, auth Services & pods shop, api, … Egress gateway fixed source IP allow-list + logs app pod needs partner API direct egress blocked by NetworkPolicy Policy & audit who may enter who may leave one place to log Implementations Istio egress gateway Cilium egress gateway proxy / NAT gateway
One controlled way in (ingress gateway) and one controlled way out (egress gateway).

Entry and exit points

So far, traffic came in through an Ingress (lessons 05–19), and pods could call anything outside. In production you want both directions controlled:

  • Ingress gateway: the single, well-configured entry point (TLS, routing, auth, rate limits).
  • Egress control: pods may only reach what they need; sensitive destinations go through an egress gateway with a fixed IP and logs.

A school has a main gate where visitors sign in (ingress gateway). Children can't just wander off the grounds either: to go out on a trip, they leave through the school office with a teacher (egress gateway), so the school knows who left, when, and where they went. Side gates stay locked (default-deny egress).

Ingress gateway with the Gateway API

The Gateway API is the successor to Ingress, implemented by many controllers (Envoy Gateway, Istio, Cilium, NGINX Gateway Fabric, Traefik, Kong and others).

apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: public
  namespace: gateway-infra               # owned by the platform team
spec:
  gatewayClassName: example-gc            # provided by your chosen implementation
  listeners:
    - name: https
      protocol: HTTPS
      port: 443
      hostname: "*.example.com"
      tls:
        mode: Terminate
        certificateRefs: [ { name: wildcard-example-com } ]
      allowedRoutes:
        namespaces: { from: All }
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: podinfo
  namespace: shop-web                     # owned by the app team
spec:
  parentRefs: [ { name: public, namespace: gateway-infra } ]
  hostnames: [ "podinfo.example.com" ]
  rules:
    - matches: [ { path: { type: PathPrefix, value: / } } ]
      backendRefs: [ { name: podinfo, port: 80 } ]

The platform owns the Gateway (addresses, certificates, which namespaces may attach); app teams own their HTTPRoutes. Traffic splitting, header matching, redirects and more are part of the standard API. More in Kubernetes Security & Hardening, lesson 08.

Egress, step 1: default-deny with NetworkPolicy

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-egress
  namespace: shop-web
spec:
  podSelector: {}
  policyTypes: [ Egress ]
  egress:
    - to:                                  # allow DNS, or nothing resolves
        - namespaceSelector:
            matchLabels: { kubernetes.io/metadata.name: kube-system }
          podSelector:
            matchLabels: { k8s-app: kube-dns }
      ports:
        - { protocol: UDP, port: 53 }
        - { protocol: TCP, port: 53 }
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: api-to-postgres
  namespace: shop-web
spec:
  podSelector:
    matchLabels: { tier: api }
  policyTypes: [ Egress ]
  egress:
    - to:
        - namespaceSelector:
            matchLabels: { kubernetes.io/metadata.name: shop-data }
          podSelector:
            matchLabels: { app: postgres }
      ports: [ { protocol: TCP, port: 5432 } ]

Standard NetworkPolicies select destinations by pods, namespaces and IP ranges (ipBlock), not by DNS names. To allow api.partner.example by name, you need CNI extensions such as Cilium's toFQDNs rules (or equivalents in other CNIs).

Egress, step 2: an egress gateway

An egress gateway sends selected outbound traffic through specific nodes or proxies:

  • a fixed source IP that partners and firewalls can allow-list,
  • one place for logging, TLS origination and policy,
  • no need to open firewalls for every node.
Option How it works Choose when
Cilium Egress Gateway CiliumEgressGatewayPolicy sends matching pod traffic out via chosen gateway nodes with a fixed egress IP You run Cilium; need fixed IPs at L3/L4
Istio egress gateway Mesh traffic to external hosts (declared with ServiceEntry) is routed through an Envoy egress gateway You run Istio; want L7 control and logs (see Service Mesh — Istio & Linkerd)
Outbound proxy / NAT gateway Pods use an HTTP(S) proxy, or the network NATs to a fixed IP Simple, CNI-independent; less per-pod granularity

A Cilium example (check field names against your Cilium version):

apiVersion: cilium.io/v2
kind: CiliumEgressGatewayPolicy
metadata:
  name: partner-api
spec:
  selectors:
    - podSelector:
        matchLabels: { app: payments, io.kubernetes.pod.namespace: shop-web }
  destinationCIDRs: [ "198.51.100.0/24" ]      # the partner's API range
  egressGateway:
    nodeSelector:
      matchLabels: { egress-gateway: "true" }
    egressIP: 10.0.0.250

Where to choose what

Need Start with
Expose HTTP apps with TLS Ingress (lesson 05) or, for new platforms, a Gateway API implementation
Stop pods calling anything they like Default-deny egress NetworkPolicies + explicit allows
Allow external services by DNS name CNI FQDN policies (e.g. Cilium) or an egress proxy
Fixed source IP for partners/firewalls Egress gateway (Cilium/Istio) or NAT gateway/proxy
L7 control and audit of outbound calls Istio egress gateway or an outbound proxy

Try it: in and out

  1. Install the Gateway API CRDs and one implementation (e.g. Envoy Gateway's quick start), create a Gateway and an HTTPRoute for podinfo, and reach it.
  2. On a cluster with an enforcing CNI, apply the default-deny egress policy without the DNS rule and watch nslookup fail; add the DNS rule back.
  3. Allow tier: api pods to reach only postgres; confirm other egress fails (wget to an external site).
  4. (Cilium lab) Label a node as egress gateway and apply an egress gateway policy; check the source IP seen by an external echo service.
  5. For one real app, list every outbound destination it needs, and draft its egress policy.

Going deeper: traffic governance

  • Keep ingress and egress policies in Git with the app; review new egress destinations like firewall changes.
  • Monitor denied flows (Cilium Hubble, Calico flow logs) to catch missing rules before users do.
  • Egress gateways are critical paths: run more than one, and know their failover behaviour.

Recap

  • Ingress gateway: one controlled entry point; the Gateway API separates platform-owned Gateways from team-owned Routes.
  • Egress: start with default-deny + DNS + explicit allows; standard policies can't match DNS names.
  • Egress gateways (Cilium, Istio, proxies/NAT) give a fixed source IP, logging and policy.
  • Choose by need: exposure, restriction, FQDN rules, fixed IP, L7 audit.

This site is a public version of my personal engineering knowledge hub. It intentionally excludes confidential company information and internal operational details.