Skip to content

Commit 513b580

Browse files
committed
add support for preserving clientIP
1 parent 1476fe0 commit 513b580

File tree

16 files changed

+684
-34
lines changed

16 files changed

+684
-34
lines changed

apis/v1alpha1/nginxproxy_types.go

+49
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,12 @@ type NginxProxySpec struct {
5353
//
5454
// +optional
5555
Telemetry *Telemetry `json:"telemetry,omitempty"`
56+
// RewriteClientIP defines configuration for rewriting the client IP to the original client's IP.
57+
// +kubebuilder:validation:XValidation:message="if mode is set, trustedAddresses is a required field",rule="!(has(self.mode) && !has(self.trustedAddresses))"
58+
//
59+
// +optional
60+
//nolint:lll
61+
RewriteClientIP *RewriteClientIP `json:"rewriteClientIP,omitempty"`
5662
// DisableHTTP2 defines if http2 should be disabled for all servers.
5763
// Default is false, meaning http2 will be enabled for all servers.
5864
//
@@ -114,3 +120,46 @@ type TelemetryExporter struct {
114120
// +kubebuilder:validation:Pattern=`^(?:http?:\/\/)?[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?(?:\.[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?)*(?::\d{1,5})?$`
115121
Endpoint string `json:"endpoint"`
116122
}
123+
124+
// RewriteClientIP specifies the configuration for rewriting the client's IP address.
125+
type RewriteClientIP struct {
126+
// Mode defines how NGINX will rewrite the client's IP address.
127+
// Possible modes: ProxyProtocol, XForwardedFor.
128+
//
129+
// +optional
130+
Mode *RewriteClientIPModeType `json:"mode,omitempty"`
131+
132+
// SetIPRecursively configures whether recursive search is used for selecting client's
133+
// address from the X-Forwarded-For header and used in conjunction with TrustedAddresses.
134+
// If enabled, NGINX will recurse on the values in X-Forwarded-Header from the end of
135+
// array to start of array and select the first untrusted IP.
136+
//
137+
// +optional
138+
SetIPRecursively *bool `json:"setIPRecursively,omitempty"`
139+
140+
// TrustedAddresses specifies the addresses that are trusted to send correct client IP information.
141+
// If a request comes from a trusted address, NGINX will rewrite the client IP information,
142+
// and forward it to the backend in the X-Forwarded-For* and X-Real-IP headers.
143+
// This field is required if mode is set.
144+
// +kubebuilder:validation:MaxItems=16
145+
//
146+
// +optional
147+
TrustedAddresses []string `json:"trustedAddresses,omitempty"`
148+
}
149+
150+
// RewriteClientIPModeType defines how NGINX Gateway Fabric will determine the client's original IP address.
151+
// +kubebuilder:validation:Enum=ProxyProtocol;XForwardedFor
152+
type RewriteClientIPModeType string
153+
154+
const (
155+
// RewriteClientIPModeProxyProtocol configures NGINX to accept PROXY protocol and,
156+
// set the client's IP address to the IP address in the PROXY protocol header.
157+
// Sets the proxy_protocol parameter to the listen directive on all servers, and sets real_ip_header
158+
// to proxy_protocol: https://nginx.org/en/docs/http/ngx_http_realip_module.html#real_ip_header.
159+
RewriteClientIPModeProxyProtocol RewriteClientIPModeType = "ProxyProtocol"
160+
161+
// RewriteClientIPModeXForwardedFor configures NGINX to set the client's IP address to the
162+
// IP address in the X-Forwarded-For HTTP header.
163+
// https://nginx.org/en/docs/http/ngx_http_realip_module.html#real_ip_header.
164+
RewriteClientIPModeXForwardedFor RewriteClientIPModeType = "XForwardedFor"
165+
)

apis/v1alpha1/zz_generated.deepcopy.go

+35
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

charts/nginx-gateway-fabric/values.yaml

+4
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,10 @@ nginx:
9393
{}
9494
# disableHTTP2: false
9595
# ipFamily: dual
96+
# rewriteClientIP:
97+
# mode: "ProxyProtocol"
98+
# trustedAddresses: ["127.0.0.0/28", "::1/128"]
99+
# setIPRecursively: true
96100
# telemetry:
97101
# exporter:
98102
# endpoint: otel-collector.default.svc:4317

config/crd/bases/gateway.nginx.org_nginxproxies.yaml

+33
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,39 @@ spec:
6262
- ipv4
6363
- ipv6
6464
type: string
65+
rewriteClientIP:
66+
description: RewriteClientIP defines configuration for rewriting the
67+
client IP to the original client's IP.
68+
properties:
69+
mode:
70+
description: |-
71+
Mode defines how NGINX will rewrite the client's IP address.
72+
Possible modes: ProxyProtocol, XForwardedFor.
73+
enum:
74+
- ProxyProtocol
75+
- XForwardedFor
76+
type: string
77+
setIPRecursively:
78+
description: |-
79+
SetIPRecursively configures whether recursive search is used for selecting client's
80+
address from the X-Forwarded-For header and used in conjunction with TrustedAddresses.
81+
If enabled, NGINX will recurse on the values in X-Forwarded-Header from the end of
82+
array to start of array and select the first untrusted IP.
83+
type: boolean
84+
trustedAddresses:
85+
description: |-
86+
TrustedAddresses specifies the addresses that are trusted to send correct client IP information.
87+
If a request comes from a trusted address, NGINX will rewrite the client IP information,
88+
and forward it to the backend in the X-Forwarded-For* and X-Real-IP headers.
89+
This field is required if mode is set.
90+
items:
91+
type: string
92+
maxItems: 16
93+
type: array
94+
type: object
95+
x-kubernetes-validations:
96+
- message: if mode is set, trustedAddresses is a required field
97+
rule: '!(has(self.mode) && !has(self.trustedAddresses))'
6598
telemetry:
6699
description: Telemetry specifies the OpenTelemetry configuration.
67100
properties:

deploy/crds.yaml

+33
Original file line numberDiff line numberDiff line change
@@ -707,6 +707,39 @@ spec:
707707
- ipv4
708708
- ipv6
709709
type: string
710+
rewriteClientIP:
711+
description: RewriteClientIP defines configuration for rewriting the
712+
client IP to the original client's IP.
713+
properties:
714+
mode:
715+
description: |-
716+
Mode defines how NGINX will rewrite the client's IP address.
717+
Possible modes: ProxyProtocol, XForwardedFor.
718+
enum:
719+
- ProxyProtocol
720+
- XForwardedFor
721+
type: string
722+
setIPRecursively:
723+
description: |-
724+
SetIPRecursively configures whether recursive search is used for selecting client's
725+
address from the X-Forwarded-For header and used in conjunction with TrustedAddresses.
726+
If enabled, NGINX will recurse on the values in X-Forwarded-Header from the end of
727+
array to start of array and select the first untrusted IP.
728+
type: boolean
729+
trustedAddresses:
730+
description: |-
731+
TrustedAddresses specifies the addresses that are trusted to send correct client IP information.
732+
If a request comes from a trusted address, NGINX will rewrite the client IP information,
733+
and forward it to the backend in the X-Forwarded-For* and X-Real-IP headers.
734+
This field is required if mode is set.
735+
items:
736+
type: string
737+
maxItems: 16
738+
type: array
739+
type: object
740+
x-kubernetes-validations:
741+
- message: if mode is set, trustedAddresses is a required field
742+
rule: '!(has(self.mode) && !has(self.trustedAddresses))'
710743
telemetry:
711744
description: Telemetry specifies the OpenTelemetry configuration.
712745
properties:

internal/mode/static/nginx/config/http/config.go

+11-2
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,15 @@ type ProxySSLVerify struct {
115115

116116
// ServerConfig holds configuration for an HTTP server and IP family to be used by NGINX.
117117
type ServerConfig struct {
118-
Servers []Server
119-
IPFamily IPFamily
118+
Servers []Server
119+
RewriteClientIP RewriteClientIPSettings
120+
IPFamily IPFamily
121+
}
122+
123+
// RewriteClientIP holds the configuration for the rewrite client IP settings.
124+
type RewriteClientIPSettings struct {
125+
RealIPHeader string
126+
RealIPFrom []string
127+
Recursive bool
128+
ProxyProtocol bool
120129
}

internal/mode/static/nginx/config/servers.go

+18-2
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,9 @@ func executeServers(conf dataplane.Configuration) []executeResult {
6161
servers, httpMatchPairs := createServers(conf.HTTPServers, conf.SSLServers)
6262

6363
serverConfig := http.ServerConfig{
64-
Servers: servers,
65-
IPFamily: getIPFamily(conf.BaseHTTPConfig),
64+
Servers: servers,
65+
IPFamily: getIPFamily(conf.BaseHTTPConfig),
66+
RewriteClientIP: getRewriteClientIPSettings(conf.BaseHTTPConfig.RewriteClientIPSettings),
6667
}
6768

6869
serverResult := executeResult{
@@ -103,6 +104,21 @@ func getIPFamily(baseHTTPConfig dataplane.BaseHTTPConfig) http.IPFamily {
103104
return http.IPFamily{IPv4: true, IPv6: true}
104105
}
105106

107+
// getRewriteClientIPSettings returns the configuration for the rewriting client IP settings.
108+
func getRewriteClientIPSettings(rewriteIP dataplane.RewriteClientIPSettings) http.RewriteClientIPSettings {
109+
var proxyProtocol bool
110+
if rewriteIP.Mode == dataplane.RewriteIPModeProxyProtocol {
111+
proxyProtocol = true
112+
}
113+
114+
return http.RewriteClientIPSettings{
115+
Recursive: rewriteIP.IPRecursive,
116+
ProxyProtocol: proxyProtocol,
117+
RealIPFrom: rewriteIP.TrustedCIDRs,
118+
RealIPHeader: string(rewriteIP.Mode),
119+
}
120+
}
121+
106122
func createAdditionFileResults(conf dataplane.Configuration) []executeResult {
107123
uniqueAdditions := make(map[string][]byte)
108124

internal/mode/static/nginx/config/servers_template.go

+38-10
Original file line numberDiff line numberDiff line change
@@ -2,38 +2,57 @@ package config
22

33
const serversTemplateText = `
44
js_preload_object matches from /etc/nginx/conf.d/matches.json;
5+
{{ $proxyProtocol := "" }}
6+
{{ if $.RewriteClientIP.ProxyProtocol }}{{ $proxyProtocol = " proxy_protocol" }}{{ end }}
7+
58
{{- range $s := .Servers -}}
69
{{ if $s.IsDefaultSSL -}}
710
server {
811
{{- if $.IPFamily.IPv4 }}
9-
listen {{ $s.Port }} ssl default_server;
12+
listen {{ $s.Port }} ssl default_server{{ $proxyProtocol }};
1013
{{- end }}
1114
{{- if $.IPFamily.IPv6 }}
12-
listen [::]:{{ $s.Port }} ssl default_server;
15+
listen [::]:{{ $s.Port }} ssl default_server{{ $proxyProtocol }};
1316
{{- end }}
14-
1517
ssl_reject_handshake on;
18+
{{- range $cidr := $.RewriteClientIP.RealIPFrom }}
19+
set_real_ip_from {{ $cidr }};
20+
{{- end}}
21+
{{- if $.RewriteClientIP.RealIPHeader}}
22+
real_ip_header {{ $.RewriteClientIP.RealIPHeader }};
23+
{{- end}}
24+
{{- if $.RewriteClientIP.Recursive }}
25+
real_ip_recursive on;
26+
{{ end }}
1627
}
1728
{{- else if $s.IsDefaultHTTP }}
1829
server {
1930
{{- if $.IPFamily.IPv4 }}
20-
listen {{ $s.Port }} default_server;
31+
listen {{ $s.Port }} default_server{{ $proxyProtocol }};
2132
{{- end }}
2233
{{- if $.IPFamily.IPv6 }}
23-
listen [::]:{{ $s.Port }} default_server;
34+
listen [::]:{{ $s.Port }} default_server{{ $proxyProtocol }};
2435
{{- end }}
25-
36+
{{- range $cidr := $.RewriteClientIP.RealIPFrom }}
37+
set_real_ip_from {{ $cidr }};
38+
{{- end}}
39+
{{- if $.RewriteClientIP.RealIPHeader}}
40+
real_ip_header {{ $.RewriteClientIP.RealIPHeader }};
41+
{{- end}}
42+
{{- if $.RewriteClientIP.Recursive }}
43+
real_ip_recursive on;
44+
{{ end }}
2645
default_type text/html;
2746
return 404;
2847
}
2948
{{- else }}
3049
server {
3150
{{- if $s.SSL }}
3251
{{- if $.IPFamily.IPv4 }}
33-
listen {{ $s.Port }} ssl;
52+
listen {{ $s.Port }} ssl{{ $proxyProtocol }};
3453
{{- end }}
3554
{{- if $.IPFamily.IPv6 }}
36-
listen [::]:{{ $s.Port }} ssl;
55+
listen [::]:{{ $s.Port }} ssl{{ $proxyProtocol }};
3756
{{- end }}
3857
ssl_certificate {{ $s.SSL.Certificate }};
3958
ssl_certificate_key {{ $s.SSL.CertificateKey }};
@@ -43,10 +62,10 @@ server {
4362
}
4463
{{- else }}
4564
{{- if $.IPFamily.IPv4 }}
46-
listen {{ $s.Port }};
65+
listen {{ $s.Port }}{{ $proxyProtocol }};
4766
{{- end }}
4867
{{- if $.IPFamily.IPv6 }}
49-
listen [::]:{{ $s.Port }};
68+
listen [::]:{{ $s.Port }}{{ $proxyProtocol }};
5069
{{- end }}
5170
{{- end }}
5271
@@ -55,6 +74,15 @@ server {
5574
{{- range $i := $s.Includes }}
5675
include {{ $i }};
5776
{{ end -}}
77+
{{- range $cidr := $.RewriteClientIP.RealIPFrom }}
78+
set_real_ip_from {{ $cidr }};
79+
{{- end}}
80+
{{- if $.RewriteClientIP.RealIPHeader}}
81+
real_ip_header {{ $.RewriteClientIP.RealIPHeader }};
82+
{{- end}}
83+
{{- if $.RewriteClientIP.Recursive }}
84+
real_ip_recursive on;
85+
{{ end }}
5886
5987
{{ range $l := $s.Locations }}
6088
location {{ $l.Path }} {

internal/mode/static/nginx/config/servers_test.go

+35-1
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ func TestExecuteServers(t *testing.T) {
137137
}
138138
}
139139

140-
func TestExecuteServersForIPFamily(t *testing.T) {
140+
func TestExecuteServerConfig(t *testing.T) {
141141
httpServers := []dataplane.VirtualServer{
142142
{
143143
IsDefault: true,
@@ -228,6 +228,40 @@ func TestExecuteServersForIPFamily(t *testing.T) {
228228
"listen [::]:8080;": 1,
229229
"listen [::]:8443 ssl default_server;": 1,
230230
"listen [::]:8443 ssl;": 1,
231+
"real_ip_header proxy-protocol;": 0,
232+
"real_ip_recursive on;": 0,
233+
},
234+
},
235+
{
236+
msg: "http and ssl servers with rewrite client IP settings",
237+
config: dataplane.Configuration{
238+
HTTPServers: httpServers,
239+
SSLServers: sslServers,
240+
BaseHTTPConfig: dataplane.BaseHTTPConfig{
241+
IPFamily: dataplane.Dual,
242+
RewriteClientIPSettings: dataplane.RewriteClientIPSettings{
243+
Mode: dataplane.RewriteIPModeProxyProtocol,
244+
TrustedCIDRs: []string{"0.0.0.0/0"},
245+
IPRecursive: true,
246+
},
247+
},
248+
},
249+
expectedHTTPConfig: map[string]int{
250+
"set_real_ip_from 0.0.0.0/0;": 4,
251+
"real_ip_header proxy-protocol;": 4,
252+
"real_ip_recursive on;": 4,
253+
"listen 8080 default_server proxy_protocol;": 1,
254+
"listen 8080 proxy_protocol;": 1,
255+
"listen 8443 ssl default_server proxy_protocol;": 1,
256+
"listen 8443 ssl proxy_protocol;": 1,
257+
"server_name example.com;": 2,
258+
"ssl_certificate /etc/nginx/secrets/test-keypair.pem;": 1,
259+
"ssl_certificate_key /etc/nginx/secrets/test-keypair.pem;": 1,
260+
"ssl_reject_handshake on;": 1,
261+
"listen [::]:8080 default_server proxy_protocol;": 1,
262+
"listen [::]:8080 proxy_protocol;": 1,
263+
"listen [::]:8443 ssl default_server proxy_protocol;": 1,
264+
"listen [::]:8443 ssl proxy_protocol;": 1,
231265
},
232266
},
233267
}

0 commit comments

Comments
 (0)