Skip to content

Adds configuration to retain client IP information #2284

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 20 commits into from
Sep 6, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
update stream servers config
  • Loading branch information
salonichf5 committed Sep 6, 2024
commit 575edb64d062dca85048afecf02f2ed7e4b9865e
10 changes: 1 addition & 9 deletions internal/mode/static/nginx/config/http/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ type ProxySSLVerify struct {
// ServerConfig holds configuration for an HTTP server and IP family to be used by NGINX.
type ServerConfig struct {
Servers []Server
RewriteClientIP RewriteClientIPSettings
RewriteClientIP shared.RewriteClientIPSettings
IPFamily shared.IPFamily
Plus bool
}
Expand All @@ -120,11 +120,3 @@ type Include struct {
Name string
Content []byte
}

// RewriteClientIP holds the configuration for the rewrite client IP settings.
type RewriteClientIPSettings struct {
RealIPHeader string
RealIPFrom []string
Recursive bool
ProxyProtocol bool
}
4 changes: 2 additions & 2 deletions internal/mode/static/nginx/config/servers.go
Original file line number Diff line number Diff line change
Expand Up @@ -909,8 +909,8 @@ func isNonSlashedPrefixPath(pathType dataplane.PathType, path string) bool {
}

// getRewriteClientIPSettings returns the configuration for the rewriting client IP settings.
func getRewriteClientIPSettings(rewriteIP dataplane.RewriteClientIPSettings) http.RewriteClientIPSettings {
return http.RewriteClientIPSettings{
func getRewriteClientIPSettings(rewriteIP dataplane.RewriteClientIPSettings) shared.RewriteClientIPSettings {
return shared.RewriteClientIPSettings{
Recursive: rewriteIP.IPRecursive,
ProxyProtocol: rewriteIP.Mode == dataplane.RewriteIPModeProxyProtocol,
RealIPFrom: rewriteIP.TrustedCIDRs,
Expand Down
34 changes: 23 additions & 11 deletions internal/mode/static/nginx/config/servers_template.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,17 @@ server {
listen [::]:{{ $s.Listen }} ssl default_server{{ $proxyProtocol }};
{{- end }}
ssl_reject_handshake on;
{{- range $cidr := $.RewriteClientIP.RealIPFrom }}
{{- if and ($.RewriteClientIP.ProxyProtocol) ($s.IsSocket)}}
set_real_ip_from unix:;
{{- else if (not $s.IsSocket)}}
{{- range $cidr := $.RewriteClientIP.RealIPFrom }}
set_real_ip_from {{ $cidr }};
{{- end}}
{{- if $.RewriteClientIP.RealIPHeader}}
{{- end}}
{{ end }}
{{- if and ($.RewriteClientIP.RealIPHeader) (not $s.IsSocket)}}
real_ip_header {{ $.RewriteClientIP.RealIPHeader }};
{{- end}}
{{- if $.RewriteClientIP.Recursive }}
{{- if and ($.RewriteClientIP.Recursive) (not $s.IsSocket)}}
real_ip_recursive on;
{{ end }}
}
Expand All @@ -33,13 +37,17 @@ server {
{{- if $.IPFamily.IPv6 }}
listen [::]:{{ $s.Listen }} default_server{{ $proxyProtocol }};
{{- end }}
{{- range $cidr := $.RewriteClientIP.RealIPFrom }}
{{- if and ($.RewriteClientIP.ProxyProtocol) ($s.IsSocket)}}
set_real_ip_from unix:;
{{- else if (not $s.IsSocket)}}
{{- range $cidr := $.RewriteClientIP.RealIPFrom }}
set_real_ip_from {{ $cidr }};
{{- end}}
{{- if $.RewriteClientIP.RealIPHeader}}
{{- end}}
{{ end }}
{{- if and ($.RewriteClientIP.RealIPHeader) (not $s.IsSocket)}}
real_ip_header {{ $.RewriteClientIP.RealIPHeader }};
{{- end}}
{{- if $.RewriteClientIP.Recursive }}
{{- if and ($.RewriteClientIP.Recursive) (not $s.IsSocket)}}
real_ip_recursive on;
{{ end }}
default_type text/html;
Expand Down Expand Up @@ -79,13 +87,17 @@ server {
include {{ $i.Name }};
{{- end }}

{{- range $cidr := $.RewriteClientIP.RealIPFrom }}
{{- if and ($.RewriteClientIP.ProxyProtocol) ($s.IsSocket)}}
set_real_ip_from unix:;
{{- else if (not $s.IsSocket)}}
{{- range $cidr := $.RewriteClientIP.RealIPFrom }}
set_real_ip_from {{ $cidr }};
{{- end}}
{{- if $.RewriteClientIP.RealIPHeader}}
{{ end }}
{{- if and ($.RewriteClientIP.RealIPHeader) (not $s.IsSocket)}}
real_ip_header {{ $.RewriteClientIP.RealIPHeader }};
{{- end}}
{{- if $.RewriteClientIP.Recursive }}
{{- if and ($.RewriteClientIP.Recursive) (not $s.IsSocket)}}
real_ip_recursive on;
{{ end }}

Expand Down
85 changes: 61 additions & 24 deletions internal/mode/static/nginx/config/servers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -284,37 +284,40 @@ func TestExecuteServers_IPFamily(t *testing.T) {
}

func TestExecuteServers_RewriteClientIP(t *testing.T) {
httpServers := []dataplane.VirtualServer{
{
IsDefault: true,
Port: 8080,
},
{
Hostname: "example.com",
Port: 8080,
},
}

sslServers := []dataplane.VirtualServer{
{
IsDefault: true,
Port: 8443,
},
{
Hostname: "example.com",
SSL: &dataplane.SSL{
KeyPairID: "test-keypair",
},
Port: 8443,
},
}
tests := []struct {
msg string
expectedHTTPConfig map[string]int
config dataplane.Configuration
}{
{
msg: "http and ssl servers with rewrite client IP settings",
msg: "rewrite client IP settings configured with proxy protocol",
config: dataplane.Configuration{
HTTPServers: []dataplane.VirtualServer{
{
IsDefault: true,
Port: 8080,
},
{
Hostname: "example.com",
Port: 8080,
},
},
SSLServers: []dataplane.VirtualServer{
{
IsDefault: true,
Port: 8443,
},
{
Hostname: "example.com",
SSL: &dataplane.SSL{
KeyPairID: "test-keypair",
},
Port: 8443,
},
},
HTTPServers: httpServers,
SSLServers: sslServers,
BaseHTTPConfig: dataplane.BaseHTTPConfig{
IPFamily: dataplane.Dual,
RewriteClientIPSettings: dataplane.RewriteClientIPSettings{
Expand All @@ -328,6 +331,7 @@ func TestExecuteServers_RewriteClientIP(t *testing.T) {
"set_real_ip_from 0.0.0.0/0;": 4,
"real_ip_header proxy_protocol;": 4,
"real_ip_recursive on;": 4,
"proxy_protocol on;": 0,
"listen 8080 default_server proxy_protocol;": 1,
"listen 8080 proxy_protocol;": 1,
"listen 8443 ssl default_server proxy_protocol;": 1,
Expand All @@ -342,6 +346,39 @@ func TestExecuteServers_RewriteClientIP(t *testing.T) {
"listen [::]:8443 ssl proxy_protocol;": 1,
},
},
{
msg: "rewrite client IP settings configured with x-forwarded-for",
config: dataplane.Configuration{
HTTPServers: httpServers,
SSLServers: sslServers,
BaseHTTPConfig: dataplane.BaseHTTPConfig{
IPFamily: dataplane.Dual,
RewriteClientIPSettings: dataplane.RewriteClientIPSettings{
Mode: dataplane.RewriteIPModeXForwardedFor,
TrustedCIDRs: []string{"0.0.0.0/0"},
IPRecursive: true,
},
},
},
expectedHTTPConfig: map[string]int{
"set_real_ip_from 0.0.0.0/0;": 4,
"real_ip_header X-Forwarded-For;": 4,
"real_ip_recursive on;": 4,
"proxy_protocol on;": 0,
"listen 8080 default_server;": 1,
"listen 8080;": 1,
"listen 8443 ssl default_server;": 1,
"listen 8443 ssl;": 1,
"server_name example.com;": 2,
"ssl_certificate /etc/nginx/secrets/test-keypair.pem;": 1,
"ssl_certificate_key /etc/nginx/secrets/test-keypair.pem;": 1,
"ssl_reject_handshake on;": 1,
"listen [::]:8080 default_server;": 1,
"listen [::]:8080;": 1,
"listen [::]:8443 ssl default_server;": 1,
"listen [::]:8443 ssl;": 1,
},
},
}

for _, test := range tests {
Expand Down
8 changes: 8 additions & 0 deletions internal/mode/static/nginx/config/shared/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,11 @@ type IPFamily struct {
IPv4 bool
IPv6 bool
}

// RewriteClientIP holds the configuration for the rewrite client IP settings.
type RewriteClientIPSettings struct {
RealIPHeader string
RealIPFrom []string
Recursive bool
ProxyProtocol bool
}
7 changes: 4 additions & 3 deletions internal/mode/static/nginx/config/stream/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ type UpstreamServer struct {

// ServerConfig holds configuration for a stream server and IP family to be used by NGINX.
type ServerConfig struct {
Servers []Server
IPFamily shared.IPFamily
Plus bool
Servers []Server
RewriteClientIP shared.RewriteClientIPSettings
IPFamily shared.IPFamily
Plus bool
}
7 changes: 4 additions & 3 deletions internal/mode/static/nginx/config/stream_servers.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,10 @@ func (g GeneratorImpl) executeStreamServers(conf dataplane.Configuration) []exec
streamServers := createStreamServers(conf)

streamServerConfig := stream.ServerConfig{
Servers: streamServers,
IPFamily: getIPFamily(conf.BaseHTTPConfig),
Plus: g.plus,
Servers: streamServers,
IPFamily: getIPFamily(conf.BaseHTTPConfig),
Plus: g.plus,
RewriteClientIP: getRewriteClientIPSettings(conf.BaseHTTPConfig.RewriteClientIPSettings),
}

streamServerResult := executeResult{
Expand Down
11 changes: 10 additions & 1 deletion internal/mode/static/nginx/config/stream_servers_template.go
Original file line number Diff line number Diff line change
@@ -1,15 +1,24 @@
package config

const streamServersTemplateText = `
{{ $proxyProtocol := "" }}
{{ if $.RewriteClientIP.ProxyProtocol }}{{ $proxyProtocol = " proxy_protocol" }}{{ end }}
{{- range $s := .Servers }}
server {
{{- if or ($.IPFamily.IPv4) ($s.IsSocket) }}
{{- if and ($.IPFamily.IPv4) (not $s.IsSocket) }}
listen {{ $s.Listen }};
{{- else if and ($.IPFamily.IPv4) ( $s.IsSocket)}}
listen {{ $s.Listen }}{{ $proxyProtocol }};
{{- end }}
{{- if and ($.IPFamily.IPv6) (not $s.IsSocket) }}
listen [::]:{{ $s.Listen }};
{{- end }}

{{- if and ($s.IsSocket) ($.RewriteClientIP.ProxyProtocol) }}
{{- range $cidr := $.RewriteClientIP.RealIPFrom }}
set_real_ip_from {{ $cidr }};
{{- end}}
{{- end }}
{{- if $.Plus }}
status_zone {{ $s.StatusZone }};
{{- end }}
Expand Down
95 changes: 95 additions & 0 deletions internal/mode/static/nginx/config/stream_servers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,101 @@ func TestExecuteStreamServersForIPFamily(t *testing.T) {
}
}

func TestExecuteStreamServers_RewriteClientIP(t *testing.T) {
passThroughServers := []dataplane.Layer4VirtualServer{
{
UpstreamName: "backend1",
Hostname: "cafe.example.com",
Port: 8443,
},
}
streamUpstreams := []dataplane.Upstream{
{
Name: "backend1",
Endpoints: []resolver.Endpoint{
{
Address: "1.1.1.1",
},
},
},
}
tests := []struct {
msg string
expectedStreamConfig map[string]int
config dataplane.Configuration
}{
{
msg: "rewrite client IP not configured",
config: dataplane.Configuration{
TLSPassthroughServers: passThroughServers,
StreamUpstreams: streamUpstreams,
},
expectedStreamConfig: map[string]int{
"listen 8443;": 1,
"listen [::]:8443;": 1,
"listen unix:/var/run/nginx/cafe.example.com-8443.sock;": 1,
},
},
{
msg: "rewrite client IP configured with proxy protocol",
config: dataplane.Configuration{
BaseHTTPConfig: dataplane.BaseHTTPConfig{
RewriteClientIPSettings: dataplane.RewriteClientIPSettings{
Mode: dataplane.RewriteIPModeProxyProtocol,
TrustedCIDRs: []string{"1.1.1.1/32"},
IPRecursive: true,
},
},
TLSPassthroughServers: passThroughServers,
StreamUpstreams: streamUpstreams,
},
expectedStreamConfig: map[string]int{
"listen 8443;": 1,
"listen [::]:8443;": 1,
"listen unix:/var/run/nginx/cafe.example.com-8443.sock proxy_protocol;": 1,
"set_real_ip_from 1.1.1.1/32;": 1,
"real_ip_recursive on;": 0,
},
},
{
msg: "rewrite client IP configured with xforwardedfor",
config: dataplane.Configuration{
BaseHTTPConfig: dataplane.BaseHTTPConfig{
RewriteClientIPSettings: dataplane.RewriteClientIPSettings{
Mode: dataplane.RewriteIPModeXForwardedFor,
TrustedCIDRs: []string{"1.1.1.1/32"},
IPRecursive: true,
},
},
TLSPassthroughServers: passThroughServers,
StreamUpstreams: streamUpstreams,
},
expectedStreamConfig: map[string]int{
"listen 8443;": 1,
"listen [::]:8443;": 1,
"listen unix:/var/run/nginx/cafe.example.com-8443.sock;": 1,
"set_real_ip_from 1.1.1.1/32;": 0,
"real_ip_recursive on;": 0,
},
},
}

for _, test := range tests {
t.Run(test.msg, func(t *testing.T) {
g := NewWithT(t)

gen := GeneratorImpl{}
results := gen.executeStreamServers(test.config)
g.Expect(results).To(HaveLen(1))
serverConf := string(results[0].data)

for expSubStr, expCount := range test.expectedStreamConfig {
g.Expect(strings.Count(serverConf, expSubStr)).To(Equal(expCount))
}
})
}
}

func TestCreateStreamServersWithNone(t *testing.T) {
conf := dataplane.Configuration{
TLSPassthroughServers: nil,
Expand Down