1
0
Fork 0

Move code to pkg

This commit is contained in:
Ludovic Fernandez 2019-03-15 09:42:03 +01:00 committed by Traefiker Bot
parent bd4c822670
commit f1b085fa36
465 changed files with 656 additions and 680 deletions

230
pkg/config/dyn_config.go Normal file
View file

@ -0,0 +1,230 @@
package config
import (
"crypto/tls"
"crypto/x509"
"fmt"
"io/ioutil"
"os"
"reflect"
traefiktls "github.com/containous/traefik/pkg/tls"
)
// Router holds the router configuration.
type Router struct {
EntryPoints []string `json:"entryPoints"`
Middlewares []string `json:"middlewares,omitempty" toml:",omitempty"`
Service string `json:"service,omitempty" toml:",omitempty"`
Rule string `json:"rule,omitempty" toml:",omitempty"`
Priority int `json:"priority,omitempty" toml:"priority,omitzero"`
TLS *RouterTLSConfig `json:"tls,omitempty" toml:"tls,omitzero" label:"allowEmpty"`
}
// RouterTLSConfig holds the TLS configuration for a router
type RouterTLSConfig struct{}
// TCPRouter holds the router configuration.
type TCPRouter struct {
EntryPoints []string `json:"entryPoints"`
Service string `json:"service,omitempty" toml:",omitempty"`
Rule string `json:"rule,omitempty" toml:",omitempty"`
TLS *RouterTCPTLSConfig `json:"tls,omitempty" toml:"tls,omitzero" label:"allowEmpty"`
}
// RouterTCPTLSConfig holds the TLS configuration for a router
type RouterTCPTLSConfig struct {
Passthrough bool `json:"passthrough" toml:"passthrough,omitzero"`
}
// LoadBalancerService holds the LoadBalancerService configuration.
type LoadBalancerService struct {
Stickiness *Stickiness `json:"stickiness,omitempty" toml:",omitempty" label:"allowEmpty"`
Servers []Server `json:"servers,omitempty" toml:",omitempty" label-slice-as-struct:"server"`
Method string `json:"method,omitempty" toml:",omitempty"`
HealthCheck *HealthCheck `json:"healthCheck,omitempty" toml:",omitempty"`
PassHostHeader bool `json:"passHostHeader" toml:",omitempty"`
ResponseForwarding *ResponseForwarding `json:"forwardingResponse,omitempty" toml:",omitempty"`
}
// TCPLoadBalancerService holds the LoadBalancerService configuration.
type TCPLoadBalancerService struct {
Servers []TCPServer `json:"servers,omitempty" toml:",omitempty" label-slice-as-struct:"server"`
Method string `json:"method,omitempty" toml:",omitempty"`
}
// Mergeable tells if the given service is mergeable.
func (l *LoadBalancerService) Mergeable(loadBalancer *LoadBalancerService) bool {
savedServers := l.Servers
defer func() {
l.Servers = savedServers
}()
l.Servers = nil
savedServersLB := loadBalancer.Servers
defer func() {
loadBalancer.Servers = savedServersLB
}()
loadBalancer.Servers = nil
return reflect.DeepEqual(l, loadBalancer)
}
// SetDefaults Default values for a LoadBalancerService.
func (l *LoadBalancerService) SetDefaults() {
l.PassHostHeader = true
l.Method = "wrr"
}
// ResponseForwarding holds configuration for the forward of the response.
type ResponseForwarding struct {
FlushInterval string `json:"flushInterval,omitempty" toml:",omitempty"`
}
// Stickiness holds the stickiness configuration.
type Stickiness struct {
CookieName string `json:"cookieName,omitempty" toml:",omitempty"`
}
// Server holds the server configuration.
type Server struct {
URL string `json:"url" label:"-"`
Scheme string `toml:"-" json:"-"`
Port string `toml:"-" json:"-"`
Weight int `json:"weight"`
}
// TCPServer holds a TCP Server configuration
type TCPServer struct {
Address string `json:"address" label:"-"`
Weight int `json:"weight"`
}
// SetDefaults Default values for a Server.
func (s *Server) SetDefaults() {
s.Weight = 1
s.Scheme = "http"
}
// HealthCheck holds the HealthCheck configuration.
type HealthCheck struct {
Scheme string `json:"scheme,omitempty" toml:",omitempty"`
Path string `json:"path,omitempty" toml:",omitempty"`
Port int `json:"port,omitempty" toml:",omitempty,omitzero"`
// FIXME change string to parse.Duration
Interval string `json:"interval,omitempty" toml:",omitempty"`
// FIXME change string to parse.Duration
Timeout string `json:"timeout,omitempty" toml:",omitempty"`
Hostname string `json:"hostname,omitempty" toml:",omitempty"`
Headers map[string]string `json:"headers,omitempty" toml:",omitempty"`
}
// CreateTLSConfig creates a TLS config from ClientTLS structures.
func (clientTLS *ClientTLS) CreateTLSConfig() (*tls.Config, error) {
if clientTLS == nil {
return nil, nil
}
var err error
caPool := x509.NewCertPool()
clientAuth := tls.NoClientCert
if clientTLS.CA != "" {
var ca []byte
if _, errCA := os.Stat(clientTLS.CA); errCA == nil {
ca, err = ioutil.ReadFile(clientTLS.CA)
if err != nil {
return nil, fmt.Errorf("failed to read CA. %s", err)
}
} else {
ca = []byte(clientTLS.CA)
}
if !caPool.AppendCertsFromPEM(ca) {
return nil, fmt.Errorf("failed to parse CA")
}
if clientTLS.CAOptional {
clientAuth = tls.VerifyClientCertIfGiven
} else {
clientAuth = tls.RequireAndVerifyClientCert
}
}
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 {
return nil, fmt.Errorf("failed to load TLS keypair: %v", err)
}
} 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 {
return nil, fmt.Errorf("failed to load TLS keypair: %v", err)
}
} else {
return nil, fmt.Errorf("TLS key is a file, but tls cert is not")
}
}
}
return &tls.Config{
Certificates: []tls.Certificate{cert},
RootCAs: caPool,
InsecureSkipVerify: clientTLS.InsecureSkipVerify,
ClientAuth: clientAuth,
}, nil
}
// Message holds configuration information exchanged between parts of traefik.
type Message struct {
ProviderName string
Configuration *Configuration
}
// Configuration is the root of the dynamic configuration
type Configuration struct {
HTTP *HTTPConfiguration
TCP *TCPConfiguration
TLS []*traefiktls.Configuration `json:"-" label:"-"`
TLSOptions map[string]traefiktls.TLS
TLSStores map[string]traefiktls.Store
}
// Configurations is for currentConfigurations Map.
type Configurations map[string]*Configuration
// HTTPConfiguration FIXME better name?
type HTTPConfiguration struct {
Routers map[string]*Router `json:"routers,omitempty" toml:",omitempty"`
Middlewares map[string]*Middleware `json:"middlewares,omitempty" toml:",omitempty"`
Services map[string]*Service `json:"services,omitempty" toml:",omitempty"`
}
// TCPConfiguration FIXME better name?
type TCPConfiguration struct {
Routers map[string]*TCPRouter `json:"routers,omitempty" toml:",omitempty"`
Services map[string]*TCPService `json:"services,omitempty" toml:",omitempty"`
}
// Service holds a service configuration (can only be of one type at the same time).
type Service struct {
LoadBalancer *LoadBalancerService `json:"loadbalancer,omitempty" toml:",omitempty,omitzero"`
}
// TCPService holds a tcp service configuration (can only be of one type at the same time).
type TCPService struct {
LoadBalancer *TCPLoadBalancerService `json:"loadbalancer,omitempty" toml:",omitempty,omitzero"`
}

