2015-11-01 15:35:01 +00:00
|
|
|
package types
|
|
|
|
|
|
|
|
import (
|
2017-09-14 19:26:02 +00:00
|
|
|
"crypto/tls"
|
|
|
|
"crypto/x509"
|
2016-11-09 18:27:04 +00:00
|
|
|
"encoding"
|
2015-11-01 15:35:01 +00:00
|
|
|
"errors"
|
2016-06-02 13:17:04 +00:00
|
|
|
"fmt"
|
2017-08-25 16:22:03 +00:00
|
|
|
"io/ioutil"
|
|
|
|
"os"
|
2017-09-14 19:26:02 +00:00
|
|
|
"strconv"
|
|
|
|
"strings"
|
2017-08-25 16:22:03 +00:00
|
|
|
|
2018-01-24 16:52:03 +00:00
|
|
|
"github.com/abronan/valkeyrie/store"
|
2018-07-31 17:28:03 +00:00
|
|
|
"github.com/containous/flaeg/parse"
|
2018-02-26 14:34:04 +00:00
|
|
|
"github.com/containous/mux"
|
2018-08-24 14:20:03 +00:00
|
|
|
"github.com/containous/traefik/ip"
|
2017-08-25 16:22:03 +00:00
|
|
|
"github.com/containous/traefik/log"
|
2018-03-06 09:12:04 +00:00
|
|
|
traefiktls "github.com/containous/traefik/tls"
|
2018-06-06 15:56:03 +00:00
|
|
|
"github.com/mitchellh/hashstructure"
|
2017-01-31 21:55:02 +00:00
|
|
|
"github.com/ryanuber/go-glob"
|
2015-11-01 15:35:01 +00:00
|
|
|
)
|
|
|
|
|
2015-11-01 18:15:05 +00:00
|
|
|
// Backend holds backend configuration.
|
2015-11-01 15:35:01 +00:00
|
|
|
type Backend struct {
|
2018-10-29 17:42:03 +00:00
|
|
|
Servers map[string]Server `json:"servers,omitempty"`
|
|
|
|
CircuitBreaker *CircuitBreaker `json:"circuitBreaker,omitempty"`
|
|
|
|
LoadBalancer *LoadBalancer `json:"loadBalancer,omitempty"`
|
|
|
|
MaxConn *MaxConn `json:"maxConn,omitempty"`
|
|
|
|
HealthCheck *HealthCheck `json:"healthCheck,omitempty"`
|
|
|
|
Buffering *Buffering `json:"buffering,omitempty"`
|
|
|
|
ResponseForwarding *ResponseForwarding `json:"forwardingResponse,omitempty"`
|
|
|
|
}
|
|
|
|
|
|
|
|
// ResponseForwarding holds configuration for the forward of the response
|
|
|
|
type ResponseForwarding struct {
|
|
|
|
FlushInterval string `json:"flushInterval,omitempty"`
|
2016-04-13 08:11:36 +00:00
|
|
|
}
|
|
|
|
|
2016-04-21 22:38:44 +00:00
|
|
|
// MaxConn holds maximum connection configuration
|
2016-04-13 08:11:36 +00:00
|
|
|
type MaxConn struct {
|
|
|
|
Amount int64 `json:"amount,omitempty"`
|
|
|
|
ExtractorFunc string `json:"extractorFunc,omitempty"`
|
2015-11-01 15:35:01 +00:00
|
|
|
}
|
|
|
|
|
2015-11-01 18:15:05 +00:00
|
|
|
// LoadBalancer holds load balancing configuration.
|
2015-11-01 15:35:01 +00:00
|
|
|
type LoadBalancer struct {
|
2017-10-10 09:10:02 +00:00
|
|
|
Method string `json:"method,omitempty"`
|
|
|
|
Stickiness *Stickiness `json:"stickiness,omitempty"`
|
|
|
|
}
|
|
|
|
|
|
|
|
// Stickiness holds sticky session configuration.
|
|
|
|
type Stickiness struct {
|
|
|
|
CookieName string `json:"cookieName,omitempty"`
|
2015-11-01 15:35:01 +00:00
|
|
|
}
|
|
|
|
|
2015-11-01 18:15:05 +00:00
|
|
|
// CircuitBreaker holds circuit breaker configuration.
|
2015-11-01 15:35:01 +00:00
|
|
|
type CircuitBreaker struct {
|
|
|
|
Expression string `json:"expression,omitempty"`
|
|
|
|
}
|
|
|
|
|
2018-01-31 14:32:04 +00:00
|
|
|
// Buffering holds request/response buffering configuration/
|
|
|
|
type Buffering struct {
|
|
|
|
MaxRequestBodyBytes int64 `json:"maxRequestBodyBytes,omitempty"`
|
|
|
|
MemRequestBodyBytes int64 `json:"memRequestBodyBytes,omitempty"`
|
|
|
|
MaxResponseBodyBytes int64 `json:"maxResponseBodyBytes,omitempty"`
|
|
|
|
MemResponseBodyBytes int64 `json:"memResponseBodyBytes,omitempty"`
|
|
|
|
RetryExpression string `json:"retryExpression,omitempty"`
|
|
|
|
}
|
|
|
|
|
2018-03-23 16:40:04 +00:00
|
|
|
// WhiteList contains white list configuration.
|
|
|
|
type WhiteList struct {
|
2018-08-24 14:20:03 +00:00
|
|
|
SourceRange []string `json:"sourceRange,omitempty"`
|
|
|
|
IPStrategy *IPStrategy `json:"ipStrategy,omitempty"`
|
2018-03-23 16:40:04 +00:00
|
|
|
}
|
|
|
|
|
2017-01-31 21:55:02 +00:00
|
|
|
// HealthCheck holds HealthCheck configuration
|
2016-11-26 18:48:49 +00:00
|
|
|
type HealthCheck struct {
|
2018-05-14 10:08:03 +00:00
|
|
|
Scheme string `json:"scheme,omitempty"`
|
2018-04-16 09:40:03 +00:00
|
|
|
Path string `json:"path,omitempty"`
|
|
|
|
Port int `json:"port,omitempty"`
|
|
|
|
Interval string `json:"interval,omitempty"`
|
2018-09-27 18:16:03 +00:00
|
|
|
Timeout string `json:"timeout,omitempty"`
|
2018-05-14 10:08:03 +00:00
|
|
|
Hostname string `json:"hostname,omitempty"`
|
|
|
|
Headers map[string]string `json:"headers,omitempty"`
|
2016-11-26 18:48:49 +00:00
|
|
|
}
|
|
|
|
|
2015-11-01 18:15:05 +00:00
|
|
|
// Server holds server configuration.
|
2015-11-01 15:35:01 +00:00
|
|
|
type Server struct {
|
|
|
|
URL string `json:"url,omitempty"`
|
2016-06-06 20:30:23 +00:00
|
|
|
Weight int `json:"weight"`
|
2015-11-01 15:35:01 +00:00
|
|
|
}
|
|
|
|
|
2015-11-01 18:15:05 +00:00
|
|
|
// Route holds route configuration.
|
2015-11-01 15:35:01 +00:00
|
|
|
type Route struct {
|
2016-03-27 00:05:17 +00:00
|
|
|
Rule string `json:"rule,omitempty"`
|
2015-11-01 15:35:01 +00:00
|
|
|
}
|
|
|
|
|
2018-02-26 14:34:04 +00:00
|
|
|
// ServerRoute holds ServerRoute configuration.
|
|
|
|
type ServerRoute struct {
|
|
|
|
Route *mux.Route
|
|
|
|
StripPrefixes []string
|
|
|
|
StripPrefixesRegex []string
|
|
|
|
AddPrefix string
|
|
|
|
ReplacePath string
|
|
|
|
ReplacePathRegex string
|
|
|
|
}
|
|
|
|
|
2018-03-23 16:40:04 +00:00
|
|
|
// ErrorPage holds custom error page configuration
|
2017-06-30 23:04:18 +00:00
|
|
|
type ErrorPage struct {
|
|
|
|
Status []string `json:"status,omitempty"`
|
|
|
|
Backend string `json:"backend,omitempty"`
|
|
|
|
Query string `json:"query,omitempty"`
|
|
|
|
}
|
|
|
|
|
2017-09-09 11:36:03 +00:00
|
|
|
// Rate holds a rate limiting configuration for a specific time period
|
|
|
|
type Rate struct {
|
2018-07-31 17:28:03 +00:00
|
|
|
Period parse.Duration `json:"period,omitempty"`
|
2017-09-09 11:36:03 +00:00
|
|
|
Average int64 `json:"average,omitempty"`
|
|
|
|
Burst int64 `json:"burst,omitempty"`
|
|
|
|
}
|
|
|
|
|
|
|
|
// RateLimit holds a rate limiting configuration for a given frontend
|
|
|
|
type RateLimit struct {
|
|
|
|
RateSet map[string]*Rate `json:"rateset,omitempty"`
|
|
|
|
ExtractorFunc string `json:"extractorFunc,omitempty"`
|
|
|
|
}
|
|
|
|
|
2017-06-13 00:48:21 +00:00
|
|
|
// Headers holds the custom header configuration
|
|
|
|
type Headers struct {
|
2018-05-14 09:44:03 +00:00
|
|
|
CustomRequestHeaders map[string]string `json:"customRequestHeaders,omitempty"`
|
|
|
|
CustomResponseHeaders map[string]string `json:"customResponseHeaders,omitempty"`
|
|
|
|
|
2017-06-13 00:48:21 +00:00
|
|
|
AllowedHosts []string `json:"allowedHosts,omitempty"`
|
|
|
|
HostsProxyHeaders []string `json:"hostsProxyHeaders,omitempty"`
|
|
|
|
SSLRedirect bool `json:"sslRedirect,omitempty"`
|
|
|
|
SSLTemporaryRedirect bool `json:"sslTemporaryRedirect,omitempty"`
|
|
|
|
SSLHost string `json:"sslHost,omitempty"`
|
|
|
|
SSLProxyHeaders map[string]string `json:"sslProxyHeaders,omitempty"`
|
2018-05-14 09:44:03 +00:00
|
|
|
SSLForceHost bool `json:"sslForceHost,omitempty"`
|
2017-06-13 00:48:21 +00:00
|
|
|
STSSeconds int64 `json:"stsSeconds,omitempty"`
|
|
|
|
STSIncludeSubdomains bool `json:"stsIncludeSubdomains,omitempty"`
|
|
|
|
STSPreload bool `json:"stsPreload,omitempty"`
|
|
|
|
ForceSTSHeader bool `json:"forceSTSHeader,omitempty"`
|
|
|
|
FrameDeny bool `json:"frameDeny,omitempty"`
|
|
|
|
CustomFrameOptionsValue string `json:"customFrameOptionsValue,omitempty"`
|
|
|
|
ContentTypeNosniff bool `json:"contentTypeNosniff,omitempty"`
|
|
|
|
BrowserXSSFilter bool `json:"browserXssFilter,omitempty"`
|
2018-03-02 13:24:03 +00:00
|
|
|
CustomBrowserXSSValue string `json:"customBrowserXSSValue,omitempty"`
|
2017-06-13 00:48:21 +00:00
|
|
|
ContentSecurityPolicy string `json:"contentSecurityPolicy,omitempty"`
|
|
|
|
PublicKey string `json:"publicKey,omitempty"`
|
|
|
|
ReferrerPolicy string `json:"referrerPolicy,omitempty"`
|
|
|
|
IsDevelopment bool `json:"isDevelopment,omitempty"`
|
|
|
|
}
|
|
|
|
|
|
|
|
// HasCustomHeadersDefined checks to see if any of the custom header elements have been set
|
2018-01-02 09:10:04 +00:00
|
|
|
func (h *Headers) HasCustomHeadersDefined() bool {
|
|
|
|
return h != nil && (len(h.CustomResponseHeaders) != 0 ||
|
|
|
|
len(h.CustomRequestHeaders) != 0)
|
2017-06-13 00:48:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// HasSecureHeadersDefined checks to see if any of the secure header elements have been set
|
2018-01-02 09:10:04 +00:00
|
|
|
func (h *Headers) HasSecureHeadersDefined() bool {
|
|
|
|
return h != nil && (len(h.AllowedHosts) != 0 ||
|
2017-06-13 00:48:21 +00:00
|
|
|
len(h.HostsProxyHeaders) != 0 ||
|
|
|
|
h.SSLRedirect ||
|
|
|
|
h.SSLTemporaryRedirect ||
|
2018-05-14 09:44:03 +00:00
|
|
|
h.SSLForceHost ||
|
2017-06-13 00:48:21 +00:00
|
|
|
h.SSLHost != "" ||
|
|
|
|
len(h.SSLProxyHeaders) != 0 ||
|
|
|
|
h.STSSeconds != 0 ||
|
|
|
|
h.STSIncludeSubdomains ||
|
|
|
|
h.STSPreload ||
|
|
|
|
h.ForceSTSHeader ||
|
|
|
|
h.FrameDeny ||
|
|
|
|
h.CustomFrameOptionsValue != "" ||
|
|
|
|
h.ContentTypeNosniff ||
|
|
|
|
h.BrowserXSSFilter ||
|
2018-03-02 13:24:03 +00:00
|
|
|
h.CustomBrowserXSSValue != "" ||
|
2017-06-13 00:48:21 +00:00
|
|
|
h.ContentSecurityPolicy != "" ||
|
|
|
|
h.PublicKey != "" ||
|
|
|
|
h.ReferrerPolicy != "" ||
|
2018-01-02 09:10:04 +00:00
|
|
|
h.IsDevelopment)
|
2017-06-13 00:48:21 +00:00
|
|
|
}
|
|
|
|
|
2015-11-01 18:15:05 +00:00
|
|
|
// Frontend holds frontend configuration.
|
2015-11-01 15:35:01 +00:00
|
|
|
type Frontend struct {
|
2018-09-07 16:19:32 +00:00
|
|
|
EntryPoints []string `json:"entryPoints,omitempty" hash:"ignore"`
|
|
|
|
Backend string `json:"backend,omitempty"`
|
|
|
|
Routes map[string]Route `json:"routes,omitempty" hash:"ignore"`
|
|
|
|
PassHostHeader bool `json:"passHostHeader,omitempty"`
|
|
|
|
PassTLSCert bool `json:"passTLSCert,omitempty"` // Deprecated use PassTLSClientCert instead
|
|
|
|
PassTLSClientCert *TLSClientHeaders `json:"passTLSClientCert,omitempty"`
|
|
|
|
Priority int `json:"priority"`
|
|
|
|
WhiteList *WhiteList `json:"whiteList,omitempty"`
|
|
|
|
Headers *Headers `json:"headers,omitempty"`
|
|
|
|
Errors map[string]*ErrorPage `json:"errors,omitempty"`
|
|
|
|
RateLimit *RateLimit `json:"ratelimit,omitempty"`
|
|
|
|
Redirect *Redirect `json:"redirect,omitempty"`
|
|
|
|
Auth *Auth `json:"auth,omitempty"`
|
2017-12-15 10:48:03 +00:00
|
|
|
}
|
|
|
|
|
2018-06-06 15:56:03 +00:00
|
|
|
// Hash returns the hash value of a Frontend struct.
|
|
|
|
func (f *Frontend) Hash() (string, error) {
|
|
|
|
hash, err := hashstructure.Hash(f, nil)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
|
|
|
return strconv.FormatUint(hash, 10), nil
|
|
|
|
}
|
|
|
|
|
2017-12-15 10:48:03 +00:00
|
|
|
// Redirect configures a redirection of an entry point to another, or to an URL
|
|
|
|
type Redirect struct {
|
|
|
|
EntryPoint string `json:"entryPoint,omitempty"`
|
|
|
|
Regex string `json:"regex,omitempty"`
|
|
|
|
Replacement string `json:"replacement,omitempty"`
|
2018-01-31 18:10:04 +00:00
|
|
|
Permanent bool `json:"permanent,omitempty"`
|
2015-11-01 15:35:01 +00:00
|
|
|
}
|
|
|
|
|
2015-11-01 18:15:05 +00:00
|
|
|
// LoadBalancerMethod holds the method of load balancing to use.
|
2015-11-01 15:35:01 +00:00
|
|
|
type LoadBalancerMethod uint8
|
|
|
|
|
|
|
|
const (
|
|
|
|
// Wrr (default) = Weighted Round Robin
|
|
|
|
Wrr LoadBalancerMethod = iota
|
|
|
|
// Drr = Dynamic Round Robin
|
|
|
|
Drr
|
|
|
|
)
|
|
|
|
|
|
|
|
var loadBalancerMethodNames = []string{
|
|
|
|
"Wrr",
|
|
|
|
"Drr",
|
|
|
|
}
|
|
|
|
|
2015-11-01 18:15:05 +00:00
|
|
|
// NewLoadBalancerMethod create a new LoadBalancerMethod from a given LoadBalancer.
|
2015-11-01 15:35:01 +00:00
|
|
|
func NewLoadBalancerMethod(loadBalancer *LoadBalancer) (LoadBalancerMethod, error) {
|
2018-05-22 07:20:03 +00:00
|
|
|
if loadBalancer == nil {
|
|
|
|
return Wrr, errors.New("no load-balancer defined, fallback to 'wrr' method")
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(loadBalancer.Method) == 0 {
|
|
|
|
return Wrr, errors.New("no load-balancing method defined, fallback to 'wrr' method")
|
|
|
|
}
|
|
|
|
|
|
|
|
for i, name := range loadBalancerMethodNames {
|
|
|
|
if strings.EqualFold(name, loadBalancer.Method) {
|
|
|
|
return LoadBalancerMethod(i), nil
|
2015-11-01 15:35:01 +00:00
|
|
|
}
|
|
|
|
}
|
2018-05-22 07:20:03 +00:00
|
|
|
|
|
|
|
return Wrr, fmt.Errorf("invalid load-balancing method %q, fallback to 'wrr' method", loadBalancer.Method)
|
2015-11-01 15:35:01 +00:00
|
|
|
}
|
|
|
|
|
2017-08-25 14:10:03 +00:00
|
|
|
// Configurations is for currentConfigurations Map
|
|
|
|
type Configurations map[string]*Configuration
|
|
|
|
|
2015-11-01 18:15:05 +00:00
|
|
|
// Configuration of a provider.
|
2015-11-01 15:35:01 +00:00
|
|
|
type Configuration struct {
|
2018-01-23 15:30:07 +00:00
|
|
|
Backends map[string]*Backend `json:"backends,omitempty"`
|
|
|
|
Frontends map[string]*Frontend `json:"frontends,omitempty"`
|
2018-07-26 10:42:03 +00:00
|
|
|
TLS []*traefiktls.Configuration `json:"-"`
|
2015-11-01 15:35:01 +00:00
|
|
|
}
|
|
|
|
|
2015-11-01 18:15:05 +00:00
|
|
|
// ConfigMessage hold configuration information exchanged between parts of traefik.
|
2015-11-01 15:35:01 +00:00
|
|
|
type ConfigMessage struct {
|
|
|
|
ProviderName string
|
|
|
|
Configuration *Configuration
|
|
|
|
}
|
2016-05-30 13:05:58 +00:00
|
|
|
|
2017-10-02 08:32:02 +00:00
|
|
|
// Constraint hold a parsed constraint expression
|
2016-05-30 13:05:58 +00:00
|
|
|
type Constraint struct {
|
2017-10-02 08:32:02 +00:00
|
|
|
Key string `export:"true"`
|
2016-05-30 13:05:58 +00:00
|
|
|
// MustMatch is true if operator is "==" or false if operator is "!="
|
2017-10-02 08:32:02 +00:00
|
|
|
MustMatch bool `export:"true"`
|
2016-05-31 07:54:42 +00:00
|
|
|
// TODO: support regex
|
2017-10-02 08:32:02 +00:00
|
|
|
Regex string `export:"true"`
|
2016-05-30 13:05:58 +00:00
|
|
|
}
|
|
|
|
|
2016-05-20 15:17:38 +00:00
|
|
|
// NewConstraint receive a string and return a *Constraint, after checking syntax and parsing the constraint expression
|
2016-05-30 13:05:58 +00:00
|
|
|
func NewConstraint(exp string) (*Constraint, error) {
|
|
|
|
sep := ""
|
|
|
|
constraint := &Constraint{}
|
|
|
|
|
|
|
|
if strings.Contains(exp, "==") {
|
|
|
|
sep = "=="
|
|
|
|
constraint.MustMatch = true
|
|
|
|
} else if strings.Contains(exp, "!=") {
|
|
|
|
sep = "!="
|
|
|
|
constraint.MustMatch = false
|
|
|
|
} else {
|
2017-10-02 08:32:02 +00:00
|
|
|
return nil, errors.New("constraint expression missing valid operator: '==' or '!='")
|
2016-05-30 13:05:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
kv := strings.SplitN(exp, sep, 2)
|
|
|
|
if len(kv) == 2 {
|
|
|
|
// At the moment, it only supports tags
|
|
|
|
if kv[0] != "tag" {
|
2017-10-02 08:32:02 +00:00
|
|
|
return nil, errors.New("constraint must be tag-based. Syntax: tag==us-*")
|
2016-05-30 13:05:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
constraint.Key = kv[0]
|
|
|
|
constraint.Regex = kv[1]
|
|
|
|
return constraint, nil
|
|
|
|
}
|
|
|
|
|
2017-10-02 08:32:02 +00:00
|
|
|
return nil, fmt.Errorf("incorrect constraint expression: %s", exp)
|
2016-05-30 13:05:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Constraint) String() string {
|
|
|
|
if c.MustMatch {
|
|
|
|
return c.Key + "==" + c.Regex
|
|
|
|
}
|
|
|
|
return c.Key + "!=" + c.Regex
|
|
|
|
}
|
|
|
|
|
2016-11-09 18:27:04 +00:00
|
|
|
var _ encoding.TextUnmarshaler = (*Constraint)(nil)
|
|
|
|
|
2016-09-20 14:56:29 +00:00
|
|
|
// UnmarshalText define how unmarshal in TOML parsing
|
|
|
|
func (c *Constraint) UnmarshalText(text []byte) error {
|
|
|
|
constraint, err := NewConstraint(string(text))
|
2016-11-09 18:27:04 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
c.Key = constraint.Key
|
|
|
|
c.MustMatch = constraint.MustMatch
|
|
|
|
c.Regex = constraint.Regex
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
var _ encoding.TextMarshaler = (*Constraint)(nil)
|
|
|
|
|
|
|
|
// MarshalText encodes the receiver into UTF-8-encoded text and returns the result.
|
|
|
|
func (c *Constraint) MarshalText() (text []byte, err error) {
|
|
|
|
return []byte(c.String()), nil
|
2016-09-20 14:56:29 +00:00
|
|
|
}
|
|
|
|
|
2016-05-20 15:17:38 +00:00
|
|
|
// MatchConstraintWithAtLeastOneTag tests a constraint for one single service
|
2016-05-30 13:05:58 +00:00
|
|
|
func (c *Constraint) MatchConstraintWithAtLeastOneTag(tags []string) bool {
|
|
|
|
for _, tag := range tags {
|
|
|
|
if glob.Glob(c.Regex, tag) {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
2016-06-02 13:17:04 +00:00
|
|
|
|
2018-03-05 19:54:04 +00:00
|
|
|
// Set []*Constraint
|
2016-06-02 13:17:04 +00:00
|
|
|
func (cs *Constraints) Set(str string) error {
|
|
|
|
exps := strings.Split(str, ",")
|
|
|
|
if len(exps) == 0 {
|
2017-10-02 08:32:02 +00:00
|
|
|
return fmt.Errorf("bad Constraint format: %s", str)
|
2016-06-02 13:17:04 +00:00
|
|
|
}
|
|
|
|
for _, exp := range exps {
|
|
|
|
constraint, err := NewConstraint(exp)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2016-11-09 18:27:04 +00:00
|
|
|
*cs = append(*cs, constraint)
|
2016-06-02 13:17:04 +00:00
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Constraints holds a Constraint parser
|
2016-11-09 18:27:04 +00:00
|
|
|
type Constraints []*Constraint
|
2016-06-02 13:17:04 +00:00
|
|
|
|
2018-03-05 19:54:04 +00:00
|
|
|
// Get []*Constraint
|
2016-11-09 18:27:04 +00:00
|
|
|
func (cs *Constraints) Get() interface{} { return []*Constraint(*cs) }
|
2016-06-02 13:17:04 +00:00
|
|
|
|
2018-03-05 19:54:04 +00:00
|
|
|
// String returns []*Constraint in string
|
2016-06-02 13:17:04 +00:00
|
|
|
func (cs *Constraints) String() string { return fmt.Sprintf("%+v", *cs) }
|
|
|
|
|
2018-03-05 19:54:04 +00:00
|
|
|
// SetValue sets []*Constraint into the parser
|
2016-06-02 13:17:04 +00:00
|
|
|
func (cs *Constraints) SetValue(val interface{}) {
|
2017-12-18 08:14:03 +00:00
|
|
|
*cs = val.(Constraints)
|
2016-06-02 13:17:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Type exports the Constraints type as a string
|
|
|
|
func (cs *Constraints) Type() string {
|
2017-08-18 00:18:02 +00:00
|
|
|
return "constraint"
|
2016-06-02 13:17:04 +00:00
|
|
|
}
|
2016-07-20 22:29:00 +00:00
|
|
|
|
2016-08-16 17:13:18 +00:00
|
|
|
// Store holds KV store cluster config
|
|
|
|
type Store struct {
|
|
|
|
store.Store
|
2017-10-02 08:32:02 +00:00
|
|
|
// like this "prefix" (without the /)
|
|
|
|
Prefix string `export:"true"`
|
2016-08-16 17:13:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Cluster holds cluster config
|
|
|
|
type Cluster struct {
|
2017-10-02 08:32:02 +00:00
|
|
|
Node string `description:"Node name" export:"true"`
|
|
|
|
Store *Store `export:"true"`
|
2016-08-16 17:13:18 +00:00
|
|
|
}
|
|
|
|
|
2016-07-20 22:29:00 +00:00
|
|
|
// Auth holds authentication configuration (BASIC, DIGEST, users)
|
|
|
|
type Auth struct {
|
2018-07-17 10:02:03 +00:00
|
|
|
Basic *Basic `json:"basic,omitempty" export:"true"`
|
|
|
|
Digest *Digest `json:"digest,omitempty" export:"true"`
|
|
|
|
Forward *Forward `json:"forward,omitempty" export:"true"`
|
|
|
|
HeaderField string `json:"headerField,omitempty" export:"true"`
|
2016-07-20 22:29:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Users authentication users
|
|
|
|
type Users []string
|
|
|
|
|
|
|
|
// Basic HTTP basic authentication
|
|
|
|
type Basic struct {
|
2018-10-04 14:46:03 +00:00
|
|
|
Realm string `json:"realm,omitempty"`
|
2018-07-17 10:02:03 +00:00
|
|
|
Users `json:"users,omitempty" mapstructure:","`
|
|
|
|
UsersFile string `json:"usersFile,omitempty"`
|
|
|
|
RemoveHeader bool `json:"removeHeader,omitempty"`
|
2016-07-20 22:29:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Digest HTTP authentication
|
|
|
|
type Digest struct {
|
2018-07-17 10:02:03 +00:00
|
|
|
Users `json:"users,omitempty" mapstructure:","`
|
|
|
|
UsersFile string `json:"usersFile,omitempty"`
|
|
|
|
RemoveHeader bool `json:"removeHeader,omitempty"`
|
2016-07-20 22:29:00 +00:00
|
|
|
}
|
2016-10-14 14:04:09 +00:00
|
|
|
|
2017-08-25 16:22:03 +00:00
|
|
|
// Forward authentication
|
|
|
|
type Forward struct {
|
2018-07-17 10:02:03 +00:00
|
|
|
Address string `description:"Authentication server address" json:"address,omitempty"`
|
|
|
|
TLS *ClientTLS `description:"Enable TLS support" json:"tls,omitempty" export:"true"`
|
|
|
|
TrustForwardHeader bool `description:"Trust X-Forwarded-* headers" json:"trustForwardHeader,omitempty" export:"true"`
|
|
|
|
AuthResponseHeaders []string `description:"Headers to be forwarded from auth response" json:"authResponseHeaders,omitempty"`
|
2017-08-25 16:22:03 +00:00
|
|
|
}
|
|
|
|
|
2016-10-14 14:04:09 +00:00
|
|
|
// CanonicalDomain returns a lower case domain with trim space
|
|
|
|
func CanonicalDomain(domain string) string {
|
|
|
|
return strings.ToLower(strings.TrimSpace(domain))
|
|
|
|
}
|
2016-10-21 08:36:07 +00:00
|
|
|
|
|
|
|
// Statistics provides options for monitoring request and response stats
|
|
|
|
type Statistics struct {
|
2017-10-02 08:32:02 +00:00
|
|
|
RecentErrors int `description:"Number of recent errors logged" export:"true"`
|
2016-10-21 08:36:07 +00:00
|
|
|
}
|
2017-01-12 13:34:54 +00:00
|
|
|
|
|
|
|
// Metrics provides options to expose and send Traefik metrics to different third party monitoring systems
|
|
|
|
type Metrics struct {
|
2017-10-02 08:32:02 +00:00
|
|
|
Prometheus *Prometheus `description:"Prometheus metrics exporter type" export:"true"`
|
|
|
|
Datadog *Datadog `description:"DataDog metrics exporter type" export:"true"`
|
|
|
|
StatsD *Statsd `description:"StatsD metrics exporter type" export:"true"`
|
2017-11-08 14:14:03 +00:00
|
|
|
InfluxDB *InfluxDB `description:"InfluxDB metrics exporter type"`
|
2017-01-12 13:34:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Prometheus can contain specific configuration used by the Prometheus Metrics exporter
|
|
|
|
type Prometheus struct {
|
2017-11-09 15:12:04 +00:00
|
|
|
Buckets Buckets `description:"Buckets for latency metrics" export:"true"`
|
|
|
|
EntryPoint string `description:"EntryPoint" export:"true"`
|
2017-01-12 13:34:54 +00:00
|
|
|
}
|
|
|
|
|
2017-07-20 22:26:43 +00:00
|
|
|
// Datadog contains address and metrics pushing interval configuration
|
|
|
|
type Datadog struct {
|
2017-08-26 10:12:44 +00:00
|
|
|
Address string `description:"DataDog's address"`
|
2017-10-02 08:32:02 +00:00
|
|
|
PushInterval string `description:"DataDog push interval" export:"true"`
|
2017-07-20 22:26:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Statsd contains address and metrics pushing interval configuration
|
|
|
|
type Statsd struct {
|
|
|
|
Address string `description:"StatsD address"`
|
2017-11-08 14:14:03 +00:00
|
|
|
PushInterval string `description:"StatsD push interval" export:"true"`
|
|
|
|
}
|
|
|
|
|
2018-07-11 15:50:03 +00:00
|
|
|
// InfluxDB contains address, login and metrics pushing interval configuration
|
2017-11-08 14:14:03 +00:00
|
|
|
type InfluxDB struct {
|
2018-05-29 20:58:03 +00:00
|
|
|
Address string `description:"InfluxDB address"`
|
|
|
|
Protocol string `description:"InfluxDB address protocol (udp or http)"`
|
|
|
|
PushInterval string `description:"InfluxDB push interval" export:"true"`
|
|
|
|
Database string `description:"InfluxDB database used when protocol is http" export:"true"`
|
|
|
|
RetentionPolicy string `description:"InfluxDB retention policy used when protocol is http" export:"true"`
|
2018-07-11 15:50:03 +00:00
|
|
|
Username string `description:"InfluxDB username (only with http)" export:"true"`
|
|
|
|
Password string `description:"InfluxDB password (only with http)" export:"true"`
|
2017-07-20 22:26:43 +00:00
|
|
|
}
|
|
|
|
|
2017-01-12 13:34:54 +00:00
|
|
|
// Buckets holds Prometheus Buckets
|
|
|
|
type Buckets []float64
|
|
|
|
|
2018-03-05 19:54:04 +00:00
|
|
|
// Set adds strings elem into the the parser
|
|
|
|
// it splits str on "," and ";" and apply ParseFloat to string
|
2017-01-12 13:34:54 +00:00
|
|
|
func (b *Buckets) Set(str string) error {
|
|
|
|
fargs := func(c rune) bool {
|
|
|
|
return c == ',' || c == ';'
|
|
|
|
}
|
|
|
|
// get function
|
|
|
|
slice := strings.FieldsFunc(str, fargs)
|
|
|
|
for _, bucket := range slice {
|
|
|
|
bu, err := strconv.ParseFloat(bucket, 64)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
*b = append(*b, bu)
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2018-03-05 19:54:04 +00:00
|
|
|
// Get []float64
|
2017-12-18 08:14:03 +00:00
|
|
|
func (b *Buckets) Get() interface{} { return *b }
|
2017-01-12 13:34:54 +00:00
|
|
|
|
2018-03-05 19:54:04 +00:00
|
|
|
// String return slice in a string
|
2017-01-12 13:34:54 +00:00
|
|
|
func (b *Buckets) String() string { return fmt.Sprintf("%v", *b) }
|
|
|
|
|
2018-03-05 19:54:04 +00:00
|
|
|
// SetValue sets []float64 into the parser
|
2017-01-12 13:34:54 +00:00
|
|
|
func (b *Buckets) SetValue(val interface{}) {
|
2017-12-18 08:14:03 +00:00
|
|
|
*b = val.(Buckets)
|
2017-01-12 13:34:54 +00:00
|
|
|
}
|
2017-05-25 11:25:53 +00:00
|
|
|
|
2017-08-25 16:22:03 +00:00
|
|
|
// ClientTLS holds TLS specific configurations as client
|
|
|
|
// CA, Cert and Key can be either path or file contents
|
|
|
|
type ClientTLS struct {
|
2018-07-17 10:02:03 +00:00
|
|
|
CA string `description:"TLS CA" json:"ca,omitempty"`
|
|
|
|
CAOptional bool `description:"TLS CA.Optional" json:"caOptional,omitempty"`
|
|
|
|
Cert string `description:"TLS cert" json:"cert,omitempty"`
|
|
|
|
Key string `description:"TLS key" json:"key,omitempty"`
|
|
|
|
InsecureSkipVerify bool `description:"TLS insecure skip verify" json:"insecureSkipVerify,omitempty"`
|
2017-08-25 16:22:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// CreateTLSConfig creates a TLS config from ClientTLS structures
|
|
|
|
func (clientTLS *ClientTLS) CreateTLSConfig() (*tls.Config, error) {
|
|
|
|
var err error
|
|
|
|
if clientTLS == nil {
|
|
|
|
log.Warnf("clientTLS is nil")
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
caPool := x509.NewCertPool()
|
2017-11-10 09:30:04 +00:00
|
|
|
clientAuth := tls.NoClientCert
|
2017-08-25 16:22:03 +00:00
|
|
|
if clientTLS.CA != "" {
|
|
|
|
var ca []byte
|
|
|
|
if _, errCA := os.Stat(clientTLS.CA); errCA == nil {
|
|
|
|
ca, err = ioutil.ReadFile(clientTLS.CA)
|
|
|
|
if err != nil {
|
2018-03-14 13:12:04 +00:00
|
|
|
return nil, fmt.Errorf("failed to read CA. %s", err)
|
2017-08-25 16:22:03 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
ca = []byte(clientTLS.CA)
|
|
|
|
}
|
2018-10-01 15:24:03 +00:00
|
|
|
if !caPool.AppendCertsFromPEM(ca) {
|
|
|
|
return nil, fmt.Errorf("failed to parse CA")
|
|
|
|
}
|
2017-11-10 09:30:04 +00:00
|
|
|
if clientTLS.CAOptional {
|
|
|
|
clientAuth = tls.VerifyClientCertIfGiven
|
|
|
|
} else {
|
|
|
|
clientAuth = tls.RequireAndVerifyClientCert
|
|
|
|
}
|
2017-08-25 16:22:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
cert := tls.Certificate{}
|
|
|
|
_, errKeyIsFile := os.Stat(clientTLS.Key)
|
|
|
|
|
|
|
|
if !clientTLS.InsecureSkipVerify && (len(clientTLS.Cert) == 0 || len(clientTLS.Key) == 0) {
|
|
|
|
return nil, fmt.Errorf("TLS Certificate or Key file must be set when TLS configuration is created")
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(clientTLS.Cert) > 0 && len(clientTLS.Key) > 0 {
|
|
|
|
if _, errCertIsFile := os.Stat(clientTLS.Cert); errCertIsFile == nil {
|
|
|
|
if errKeyIsFile == nil {
|
|
|
|
cert, err = tls.LoadX509KeyPair(clientTLS.Cert, clientTLS.Key)
|
|
|
|
if err != nil {
|
2018-03-14 13:12:04 +00:00
|
|
|
return nil, fmt.Errorf("failed to load TLS keypair: %v", err)
|
2017-08-25 16:22:03 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
return nil, fmt.Errorf("tls cert is a file, but tls key is not")
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if errKeyIsFile != nil {
|
|
|
|
cert, err = tls.X509KeyPair([]byte(clientTLS.Cert), []byte(clientTLS.Key))
|
|
|
|
if err != nil {
|
2018-03-14 13:12:04 +00:00
|
|
|
return nil, fmt.Errorf("failed to load TLS keypair: %v", err)
|
2017-08-25 16:22:03 +00:00
|
|
|
|
|
|
|
}
|
|
|
|
} else {
|
2018-03-14 13:12:04 +00:00
|
|
|
return nil, fmt.Errorf("TLS key is a file, but tls cert is not")
|
2017-08-25 16:22:03 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
TLSConfig := &tls.Config{
|
|
|
|
Certificates: []tls.Certificate{cert},
|
|
|
|
RootCAs: caPool,
|
|
|
|
InsecureSkipVerify: clientTLS.InsecureSkipVerify,
|
2017-11-10 09:30:04 +00:00
|
|
|
ClientAuth: clientAuth,
|
2017-08-25 16:22:03 +00:00
|
|
|
}
|
|
|
|
return TLSConfig, nil
|
|
|
|
}
|
2018-03-14 13:12:04 +00:00
|
|
|
|
|
|
|
// HTTPCodeRanges holds HTTP code ranges
|
|
|
|
type HTTPCodeRanges [][2]int
|
|
|
|
|
2018-03-23 08:28:03 +00:00
|
|
|
// NewHTTPCodeRanges creates HTTPCodeRanges from a given []string.
|
2018-03-14 13:12:04 +00:00
|
|
|
// Break out the http status code ranges into a low int and high int
|
|
|
|
// for ease of use at runtime
|
|
|
|
func NewHTTPCodeRanges(strBlocks []string) (HTTPCodeRanges, error) {
|
|
|
|
var blocks HTTPCodeRanges
|
|
|
|
for _, block := range strBlocks {
|
|
|
|
codes := strings.Split(block, "-")
|
2018-03-23 16:40:04 +00:00
|
|
|
// if only a single HTTP code was configured, assume the best and create the correct configuration on the user's behalf
|
2018-03-14 13:12:04 +00:00
|
|
|
if len(codes) == 1 {
|
|
|
|
codes = append(codes, codes[0])
|
|
|
|
}
|
|
|
|
lowCode, err := strconv.Atoi(codes[0])
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
highCode, err := strconv.Atoi(codes[1])
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
blocks = append(blocks, [2]int{lowCode, highCode})
|
|
|
|
}
|
|
|
|
return blocks, nil
|
|
|
|
}
|
2018-03-23 08:28:03 +00:00
|
|
|
|
|
|
|
// Contains tests whether the passed status code is within
|
|
|
|
// one of its HTTP code ranges.
|
|
|
|
func (h HTTPCodeRanges) Contains(statusCode int) bool {
|
|
|
|
for _, block := range h {
|
|
|
|
if statusCode >= block[0] && statusCode <= block[1] {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
2018-08-24 14:20:03 +00:00
|
|
|
|
|
|
|
// IPStrategy Configuration to choose the IP selection strategy.
|
|
|
|
type IPStrategy struct {
|
|
|
|
Depth int `json:"depth,omitempty" export:"true"`
|
|
|
|
ExcludedIPs []string `json:"excludedIPs,omitempty"`
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get an IP selection strategy
|
|
|
|
// if nil return the RemoteAddr strategy
|
|
|
|
// else return a strategy base on the configuration using the X-Forwarded-For Header.
|
|
|
|
// Depth override the ExcludedIPs
|
|
|
|
func (s *IPStrategy) Get() (ip.Strategy, error) {
|
|
|
|
if s == nil {
|
|
|
|
return &ip.RemoteAddrStrategy{}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
if s.Depth > 0 {
|
|
|
|
return &ip.DepthStrategy{
|
|
|
|
Depth: s.Depth,
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(s.ExcludedIPs) > 0 {
|
|
|
|
checker, err := ip.NewChecker(s.ExcludedIPs)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return &ip.CheckerStrategy{
|
|
|
|
Checker: checker,
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
return &ip.RemoteAddrStrategy{}, nil
|
|
|
|
}
|
2018-09-07 16:19:32 +00:00
|
|
|
|
2018-08-29 09:36:03 +00:00
|
|
|
// TLSClientHeaders holds the TLS client cert headers configuration.
|
|
|
|
type TLSClientHeaders struct {
|
|
|
|
PEM bool `description:"Enable header with escaped client pem" json:"pem"`
|
|
|
|
Infos *TLSClientCertificateInfos `description:"Enable header with configured client cert infos" json:"infos,omitempty"`
|
|
|
|
}
|
|
|
|
|
|
|
|
// TLSClientCertificateInfos holds the client TLS certificate infos configuration
|
|
|
|
type TLSClientCertificateInfos struct {
|
|
|
|
NotAfter bool `description:"Add NotAfter info in header" json:"notAfter"`
|
|
|
|
NotBefore bool `description:"Add NotBefore info in header" json:"notBefore"`
|
|
|
|
Subject *TLSCLientCertificateSubjectInfos `description:"Add Subject info in header" json:"subject,omitempty"`
|
|
|
|
Sans bool `description:"Add Sans info in header" json:"sans"`
|
|
|
|
}
|
|
|
|
|
|
|
|
// TLSCLientCertificateSubjectInfos holds the client TLS certificate subject infos configuration
|
|
|
|
type TLSCLientCertificateSubjectInfos struct {
|
|
|
|
Country bool `description:"Add Country info in header" json:"country"`
|
|
|
|
Province bool `description:"Add Province info in header" json:"province"`
|
|
|
|
Locality bool `description:"Add Locality info in header" json:"locality"`
|
|
|
|
Organization bool `description:"Add Organization info in header" json:"organization"`
|
|
|
|
CommonName bool `description:"Add CommonName info in header" json:"commonName"`
|
|
|
|
SerialNumber bool `description:"Add SerialNumber info in header" json:"serialNumber"`
|
|
|
|
}
|