Kubernetes Ingress Setup
This guide covers deploying Gordon behind a Kubernetes ingress controller for TLS termination.
Overview
In Kubernetes, an Ingress Controller handles external traffic:
- Terminates TLS connections
- Routes requests to services based on hostname/path
- Sets
X-Forwarded-Forheader with the client's real IP
Common ingress controllers: nginx-ingress, Traefik, Contour, Kong.
Architecture
Client → Ingress Controller (HTTPS/443) → Gordon Service (HTTP) → Gordon Pod
↓
TLS terminated
X-Forwarded-For set
Understanding Pod Network CIDR
Kubernetes assigns each pod an IP from the pod network CIDR. When the ingress controller forwards requests to Gordon, it comes from the ingress pod's IP.
To find your pod network CIDR:
# Method 1: Check cluster-info
kubectl cluster-info dump | grep -m 1 cluster-cidr
# Method 2: Check CNI config (varies by CNI plugin)
kubectl get cm -n kube-system kubeadm-config -o yaml | grep podSubnet
# Method 3: Check a pod's IP to infer the range
kubectl get pods -o wide
Common defaults by CNI:
| CNI Plugin | Default Pod CIDR |
|---|---|
| Flannel | 10.244.0.0/16 |
| Calico | 192.168.0.0/16 |
| Weave | 10.32.0.0/12 |
| Cilium | 10.0.0.0/8 |
| GKE | 10.0.0.0/14 or /9 |
| EKS (VPC CNI) | VPC CIDR |
Gordon Configuration
[api.rate_limit]
enabled = true
# Trust pod network so Gordon reads X-Forwarded-For from ingress
trusted_proxies = ["10.244.0.0/16"] # Replace with your pod CIDR
Ingress Controller Configuration
The ingress controller must be configured to forward the real client IP.
nginx-ingress
Create or update the ConfigMap:
apiVersion: v1
kind: ConfigMap
metadata:
name: ingress-nginx-controller
namespace: ingress-nginx
data:
# Enable X-Forwarded-For processing
use-forwarded-headers: "true"
# Trust these CIDRs to set X-Forwarded-For
# Include your cloud provider's load balancer IPs
proxy-real-ip-cidr: "0.0.0.0/0" # Or specific LB CIDR
If you have a cloud load balancer in front of nginx-ingress (common in EKS, GKE):
data:
use-forwarded-headers: "true"
use-proxy-protocol: "false" # Set to "true" if using NLB with proxy protocol
proxy-real-ip-cidr: "10.0.0.0/8,192.168.0.0/16"
Traefik
In your Traefik configuration:
# traefik.yaml or Helm values
entryPoints:
web:
address: ":80"
forwardedHeaders:
trustedIPs:
- "10.0.0.0/8"
- "192.168.0.0/16"
websecure:
address: ":443"
forwardedHeaders:
trustedIPs:
- "10.0.0.0/8"
- "192.168.0.0/16"
Complete Example
Gordon Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: gordon
spec:
replicas: 1
selector:
matchLabels:
app: gordon
template:
metadata:
labels:
app: gordon
spec:
containers:
- name: gordon
image: your-registry/gordon:latest
ports:
- containerPort: 80
- containerPort: 5000
volumeMounts:
- name: config
mountPath: /etc/gordon
- name: docker-sock
mountPath: /var/run/docker.sock
volumes:
- name: config
configMap:
name: gordon-config
- name: docker-sock
hostPath:
path: /var/run/docker.sock
---
apiVersion: v1
kind: Service
metadata:
name: gordon
spec:
selector:
app: gordon
ports:
- name: http
port: 80
targetPort: 80
- name: registry
port: 5000
targetPort: 5000
Gordon ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
name: gordon-config
data:
gordon.toml: |
[server]
port = 80
registry_port = 5000
gordon_domain = "gordon.example.com"
[api.rate_limit]
enabled = true
trusted_proxies = ["10.244.0.0/16"]
[auth]
enabled = false # Configure auth as needed
Ingress Resource
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: gordon
annotations:
nginx.ingress.kubernetes.io/proxy-body-size: "0" # Unlimited for image uploads
spec:
ingressClassName: nginx
tls:
- hosts:
- gordon.example.com
secretName: gordon-tls
rules:
- host: gordon.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: gordon
port:
number: 80
Verifying the Setup
- Deploy Gordon and the ingress
- Make a request through the ingress
- Check Gordon logs:
kubectl logs -l app=gordon | grep "client_ip"
If you see the ingress pod's IP instead of the client's IP:
- Verify
trusted_proxiesincludes your pod CIDR - Verify ingress controller has
use-forwarded-headers: "true"
Cloud-Specific Notes
GKE
GKE uses a VPC-native pod network. Find the pod CIDR in:
- Console → Kubernetes Engine → Cluster → Networking → Pod address range
trusted_proxies = ["10.0.0.0/14"] # GKE default
EKS
EKS with VPC CNI assigns pod IPs from your VPC subnets:
trusted_proxies = ["10.0.0.0/16"] # Your VPC CIDR
AKS
AKS default pod CIDR:
trusted_proxies = ["10.244.0.0/16"] # Azure CNI default
Troubleshooting
All requests show the same IP
Symptom: Rate limiting ineffective, logs show ingress pod IP.
Cause: trusted_proxies missing pod CIDR or ingress not forwarding headers.
Fix:
- Add pod CIDR to
trusted_proxies - Enable
use-forwarded-headersin ingress controller
X-Forwarded-For contains multiple IPs
Symptom: X-Forwarded-For: 203.0.113.50, 10.0.1.5, 10.244.0.3
This is normal when multiple proxies are involved (cloud LB → ingress → Gordon). Gordon takes the first IP (leftmost), which is the original client.
Client IP shows cloud load balancer
Symptom: Client IP is AWS/GCP/Azure LB IP, not real client.
Cause: Cloud LB not configured to forward client IP.
Fix: Configure cloud LB to preserve or pass client IP (varies by provider).