363
pkg/config/middlewares.go Normal file
View file

@ -0,0 +1,363 @@
package config
import (
"github.com/containous/flaeg/parse"
"github.com/containous/traefik/pkg/ip"
)
// +k8s:deepcopy-gen=true
// Middleware holds the Middleware configuration.
type Middleware struct {
AddPrefix *AddPrefix `json:"addPrefix,omitempty"`
StripPrefix *StripPrefix `json:"stripPrefix,omitempty"`
StripPrefixRegex *StripPrefixRegex `json:"stripPrefixRegex,omitempty"`
ReplacePath *ReplacePath `json:"replacePath,omitempty"`
ReplacePathRegex *ReplacePathRegex `json:"replacePathRegex,omitempty"`
Chain *Chain `json:"chain,omitempty"`
IPWhiteList *IPWhiteList `json:"ipWhiteList,omitempty"`
Headers *Headers `json:"headers,omitempty"`
Errors *ErrorPage `json:"errors,omitempty"`
RateLimit *RateLimit `json:"rateLimit,omitempty"`
RedirectRegex *RedirectRegex `json:"redirectregex,omitempty"`
RedirectScheme *RedirectScheme `json:"redirectscheme,omitempty"`
BasicAuth *BasicAuth `json:"basicAuth,omitempty"`
DigestAuth *DigestAuth `json:"digestAuth,omitempty"`
ForwardAuth *ForwardAuth `json:"forwardAuth,omitempty"`
MaxConn *MaxConn `json:"maxConn,omitempty"`
Buffering *Buffering `json:"buffering,omitempty"`
CircuitBreaker *CircuitBreaker `json:"circuitBreaker,omitempty"`
Compress *Compress `json:"compress,omitempty" label:"allowEmpty"`
PassTLSClientCert *PassTLSClientCert `json:"passTLSClientCert,omitempty"`
Retry *Retry `json:"retry,omitempty"`
}
// +k8s:deepcopy-gen=true
// AddPrefix holds the AddPrefix configuration.
type AddPrefix struct {
Prefix string `json:"prefix,omitempty"`
}
// +k8s:deepcopy-gen=true
// Auth holds the authentication configuration (BASIC, DIGEST, users).
type Auth struct {
Basic *BasicAuth `json:"basic,omitempty" export:"true"`
Digest *DigestAuth `json:"digest,omitempty" export:"true"`
Forward *ForwardAuth `json:"forward,omitempty" export:"true"`
}
// +k8s:deepcopy-gen=true
// BasicAuth holds the HTTP basic authentication configuration.
type BasicAuth struct {
Users `json:"users,omitempty" mapstructure:","`
UsersFile string `json:"usersFile,omitempty"`
Realm string `json:"realm,omitempty"`
RemoveHeader bool `json:"removeHeader,omitempty"`
HeaderField string `json:"headerField,omitempty" export:"true"`
}
// +k8s:deepcopy-gen=true
// Buffering holds the 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"`
}
// +k8s:deepcopy-gen=true
// Chain holds a chain of middlewares
type Chain struct {
Middlewares []string `json:"middlewares"`
}
// +k8s:deepcopy-gen=true
// CircuitBreaker holds the circuit breaker configuration.
type CircuitBreaker struct {
Expression string `json:"expression,omitempty"`
}
// +k8s:deepcopy-gen=true
// Compress holds the compress configuration.
type Compress struct{}
// +k8s:deepcopy-gen=true
// DigestAuth holds the Digest HTTP authentication configuration.
type DigestAuth struct {
Users `json:"users,omitempty" mapstructure:","`
UsersFile string `json:"usersFile,omitempty"`
RemoveHeader bool `json:"removeHeader,omitempty"`
Realm string `json:"realm,omitempty" mapstructure:","`
HeaderField string `json:"headerField,omitempty" export:"true"`
}
// +k8s:deepcopy-gen=true
// ErrorPage holds the custom error page configuration.
type ErrorPage struct {
Status []string `json:"status,omitempty"`
Service string `json:"service,omitempty"`
Query string `json:"query,omitempty"`
}
// +k8s:deepcopy-gen=true
// ForwardAuth holds the http forward authentication configuration.
type ForwardAuth struct {
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"`
}
// +k8s:deepcopy-gen=true
// Headers holds the custom header configuration.
type Headers struct {
CustomRequestHeaders map[string]string `json:"customRequestHeaders,omitempty"`
CustomResponseHeaders map[string]string `json:"customResponseHeaders,omitempty"`
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"`
SSLForceHost bool `json:"sslForceHost,omitempty"`
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"`
CustomBrowserXSSValue string `json:"customBrowserXSSValue,omitempty"`
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
func (h *Headers) HasCustomHeadersDefined() bool {
return h != nil && (len(h.CustomResponseHeaders) != 0 ||
len(h.CustomRequestHeaders) != 0)
}
// HasSecureHeadersDefined checks to see if any of the secure header elements have been set
func (h *Headers) HasSecureHeadersDefined() bool {
return h != nil && (len(h.AllowedHosts) != 0 ||
len(h.HostsProxyHeaders) != 0 ||
h.SSLRedirect ||
h.SSLTemporaryRedirect ||
h.SSLForceHost ||
h.SSLHost != "" ||
len(h.SSLProxyHeaders) != 0 ||
h.STSSeconds != 0 ||
h.STSIncludeSubdomains ||
h.STSPreload ||
h.ForceSTSHeader ||
h.FrameDeny ||
h.CustomFrameOptionsValue != "" ||
h.ContentTypeNosniff ||
h.BrowserXSSFilter ||
h.CustomBrowserXSSValue != "" ||
h.ContentSecurityPolicy != "" ||
h.PublicKey != "" ||
h.ReferrerPolicy != "" ||
h.IsDevelopment)
}
// +k8s:deepcopy-gen=true
// IPStrategy holds the ip strategy configuration.
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
}
// +k8s:deepcopy-gen=true
// IPWhiteList holds the ip white list configuration.
type IPWhiteList struct {
SourceRange []string `json:"sourceRange,omitempty"`
IPStrategy *IPStrategy `json:"ipStrategy,omitempty" label:"allowEmpty"`
}
// +k8s:deepcopy-gen=true
// MaxConn holds maximum connection configuration.
type MaxConn struct {
Amount int64 `json:"amount,omitempty"`
ExtractorFunc string `json:"extractorFunc,omitempty"`
}
// SetDefaults Default values for a MaxConn.
func (m *MaxConn) SetDefaults() {
m.ExtractorFunc = "request.host"
}
// +k8s:deepcopy-gen=true
// PassTLSClientCert holds the TLS client cert headers configuration.
type PassTLSClientCert struct {
PEM bool `description:"Enable header with escaped client pem" json:"pem"`
Info *TLSClientCertificateInfo `description:"Enable header with configured client cert info" json:"info,omitempty"`
}
// +k8s:deepcopy-gen=true
// Rate holds the rate limiting configuration for a specific time period.
type Rate struct {
Period parse.Duration `json:"period,omitempty"`
Average int64 `json:"average,omitempty"`
Burst int64 `json:"burst,omitempty"`
}
// +k8s:deepcopy-gen=true
// RateLimit holds the rate limiting configuration for a given frontend.
type RateLimit struct {
RateSet map[string]*Rate `json:"rateset,omitempty"`
// FIXME replace by ipStrategy see oxy and replace
ExtractorFunc string `json:"extractorFunc,omitempty"`
}
// SetDefaults Default values for a MaxConn.
func (r *RateLimit) SetDefaults() {
r.ExtractorFunc = "request.host"
}
// +k8s:deepcopy-gen=true
// RedirectRegex holds the redirection configuration.
type RedirectRegex struct {
Regex string `json:"regex,omitempty"`
Replacement string `json:"replacement,omitempty"`
Permanent bool `json:"permanent,omitempty"`
}
// +k8s:deepcopy-gen=true
// RedirectScheme holds the scheme redirection configuration.
type RedirectScheme struct {
Scheme string `json:"scheme,omitempty"`
Port string `json:"port,omitempty"`
Permanent bool `json:"permanent,omitempty"`
}
// +k8s:deepcopy-gen=true
// ReplacePath holds the ReplacePath configuration.
type ReplacePath struct {
Path string `json:"path,omitempty"`
}
// +k8s:deepcopy-gen=true
// ReplacePathRegex holds the ReplacePathRegex configuration.
type ReplacePathRegex struct {
Regex string `json:"regex,omitempty"`
Replacement string `json:"replacement,omitempty"`
}
// +k8s:deepcopy-gen=true
// Retry holds the retry configuration.
type Retry struct {
Attempts int `description:"Number of attempts" export:"true"`
}
// +k8s:deepcopy-gen=true
// StripPrefix holds the StripPrefix configuration.
type StripPrefix struct {
Prefixes []string `json:"prefixes,omitempty"`
}
// +k8s:deepcopy-gen=true
// StripPrefixRegex holds the StripPrefixRegex configuration.
type StripPrefixRegex struct {
Regex []string `json:"regex,omitempty"`
}
// +k8s:deepcopy-gen=true
// TLSClientCertificateInfo holds the client TLS certificate info configuration.
type TLSClientCertificateInfo struct {
NotAfter bool `description:"Add NotAfter info in header" json:"notAfter"`
NotBefore bool `description:"Add NotBefore info in header" json:"notBefore"`
Sans bool `description:"Add Sans info in header" json:"sans"`
Subject *TLSCLientCertificateDNInfo `description:"Add Subject info in header" json:"subject,omitempty"`
Issuer *TLSCLientCertificateDNInfo `description:"Add Issuer info in header" json:"issuer,omitempty"`
}
// +k8s:deepcopy-gen=true
// TLSCLientCertificateDNInfo holds the client TLS certificate distinguished name info configuration
// cf https://tools.ietf.org/html/rfc3739
type TLSCLientCertificateDNInfo 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"`
DomainComponent bool `description:"Add Domain Component info in header" json:"domainComponent"`
}
// +k8s:deepcopy-gen=true
// Users holds a list of users
type Users []string
// +k8s:deepcopy-gen=true
// ClientTLS holds the TLS specific configurations as client
// CA, Cert and Key can be either path or file contents.
type ClientTLS struct {
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"`
}

