Description
What happened:
It is possible to spoof the X-Forwarded-Host header during the HTTP to HTTPS redirect in Ingress NGINX. This may allow an attacker to manipulate the redirect behavior by providing a malicious or incorrect X-Forwarded-Host value.
What you expected to happen:
Ingress NGINX should properly handle the X-Forwarded-Host header and prevent it from being spoofed. X-Forwarded headers should only be considered safe from the CIDR ranges defined in property proxy-real-ip-cidr.
NGINX Ingress controller version (exec into the pod and run /nginx-ingress-controller --version
):
-------------------------------------------------------------------------------
NGINX Ingress controller
Release: v1.12.1
Build: 51c2b819690bbf1709b844dbf321a9acf6eda5a7
Repository: https://github.com/kubernetes/ingress-nginx
nginx version: nginx/1.25.5
-------------------------------------------------------------------------------
Kubernetes version (use kubectl version
): 1.32
Environment:
- Cloud provider or hardware configuration: AWS EKS
- OS (e.g. from /etc/os-release): Bottlerocket
- Kernel (e.g.
uname -a
):Linux ingress-nginx-gh-controller-649f86695b-7bsm2 6.1.124 #1 SMP PREEMPT_DYNAMIC Sat Jan 25 00:17:27 UTC 2025 x86_64 Linux
- Install tools:
- Cluster deployed via Terraform
- Basic cluster related info:
Client Version: v1.32.3 Kustomize Version: v5.5.0 Server Version: v1.32.2-eks-bc803b4
NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME
xxx Ready <none> 9d v1.32.0-eks-5ca49cb 10.0.204.226 <none> Amazon Linux 2 5.10.234-225.910.amzn2.x86_64 containerd://1.7.25
xxx Ready <none> 11d v1.32.0-eks-5ca49cb 10.0.74.3 <none> Amazon Linux 2 5.10.234-225.910.amzn2.x86_64 containerd://1.7.25
xxx Ready <none> 11h v1.32.0-eks-2e66e76 10.0.101.104 <none> Bottlerocket OS 1.32.0 (aws-k8s-1.32) 6.1.124 containerd://1.7.24+bottlerocket
xxx Ready <none> 11h v1.32.0-eks-2e66e76 10.0.102.164 <none> Bottlerocket OS 1.32.0 (aws-k8s-1.32) 6.1.124 containerd://1.7.24+bottlerocket
xxx Ready <none> 11h v1.32.0-eks-2e66e76 10.0.109.145 <none> Bottlerocket OS 1.32.0 (aws-k8s-1.32) 6.1.124 containerd://1.7.24+bottlerocket
xxx Ready <none> 11h v1.32.0-eks-2e66e76 10.0.116.60 <none> Bottlerocket OS 1.32.0 (aws-k8s-1.32) 6.1.124 containerd://1.7.24+bottlerocket
xxx Ready <none> 11h v1.32.0-eks-2e66e76 10.0.153.23 <none> Bottlerocket OS 1.32.0 (aws-k8s-1.32) 6.1.124 containerd://1.7.24+bottlerocket
xxx Ready <none> 11h v1.32.0-eks-2e66e76 10.0.172.111 <none> Bottlerocket OS 1.32.0 (aws-k8s-1.32) 6.1.124 containerd://1.7.24+bottlerocket
xxx Ready <none> 11h v1.32.0-eks-2e66e76 10.0.172.44 <none> Bottlerocket OS 1.32.0 (aws-k8s-1.32) 6.1.124 containerd://1.7.24+bottlerocket
xxx Ready <none> 11h v1.32.0-eks-2e66e76 10.0.177.173 <none> Bottlerocket OS 1.32.0 (aws-k8s-1.32) 6.1.124 containerd://1.7.24+bottlerocket
xxx Ready <none> 11h v1.32.0-eks-2e66e76 10.0.189.164 <none> Bottlerocket OS 1.32.0 (aws-k8s-1.32) 6.1.124 containerd://1.7.24+bottlerocket
xxx Ready <none> 11h v1.32.0-eks-2e66e76 10.0.198.209 <none> Bottlerocket OS 1.32.0 (aws-k8s-1.32) 6.1.124 containerd://1.7.24+bottlerocket
xxx Ready <none> 11h v1.32.0-eks-2e66e76 10.0.211.8 <none> Bottlerocket OS 1.32.0 (aws-k8s-1.32) 6.1.124 containerd://1.7.24+bottlerocket
xxx Ready <none> 11h v1.32.0-eks-2e66e76 10.0.227.230 <none> Bottlerocket OS 1.32.0 (aws-k8s-1.32) 6.1.124 containerd://1.7.24+bottlerocket
xxx Ready <none> 11h v1.32.0-eks-2e66e76 10.0.246.197 <none> Bottlerocket OS 1.32.0 (aws-k8s-1.32) 6.1.124 containerd://1.7.24+bottlerocket
xxx Ready <none> 11h v1.32.0-eks-2e66e76 10.0.253.65 <none> Bottlerocket OS 1.32.0 (aws-k8s-1.32) 6.1.124 containerd://1.7.24+bottlerocket
xxx Ready <none> 11h v1.32.0-eks-2e66e76 10.0.94.237 <none> Bottlerocket OS 1.32.0 (aws-k8s-1.32) 6.1.124 containerd://1.7.24+bottlerocket
-
How was the ingress-nginx-controller installed:
- If helm was used then please show output of
helm ls -A | grep -i ingress
- If helm was used then please show output of
ingress-nginx xxx 7 2025-03-06 05:24:31.654774386 +0000 UTC deployed ingress-nginx-4.12.0 1.12.0
- If helm was used then please show output of
helm -n <ingresscontrollernamespace> get values <helmreleasename>
USER-SUPPLIED VALUES:
controller:
allowSnippetAnnotations: false
config:
enable-ocsp: true
enable-owasp-modsecurity-crs: "true"
enable-real-ip: true
generate-request-id: true
hsts-max-age: "31536000"
http-snippet: ssl_conf_command Ciphersuites TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256;
proxy-connect-timeout: "60"
proxy-read-timeout: "1800"
proxy-real-ip-cidr: 10.0.0.0/16
proxy-send-timeout: "1800"
real-ip-recursive: "on"
ssl-ciphers: ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES256-GCM-SHA384
ssl-protocols: TLSv1.2 TLSv1.3
ssl-session-cache: "true"
ssl-session-cache-size: 10m
upstream-keepalive-requests: "1000"
upstream-keepalive-timeout: "55"
use-forwarded-headers: "true"
use-http2: true
use-proxy-protocol: "true"
worker-shutdown-timeout: 60s
service:
annotations:
service.beta.kubernetes.io/aws-load-balancer-access-log-emit-interval: "60"
service.beta.kubernetes.io/aws-load-balancer-access-log-enabled: "true"
service.beta.kubernetes.io/aws-load-balancer-access-log-s3-bucket-name: xxx
service.beta.kubernetes.io/aws-load-balancer-additional-resource-tags: xxx
service.beta.kubernetes.io/aws-load-balancer-attributes: dns_record.client_routing_policy=availability_zone_affinity
service.beta.kubernetes.io/aws-load-balancer-backend-protocol: tcp
service.beta.kubernetes.io/aws-load-balancer-cross-zone-load-balancing-enabled: "false"
service.beta.kubernetes.io/aws-load-balancer-nlb-target-type: instance
service.beta.kubernetes.io/aws-load-balancer-proxy-protocol: '*'
service.beta.kubernetes.io/aws-load-balancer-scheme: internet-facing
service.beta.kubernetes.io/aws-load-balancer-type: nlb-ip
externalTrafficPolicy: Local
...
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
kubernetes.io/ingress.class: xxx
nginx.ingress.kubernetes.io/backend-protocol: HTTP
nginx.ingress.kubernetes.io/enable-opentracing: "false"
nginx.ingress.kubernetes.io/proxy-body-size: 0m
nginx.ingress.kubernetes.io/proxy-read-timeout: "3600"
nginx.ingress.kubernetes.io/proxy-send-timeout: "3600"
nginx.ingress.kubernetes.io/service-upstream: "true"
nginx.ingress.kubernetes.io/ssl-redirect: "true"
creationTimestamp: "2025-02-25T08:32:21Z"
generation: 1
name: ingress
namespace: xxx
spec:
rules:
- host: xxx.mysaas.com
http:
paths:
- backend:
service:
name: xxx
port:
number: 80
path: /
pathType: Prefix
tls:
- hosts:
- xxx.mysaas.com
secretName: tls-secret
status:
loadBalancer:
ingress:
- hostname: xxx
How to reproduce this issue:
- Install Ingress NGINX via Helm chart and set parameter proxy-real-ip-cidr.
- Create an ingress with annotation nginx.ingress.kubernetes.io/ssl-redirect: "true"
- make a request with spoofed X-Forwarded-Host header
Note that the request needs to be via HTTP and not HTTPS to reproduce the redirect.
curl --path-as-is -i -s -k -v -X $'GET' -H $'Host: xxx.mysaas.com' -H $'X-Forwarded-Host: kubernetes.github.io/ingress-nginx' $'http://xxx.mysaas.com/'
Result:
HTTP/1.1 308 Permanent Redirect
Date: Fri, 04 Apr 2025 15:45:46 GMT
Content-Type: text/html
Content-Length: 164
Connection: keep-alive
Location: https://kubernetes.github.io/ingress-nginx
<html>
<head><title>308 Permanent Redirect</title></head>
<body>
<center><h1>308 Permanent Redirect</h1></center>
<hr><center>nginx</center>
</body>
</html>
Metadata
Metadata
Assignees
Labels
Type
Projects
Status