Add new options to the CLI entrypoint definition.
This commit is contained in:
parent
f5adea1061
commit
6a92ac0b7b
8 changed files with 708 additions and 454 deletions
|
@ -44,7 +44,6 @@ func TestDo_globalConfiguration(t *testing.T) {
|
||||||
config.LogLevel = "LogLevel"
|
config.LogLevel = "LogLevel"
|
||||||
config.EntryPoints = configuration.EntryPoints{
|
config.EntryPoints = configuration.EntryPoints{
|
||||||
"foo": {
|
"foo": {
|
||||||
Network: "foo Network",
|
|
||||||
Address: "foo Address",
|
Address: "foo Address",
|
||||||
TLS: &traefikTls.TLS{
|
TLS: &traefikTls.TLS{
|
||||||
MinVersion: "foo MinVersion",
|
MinVersion: "foo MinVersion",
|
||||||
|
@ -90,7 +89,6 @@ func TestDo_globalConfiguration(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"fii": {
|
"fii": {
|
||||||
Network: "fii Network",
|
|
||||||
Address: "fii Address",
|
Address: "fii Address",
|
||||||
TLS: &traefikTls.TLS{
|
TLS: &traefikTls.TLS{
|
||||||
MinVersion: "fii MinVersion",
|
MinVersion: "fii MinVersion",
|
||||||
|
|
|
@ -29,7 +29,6 @@ func Test_doOnJSON(t *testing.T) {
|
||||||
"Compress": false
|
"Compress": false
|
||||||
},
|
},
|
||||||
"https": {
|
"https": {
|
||||||
"Network": "",
|
|
||||||
"Address": ":443",
|
"Address": ":443",
|
||||||
"TLS": {
|
"TLS": {
|
||||||
"MinVersion": "",
|
"MinVersion": "",
|
||||||
|
@ -119,7 +118,6 @@ func Test_doOnJSON(t *testing.T) {
|
||||||
"Compress": false
|
"Compress": false
|
||||||
},
|
},
|
||||||
"https": {
|
"https": {
|
||||||
"Network": "",
|
|
||||||
"Address": ":443",
|
"Address": ":443",
|
||||||
"TLS": {
|
"TLS": {
|
||||||
"MinVersion": "",
|
"MinVersion": "",
|
||||||
|
|
|
@ -313,157 +313,6 @@ func (dep *DefaultEntryPoints) Type() string {
|
||||||
return "defaultentrypoints"
|
return "defaultentrypoints"
|
||||||
}
|
}
|
||||||
|
|
||||||
// EntryPoints holds entry points configuration of the reverse proxy (ip, port, TLS...)
|
|
||||||
type EntryPoints map[string]*EntryPoint
|
|
||||||
|
|
||||||
// String is the method to format the flag's value, part of the flag.Value interface.
|
|
||||||
// The String method's output will be used in diagnostics.
|
|
||||||
func (ep *EntryPoints) String() string {
|
|
||||||
return fmt.Sprintf("%+v", *ep)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set is the method to set the flag value, part of the flag.Value interface.
|
|
||||||
// Set's argument is a string to be parsed to set the flag.
|
|
||||||
// It's a comma-separated list, so we split it.
|
|
||||||
func (ep *EntryPoints) Set(value string) error {
|
|
||||||
result := parseEntryPointsConfiguration(value)
|
|
||||||
|
|
||||||
var configTLS *tls.TLS
|
|
||||||
if len(result["tls"]) > 0 {
|
|
||||||
certs := tls.Certificates{}
|
|
||||||
if err := certs.Set(result["tls"]); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
configTLS = &tls.TLS{
|
|
||||||
Certificates: certs,
|
|
||||||
}
|
|
||||||
} else if len(result["tls_acme"]) > 0 {
|
|
||||||
configTLS = &tls.TLS{
|
|
||||||
Certificates: tls.Certificates{},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if len(result["ca"]) > 0 {
|
|
||||||
files := strings.Split(result["ca"], ",")
|
|
||||||
optional := toBool(result, "ca_optional")
|
|
||||||
configTLS.ClientCA = tls.ClientCA{
|
|
||||||
Files: files,
|
|
||||||
Optional: optional,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var redirect *types.Redirect
|
|
||||||
if len(result["redirect_entrypoint"]) > 0 || len(result["redirect_regex"]) > 0 || len(result["redirect_replacement"]) > 0 {
|
|
||||||
redirect = &types.Redirect{
|
|
||||||
EntryPoint: result["redirect_entrypoint"],
|
|
||||||
Regex: result["redirect_regex"],
|
|
||||||
Replacement: result["redirect_replacement"],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
whiteListSourceRange := []string{}
|
|
||||||
if len(result["whitelistsourcerange"]) > 0 {
|
|
||||||
whiteListSourceRange = strings.Split(result["whitelistsourcerange"], ",")
|
|
||||||
}
|
|
||||||
|
|
||||||
compress := toBool(result, "compress")
|
|
||||||
|
|
||||||
var proxyProtocol *ProxyProtocol
|
|
||||||
ppTrustedIPs := result["proxyprotocol_trustedips"]
|
|
||||||
if len(result["proxyprotocol_insecure"]) > 0 || len(ppTrustedIPs) > 0 {
|
|
||||||
proxyProtocol = &ProxyProtocol{
|
|
||||||
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{
|
|
||||||
Address: result["address"],
|
|
||||||
TLS: configTLS,
|
|
||||||
Redirect: redirect,
|
|
||||||
Compress: compress,
|
|
||||||
WhitelistSourceRange: whiteListSourceRange,
|
|
||||||
ProxyProtocol: proxyProtocol,
|
|
||||||
ForwardedHeaders: forwardedHeaders,
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseEntryPointsConfiguration(raw string) map[string]string {
|
|
||||||
sections := strings.Fields(raw)
|
|
||||||
|
|
||||||
config := make(map[string]string)
|
|
||||||
for _, part := range sections {
|
|
||||||
field := strings.SplitN(part, ":", 2)
|
|
||||||
name := strings.ToLower(strings.Replace(field[0], ".", "_", -1))
|
|
||||||
if len(field) > 1 {
|
|
||||||
config[name] = field[1]
|
|
||||||
} else {
|
|
||||||
if strings.EqualFold(name, "TLS") {
|
|
||||||
config["tls_acme"] = "TLS"
|
|
||||||
} else {
|
|
||||||
config[name] = ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return config
|
|
||||||
}
|
|
||||||
|
|
||||||
func toBool(conf map[string]string, key string) bool {
|
|
||||||
if val, ok := conf[key]; ok {
|
|
||||||
return strings.EqualFold(val, "true") ||
|
|
||||||
strings.EqualFold(val, "enable") ||
|
|
||||||
strings.EqualFold(val, "on")
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get return the EntryPoints map
|
|
||||||
func (ep *EntryPoints) Get() interface{} {
|
|
||||||
return *ep
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetValue sets the EntryPoints map with val
|
|
||||||
func (ep *EntryPoints) SetValue(val interface{}) {
|
|
||||||
*ep = val.(EntryPoints)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Type is type of the struct
|
|
||||||
func (ep *EntryPoints) Type() string {
|
|
||||||
return "entrypoints"
|
|
||||||
}
|
|
||||||
|
|
||||||
// EntryPoint holds an entry point configuration of the reverse proxy (ip, port, TLS...)
|
|
||||||
type EntryPoint struct {
|
|
||||||
Network string
|
|
||||||
Address string
|
|
||||||
TLS *tls.TLS `export:"true"`
|
|
||||||
Redirect *types.Redirect `export:"true"`
|
|
||||||
Auth *types.Auth `export:"true"`
|
|
||||||
WhitelistSourceRange []string
|
|
||||||
Compress bool `export:"true"`
|
|
||||||
ProxyProtocol *ProxyProtocol `export:"true"`
|
|
||||||
ForwardedHeaders *ForwardedHeaders `export:"true"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Retry contains request retry config
|
// Retry contains request retry config
|
||||||
type Retry struct {
|
type Retry struct {
|
||||||
Attempts int `description:"Number of attempts" export:"true"`
|
Attempts int `description:"Number of attempts" export:"true"`
|
||||||
|
|
|
@ -7,305 +7,10 @@ import (
|
||||||
"github.com/containous/flaeg"
|
"github.com/containous/flaeg"
|
||||||
"github.com/containous/traefik/provider"
|
"github.com/containous/traefik/provider"
|
||||||
"github.com/containous/traefik/provider/file"
|
"github.com/containous/traefik/provider/file"
|
||||||
"github.com/containous/traefik/tls"
|
|
||||||
"github.com/containous/traefik/types"
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const defaultConfigFile = "traefik.toml"
|
const defaultConfigFile = "traefik.toml"
|
||||||
|
|
||||||
func Test_parseEntryPointsConfiguration(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
name string
|
|
||||||
value string
|
|
||||||
expectedResult map[string]string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
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 ProxyProtocol.Insecure:false Address::8000",
|
|
||||||
expectedResult: map[string]string{
|
|
||||||
"name": "foo",
|
|
||||||
"address": ":8000",
|
|
||||||
"ca": "car",
|
|
||||||
"tls": "goo",
|
|
||||||
"tls_acme": "TLS",
|
|
||||||
"redirect_entrypoint": "RedirectEntryPoint",
|
|
||||||
"redirect_regex": "RedirectRegex",
|
|
||||||
"redirect_replacement": "RedirectReplacement",
|
|
||||||
"whitelistsourcerange": "WhiteListSourceRange",
|
|
||||||
"proxyprotocol_trustedips": "192.168.0.1",
|
|
||||||
"proxyprotocol_insecure": "false",
|
|
||||||
"compress": "true",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "compress on",
|
|
||||||
value: "name:foo Compress:on",
|
|
||||||
expectedResult: map[string]string{
|
|
||||||
"name": "foo",
|
|
||||||
"compress": "on",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "TLS",
|
|
||||||
value: "Name:foo TLS:goo TLS",
|
|
||||||
expectedResult: map[string]string{
|
|
||||||
"name": "foo",
|
|
||||||
"tls": "goo",
|
|
||||||
"tls_acme": "TLS",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, test := range testCases {
|
|
||||||
test := test
|
|
||||||
t.Run(test.name, func(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
conf := parseEntryPointsConfiguration(test.value)
|
|
||||||
|
|
||||||
assert.Len(t, conf, len(test.expectedResult))
|
|
||||||
assert.Equal(t, test.expectedResult, conf)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_toBool(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
name string
|
|
||||||
value string
|
|
||||||
key string
|
|
||||||
expectedBool bool
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "on",
|
|
||||||
value: "on",
|
|
||||||
key: "foo",
|
|
||||||
expectedBool: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "true",
|
|
||||||
value: "true",
|
|
||||||
key: "foo",
|
|
||||||
expectedBool: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "enable",
|
|
||||||
value: "enable",
|
|
||||||
key: "foo",
|
|
||||||
expectedBool: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "arbitrary string",
|
|
||||||
value: "bar",
|
|
||||||
key: "foo",
|
|
||||||
expectedBool: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "no existing entry",
|
|
||||||
value: "bar",
|
|
||||||
key: "fii",
|
|
||||||
expectedBool: false,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, test := range testCases {
|
|
||||||
test := test
|
|
||||||
t.Run(test.name, func(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
conf := map[string]string{
|
|
||||||
"foo": test.value,
|
|
||||||
}
|
|
||||||
|
|
||||||
result := toBool(conf, test.key)
|
|
||||||
|
|
||||||
assert.Equal(t, test.expectedBool, result)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestEntryPoints_Set(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
name string
|
|
||||||
expression string
|
|
||||||
expectedEntryPointName string
|
|
||||||
expectedEntryPoint *EntryPoint
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "all parameters camelcase",
|
|
||||||
expression: "Name:foo Address::8000 TLS:goo,gii TLS CA:car CA.Optional:false 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",
|
|
||||||
expectedEntryPoint: &EntryPoint{
|
|
||||||
Address: ":8000",
|
|
||||||
Redirect: &types.Redirect{
|
|
||||||
EntryPoint: "RedirectEntryPoint",
|
|
||||||
Regex: "RedirectRegex",
|
|
||||||
Replacement: "RedirectReplacement",
|
|
||||||
},
|
|
||||||
Compress: true,
|
|
||||||
ProxyProtocol: &ProxyProtocol{
|
|
||||||
TrustedIPs: []string{"192.168.0.1"},
|
|
||||||
},
|
|
||||||
ForwardedHeaders: &ForwardedHeaders{
|
|
||||||
TrustedIPs: []string{"10.0.0.3/24", "20.0.0.3/24"},
|
|
||||||
},
|
|
||||||
WhitelistSourceRange: []string{"Range"},
|
|
||||||
TLS: &tls.TLS{
|
|
||||||
ClientCA: tls.ClientCA{
|
|
||||||
Files: []string{"car"},
|
|
||||||
Optional: false,
|
|
||||||
},
|
|
||||||
Certificates: tls.Certificates{
|
|
||||||
{
|
|
||||||
CertFile: tls.FileOrContent("goo"),
|
|
||||||
KeyFile: tls.FileOrContent("gii"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "all parameters lowercase",
|
|
||||||
expression: "name:foo address::8000 tls:goo,gii tls ca:car ca.optional:true 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",
|
|
||||||
expectedEntryPoint: &EntryPoint{
|
|
||||||
Address: ":8000",
|
|
||||||
Redirect: &types.Redirect{
|
|
||||||
EntryPoint: "RedirectEntryPoint",
|
|
||||||
Regex: "RedirectRegex",
|
|
||||||
Replacement: "RedirectReplacement",
|
|
||||||
},
|
|
||||||
Compress: true,
|
|
||||||
ProxyProtocol: &ProxyProtocol{
|
|
||||||
TrustedIPs: []string{"192.168.0.1"},
|
|
||||||
},
|
|
||||||
ForwardedHeaders: &ForwardedHeaders{
|
|
||||||
TrustedIPs: []string{"10.0.0.3/24", "20.0.0.3/24"},
|
|
||||||
},
|
|
||||||
WhitelistSourceRange: []string{"Range"},
|
|
||||||
TLS: &tls.TLS{
|
|
||||||
ClientCA: tls.ClientCA{
|
|
||||||
Files: []string{"car"},
|
|
||||||
Optional: true,
|
|
||||||
},
|
|
||||||
Certificates: tls.Certificates{
|
|
||||||
{
|
|
||||||
CertFile: tls.FileOrContent("goo"),
|
|
||||||
KeyFile: tls.FileOrContent("gii"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
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",
|
|
||||||
expression: "Name:foo Compress:on",
|
|
||||||
expectedEntryPointName: "foo",
|
|
||||||
expectedEntryPoint: &EntryPoint{
|
|
||||||
Compress: true,
|
|
||||||
WhitelistSourceRange: []string{},
|
|
||||||
ForwardedHeaders: &ForwardedHeaders{Insecure: true},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "compress true",
|
|
||||||
expression: "Name:foo Compress:true",
|
|
||||||
expectedEntryPointName: "foo",
|
|
||||||
expectedEntryPoint: &EntryPoint{
|
|
||||||
Compress: true,
|
|
||||||
WhitelistSourceRange: []string{},
|
|
||||||
ForwardedHeaders: &ForwardedHeaders{Insecure: true},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, test := range testCases {
|
|
||||||
test := test
|
|
||||||
t.Run(test.name, func(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
eps := EntryPoints{}
|
|
||||||
err := eps.Set(test.expression)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
ep := eps[test.expectedEntryPointName]
|
|
||||||
assert.EqualValues(t, test.expectedEntryPoint, ep)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSetEffectiveConfigurationGraceTimeout(t *testing.T) {
|
func TestSetEffectiveConfigurationGraceTimeout(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
desc string
|
desc string
|
||||||
|
|
241
configuration/entrypoints.go
Normal file
241
configuration/entrypoints.go
Normal file
|
@ -0,0 +1,241 @@
|
||||||
|
package configuration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/containous/traefik/log"
|
||||||
|
"github.com/containous/traefik/tls"
|
||||||
|
"github.com/containous/traefik/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
// EntryPoint holds an entry point configuration of the reverse proxy (ip, port, TLS...)
|
||||||
|
type EntryPoint struct {
|
||||||
|
Address string
|
||||||
|
TLS *tls.TLS `export:"true"`
|
||||||
|
Redirect *types.Redirect `export:"true"`
|
||||||
|
Auth *types.Auth `export:"true"`
|
||||||
|
WhitelistSourceRange []string
|
||||||
|
Compress bool `export:"true"`
|
||||||
|
ProxyProtocol *ProxyProtocol `export:"true"`
|
||||||
|
ForwardedHeaders *ForwardedHeaders `export:"true"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// EntryPoints holds entry points configuration of the reverse proxy (ip, port, TLS...)
|
||||||
|
type EntryPoints map[string]*EntryPoint
|
||||||
|
|
||||||
|
// String is the method to format the flag's value, part of the flag.Value interface.
|
||||||
|
// The String method's output will be used in diagnostics.
|
||||||
|
func (ep EntryPoints) String() string {
|
||||||
|
return fmt.Sprintf("%+v", map[string]*EntryPoint(ep))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get return the EntryPoints map
|
||||||
|
func (ep *EntryPoints) Get() interface{} {
|
||||||
|
return *ep
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetValue sets the EntryPoints map with val
|
||||||
|
func (ep *EntryPoints) SetValue(val interface{}) {
|
||||||
|
*ep = val.(EntryPoints)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Type is type of the struct
|
||||||
|
func (ep *EntryPoints) Type() string {
|
||||||
|
return "entrypoints"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set is the method to set the flag value, part of the flag.Value interface.
|
||||||
|
// Set's argument is a string to be parsed to set the flag.
|
||||||
|
// It's a comma-separated list, so we split it.
|
||||||
|
func (ep *EntryPoints) Set(value string) error {
|
||||||
|
result := parseEntryPointsConfiguration(value)
|
||||||
|
|
||||||
|
var whiteListSourceRange []string
|
||||||
|
if len(result["whitelistsourcerange"]) > 0 {
|
||||||
|
whiteListSourceRange = strings.Split(result["whitelistsourcerange"], ",")
|
||||||
|
}
|
||||||
|
|
||||||
|
compress := toBool(result, "compress")
|
||||||
|
|
||||||
|
configTLS, err := makeEntryPointTLS(result)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
(*ep)[result["name"]] = &EntryPoint{
|
||||||
|
Address: result["address"],
|
||||||
|
TLS: configTLS,
|
||||||
|
Auth: makeEntryPointAuth(result),
|
||||||
|
Redirect: makeEntryPointRedirect(result),
|
||||||
|
Compress: compress,
|
||||||
|
WhitelistSourceRange: whiteListSourceRange,
|
||||||
|
ProxyProtocol: makeEntryPointProxyProtocol(result),
|
||||||
|
ForwardedHeaders: makeEntryPointForwardedHeaders(result),
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeEntryPointAuth(result map[string]string) *types.Auth {
|
||||||
|
var basic *types.Basic
|
||||||
|
if v, ok := result["auth_basic_users"]; ok {
|
||||||
|
basic = &types.Basic{
|
||||||
|
Users: strings.Split(v, ","),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var digest *types.Digest
|
||||||
|
if v, ok := result["auth_digest_users"]; ok {
|
||||||
|
digest = &types.Digest{
|
||||||
|
Users: strings.Split(v, ","),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var forward *types.Forward
|
||||||
|
if address, ok := result["auth_forward_address"]; ok {
|
||||||
|
var clientTLS *types.ClientTLS
|
||||||
|
|
||||||
|
cert := result["auth_forward_tls_cert"]
|
||||||
|
key := result["auth_forward_tls_key"]
|
||||||
|
insecureSkipVerify := toBool(result, "auth_forward_tls_insecureskipverify")
|
||||||
|
|
||||||
|
if len(cert) > 0 && len(key) > 0 || insecureSkipVerify {
|
||||||
|
clientTLS = &types.ClientTLS{
|
||||||
|
CA: result["auth_forward_tls_ca"],
|
||||||
|
CAOptional: toBool(result, "auth_forward_tls_caoptional"),
|
||||||
|
Cert: cert,
|
||||||
|
Key: key,
|
||||||
|
InsecureSkipVerify: insecureSkipVerify,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
forward = &types.Forward{
|
||||||
|
Address: address,
|
||||||
|
TLS: clientTLS,
|
||||||
|
TrustForwardHeader: toBool(result, "auth_forward_trustforwardheader"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var auth *types.Auth
|
||||||
|
if basic != nil || digest != nil || forward != nil {
|
||||||
|
auth = &types.Auth{
|
||||||
|
Basic: basic,
|
||||||
|
Digest: digest,
|
||||||
|
Forward: forward,
|
||||||
|
HeaderField: result["auth_headerfield"],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return auth
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeEntryPointProxyProtocol(result map[string]string) *ProxyProtocol {
|
||||||
|
var proxyProtocol *ProxyProtocol
|
||||||
|
|
||||||
|
ppTrustedIPs := result["proxyprotocol_trustedips"]
|
||||||
|
if len(result["proxyprotocol_insecure"]) > 0 || len(ppTrustedIPs) > 0 {
|
||||||
|
proxyProtocol = &ProxyProtocol{
|
||||||
|
Insecure: toBool(result, "proxyprotocol_insecure"),
|
||||||
|
}
|
||||||
|
if len(ppTrustedIPs) > 0 {
|
||||||
|
proxyProtocol.TrustedIPs = strings.Split(ppTrustedIPs, ",")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if proxyProtocol != nil && proxyProtocol.Insecure {
|
||||||
|
log.Warn("ProxyProtocol.Insecure:true is dangerous. Please use 'ProxyProtocol.TrustedIPs:IPs' and remove 'ProxyProtocol.Insecure:true'")
|
||||||
|
}
|
||||||
|
|
||||||
|
return proxyProtocol
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeEntryPointForwardedHeaders(result map[string]string) *ForwardedHeaders {
|
||||||
|
// 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, ",")
|
||||||
|
}
|
||||||
|
|
||||||
|
return forwardedHeaders
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeEntryPointRedirect(result map[string]string) *types.Redirect {
|
||||||
|
var redirect *types.Redirect
|
||||||
|
|
||||||
|
if len(result["redirect_entrypoint"]) > 0 || len(result["redirect_regex"]) > 0 || len(result["redirect_replacement"]) > 0 {
|
||||||
|
redirect = &types.Redirect{
|
||||||
|
EntryPoint: result["redirect_entrypoint"],
|
||||||
|
Regex: result["redirect_regex"],
|
||||||
|
Replacement: result["redirect_replacement"],
|
||||||
|
Permanent: toBool(result, "redirect_permanent"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return redirect
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeEntryPointTLS(result map[string]string) (*tls.TLS, error) {
|
||||||
|
var configTLS *tls.TLS
|
||||||
|
|
||||||
|
if len(result["tls"]) > 0 {
|
||||||
|
certs := tls.Certificates{}
|
||||||
|
if err := certs.Set(result["tls"]); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
configTLS = &tls.TLS{
|
||||||
|
Certificates: certs,
|
||||||
|
}
|
||||||
|
} else if len(result["tls_acme"]) > 0 {
|
||||||
|
configTLS = &tls.TLS{
|
||||||
|
Certificates: tls.Certificates{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(result["ca"]) > 0 {
|
||||||
|
files := strings.Split(result["ca"], ",")
|
||||||
|
optional := toBool(result, "ca_optional")
|
||||||
|
configTLS.ClientCA = tls.ClientCA{
|
||||||
|
Files: files,
|
||||||
|
Optional: optional,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return configTLS, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseEntryPointsConfiguration(raw string) map[string]string {
|
||||||
|
sections := strings.Fields(raw)
|
||||||
|
|
||||||
|
config := make(map[string]string)
|
||||||
|
for _, part := range sections {
|
||||||
|
field := strings.SplitN(part, ":", 2)
|
||||||
|
name := strings.ToLower(strings.Replace(field[0], ".", "_", -1))
|
||||||
|
if len(field) > 1 {
|
||||||
|
config[name] = field[1]
|
||||||
|
} else {
|
||||||
|
if strings.EqualFold(name, "TLS") {
|
||||||
|
config["tls_acme"] = "TLS"
|
||||||
|
} else {
|
||||||
|
config[name] = ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return config
|
||||||
|
}
|
||||||
|
|
||||||
|
func toBool(conf map[string]string, key string) bool {
|
||||||
|
if val, ok := conf[key]; ok {
|
||||||
|
return strings.EqualFold(val, "true") ||
|
||||||
|
strings.EqualFold(val, "enable") ||
|
||||||
|
strings.EqualFold(val, "on")
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
445
configuration/entrypoints_test.go
Normal file
445
configuration/entrypoints_test.go
Normal file
|
@ -0,0 +1,445 @@
|
||||||
|
package configuration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/containous/traefik/tls"
|
||||||
|
"github.com/containous/traefik/types"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_parseEntryPointsConfiguration(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
value string
|
||||||
|
expectedResult map[string]string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "all parameters",
|
||||||
|
value: "Name:foo " +
|
||||||
|
"Address::8000 " +
|
||||||
|
"TLS:goo,gii " +
|
||||||
|
"TLS " +
|
||||||
|
"CA:car " +
|
||||||
|
"CA.Optional:true " +
|
||||||
|
"Redirect.EntryPoint:https " +
|
||||||
|
"Redirect.Regex:http://localhost/(.*) " +
|
||||||
|
"Redirect.Replacement:http://mydomain/$1 " +
|
||||||
|
"Redirect.Permanent:true " +
|
||||||
|
"Compress:true " +
|
||||||
|
"WhiteListSourceRange:10.42.0.0/16,152.89.1.33/32,afed:be44::/16 " +
|
||||||
|
"ProxyProtocol.TrustedIPs:192.168.0.1 " +
|
||||||
|
"ForwardedHeaders.TrustedIPs:10.0.0.3/24,20.0.0.3/24 " +
|
||||||
|
"Auth.Basic.Users:test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0 " +
|
||||||
|
"Auth.Digest.Users:test:traefik:a2688e031edb4be6a3797f3882655c05,test2:traefik:518845800f9e2bfb1f1f740ec24f074e " +
|
||||||
|
"Auth.HeaderField:X-WebAuth-User " +
|
||||||
|
"Auth.Forward.Address:https://authserver.com/auth " +
|
||||||
|
"Auth.Forward.TrustForwardHeader:true " +
|
||||||
|
"Auth.Forward.TLS.CA:path/to/local.crt " +
|
||||||
|
"Auth.Forward.TLS.CAOptional:true " +
|
||||||
|
"Auth.Forward.TLS.Cert:path/to/foo.cert " +
|
||||||
|
"Auth.Forward.TLS.Key:path/to/foo.key " +
|
||||||
|
"Auth.Forward.TLS.InsecureSkipVerify:true ",
|
||||||
|
expectedResult: map[string]string{
|
||||||
|
"address": ":8000",
|
||||||
|
"auth_basic_users": "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0",
|
||||||
|
"auth_digest_users": "test:traefik:a2688e031edb4be6a3797f3882655c05,test2:traefik:518845800f9e2bfb1f1f740ec24f074e",
|
||||||
|
"auth_forward_address": "https://authserver.com/auth",
|
||||||
|
"auth_forward_tls_ca": "path/to/local.crt",
|
||||||
|
"auth_forward_tls_caoptional": "true",
|
||||||
|
"auth_forward_tls_cert": "path/to/foo.cert",
|
||||||
|
"auth_forward_tls_insecureskipverify": "true",
|
||||||
|
"auth_forward_tls_key": "path/to/foo.key",
|
||||||
|
"auth_forward_trustforwardheader": "true",
|
||||||
|
"auth_headerfield": "X-WebAuth-User",
|
||||||
|
"ca": "car",
|
||||||
|
"ca_optional": "true",
|
||||||
|
"compress": "true",
|
||||||
|
"forwardedheaders_trustedips": "10.0.0.3/24,20.0.0.3/24",
|
||||||
|
"name": "foo",
|
||||||
|
"proxyprotocol_trustedips": "192.168.0.1",
|
||||||
|
"redirect_entrypoint": "https",
|
||||||
|
"redirect_permanent": "true",
|
||||||
|
"redirect_regex": "http://localhost/(.*)",
|
||||||
|
"redirect_replacement": "http://mydomain/$1",
|
||||||
|
"tls": "goo,gii",
|
||||||
|
"tls_acme": "TLS",
|
||||||
|
"whitelistsourcerange": "10.42.0.0/16,152.89.1.33/32,afed:be44::/16",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "compress on",
|
||||||
|
value: "name:foo Compress:on",
|
||||||
|
expectedResult: map[string]string{
|
||||||
|
"name": "foo",
|
||||||
|
"compress": "on",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "TLS",
|
||||||
|
value: "Name:foo TLS:goo TLS",
|
||||||
|
expectedResult: map[string]string{
|
||||||
|
"name": "foo",
|
||||||
|
"tls": "goo",
|
||||||
|
"tls_acme": "TLS",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range testCases {
|
||||||
|
test := test
|
||||||
|
t.Run(test.name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
conf := parseEntryPointsConfiguration(test.value)
|
||||||
|
|
||||||
|
assert.Len(t, conf, len(test.expectedResult))
|
||||||
|
assert.Equal(t, test.expectedResult, conf)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_toBool(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
value string
|
||||||
|
key string
|
||||||
|
expectedBool bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "on",
|
||||||
|
value: "on",
|
||||||
|
key: "foo",
|
||||||
|
expectedBool: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "true",
|
||||||
|
value: "true",
|
||||||
|
key: "foo",
|
||||||
|
expectedBool: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "enable",
|
||||||
|
value: "enable",
|
||||||
|
key: "foo",
|
||||||
|
expectedBool: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "arbitrary string",
|
||||||
|
value: "bar",
|
||||||
|
key: "foo",
|
||||||
|
expectedBool: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "no existing entry",
|
||||||
|
value: "bar",
|
||||||
|
key: "fii",
|
||||||
|
expectedBool: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range testCases {
|
||||||
|
test := test
|
||||||
|
t.Run(test.name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
conf := map[string]string{
|
||||||
|
"foo": test.value,
|
||||||
|
}
|
||||||
|
|
||||||
|
result := toBool(conf, test.key)
|
||||||
|
|
||||||
|
assert.Equal(t, test.expectedBool, result)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEntryPoints_Set(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
expression string
|
||||||
|
expectedEntryPointName string
|
||||||
|
expectedEntryPoint *EntryPoint
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "all parameters camelcase",
|
||||||
|
expression: "Name:foo " +
|
||||||
|
"Address::8000 " +
|
||||||
|
"TLS:goo,gii " +
|
||||||
|
"TLS " +
|
||||||
|
"CA:car " +
|
||||||
|
"CA.Optional:true " +
|
||||||
|
"Redirect.EntryPoint:https " +
|
||||||
|
"Redirect.Regex:http://localhost/(.*) " +
|
||||||
|
"Redirect.Replacement:http://mydomain/$1 " +
|
||||||
|
"Redirect.Permanent:true " +
|
||||||
|
"Compress:true " +
|
||||||
|
"WhiteListSourceRange:10.42.0.0/16,152.89.1.33/32,afed:be44::/16 " +
|
||||||
|
"ProxyProtocol.TrustedIPs:192.168.0.1 " +
|
||||||
|
"ForwardedHeaders.TrustedIPs:10.0.0.3/24,20.0.0.3/24 " +
|
||||||
|
"Auth.Basic.Users:test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0 " +
|
||||||
|
"Auth.Digest.Users:test:traefik:a2688e031edb4be6a3797f3882655c05,test2:traefik:518845800f9e2bfb1f1f740ec24f074e " +
|
||||||
|
"Auth.HeaderField:X-WebAuth-User " +
|
||||||
|
"Auth.Forward.Address:https://authserver.com/auth " +
|
||||||
|
"Auth.Forward.TrustForwardHeader:true " +
|
||||||
|
"Auth.Forward.TLS.CA:path/to/local.crt " +
|
||||||
|
"Auth.Forward.TLS.CAOptional:true " +
|
||||||
|
"Auth.Forward.TLS.Cert:path/to/foo.cert " +
|
||||||
|
"Auth.Forward.TLS.Key:path/to/foo.key " +
|
||||||
|
"Auth.Forward.TLS.InsecureSkipVerify:true ",
|
||||||
|
expectedEntryPointName: "foo",
|
||||||
|
expectedEntryPoint: &EntryPoint{
|
||||||
|
Address: ":8000",
|
||||||
|
TLS: &tls.TLS{
|
||||||
|
Certificates: tls.Certificates{
|
||||||
|
{
|
||||||
|
CertFile: tls.FileOrContent("goo"),
|
||||||
|
KeyFile: tls.FileOrContent("gii"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ClientCA: tls.ClientCA{
|
||||||
|
Files: []string{"car"},
|
||||||
|
Optional: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Redirect: &types.Redirect{
|
||||||
|
EntryPoint: "https",
|
||||||
|
Regex: "http://localhost/(.*)",
|
||||||
|
Replacement: "http://mydomain/$1",
|
||||||
|
Permanent: true,
|
||||||
|
},
|
||||||
|
Auth: &types.Auth{
|
||||||
|
Basic: &types.Basic{
|
||||||
|
Users: types.Users{
|
||||||
|
"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/",
|
||||||
|
"test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Digest: &types.Digest{
|
||||||
|
Users: types.Users{
|
||||||
|
"test:traefik:a2688e031edb4be6a3797f3882655c05",
|
||||||
|
"test2:traefik:518845800f9e2bfb1f1f740ec24f074e",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Forward: &types.Forward{
|
||||||
|
Address: "https://authserver.com/auth",
|
||||||
|
TLS: &types.ClientTLS{
|
||||||
|
CA: "path/to/local.crt",
|
||||||
|
CAOptional: true,
|
||||||
|
Cert: "path/to/foo.cert",
|
||||||
|
Key: "path/to/foo.key",
|
||||||
|
InsecureSkipVerify: true,
|
||||||
|
},
|
||||||
|
TrustForwardHeader: true,
|
||||||
|
},
|
||||||
|
HeaderField: "X-WebAuth-User",
|
||||||
|
},
|
||||||
|
WhitelistSourceRange: []string{
|
||||||
|
"10.42.0.0/16",
|
||||||
|
"152.89.1.33/32",
|
||||||
|
"afed:be44::/16",
|
||||||
|
},
|
||||||
|
Compress: true,
|
||||||
|
ProxyProtocol: &ProxyProtocol{
|
||||||
|
Insecure: false,
|
||||||
|
TrustedIPs: []string{"192.168.0.1"},
|
||||||
|
},
|
||||||
|
ForwardedHeaders: &ForwardedHeaders{
|
||||||
|
Insecure: false,
|
||||||
|
TrustedIPs: []string{
|
||||||
|
"10.0.0.3/24",
|
||||||
|
"20.0.0.3/24",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "all parameters lowercase",
|
||||||
|
expression: "Name:foo " +
|
||||||
|
"address::8000 " +
|
||||||
|
"tls:goo,gii " +
|
||||||
|
"tls " +
|
||||||
|
"ca:car " +
|
||||||
|
"ca.Optional:true " +
|
||||||
|
"redirect.entryPoint:https " +
|
||||||
|
"redirect.regex:http://localhost/(.*) " +
|
||||||
|
"redirect.replacement:http://mydomain/$1 " +
|
||||||
|
"redirect.permanent:true " +
|
||||||
|
"compress:true " +
|
||||||
|
"whiteListSourceRange:10.42.0.0/16,152.89.1.33/32,afed:be44::/16 " +
|
||||||
|
"proxyProtocol.TrustedIPs:192.168.0.1 " +
|
||||||
|
"forwardedHeaders.TrustedIPs:10.0.0.3/24,20.0.0.3/24 " +
|
||||||
|
"auth.basic.users:test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0 " +
|
||||||
|
"auth.digest.users:test:traefik:a2688e031edb4be6a3797f3882655c05,test2:traefik:518845800f9e2bfb1f1f740ec24f074e " +
|
||||||
|
"auth.headerField:X-WebAuth-User " +
|
||||||
|
"auth.forward.address:https://authserver.com/auth " +
|
||||||
|
"auth.forward.trustForwardHeader:true " +
|
||||||
|
"auth.forward.tls.ca:path/to/local.crt " +
|
||||||
|
"auth.forward.tls.caOptional:true " +
|
||||||
|
"auth.forward.tls.cert:path/to/foo.cert " +
|
||||||
|
"auth.forward.tls.key:path/to/foo.key " +
|
||||||
|
"auth.forward.tls.insecureSkipVerify:true ",
|
||||||
|
expectedEntryPointName: "foo",
|
||||||
|
expectedEntryPoint: &EntryPoint{
|
||||||
|
Address: ":8000",
|
||||||
|
TLS: &tls.TLS{
|
||||||
|
Certificates: tls.Certificates{
|
||||||
|
{
|
||||||
|
CertFile: tls.FileOrContent("goo"),
|
||||||
|
KeyFile: tls.FileOrContent("gii"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ClientCA: tls.ClientCA{
|
||||||
|
Files: []string{"car"},
|
||||||
|
Optional: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Redirect: &types.Redirect{
|
||||||
|
EntryPoint: "https",
|
||||||
|
Regex: "http://localhost/(.*)",
|
||||||
|
Replacement: "http://mydomain/$1",
|
||||||
|
Permanent: true,
|
||||||
|
},
|
||||||
|
Auth: &types.Auth{
|
||||||
|
Basic: &types.Basic{
|
||||||
|
Users: types.Users{
|
||||||
|
"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/",
|
||||||
|
"test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Digest: &types.Digest{
|
||||||
|
Users: types.Users{
|
||||||
|
"test:traefik:a2688e031edb4be6a3797f3882655c05",
|
||||||
|
"test2:traefik:518845800f9e2bfb1f1f740ec24f074e",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Forward: &types.Forward{
|
||||||
|
Address: "https://authserver.com/auth",
|
||||||
|
TLS: &types.ClientTLS{
|
||||||
|
CA: "path/to/local.crt",
|
||||||
|
CAOptional: true,
|
||||||
|
Cert: "path/to/foo.cert",
|
||||||
|
Key: "path/to/foo.key",
|
||||||
|
InsecureSkipVerify: true,
|
||||||
|
},
|
||||||
|
TrustForwardHeader: true,
|
||||||
|
},
|
||||||
|
HeaderField: "X-WebAuth-User",
|
||||||
|
},
|
||||||
|
WhitelistSourceRange: []string{
|
||||||
|
"10.42.0.0/16",
|
||||||
|
"152.89.1.33/32",
|
||||||
|
"afed:be44::/16",
|
||||||
|
},
|
||||||
|
Compress: true,
|
||||||
|
ProxyProtocol: &ProxyProtocol{
|
||||||
|
Insecure: false,
|
||||||
|
TrustedIPs: []string{"192.168.0.1"},
|
||||||
|
},
|
||||||
|
ForwardedHeaders: &ForwardedHeaders{
|
||||||
|
Insecure: false,
|
||||||
|
TrustedIPs: []string{
|
||||||
|
"10.0.0.3/24",
|
||||||
|
"20.0.0.3/24",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "default",
|
||||||
|
expression: "Name:foo",
|
||||||
|
expectedEntryPointName: "foo",
|
||||||
|
expectedEntryPoint: &EntryPoint{
|
||||||
|
ForwardedHeaders: &ForwardedHeaders{Insecure: true},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ForwardedHeaders insecure true",
|
||||||
|
expression: "Name:foo ForwardedHeaders.Insecure:true",
|
||||||
|
expectedEntryPointName: "foo",
|
||||||
|
expectedEntryPoint: &EntryPoint{
|
||||||
|
ForwardedHeaders: &ForwardedHeaders{Insecure: true},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ForwardedHeaders insecure false",
|
||||||
|
expression: "Name:foo ForwardedHeaders.Insecure:false",
|
||||||
|
expectedEntryPointName: "foo",
|
||||||
|
expectedEntryPoint: &EntryPoint{
|
||||||
|
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{
|
||||||
|
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{
|
||||||
|
ForwardedHeaders: &ForwardedHeaders{Insecure: true},
|
||||||
|
ProxyProtocol: &ProxyProtocol{Insecure: true},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ProxyProtocol insecure false",
|
||||||
|
expression: "Name:foo ProxyProtocol.Insecure:false",
|
||||||
|
expectedEntryPointName: "foo",
|
||||||
|
expectedEntryPoint: &EntryPoint{
|
||||||
|
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{
|
||||||
|
ForwardedHeaders: &ForwardedHeaders{Insecure: true},
|
||||||
|
ProxyProtocol: &ProxyProtocol{
|
||||||
|
TrustedIPs: []string{"10.0.0.3/24", "20.0.0.3/24"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "compress on",
|
||||||
|
expression: "Name:foo Compress:on",
|
||||||
|
expectedEntryPointName: "foo",
|
||||||
|
expectedEntryPoint: &EntryPoint{
|
||||||
|
Compress: true,
|
||||||
|
ForwardedHeaders: &ForwardedHeaders{Insecure: true},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "compress true",
|
||||||
|
expression: "Name:foo Compress:true",
|
||||||
|
expectedEntryPointName: "foo",
|
||||||
|
expectedEntryPoint: &EntryPoint{
|
||||||
|
Compress: true,
|
||||||
|
ForwardedHeaders: &ForwardedHeaders{Insecure: true},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range testCases {
|
||||||
|
test := test
|
||||||
|
t.Run(test.name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
eps := EntryPoints{}
|
||||||
|
err := eps.Set(test.expression)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
ep := eps[test.expectedEntryPointName]
|
||||||
|
assert.EqualValues(t, test.expectedEntryPoint, ep)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -91,11 +91,22 @@ CA.Optional:true
|
||||||
Redirect.EntryPoint:https
|
Redirect.EntryPoint:https
|
||||||
Redirect.Regex:http://localhost/(.*)
|
Redirect.Regex:http://localhost/(.*)
|
||||||
Redirect.Replacement:http://mydomain/$1
|
Redirect.Replacement:http://mydomain/$1
|
||||||
|
Redirect.Permanent:true
|
||||||
Compress:true
|
Compress:true
|
||||||
WhiteListSourceRange:10.42.0.0/16,152.89.1.33/32,afed:be44::/16
|
WhiteListSourceRange:10.42.0.0/16,152.89.1.33/32,afed:be44::/16
|
||||||
ProxyProtocol.TrustedIPs:192.168.0.1
|
ProxyProtocol.TrustedIPs:192.168.0.1
|
||||||
ProxyProtocol.Insecure:tue
|
ProxyProtocol.Insecure:tue
|
||||||
ForwardedHeaders.TrustedIPs:10.0.0.3/24,20.0.0.3/24
|
ForwardedHeaders.TrustedIPs:10.0.0.3/24,20.0.0.3/24
|
||||||
|
Auth.Basic.Users:test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0
|
||||||
|
Auth.Digest.Users:test:traefik:a2688e031edb4be6a3797f3882655c05,test2:traefik:518845800f9e2bfb1f1f740ec24f074e
|
||||||
|
Auth.HeaderField:X-WebAuth-User
|
||||||
|
Auth.Forward.Address:https://authserver.com/auth
|
||||||
|
Auth.Forward.TrustForwardHeader:true
|
||||||
|
Auth.Forward.TLS.CA:path/to/local.crt
|
||||||
|
Auth.Forward.TLS.CAOptional:true
|
||||||
|
Auth.Forward.TLS.Cert:path/to/foo.cert
|
||||||
|
Auth.Forward.TLS.Key:path/to/foo.key
|
||||||
|
Auth.Forward.TLS.InsecureSkipVerify:true
|
||||||
```
|
```
|
||||||
|
|
||||||
## Basic
|
## Basic
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package redirect
|
package redirect
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"testing"
|
"testing"
|
||||||
|
@ -108,7 +107,15 @@ func TestNewRegexHandler(t *testing.T) {
|
||||||
{
|
{
|
||||||
desc: "simple redirection",
|
desc: "simple redirection",
|
||||||
regex: `^(?:http?:\/\/)(foo)(\.com)(:\d+)(.*)$`,
|
regex: `^(?:http?:\/\/)(foo)(\.com)(:\d+)(.*)$`,
|
||||||
replacement: "https://$1{{\"bar\"}}$2:443$4",
|
replacement: "https://${1}bar$2:443$4",
|
||||||
|
url: "http://foo.com:80",
|
||||||
|
expectedURL: "https://foobar.com:443",
|
||||||
|
expectedStatus: http.StatusFound,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "use request header",
|
||||||
|
regex: `^(?:http?:\/\/)(foo)(\.com)(:\d+)(.*)$`,
|
||||||
|
replacement: `https://${1}{{ .Request.Header.Get "X-Foo" }}$2:443$4`,
|
||||||
url: "http://foo.com:80",
|
url: "http://foo.com:80",
|
||||||
expectedURL: "https://foobar.com:443",
|
expectedURL: "https://foobar.com:443",
|
||||||
expectedStatus: http.StatusFound,
|
expectedStatus: http.StatusFound,
|
||||||
|
@ -116,7 +123,7 @@ func TestNewRegexHandler(t *testing.T) {
|
||||||
{
|
{
|
||||||
desc: "URL doesn't match regex",
|
desc: "URL doesn't match regex",
|
||||||
regex: `^(?:http?:\/\/)(foo)(\.com)(:\d+)(.*)$`,
|
regex: `^(?:http?:\/\/)(foo)(\.com)(:\d+)(.*)$`,
|
||||||
replacement: "https://$1{{\"bar\"}}$2:443$4",
|
replacement: "https://${1}bar$2:443$4",
|
||||||
url: "http://bar.com:80",
|
url: "http://bar.com:80",
|
||||||
expectedStatus: http.StatusOK,
|
expectedStatus: http.StatusOK,
|
||||||
},
|
},
|
||||||
|
@ -145,7 +152,6 @@ func TestNewRegexHandler(t *testing.T) {
|
||||||
|
|
||||||
if test.errorExpected {
|
if test.errorExpected {
|
||||||
require.Nil(t, handler)
|
require.Nil(t, handler)
|
||||||
fmt.Println(err == nil)
|
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
} else {
|
} else {
|
||||||
require.NotNil(t, handler)
|
require.NotNil(t, handler)
|
||||||
|
@ -153,6 +159,7 @@ func TestNewRegexHandler(t *testing.T) {
|
||||||
|
|
||||||
recorder := httptest.NewRecorder()
|
recorder := httptest.NewRecorder()
|
||||||
r := testhelpers.MustNewRequest(http.MethodGet, test.url, nil)
|
r := testhelpers.MustNewRequest(http.MethodGet, test.url, nil)
|
||||||
|
r.Header.Set("X-Foo", "bar")
|
||||||
next := func(rw http.ResponseWriter, req *http.Request) {}
|
next := func(rw http.ResponseWriter, req *http.Request) {}
|
||||||
handler.ServeHTTP(recorder, r, next)
|
handler.ServeHTTP(recorder, r, next)
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue