Skip to content

Commit da54ac6

Browse files
clementnussGacko
andauthored
NGINX: Add X-Original-Forwarded-Host header. (#12999)
Signed-off-by: Clément Nussbaumer <[email protected]> Co-authored-by: Marco Ebert <[email protected]>
1 parent 974ca67 commit da54ac6

File tree

7 files changed

+52
-6
lines changed

7 files changed

+52
-6
lines changed

docs/user-guide/nginx-configuration/custom-template.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ TODO:
4444
- buildDenyVariable:
4545
- buildUpstreamName:
4646
- buildForwardedFor:
47+
- buildForwardedHost:
4748
- buildAuthSignURL:
4849
- buildNextUpstream:
4950
- filterRateLimits:

internal/ingress/controller/config/config.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -567,6 +567,10 @@ type Configuration struct {
567567
// Default is X-Forwarded-For
568568
ForwardedForHeader string `json:"forwarded-for-header,omitempty"`
569569

570+
// Sets the header field for identifying the originating Host header of a client
571+
// Default is X-Forwarded-Host
572+
ForwardedHostHeader string `json:"forwarded-host-header,omitempty"`
573+
570574
// Append the remote address to the X-Forwarded-For header instead of replacing it
571575
// Default: false
572576
ComputeFullForwardedFor bool `json:"compute-full-forwarded-for,omitempty"`
@@ -778,6 +782,7 @@ func NewDefault() Configuration {
778782
UseForwardedHeaders: false,
779783
EnableRealIP: false,
780784
ForwardedForHeader: "X-Forwarded-For",
785+
ForwardedHostHeader: "X-Forwarded-Host",
781786
ComputeFullForwardedFor: false,
782787
ProxyAddOriginalURIHeader: false,
783788
GenerateRequestID: true,

internal/ingress/controller/template/template.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,7 @@ var funcMap = text_template.FuncMap{
314314
},
315315
"isValidByteSize": isValidByteSize,
316316
"buildForwardedFor": buildForwardedFor,
317+
"buildForwardedHost": buildForwardedHost,
317318
"buildAuthSignURL": buildAuthSignURL,
318319
"buildAuthSignURLLocation": buildAuthSignURLLocation,
319320
"buildOpentelemetry": buildOpentelemetry,
@@ -1153,6 +1154,18 @@ func buildForwardedFor(input interface{}) string {
11531154
return fmt.Sprintf("$http_%v", ffh)
11541155
}
11551156

1157+
func buildForwardedHost(input interface{}) string {
1158+
s, ok := input.(string)
1159+
if !ok {
1160+
klog.Errorf("expected a 'string' type but %T was returned", input)
1161+
return ""
1162+
}
1163+
1164+
fhh := strings.ReplaceAll(s, "-", "_")
1165+
fhh = strings.ToLower(fhh)
1166+
return fmt.Sprintf("$http_%v", fhh)
1167+
}
1168+
11561169
func buildAuthSignURL(authSignURL, authRedirectParam string) string {
11571170
u, err := url.Parse(authSignURL)
11581171
if err != nil {

internal/ingress/controller/template/template_test.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -857,6 +857,24 @@ func TestBuildForwardedFor(t *testing.T) {
857857
}
858858
}
859859

860+
func TestBuildForwardedHost(t *testing.T) {
861+
invalidType := &ingress.Ingress{}
862+
expected := ""
863+
actual := buildForwardedHost(invalidType)
864+
865+
if expected != actual {
866+
t.Errorf("Expected '%v' but returned '%v'", expected, actual)
867+
}
868+
869+
inputStr := "X-Forwarded-Host"
870+
expected = "$http_x_forwarded_host"
871+
actual = buildForwardedHost(inputStr)
872+
873+
if expected != actual {
874+
t.Errorf("Expected '%v' but returned '%v'", expected, actual)
875+
}
876+
}
877+
860878
func TestBuildResolvers(t *testing.T) {
861879
ipOne := net.ParseIP("192.0.0.1")
862880
ipTwo := net.ParseIP("2001:db8:1234:0000:0000:0000:0000:0000")

rootfs/etc/nginx/template/nginx.tmpl

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1279,7 +1279,9 @@ stream {
12791279
{{ $proxySetHeader }} X-Scheme $pass_access_scheme;
12801280

12811281
# Pass the original X-Forwarded-For
1282-
{{ $proxySetHeader }} X-Original-Forwarded-For {{ buildForwardedFor $all.Cfg.ForwardedForHeader }};
1282+
{{ $proxySetHeader }} X-Original-Forwarded-For {{ buildForwardedFor $all.Cfg.ForwardedForHeader }};
1283+
# Pass the original X-Forwarded-Host
1284+
{{ $proxySetHeader }} X-Original-Forwarded-Host {{ buildForwardedHost $all.Cfg.ForwardedHostHeader }};
12831285

12841286
# mitigate HTTPoxy Vulnerability
12851287
# https://www.nginx.com/blog/mitigating-the-httpoxy-vulnerability-with-nginx/

test/e2e/settings/enable_real_ip.go

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,8 @@ var _ = framework.DescribeSetting("enable-real-ip", func() {
6565
Body().
6666
Raw()
6767

68-
assert.NotContains(ginkgo.GinkgoT(), body, "host=myhost")
68+
// we use a regexp to prevent matching the expression in the middle of the x-original-forwarded-host header
69+
assert.NotRegexp(ginkgo.GinkgoT(), `(\s)host=myhost`, body)
6970
assert.NotContains(ginkgo.GinkgoT(), body, "x-forwarded-host=myhost")
7071
assert.NotContains(ginkgo.GinkgoT(), body, "x-forwarded-proto=myproto")
7172
assert.NotContains(ginkgo.GinkgoT(), body, "x-forwarded-port=1234")
@@ -105,7 +106,9 @@ var _ = framework.DescribeSetting("enable-real-ip", func() {
105106
assert.Contains(ginkgo.GinkgoT(), body, "x-forwarded-port=80")
106107
assert.Contains(ginkgo.GinkgoT(), body, "x-forwarded-proto=http")
107108
assert.Contains(ginkgo.GinkgoT(), body, "x-original-forwarded-for=1.2.3.4")
108-
assert.NotContains(ginkgo.GinkgoT(), body, "host=myhost")
109+
assert.Contains(ginkgo.GinkgoT(), body, "x-original-forwarded-host=myhost")
110+
// we use a regexp to prevent matching the expression in the middle of the x-original-forwarded-host header
111+
assert.NotRegexp(ginkgo.GinkgoT(), `(\s)host=myhost`, body)
109112
assert.NotContains(ginkgo.GinkgoT(), body, "x-forwarded-host=myhost")
110113
assert.NotContains(ginkgo.GinkgoT(), body, "x-forwarded-proto=myproto")
111114
assert.NotContains(ginkgo.GinkgoT(), body, "x-forwarded-port=1234")

test/e2e/settings/forwarded_headers.go

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,8 @@ var _ = framework.DescribeSetting("use-forwarded-headers", func() {
6666
Body().
6767
Raw()
6868

69-
assert.Contains(ginkgo.GinkgoT(), body, "host=myhost")
69+
// we use a regexp to prevent matching the expression in the middle of the x-original-forwarded-host header
70+
assert.Regexp(ginkgo.GinkgoT(), `(\s)host=myhost`, body)
7071
assert.Contains(ginkgo.GinkgoT(), body, "x-forwarded-host=myhost")
7172
assert.Contains(ginkgo.GinkgoT(), body, "x-forwarded-proto=myproto")
7273
assert.Contains(ginkgo.GinkgoT(), body, "x-forwarded-scheme=myproto")
@@ -86,7 +87,8 @@ var _ = framework.DescribeSetting("use-forwarded-headers", func() {
8687
Body().
8788
Raw()
8889

89-
assert.Contains(ginkgo.GinkgoT(), body, "host=myhost.com")
90+
// we use a regexp to prevent matching the expression in the middle of the x-original-forwarded-host header
91+
assert.Regexp(ginkgo.GinkgoT(), `(\s)host=myhost.com`, body)
9092
assert.Contains(ginkgo.GinkgoT(), body, "x-forwarded-host=myhost.com")
9193
})
9294

@@ -121,7 +123,9 @@ var _ = framework.DescribeSetting("use-forwarded-headers", func() {
121123
assert.Contains(ginkgo.GinkgoT(), body, "x-forwarded-proto=http")
122124
assert.Contains(ginkgo.GinkgoT(), body, "x-forwarded-scheme=http")
123125
assert.Contains(ginkgo.GinkgoT(), body, "x-original-forwarded-for=1.2.3.4")
124-
assert.NotContains(ginkgo.GinkgoT(), body, "host=myhost")
126+
assert.Contains(ginkgo.GinkgoT(), body, "x-original-forwarded-host=myhost")
127+
// we use a regexp to prevent matching the expression in the middle of the x-original-forwarded-host header
128+
assert.NotRegexp(ginkgo.GinkgoT(), `(\s)host=myhost`, body)
125129
assert.NotContains(ginkgo.GinkgoT(), body, "x-forwarded-host=myhost")
126130
assert.NotContains(ginkgo.GinkgoT(), body, "x-forwarded-proto=myproto")
127131
assert.NotContains(ginkgo.GinkgoT(), body, "x-forwarded-scheme=myproto")

0 commit comments

Comments
 (0)