Add TrustForwardHeader options.
This commit is contained in:
parent
9598f646f5
commit
aa308b7a3a
10 changed files with 265 additions and 40 deletions
|
@ -229,7 +229,10 @@ func run(globalConfiguration *configuration.GlobalConfiguration) {
|
||||||
http.DefaultTransport.(*http.Transport).Proxy = http.ProxyFromEnvironment
|
http.DefaultTransport.(*http.Transport).Proxy = http.ProxyFromEnvironment
|
||||||
|
|
||||||
if len(globalConfiguration.EntryPoints) == 0 {
|
if len(globalConfiguration.EntryPoints) == 0 {
|
||||||
globalConfiguration.EntryPoints = map[string]*configuration.EntryPoint{"http": {Address: ":80"}}
|
globalConfiguration.EntryPoints = map[string]*configuration.EntryPoint{"http": {
|
||||||
|
Address: ":80",
|
||||||
|
ForwardedHeaders: &configuration.ForwardedHeaders{Insecure: true},
|
||||||
|
}}
|
||||||
globalConfiguration.DefaultEntryPoints = []string{"http"}
|
globalConfiguration.DefaultEntryPoints = []string{"http"}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -259,6 +262,14 @@ func run(globalConfiguration *configuration.GlobalConfiguration) {
|
||||||
globalConfiguration.LogLevel = "DEBUG"
|
globalConfiguration.LogLevel = "DEBUG"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ForwardedHeaders must be remove in the next breaking version
|
||||||
|
for entryPointName := range globalConfiguration.EntryPoints {
|
||||||
|
entryPoint := globalConfiguration.EntryPoints[entryPointName]
|
||||||
|
if entryPoint.ForwardedHeaders == nil {
|
||||||
|
entryPoint.ForwardedHeaders = &configuration.ForwardedHeaders{Insecure: true}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// logging
|
// logging
|
||||||
level, err := logrus.ParseLevel(strings.ToLower(globalConfiguration.LogLevel))
|
level, err := logrus.ParseLevel(strings.ToLower(globalConfiguration.LogLevel))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -10,6 +10,7 @@ import (
|
||||||
|
|
||||||
"github.com/containous/flaeg"
|
"github.com/containous/flaeg"
|
||||||
"github.com/containous/traefik/acme"
|
"github.com/containous/traefik/acme"
|
||||||
|
"github.com/containous/traefik/log"
|
||||||
"github.com/containous/traefik/provider/boltdb"
|
"github.com/containous/traefik/provider/boltdb"
|
||||||
"github.com/containous/traefik/provider/consul"
|
"github.com/containous/traefik/provider/consul"
|
||||||
"github.com/containous/traefik/provider/docker"
|
"github.com/containous/traefik/provider/docker"
|
||||||
|
@ -229,11 +230,31 @@ func (ep *EntryPoints) Set(value string) error {
|
||||||
compress := toBool(result, "compress")
|
compress := toBool(result, "compress")
|
||||||
|
|
||||||
var proxyProtocol *ProxyProtocol
|
var proxyProtocol *ProxyProtocol
|
||||||
if len(result["proxyprotocol_trustedips"]) > 0 {
|
ppTrustedIPs := result["proxyprotocol_trustedips"]
|
||||||
trustedIPs := strings.Split(result["proxyprotocol_trustedips"], ",")
|
if len(result["proxyprotocol_insecure"]) > 0 || len(ppTrustedIPs) > 0 {
|
||||||
proxyProtocol = &ProxyProtocol{
|
proxyProtocol = &ProxyProtocol{
|
||||||
TrustedIPs: trustedIPs,
|
Insecure: toBool(result, "proxyprotocol_insecure"),
|
||||||
}
|
}
|
||||||
|
if len(ppTrustedIPs) > 0 {
|
||||||
|
proxyProtocol.TrustedIPs = strings.Split(ppTrustedIPs, ",")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO must be changed to false by default in the next breaking version.
|
||||||
|
forwardedHeaders := &ForwardedHeaders{Insecure: true}
|
||||||
|
if _, ok := result["forwardedheaders_insecure"]; ok {
|
||||||
|
forwardedHeaders.Insecure = toBool(result, "forwardedheaders_insecure")
|
||||||
|
}
|
||||||
|
|
||||||
|
fhTrustedIPs := result["forwardedheaders_trustedips"]
|
||||||
|
if len(fhTrustedIPs) > 0 {
|
||||||
|
// TODO must be removed in the next breaking version.
|
||||||
|
forwardedHeaders.Insecure = toBool(result, "forwardedheaders_insecure")
|
||||||
|
forwardedHeaders.TrustedIPs = strings.Split(fhTrustedIPs, ",")
|
||||||
|
}
|
||||||
|
|
||||||
|
if proxyProtocol != nil && proxyProtocol.Insecure {
|
||||||
|
log.Warn("ProxyProtocol.Insecure:true is dangerous. Please use 'ProxyProtocol.TrustedIPs:IPs' and remove 'ProxyProtocol.Insecure:true'")
|
||||||
}
|
}
|
||||||
|
|
||||||
(*ep)[result["name"]] = &EntryPoint{
|
(*ep)[result["name"]] = &EntryPoint{
|
||||||
|
@ -243,6 +264,7 @@ func (ep *EntryPoints) Set(value string) error {
|
||||||
Compress: compress,
|
Compress: compress,
|
||||||
WhitelistSourceRange: whiteListSourceRange,
|
WhitelistSourceRange: whiteListSourceRange,
|
||||||
ProxyProtocol: proxyProtocol,
|
ProxyProtocol: proxyProtocol,
|
||||||
|
ForwardedHeaders: forwardedHeaders,
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -302,6 +324,7 @@ type EntryPoint struct {
|
||||||
WhitelistSourceRange []string
|
WhitelistSourceRange []string
|
||||||
Compress bool `export:"true"`
|
Compress bool `export:"true"`
|
||||||
ProxyProtocol *ProxyProtocol `export:"true"`
|
ProxyProtocol *ProxyProtocol `export:"true"`
|
||||||
|
ForwardedHeaders *ForwardedHeaders `export:"true"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Redirect configures a redirection of an entry point to another, or to an URL
|
// Redirect configures a redirection of an entry point to another, or to an URL
|
||||||
|
@ -453,5 +476,12 @@ type ForwardingTimeouts struct {
|
||||||
|
|
||||||
// ProxyProtocol contains Proxy-Protocol configuration
|
// ProxyProtocol contains Proxy-Protocol configuration
|
||||||
type ProxyProtocol struct {
|
type ProxyProtocol struct {
|
||||||
|
Insecure bool
|
||||||
|
TrustedIPs []string
|
||||||
|
}
|
||||||
|
|
||||||
|
// ForwardedHeaders Trust client forwarding headers
|
||||||
|
type ForwardedHeaders struct {
|
||||||
|
Insecure bool
|
||||||
TrustedIPs []string
|
TrustedIPs []string
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package configuration
|
package configuration
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
@ -16,7 +15,7 @@ func Test_parseEntryPointsConfiguration(t *testing.T) {
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "all parameters",
|
name: "all parameters",
|
||||||
value: "Name:foo TLS:goo TLS CA:car Redirect.EntryPoint:RedirectEntryPoint Redirect.Regex:RedirectRegex Redirect.Replacement:RedirectReplacement Compress:true WhiteListSourceRange:WhiteListSourceRange ProxyProtocol.TrustedIPs:192.168.0.1 Address::8000",
|
value: "Name:foo TLS:goo TLS CA:car Redirect.EntryPoint:RedirectEntryPoint Redirect.Regex:RedirectRegex Redirect.Replacement:RedirectReplacement Compress:true WhiteListSourceRange:WhiteListSourceRange ProxyProtocol.TrustedIPs:192.168.0.1 ProxyProtocol.Insecure:false Address::8000",
|
||||||
expectedResult: map[string]string{
|
expectedResult: map[string]string{
|
||||||
"name": "foo",
|
"name": "foo",
|
||||||
"address": ":8000",
|
"address": ":8000",
|
||||||
|
@ -28,6 +27,7 @@ func Test_parseEntryPointsConfiguration(t *testing.T) {
|
||||||
"redirect_replacement": "RedirectReplacement",
|
"redirect_replacement": "RedirectReplacement",
|
||||||
"whitelistsourcerange": "WhiteListSourceRange",
|
"whitelistsourcerange": "WhiteListSourceRange",
|
||||||
"proxyprotocol_trustedips": "192.168.0.1",
|
"proxyprotocol_trustedips": "192.168.0.1",
|
||||||
|
"proxyprotocol_insecure": "false",
|
||||||
"compress": "true",
|
"compress": "true",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -57,10 +57,6 @@ func Test_parseEntryPointsConfiguration(t *testing.T) {
|
||||||
|
|
||||||
conf := parseEntryPointsConfiguration(test.value)
|
conf := parseEntryPointsConfiguration(test.value)
|
||||||
|
|
||||||
for key, value := range conf {
|
|
||||||
fmt.Println(key, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
assert.Len(t, conf, len(test.expectedResult))
|
assert.Len(t, conf, len(test.expectedResult))
|
||||||
assert.Equal(t, test.expectedResult, conf)
|
assert.Equal(t, test.expectedResult, conf)
|
||||||
})
|
})
|
||||||
|
@ -131,7 +127,7 @@ func TestEntryPoints_Set(t *testing.T) {
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "all parameters camelcase",
|
name: "all parameters camelcase",
|
||||||
expression: "Name:foo Address::8000 TLS:goo,gii TLS CA:car Redirect.EntryPoint:RedirectEntryPoint Redirect.Regex:RedirectRegex Redirect.Replacement:RedirectReplacement Compress:true WhiteListSourceRange:Range ProxyProtocol.TrustedIPs:192.168.0.1",
|
expression: "Name:foo Address::8000 TLS:goo,gii TLS CA:car Redirect.EntryPoint:RedirectEntryPoint Redirect.Regex:RedirectRegex Redirect.Replacement:RedirectReplacement Compress:true WhiteListSourceRange:Range ProxyProtocol.TrustedIPs:192.168.0.1 ForwardedHeaders.TrustedIPs:10.0.0.3/24,20.0.0.3/24",
|
||||||
expectedEntryPointName: "foo",
|
expectedEntryPointName: "foo",
|
||||||
expectedEntryPoint: &EntryPoint{
|
expectedEntryPoint: &EntryPoint{
|
||||||
Address: ":8000",
|
Address: ":8000",
|
||||||
|
@ -144,6 +140,9 @@ func TestEntryPoints_Set(t *testing.T) {
|
||||||
ProxyProtocol: &ProxyProtocol{
|
ProxyProtocol: &ProxyProtocol{
|
||||||
TrustedIPs: []string{"192.168.0.1"},
|
TrustedIPs: []string{"192.168.0.1"},
|
||||||
},
|
},
|
||||||
|
ForwardedHeaders: &ForwardedHeaders{
|
||||||
|
TrustedIPs: []string{"10.0.0.3/24", "20.0.0.3/24"},
|
||||||
|
},
|
||||||
WhitelistSourceRange: []string{"Range"},
|
WhitelistSourceRange: []string{"Range"},
|
||||||
TLS: &TLS{
|
TLS: &TLS{
|
||||||
ClientCAFiles: []string{"car"},
|
ClientCAFiles: []string{"car"},
|
||||||
|
@ -158,7 +157,7 @@ func TestEntryPoints_Set(t *testing.T) {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "all parameters lowercase",
|
name: "all parameters lowercase",
|
||||||
expression: "name:foo address::8000 tls:goo,gii tls ca:car redirect.entryPoint:RedirectEntryPoint redirect.regex:RedirectRegex redirect.replacement:RedirectReplacement compress:true whiteListSourceRange:Range proxyProtocol.TrustedIPs:192.168.0.1",
|
expression: "name:foo address::8000 tls:goo,gii tls ca:car redirect.entryPoint:RedirectEntryPoint redirect.regex:RedirectRegex redirect.replacement:RedirectReplacement compress:true whiteListSourceRange:Range proxyProtocol.trustedIPs:192.168.0.1 forwardedHeaders.trustedIPs:10.0.0.3/24,20.0.0.3/24",
|
||||||
expectedEntryPointName: "foo",
|
expectedEntryPointName: "foo",
|
||||||
expectedEntryPoint: &EntryPoint{
|
expectedEntryPoint: &EntryPoint{
|
||||||
Address: ":8000",
|
Address: ":8000",
|
||||||
|
@ -171,6 +170,9 @@ func TestEntryPoints_Set(t *testing.T) {
|
||||||
ProxyProtocol: &ProxyProtocol{
|
ProxyProtocol: &ProxyProtocol{
|
||||||
TrustedIPs: []string{"192.168.0.1"},
|
TrustedIPs: []string{"192.168.0.1"},
|
||||||
},
|
},
|
||||||
|
ForwardedHeaders: &ForwardedHeaders{
|
||||||
|
TrustedIPs: []string{"10.0.0.3/24", "20.0.0.3/24"},
|
||||||
|
},
|
||||||
WhitelistSourceRange: []string{"Range"},
|
WhitelistSourceRange: []string{"Range"},
|
||||||
TLS: &TLS{
|
TLS: &TLS{
|
||||||
ClientCAFiles: []string{"car"},
|
ClientCAFiles: []string{"car"},
|
||||||
|
@ -183,6 +185,76 @@ func TestEntryPoints_Set(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "default",
|
||||||
|
expression: "Name:foo",
|
||||||
|
expectedEntryPointName: "foo",
|
||||||
|
expectedEntryPoint: &EntryPoint{
|
||||||
|
WhitelistSourceRange: []string{},
|
||||||
|
ForwardedHeaders: &ForwardedHeaders{Insecure: true},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ForwardedHeaders insecure true",
|
||||||
|
expression: "Name:foo ForwardedHeaders.Insecure:true",
|
||||||
|
expectedEntryPointName: "foo",
|
||||||
|
expectedEntryPoint: &EntryPoint{
|
||||||
|
WhitelistSourceRange: []string{},
|
||||||
|
ForwardedHeaders: &ForwardedHeaders{Insecure: true},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ForwardedHeaders insecure false",
|
||||||
|
expression: "Name:foo ForwardedHeaders.Insecure:false",
|
||||||
|
expectedEntryPointName: "foo",
|
||||||
|
expectedEntryPoint: &EntryPoint{
|
||||||
|
WhitelistSourceRange: []string{},
|
||||||
|
ForwardedHeaders: &ForwardedHeaders{Insecure: false},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ForwardedHeaders TrustedIPs",
|
||||||
|
expression: "Name:foo ForwardedHeaders.TrustedIPs:10.0.0.3/24,20.0.0.3/24",
|
||||||
|
expectedEntryPointName: "foo",
|
||||||
|
expectedEntryPoint: &EntryPoint{
|
||||||
|
WhitelistSourceRange: []string{},
|
||||||
|
ForwardedHeaders: &ForwardedHeaders{
|
||||||
|
TrustedIPs: []string{"10.0.0.3/24", "20.0.0.3/24"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ProxyProtocol insecure true",
|
||||||
|
expression: "Name:foo ProxyProtocol.Insecure:true",
|
||||||
|
expectedEntryPointName: "foo",
|
||||||
|
expectedEntryPoint: &EntryPoint{
|
||||||
|
WhitelistSourceRange: []string{},
|
||||||
|
ForwardedHeaders: &ForwardedHeaders{Insecure: true},
|
||||||
|
ProxyProtocol: &ProxyProtocol{Insecure: true},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ProxyProtocol insecure false",
|
||||||
|
expression: "Name:foo ProxyProtocol.Insecure:false",
|
||||||
|
expectedEntryPointName: "foo",
|
||||||
|
expectedEntryPoint: &EntryPoint{
|
||||||
|
WhitelistSourceRange: []string{},
|
||||||
|
ForwardedHeaders: &ForwardedHeaders{Insecure: true},
|
||||||
|
ProxyProtocol: &ProxyProtocol{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ProxyProtocol TrustedIPs",
|
||||||
|
expression: "Name:foo ProxyProtocol.TrustedIPs:10.0.0.3/24,20.0.0.3/24",
|
||||||
|
expectedEntryPointName: "foo",
|
||||||
|
expectedEntryPoint: &EntryPoint{
|
||||||
|
WhitelistSourceRange: []string{},
|
||||||
|
ForwardedHeaders: &ForwardedHeaders{Insecure: true},
|
||||||
|
ProxyProtocol: &ProxyProtocol{
|
||||||
|
TrustedIPs: []string{"10.0.0.3/24", "20.0.0.3/24"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "compress on",
|
name: "compress on",
|
||||||
expression: "Name:foo Compress:on",
|
expression: "Name:foo Compress:on",
|
||||||
|
@ -190,6 +262,7 @@ func TestEntryPoints_Set(t *testing.T) {
|
||||||
expectedEntryPoint: &EntryPoint{
|
expectedEntryPoint: &EntryPoint{
|
||||||
Compress: true,
|
Compress: true,
|
||||||
WhitelistSourceRange: []string{},
|
WhitelistSourceRange: []string{},
|
||||||
|
ForwardedHeaders: &ForwardedHeaders{Insecure: true},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -199,6 +272,7 @@ func TestEntryPoints_Set(t *testing.T) {
|
||||||
expectedEntryPoint: &EntryPoint{
|
expectedEntryPoint: &EntryPoint{
|
||||||
Compress: true,
|
Compress: true,
|
||||||
WhitelistSourceRange: []string{},
|
WhitelistSourceRange: []string{},
|
||||||
|
ForwardedHeaders: &ForwardedHeaders{Insecure: true},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -191,7 +191,7 @@ To enable IP whitelisting at the entrypoint level.
|
||||||
## ProxyProtocol
|
## ProxyProtocol
|
||||||
|
|
||||||
To enable [ProxyProtocol](https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt) support.
|
To enable [ProxyProtocol](https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt) support.
|
||||||
Only IPs in `trustedIPs` will lead to remote client address replacement: you should declare your load-balancer IP or CIDR range here (in testing environment, you can trust everyone using `0.0.0.0/0`).
|
Only IPs in `trustedIPs` will lead to remote client address replacement: you should declare your load-balancer IP or CIDR range here (in testing environment, you can trust everyone using `insecure = true`).
|
||||||
|
|
||||||
!!! danger
|
!!! danger
|
||||||
When queuing Træfik behind another load-balancer, be sure to carefully configure Proxy Protocol on both sides.
|
When queuing Træfik behind another load-balancer, be sure to carefully configure Proxy Protocol on both sides.
|
||||||
|
@ -201,6 +201,39 @@ Only IPs in `trustedIPs` will lead to remote client address replacement: you sho
|
||||||
[entryPoints]
|
[entryPoints]
|
||||||
[entryPoints.http]
|
[entryPoints.http]
|
||||||
address = ":80"
|
address = ":80"
|
||||||
|
|
||||||
|
# Enable ProxyProtocol
|
||||||
[entryPoints.http.proxyProtocol]
|
[entryPoints.http.proxyProtocol]
|
||||||
|
# List of trusted IPs
|
||||||
|
#
|
||||||
|
# Required
|
||||||
|
# Default: []
|
||||||
|
#
|
||||||
|
trustedIPs = ["127.0.0.1/32", "192.168.1.7"]
|
||||||
|
|
||||||
|
# Insecure mode FOR TESTING ENVIRONNEMENT ONLY
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
# Default: false
|
||||||
|
#
|
||||||
|
# insecure = true
|
||||||
|
```
|
||||||
|
|
||||||
|
## Forwarded Header
|
||||||
|
|
||||||
|
Only IPs in `trustedIPs` will be authorize to trust the client forwarded headers (`X-Forwarded-*`).
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[entryPoints]
|
||||||
|
[entryPoints.http]
|
||||||
|
address = ":80"
|
||||||
|
|
||||||
|
# Enable Forwarded Headers
|
||||||
|
[entryPoints.http.forwardedHeaders]
|
||||||
|
# List of trusted IPs
|
||||||
|
#
|
||||||
|
# Required
|
||||||
|
# Default: []
|
||||||
|
#
|
||||||
trustedIPs = ["127.0.0.1/32", "192.168.1.7"]
|
trustedIPs = ["127.0.0.1/32", "192.168.1.7"]
|
||||||
```
|
```
|
||||||
|
|
|
@ -26,7 +26,7 @@ func NewIPWhitelister(whitelistStrings []string) (*IPWhiteLister, error) {
|
||||||
|
|
||||||
whiteLister := IPWhiteLister{}
|
whiteLister := IPWhiteLister{}
|
||||||
|
|
||||||
ip, err := whitelist.NewIP(whitelistStrings)
|
ip, err := whitelist.NewIP(whitelistStrings, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("parsing CIDR whitelist %s: %v", whitelistStrings, err)
|
return nil, fmt.Errorf("parsing CIDR whitelist %s: %v", whitelistStrings, err)
|
||||||
}
|
}
|
||||||
|
|
51
server/header_rewriter.go
Normal file
51
server/header_rewriter.go
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
package server
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/containous/traefik/whitelist"
|
||||||
|
"github.com/vulcand/oxy/forward"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewHeaderRewriter Create a header rewriter
|
||||||
|
func NewHeaderRewriter(trustedIPs []string, insecure bool) (forward.ReqRewriter, error) {
|
||||||
|
IPs, err := whitelist.NewIP(trustedIPs, insecure)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
h, err := os.Hostname()
|
||||||
|
if err != nil {
|
||||||
|
h = "localhost"
|
||||||
|
}
|
||||||
|
|
||||||
|
return &headerRewriter{
|
||||||
|
secureRewriter: &forward.HeaderRewriter{TrustForwardHeader: true, Hostname: h},
|
||||||
|
insecureRewriter: &forward.HeaderRewriter{TrustForwardHeader: false, Hostname: h},
|
||||||
|
ips: IPs,
|
||||||
|
insecure: insecure,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type headerRewriter struct {
|
||||||
|
secureRewriter forward.ReqRewriter
|
||||||
|
insecureRewriter forward.ReqRewriter
|
||||||
|
insecure bool
|
||||||
|
ips *whitelist.IP
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *headerRewriter) Rewrite(req *http.Request) {
|
||||||
|
clientIP, _, err := net.SplitHostPort(req.RemoteAddr)
|
||||||
|
if err != nil {
|
||||||
|
h.secureRewriter.Rewrite(req)
|
||||||
|
}
|
||||||
|
|
||||||
|
authorized, _, err := h.ips.Contains(clientIP)
|
||||||
|
if h.insecure || authorized {
|
||||||
|
h.secureRewriter.Rewrite(req)
|
||||||
|
} else {
|
||||||
|
h.insecureRewriter.Rewrite(req)
|
||||||
|
}
|
||||||
|
}
|
|
@ -655,7 +655,7 @@ func (server *Server) prepareServer(entryPointName string, entryPoint *configura
|
||||||
}
|
}
|
||||||
|
|
||||||
if entryPoint.ProxyProtocol != nil {
|
if entryPoint.ProxyProtocol != nil {
|
||||||
IPs, err := whitelist.NewIP(entryPoint.ProxyProtocol.TrustedIPs)
|
IPs, err := whitelist.NewIP(entryPoint.ProxyProtocol.TrustedIPs, entryPoint.ProxyProtocol.Insecure)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, fmt.Errorf("Error creating whitelist: %s", err)
|
return nil, nil, fmt.Errorf("Error creating whitelist: %s", err)
|
||||||
}
|
}
|
||||||
|
@ -806,11 +806,19 @@ func (server *Server) loadConfig(configurations types.Configurations, globalConf
|
||||||
continue frontend
|
continue frontend
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rewriter, err := NewHeaderRewriter(entryPoint.ForwardedHeaders.TrustedIPs, entryPoint.ForwardedHeaders.Insecure)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("Error creating rewriter for frontend %s: %v", frontendName, err)
|
||||||
|
log.Errorf("Skipping frontend %s...", frontendName)
|
||||||
|
continue frontend
|
||||||
|
}
|
||||||
|
|
||||||
fwd, err := forward.New(
|
fwd, err := forward.New(
|
||||||
forward.Logger(oxyLogger),
|
forward.Logger(oxyLogger),
|
||||||
forward.PassHostHeader(frontend.PassHostHeader),
|
forward.PassHostHeader(frontend.PassHostHeader),
|
||||||
forward.RoundTripper(roundTripper),
|
forward.RoundTripper(roundTripper),
|
||||||
forward.ErrorHandler(errorHandler),
|
forward.ErrorHandler(errorHandler),
|
||||||
|
forward.Rewriter(rewriter),
|
||||||
)
|
)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -96,7 +96,10 @@ func TestPrepareServerTimeouts(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
entryPointName := "http"
|
entryPointName := "http"
|
||||||
entryPoint := &configuration.EntryPoint{Address: "localhost:0"}
|
entryPoint := &configuration.EntryPoint{
|
||||||
|
Address: "localhost:0",
|
||||||
|
ForwardedHeaders: &configuration.ForwardedHeaders{Insecure: true},
|
||||||
|
}
|
||||||
router := middlewares.NewHandlerSwitcher(mux.NewRouter())
|
router := middlewares.NewHandlerSwitcher(mux.NewRouter())
|
||||||
|
|
||||||
srv := NewServer(test.globalConfig)
|
srv := NewServer(test.globalConfig)
|
||||||
|
@ -210,7 +213,9 @@ func TestServerLoadConfigHealthCheckOptions(t *testing.T) {
|
||||||
t.Run(fmt.Sprintf("%s/hc=%t", lbMethod, healthCheck != nil), func(t *testing.T) {
|
t.Run(fmt.Sprintf("%s/hc=%t", lbMethod, healthCheck != nil), func(t *testing.T) {
|
||||||
globalConfig := configuration.GlobalConfiguration{
|
globalConfig := configuration.GlobalConfiguration{
|
||||||
EntryPoints: configuration.EntryPoints{
|
EntryPoints: configuration.EntryPoints{
|
||||||
"http": &configuration.EntryPoint{},
|
"http": &configuration.EntryPoint{
|
||||||
|
ForwardedHeaders: &configuration.ForwardedHeaders{Insecure: true},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
HealthCheck: &configuration.HealthCheckConfig{Interval: flaeg.Duration(5 * time.Second)},
|
HealthCheck: &configuration.HealthCheckConfig{Interval: flaeg.Duration(5 * time.Second)},
|
||||||
}
|
}
|
||||||
|
@ -383,7 +388,7 @@ func TestNewServerWithWhitelistSourceRange(t *testing.T) {
|
||||||
func TestServerLoadConfigEmptyBasicAuth(t *testing.T) {
|
func TestServerLoadConfigEmptyBasicAuth(t *testing.T) {
|
||||||
globalConfig := configuration.GlobalConfiguration{
|
globalConfig := configuration.GlobalConfiguration{
|
||||||
EntryPoints: configuration.EntryPoints{
|
EntryPoints: configuration.EntryPoints{
|
||||||
"http": &configuration.EntryPoint{},
|
"http": &configuration.EntryPoint{ForwardedHeaders: &configuration.ForwardedHeaders{Insecure: true}},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -492,7 +497,7 @@ func TestConfigureBackends(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestServerEntrypointWhitelistConfig(t *testing.T) {
|
func TestServerEntryPointWhitelistConfig(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
desc string
|
desc string
|
||||||
entrypoint *configuration.EntryPoint
|
entrypoint *configuration.EntryPoint
|
||||||
|
@ -502,6 +507,7 @@ func TestServerEntrypointWhitelistConfig(t *testing.T) {
|
||||||
desc: "no whitelist middleware if no config on entrypoint",
|
desc: "no whitelist middleware if no config on entrypoint",
|
||||||
entrypoint: &configuration.EntryPoint{
|
entrypoint: &configuration.EntryPoint{
|
||||||
Address: ":0",
|
Address: ":0",
|
||||||
|
ForwardedHeaders: &configuration.ForwardedHeaders{Insecure: true},
|
||||||
},
|
},
|
||||||
wantMiddleware: false,
|
wantMiddleware: false,
|
||||||
},
|
},
|
||||||
|
@ -512,6 +518,7 @@ func TestServerEntrypointWhitelistConfig(t *testing.T) {
|
||||||
WhitelistSourceRange: []string{
|
WhitelistSourceRange: []string{
|
||||||
"127.0.0.1/32",
|
"127.0.0.1/32",
|
||||||
},
|
},
|
||||||
|
ForwardedHeaders: &configuration.ForwardedHeaders{Insecure: true},
|
||||||
},
|
},
|
||||||
wantMiddleware: true,
|
wantMiddleware: true,
|
||||||
},
|
},
|
||||||
|
@ -633,7 +640,7 @@ func TestServerResponseEmptyBackend(t *testing.T) {
|
||||||
|
|
||||||
globalConfig := configuration.GlobalConfiguration{
|
globalConfig := configuration.GlobalConfiguration{
|
||||||
EntryPoints: configuration.EntryPoints{
|
EntryPoints: configuration.EntryPoints{
|
||||||
"http": &configuration.EntryPoint{},
|
"http": &configuration.EntryPoint{ForwardedHeaders: &configuration.ForwardedHeaders{Insecure: true}},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
dynamicConfigs := types.Configurations{"config": test.dynamicConfig(testServer.URL)}
|
dynamicConfigs := types.Configurations{"config": test.dynamicConfig(testServer.URL)}
|
||||||
|
|
|
@ -11,16 +11,18 @@ import (
|
||||||
type IP struct {
|
type IP struct {
|
||||||
whiteListsIPs []*net.IP
|
whiteListsIPs []*net.IP
|
||||||
whiteListsNet []*net.IPNet
|
whiteListsNet []*net.IPNet
|
||||||
|
insecure bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewIP builds a new IP given a list of CIDR-Strings to whitelist
|
// NewIP builds a new IP given a list of CIDR-Strings to whitelist
|
||||||
func NewIP(whitelistStrings []string) (*IP, error) {
|
func NewIP(whitelistStrings []string, insecure bool) (*IP, error) {
|
||||||
if len(whitelistStrings) == 0 {
|
if len(whitelistStrings) == 0 && !insecure {
|
||||||
return nil, errors.New("no whiteListsNet provided")
|
return nil, errors.New("no whiteListsNet provided")
|
||||||
}
|
}
|
||||||
|
|
||||||
ip := IP{}
|
ip := IP{}
|
||||||
|
|
||||||
|
if !insecure {
|
||||||
for _, whitelistString := range whitelistStrings {
|
for _, whitelistString := range whitelistStrings {
|
||||||
ipAddr := net.ParseIP(whitelistString)
|
ipAddr := net.ParseIP(whitelistString)
|
||||||
if ipAddr != nil {
|
if ipAddr != nil {
|
||||||
|
@ -33,12 +35,17 @@ func NewIP(whitelistStrings []string) (*IP, error) {
|
||||||
ip.whiteListsNet = append(ip.whiteListsNet, whitelist)
|
ip.whiteListsNet = append(ip.whiteListsNet, whitelist)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return &ip, nil
|
return &ip, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Contains checks if provided address is in the white list
|
// Contains checks if provided address is in the white list
|
||||||
func (ip *IP) Contains(addr string) (bool, net.IP, error) {
|
func (ip *IP) Contains(addr string) (bool, net.IP, error) {
|
||||||
|
if ip.insecure {
|
||||||
|
return true, nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
ipAddr, err := ipFromRemoteAddr(addr)
|
ipAddr, err := ipFromRemoteAddr(addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, nil, fmt.Errorf("unable to parse address: %s: %s", addr, err)
|
return false, nil, fmt.Errorf("unable to parse address: %s: %s", addr, err)
|
||||||
|
@ -50,6 +57,10 @@ func (ip *IP) Contains(addr string) (bool, net.IP, error) {
|
||||||
|
|
||||||
// ContainsIP checks if provided address is in the white list
|
// ContainsIP checks if provided address is in the white list
|
||||||
func (ip *IP) ContainsIP(addr net.IP) (bool, error) {
|
func (ip *IP) ContainsIP(addr net.IP) (bool, error) {
|
||||||
|
if ip.insecure {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
for _, whiteListIP := range ip.whiteListsIPs {
|
for _, whiteListIP := range ip.whiteListsIPs {
|
||||||
if whiteListIP.Equal(addr) {
|
if whiteListIP.Equal(addr) {
|
||||||
return true, nil
|
return true, nil
|
||||||
|
|
|
@ -75,7 +75,7 @@ func TestNew(t *testing.T) {
|
||||||
test := test
|
test := test
|
||||||
t.Run(test.desc, func(t *testing.T) {
|
t.Run(test.desc, func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
whitelister, err := NewIP(test.whitelistStrings)
|
whitelister, err := NewIP(test.whitelistStrings, false)
|
||||||
if test.errMessage != "" {
|
if test.errMessage != "" {
|
||||||
require.EqualError(t, err, test.errMessage)
|
require.EqualError(t, err, test.errMessage)
|
||||||
} else {
|
} else {
|
||||||
|
@ -275,7 +275,7 @@ func TestIsAllowed(t *testing.T) {
|
||||||
test := test
|
test := test
|
||||||
t.Run(test.desc, func(t *testing.T) {
|
t.Run(test.desc, func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
whiteLister, err := NewIP(test.whitelistStrings)
|
whiteLister, err := NewIP(test.whitelistStrings, false)
|
||||||
|
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NotNil(t, whiteLister)
|
require.NotNil(t, whiteLister)
|
||||||
|
@ -306,7 +306,7 @@ func TestBrokenIPs(t *testing.T) {
|
||||||
"\\&$§&/(",
|
"\\&$§&/(",
|
||||||
}
|
}
|
||||||
|
|
||||||
whiteLister, err := NewIP([]string{"1.2.3.4/24"})
|
whiteLister, err := NewIP([]string{"1.2.3.4/24"}, false)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
for _, testIP := range brokenIPs {
|
for _, testIP := range brokenIPs {
|
||||||
|
|
Loading…
Reference in a new issue