Skip to content

Commit

Permalink
add support for preserving clientIP
Browse files Browse the repository at this point in the history
  • Loading branch information
salonichf5 committed Aug 13, 2024
1 parent 1476fe0 commit 2a818c9
Show file tree
Hide file tree
Showing 16 changed files with 721 additions and 85 deletions.
49 changes: 49 additions & 0 deletions apis/v1alpha1/nginxproxy_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,12 @@ type NginxProxySpec struct {
//
// +optional
Telemetry *Telemetry `json:"telemetry,omitempty"`
// RewriteClientIP defines configuration for rewriting the client IP to the original client's IP.
// +kubebuilder:validation:XValidation:message="if mode is set, trustedAddresses is a required field",rule="!(has(self.mode) && !has(self.trustedAddresses))"
//
// +optional
//nolint:lll
RewriteClientIP *RewriteClientIP `json:"rewriteClientIP,omitempty"`
// DisableHTTP2 defines if http2 should be disabled for all servers.
// Default is false, meaning http2 will be enabled for all servers.
//
Expand Down Expand Up @@ -114,3 +120,46 @@ type TelemetryExporter struct {
// +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})?$`
Endpoint string `json:"endpoint"`
}

// RewriteClientIP specifies the configuration for rewriting the client's IP address.
type RewriteClientIP struct {
// Mode defines how NGINX will rewrite the client's IP address.
// Possible modes: ProxyProtocol, XForwardedFor.
//
// +optional
Mode *RewriteClientIPModeType `json:"mode,omitempty"`

// SetIPRecursively configures whether recursive search is used for selecting client's
// address from the X-Forwarded-For header and used in conjunction with TrustedAddresses.
// If enabled, NGINX will recurse on the values in X-Forwarded-Header from the end of
// array to start of array and select the first untrusted IP.
//
// +optional
SetIPRecursively *bool `json:"setIPRecursively,omitempty"`

// TrustedAddresses specifies the addresses that are trusted to send correct client IP information.
// If a request comes from a trusted address, NGINX will rewrite the client IP information,
// and forward it to the backend in the X-Forwarded-For* and X-Real-IP headers.
// This field is required if mode is set.
// +kubebuilder:validation:MaxItems=16
//
// +optional
TrustedAddresses []string `json:"trustedAddresses,omitempty"`
}

// RewriteClientIPModeType defines how NGINX Gateway Fabric will determine the client's original IP address.
// +kubebuilder:validation:Enum=ProxyProtocol;XForwardedFor
type RewriteClientIPModeType string

const (
// RewriteClientIPModeProxyProtocol configures NGINX to accept PROXY protocol and,
// set the client's IP address to the IP address in the PROXY protocol header.
// Sets the proxy_protocol parameter to the listen directive on all servers, and sets real_ip_header
// to proxy_protocol: https://nginx.org/en/docs/http/ngx_http_realip_module.html#real_ip_header.
RewriteClientIPModeProxyProtocol RewriteClientIPModeType = "ProxyProtocol"

// RewriteClientIPModeXForwardedFor configures NGINX to set the client's IP address to the
// IP address in the X-Forwarded-For HTTP header.
// https://nginx.org/en/docs/http/ngx_http_realip_module.html#real_ip_header.
RewriteClientIPModeXForwardedFor RewriteClientIPModeType = "XForwardedFor"
)
35 changes: 35 additions & 0 deletions apis/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions charts/nginx-gateway-fabric/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,11 @@ nginx:
{}
# disableHTTP2: false
# ipFamily: dual
# enableProxyProtocol: true
# rewriteClientIP:
# mode: "ProxyProtocol"
# trustedAddresses: ["127.0.0.0/28", "::1/128"]
# setIPRecursively: true
# telemetry:
# exporter:
# endpoint: otel-collector.default.svc:4317
Expand Down
33 changes: 33 additions & 0 deletions config/crd/bases/gateway.nginx.org_nginxproxies.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,39 @@ spec:
- ipv4
- ipv6
type: string
rewriteClientIP:
description: RewriteClientIP defines configuration for rewriting the
client IP to the original client's IP.
properties:
mode:
description: |-
Mode defines how NGINX will rewrite the client's IP address.
Possible modes: ProxyProtocol, XForwardedFor.
enum:
- ProxyProtocol
- XForwardedFor
type: string
setIPRecursively:
description: |-
SetIPRecursively configures whether recursive search is used for selecting client's
address from the X-Forwarded-For header and used in conjunction with TrustedAddresses.
If enabled, NGINX will recurse on the values in X-Forwarded-Header from the end of
array to start of array and select the first untrusted IP.
type: boolean
trustedAddresses:
description: |-
TrustedAddresses specifies the addresses that are trusted to send correct client IP information.
If a request comes from a trusted address, NGINX will rewrite the client IP information,
and forward it to the backend in the X-Forwarded-For* and X-Real-IP headers.
This field is required if mode is set.
items:
type: string
maxItems: 16
type: array
type: object
x-kubernetes-validations:
- message: if mode is set, trustedAddresses is a required field
rule: '!(has(self.mode) && !has(self.trustedAddresses))'
telemetry:
description: Telemetry specifies the OpenTelemetry configuration.
properties:
Expand Down
33 changes: 33 additions & 0 deletions deploy/crds.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -707,6 +707,39 @@ spec:
- ipv4
- ipv6
type: string
rewriteClientIP:
description: RewriteClientIP defines configuration for rewriting the
client IP to the original client's IP.
properties:
mode:
description: |-
Mode defines how NGINX will rewrite the client's IP address.
Possible modes: ProxyProtocol, XForwardedFor.
enum:
- ProxyProtocol
- XForwardedFor
type: string
setIPRecursively:
description: |-
SetIPRecursively configures whether recursive search is used for selecting client's
address from the X-Forwarded-For header and used in conjunction with TrustedAddresses.
If enabled, NGINX will recurse on the values in X-Forwarded-Header from the end of
array to start of array and select the first untrusted IP.
type: boolean
trustedAddresses:
description: |-
TrustedAddresses specifies the addresses that are trusted to send correct client IP information.
If a request comes from a trusted address, NGINX will rewrite the client IP information,
and forward it to the backend in the X-Forwarded-For* and X-Real-IP headers.
This field is required if mode is set.
items:
type: string
maxItems: 16
type: array
type: object
x-kubernetes-validations:
- message: if mode is set, trustedAddresses is a required field
rule: '!(has(self.mode) && !has(self.trustedAddresses))'
telemetry:
description: Telemetry specifies the OpenTelemetry configuration.
properties:
Expand Down
13 changes: 11 additions & 2 deletions internal/mode/static/nginx/config/http/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,15 @@ type ProxySSLVerify struct {

// ServerConfig holds configuration for an HTTP server and IP family to be used by NGINX.
type ServerConfig struct {
Servers []Server
IPFamily IPFamily
Servers []Server
RewriteClientIP RewriteClientIPSettings
IPFamily IPFamily
}

// RewriteClientIP holds the configuration for the rewrite client IP settings.
type RewriteClientIPSettings struct {
RealIPHeader string
RealIPFrom []string
Recursive bool
ProxyProtocol bool
}
20 changes: 18 additions & 2 deletions internal/mode/static/nginx/config/servers.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,9 @@ func executeServers(conf dataplane.Configuration) []executeResult {
servers, httpMatchPairs := createServers(conf.HTTPServers, conf.SSLServers)

serverConfig := http.ServerConfig{
Servers: servers,
IPFamily: getIPFamily(conf.BaseHTTPConfig),
Servers: servers,
IPFamily: getIPFamily(conf.BaseHTTPConfig),
RewriteClientIP: getRewriteClientIPSettings(conf.BaseHTTPConfig.RewriteClientIPSettings),
}

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

// getRewriteClientIPSettings returns the configuration for the rewriting client IP settings.
func getRewriteClientIPSettings(rewriteIP dataplane.RewriteClientIPSettings) http.RewriteClientIPSettings {
var proxyProtocol bool
if rewriteIP.Mode == dataplane.RewriteIPModeProxyProtocol {
proxyProtocol = true
}

return http.RewriteClientIPSettings{
Recursive: rewriteIP.IPRecursive,
ProxyProtocol: proxyProtocol,
RealIPFrom: rewriteIP.TrustedCIDRs,
RealIPHeader: string(rewriteIP.Mode),
}
}

func createAdditionFileResults(conf dataplane.Configuration) []executeResult {
uniqueAdditions := make(map[string][]byte)

Expand Down
48 changes: 38 additions & 10 deletions internal/mode/static/nginx/config/servers_template.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,38 +2,57 @@ package config

const serversTemplateText = `
js_preload_object matches from /etc/nginx/conf.d/matches.json;
{{ $proxyProtocol := "" }}
{{ if $.RewriteClientIP.ProxyProtocol }}{{ $proxyProtocol = " proxy_protocol" }}{{ end }}
{{- range $s := .Servers -}}
{{ if $s.IsDefaultSSL -}}
server {
{{- if $.IPFamily.IPv4 }}
listen {{ $s.Port }} ssl default_server;
listen {{ $s.Port }} ssl default_server{{ $proxyProtocol }};
{{- end }}
{{- if $.IPFamily.IPv6 }}
listen [::]:{{ $s.Port }} ssl default_server;
listen [::]:{{ $s.Port }} ssl default_server{{ $proxyProtocol }};
{{- end }}
ssl_reject_handshake on;
{{- range $cidr := $.RewriteClientIP.RealIPFrom }}
set_real_ip_from {{ $cidr }};
{{- end}}
{{- if $.RewriteClientIP.RealIPHeader}}
real_ip_header {{ $.RewriteClientIP.RealIPHeader }};
{{- end}}
{{- if $.RewriteClientIP.Recursive }}
real_ip_recursive on;
{{ end }}
}
{{- else if $s.IsDefaultHTTP }}
server {
{{- if $.IPFamily.IPv4 }}
listen {{ $s.Port }} default_server;
listen {{ $s.Port }} default_server{{ $proxyProtocol }};
{{- end }}
{{- if $.IPFamily.IPv6 }}
listen [::]:{{ $s.Port }} default_server;
listen [::]:{{ $s.Port }} default_server{{ $proxyProtocol }};
{{- end }}
{{- range $cidr := $.RewriteClientIP.RealIPFrom }}
set_real_ip_from {{ $cidr }};
{{- end}}
{{- if $.RewriteClientIP.RealIPHeader}}
real_ip_header {{ $.RewriteClientIP.RealIPHeader }};
{{- end}}
{{- if $.RewriteClientIP.Recursive }}
real_ip_recursive on;
{{ end }}
default_type text/html;
return 404;
}
{{- else }}
server {
{{- if $s.SSL }}
{{- if $.IPFamily.IPv4 }}
listen {{ $s.Port }} ssl;
listen {{ $s.Port }} ssl{{ $proxyProtocol }};
{{- end }}
{{- if $.IPFamily.IPv6 }}
listen [::]:{{ $s.Port }} ssl;
listen [::]:{{ $s.Port }} ssl{{ $proxyProtocol }};
{{- end }}
ssl_certificate {{ $s.SSL.Certificate }};
ssl_certificate_key {{ $s.SSL.CertificateKey }};
Expand All @@ -43,10 +62,10 @@ server {
}
{{- else }}
{{- if $.IPFamily.IPv4 }}
listen {{ $s.Port }};
listen {{ $s.Port }}{{ $proxyProtocol }};
{{- end }}
{{- if $.IPFamily.IPv6 }}
listen [::]:{{ $s.Port }};
listen [::]:{{ $s.Port }}{{ $proxyProtocol }};
{{- end }}
{{- end }}
Expand All @@ -55,6 +74,15 @@ server {
{{- range $i := $s.Includes }}
include {{ $i }};
{{ end -}}
{{- range $cidr := $.RewriteClientIP.RealIPFrom }}
set_real_ip_from {{ $cidr }};
{{- end}}
{{- if $.RewriteClientIP.RealIPHeader}}
real_ip_header {{ $.RewriteClientIP.RealIPHeader }};
{{- end}}
{{- if $.RewriteClientIP.Recursive }}
real_ip_recursive on;
{{ end }}
{{ range $l := $s.Locations }}
location {{ $l.Path }} {
Expand Down
Loading

0 comments on commit 2a818c9

Please sign in to comment.