View file

@ -0,0 +1,134 @@
package static
import (
"fmt"
"strings"
"github.com/containous/traefik/pkg/log"
)
// EntryPoint holds the entry point configuration.
type EntryPoint struct {
Address string
Transport *EntryPointsTransport
ProxyProtocol *ProxyProtocol
ForwardedHeaders *ForwardedHeaders
}
// ForwardedHeaders Trust client forwarding headers.
type ForwardedHeaders struct {
Insecure bool
TrustedIPs []string
}
// ProxyProtocol contains Proxy-Protocol configuration.
type ProxyProtocol struct {
Insecure bool `export:"true"`
TrustedIPs []string
}
// EntryPoints holds the HTTP entry point list.
type EntryPoints map[string]*EntryPoint
// EntryPointsTransport configures communication between clients and Traefik.
type EntryPointsTransport struct {
LifeCycle *LifeCycle `description:"Timeouts influencing the server life cycle" export:"true"`
RespondingTimeouts *RespondingTimeouts `description:"Timeouts for incoming requests to the Traefik instance" export:"true"`
}
// 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)
(*ep)[result["name"]] = &EntryPoint{
Address: result["address"],
ProxyProtocol: makeEntryPointProxyProtocol(result),
ForwardedHeaders: makeEntryPointForwardedHeaders(result),
}
return nil
}
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 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
}
func makeEntryPointForwardedHeaders(result map[string]string) *ForwardedHeaders {
forwardedHeaders := &ForwardedHeaders{}
forwardedHeaders.Insecure = toBool(result, "forwardedheaders_insecure")
fhTrustedIPs := result["forwardedheaders_trustedips"]
if len(fhTrustedIPs) > 0 {
forwardedHeaders.TrustedIPs = strings.Split(fhTrustedIPs, ",")
}
return forwardedHeaders
}

View file

