Merge cee878be25
into c25b80ca00
This commit is contained in:
commit
a53d0619db
6 changed files with 568 additions and 0 deletions
|
@ -132,6 +132,11 @@ The following table shows a configuration option's name, type, and the default v
|
|||
|[enable-real-ip](#enable-real-ip)|bool|"false"||
|
||||
|[forwarded-for-header](#forwarded-for-header)|string|"X-Forwarded-For"||
|
||||
|[compute-full-forwarded-for](#compute-full-forwarded-for)|bool|"false"||
|
||||
|[enable-forwarded-rfc7239](#enable-forwarded-rfc7239)|bool|"false"||
|
||||
|[forwarded-rfc7239-strip-incomming](#forwarded-rfc7239-strip-incomming)|bool|"false"||
|
||||
|[forwarded-rfc7239](#forwarded-rfc7239)|[]string|"for"||
|
||||
|[forwarded-rfc7239-for](#forwarded-rfc7239-for)|string|"ip"||
|
||||
|[forwarded-rfc7239-by](#forwarded-rfc7239-by)|string|"ip"||
|
||||
|[proxy-add-original-uri-header](#proxy-add-original-uri-header)|bool|"false"||
|
||||
|[generate-request-id](#generate-request-id)|bool|"true"||
|
||||
|[enable-opentracing](#enable-opentracing)|bool|"false"||
|
||||
|
@ -934,6 +939,26 @@ Sets the header field for identifying the originating IP address of a client. _*
|
|||
|
||||
Append the remote address to the X-Forwarded-For header instead of replacing it. When this option is enabled, the upstream application is responsible for extracting the client IP based on its own list of trusted proxies.
|
||||
|
||||
## enable-forwarded-rfc7239
|
||||
|
||||
Enable standard Forwarded header defined in RFC 7239, or the Forwared header will not be sent to upstream and the incoming Forwarded header will be discarded. The parameters can be configured using [forwarded-rfc7239](#forwarded-rfc7239). Transition between Forwarded header and X-Forwarded headers will not happen. _**default:**_ false
|
||||
|
||||
## forwarded-rfc7239-strip-incomming
|
||||
|
||||
Whether or not retain Forwarded header from downstream. _**default:**_ false
|
||||
|
||||
## forwarded-rfc7239
|
||||
|
||||
Sets enabled parameters and their order. Supported parameters are "for", "by", "host" and, "proto". _**default:**_ for
|
||||
|
||||
## forwarded-rfc7239-for
|
||||
|
||||
Sets value of "for" parameter. It can be "ip" or static obfuscated string. If "ip" is given, the remote client address will be used. The static obfuscated string is selected by user and must start with a underscore "_" and consist of only digits, letters and characters ".", "_", and "-". _**default:**_ ip
|
||||
|
||||
## forwarded-rfc7239-by
|
||||
|
||||
Sets value of "by" parameter. It can be "ip" or static obfuscated string. If "ip" is given, the server's host-port pair that remote client connects to will be used. The static obfuscated string is selected by user and must start with a underscore "_" and consist of only digits, letters and characters ".", "_", and "-". _**default:**_ ip
|
||||
|
||||
## proxy-add-original-uri-header
|
||||
|
||||
Adds an X-Original-Uri header with the original request URI to the backend request
|
||||
|
|
|
@ -568,6 +568,26 @@ type Configuration struct {
|
|||
// Default: false
|
||||
ComputeFullForwardedFor bool `json:"compute-full-forwarded-for,omitempty"`
|
||||
|
||||
// Enable standard forwarded header.
|
||||
// Default: false
|
||||
EnableForwardedRFC7239 bool `json:"enable-forwarded-rfc7239"`
|
||||
|
||||
// Sets if strips incoming Forwarded header.
|
||||
// Default: false
|
||||
ForwardedRFC7239StripIncomming bool `json:"forwarded-rfc7239-strip-incomming"`
|
||||
|
||||
// Sets Forwarded parameters and their order. Available options are "for", "by", "host", "proto".
|
||||
// Default: "for"
|
||||
ForwardedRFC7239 []string `json:"forwarded-rfc7239"`
|
||||
|
||||
// Sets Forwarded "for" parameter node identifier, should be "ip" or a static obfuscated string.
|
||||
// Default: "ip"
|
||||
ForwardedRFC7239For string `json:"forwarded-rfc7239-for,omitempty"`
|
||||
|
||||
// Sets Forwarded "by" parameter node identifier, should be "ip" or a static obfuscated string.
|
||||
// Default: "ip"
|
||||
ForwardedRFC7239By string `json:"forwarded-rfc7239-by,omitempty"`
|
||||
|
||||
// If the request does not have a request-id, should we generate a random value?
|
||||
// Default: true
|
||||
GenerateRequestID bool `json:"generate-request-id,omitempty"`
|
||||
|
@ -796,6 +816,11 @@ func NewDefault() Configuration {
|
|||
EnableRealIP: false,
|
||||
ForwardedForHeader: "X-Forwarded-For",
|
||||
ComputeFullForwardedFor: false,
|
||||
EnableForwardedRFC7239: false,
|
||||
ForwardedRFC7239StripIncomming: false,
|
||||
ForwardedRFC7239: []string{"for"},
|
||||
ForwardedRFC7239For: "ip",
|
||||
ForwardedRFC7239By: "ip",
|
||||
ProxyAddOriginalURIHeader: false,
|
||||
GenerateRequestID: true,
|
||||
HTTP2MaxFieldSize: "",
|
||||
|
|
|
@ -53,6 +53,9 @@ const (
|
|||
nginxStatusIpv4Whitelist = "nginx-status-ipv4-whitelist"
|
||||
nginxStatusIpv6Whitelist = "nginx-status-ipv6-whitelist"
|
||||
proxyHeaderTimeout = "proxy-protocol-header-timeout"
|
||||
forwardedRFC7239 = "forwarded-rfc7239"
|
||||
forwardedRFC7239For = "forwarded-rfc7239-for"
|
||||
forwardedRFC7239By = "forwarded-rfc7239-by"
|
||||
workerProcesses = "worker-processes"
|
||||
globalAuthURL = "global-auth-url"
|
||||
globalAuthMethod = "global-auth-method"
|
||||
|
@ -83,6 +86,7 @@ var (
|
|||
"global_throttle_cache": 10240,
|
||||
}
|
||||
defaultGlobalAuthRedirectParam = "rd"
|
||||
forwardedRFC7239ValidParams = [...]string{"for", "by", "host", "proto"}
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -354,6 +358,36 @@ func ReadConfig(src map[string]string) config.Configuration {
|
|||
}
|
||||
}
|
||||
|
||||
if val, ok := conf[forwardedRFC7239]; ok {
|
||||
delete(conf, forwardedRFC7239)
|
||||
params := []string{} // non-nil value
|
||||
for _, param := range splitAndTrimSpace(val, ",") {
|
||||
param = strings.ToLower(param)
|
||||
if sliceContains(forwardedRFC7239ValidParams[:], param) {
|
||||
if sliceContains(params, param) {
|
||||
klog.Warningf("%v ignores duplicate parameter: %v", forwardedRFC7239, param)
|
||||
continue
|
||||
}
|
||||
params = append(params, param)
|
||||
}
|
||||
}
|
||||
to.ForwardedRFC7239 = params
|
||||
}
|
||||
|
||||
if val, ok := conf[forwardedRFC7239For]; ok {
|
||||
if !isValidObfuscatedIdentifier(val) {
|
||||
delete(conf, forwardedRFC7239For)
|
||||
klog.Warningf("%v is not a valid obfuscated identifier", val)
|
||||
}
|
||||
}
|
||||
|
||||
if val, ok := conf[forwardedRFC7239By]; ok {
|
||||
if !isValidObfuscatedIdentifier(val) {
|
||||
delete(conf, forwardedRFC7239By)
|
||||
klog.Warningf("%v is not a valid obfuscated identifier", val)
|
||||
}
|
||||
}
|
||||
|
||||
streamResponses := 1
|
||||
if val, ok := conf[proxyStreamResponses]; ok {
|
||||
delete(conf, proxyStreamResponses)
|
||||
|
@ -499,3 +533,19 @@ func dictKbToStr(size int) string {
|
|||
}
|
||||
return fmt.Sprintf("%dK", size)
|
||||
}
|
||||
|
||||
func sliceContains(slice []string, s string) bool {
|
||||
for _, value := range slice {
|
||||
if value == s {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// isValidObfuscatedIdentifier tests if s is a valid obfuscated identifier
|
||||
// as section 6.3 of rfc7239 defines.
|
||||
func isValidObfuscatedIdentifier(s string) bool {
|
||||
re := regexp.MustCompile("^_[0-9a-zA-Z._-]*$")
|
||||
return re.MatchString(s)
|
||||
}
|
||||
|
|
|
@ -364,6 +364,38 @@ func TestGlobalExternalAuthCacheDurationParsing(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestForwardedRFC7239Parsing(t *testing.T) {
|
||||
testCases := map[string]struct {
|
||||
params string
|
||||
expect []string
|
||||
}{
|
||||
"nothing": {"", []string{}},
|
||||
"spaces": {" ", []string{}},
|
||||
"one param": {"for", []string{"for"}},
|
||||
"two params": {"by,for", []string{"by", "for"}},
|
||||
"case insensitive": {"for,HOST,By", []string{"for", "host", "by"}},
|
||||
"duplicate params": {"for,host,for,by", []string{"for", "host", "by"}},
|
||||
}
|
||||
|
||||
{
|
||||
name := "absent field"
|
||||
expect := []string{"for"} // default
|
||||
cfg := ReadConfig(map[string]string{})
|
||||
|
||||
if !reflect.DeepEqual(cfg.ForwardedRFC7239, expect) {
|
||||
t.Errorf("Testing %v. Expected \"%v\" but \"%v\" was returned", name, expect, cfg.ForwardedRFC7239)
|
||||
}
|
||||
}
|
||||
|
||||
for name, tc := range testCases {
|
||||
cfg := ReadConfig(map[string]string{"forwarded-rfc7239": tc.params})
|
||||
|
||||
if !reflect.DeepEqual(cfg.ForwardedRFC7239, tc.expect) {
|
||||
t.Errorf("Testing %v. Expected \"%v\" but \"%v\" was returned", name, tc.expect, cfg.ForwardedRFC7239)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestLuaSharedDictsParsing(t *testing.T) {
|
||||
testsCases := []struct {
|
||||
name string
|
||||
|
@ -530,3 +562,57 @@ func TestDictKbToStr(t *testing.T) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSliceContains(t *testing.T) {
|
||||
testCases := map[string]struct {
|
||||
slice []string
|
||||
find string
|
||||
expect bool
|
||||
}{
|
||||
"containing": {
|
||||
slice: []string{"ip", "_obf"},
|
||||
find: "ip",
|
||||
expect: true,
|
||||
},
|
||||
"not containing": {
|
||||
slice: []string{"ip", "_obf"},
|
||||
find: "obfuscated",
|
||||
expect: false,
|
||||
},
|
||||
}
|
||||
|
||||
for name, tc := range testCases {
|
||||
if b := sliceContains(tc.slice, tc.find); b != tc.expect {
|
||||
t.Errorf("Testing %v. Expected \"%v\" but \"%v\" was returned", name, tc.expect, b)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsValidObfuscatedIdentifier(t *testing.T) {
|
||||
testCases := map[string]struct {
|
||||
input string
|
||||
expect bool
|
||||
}{
|
||||
"rfc example _hidden": {
|
||||
input: "_hidden",
|
||||
expect: true,
|
||||
},
|
||||
"rfc example _SEVKISEK": {
|
||||
input: "_SEVKISEK",
|
||||
expect: true,
|
||||
},
|
||||
"non-leading underscore": {
|
||||
input: "hidden",
|
||||
expect: false,
|
||||
},
|
||||
"containing invalid characters": {
|
||||
input: "_hidden:",
|
||||
expect: false,
|
||||
},
|
||||
}
|
||||
for name, tc := range testCases {
|
||||
if isValid := isValidObfuscatedIdentifier(tc.input); isValid != tc.expect {
|
||||
t.Errorf("Testing %v. Expected \"%v\" but \"%v\" was returned", name, tc.input, isValid)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -465,6 +465,137 @@ http {
|
|||
|
||||
{{ end }}
|
||||
|
||||
{{ if $cfg.EnableForwardedRFC7239 }}
|
||||
# Enabled standard forwarded (RFC 7239).
|
||||
|
||||
# Used to declare variables as constants.
|
||||
map $nginx_version $nginx_constant {
|
||||
default '';
|
||||
}
|
||||
|
||||
{{ if eq "ip" $cfg.ForwardedRFC7239For }}
|
||||
# Set proxy_forwarded_rfc2379_for as IP of remote end.
|
||||
# We use $remote_addr instead of $realip_remote_addr because we don't
|
||||
# translate "x-" header to standard forwarded header.
|
||||
map $remote_addr $proxy_forwarded_rfc2379_for {
|
||||
# IPv4 addresses can be sent as-is
|
||||
~^[0-9.]+$ "for=$remote_addr";
|
||||
|
||||
# IPv6 addresses need to be bracketed and quoted
|
||||
~^[0-9A-Fa-f:.]+$ "for=\"[$remote_addr]\"";
|
||||
|
||||
# Unix domain socket names cannot be represented in RFC 7239 syntax
|
||||
default "for=unknown";
|
||||
}
|
||||
{{ else }}
|
||||
# Set proxy_forwarded_rfc2379_for as a obfuscated string.
|
||||
map $nginx_constant $proxy_forwarded_rfc2379_for {
|
||||
default "for={{ $cfg.ForwardedRFC7239For }}";
|
||||
}
|
||||
{{ end }}
|
||||
|
||||
{{ if eq "ip" $cfg.ForwardedRFC7239By }}
|
||||
# Set proxy_forwarded_rfc2379_by as "IP:port" that the remote end connects to.
|
||||
map $server_addr $proxy_forwarded_rfc2379_by {
|
||||
# IPv4:port need to be quoted since ":" is not a valid
|
||||
# header field value character.
|
||||
~^[0-9.]+$ "by=\"$server_addr:$server_port\"";
|
||||
|
||||
# IPv6 addresses need to be bracketed and quoted with port joined by ":"
|
||||
~^[0-9A-Fa-f:.]+$ "by=\"[$server_addr]:$server_port\"";
|
||||
|
||||
# Unix domain socket names cannot be represented in RFC 7239 syntax
|
||||
default "by=unknown";
|
||||
}
|
||||
{{ else }}
|
||||
# Set proxy_forwarded_rfc2379_by as a static obfuscated string.
|
||||
map $nginx_constant $proxy_forwarded_rfc2379_by {
|
||||
default "by={{ $cfg.ForwardedRFC7239By }}";
|
||||
}
|
||||
{{ end }}
|
||||
|
||||
# Set proxy_forwarded_rfc2379_host as the original HOST header and quote
|
||||
# it if needed. Section 3.2.2 of RFC 3986 defines host syntax, and it must
|
||||
# be one of ipv4, ipv6, and reg-name optionaly followed by a port after
|
||||
# a colon.
|
||||
map $http_host $proxy_forwarded_rfc2379_host {
|
||||
# IPv4 and reg-name formed of valid field value characters can be sent
|
||||
# as-is. The regular expression of IPv4 is subset of this one, and this
|
||||
# expression also matches IPv4.
|
||||
# Since characters "#", "^", "|", and "`" are invalid for reg-name,
|
||||
# these four characters are removed from character set.
|
||||
"~^[!$%&'*+._~0-9A-Za-z-]+$" "host=$http_host";
|
||||
|
||||
# reg-names are not matched last regular expression need to be quoted.
|
||||
"~^[!$&'()*+,;=._~%0-9A-Za-z-]$" "host=\"$http_host\"";
|
||||
|
||||
# IPv6 addresses (with port or not) need to quoted (it must be already
|
||||
# bracketed).
|
||||
"~^\[[0-9A-Fa-f:.]+\](:[0-9]+)*$" "host=\"$http_host\"";
|
||||
|
||||
# IPv4:port and reg-name:port and need to be quoted.
|
||||
"~^[!$&'()*+,;=._~%0-9A-Za-z-]+:[0-9]+$" "host=\"$http_host\"";
|
||||
|
||||
# Otherwise it is invalid.
|
||||
default "host=unknown";
|
||||
}
|
||||
|
||||
# Section 3.1 of RFC3986 defines scheme syntax.
|
||||
map $scheme $proxy_forwarded_rfc2379_proto {
|
||||
~^[a-zA-Z][0-9a-zA-Z.+-]+$ "proto=$scheme";
|
||||
default "proto=unknown";
|
||||
}
|
||||
|
||||
# Add incoming Forwarded header if valid and not strip.
|
||||
# Ref: https://www.nginx.com/resources/wiki/start/topics/examples/forwarded/#how-to-use-it-in-nginx
|
||||
map $http_forwarded $proxy_forwarded_rfc2379_incoming {
|
||||
{{ if not $cfg.ForwardedRFC7239StripIncomming }}
|
||||
# If the incoming Forwarded header is syntactically valid, use it.
|
||||
"~^(,[ \\t]*)*([!#$%&'*+.^_`|~0-9A-Za-z-]+=([!#$%&'*+.^_`|~0-9A-Za-z-]+|\"([\\t \\x21\\x23-\\x5B\\x5D-\\x7E\\x80-\\xFF]|\\\\[\\t \\x21-\\x7E\\x80-\\xFF])*\"))?(;([!#$%&'*+.^_`|~0-9A-Za-z-]+=([!#$%&'*+.^_`|~0-9A-Za-z-]+|\"([\\t \\x21\\x23-\\x5B\\x5D-\\x7E\\x80-\\xFF]|\\\\[\\t \\x21-\\x7E\\x80-\\xFF])*\"))?)*([ \\t]*,([ \\t]*([!#$%&'*+.^_`|~0-9A-Za-z-]+=([!#$%&'*+.^_`|~0-9A-Za-z-]+|\"([\\t \\x21\\x23-\\x5B\\x5D-\\x7E\\x80-\\xFF]|\\\\[\\t \\x21-\\x7E\\x80-\\xFF])*\"))?(;([!#$%&'*+.^_`|~0-9A-Za-z-]+=([!#$%&'*+.^_`|~0-9A-Za-z-]+|\"([\\t \\x21\\x23-\\x5B\\x5D-\\x7E\\x80-\\xFF]|\\\\[\\t \\x21-\\x7E\\x80-\\xFF])*\"))?)*)?)*$" "$http_forwarded";
|
||||
|
||||
# Otherwise, discard it.
|
||||
default '';
|
||||
{{ else }}
|
||||
# Strip incomming forwarded header.
|
||||
default '';
|
||||
{{ end }}
|
||||
}
|
||||
|
||||
# Add a comma separator when both the incoming part and the current node's part exist.
|
||||
map $proxy_forwarded_rfc2379_incoming $proxy_forwarded_rfc2379_sep {
|
||||
{{ if not $cfg.ForwardedRFC7239 }}
|
||||
# Pass through the incoming forwarded header.
|
||||
default '';
|
||||
{{ else }}
|
||||
# Incoming forwarded header is empty.
|
||||
'' '';
|
||||
# Both of two parts exist, so insert a comma.
|
||||
default ', ';
|
||||
{{ end }}
|
||||
}
|
||||
|
||||
# Append Forwarded field to the existing Forwarded header after a comma separator,
|
||||
# or generate a new one if the incoming Forwarded header is invalid or empty.
|
||||
map $proxy_forwarded_rfc2379_incoming $proxy_add_forwarded_rfc2379 {
|
||||
default "$proxy_forwarded_rfc2379_incoming$proxy_forwarded_rfc2379_sep
|
||||
{{- /* NOTE: We need to trip both leading and trailing spaces to ensure the format correct. */ -}}
|
||||
{{- range $index, $param := $cfg.ForwardedRFC7239 -}}
|
||||
{{- if gt $index 0 -}} ; {{- end -}}
|
||||
{{- if eq "for" $param -}}
|
||||
$proxy_forwarded_rfc2379_for
|
||||
{{- else if eq "by" $param -}}
|
||||
$proxy_forwarded_rfc2379_by
|
||||
{{- else if eq "host" $param -}}
|
||||
$proxy_forwarded_rfc2379_host
|
||||
{{- else if eq "proto" $param -}}
|
||||
$proxy_forwarded_rfc2379_proto
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
";
|
||||
}
|
||||
|
||||
{{ end }}{{- /* if $cfg.EnableForwardedRFC7239 */ -}}
|
||||
|
||||
# Create a variable that contains the literal $ character.
|
||||
# This works because the geo module will not resolve variables.
|
||||
geo $literal_dollar {
|
||||
|
@ -1162,6 +1293,10 @@ stream {
|
|||
proxy_set_header X-Forwarded-For $remote_addr;
|
||||
{{ end }}
|
||||
|
||||
{{ if $all.Cfg.EnableForwardedRFC7239 }}
|
||||
proxy_set_header Forwarded $proxy_add_forwarded_rfc2379;
|
||||
{{ end }}
|
||||
|
||||
{{ if $externalAuth.RequestRedirect }}
|
||||
proxy_set_header X-Auth-Request-Redirect {{ $externalAuth.RequestRedirect }};
|
||||
{{ else }}
|
||||
|
@ -1447,6 +1582,10 @@ stream {
|
|||
# Pass the original X-Forwarded-For
|
||||
{{ $proxySetHeader }} X-Original-Forwarded-For {{ buildForwardedFor $all.Cfg.ForwardedForHeader }};
|
||||
|
||||
{{ if $all.Cfg.EnableForwardedRFC7239 }}
|
||||
{{ $proxySetHeader }} Forwarded $proxy_add_forwarded_rfc2379;
|
||||
{{ end }}
|
||||
|
||||
# mitigate HTTPoxy Vulnerability
|
||||
# https://www.nginx.com/blog/mitigating-the-httpoxy-vulnerability-with-nginx/
|
||||
{{ $proxySetHeader }} Proxy "";
|
||||
|
|
243
test/e2e/settings/forwarded_rfc7239.go
Normal file
243
test/e2e/settings/forwarded_rfc7239.go
Normal file
|
@ -0,0 +1,243 @@
|
|||
/*
|
||||
Copyright 2023 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package settings
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/onsi/ginkgo/v2"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"k8s.io/ingress-nginx/test/e2e/framework"
|
||||
)
|
||||
|
||||
const (
|
||||
enableForwardedRFC7239 = "enable-forwarded-rfc7239"
|
||||
forwardedRFC7239StripIncomming = "forwarded-rfc7239-strip-incomming"
|
||||
forwardedRFC7239 = "forwarded-rfc7239"
|
||||
forwardedRFC7239By = "forwarded-rfc7239-by"
|
||||
forwardedRFC7239For = "forwarded-rfc7239-for"
|
||||
)
|
||||
|
||||
var _ = framework.DescribeSetting("Configure Forwarded RFC7239", func() {
|
||||
f := framework.NewDefaultFramework("enable-forwarded-rfc7239")
|
||||
|
||||
ginkgo.BeforeEach(func() {
|
||||
f.NewEchoDeployment()
|
||||
})
|
||||
|
||||
ginkgo.AfterEach(func() {
|
||||
})
|
||||
|
||||
ginkgo.It("should not send forwarded header when disabled", func() {
|
||||
host := "forwarded-rfc7239"
|
||||
|
||||
f.UpdateNginxConfigMapData(enableForwardedRFC7239, "false")
|
||||
|
||||
ing := framework.NewSingleIngress(host, "/", host, f.Namespace, framework.EchoService, 80, nil)
|
||||
f.EnsureIngress(ing)
|
||||
|
||||
f.WaitForNginxServer(host,
|
||||
func(server string) bool {
|
||||
return strings.Contains(server, "server_name forwarded-rfc7239") &&
|
||||
!strings.Contains(server, "proxy_set_header Forwarded $proxy_add_forwarded_rfc2379;")
|
||||
})
|
||||
|
||||
body := f.HTTPTestClient().
|
||||
GET("/").
|
||||
WithHeader("Host", host).
|
||||
Expect().
|
||||
Status(http.StatusOK).
|
||||
Body().
|
||||
Raw()
|
||||
|
||||
assert.NotContains(ginkgo.GinkgoT(), body, "forwarded=")
|
||||
})
|
||||
|
||||
ginkgo.It("should trust Forwarded header when striping-incoming is false", func() {
|
||||
host := "forwarded-rfc7239"
|
||||
|
||||
config := map[string]string{}
|
||||
config[enableForwardedRFC7239] = "true"
|
||||
config[forwardedRFC7239StripIncomming] = "false"
|
||||
config[forwardedRFC7239] = "for"
|
||||
config[forwardedRFC7239For] = "ip"
|
||||
f.SetNginxConfigMapData(config)
|
||||
|
||||
ing := framework.NewSingleIngress(host, "/", host, f.Namespace, framework.EchoService, 80, nil)
|
||||
f.EnsureIngress(ing)
|
||||
|
||||
f.WaitForNginxServer(host,
|
||||
func(server string) bool {
|
||||
return strings.Contains(server, "server_name forwarded-rfc7239") &&
|
||||
strings.Contains(server, "proxy_set_header Forwarded $proxy_add_forwarded_rfc2379;")
|
||||
})
|
||||
|
||||
serverIP := f.GetNginxPodIP()
|
||||
clientIP := getClientIP(serverIP)
|
||||
|
||||
ginkgo.By("ensuring valid headers are passed through correctly")
|
||||
body := f.HTTPTestClient().
|
||||
GET("/").
|
||||
WithHeader("Host", host).
|
||||
WithHeader("Forwarded", "for=1.1.1.1;secret=_5ecREy, for=\"[2001:4860:4860::8888]\"").
|
||||
Expect().
|
||||
Status(http.StatusOK).
|
||||
Body().
|
||||
Raw()
|
||||
|
||||
assert.Contains(ginkgo.GinkgoT(), body, fmt.Sprintf("forwarded=for=1.1.1.1;secret=_5ecREy, for=\"[2001:4860:4860::8888]\", for=%s", clientIP))
|
||||
|
||||
ginkgo.By("ensuring invalid headers are striped")
|
||||
body = f.HTTPTestClient().
|
||||
GET("/").
|
||||
WithHeader("Host", host).
|
||||
WithHeader("Forwarded", "for=2001:4860:4860::8888"). // invalid header, ipv6 should be bracked and quoted
|
||||
Expect().
|
||||
Status(http.StatusOK).
|
||||
Body().
|
||||
Raw()
|
||||
|
||||
assert.Contains(ginkgo.GinkgoT(), body, fmt.Sprintf("forwarded=for=%s", clientIP))
|
||||
})
|
||||
|
||||
ginkgo.It("should contain parameters in order as setting forwarded-rfc7239 specified", func() {
|
||||
host := "forwarded-rfc7239"
|
||||
|
||||
config := map[string]string{}
|
||||
config[enableForwardedRFC7239] = "true"
|
||||
config[forwardedRFC7239StripIncomming] = "false"
|
||||
config[forwardedRFC7239] = "for"
|
||||
config[forwardedRFC7239For] = "ip"
|
||||
config[forwardedRFC7239By] = "ip"
|
||||
f.SetNginxConfigMapData(config)
|
||||
|
||||
ing := framework.NewSingleIngress(host, "/", host, f.Namespace, framework.EchoService, 80, nil)
|
||||
f.EnsureIngress(ing)
|
||||
|
||||
f.WaitForNginxServer(host,
|
||||
func(server string) bool {
|
||||
return strings.Contains(server, "server_name forwarded-rfc7239") &&
|
||||
strings.Contains(server, "proxy_set_header Forwarded $proxy_add_forwarded_rfc2379;")
|
||||
})
|
||||
|
||||
serverIP := f.GetNginxPodIP()
|
||||
clientIP := getClientIP(serverIP)
|
||||
|
||||
ginkgo.By("ensuring singly pass through incoming header when empty parameter list")
|
||||
f.UpdateNginxConfigMapData(forwardedRFC7239, "")
|
||||
body := f.HTTPTestClient().
|
||||
GET("/").
|
||||
WithHeader("Host", host).
|
||||
WithHeader("Forwarded", "for=1.1.1.1").
|
||||
Expect().
|
||||
Status(http.StatusOK).
|
||||
Body().
|
||||
Raw()
|
||||
|
||||
assert.Contains(ginkgo.GinkgoT(), body, "forwarded=for=1.1.1.1")
|
||||
|
||||
ginkgo.By("ensuring any parameter combinations work")
|
||||
f.UpdateNginxConfigMapData(forwardedRFC7239, "for,by,proto,host")
|
||||
body = f.HTTPTestClient().
|
||||
GET("/").
|
||||
WithHeader("Host", host).
|
||||
Expect().
|
||||
Status(http.StatusOK).
|
||||
Body().
|
||||
Raw()
|
||||
|
||||
assert.Contains(ginkgo.GinkgoT(), body, fmt.Sprintf(`forwarded=for=%s;by="%s:80";proto=http;host=forwarded-rfc7239`, clientIP, serverIP))
|
||||
})
|
||||
|
||||
ginkgo.It("should config \"for\" and \"by\" parameters as static obfuscated strings", func() {
|
||||
host := "forwarded-rfc7239"
|
||||
|
||||
config := map[string]string{}
|
||||
config[enableForwardedRFC7239] = "true"
|
||||
config[forwardedRFC7239StripIncomming] = "false"
|
||||
config[forwardedRFC7239] = "for,by"
|
||||
config[forwardedRFC7239For] = "ip"
|
||||
config[forwardedRFC7239By] = "_SERVER1"
|
||||
f.SetNginxConfigMapData(config)
|
||||
|
||||
ing := framework.NewSingleIngress(host, "/", host, f.Namespace, framework.EchoService, 80, nil)
|
||||
f.EnsureIngress(ing)
|
||||
|
||||
f.WaitForNginxServer(host,
|
||||
func(server string) bool {
|
||||
return strings.Contains(server, "server_name forwarded-rfc7239") &&
|
||||
strings.Contains(server, "proxy_set_header Forwarded $proxy_add_forwarded_rfc2379;")
|
||||
})
|
||||
|
||||
serverIP := f.GetNginxPodIP()
|
||||
clientIP := getClientIP(serverIP)
|
||||
|
||||
ginkgo.By("ensuring \"by\" parameter is a static obfuscated string")
|
||||
body := f.HTTPTestClient().
|
||||
GET("/").
|
||||
WithHeader("Host", host).
|
||||
Expect().
|
||||
Status(http.StatusOK).
|
||||
Body().
|
||||
Raw()
|
||||
|
||||
assert.Contains(ginkgo.GinkgoT(), body, fmt.Sprintf("forwarded=for=%s;by=_SERVER1", clientIP))
|
||||
|
||||
ginkgo.By("ensuring \"for\" parameter is a static obfuscated string")
|
||||
config[forwardedRFC7239For] = "_HOST1"
|
||||
config[forwardedRFC7239By] = "ip"
|
||||
f.SetNginxConfigMapData(config)
|
||||
body = f.HTTPTestClient().
|
||||
GET("/").
|
||||
WithHeader("Host", host).
|
||||
Expect().
|
||||
Status(http.StatusOK).
|
||||
Body().
|
||||
Raw()
|
||||
|
||||
assert.Contains(ginkgo.GinkgoT(), body, fmt.Sprintf(`forwarded=for=_HOST1;by="%s:80"`, serverIP))
|
||||
|
||||
ginkgo.By("ensuring invalid static obfuscated strings are ingored")
|
||||
config[forwardedRFC7239For] = "_HOST1"
|
||||
config[forwardedRFC7239By] = "_%"
|
||||
f.SetNginxConfigMapData(config)
|
||||
body = f.HTTPTestClient().
|
||||
GET("/").
|
||||
WithHeader("Host", host).
|
||||
Expect().
|
||||
Status(http.StatusOK).
|
||||
Body().
|
||||
Raw()
|
||||
|
||||
assert.Contains(ginkgo.GinkgoT(), body, fmt.Sprintf(`forwarded=for=_HOST1;by="%s:80"`, serverIP))
|
||||
})
|
||||
})
|
||||
|
||||
func getClientIP(serverIP string) net.IP {
|
||||
conn, err := net.Dial("tcp", serverIP+":80")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
localAddr := conn.LocalAddr().(*net.TCPAddr)
|
||||
return localAddr.IP
|
||||
}
|
Loading…
Reference in a new issue