v2.15.1 c808592

kubernetes-ingress.md RawHistoryFolder

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-For header 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

  1. Deploy Gordon and the ingress
  2. Make a request through the ingress
  3. 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_proxies includes 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:

  1. Add pod CIDR to trusted_proxies
  2. Enable use-forwarded-headers in 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).

source
1# Kubernetes Ingress Setup
2
3This guide covers deploying Gordon behind a Kubernetes ingress controller for TLS termination.
4
5## Overview
6
7In Kubernetes, an **Ingress Controller** handles external traffic:
8- Terminates TLS connections
9- Routes requests to services based on hostname/path
10- Sets `X-Forwarded-For` header with the client's real IP
11
12Common ingress controllers: **nginx-ingress**, **Traefik**, **Contour**, **Kong**.
13
14## Architecture
15
16```
17Client → Ingress Controller (HTTPS/443) → Gordon Service (HTTP) → Gordon Pod
18                    ↓
19              TLS terminated
20              X-Forwarded-For set
21```
22
23## Understanding Pod Network CIDR
24
25Kubernetes 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.
26
27**To find your pod network CIDR:**
28
29```bash
30# Method 1: Check cluster-info
31kubectl cluster-info dump | grep -m 1 cluster-cidr
32
33# Method 2: Check CNI config (varies by CNI plugin)
34kubectl get cm -n kube-system kubeadm-config -o yaml | grep podSubnet
35
36# Method 3: Check a pod's IP to infer the range
37kubectl get pods -o wide
38```
39
40**Common defaults by CNI:**
41
42| CNI Plugin | Default Pod CIDR |
43|------------|------------------|
44| Flannel | `10.244.0.0/16` |
45| Calico | `192.168.0.0/16` |
46| Weave | `10.32.0.0/12` |
47| Cilium | `10.0.0.0/8` |
48| GKE | `10.0.0.0/14` or `/9` |
49| EKS (VPC CNI) | VPC CIDR |
50
51## Gordon Configuration
52
53```toml
54[api.rate_limit]
55enabled = true
56# Trust pod network so Gordon reads X-Forwarded-For from ingress
57trusted_proxies = ["10.244.0.0/16"]  # Replace with your pod CIDR
58```
59
60## Ingress Controller Configuration
61
62The ingress controller must be configured to forward the real client IP.
63
64### nginx-ingress
65
66Create or update the ConfigMap:
67
68```yaml
69apiVersion: v1
70kind: ConfigMap
71metadata:
72  name: ingress-nginx-controller
73  namespace: ingress-nginx
74data:
75  # Enable X-Forwarded-For processing
76  use-forwarded-headers: "true"
77  # Trust these CIDRs to set X-Forwarded-For
78  # Include your cloud provider's load balancer IPs
79  proxy-real-ip-cidr: "0.0.0.0/0"  # Or specific LB CIDR
80```
81
82If you have a cloud load balancer in front of nginx-ingress (common in EKS, GKE):
83
84```yaml
85data:
86  use-forwarded-headers: "true"
87  use-proxy-protocol: "false"  # Set to "true" if using NLB with proxy protocol
88  proxy-real-ip-cidr: "10.0.0.0/8,192.168.0.0/16"
89```
90
91### Traefik
92
93In your Traefik configuration:
94
95```yaml
96# traefik.yaml or Helm values
97entryPoints:
98  web:
99    address: ":80"
100    forwardedHeaders:
101      trustedIPs:
102        - "10.0.0.0/8"
103        - "192.168.0.0/16"
104  websecure:
105    address: ":443"
106    forwardedHeaders:
107      trustedIPs:
108        - "10.0.0.0/8"
109        - "192.168.0.0/16"
110```
111
112## Complete Example
113
114### Gordon Deployment
115
116```yaml
117apiVersion: apps/v1
118kind: Deployment
119metadata:
120  name: gordon
121spec:
122  replicas: 1
123  selector:
124    matchLabels:
125      app: gordon
126  template:
127    metadata:
128      labels:
129        app: gordon
130    spec:
131      containers:
132      - name: gordon
133        image: your-registry/gordon:latest
134        ports:
135        - containerPort: 80
136        - containerPort: 5000
137        volumeMounts:
138        - name: config
139          mountPath: /etc/gordon
140        - name: docker-sock
141          mountPath: /var/run/docker.sock
142      volumes:
143      - name: config
144        configMap:
145          name: gordon-config
146      - name: docker-sock
147        hostPath:
148          path: /var/run/docker.sock
149---
150apiVersion: v1
151kind: Service
152metadata:
153  name: gordon
154spec:
155  selector:
156    app: gordon
157  ports:
158  - name: http
159    port: 80
160    targetPort: 80
161  - name: registry
162    port: 5000
163    targetPort: 5000
164```
165
166### Gordon ConfigMap
167
168```yaml
169apiVersion: v1
170kind: ConfigMap
171metadata:
172  name: gordon-config
173data:
174  gordon.toml: |
175    [server]
176    port = 80
177    registry_port = 5000
178    gordon_domain = "gordon.example.com"
179
180    [api.rate_limit]
181    enabled = true
182    trusted_proxies = ["10.244.0.0/16"]
183
184    [auth]
185    enabled = false  # Configure auth as needed
186```
187
188### Ingress Resource
189
190```yaml
191apiVersion: networking.k8s.io/v1
192kind: Ingress
193metadata:
194  name: gordon
195  annotations:
196    nginx.ingress.kubernetes.io/proxy-body-size: "0"  # Unlimited for image uploads
197spec:
198  ingressClassName: nginx
199  tls:
200  - hosts:
201    - gordon.example.com
202    secretName: gordon-tls
203  rules:
204  - host: gordon.example.com
205    http:
206      paths:
207      - path: /
208        pathType: Prefix
209        backend:
210          service:
211            name: gordon
212            port:
213              number: 80
214```
215
216## Verifying the Setup
217
2181. Deploy Gordon and the ingress
2192. Make a request through the ingress
2203. Check Gordon logs:
221
222```bash
223kubectl logs -l app=gordon | grep "client_ip"
224```
225
226If you see the ingress pod's IP instead of the client's IP:
227- Verify `trusted_proxies` includes your pod CIDR
228- Verify ingress controller has `use-forwarded-headers: "true"`
229
230## Cloud-Specific Notes
231
232### GKE
233
234GKE uses a VPC-native pod network. Find the pod CIDR in:
235- Console → Kubernetes Engine → Cluster → Networking → Pod address range
236
237```toml
238trusted_proxies = ["10.0.0.0/14"]  # GKE default
239```
240
241### EKS
242
243EKS with VPC CNI assigns pod IPs from your VPC subnets:
244
245```toml
246trusted_proxies = ["10.0.0.0/16"]  # Your VPC CIDR
247```
248
249### AKS
250
251AKS default pod CIDR:
252
253```toml
254trusted_proxies = ["10.244.0.0/16"]  # Azure CNI default
255```
256
257## Troubleshooting
258
259### All requests show the same IP
260
261**Symptom:** Rate limiting ineffective, logs show ingress pod IP.
262
263**Cause:** `trusted_proxies` missing pod CIDR or ingress not forwarding headers.
264
265**Fix:**
2661. Add pod CIDR to `trusted_proxies`
2672. Enable `use-forwarded-headers` in ingress controller
268
269### X-Forwarded-For contains multiple IPs
270
271**Symptom:** `X-Forwarded-For: 203.0.113.50, 10.0.1.5, 10.244.0.3`
272
273This is normal when multiple proxies are involved (cloud LB → ingress → Gordon). Gordon takes the **first** IP (leftmost), which is the original client.
274
275### Client IP shows cloud load balancer
276
277**Symptom:** Client IP is AWS/GCP/Azure LB IP, not real client.
278
279**Cause:** Cloud LB not configured to forward client IP.
280
281**Fix:** Configure cloud LB to preserve or pass client IP (varies by provider).
282
283## Related
284
285- [Rate Limiting Configuration](../../docs/config/rate-limiting.md)
286- [AWS ALB Guide](./aws-alb.md)
287- [Running Gordon in a Container](./running-in-container.md)

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 7

Tags 106

↑↓ moveenter openesc close