@ -0,0 +1,257 @@
package static
import (
"testing"
"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 " +
"CA:car " +
"CA.Optional:true " +
"Redirect.EntryPoint:https " +
"Redirect.Regex:http://localhost/(.*) " +
"Redirect.Replacement:http://mydomain/$1 " +
"Redirect.Permanent:true " +
"Compress:true " +
"ProxyProtocol.TrustedIPs:192.168.0.1 " +
"ForwardedHeaders.TrustedIPs:10.0.0.3/24,20.0.0.3/24 " +
"Auth.Basic.Realm:myRealm " +
"Auth.Basic.Users:test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0 " +
"Auth.Basic.RemoveHeader:true " +
"Auth.Digest.Users:test:traefik:a2688e031edb4be6a3797f3882655c05,test2:traefik:518845800f9e2bfb1f1f740ec24f074e " +
"Auth.Digest.RemoveHeader:true " +
"Auth.HeaderField:X-WebAuth-User " +
"Auth.Forward.Address:https://authserver.com/auth " +
"Auth.Forward.AuthResponseHeaders:X-Auth,X-Test,X-Secret " +
"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 " +
"WhiteList.SourceRange:10.42.0.0/16,152.89.1.33/32,afed:be44::/16 " +
"WhiteList.IPStrategy.depth:3 " +
"WhiteList.IPStrategy.ExcludedIPs:10.0.0.3/24,20.0.0.3/24 " +
"ClientIPStrategy.depth:3 " +
"ClientIPStrategy.ExcludedIPs:10.0.0.3/24,20.0.0.3/24 ",
expectedResult: map[string]string{
"address": ":8000",
"auth_basic_realm": "myRealm",
"auth_basic_users": "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0",
"auth_basic_removeheader": "true",
"auth_digest_users": "test:traefik:a2688e031edb4be6a3797f3882655c05,test2:traefik:518845800f9e2bfb1f1f740ec24f074e",
"auth_digest_removeheader": "true",
"auth_forward_address": "https://authserver.com/auth",
"auth_forward_authresponseheaders": "X-Auth,X-Test,X-Secret",
"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",
"whitelist_sourcerange": "10.42.0.0/16,152.89.1.33/32,afed:be44::/16",
"whitelist_ipstrategy_depth": "3",
"whitelist_ipstrategy_excludedips": "10.0.0.3/24,20.0.0.3/24",
"clientipstrategy_depth": "3",
"clientipstrategy_excludedips": "10.0.0.3/24,20.0.0.3/24",
},
},
{
name: "compress on",
value: "name:foo Compress:on",
expectedResult: map[string]string{
"name": "foo",
"compress": "on",
},
},
}
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 " +
"CA:car " +
"CA.Optional:true " +
"ProxyProtocol.TrustedIPs:192.168.0.1 ",
expectedEntryPointName: "foo",
expectedEntryPoint: &EntryPoint{
Address: ":8000",
ProxyProtocol: &ProxyProtocol{
Insecure: false,
TrustedIPs: []string{"192.168.0.1"},
},
ForwardedHeaders: &ForwardedHeaders{},
// FIXME Test ServersTransport
},
},
{
name: "all parameters lowercase",
expression: "Name:foo " +
"address::8000 " +
"tls " +
"tls.minversion:VersionTLS11 " +
"tls.ciphersuites:TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA384,TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA " +
"ca:car " +
"ca.Optional:true " +
"proxyProtocol.TrustedIPs:192.168.0.1 ",
expectedEntryPointName: "foo",
expectedEntryPoint: &EntryPoint{
Address: ":8000",
ProxyProtocol: &ProxyProtocol{
Insecure: false,
TrustedIPs: []string{"192.168.0.1"},
},
ForwardedHeaders: &ForwardedHeaders{},
// FIXME Test ServersTransport
},
},
{
name: "default",
expression: "Name:foo",
expectedEntryPointName: "foo",
expectedEntryPoint: &EntryPoint{
ForwardedHeaders: &ForwardedHeaders{},
},
},
{
name: "ProxyProtocol insecure true",
expression: "Name:foo ProxyProtocol.insecure:true",
expectedEntryPointName: "foo",
expectedEntryPoint: &EntryPoint{
ProxyProtocol: &ProxyProtocol{Insecure: true},
ForwardedHeaders: &ForwardedHeaders{},
},
},
{
name: "ProxyProtocol insecure false",
expression: "Name:foo ProxyProtocol.insecure:false",
expectedEntryPointName: "foo",
expectedEntryPoint: &EntryPoint{
ProxyProtocol: &ProxyProtocol{},
ForwardedHeaders: &ForwardedHeaders{},
},
},
{
name: "ProxyProtocol TrustedIPs",
expression: "Name:foo ProxyProtocol.TrustedIPs:10.0.0.3/24,20.0.0.3/24",
expectedEntryPointName: "foo",
expectedEntryPoint: &EntryPoint{
ProxyProtocol: &ProxyProtocol{
TrustedIPs: []string{"10.0.0.3/24", "20.0.0.3/24"},
},
ForwardedHeaders: &ForwardedHeaders{},
},
},
}
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)
})
}
}

View file

