242 lines
6.7 KiB
Go
242 lines
6.7 KiB
Go
|
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
|
||
|
}
|