Egress Control in Kubernetes
Egress control restricts outbound traffic from Kubernetes pods to external destinations. Without egress controls, compromised pods can freely communicate with attacker infrastructure, exfiltrate data, or abuse cloud metadata services.
Implementing egress controls is a fundamental part of defense in depth and zero-trust networking.
1. Default Deny Egress Policies
Required knowledge for the CKS certification.
Issue: By default, Kubernetes pods can initiate connections to any external IP address, allowing compromised workloads to exfiltrate data or contact command-and-control servers.
Fix: Implement default-deny egress policies and explicitly allow only required outbound traffic.
Deny All Egress
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-egress
namespace: production
spec:
podSelector: {}
policyTypes:
- Egress
Allow Specific Destinations
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-specific-egress
namespace: production
spec:
podSelector:
matchLabels:
app: backend
policyTypes:
- Egress
egress:
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
podSelector:
matchLabels:
k8s-app: kube-dns
ports:
- protocol: UDP
port: 53
- protocol: TCP
port: 53
- to:
- ipBlock:
cidr: 10.0.0.0/8
- to:
- ipBlock:
cidr: 203.0.113.0/24
ports:
- protocol: TCP
port: 443
2. Block Cloud Metadata Service
Issue: Pods can access cloud metadata endpoints (169.254.169.254) to steal IAM credentials, access secrets, and pivot to other cloud resources.
Fix: Explicitly block access to cloud metadata service IP addresses in egress policies.
Block Metadata Access
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: block-metadata-service
namespace: production
spec:
podSelector: {}
policyTypes:
- Egress
egress:
- to:
- ipBlock:
cidr: 0.0.0.0/0
except:
- 169.254.169.254/32
- 169.254.170.2/32
Metadata endpoints to block:
- AWS EC2/EKS:
169.254.169.254 - AWS ECS:
169.254.170.2 - GCP:
169.254.169.254 - Azure:
169.254.169.254
3. Egress Gateways
Issue: Distributed egress makes it difficult to monitor, log, and control all outbound traffic from the cluster.
Fix: Route egress traffic through centralized gateways for consistent logging, policy enforcement, and IP allowlisting.
Istio Egress Gateway
apiVersion: networking.istio.io/v1beta1
kind: Gateway
metadata:
name: egress-gateway
namespace: istio-system
spec:
selector:
istio: egressgateway
servers:
- port:
number: 443
name: https
protocol: HTTPS
hosts:
- api.external-service.com
tls:
mode: PASSTHROUGH
---
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: external-api
namespace: production
spec:
hosts:
- api.external-service.com
gateways:
- mesh
- istio-system/egress-gateway
tls:
- match:
- gateways:
- mesh
port: 443
sniHosts:
- api.external-service.com
route:
- destination:
host: istio-egressgateway.istio-system.svc.cluster.local
port:
number: 443
4. FQDN-Based Egress Policies
Issue: IP-based egress policies are difficult to maintain when external services use dynamic IPs or CDNs.
Fix: Use FQDN-based policies (Cilium) to allow egress based on domain names instead of IP addresses.
Cilium FQDN Policy
apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
name: allow-github-egress
namespace: ci-cd
spec:
endpointSelector:
matchLabels:
app: build-agent
egress:
- toEndpoints:
- matchLabels:
k8s:io.kubernetes.pod.namespace: kube-system
k8s-app: kube-dns
toPorts:
- ports:
- port: "53"
protocol: UDP
rules:
dns:
- matchPattern: "*.github.com"
- matchPattern: "*.githubusercontent.com"
- toFQDNs:
- matchPattern: "*.github.com"
- matchPattern: "*.githubusercontent.com"
toPorts:
- ports:
- port: "443"
5. Proxy-Based Egress Control
Issue: Network policies cannot inspect HTTP traffic content or enforce URL-level restrictions.
Fix: Route HTTP/HTTPS traffic through a forward proxy to enable domain allowlisting and request logging.