@ -0,0 +1,401 @@
package static
import (
"errors"
"strings"
"time"
"github.com/containous/flaeg/parse"
"github.com/containous/traefik/old/provider/boltdb"
"github.com/containous/traefik/old/provider/consul"
"github.com/containous/traefik/old/provider/consulcatalog"
"github.com/containous/traefik/old/provider/dynamodb"
"github.com/containous/traefik/old/provider/ecs"
"github.com/containous/traefik/old/provider/etcd"
"github.com/containous/traefik/old/provider/eureka"
"github.com/containous/traefik/old/provider/mesos"
"github.com/containous/traefik/old/provider/rancher"
"github.com/containous/traefik/old/provider/zk"
"github.com/containous/traefik/pkg/log"
"github.com/containous/traefik/pkg/ping"
acmeprovider "github.com/containous/traefik/pkg/provider/acme"
"github.com/containous/traefik/pkg/provider/docker"
"github.com/containous/traefik/pkg/provider/file"
"github.com/containous/traefik/pkg/provider/kubernetes/crd"
"github.com/containous/traefik/pkg/provider/kubernetes/ingress"
"github.com/containous/traefik/pkg/provider/marathon"
"github.com/containous/traefik/pkg/provider/rest"
"github.com/containous/traefik/pkg/tls"
"github.com/containous/traefik/pkg/tracing/datadog"
"github.com/containous/traefik/pkg/tracing/instana"
"github.com/containous/traefik/pkg/tracing/jaeger"
"github.com/containous/traefik/pkg/tracing/zipkin"
"github.com/containous/traefik/pkg/types"
assetfs "github.com/elazarl/go-bindata-assetfs"
"github.com/go-acme/lego/challenge/dns01"
jaegercli "github.com/uber/jaeger-client-go"
)
const (
// DefaultInternalEntryPointName the name of the default internal entry point
DefaultInternalEntryPointName = "traefik"
// DefaultGraceTimeout controls how long Traefik serves pending requests
// prior to shutting down.
DefaultGraceTimeout = 10 * time.Second
// DefaultIdleTimeout before closing an idle connection.
DefaultIdleTimeout = 180 * time.Second
// DefaultAcmeCAServer is the default ACME API endpoint
DefaultAcmeCAServer = "https://acme-v02.api.letsencrypt.org/directory"
)
// Configuration is the static configuration
type Configuration struct {
Global *Global `description:"Global configuration options" export:"true"`
ServersTransport *ServersTransport `description:"Servers default transport" export:"true"`
EntryPoints EntryPoints `description:"Entrypoints definition using format: --entryPoints='Name:http Address::8000 Redirect.EntryPoint:https' --entryPoints='Name:https Address::4442 TLS:tests/traefik.crt,tests/traefik.key;prod/traefik.crt,prod/traefik.key'" export:"true"`
Providers *Providers `description:"Providers configuration" export:"true"`
API *API `description:"Enable api/dashboard" export:"true"`
Metrics *types.Metrics `description:"Enable a metrics exporter" export:"true"`
Ping *ping.Handler `description:"Enable ping" export:"true"`
// Rest *rest.Provider `description:"Enable Rest backend with default settings" export:"true"`
Log *types.TraefikLog
AccessLog *types.AccessLog `description:"Access log settings" export:"true"`
Tracing *Tracing `description:"OpenTracing configuration" export:"true"`
HostResolver *types.HostResolverConfig `description:"Enable CNAME Flattening" export:"true"`
ACME *acmeprovider.Configuration `description:"Enable ACME (Let's Encrypt): automatic SSL" export:"true"`
}
// Global holds the global configuration.
type Global struct {
Debug bool `short:"d" description:"Enable debug mode" export:"true"`
CheckNewVersion bool `description:"Periodically check if a new version has been released" export:"true"`
SendAnonymousUsage *bool `description:"send periodically anonymous usage statistics" export:"true"`
}
// ServersTransport options to configure communication between Traefik and the servers
type ServersTransport struct {
InsecureSkipVerify bool `description:"Disable SSL certificate verification" export:"true"`
RootCAs tls.FilesOrContents `description:"Add cert file for self-signed certificate"`
MaxIdleConnsPerHost int `description:"If non-zero, controls the maximum idle (keep-alive) to keep per-host. If zero, DefaultMaxIdleConnsPerHost is used" export:"true"`
ForwardingTimeouts *ForwardingTimeouts `description:"Timeouts for requests forwarded to the backend servers" export:"true"`
}
// API holds the API configuration
type API struct {
EntryPoint string `description:"EntryPoint" export:"true"`
Dashboard bool `description:"Activate dashboard" export:"true"`
Statistics *types.Statistics `description:"Enable more detailed statistics" export:"true"`
Middlewares []string `description:"Middleware list" export:"true"`
DashboardAssets *assetfs.AssetFS `json:"-"`
}
// RespondingTimeouts contains timeout configurations for incoming requests to the Traefik instance.
type RespondingTimeouts struct {
ReadTimeout parse.Duration `description:"ReadTimeout is the maximum duration for reading the entire request, including the body. If zero, no timeout is set" export:"true"`
WriteTimeout parse.Duration `description:"WriteTimeout is the maximum duration before timing out writes of the response. If zero, no timeout is set" export:"true"`
IdleTimeout parse.Duration `description:"IdleTimeout is the maximum amount duration an idle (keep-alive) connection will remain idle before closing itself. Defaults to 180 seconds. If zero, no timeout is set" export:"true"`
}
// ForwardingTimeouts contains timeout configurations for forwarding requests to the backend servers.
type ForwardingTimeouts struct {
DialTimeout parse.Duration `description:"The amount of time to wait until a connection to a backend server can be established. Defaults to 30 seconds. If zero, no timeout exists" export:"true"`
ResponseHeaderTimeout parse.Duration `description:"The amount of time to wait for a server's response headers after fully writing the request (including its body, if any). If zero, no timeout exists" export:"true"`
}
// LifeCycle contains configurations relevant to the lifecycle (such as the shutdown phase) of Traefik.
type LifeCycle struct {
RequestAcceptGraceTimeout parse.Duration `description:"Duration to keep accepting requests before Traefik initiates the graceful shutdown procedure"`
GraceTimeOut parse.Duration `description:"Duration to give active requests a chance to finish before Traefik stops"`
}
// Tracing holds the tracing configuration.
type Tracing struct {
Backend string `description:"Selects the tracking backend ('jaeger','zipkin','datadog','instana')." export:"true"`
ServiceName string `description:"Set the name for this service" export:"true"`
SpanNameLimit int `description:"Set the maximum character limit for Span names (default 0 = no limit)" export:"true"`
Jaeger *jaeger.Config `description:"Settings for jaeger"`
Zipkin *zipkin.Config `description:"Settings for zipkin"`
DataDog *datadog.Config `description:"Settings for DataDog"`
Instana *instana.Config `description:"Settings for Instana"`
}
// Providers contains providers configuration
type Providers struct {
ProvidersThrottleDuration parse.Duration `description:"Backends throttle duration: minimum duration between 2 events from providers before applying a new configuration. It avoids unnecessary reloads if multiples events are sent in a short amount of time." export:"true"`
Docker *docker.Provider `description:"Enable Docker backend with default settings" export:"true"`
File *file.Provider `description:"Enable File backend with default settings" export:"true"`
Marathon *marathon.Provider `description:"Enable Marathon backend with default settings" export:"true"`
Consul *consul.Provider `description:"Enable Consul backend with default settings" export:"true"`
ConsulCatalog *consulcatalog.Provider `description:"Enable Consul catalog backend with default settings" export:"true"`
Etcd *etcd.Provider `description:"Enable Etcd backend with default settings" export:"true"`
Zookeeper *zk.Provider `description:"Enable Zookeeper backend with default settings" export:"true"`
Boltdb *boltdb.Provider `description:"Enable Boltdb backend with default settings" export:"true"`
Kubernetes *ingress.Provider `description:"Enable Kubernetes backend with default settings" export:"true"`
KubernetesCRD *crd.Provider `description:"Enable Kubernetes backend with default settings" export:"true"`
Mesos *mesos.Provider `description:"Enable Mesos backend with default settings" export:"true"`
Eureka *eureka.Provider `description:"Enable Eureka backend with default settings" export:"true"`
ECS *ecs.Provider `description:"Enable ECS backend with default settings" export:"true"`
Rancher *rancher.Provider `description:"Enable Rancher backend with default settings" export:"true"`
DynamoDB *dynamodb.Provider `description:"Enable DynamoDB backend with default settings" export:"true"`
Rest *rest.Provider `description:"Enable Rest backend with default settings" export:"true"`
}
// SetEffectiveConfiguration adds missing configuration parameters derived from existing ones.
// It also takes care of maintaining backwards compatibility.
func (c *Configuration) SetEffectiveConfiguration(configFile string) {
if len(c.EntryPoints) == 0 {
c.EntryPoints = EntryPoints{
"http": &EntryPoint{
Address: ":80",
},
}
}
if (c.API != nil && c.API.EntryPoint == DefaultInternalEntryPointName) ||
(c.Ping != nil && c.Ping.EntryPoint == DefaultInternalEntryPointName) ||
(c.Metrics != nil && c.Metrics.Prometheus != nil && c.Metrics.Prometheus.EntryPoint == DefaultInternalEntryPointName) ||
(c.Providers.Rest != nil && c.Providers.Rest.EntryPoint == DefaultInternalEntryPointName) {
if _, ok := c.EntryPoints[DefaultInternalEntryPointName]; !ok {
c.EntryPoints[DefaultInternalEntryPointName] = &EntryPoint{Address: ":8080"}
}
}
for _, entryPoint := range c.EntryPoints {
if entryPoint.Transport == nil {
entryPoint.Transport = &EntryPointsTransport{}
}
// Make sure LifeCycle isn't nil to spare nil checks elsewhere.
if entryPoint.Transport.LifeCycle == nil {
entryPoint.Transport.LifeCycle = &LifeCycle{
GraceTimeOut: parse.Duration(DefaultGraceTimeout),
}
entryPoint.Transport.RespondingTimeouts = &RespondingTimeouts{
IdleTimeout: parse.Duration(DefaultIdleTimeout),
}
}
if entryPoint.ForwardedHeaders == nil {
entryPoint.ForwardedHeaders = &ForwardedHeaders{}
}
}
if c.Providers.Rancher != nil {
// Ensure backwards compatibility for now
if len(c.Providers.Rancher.AccessKey) > 0 ||
len(c.Providers.Rancher.Endpoint) > 0 ||
len(c.Providers.Rancher.SecretKey) > 0 {
if c.Providers.Rancher.API == nil {
c.Providers.Rancher.API = &rancher.APIConfiguration{
AccessKey: c.Providers.Rancher.AccessKey,
SecretKey: c.Providers.Rancher.SecretKey,
Endpoint: c.Providers.Rancher.Endpoint,
}
}
log.Warn("Deprecated configuration found: rancher.[accesskey|secretkey|endpoint]. " +
"Please use rancher.api.[accesskey|secretkey|endpoint] instead.")
}
if c.Providers.Rancher.Metadata != nil && len(c.Providers.Rancher.Metadata.Prefix) == 0 {
c.Providers.Rancher.Metadata.Prefix = "latest"
}
}
if c.Providers.Docker != nil {
if c.Providers.Docker.SwarmModeRefreshSeconds <= 0 {
c.Providers.Docker.SwarmModeRefreshSeconds = 15
}
}
if c.Providers.File != nil {
c.Providers.File.TraefikFile = configFile
}
c.initACMEProvider()
c.initTracing()
}
func (c *Configuration) initTracing() {
if c.Tracing != nil {
switch c.Tracing.Backend {
case jaeger.Name:
if c.Tracing.Jaeger == nil {
c.Tracing.Jaeger = &jaeger.Config{
SamplingServerURL: "http://localhost:5778/sampling",
SamplingType: "const",
SamplingParam: 1.0,
LocalAgentHostPort: "127.0.0.1:6831",
Propagation: "jaeger",
Gen128Bit: false,
TraceContextHeaderName: jaegercli.TraceContextHeaderName,
}
}
if c.Tracing.Zipkin != nil {
log.Warn("Zipkin configuration will be ignored")
c.Tracing.Zipkin = nil
}
if c.Tracing.DataDog != nil {
log.Warn("DataDog configuration will be ignored")
c.Tracing.DataDog = nil
}
if c.Tracing.Instana != nil {
log.Warn("Instana configuration will be ignored")
c.Tracing.Instana = nil
}
case zipkin.Name:
if c.Tracing.Zipkin == nil {
c.Tracing.Zipkin = &zipkin.Config{
HTTPEndpoint: "http://localhost:9411/api/v1/spans",
SameSpan: false,
ID128Bit: true,
Debug: false,
SampleRate: 1.0,
}
}
if c.Tracing.Jaeger != nil {
log.Warn("Jaeger configuration will be ignored")
c.Tracing.Jaeger = nil
}
if c.Tracing.DataDog != nil {
log.Warn("DataDog configuration will be ignored")
c.Tracing.DataDog = nil
}
if c.Tracing.Instana != nil {
log.Warn("Instana configuration will be ignored")
c.Tracing.Instana = nil
}
case datadog.Name:
if c.Tracing.DataDog == nil {
c.Tracing.DataDog = &datadog.Config{
LocalAgentHostPort: "localhost:8126",
GlobalTag: "",
Debug: false,
}
}
if c.Tracing.Zipkin != nil {
log.Warn("Zipkin configuration will be ignored")
c.Tracing.Zipkin = nil
}
if c.Tracing.Jaeger != nil {
log.Warn("Jaeger configuration will be ignored")
c.Tracing.Jaeger = nil
}
if c.Tracing.Instana != nil {
log.Warn("Instana configuration will be ignored")
c.Tracing.Instana = nil
}
case instana.Name:
if c.Tracing.Instana == nil {
c.Tracing.Instana = &instana.Config{
LocalAgentHost: "localhost",
LocalAgentPort: 42699,
LogLevel: "info",
}
}
if c.Tracing.Zipkin != nil {
log.Warn("Zipkin configuration will be ignored")
c.Tracing.Zipkin = nil
}
if c.Tracing.Jaeger != nil {
log.Warn("Jaeger configuration will be ignored")
c.Tracing.Jaeger = nil
}
if c.Tracing.DataDog != nil {
log.Warn("DataDog configuration will be ignored")
c.Tracing.DataDog = nil
}
default:
log.Warnf("Unknown tracer %q", c.Tracing.Backend)
return
}
}
}
// FIXME handle on new configuration ACME struct
func (c *Configuration) initACMEProvider() {
if c.ACME != nil {
c.ACME.CAServer = getSafeACMECAServer(c.ACME.CAServer)
if c.ACME.DNSChallenge != nil && c.ACME.HTTPChallenge != nil {
log.Warn("Unable to use DNS challenge and HTTP challenge at the same time. Fallback to DNS challenge.")
c.ACME.HTTPChallenge = nil
}
if c.ACME.DNSChallenge != nil && c.ACME.TLSChallenge != nil {
log.Warn("Unable to use DNS challenge and TLS challenge at the same time. Fallback to DNS challenge.")
c.ACME.TLSChallenge = nil
}
if c.ACME.HTTPChallenge != nil && c.ACME.TLSChallenge != nil {
log.Warn("Unable to use HTTP challenge and TLS challenge at the same time. Fallback to TLS challenge.")
c.ACME.HTTPChallenge = nil
}
}
}
// InitACMEProvider create an acme provider from the ACME part of globalConfiguration
func (c *Configuration) InitACMEProvider() (*acmeprovider.Provider, error) {
if c.ACME != nil {
if len(c.ACME.Storage) == 0 {
return nil, errors.New("unable to initialize ACME provider with no storage location for the certificates")
}
return &acmeprovider.Provider{
Configuration: c.ACME,
}, nil
}
return nil, nil
}
// ValidateConfiguration validate that configuration is coherent
func (c *Configuration) ValidateConfiguration() {
if c.ACME != nil {
for _, domain := range c.ACME.Domains {
if domain.Main != dns01.UnFqdn(domain.Main) {
log.Warnf("FQDN detected, please remove the trailing dot: %s", domain.Main)
}
for _, san := range domain.SANs {
if san != dns01.UnFqdn(san) {
log.Warnf("FQDN detected, please remove the trailing dot: %s", san)
}
}
}
}
// FIXME Validate store config?
// if c.ACME != nil {
// if _, ok := c.EntryPoints[c.ACME.EntryPoint]; !ok {
// log.Fatalf("Unknown entrypoint %q for ACME configuration", c.ACME.EntryPoint)
// }
// else if c.EntryPoints[c.ACME.EntryPoint].TLS == nil {
// log.Fatalf("Entrypoint %q has no TLS configuration for ACME configuration", c.ACME.EntryPoint)
// }
// }
}
func getSafeACMECAServer(caServerSrc string) string {
if len(caServerSrc) == 0 {
return DefaultAcmeCAServer
}
if strings.HasPrefix(caServerSrc, "https://acme-v01.api.letsencrypt.org") {
caServer := strings.Replace(caServerSrc, "v01", "v02", 1)
log.Warnf("The CA server %[1]q refers to a v01 endpoint of the ACME API, please change to %[2]q. Fallback to %[2]q.", caServerSrc, caServer)
return caServer
}
if strings.HasPrefix(caServerSrc, "https://acme-staging.api.letsencrypt.org") {
caServer := strings.Replace(caServerSrc, "https://acme-staging.api.letsencrypt.org", "https://acme-staging-v02.api.letsencrypt.org", 1)
log.Warnf("The CA server %[1]q refers to a v01 endpoint of the ACME API, please change to %[2]q. Fallback to %[2]q.", caServerSrc, caServer)
return caServer
}
return caServerSrc
}

View file

@ -0,0 +1,733 @@
// +build !ignore_autogenerated
/*
The MIT License (MIT)
Copyright (c) 2016-2019 Containous SAS
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
// Code generated by deepcopy-gen. DO NOT EDIT.
package config
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *AddPrefix) DeepCopyInto(out *AddPrefix) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AddPrefix.
func (in *AddPrefix) DeepCopy() *AddPrefix {
if in == nil {
return nil
}
out := new(AddPrefix)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Auth) DeepCopyInto(out *Auth) {
*out = *in
if in.Basic != nil {
in, out := &in.Basic, &out.Basic
*out = new(BasicAuth)
(*in).DeepCopyInto(*out)
}
if in.Digest != nil {
in, out := &in.Digest, &out.Digest
*out = new(DigestAuth)
(*in).DeepCopyInto(*out)
}
if in.Forward != nil {
in, out := &in.Forward, &out.Forward
*out = new(ForwardAuth)
(*in).DeepCopyInto(*out)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Auth.
func (in *Auth) DeepCopy() *Auth {
if in == nil {
return nil
}
out := new(Auth)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *BasicAuth) DeepCopyInto(out *BasicAuth) {
*out = *in
if in.Users != nil {
in, out := &in.Users, &out.Users
*out = make(Users, len(*in))
copy(*out, *in)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BasicAuth.
func (in *BasicAuth) DeepCopy() *BasicAuth {
if in == nil {
return nil
}
out := new(BasicAuth)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Buffering) DeepCopyInto(out *Buffering) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Buffering.
func (in *Buffering) DeepCopy() *Buffering {
if in == nil {
return nil
}
out := new(Buffering)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Chain) DeepCopyInto(out *Chain) {
*out = *in
if in.Middlewares != nil {
in, out := &in.Middlewares, &out.Middlewares
*out = make([]string, len(*in))
copy(*out, *in)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Chain.
func (in *Chain) DeepCopy() *Chain {
if in == nil {
return nil
}
out := new(Chain)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *CircuitBreaker) DeepCopyInto(out *CircuitBreaker) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CircuitBreaker.
func (in *CircuitBreaker) DeepCopy() *CircuitBreaker {
if in == nil {
return nil
}
out := new(CircuitBreaker)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ClientTLS) DeepCopyInto(out *ClientTLS) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClientTLS.
func (in *ClientTLS) DeepCopy() *ClientTLS {
if in == nil {
return nil
}
out := new(ClientTLS)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Compress) DeepCopyInto(out *Compress) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Compress.
func (in *Compress) DeepCopy() *Compress {
if in == nil {
return nil
}
out := new(Compress)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *DigestAuth) DeepCopyInto(out *DigestAuth) {
*out = *in
if in.Users != nil {
in, out := &in.Users, &out.Users
*out = make(Users, len(*in))
copy(*out, *in)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DigestAuth.
func (in *DigestAuth) DeepCopy() *DigestAuth {
if in == nil {
return nil
}
out := new(DigestAuth)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ErrorPage) DeepCopyInto(out *ErrorPage) {
*out = *in
if in.Status != nil {
in, out := &in.Status, &out.Status
*out = make([]string, len(*in))
copy(*out, *in)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ErrorPage.
func (in *ErrorPage) DeepCopy() *ErrorPage {
if in == nil {
return nil
}
out := new(ErrorPage)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ForwardAuth) DeepCopyInto(out *ForwardAuth) {
*out = *in
if in.TLS != nil {
in, out := &in.TLS, &out.TLS
*out = new(ClientTLS)
**out = **in
}
if in.AuthResponseHeaders != nil {
in, out := &in.AuthResponseHeaders, &out.AuthResponseHeaders
*out = make([]string, len(*in))
copy(*out, *in)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ForwardAuth.
func (in *ForwardAuth) DeepCopy() *ForwardAuth {
if in == nil {
return nil
}
out := new(ForwardAuth)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Headers) DeepCopyInto(out *Headers) {
*out = *in
if in.CustomRequestHeaders != nil {
in, out := &in.CustomRequestHeaders, &out.CustomRequestHeaders
*out = make(map[string]string, len(*in))
for key, val := range *in {
(*out)[key] = val
}
}
if in.CustomResponseHeaders != nil {
in, out := &in.CustomResponseHeaders, &out.CustomResponseHeaders
*out = make(map[string]string, len(*in))
for key, val := range *in {
(*out)[key] = val
}
}
if in.AllowedHosts != nil {
in, out := &in.AllowedHosts, &out.AllowedHosts
*out = make([]string, len(*in))
copy(*out, *in)
}
if in.HostsProxyHeaders != nil {
in, out := &in.HostsProxyHeaders, &out.HostsProxyHeaders
*out = make([]string, len(*in))
copy(*out, *in)
}
if in.SSLProxyHeaders != nil {
in, out := &in.SSLProxyHeaders, &out.SSLProxyHeaders
*out = make(map[string]string, len(*in))
for key, val := range *in {
(*out)[key] = val
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Headers.
func (in *Headers) DeepCopy() *Headers {
if in == nil {
return nil
}
out := new(Headers)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *IPStrategy) DeepCopyInto(out *IPStrategy) {
*out = *in
if in.ExcludedIPs != nil {
in, out := &in.ExcludedIPs, &out.ExcludedIPs
*out = make([]string, len(*in))
copy(*out, *in)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IPStrategy.
func (in *IPStrategy) DeepCopy() *IPStrategy {
if in == nil {
return nil
}
out := new(IPStrategy)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *IPWhiteList) DeepCopyInto(out *IPWhiteList) {
*out = *in
if in.SourceRange != nil {
in, out := &in.SourceRange, &out.SourceRange
*out = make([]string, len(*in))
copy(*out, *in)
}
if in.IPStrategy != nil {
in, out := &in.IPStrategy, &out.IPStrategy
*out = new(IPStrategy)
(*in).DeepCopyInto(*out)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IPWhiteList.
func (in *IPWhiteList) DeepCopy() *IPWhiteList {
if in == nil {
return nil
}
out := new(IPWhiteList)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *MaxConn) DeepCopyInto(out *MaxConn) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MaxConn.
func (in *MaxConn) DeepCopy() *MaxConn {
if in == nil {
return nil
}
out := new(MaxConn)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Middleware) DeepCopyInto(out *Middleware) {
*out = *in
if in.AddPrefix != nil {
in, out := &in.AddPrefix, &out.AddPrefix
*out = new(AddPrefix)
**out = **in
}
if in.StripPrefix != nil {
in, out := &in.StripPrefix, &out.StripPrefix
*out = new(StripPrefix)
(*in).DeepCopyInto(*out)
}
if in.StripPrefixRegex != nil {
in, out := &in.StripPrefixRegex, &out.StripPrefixRegex
*out = new(StripPrefixRegex)
(*in).DeepCopyInto(*out)
}
if in.ReplacePath != nil {
in, out := &in.ReplacePath, &out.ReplacePath
*out = new(ReplacePath)
**out = **in
}
if in.ReplacePathRegex != nil {
in, out := &in.ReplacePathRegex, &out.ReplacePathRegex
*out = new(ReplacePathRegex)
**out = **in
}
if in.Chain != nil {
in, out := &in.Chain, &out.Chain
*out = new(Chain)
(*in).DeepCopyInto(*out)
}
if in.IPWhiteList != nil {
in, out := &in.IPWhiteList, &out.IPWhiteList
*out = new(IPWhiteList)
(*in).DeepCopyInto(*out)
}
if in.Headers != nil {
in, out := &in.Headers, &out.Headers
*out = new(Headers)
(*in).DeepCopyInto(*out)
}
if in.Errors != nil {
in, out := &in.Errors, &out.Errors
*out = new(ErrorPage)
(*in).DeepCopyInto(*out)
}
if in.RateLimit != nil {
in, out := &in.RateLimit, &out.RateLimit
*out = new(RateLimit)
(*in).DeepCopyInto(*out)
}
if in.RedirectRegex != nil {
in, out := &in.RedirectRegex, &out.RedirectRegex
*out = new(RedirectRegex)
**out = **in
}
if in.RedirectScheme != nil {
in, out := &in.RedirectScheme, &out.RedirectScheme
*out = new(RedirectScheme)
**out = **in
}
if in.BasicAuth != nil {
in, out := &in.BasicAuth, &out.BasicAuth
*out = new(BasicAuth)
(*in).DeepCopyInto(*out)
}
if in.DigestAuth != nil {
in, out := &in.DigestAuth, &out.DigestAuth
*out = new(DigestAuth)
(*in).DeepCopyInto(*out)
}
if in.ForwardAuth != nil {
in, out := &in.ForwardAuth, &out.ForwardAuth
*out = new(ForwardAuth)
(*in).DeepCopyInto(*out)
}
if in.MaxConn != nil {
in, out := &in.MaxConn, &out.MaxConn
*out = new(MaxConn)
**out = **in
}
if in.Buffering != nil {
in, out := &in.Buffering, &out.Buffering
*out = new(Buffering)
**out = **in
}
if in.CircuitBreaker != nil {
in, out := &in.CircuitBreaker, &out.CircuitBreaker
*out = new(CircuitBreaker)
**out = **in
}
if in.Compress != nil {
in, out := &in.Compress, &out.Compress
*out = new(Compress)
**out = **in
}
if in.PassTLSClientCert != nil {
in, out := &in.PassTLSClientCert, &out.PassTLSClientCert
*out = new(PassTLSClientCert)
(*in).DeepCopyInto(*out)
}
if in.Retry != nil {
in, out := &in.Retry, &out.Retry
*out = new(Retry)
**out = **in
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Middleware.
func (in *Middleware) DeepCopy() *Middleware {
if in == nil {
return nil
}
out := new(Middleware)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *PassTLSClientCert) DeepCopyInto(out *PassTLSClientCert) {
*out = *in
if in.Info != nil {
in, out := &in.Info, &out.Info
*out = new(TLSClientCertificateInfo)
(*in).DeepCopyInto(*out)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PassTLSClientCert.
func (in *PassTLSClientCert) DeepCopy() *PassTLSClientCert {
if in == nil {
return nil
}
out := new(PassTLSClientCert)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Rate) DeepCopyInto(out *Rate) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Rate.
func (in *Rate) DeepCopy() *Rate {
if in == nil {
return nil
}
out := new(Rate)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *RateLimit) DeepCopyInto(out *RateLimit) {
*out = *in
if in.RateSet != nil {
in, out := &in.RateSet, &out.RateSet
*out = make(map[string]*Rate, len(*in))
for key, val := range *in {
var outVal *Rate
if val == nil {
(*out)[key] = nil
} else {
in, out := &val, &outVal
*out = new(Rate)
**out = **in
}
(*out)[key] = outVal
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RateLimit.
func (in *RateLimit) DeepCopy() *RateLimit {
if in == nil {
return nil
}
out := new(RateLimit)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *RedirectRegex) DeepCopyInto(out *RedirectRegex) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RedirectRegex.
func (in *RedirectRegex) DeepCopy() *RedirectRegex {
if in == nil {
return nil
}
out := new(RedirectRegex)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *RedirectScheme) DeepCopyInto(out *RedirectScheme) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RedirectScheme.
func (in *RedirectScheme) DeepCopy() *RedirectScheme {
if in == nil {
return nil
}
out := new(RedirectScheme)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ReplacePath) DeepCopyInto(out *ReplacePath) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ReplacePath.
func (in *ReplacePath) DeepCopy() *ReplacePath {
if in == nil {
return nil
}
out := new(ReplacePath)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ReplacePathRegex) DeepCopyInto(out *ReplacePathRegex) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ReplacePathRegex.
func (in *ReplacePathRegex) DeepCopy() *ReplacePathRegex {
if in == nil {
return nil
}
out := new(ReplacePathRegex)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Retry) DeepCopyInto(out *Retry) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Retry.
func (in *Retry) DeepCopy() *Retry {
if in == nil {
return nil
}
out := new(Retry)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *StripPrefix) DeepCopyInto(out *StripPrefix) {
*out = *in
if in.Prefixes != nil {
in, out := &in.Prefixes, &out.Prefixes
*out = make([]string, len(*in))
copy(*out, *in)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new StripPrefix.
func (in *StripPrefix) DeepCopy() *StripPrefix {
if in == nil {
return nil
}
out := new(StripPrefix)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *StripPrefixRegex) DeepCopyInto(out *StripPrefixRegex) {
*out = *in
if in.Regex != nil {
in, out := &in.Regex, &out.Regex
*out = make([]string, len(*in))
copy(*out, *in)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new StripPrefixRegex.
func (in *StripPrefixRegex) DeepCopy() *StripPrefixRegex {
if in == nil {
return nil
}
out := new(StripPrefixRegex)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *TLSCLientCertificateDNInfo) DeepCopyInto(out *TLSCLientCertificateDNInfo) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TLSCLientCertificateDNInfo.
func (in *TLSCLientCertificateDNInfo) DeepCopy() *TLSCLientCertificateDNInfo {
if in == nil {
return nil
}
out := new(TLSCLientCertificateDNInfo)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *TLSClientCertificateInfo) DeepCopyInto(out *TLSClientCertificateInfo) {
*out = *in
if in.Subject != nil {
in, out := &in.Subject, &out.Subject
*out = new(TLSCLientCertificateDNInfo)
**out = **in
}
if in.Issuer != nil {
in, out := &in.Issuer, &out.Issuer
*out = new(TLSCLientCertificateDNInfo)
**out = **in
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TLSClientCertificateInfo.
func (in *TLSClientCertificateInfo) DeepCopy() *TLSClientCertificateInfo {
if in == nil {
return nil
}
out := new(TLSClientCertificateInfo)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in Users) DeepCopyInto(out *Users) {
{
in := &in
*out = make(Users, len(*in))
copy(*out, *in)
return
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Users.
func (in Users) DeepCopy() Users {
if in == nil {
return nil
}
out := new(Users)
in.DeepCopyInto(out)
return *out
}