diff --git a/cmd/traefik/traefik.go b/cmd/traefik/traefik.go index ffa01a2b2..d55eaa6a6 100644 --- a/cmd/traefik/traefik.go +++ b/cmd/traefik/traefik.go @@ -214,6 +214,7 @@ func setupServer(staticConfiguration *static.Configuration) (*server.Server, err pilotRegistry = metrics.RegisterPilot() aviator = pilot.New(staticConfiguration.Pilot.Token, pilotRegistry, routinesPool) + routinesPool.GoCtx(func(ctx context.Context) { aviator.Tick(ctx) }) @@ -348,7 +349,7 @@ func switchRouter(routerFactory *server.RouterFactory, serverEntryPointsTCP serv routers, udpRouters := routerFactory.CreateRouters(rtConf) if aviator != nil { - aviator.SetRuntimeConfiguration(rtConf) + aviator.SetDynamicConfiguration(conf) } serverEntryPointsTCP.Switch(routers) diff --git a/pkg/anonymize/anonymize.go b/pkg/anonymize/anonymize.go index 00602b1fe..68b6289c7 100644 --- a/pkg/anonymize/anonymize.go +++ b/pkg/anonymize/anonymize.go @@ -7,6 +7,8 @@ import ( "regexp" "github.com/mitchellh/copystructure" + "github.com/traefik/traefik/v2/pkg/config/dynamic" + "github.com/traefik/traefik/v2/pkg/tls" "mvdan.cc/xurls/v2" ) @@ -43,6 +45,11 @@ func doOnJSON(input string) string { } func doOnStruct(field reflect.Value) error { + if field.Type().AssignableTo(reflect.TypeOf(dynamic.PluginConf{})) { + resetPlugin(field) + return nil + } + switch field.Kind() { case reflect.Ptr: if !field.IsNil() { @@ -57,19 +64,48 @@ func doOnStruct(field reflect.Value) error { if !isExported(stField) { continue } + if stField.Tag.Get("export") == "true" { + // A struct field cannot be set it must be filled as pointer. + if fld.Kind() == reflect.Struct { + fldPtr := reflect.New(fld.Type()) + fldPtr.Elem().Set(fld) + + if err := doOnStruct(fldPtr); err != nil { + return err + } + + fld.Set(fldPtr.Elem()) + + continue + } + if err := doOnStruct(fld); err != nil { return err } - } else { - if err := reset(fld, stField.Name); err != nil { - return err - } + } else if err := reset(fld, stField.Name); err != nil { + return err } } case reflect.Map: for _, key := range field.MapKeys() { - if err := doOnStruct(field.MapIndex(key)); err != nil { + val := field.MapIndex(key) + + // A struct value cannot be set it must be filled as pointer. + if val.Kind() == reflect.Struct { + valPtr := reflect.New(val.Type()) + valPtr.Elem().Set(val) + + if err := doOnStruct(valPtr); err != nil { + return err + } + + field.SetMapIndex(key, valPtr.Elem()) + + continue + } + + if err := doOnStruct(val); err != nil { return err } } @@ -100,7 +136,11 @@ func reset(field reflect.Value, name string) error { } case reflect.String: if field.String() != "" { - field.Set(reflect.ValueOf(maskShort)) + if field.Type().AssignableTo(reflect.TypeOf(tls.FileOrContent(""))) { + field.Set(reflect.ValueOf(tls.FileOrContent(maskShort))) + } else { + field.Set(reflect.ValueOf(maskShort)) + } } case reflect.Map: if field.Len() > 0 { @@ -130,6 +170,13 @@ func reset(field reflect.Value, name string) error { return nil } +// resetPlugin resets the plugin configuration so it keep the plugin name but not its configuration. +func resetPlugin(field reflect.Value) { + for _, key := range field.MapKeys() { + field.SetMapIndex(key, reflect.ValueOf(struct{}{})) + } +} + // isExported return true is a struct field is exported, else false. func isExported(f reflect.StructField) bool { if f.PkgPath != "" && !f.Anonymous { diff --git a/pkg/anonymize/anonymize_config_test.go b/pkg/anonymize/anonymize_config_test.go index cf2feb9aa..98877ac2d 100644 --- a/pkg/anonymize/anonymize_config_test.go +++ b/pkg/anonymize/anonymize_config_test.go @@ -12,6 +12,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ptypes "github.com/traefik/paerser/types" + "github.com/traefik/traefik/v2/pkg/config/dynamic" "github.com/traefik/traefik/v2/pkg/config/static" "github.com/traefik/traefik/v2/pkg/ping" "github.com/traefik/traefik/v2/pkg/plugins" @@ -43,7 +44,438 @@ import ( var updateExpected = flag.Bool("update_expected", false, "Update expected files in fixtures") -func TestDo_globalConfiguration(t *testing.T) { +func TestDo_dynamicConfiguration(t *testing.T) { + config := &dynamic.Configuration{} + config.HTTP = &dynamic.HTTPConfiguration{ + Routers: map[string]*dynamic.Router{ + "foo": { + EntryPoints: []string{"foo"}, + Middlewares: []string{"foo"}, + Service: "foo", + Rule: "foo", + Priority: 42, + TLS: &dynamic.RouterTLSConfig{ + Options: "foo", + CertResolver: "foo", + Domains: []types.Domain{ + { + Main: "foo", + SANs: []string{"foo"}, + }, + }, + }, + }, + }, + Services: map[string]*dynamic.Service{ + "foo": { + LoadBalancer: &dynamic.ServersLoadBalancer{ + Sticky: &dynamic.Sticky{ + Cookie: &dynamic.Cookie{ + Name: "foo", + Secure: true, + HTTPOnly: true, + SameSite: "foo", + }, + }, + HealthCheck: &dynamic.HealthCheck{ + Scheme: "foo", + Path: "foo", + Port: 42, + Interval: "foo", + Timeout: "foo", + Hostname: "foo", + FollowRedirects: boolPtr(true), + Headers: map[string]string{ + "foo": "bar", + }, + }, + PassHostHeader: boolPtr(true), + ResponseForwarding: &dynamic.ResponseForwarding{ + FlushInterval: "foo", + }, + ServersTransport: "foo", + Servers: []dynamic.Server{ + { + URL: "http://127.0.0.1:8080", + }, + }, + }, + }, + "bar": { + Weighted: &dynamic.WeightedRoundRobin{ + Services: []dynamic.WRRService{ + { + Name: "foo", + Weight: intPtr(42), + }, + }, + Sticky: &dynamic.Sticky{ + Cookie: &dynamic.Cookie{ + Name: "foo", + Secure: true, + HTTPOnly: true, + SameSite: "foo", + }, + }, + }, + }, + "baz": { + Mirroring: &dynamic.Mirroring{ + Service: "foo", + MaxBodySize: int64Ptr(42), + Mirrors: []dynamic.MirrorService{ + { + Name: "foo", + Percent: 42, + }, + }, + }, + }, + }, + ServersTransports: map[string]*dynamic.ServersTransport{ + "foo": { + ServerName: "foo", + InsecureSkipVerify: true, + RootCAs: []traefiktls.FileOrContent{"rootca.pem"}, + Certificates: []traefiktls.Certificate{ + { + CertFile: "cert.pem", + KeyFile: "key.pem", + }, + }, + MaxIdleConnsPerHost: 42, + ForwardingTimeouts: &dynamic.ForwardingTimeouts{ + DialTimeout: 42, + ResponseHeaderTimeout: 42, + IdleConnTimeout: 42, + }, + }, + }, + Models: map[string]*dynamic.Model{ + "foo": { + Middlewares: []string{"foo"}, + TLS: &dynamic.RouterTLSConfig{ + Options: "foo", + CertResolver: "foo", + Domains: []types.Domain{ + { + Main: "foo", + SANs: []string{"foo"}, + }, + }, + }, + }, + }, + Middlewares: map[string]*dynamic.Middleware{ + "foo": { + AddPrefix: &dynamic.AddPrefix{ + Prefix: "foo", + }, + StripPrefix: &dynamic.StripPrefix{ + Prefixes: []string{"foo"}, + ForceSlash: true, + }, + StripPrefixRegex: &dynamic.StripPrefixRegex{ + Regex: []string{"foo"}, + }, + ReplacePath: &dynamic.ReplacePath{ + Path: "foo", + }, + ReplacePathRegex: &dynamic.ReplacePathRegex{ + Regex: "foo", + Replacement: "foo", + }, + Chain: &dynamic.Chain{ + Middlewares: []string{"foo"}, + }, + IPWhiteList: &dynamic.IPWhiteList{ + SourceRange: []string{"foo"}, + IPStrategy: &dynamic.IPStrategy{ + Depth: 42, + ExcludedIPs: []string{"127.0.0.1"}, + }, + }, + Headers: &dynamic.Headers{ + CustomRequestHeaders: map[string]string{"foo": "bar"}, + CustomResponseHeaders: map[string]string{"foo": "bar"}, + AccessControlAllowCredentials: true, + AccessControlAllowHeaders: []string{"foo"}, + AccessControlAllowMethods: []string{"foo"}, + AccessControlAllowOrigin: "foo", + AccessControlAllowOriginList: []string{"foo"}, + AccessControlAllowOriginListRegex: []string{"foo"}, + AccessControlExposeHeaders: []string{"foo"}, + AccessControlMaxAge: 42, + AddVaryHeader: true, + AllowedHosts: []string{"foo"}, + HostsProxyHeaders: []string{"foo"}, + SSLRedirect: true, + SSLTemporaryRedirect: true, + SSLHost: "foo", + SSLProxyHeaders: map[string]string{"foo": "bar"}, + SSLForceHost: true, + STSSeconds: 42, + STSIncludeSubdomains: true, + STSPreload: true, + ForceSTSHeader: true, + FrameDeny: true, + CustomFrameOptionsValue: "foo", + ContentTypeNosniff: true, + BrowserXSSFilter: true, + CustomBrowserXSSValue: "foo", + ContentSecurityPolicy: "foo", + PublicKey: "foo", + ReferrerPolicy: "foo", + FeaturePolicy: "foo", + IsDevelopment: true, + }, + Errors: &dynamic.ErrorPage{ + Status: []string{"foo"}, + Service: "foo", + Query: "foo", + }, + RateLimit: &dynamic.RateLimit{ + Average: 42, + Period: 42, + Burst: 42, + SourceCriterion: &dynamic.SourceCriterion{ + IPStrategy: &dynamic.IPStrategy{ + Depth: 42, + ExcludedIPs: []string{"foo"}, + }, + RequestHeaderName: "foo", + RequestHost: true, + }, + }, + RedirectRegex: &dynamic.RedirectRegex{ + Regex: "foo", + Replacement: "foo", + Permanent: true, + }, + RedirectScheme: &dynamic.RedirectScheme{ + Scheme: "foo", + Port: "foo", + Permanent: true, + }, + BasicAuth: &dynamic.BasicAuth{ + Users: []string{"foo"}, + UsersFile: "foo", + Realm: "foo", + RemoveHeader: true, + HeaderField: "foo", + }, + DigestAuth: &dynamic.DigestAuth{ + Users: []string{"foo"}, + UsersFile: "foo", + RemoveHeader: true, + Realm: "foo", + HeaderField: "foo", + }, + ForwardAuth: &dynamic.ForwardAuth{ + Address: "127.0.0.1", + TLS: &dynamic.ClientTLS{ + CA: "ca.pem", + CAOptional: true, + Cert: "cert.pem", + Key: "cert.pem", + InsecureSkipVerify: true, + }, + TrustForwardHeader: true, + AuthResponseHeaders: []string{"foo"}, + AuthResponseHeadersRegex: "foo", + AuthRequestHeaders: []string{"foo"}, + }, + InFlightReq: &dynamic.InFlightReq{ + Amount: 42, + SourceCriterion: &dynamic.SourceCriterion{ + IPStrategy: &dynamic.IPStrategy{ + Depth: 42, + ExcludedIPs: []string{"foo"}, + }, + RequestHeaderName: "foo", + RequestHost: true, + }, + }, + Buffering: &dynamic.Buffering{ + MaxRequestBodyBytes: 42, + MemRequestBodyBytes: 42, + MaxResponseBodyBytes: 42, + MemResponseBodyBytes: 42, + RetryExpression: "foo", + }, + CircuitBreaker: &dynamic.CircuitBreaker{ + Expression: "foo", + }, + Compress: &dynamic.Compress{ + ExcludedContentTypes: []string{"foo"}, + }, + PassTLSClientCert: &dynamic.PassTLSClientCert{ + PEM: true, + Info: &dynamic.TLSClientCertificateInfo{ + NotAfter: true, + NotBefore: true, + Sans: true, + Subject: &dynamic.TLSCLientCertificateDNInfo{ + Country: true, + Province: true, + Locality: true, + Organization: true, + CommonName: true, + SerialNumber: true, + DomainComponent: true, + }, + Issuer: &dynamic.TLSCLientCertificateDNInfo{ + Country: true, + Province: true, + Locality: true, + Organization: true, + CommonName: true, + SerialNumber: true, + DomainComponent: true, + }, + SerialNumber: true, + }, + }, + Retry: &dynamic.Retry{ + Attempts: 42, + InitialInterval: 42, + }, + ContentType: &dynamic.ContentType{ + AutoDetect: true, + }, + Plugin: map[string]dynamic.PluginConf{ + "foo": { + "answer": struct{ Answer int }{ + Answer: 42, + }, + }, + }, + }, + }, + } + config.TCP = &dynamic.TCPConfiguration{ + Routers: map[string]*dynamic.TCPRouter{ + "foo": { + EntryPoints: []string{"foo"}, + Service: "foo", + Rule: "foo", + TLS: &dynamic.RouterTCPTLSConfig{ + Passthrough: true, + Options: "foo", + CertResolver: "foo", + Domains: []types.Domain{ + { + Main: "foo", + SANs: []string{"foo"}, + }, + }, + }, + }, + }, + Services: map[string]*dynamic.TCPService{ + "foo": { + LoadBalancer: &dynamic.TCPServersLoadBalancer{ + TerminationDelay: intPtr(42), + ProxyProtocol: &dynamic.ProxyProtocol{ + Version: 42, + }, + Servers: []dynamic.TCPServer{ + { + Address: "127.0.0.1:8080", + }, + }, + }, + }, + "bar": { + Weighted: &dynamic.TCPWeightedRoundRobin{ + Services: []dynamic.TCPWRRService{ + { + Name: "foo", + Weight: intPtr(42), + }, + }, + }, + }, + }, + } + config.UDP = &dynamic.UDPConfiguration{ + Routers: map[string]*dynamic.UDPRouter{ + "foo": { + EntryPoints: []string{"foo"}, + Service: "foo", + }, + }, + Services: map[string]*dynamic.UDPService{ + "foo": { + LoadBalancer: &dynamic.UDPServersLoadBalancer{ + Servers: []dynamic.UDPServer{ + { + Address: "127.0.0.1:8080", + }, + }, + }, + }, + "bar": { + Weighted: &dynamic.UDPWeightedRoundRobin{ + Services: []dynamic.UDPWRRService{ + { + Name: "foo", + Weight: intPtr(42), + }, + }, + }, + }, + }, + } + config.TLS = &dynamic.TLSConfiguration{ + Options: map[string]traefiktls.Options{ + "foo": { + MinVersion: "foo", + MaxVersion: "foo", + CipherSuites: []string{"foo"}, + CurvePreferences: []string{"foo"}, + ClientAuth: traefiktls.ClientAuth{ + CAFiles: []traefiktls.FileOrContent{"ca.pem"}, + ClientAuthType: "RequireAndVerifyClientCert", + }, + SniStrict: true, + PreferServerCipherSuites: true, + }, + }, + Certificates: []*traefiktls.CertAndStores{ + { + Certificate: traefiktls.Certificate{ + CertFile: "cert.pem", + KeyFile: "key.pem", + }, + Stores: []string{"foo"}, + }, + }, + Stores: map[string]traefiktls.Store{ + "foo": { + DefaultCertificate: &traefiktls.Certificate{ + CertFile: "cert.pem", + KeyFile: "key.pem", + }, + }, + }, + } + + expectedConfiguration, err := ioutil.ReadFile("./testdata/anonymized-dynamic-config.json") + require.NoError(t, err) + + cleanJSON, err := Do(config, true) + require.NoError(t, err) + + if *updateExpected { + require.NoError(t, ioutil.WriteFile("testdata/anonymized-dynamic-config.json", []byte(cleanJSON), 0666)) + } + + expected := strings.TrimSuffix(string(expectedConfiguration), "\n") + assert.Equal(t, expected, cleanJSON) +} + +func TestDo_staticConfiguration(t *testing.T) { config := &static.Configuration{} config.Global = &static.Global{ @@ -538,3 +970,15 @@ func TestDo_globalConfiguration(t *testing.T) { expected := strings.TrimSuffix(string(expectedConfiguration), "\n") assert.Equal(t, expected, cleanJSON) } + +func boolPtr(value bool) *bool { + return &value +} + +func intPtr(value int) *int { + return &value +} + +func int64Ptr(value int64) *int64 { + return &value +} diff --git a/pkg/anonymize/anonymize_doOnStruct_test.go b/pkg/anonymize/anonymize_doOnStruct_test.go index 19f1089e6..2f72f21f6 100644 --- a/pkg/anonymize/anonymize_doOnStruct_test.go +++ b/pkg/anonymize/anonymize_doOnStruct_test.go @@ -5,6 +5,7 @@ import ( "testing" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) type Courgette struct { @@ -39,7 +40,6 @@ func Test_doOnStruct(t *testing.T) { name string base *Carotte expected *Carotte - hasError bool }{ { name: "primitive", @@ -145,7 +145,7 @@ func Test_doOnStruct(t *testing.T) { }, }, { - name: "export map string/struct (UNSAFE)", + name: "export map string/struct", base: &Carotte{ Name: "koko", ESAubergine: map[string]Tomate{ @@ -158,11 +158,10 @@ func Test_doOnStruct(t *testing.T) { Name: "xxxx", ESAubergine: map[string]Tomate{ "foo": { - Ji: "JiJiJi", + Ji: "xxxx", }, }, }, - hasError: true, }, } @@ -170,12 +169,7 @@ func Test_doOnStruct(t *testing.T) { t.Run(test.name, func(t *testing.T) { val := reflect.ValueOf(test.base).Elem() err := doOnStruct(val) - if !test.hasError && err != nil { - t.Fatal(err) - } - if test.hasError && err == nil { - t.Fatal("Got no error but want an error.") - } + require.NoError(t, err) assert.EqualValues(t, test.expected, test.base) }) diff --git a/pkg/anonymize/testdata/anonymized-dynamic-config.json b/pkg/anonymize/testdata/anonymized-dynamic-config.json new file mode 100644 index 000000000..ca116768b --- /dev/null +++ b/pkg/anonymize/testdata/anonymized-dynamic-config.json @@ -0,0 +1,476 @@ +{ + "http": { + "routers": { + "foo": { + "entryPoints": [ + "foo" + ], + "middlewares": [ + "foo" + ], + "service": "foo", + "rule": "xxxx", + "priority": 42, + "tls": { + "options": "foo", + "certResolver": "foo", + "domains": [ + { + "main": "xxxx", + "sans": [ + "xxxx" + ] + } + ] + } + } + }, + "services": { + "bar": { + "weighted": { + "services": [ + { + "name": "foo", + "weight": 42 + } + ], + "sticky": { + "cookie": { + "name": "foo", + "secure": true, + "httpOnly": true, + "sameSite": "foo" + } + } + } + }, + "baz": { + "mirroring": { + "service": "foo", + "maxBodySize": 42, + "mirrors": [ + { + "name": "foo", + "percent": 42 + } + ] + } + }, + "foo": { + "loadBalancer": { + "sticky": { + "cookie": { + "name": "foo", + "secure": true, + "httpOnly": true, + "sameSite": "foo" + } + }, + "servers": [ + { + "url": "xxxx" + } + ], + "healthCheck": { + "scheme": "foo", + "path": "foo", + "port": 42, + "interval": "foo", + "timeout": "foo", + "hostname": "xxxx", + "followRedirects": true, + "headers": { + "foo": "bar" + } + }, + "passHostHeader": true, + "responseForwarding": { + "flushInterval": "foo" + }, + "serversTransport": "foo" + } + } + }, + "middlewares": { + "foo": { + "addPrefix": { + "prefix": "foo" + }, + "stripPrefix": { + "prefixes": [ + "foo" + ], + "forceSlash": true + }, + "stripPrefixRegex": { + "regex": [ + "foo" + ] + }, + "replacePath": { + "path": "foo" + }, + "replacePathRegex": { + "regex": "foo", + "replacement": "foo" + }, + "chain": { + "middlewares": [ + "foo" + ] + }, + "ipWhiteList": { + "sourceRange": [ + "xxxx" + ], + "ipStrategy": { + "depth": 42, + "excludedIPs": [ + "xxxx" + ] + } + }, + "headers": { + "customRequestHeaders": { + "foo": "bar" + }, + "customResponseHeaders": { + "foo": "bar" + }, + "accessControlAllowCredentials": true, + "accessControlAllowHeaders": [ + "foo" + ], + "accessControlAllowMethods": [ + "foo" + ], + "accessControlAllowOrigin": "xxxx", + "accessControlAllowOriginList": [ + "xxxx" + ], + "accessControlAllowOriginListRegex": [ + "xxxx" + ], + "accessControlExposeHeaders": [ + "foo" + ], + "accessControlMaxAge": 42, + "addVaryHeader": true, + "allowedHosts": [ + "xxxx" + ], + "hostsProxyHeaders": [ + "foo" + ], + "sslRedirect": true, + "sslTemporaryRedirect": true, + "sslHost": "xxxx", + "sslForceHost": true, + "stsSeconds": 42, + "stsIncludeSubdomains": true, + "stsPreload": true, + "forceSTSHeader": true, + "frameDeny": true, + "customFrameOptionsValue": "xxxx", + "contentTypeNosniff": true, + "browserXssFilter": true, + "customBrowserXSSValue": "xxxx", + "contentSecurityPolicy": "xxxx", + "publicKey": "xxxx", + "referrerPolicy": "foo", + "featurePolicy": "foo", + "isDevelopment": true + }, + "errors": { + "status": [ + "foo" + ], + "service": "foo", + "query": "foo" + }, + "rateLimit": { + "average": 42, + "period": 42, + "burst": 42, + "sourceCriterion": { + "ipStrategy": { + "depth": 42, + "excludedIPs": [ + "xxxx" + ] + }, + "requestHeaderName": "foo", + "requestHost": true + } + }, + "redirectRegex": { + "regex": "xxxx", + "replacement": "xxxx", + "permanent": true + }, + "redirectScheme": { + "scheme": "foo", + "port": "foo", + "permanent": true + }, + "basicAuth": { + "users": [ + "xxxx" + ], + "usersFile": "xxxx", + "realm": "xxxx", + "removeHeader": true, + "headerField": "foo" + }, + "digestAuth": { + "users": [ + "xxxx" + ], + "usersFile": "xxxx", + "removeHeader": true, + "realm": "xxxx", + "headerField": "foo" + }, + "forwardAuth": { + "address": "xxxx", + "tls": { + "ca": "xxxx", + "caOptional": true, + "cert": "xxxx", + "key": "xxxx", + "insecureSkipVerify": true + }, + "trustForwardHeader": true, + "authResponseHeaders": [ + "foo" + ], + "authResponseHeadersRegex": "foo", + "authRequestHeaders": [ + "foo" + ] + }, + "inFlightReq": { + "amount": 42, + "sourceCriterion": { + "ipStrategy": { + "depth": 42, + "excludedIPs": [ + "xxxx" + ] + }, + "requestHeaderName": "foo", + "requestHost": true + } + }, + "buffering": { + "maxRequestBodyBytes": 42, + "memRequestBodyBytes": 42, + "maxResponseBodyBytes": 42, + "memResponseBodyBytes": 42, + "retryExpression": "foo" + }, + "circuitBreaker": { + "expression": "foo" + }, + "compress": { + "excludedContentTypes": [ + "foo" + ] + }, + "passTLSClientCert": { + "pem": true, + "info": { + "notAfter": true, + "notBefore": true, + "sans": true, + "subject": { + "country": true, + "province": true, + "locality": true, + "organization": true, + "commonName": true, + "serialNumber": true, + "domainComponent": true + }, + "issuer": { + "country": true, + "province": true, + "locality": true, + "organization": true, + "commonName": true, + "serialNumber": true, + "domainComponent": true + }, + "serialNumber": true + } + }, + "retry": { + "attempts": 42, + "initialInterval": 42 + }, + "contentType": { + "autoDetect": true + }, + "plugin": { + "foo": { + "answer": {} + } + } + } + }, + "models": { + "foo": { + "middlewares": [ + "foo" + ], + "tls": { + "options": "foo", + "certResolver": "foo", + "domains": [ + { + "main": "xxxx", + "sans": [ + "xxxx" + ] + } + ] + } + } + }, + "serversTransports": { + "foo": { + "serverName": "xxxx", + "insecureSkipVerify": true, + "rootCAs": [ + "xxxx" + ], + "certificates": [ + { + "certFile": "xxxx", + "keyFile": "xxxx" + } + ], + "maxIdleConnsPerHost": 42, + "forwardingTimeouts": { + "dialTimeout": 42, + "responseHeaderTimeout": 42, + "idleConnTimeout": 42 + } + } + } + }, + "tcp": { + "routers": { + "foo": { + "entryPoints": [ + "foo" + ], + "service": "foo", + "rule": "xxxx", + "tls": { + "passthrough": true, + "options": "foo", + "certResolver": "foo", + "domains": [ + { + "main": "xxxx", + "sans": [ + "xxxx" + ] + } + ] + } + } + }, + "services": { + "bar": { + "weighted": { + "services": [ + { + "name": "foo", + "weight": 42 + } + ] + } + }, + "foo": { + "loadBalancer": { + "terminationDelay": 42, + "proxyProtocol": { + "version": 42 + }, + "servers": [ + { + "address": "xxxx" + } + ] + } + } + } + }, + "udp": { + "routers": { + "foo": { + "entryPoints": [ + "foo" + ], + "service": "foo" + } + }, + "services": { + "bar": { + "weighted": { + "services": [ + { + "name": "foo", + "weight": 42 + } + ] + } + }, + "foo": { + "loadBalancer": { + "servers": [ + { + "address": "xxxx" + } + ] + } + } + } + }, + "tls": { + "certificates": [ + { + "certFile": "xxxx", + "keyFile": "xxxx", + "stores": [ + "foo" + ] + } + ], + "options": { + "foo": { + "minVersion": "foo", + "maxVersion": "foo", + "cipherSuites": [ + "foo" + ], + "curvePreferences": [ + "foo" + ], + "clientAuth": {}, + "sniStrict": true, + "preferServerCipherSuites": true + } + }, + "stores": { + "foo": { + "defaultCertificate": { + "certFile": "xxxx", + "keyFile": "xxxx" + } + } + } + } +} diff --git a/pkg/config/dynamic/config.go b/pkg/config/dynamic/config.go index bc4da26dd..d23315d6b 100644 --- a/pkg/config/dynamic/config.go +++ b/pkg/config/dynamic/config.go @@ -21,17 +21,17 @@ type Configurations map[string]*Configuration // Configuration is the root of the dynamic configuration. type Configuration struct { - HTTP *HTTPConfiguration `json:"http,omitempty" toml:"http,omitempty" yaml:"http,omitempty"` - TCP *TCPConfiguration `json:"tcp,omitempty" toml:"tcp,omitempty" yaml:"tcp,omitempty"` - UDP *UDPConfiguration `json:"udp,omitempty" toml:"udp,omitempty" yaml:"udp,omitempty"` - TLS *TLSConfiguration `json:"tls,omitempty" toml:"tls,omitempty" yaml:"tls,omitempty"` + HTTP *HTTPConfiguration `json:"http,omitempty" toml:"http,omitempty" yaml:"http,omitempty" export:"true"` + TCP *TCPConfiguration `json:"tcp,omitempty" toml:"tcp,omitempty" yaml:"tcp,omitempty" export:"true"` + UDP *UDPConfiguration `json:"udp,omitempty" toml:"udp,omitempty" yaml:"udp,omitempty" export:"true"` + TLS *TLSConfiguration `json:"tls,omitempty" toml:"tls,omitempty" yaml:"tls,omitempty" export:"true"` } // +k8s:deepcopy-gen=true // TLSConfiguration contains all the configuration parameters of a TLS connection. type TLSConfiguration struct { - Certificates []*tls.CertAndStores `json:"certificates,omitempty" toml:"certificates,omitempty" yaml:"certificates,omitempty" label:"-"` - Options map[string]tls.Options `json:"options,omitempty" toml:"options,omitempty" yaml:"options,omitempty"` - Stores map[string]tls.Store `json:"stores,omitempty" toml:"stores,omitempty" yaml:"stores,omitempty"` + Certificates []*tls.CertAndStores `json:"certificates,omitempty" toml:"certificates,omitempty" yaml:"certificates,omitempty" label:"-" export:"true"` + Options map[string]tls.Options `json:"options,omitempty" toml:"options,omitempty" yaml:"options,omitempty" export:"true"` + Stores map[string]tls.Store `json:"stores,omitempty" toml:"stores,omitempty" yaml:"stores,omitempty" export:"true"` } diff --git a/pkg/config/dynamic/http_config.go b/pkg/config/dynamic/http_config.go index 48c5ec801..1d727a2b8 100644 --- a/pkg/config/dynamic/http_config.go +++ b/pkg/config/dynamic/http_config.go @@ -13,58 +13,58 @@ import ( // HTTPConfiguration contains all the HTTP configuration parameters. type HTTPConfiguration struct { - Routers map[string]*Router `json:"routers,omitempty" toml:"routers,omitempty" yaml:"routers,omitempty"` - Services map[string]*Service `json:"services,omitempty" toml:"services,omitempty" yaml:"services,omitempty"` - Middlewares map[string]*Middleware `json:"middlewares,omitempty" toml:"middlewares,omitempty" yaml:"middlewares,omitempty"` - Models map[string]*Model `json:"models,omitempty" toml:"models,omitempty" yaml:"models,omitempty"` - ServersTransports map[string]*ServersTransport `json:"serversTransports,omitempty" toml:"serversTransports,omitempty" yaml:"serversTransports,omitempty" label:"-"` + Routers map[string]*Router `json:"routers,omitempty" toml:"routers,omitempty" yaml:"routers,omitempty" export:"true"` + Services map[string]*Service `json:"services,omitempty" toml:"services,omitempty" yaml:"services,omitempty" export:"true"` + Middlewares map[string]*Middleware `json:"middlewares,omitempty" toml:"middlewares,omitempty" yaml:"middlewares,omitempty" export:"true"` + Models map[string]*Model `json:"models,omitempty" toml:"models,omitempty" yaml:"models,omitempty" export:"true"` + ServersTransports map[string]*ServersTransport `json:"serversTransports,omitempty" toml:"serversTransports,omitempty" yaml:"serversTransports,omitempty" label:"-" export:"true"` } // +k8s:deepcopy-gen=true // Model is a set of default router's values. type Model struct { - Middlewares []string `json:"middlewares,omitempty" toml:"middlewares,omitempty" yaml:"middlewares,omitempty"` - TLS *RouterTLSConfig `json:"tls,omitempty" toml:"tls,omitempty" yaml:"tls,omitempty" label:"allowEmpty" file:"allowEmpty"` + Middlewares []string `json:"middlewares,omitempty" toml:"middlewares,omitempty" yaml:"middlewares,omitempty" export:"true"` + TLS *RouterTLSConfig `json:"tls,omitempty" toml:"tls,omitempty" yaml:"tls,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"` } // +k8s:deepcopy-gen=true // Service holds a service configuration (can only be of one type at the same time). type Service struct { - LoadBalancer *ServersLoadBalancer `json:"loadBalancer,omitempty" toml:"loadBalancer,omitempty" yaml:"loadBalancer,omitempty"` - Weighted *WeightedRoundRobin `json:"weighted,omitempty" toml:"weighted,omitempty" yaml:"weighted,omitempty" label:"-"` - Mirroring *Mirroring `json:"mirroring,omitempty" toml:"mirroring,omitempty" yaml:"mirroring,omitempty" label:"-"` + LoadBalancer *ServersLoadBalancer `json:"loadBalancer,omitempty" toml:"loadBalancer,omitempty" yaml:"loadBalancer,omitempty" export:"true"` + Weighted *WeightedRoundRobin `json:"weighted,omitempty" toml:"weighted,omitempty" yaml:"weighted,omitempty" label:"-" export:"true"` + Mirroring *Mirroring `json:"mirroring,omitempty" toml:"mirroring,omitempty" yaml:"mirroring,omitempty" label:"-" export:"true"` } // +k8s:deepcopy-gen=true // Router holds the router configuration. type Router struct { - EntryPoints []string `json:"entryPoints,omitempty" toml:"entryPoints,omitempty" yaml:"entryPoints,omitempty"` - Middlewares []string `json:"middlewares,omitempty" toml:"middlewares,omitempty" yaml:"middlewares,omitempty"` - Service string `json:"service,omitempty" toml:"service,omitempty" yaml:"service,omitempty"` + EntryPoints []string `json:"entryPoints,omitempty" toml:"entryPoints,omitempty" yaml:"entryPoints,omitempty" export:"true"` + Middlewares []string `json:"middlewares,omitempty" toml:"middlewares,omitempty" yaml:"middlewares,omitempty" export:"true"` + Service string `json:"service,omitempty" toml:"service,omitempty" yaml:"service,omitempty" export:"true"` Rule string `json:"rule,omitempty" toml:"rule,omitempty" yaml:"rule,omitempty"` - Priority int `json:"priority,omitempty" toml:"priority,omitempty,omitzero" yaml:"priority,omitempty"` - TLS *RouterTLSConfig `json:"tls,omitempty" toml:"tls,omitempty" yaml:"tls,omitempty" label:"allowEmpty" file:"allowEmpty"` + Priority int `json:"priority,omitempty" toml:"priority,omitempty,omitzero" yaml:"priority,omitempty" export:"true"` + TLS *RouterTLSConfig `json:"tls,omitempty" toml:"tls,omitempty" yaml:"tls,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"` } // +k8s:deepcopy-gen=true // RouterTLSConfig holds the TLS configuration for a router. type RouterTLSConfig struct { - Options string `json:"options,omitempty" toml:"options,omitempty" yaml:"options,omitempty"` - CertResolver string `json:"certResolver,omitempty" toml:"certResolver,omitempty" yaml:"certResolver,omitempty"` - Domains []types.Domain `json:"domains,omitempty" toml:"domains,omitempty" yaml:"domains,omitempty"` + Options string `json:"options,omitempty" toml:"options,omitempty" yaml:"options,omitempty" export:"true"` + CertResolver string `json:"certResolver,omitempty" toml:"certResolver,omitempty" yaml:"certResolver,omitempty" export:"true"` + Domains []types.Domain `json:"domains,omitempty" toml:"domains,omitempty" yaml:"domains,omitempty" export:"true"` } // +k8s:deepcopy-gen=true // Mirroring holds the Mirroring configuration. type Mirroring struct { - Service string `json:"service,omitempty" toml:"service,omitempty" yaml:"service,omitempty"` - MaxBodySize *int64 `json:"maxBodySize,omitempty" toml:"maxBodySize,omitempty" yaml:"maxBodySize,omitempty"` - Mirrors []MirrorService `json:"mirrors,omitempty" toml:"mirrors,omitempty" yaml:"mirrors,omitempty"` + Service string `json:"service,omitempty" toml:"service,omitempty" yaml:"service,omitempty" export:"true"` + MaxBodySize *int64 `json:"maxBodySize,omitempty" toml:"maxBodySize,omitempty" yaml:"maxBodySize,omitempty" export:"true"` + Mirrors []MirrorService `json:"mirrors,omitempty" toml:"mirrors,omitempty" yaml:"mirrors,omitempty" export:"true"` } // SetDefaults Default values for a WRRService. @@ -77,24 +77,24 @@ func (m *Mirroring) SetDefaults() { // MirrorService holds the MirrorService configuration. type MirrorService struct { - Name string `json:"name,omitempty" toml:"name,omitempty" yaml:"name,omitempty"` - Percent int `json:"percent,omitempty" toml:"percent,omitempty" yaml:"percent,omitempty"` + Name string `json:"name,omitempty" toml:"name,omitempty" yaml:"name,omitempty" export:"true"` + Percent int `json:"percent,omitempty" toml:"percent,omitempty" yaml:"percent,omitempty" export:"true"` } // +k8s:deepcopy-gen=true // WeightedRoundRobin is a weighted round robin load-balancer of services. type WeightedRoundRobin struct { - Services []WRRService `json:"services,omitempty" toml:"services,omitempty" yaml:"services,omitempty"` - Sticky *Sticky `json:"sticky,omitempty" toml:"sticky,omitempty" yaml:"sticky,omitempty"` + Services []WRRService `json:"services,omitempty" toml:"services,omitempty" yaml:"services,omitempty" export:"true"` + Sticky *Sticky `json:"sticky,omitempty" toml:"sticky,omitempty" yaml:"sticky,omitempty" export:"true"` } // +k8s:deepcopy-gen=true // WRRService is a reference to a service load-balanced with weighted round robin. type WRRService struct { - Name string `json:"name,omitempty" toml:"name,omitempty" yaml:"name,omitempty"` - Weight *int `json:"weight,omitempty" toml:"weight,omitempty" yaml:"weight,omitempty"` + Name string `json:"name,omitempty" toml:"name,omitempty" yaml:"name,omitempty" export:"true"` + Weight *int `json:"weight,omitempty" toml:"weight,omitempty" yaml:"weight,omitempty" export:"true"` } // SetDefaults Default values for a WRRService. @@ -107,29 +107,29 @@ func (w *WRRService) SetDefaults() { // Sticky holds the sticky configuration. type Sticky struct { - Cookie *Cookie `json:"cookie,omitempty" toml:"cookie,omitempty" yaml:"cookie,omitempty" label:"allowEmpty" file:"allowEmpty"` + Cookie *Cookie `json:"cookie,omitempty" toml:"cookie,omitempty" yaml:"cookie,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"` } // +k8s:deepcopy-gen=true // Cookie holds the sticky configuration based on cookie. type Cookie struct { - Name string `json:"name,omitempty" toml:"name,omitempty" yaml:"name,omitempty"` - Secure bool `json:"secure,omitempty" toml:"secure,omitempty" yaml:"secure,omitempty"` - HTTPOnly bool `json:"httpOnly,omitempty" toml:"httpOnly,omitempty" yaml:"httpOnly,omitempty"` - SameSite string `json:"sameSite,omitempty" toml:"sameSite,omitempty" yaml:"sameSite,omitempty"` + Name string `json:"name,omitempty" toml:"name,omitempty" yaml:"name,omitempty" export:"true"` + Secure bool `json:"secure,omitempty" toml:"secure,omitempty" yaml:"secure,omitempty" export:"true"` + HTTPOnly bool `json:"httpOnly,omitempty" toml:"httpOnly,omitempty" yaml:"httpOnly,omitempty" export:"true"` + SameSite string `json:"sameSite,omitempty" toml:"sameSite,omitempty" yaml:"sameSite,omitempty" export:"true"` } // +k8s:deepcopy-gen=true // ServersLoadBalancer holds the ServersLoadBalancer configuration. type ServersLoadBalancer struct { - Sticky *Sticky `json:"sticky,omitempty" toml:"sticky,omitempty" yaml:"sticky,omitempty" label:"allowEmpty" file:"allowEmpty"` - Servers []Server `json:"servers,omitempty" toml:"servers,omitempty" yaml:"servers,omitempty" label-slice-as-struct:"server"` - HealthCheck *HealthCheck `json:"healthCheck,omitempty" toml:"healthCheck,omitempty" yaml:"healthCheck,omitempty"` - PassHostHeader *bool `json:"passHostHeader" toml:"passHostHeader" yaml:"passHostHeader"` - ResponseForwarding *ResponseForwarding `json:"responseForwarding,omitempty" toml:"responseForwarding,omitempty" yaml:"responseForwarding,omitempty"` - ServersTransport string `json:"serversTransport,omitempty" toml:"serversTransport,omitempty" yaml:"serversTransport,omitempty"` + Sticky *Sticky `json:"sticky,omitempty" toml:"sticky,omitempty" yaml:"sticky,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"` + Servers []Server `json:"servers,omitempty" toml:"servers,omitempty" yaml:"servers,omitempty" label-slice-as-struct:"server" export:"true"` + HealthCheck *HealthCheck `json:"healthCheck,omitempty" toml:"healthCheck,omitempty" yaml:"healthCheck,omitempty" export:"true"` + PassHostHeader *bool `json:"passHostHeader" toml:"passHostHeader" yaml:"passHostHeader" export:"true"` + ResponseForwarding *ResponseForwarding `json:"responseForwarding,omitempty" toml:"responseForwarding,omitempty" yaml:"responseForwarding,omitempty" export:"true"` + ServersTransport string `json:"serversTransport,omitempty" toml:"serversTransport,omitempty" yaml:"serversTransport,omitempty" export:"true"` } // Mergeable tells if the given service is mergeable. @@ -159,7 +159,7 @@ func (l *ServersLoadBalancer) SetDefaults() { // ResponseForwarding holds configuration for the forward of the response. type ResponseForwarding struct { - FlushInterval string `json:"flushInterval,omitempty" toml:"flushInterval,omitempty" yaml:"flushInterval,omitempty"` + FlushInterval string `json:"flushInterval,omitempty" toml:"flushInterval,omitempty" yaml:"flushInterval,omitempty" export:"true"` } // +k8s:deepcopy-gen=true @@ -180,16 +180,16 @@ func (s *Server) SetDefaults() { // HealthCheck holds the HealthCheck configuration. type HealthCheck struct { - Scheme string `json:"scheme,omitempty" toml:"scheme,omitempty" yaml:"scheme,omitempty"` - Path string `json:"path,omitempty" toml:"path,omitempty" yaml:"path,omitempty"` - Port int `json:"port,omitempty" toml:"port,omitempty,omitzero" yaml:"port,omitempty"` + Scheme string `json:"scheme,omitempty" toml:"scheme,omitempty" yaml:"scheme,omitempty" export:"true"` + Path string `json:"path,omitempty" toml:"path,omitempty" yaml:"path,omitempty" export:"true"` + Port int `json:"port,omitempty" toml:"port,omitempty,omitzero" yaml:"port,omitempty" export:"true"` // FIXME change string to ptypes.Duration - Interval string `json:"interval,omitempty" toml:"interval,omitempty" yaml:"interval,omitempty"` + Interval string `json:"interval,omitempty" toml:"interval,omitempty" yaml:"interval,omitempty" export:"true"` // FIXME change string to ptypes.Duration - Timeout string `json:"timeout,omitempty" toml:"timeout,omitempty" yaml:"timeout,omitempty"` + Timeout string `json:"timeout,omitempty" toml:"timeout,omitempty" yaml:"timeout,omitempty" export:"true"` Hostname string `json:"hostname,omitempty" toml:"hostname,omitempty" yaml:"hostname,omitempty"` - FollowRedirects *bool `json:"followRedirects" toml:"followRedirects" yaml:"followRedirects"` - Headers map[string]string `json:"headers,omitempty" toml:"headers,omitempty" yaml:"headers,omitempty"` + FollowRedirects *bool `json:"followRedirects" toml:"followRedirects" yaml:"followRedirects" export:"true"` + Headers map[string]string `json:"headers,omitempty" toml:"headers,omitempty" yaml:"headers,omitempty" export:"true"` } // SetDefaults Default values for a HealthCheck. @@ -202,10 +202,10 @@ func (h *HealthCheck) SetDefaults() { // ServersTransport options to configure communication between Traefik and the servers. type ServersTransport struct { - ServerName string `description:"ServerName used to contact the server" json:"serverName,omitempty" toml:"serverName,omitempty" yaml:"serverName,omitempty" export:"true"` + ServerName string `description:"ServerName used to contact the server" json:"serverName,omitempty" toml:"serverName,omitempty" yaml:"serverName,omitempty"` InsecureSkipVerify bool `description:"Disable SSL certificate verification." json:"insecureSkipVerify,omitempty" toml:"insecureSkipVerify,omitempty" yaml:"insecureSkipVerify,omitempty" export:"true"` RootCAs []tls.FileOrContent `description:"Add cert file for self-signed certificate." json:"rootCAs,omitempty" toml:"rootCAs,omitempty" yaml:"rootCAs,omitempty"` - Certificates tls.Certificates `description:"Certificates for mTLS." json:"certificates,omitempty" toml:"certificates,omitempty" yaml:"certificates,omitempty"` + Certificates tls.Certificates `description:"Certificates for mTLS." json:"certificates,omitempty" toml:"certificates,omitempty" yaml:"certificates,omitempty" export:"true"` MaxIdleConnsPerHost int `description:"If non-zero, controls the maximum idle (keep-alive) to keep per-host. If zero, DefaultMaxIdleConnsPerHost is used" json:"maxIdleConnsPerHost,omitempty" toml:"maxIdleConnsPerHost,omitempty" yaml:"maxIdleConnsPerHost,omitempty" export:"true"` ForwardingTimeouts *ForwardingTimeouts `description:"Timeouts for requests forwarded to the backend servers." json:"forwardingTimeouts,omitempty" toml:"forwardingTimeouts,omitempty" yaml:"forwardingTimeouts,omitempty" export:"true"` } diff --git a/pkg/config/dynamic/middlewares.go b/pkg/config/dynamic/middlewares.go index 17f949c50..b6f763465 100644 --- a/pkg/config/dynamic/middlewares.go +++ b/pkg/config/dynamic/middlewares.go @@ -16,30 +16,30 @@ import ( // Middleware holds the Middleware configuration. type Middleware struct { - AddPrefix *AddPrefix `json:"addPrefix,omitempty" toml:"addPrefix,omitempty" yaml:"addPrefix,omitempty"` - StripPrefix *StripPrefix `json:"stripPrefix,omitempty" toml:"stripPrefix,omitempty" yaml:"stripPrefix,omitempty"` - StripPrefixRegex *StripPrefixRegex `json:"stripPrefixRegex,omitempty" toml:"stripPrefixRegex,omitempty" yaml:"stripPrefixRegex,omitempty"` - ReplacePath *ReplacePath `json:"replacePath,omitempty" toml:"replacePath,omitempty" yaml:"replacePath,omitempty"` - ReplacePathRegex *ReplacePathRegex `json:"replacePathRegex,omitempty" toml:"replacePathRegex,omitempty" yaml:"replacePathRegex,omitempty"` - Chain *Chain `json:"chain,omitempty" toml:"chain,omitempty" yaml:"chain,omitempty"` - IPWhiteList *IPWhiteList `json:"ipWhiteList,omitempty" toml:"ipWhiteList,omitempty" yaml:"ipWhiteList,omitempty"` - Headers *Headers `json:"headers,omitempty" toml:"headers,omitempty" yaml:"headers,omitempty"` - Errors *ErrorPage `json:"errors,omitempty" toml:"errors,omitempty" yaml:"errors,omitempty"` - RateLimit *RateLimit `json:"rateLimit,omitempty" toml:"rateLimit,omitempty" yaml:"rateLimit,omitempty"` - RedirectRegex *RedirectRegex `json:"redirectRegex,omitempty" toml:"redirectRegex,omitempty" yaml:"redirectRegex,omitempty"` - RedirectScheme *RedirectScheme `json:"redirectScheme,omitempty" toml:"redirectScheme,omitempty" yaml:"redirectScheme,omitempty"` - BasicAuth *BasicAuth `json:"basicAuth,omitempty" toml:"basicAuth,omitempty" yaml:"basicAuth,omitempty"` - DigestAuth *DigestAuth `json:"digestAuth,omitempty" toml:"digestAuth,omitempty" yaml:"digestAuth,omitempty"` - ForwardAuth *ForwardAuth `json:"forwardAuth,omitempty" toml:"forwardAuth,omitempty" yaml:"forwardAuth,omitempty"` - InFlightReq *InFlightReq `json:"inFlightReq,omitempty" toml:"inFlightReq,omitempty" yaml:"inFlightReq,omitempty"` - Buffering *Buffering `json:"buffering,omitempty" toml:"buffering,omitempty" yaml:"buffering,omitempty"` - CircuitBreaker *CircuitBreaker `json:"circuitBreaker,omitempty" toml:"circuitBreaker,omitempty" yaml:"circuitBreaker,omitempty"` - Compress *Compress `json:"compress,omitempty" toml:"compress,omitempty" yaml:"compress,omitempty" label:"allowEmpty" file:"allowEmpty"` - PassTLSClientCert *PassTLSClientCert `json:"passTLSClientCert,omitempty" toml:"passTLSClientCert,omitempty" yaml:"passTLSClientCert,omitempty"` - Retry *Retry `json:"retry,omitempty" toml:"retry,omitempty" yaml:"retry,omitempty"` - ContentType *ContentType `json:"contentType,omitempty" toml:"contentType,omitempty" yaml:"contentType,omitempty"` + AddPrefix *AddPrefix `json:"addPrefix,omitempty" toml:"addPrefix,omitempty" yaml:"addPrefix,omitempty" export:"true"` + StripPrefix *StripPrefix `json:"stripPrefix,omitempty" toml:"stripPrefix,omitempty" yaml:"stripPrefix,omitempty" export:"true"` + StripPrefixRegex *StripPrefixRegex `json:"stripPrefixRegex,omitempty" toml:"stripPrefixRegex,omitempty" yaml:"stripPrefixRegex,omitempty" export:"true"` + ReplacePath *ReplacePath `json:"replacePath,omitempty" toml:"replacePath,omitempty" yaml:"replacePath,omitempty" export:"true"` + ReplacePathRegex *ReplacePathRegex `json:"replacePathRegex,omitempty" toml:"replacePathRegex,omitempty" yaml:"replacePathRegex,omitempty" export:"true"` + Chain *Chain `json:"chain,omitempty" toml:"chain,omitempty" yaml:"chain,omitempty" export:"true"` + IPWhiteList *IPWhiteList `json:"ipWhiteList,omitempty" toml:"ipWhiteList,omitempty" yaml:"ipWhiteList,omitempty" export:"true"` + Headers *Headers `json:"headers,omitempty" toml:"headers,omitempty" yaml:"headers,omitempty" export:"true"` + Errors *ErrorPage `json:"errors,omitempty" toml:"errors,omitempty" yaml:"errors,omitempty" export:"true"` + RateLimit *RateLimit `json:"rateLimit,omitempty" toml:"rateLimit,omitempty" yaml:"rateLimit,omitempty" export:"true"` + RedirectRegex *RedirectRegex `json:"redirectRegex,omitempty" toml:"redirectRegex,omitempty" yaml:"redirectRegex,omitempty" export:"true"` + RedirectScheme *RedirectScheme `json:"redirectScheme,omitempty" toml:"redirectScheme,omitempty" yaml:"redirectScheme,omitempty" export:"true"` + BasicAuth *BasicAuth `json:"basicAuth,omitempty" toml:"basicAuth,omitempty" yaml:"basicAuth,omitempty" export:"true"` + DigestAuth *DigestAuth `json:"digestAuth,omitempty" toml:"digestAuth,omitempty" yaml:"digestAuth,omitempty" export:"true"` + ForwardAuth *ForwardAuth `json:"forwardAuth,omitempty" toml:"forwardAuth,omitempty" yaml:"forwardAuth,omitempty" export:"true"` + InFlightReq *InFlightReq `json:"inFlightReq,omitempty" toml:"inFlightReq,omitempty" yaml:"inFlightReq,omitempty" export:"true"` + Buffering *Buffering `json:"buffering,omitempty" toml:"buffering,omitempty" yaml:"buffering,omitempty" export:"true"` + CircuitBreaker *CircuitBreaker `json:"circuitBreaker,omitempty" toml:"circuitBreaker,omitempty" yaml:"circuitBreaker,omitempty" export:"true"` + Compress *Compress `json:"compress,omitempty" toml:"compress,omitempty" yaml:"compress,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"` + PassTLSClientCert *PassTLSClientCert `json:"passTLSClientCert,omitempty" toml:"passTLSClientCert,omitempty" yaml:"passTLSClientCert,omitempty" export:"true"` + Retry *Retry `json:"retry,omitempty" toml:"retry,omitempty" yaml:"retry,omitempty" export:"true"` + ContentType *ContentType `json:"contentType,omitempty" toml:"contentType,omitempty" yaml:"contentType,omitempty" export:"true"` - Plugin map[string]PluginConf `json:"plugin,omitempty" toml:"plugin,omitempty" yaml:"plugin,omitempty"` + Plugin map[string]PluginConf `json:"plugin,omitempty" toml:"plugin,omitempty" yaml:"plugin,omitempty" export:"true"` } // +k8s:deepcopy-gen=true @@ -54,23 +54,14 @@ type Middleware struct { // and it is going to be kept that way in order to support users currently relying on it. // This middleware exists to enable the correct behavior until at least the default one can be changed in a future version. type ContentType struct { - AutoDetect bool `json:"autoDetect,omitempty" toml:"autoDetect,omitempty" yaml:"autoDetect,omitempty"` + AutoDetect bool `json:"autoDetect,omitempty" toml:"autoDetect,omitempty" yaml:"autoDetect,omitempty" export:"true"` } // +k8s:deepcopy-gen=true // AddPrefix holds the AddPrefix configuration. type AddPrefix struct { - Prefix string `json:"prefix,omitempty" toml:"prefix,omitempty" yaml:"prefix,omitempty"` -} - -// +k8s:deepcopy-gen=true - -// Auth holds the authentication configuration (BASIC, DIGEST, users). -type Auth struct { - Basic *BasicAuth `json:"basic,omitempty" toml:"basic,omitempty" yaml:"basic,omitempty" export:"true"` - Digest *DigestAuth `json:"digest,omitempty" toml:"digest,omitempty" yaml:"digest,omitempty" export:"true"` - Forward *ForwardAuth `json:"forward,omitempty" toml:"forward,omitempty" yaml:"forward,omitempty" export:"true"` + Prefix string `json:"prefix,omitempty" toml:"prefix,omitempty" yaml:"prefix,omitempty" export:"true"` } // +k8s:deepcopy-gen=true @@ -80,7 +71,7 @@ type BasicAuth struct { Users Users `json:"users,omitempty" toml:"users,omitempty" yaml:"users,omitempty"` UsersFile string `json:"usersFile,omitempty" toml:"usersFile,omitempty" yaml:"usersFile,omitempty"` Realm string `json:"realm,omitempty" toml:"realm,omitempty" yaml:"realm,omitempty"` - RemoveHeader bool `json:"removeHeader,omitempty" toml:"removeHeader,omitempty" yaml:"removeHeader,omitempty"` + RemoveHeader bool `json:"removeHeader,omitempty" toml:"removeHeader,omitempty" yaml:"removeHeader,omitempty" export:"true"` HeaderField string `json:"headerField,omitempty" toml:"headerField,omitempty" yaml:"headerField,omitempty" export:"true"` } @@ -88,25 +79,25 @@ type BasicAuth struct { // Buffering holds the request/response buffering configuration. type Buffering struct { - MaxRequestBodyBytes int64 `json:"maxRequestBodyBytes,omitempty" toml:"maxRequestBodyBytes,omitempty" yaml:"maxRequestBodyBytes,omitempty"` - MemRequestBodyBytes int64 `json:"memRequestBodyBytes,omitempty" toml:"memRequestBodyBytes,omitempty" yaml:"memRequestBodyBytes,omitempty"` - MaxResponseBodyBytes int64 `json:"maxResponseBodyBytes,omitempty" toml:"maxResponseBodyBytes,omitempty" yaml:"maxResponseBodyBytes,omitempty"` - MemResponseBodyBytes int64 `json:"memResponseBodyBytes,omitempty" toml:"memResponseBodyBytes,omitempty" yaml:"memResponseBodyBytes,omitempty"` - RetryExpression string `json:"retryExpression,omitempty" toml:"retryExpression,omitempty" yaml:"retryExpression,omitempty"` + MaxRequestBodyBytes int64 `json:"maxRequestBodyBytes,omitempty" toml:"maxRequestBodyBytes,omitempty" yaml:"maxRequestBodyBytes,omitempty" export:"true"` + MemRequestBodyBytes int64 `json:"memRequestBodyBytes,omitempty" toml:"memRequestBodyBytes,omitempty" yaml:"memRequestBodyBytes,omitempty" export:"true"` + MaxResponseBodyBytes int64 `json:"maxResponseBodyBytes,omitempty" toml:"maxResponseBodyBytes,omitempty" yaml:"maxResponseBodyBytes,omitempty" export:"true"` + MemResponseBodyBytes int64 `json:"memResponseBodyBytes,omitempty" toml:"memResponseBodyBytes,omitempty" yaml:"memResponseBodyBytes,omitempty" export:"true"` + RetryExpression string `json:"retryExpression,omitempty" toml:"retryExpression,omitempty" yaml:"retryExpression,omitempty" export:"true"` } // +k8s:deepcopy-gen=true // Chain holds a chain of middlewares. type Chain struct { - Middlewares []string `json:"middlewares,omitempty" toml:"middlewares,omitempty" yaml:"middlewares,omitempty"` + Middlewares []string `json:"middlewares,omitempty" toml:"middlewares,omitempty" yaml:"middlewares,omitempty" export:"true"` } // +k8s:deepcopy-gen=true // CircuitBreaker holds the circuit breaker configuration. type CircuitBreaker struct { - Expression string `json:"expression,omitempty" toml:"expression,omitempty" yaml:"expression,omitempty"` + Expression string `json:"expression,omitempty" toml:"expression,omitempty" yaml:"expression,omitempty" export:"true"` } // +k8s:deepcopy-gen=true @@ -122,7 +113,7 @@ type Compress struct { type DigestAuth struct { Users Users `json:"users,omitempty" toml:"users,omitempty" yaml:"users,omitempty"` UsersFile string `json:"usersFile,omitempty" toml:"usersFile,omitempty" yaml:"usersFile,omitempty"` - RemoveHeader bool `json:"removeHeader,omitempty" toml:"removeHeader,omitempty" yaml:"removeHeader,omitempty"` + RemoveHeader bool `json:"removeHeader,omitempty" toml:"removeHeader,omitempty" yaml:"removeHeader,omitempty" export:"true"` Realm string `json:"realm,omitempty" toml:"realm,omitempty" yaml:"realm,omitempty"` HeaderField string `json:"headerField,omitempty" toml:"headerField,omitempty" yaml:"headerField,omitempty" export:"true"` } @@ -131,9 +122,9 @@ type DigestAuth struct { // ErrorPage holds the custom error page configuration. type ErrorPage struct { - Status []string `json:"status,omitempty" toml:"status,omitempty" yaml:"status,omitempty"` - Service string `json:"service,omitempty" toml:"service,omitempty" yaml:"service,omitempty"` - Query string `json:"query,omitempty" toml:"query,omitempty" yaml:"query,omitempty"` + Status []string `json:"status,omitempty" toml:"status,omitempty" yaml:"status,omitempty" export:"true"` + Service string `json:"service,omitempty" toml:"service,omitempty" yaml:"service,omitempty" export:"true"` + Query string `json:"query,omitempty" toml:"query,omitempty" yaml:"query,omitempty" export:"true"` } // +k8s:deepcopy-gen=true @@ -141,26 +132,26 @@ type ErrorPage struct { // ForwardAuth holds the http forward authentication configuration. type ForwardAuth struct { Address string `json:"address,omitempty" toml:"address,omitempty" yaml:"address,omitempty"` - TLS *ClientTLS `json:"tls,omitempty" toml:"tls,omitempty" yaml:"tls,omitempty"` + TLS *ClientTLS `json:"tls,omitempty" toml:"tls,omitempty" yaml:"tls,omitempty" export:"true"` TrustForwardHeader bool `json:"trustForwardHeader,omitempty" toml:"trustForwardHeader,omitempty" yaml:"trustForwardHeader,omitempty" export:"true"` - AuthResponseHeaders []string `json:"authResponseHeaders,omitempty" toml:"authResponseHeaders,omitempty" yaml:"authResponseHeaders,omitempty"` - AuthResponseHeadersRegex string `json:"authResponseHeadersRegex,omitempty" toml:"authResponseHeadersRegex,omitempty" yaml:"authResponseHeadersRegex,omitempty"` - AuthRequestHeaders []string `json:"authRequestHeaders,omitempty" toml:"authRequestHeaders,omitempty" yaml:"authRequestHeaders,omitempty"` + AuthResponseHeaders []string `json:"authResponseHeaders,omitempty" toml:"authResponseHeaders,omitempty" yaml:"authResponseHeaders,omitempty" export:"true"` + AuthResponseHeadersRegex string `json:"authResponseHeadersRegex,omitempty" toml:"authResponseHeadersRegex,omitempty" yaml:"authResponseHeadersRegex,omitempty" export:"true"` + AuthRequestHeaders []string `json:"authRequestHeaders,omitempty" toml:"authRequestHeaders,omitempty" yaml:"authRequestHeaders,omitempty" export:"true"` } // +k8s:deepcopy-gen=true // Headers holds the custom header configuration. type Headers struct { - CustomRequestHeaders map[string]string `json:"customRequestHeaders,omitempty" toml:"customRequestHeaders,omitempty" yaml:"customRequestHeaders,omitempty"` - CustomResponseHeaders map[string]string `json:"customResponseHeaders,omitempty" toml:"customResponseHeaders,omitempty" yaml:"customResponseHeaders,omitempty"` + CustomRequestHeaders map[string]string `json:"customRequestHeaders,omitempty" toml:"customRequestHeaders,omitempty" yaml:"customRequestHeaders,omitempty" export:"true"` + CustomResponseHeaders map[string]string `json:"customResponseHeaders,omitempty" toml:"customResponseHeaders,omitempty" yaml:"customResponseHeaders,omitempty" export:"true"` // AccessControlAllowCredentials is only valid if true. false is ignored. - AccessControlAllowCredentials bool `json:"accessControlAllowCredentials,omitempty" toml:"accessControlAllowCredentials,omitempty" yaml:"accessControlAllowCredentials,omitempty"` + AccessControlAllowCredentials bool `json:"accessControlAllowCredentials,omitempty" toml:"accessControlAllowCredentials,omitempty" yaml:"accessControlAllowCredentials,omitempty" export:"true"` // AccessControlAllowHeaders must be used in response to a preflight request with Access-Control-Request-Headers set. - AccessControlAllowHeaders []string `json:"accessControlAllowHeaders,omitempty" toml:"accessControlAllowHeaders,omitempty" yaml:"accessControlAllowHeaders,omitempty"` + AccessControlAllowHeaders []string `json:"accessControlAllowHeaders,omitempty" toml:"accessControlAllowHeaders,omitempty" yaml:"accessControlAllowHeaders,omitempty" export:"true"` // AccessControlAllowMethods must be used in response to a preflight request with Access-Control-Request-Method set. - AccessControlAllowMethods []string `json:"accessControlAllowMethods,omitempty" toml:"accessControlAllowMethods,omitempty" yaml:"accessControlAllowMethods,omitempty"` + AccessControlAllowMethods []string `json:"accessControlAllowMethods,omitempty" toml:"accessControlAllowMethods,omitempty" yaml:"accessControlAllowMethods,omitempty" export:"true"` // AccessControlAllowOrigin Can be "origin-list-or-null" or "*". From (https://www.w3.org/TR/cors/#access-control-allow-origin-response-header) AccessControlAllowOrigin string `json:"accessControlAllowOrigin,omitempty" toml:"accessControlAllowOrigin,omitempty" yaml:"accessControlAllowOrigin,omitempty"` // Deprecated // AccessControlAllowOriginList is a list of allowable origins. Can also be a wildcard origin "*". @@ -168,33 +159,33 @@ type Headers struct { // AccessControlAllowOriginListRegex is a list of allowable origins written following the Regular Expression syntax (https://golang.org/pkg/regexp/). AccessControlAllowOriginListRegex []string `json:"accessControlAllowOriginListRegex,omitempty" toml:"accessControlAllowOriginListRegex,omitempty" yaml:"accessControlAllowOriginListRegex,omitempty"` // AccessControlExposeHeaders sets valid headers for the response. - AccessControlExposeHeaders []string `json:"accessControlExposeHeaders,omitempty" toml:"accessControlExposeHeaders,omitempty" yaml:"accessControlExposeHeaders,omitempty"` + AccessControlExposeHeaders []string `json:"accessControlExposeHeaders,omitempty" toml:"accessControlExposeHeaders,omitempty" yaml:"accessControlExposeHeaders,omitempty" export:"true"` // AccessControlMaxAge sets the time that a preflight request may be cached. - AccessControlMaxAge int64 `json:"accessControlMaxAge,omitempty" toml:"accessControlMaxAge,omitempty" yaml:"accessControlMaxAge,omitempty"` + AccessControlMaxAge int64 `json:"accessControlMaxAge,omitempty" toml:"accessControlMaxAge,omitempty" yaml:"accessControlMaxAge,omitempty" export:"true"` // AddVaryHeader controls if the Vary header is automatically added/updated when the AccessControlAllowOrigin is set. - AddVaryHeader bool `json:"addVaryHeader,omitempty" toml:"addVaryHeader,omitempty" yaml:"addVaryHeader,omitempty"` + AddVaryHeader bool `json:"addVaryHeader,omitempty" toml:"addVaryHeader,omitempty" yaml:"addVaryHeader,omitempty" export:"true"` AllowedHosts []string `json:"allowedHosts,omitempty" toml:"allowedHosts,omitempty" yaml:"allowedHosts,omitempty"` - HostsProxyHeaders []string `json:"hostsProxyHeaders,omitempty" toml:"hostsProxyHeaders,omitempty" yaml:"hostsProxyHeaders,omitempty"` - SSLRedirect bool `json:"sslRedirect,omitempty" toml:"sslRedirect,omitempty" yaml:"sslRedirect,omitempty"` - SSLTemporaryRedirect bool `json:"sslTemporaryRedirect,omitempty" toml:"sslTemporaryRedirect,omitempty" yaml:"sslTemporaryRedirect,omitempty"` + HostsProxyHeaders []string `json:"hostsProxyHeaders,omitempty" toml:"hostsProxyHeaders,omitempty" yaml:"hostsProxyHeaders,omitempty" export:"true"` + SSLRedirect bool `json:"sslRedirect,omitempty" toml:"sslRedirect,omitempty" yaml:"sslRedirect,omitempty" export:"true"` + SSLTemporaryRedirect bool `json:"sslTemporaryRedirect,omitempty" toml:"sslTemporaryRedirect,omitempty" yaml:"sslTemporaryRedirect,omitempty" export:"true"` SSLHost string `json:"sslHost,omitempty" toml:"sslHost,omitempty" yaml:"sslHost,omitempty"` SSLProxyHeaders map[string]string `json:"sslProxyHeaders,omitempty" toml:"sslProxyHeaders,omitempty" yaml:"sslProxyHeaders,omitempty"` - SSLForceHost bool `json:"sslForceHost,omitempty" toml:"sslForceHost,omitempty" yaml:"sslForceHost,omitempty"` - STSSeconds int64 `json:"stsSeconds,omitempty" toml:"stsSeconds,omitempty" yaml:"stsSeconds,omitempty"` - STSIncludeSubdomains bool `json:"stsIncludeSubdomains,omitempty" toml:"stsIncludeSubdomains,omitempty" yaml:"stsIncludeSubdomains,omitempty"` - STSPreload bool `json:"stsPreload,omitempty" toml:"stsPreload,omitempty" yaml:"stsPreload,omitempty"` - ForceSTSHeader bool `json:"forceSTSHeader,omitempty" toml:"forceSTSHeader,omitempty" yaml:"forceSTSHeader,omitempty"` - FrameDeny bool `json:"frameDeny,omitempty" toml:"frameDeny,omitempty" yaml:"frameDeny,omitempty"` + SSLForceHost bool `json:"sslForceHost,omitempty" toml:"sslForceHost,omitempty" yaml:"sslForceHost,omitempty" export:"true"` + STSSeconds int64 `json:"stsSeconds,omitempty" toml:"stsSeconds,omitempty" yaml:"stsSeconds,omitempty" export:"true"` + STSIncludeSubdomains bool `json:"stsIncludeSubdomains,omitempty" toml:"stsIncludeSubdomains,omitempty" yaml:"stsIncludeSubdomains,omitempty" export:"true"` + STSPreload bool `json:"stsPreload,omitempty" toml:"stsPreload,omitempty" yaml:"stsPreload,omitempty" export:"true"` + ForceSTSHeader bool `json:"forceSTSHeader,omitempty" toml:"forceSTSHeader,omitempty" yaml:"forceSTSHeader,omitempty" export:"true"` + FrameDeny bool `json:"frameDeny,omitempty" toml:"frameDeny,omitempty" yaml:"frameDeny,omitempty" export:"true"` CustomFrameOptionsValue string `json:"customFrameOptionsValue,omitempty" toml:"customFrameOptionsValue,omitempty" yaml:"customFrameOptionsValue,omitempty"` - ContentTypeNosniff bool `json:"contentTypeNosniff,omitempty" toml:"contentTypeNosniff,omitempty" yaml:"contentTypeNosniff,omitempty"` - BrowserXSSFilter bool `json:"browserXssFilter,omitempty" toml:"browserXssFilter,omitempty" yaml:"browserXssFilter,omitempty"` + ContentTypeNosniff bool `json:"contentTypeNosniff,omitempty" toml:"contentTypeNosniff,omitempty" yaml:"contentTypeNosniff,omitempty" export:"true"` + BrowserXSSFilter bool `json:"browserXssFilter,omitempty" toml:"browserXssFilter,omitempty" yaml:"browserXssFilter,omitempty" export:"true"` CustomBrowserXSSValue string `json:"customBrowserXSSValue,omitempty" toml:"customBrowserXSSValue,omitempty" yaml:"customBrowserXSSValue,omitempty"` ContentSecurityPolicy string `json:"contentSecurityPolicy,omitempty" toml:"contentSecurityPolicy,omitempty" yaml:"contentSecurityPolicy,omitempty"` PublicKey string `json:"publicKey,omitempty" toml:"publicKey,omitempty" yaml:"publicKey,omitempty"` - ReferrerPolicy string `json:"referrerPolicy,omitempty" toml:"referrerPolicy,omitempty" yaml:"referrerPolicy,omitempty"` - FeaturePolicy string `json:"featurePolicy,omitempty" toml:"featurePolicy,omitempty" yaml:"featurePolicy,omitempty"` - IsDevelopment bool `json:"isDevelopment,omitempty" toml:"isDevelopment,omitempty" yaml:"isDevelopment,omitempty"` + ReferrerPolicy string `json:"referrerPolicy,omitempty" toml:"referrerPolicy,omitempty" yaml:"referrerPolicy,omitempty" export:"true"` + FeaturePolicy string `json:"featurePolicy,omitempty" toml:"featurePolicy,omitempty" yaml:"featurePolicy,omitempty" export:"true"` + IsDevelopment bool `json:"isDevelopment,omitempty" toml:"isDevelopment,omitempty" yaml:"isDevelopment,omitempty" export:"true"` } // HasCustomHeadersDefined checks to see if any of the custom header elements have been set. @@ -282,23 +273,23 @@ func (s *IPStrategy) Get() (ip.Strategy, error) { // IPWhiteList holds the ip white list configuration. type IPWhiteList struct { SourceRange []string `json:"sourceRange,omitempty" toml:"sourceRange,omitempty" yaml:"sourceRange,omitempty"` - IPStrategy *IPStrategy `json:"ipStrategy,omitempty" toml:"ipStrategy,omitempty" yaml:"ipStrategy,omitempty" label:"allowEmpty" file:"allowEmpty"` + IPStrategy *IPStrategy `json:"ipStrategy,omitempty" toml:"ipStrategy,omitempty" yaml:"ipStrategy,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"` } // +k8s:deepcopy-gen=true // InFlightReq limits the number of requests being processed and served concurrently. type InFlightReq struct { - Amount int64 `json:"amount,omitempty" toml:"amount,omitempty" yaml:"amount,omitempty"` - SourceCriterion *SourceCriterion `json:"sourceCriterion,omitempty" toml:"sourceCriterion,omitempty" yaml:"sourceCriterion,omitempty"` + Amount int64 `json:"amount,omitempty" toml:"amount,omitempty" yaml:"amount,omitempty" export:"true"` + SourceCriterion *SourceCriterion `json:"sourceCriterion,omitempty" toml:"sourceCriterion,omitempty" yaml:"sourceCriterion,omitempty" export:"true"` } // +k8s:deepcopy-gen=true // PassTLSClientCert holds the TLS client cert headers configuration. type PassTLSClientCert struct { - PEM bool `json:"pem,omitempty" toml:"pem,omitempty" yaml:"pem,omitempty"` - Info *TLSClientCertificateInfo `json:"info,omitempty" toml:"info,omitempty" yaml:"info,omitempty"` + PEM bool `json:"pem,omitempty" toml:"pem,omitempty" yaml:"pem,omitempty" export:"true"` + Info *TLSClientCertificateInfo `json:"info,omitempty" toml:"info,omitempty" yaml:"info,omitempty" export:"true"` } // +k8s:deepcopy-gen=true @@ -307,9 +298,9 @@ type PassTLSClientCert struct { // If none are set, the default is to use the request's remote address field. // All fields are mutually exclusive. type SourceCriterion struct { - IPStrategy *IPStrategy `json:"ipStrategy,omitempty" toml:"ipStrategy,omitempty" yaml:"ipStrategy,omitempty"` - RequestHeaderName string `json:"requestHeaderName,omitempty" toml:"requestHeaderName,omitempty" yaml:"requestHeaderName,omitempty"` - RequestHost bool `json:"requestHost,omitempty" toml:"requestHost,omitempty" yaml:"requestHost,omitempty"` + IPStrategy *IPStrategy `json:"ipStrategy,omitempty" toml:"ipStrategy,omitempty" yaml:"ipStrategy,omitempty" export:"true"` + RequestHeaderName string `json:"requestHeaderName,omitempty" toml:"requestHeaderName,omitempty" yaml:"requestHeaderName,omitempty" export:"true"` + RequestHost bool `json:"requestHost,omitempty" toml:"requestHost,omitempty" yaml:"requestHost,omitempty" export:"true"` } // +k8s:deepcopy-gen=true @@ -320,17 +311,17 @@ type RateLimit struct { // It defaults to 0, which means no rate limiting. // The rate is actually defined by dividing Average by Period. So for a rate below 1req/s, // one needs to define a Period larger than a second. - Average int64 `json:"average,omitempty" toml:"average,omitempty" yaml:"average,omitempty"` + Average int64 `json:"average,omitempty" toml:"average,omitempty" yaml:"average,omitempty" export:"true"` // Period, in combination with Average, defines the actual maximum rate, such as: // r = Average / Period. It defaults to a second. - Period ptypes.Duration `json:"period,omitempty" toml:"period,omitempty" yaml:"period,omitempty"` + Period ptypes.Duration `json:"period,omitempty" toml:"period,omitempty" yaml:"period,omitempty" export:"true"` // Burst is the maximum number of requests allowed to arrive in the same arbitrarily small period of time. // It defaults to 1. - Burst int64 `json:"burst,omitempty" toml:"burst,omitempty" yaml:"burst,omitempty"` + Burst int64 `json:"burst,omitempty" toml:"burst,omitempty" yaml:"burst,omitempty" export:"true"` - SourceCriterion *SourceCriterion `json:"sourceCriterion,omitempty" toml:"sourceCriterion,omitempty" yaml:"sourceCriterion,omitempty"` + SourceCriterion *SourceCriterion `json:"sourceCriterion,omitempty" toml:"sourceCriterion,omitempty" yaml:"sourceCriterion,omitempty" export:"true"` } // SetDefaults sets the default values on a RateLimit. @@ -345,47 +336,47 @@ func (r *RateLimit) SetDefaults() { type RedirectRegex struct { Regex string `json:"regex,omitempty" toml:"regex,omitempty" yaml:"regex,omitempty"` Replacement string `json:"replacement,omitempty" toml:"replacement,omitempty" yaml:"replacement,omitempty"` - Permanent bool `json:"permanent,omitempty" toml:"permanent,omitempty" yaml:"permanent,omitempty"` + Permanent bool `json:"permanent,omitempty" toml:"permanent,omitempty" yaml:"permanent,omitempty" export:"true"` } // +k8s:deepcopy-gen=true // RedirectScheme holds the scheme redirection configuration. type RedirectScheme struct { - Scheme string `json:"scheme,omitempty" toml:"scheme,omitempty" yaml:"scheme,omitempty"` - Port string `json:"port,omitempty" toml:"port,omitempty" yaml:"port,omitempty"` - Permanent bool `json:"permanent,omitempty" toml:"permanent,omitempty" yaml:"permanent,omitempty"` + Scheme string `json:"scheme,omitempty" toml:"scheme,omitempty" yaml:"scheme,omitempty" export:"true"` + Port string `json:"port,omitempty" toml:"port,omitempty" yaml:"port,omitempty" export:"true"` + Permanent bool `json:"permanent,omitempty" toml:"permanent,omitempty" yaml:"permanent,omitempty" export:"true"` } // +k8s:deepcopy-gen=true // ReplacePath holds the ReplacePath configuration. type ReplacePath struct { - Path string `json:"path,omitempty" toml:"path,omitempty" yaml:"path,omitempty"` + Path string `json:"path,omitempty" toml:"path,omitempty" yaml:"path,omitempty" export:"true"` } // +k8s:deepcopy-gen=true // ReplacePathRegex holds the ReplacePathRegex configuration. type ReplacePathRegex struct { - Regex string `json:"regex,omitempty" toml:"regex,omitempty" yaml:"regex,omitempty"` - Replacement string `json:"replacement,omitempty" toml:"replacement,omitempty" yaml:"replacement,omitempty"` + Regex string `json:"regex,omitempty" toml:"regex,omitempty" yaml:"regex,omitempty" export:"true"` + Replacement string `json:"replacement,omitempty" toml:"replacement,omitempty" yaml:"replacement,omitempty" export:"true"` } // +k8s:deepcopy-gen=true // Retry holds the retry configuration. type Retry struct { - Attempts int `json:"attempts,omitempty" toml:"attempts,omitempty" yaml:"attempts,omitempty"` - InitialInterval ptypes.Duration `json:"initialInterval,omitempty" toml:"initialInterval,omitempty" yaml:"initialInterval,omitempty"` + Attempts int `json:"attempts,omitempty" toml:"attempts,omitempty" yaml:"attempts,omitempty" export:"true"` + InitialInterval ptypes.Duration `json:"initialInterval,omitempty" toml:"initialInterval,omitempty" yaml:"initialInterval,omitempty" export:"true"` } // +k8s:deepcopy-gen=true // StripPrefix holds the StripPrefix configuration. type StripPrefix struct { - Prefixes []string `json:"prefixes,omitempty" toml:"prefixes,omitempty" yaml:"prefixes,omitempty"` - ForceSlash bool `json:"forceSlash,omitempty" toml:"forceSlash,omitempty" yaml:"forceSlash,omitempty"` // Deprecated + Prefixes []string `json:"prefixes,omitempty" toml:"prefixes,omitempty" yaml:"prefixes,omitempty" export:"true"` + ForceSlash bool `json:"forceSlash,omitempty" toml:"forceSlash,omitempty" yaml:"forceSlash,omitempty" export:"true"` // Deprecated } // SetDefaults Default values for a StripPrefix. @@ -397,33 +388,33 @@ func (s *StripPrefix) SetDefaults() { // StripPrefixRegex holds the StripPrefixRegex configuration. type StripPrefixRegex struct { - Regex []string `json:"regex,omitempty" toml:"regex,omitempty" yaml:"regex,omitempty"` + Regex []string `json:"regex,omitempty" toml:"regex,omitempty" yaml:"regex,omitempty" export:"true"` } // +k8s:deepcopy-gen=true // TLSClientCertificateInfo holds the client TLS certificate info configuration. type TLSClientCertificateInfo struct { - NotAfter bool `json:"notAfter,omitempty" toml:"notAfter,omitempty" yaml:"notAfter,omitempty"` - NotBefore bool `json:"notBefore,omitempty" toml:"notBefore,omitempty" yaml:"notBefore,omitempty"` - Sans bool `json:"sans,omitempty" toml:"sans,omitempty" yaml:"sans,omitempty"` - Subject *TLSCLientCertificateDNInfo `json:"subject,omitempty" toml:"subject,omitempty" yaml:"subject,omitempty"` - Issuer *TLSCLientCertificateDNInfo `json:"issuer,omitempty" toml:"issuer,omitempty" yaml:"issuer,omitempty"` - SerialNumber bool `json:"serialNumber,omitempty" toml:"serialNumber,omitempty" yaml:"serialNumber,omitempty"` + NotAfter bool `json:"notAfter,omitempty" toml:"notAfter,omitempty" yaml:"notAfter,omitempty" export:"true"` + NotBefore bool `json:"notBefore,omitempty" toml:"notBefore,omitempty" yaml:"notBefore,omitempty" export:"true"` + Sans bool `json:"sans,omitempty" toml:"sans,omitempty" yaml:"sans,omitempty" export:"true"` + Subject *TLSCLientCertificateDNInfo `json:"subject,omitempty" toml:"subject,omitempty" yaml:"subject,omitempty" export:"true"` + Issuer *TLSCLientCertificateDNInfo `json:"issuer,omitempty" toml:"issuer,omitempty" yaml:"issuer,omitempty" export:"true"` + SerialNumber bool `json:"serialNumber,omitempty" toml:"serialNumber,omitempty" yaml:"serialNumber,omitempty" export:"true"` } // +k8s:deepcopy-gen=true -// TLSCLientCertificateDNInfo holds the client TLS certificate distinguished name info configuration +// TLSCLientCertificateDNInfo holds the client TLS certificate distinguished name info configuration. // cf https://tools.ietf.org/html/rfc3739 type TLSCLientCertificateDNInfo struct { - Country bool `json:"country,omitempty" toml:"country,omitempty" yaml:"country,omitempty"` - Province bool `json:"province,omitempty" toml:"province,omitempty" yaml:"province,omitempty"` - Locality bool `json:"locality,omitempty" toml:"locality,omitempty" yaml:"locality,omitempty"` - Organization bool `json:"organization,omitempty" toml:"organization,omitempty" yaml:"organization,omitempty"` - CommonName bool `json:"commonName,omitempty" toml:"commonName,omitempty" yaml:"commonName,omitempty"` - SerialNumber bool `json:"serialNumber,omitempty" toml:"serialNumber,omitempty" yaml:"serialNumber,omitempty"` - DomainComponent bool `json:"domainComponent,omitempty" toml:"domainComponent,omitempty" yaml:"domainComponent,omitempty"` + Country bool `json:"country,omitempty" toml:"country,omitempty" yaml:"country,omitempty" export:"true"` + Province bool `json:"province,omitempty" toml:"province,omitempty" yaml:"province,omitempty" export:"true"` + Locality bool `json:"locality,omitempty" toml:"locality,omitempty" yaml:"locality,omitempty" export:"true"` + Organization bool `json:"organization,omitempty" toml:"organization,omitempty" yaml:"organization,omitempty" export:"true"` + CommonName bool `json:"commonName,omitempty" toml:"commonName,omitempty" yaml:"commonName,omitempty" export:"true"` + SerialNumber bool `json:"serialNumber,omitempty" toml:"serialNumber,omitempty" yaml:"serialNumber,omitempty" export:"true"` + DomainComponent bool `json:"domainComponent,omitempty" toml:"domainComponent,omitempty" yaml:"domainComponent,omitempty" export:"true"` } // +k8s:deepcopy-gen=true @@ -437,10 +428,10 @@ type Users []string // CA, Cert and Key can be either path or file contents. type ClientTLS struct { CA string `json:"ca,omitempty" toml:"ca,omitempty" yaml:"ca,omitempty"` - CAOptional bool `json:"caOptional,omitempty" toml:"caOptional,omitempty" yaml:"caOptional,omitempty"` + CAOptional bool `json:"caOptional,omitempty" toml:"caOptional,omitempty" yaml:"caOptional,omitempty" export:"true"` Cert string `json:"cert,omitempty" toml:"cert,omitempty" yaml:"cert,omitempty"` Key string `json:"key,omitempty" toml:"key,omitempty" yaml:"key,omitempty"` - InsecureSkipVerify bool `json:"insecureSkipVerify,omitempty" toml:"insecureSkipVerify,omitempty" yaml:"insecureSkipVerify,omitempty"` + InsecureSkipVerify bool `json:"insecureSkipVerify,omitempty" toml:"insecureSkipVerify,omitempty" yaml:"insecureSkipVerify,omitempty" export:"true"` } // CreateTLSConfig creates a TLS config from ClientTLS structures. diff --git a/pkg/config/dynamic/tcp_config.go b/pkg/config/dynamic/tcp_config.go index e6b604b23..ebf8423f3 100644 --- a/pkg/config/dynamic/tcp_config.go +++ b/pkg/config/dynamic/tcp_config.go @@ -10,31 +10,31 @@ import ( // TCPConfiguration contains all the TCP configuration parameters. type TCPConfiguration struct { - Routers map[string]*TCPRouter `json:"routers,omitempty" toml:"routers,omitempty" yaml:"routers,omitempty"` - Services map[string]*TCPService `json:"services,omitempty" toml:"services,omitempty" yaml:"services,omitempty"` + Routers map[string]*TCPRouter `json:"routers,omitempty" toml:"routers,omitempty" yaml:"routers,omitempty" export:"true"` + Services map[string]*TCPService `json:"services,omitempty" toml:"services,omitempty" yaml:"services,omitempty" export:"true"` } // +k8s:deepcopy-gen=true // TCPService holds a tcp service configuration (can only be of one type at the same time). type TCPService struct { - LoadBalancer *TCPServersLoadBalancer `json:"loadBalancer,omitempty" toml:"loadBalancer,omitempty" yaml:"loadBalancer,omitempty"` - Weighted *TCPWeightedRoundRobin `json:"weighted,omitempty" toml:"weighted,omitempty" yaml:"weighted,omitempty" label:"-"` + LoadBalancer *TCPServersLoadBalancer `json:"loadBalancer,omitempty" toml:"loadBalancer,omitempty" yaml:"loadBalancer,omitempty" export:"true"` + Weighted *TCPWeightedRoundRobin `json:"weighted,omitempty" toml:"weighted,omitempty" yaml:"weighted,omitempty" label:"-" export:"true"` } // +k8s:deepcopy-gen=true // TCPWeightedRoundRobin is a weighted round robin tcp load-balancer of services. type TCPWeightedRoundRobin struct { - Services []TCPWRRService `json:"services,omitempty" toml:"services,omitempty" yaml:"services,omitempty"` + Services []TCPWRRService `json:"services,omitempty" toml:"services,omitempty" yaml:"services,omitempty" export:"true"` } // +k8s:deepcopy-gen=true // TCPWRRService is a reference to a tcp service load-balanced with weighted round robin. type TCPWRRService struct { - Name string `json:"name,omitempty" toml:"name,omitempty" yaml:"name,omitempty"` - Weight *int `json:"weight,omitempty" toml:"weight,omitempty" yaml:"weight,omitempty"` + Name string `json:"name,omitempty" toml:"name,omitempty" yaml:"name,omitempty" export:"true"` + Weight *int `json:"weight,omitempty" toml:"weight,omitempty" yaml:"weight,omitempty" export:"true"` } // SetDefaults Default values for a TCPWRRService. @@ -47,20 +47,20 @@ func (w *TCPWRRService) SetDefaults() { // TCPRouter holds the router configuration. type TCPRouter struct { - EntryPoints []string `json:"entryPoints,omitempty" toml:"entryPoints,omitempty" yaml:"entryPoints,omitempty"` - Service string `json:"service,omitempty" toml:"service,omitempty" yaml:"service,omitempty"` + EntryPoints []string `json:"entryPoints,omitempty" toml:"entryPoints,omitempty" yaml:"entryPoints,omitempty" export:"true"` + Service string `json:"service,omitempty" toml:"service,omitempty" yaml:"service,omitempty" export:"true"` Rule string `json:"rule,omitempty" toml:"rule,omitempty" yaml:"rule,omitempty"` - TLS *RouterTCPTLSConfig `json:"tls,omitempty" toml:"tls,omitempty" yaml:"tls,omitempty" label:"allowEmpty" file:"allowEmpty"` + TLS *RouterTCPTLSConfig `json:"tls,omitempty" toml:"tls,omitempty" yaml:"tls,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"` } // +k8s:deepcopy-gen=true // RouterTCPTLSConfig holds the TLS configuration for a router. type RouterTCPTLSConfig struct { - Passthrough bool `json:"passthrough" toml:"passthrough" yaml:"passthrough"` - Options string `json:"options,omitempty" toml:"options,omitempty" yaml:"options,omitempty"` - CertResolver string `json:"certResolver,omitempty" toml:"certResolver,omitempty" yaml:"certResolver,omitempty"` - Domains []types.Domain `json:"domains,omitempty" toml:"domains,omitempty" yaml:"domains,omitempty"` + Passthrough bool `json:"passthrough" toml:"passthrough" yaml:"passthrough" export:"true"` + Options string `json:"options,omitempty" toml:"options,omitempty" yaml:"options,omitempty" export:"true"` + CertResolver string `json:"certResolver,omitempty" toml:"certResolver,omitempty" yaml:"certResolver,omitempty" export:"true"` + Domains []types.Domain `json:"domains,omitempty" toml:"domains,omitempty" yaml:"domains,omitempty" export:"true"` } // +k8s:deepcopy-gen=true @@ -72,9 +72,9 @@ type TCPServersLoadBalancer struct { // connection, to close the reading capability as well, hence fully terminating the // connection. It is a duration in milliseconds, defaulting to 100. A negative value // means an infinite deadline (i.e. the reading capability is never closed). - TerminationDelay *int `json:"terminationDelay,omitempty" toml:"terminationDelay,omitempty" yaml:"terminationDelay,omitempty"` - ProxyProtocol *ProxyProtocol `json:"proxyProtocol,omitempty" toml:"proxyProtocol,omitempty" yaml:"proxyProtocol,omitempty" label:"allowEmpty" file:"allowEmpty"` - Servers []TCPServer `json:"servers,omitempty" toml:"servers,omitempty" yaml:"servers,omitempty" label-slice-as-struct:"server"` + TerminationDelay *int `json:"terminationDelay,omitempty" toml:"terminationDelay,omitempty" yaml:"terminationDelay,omitempty" export:"true"` + ProxyProtocol *ProxyProtocol `json:"proxyProtocol,omitempty" toml:"proxyProtocol,omitempty" yaml:"proxyProtocol,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"` + Servers []TCPServer `json:"servers,omitempty" toml:"servers,omitempty" yaml:"servers,omitempty" label-slice-as-struct:"server" export:"true"` } // SetDefaults Default values for a TCPServersLoadBalancer. @@ -112,7 +112,7 @@ type TCPServer struct { // ProxyProtocol holds the ProxyProtocol configuration. type ProxyProtocol struct { - Version int `json:"version,omitempty" toml:"version,omitempty" yaml:"version,omitempty"` + Version int `json:"version,omitempty" toml:"version,omitempty" yaml:"version,omitempty" export:"true"` } // SetDefaults Default values for a ProxyProtocol. diff --git a/pkg/config/dynamic/udp_config.go b/pkg/config/dynamic/udp_config.go index f94aa1095..e6a711e86 100644 --- a/pkg/config/dynamic/udp_config.go +++ b/pkg/config/dynamic/udp_config.go @@ -8,31 +8,31 @@ import ( // UDPConfiguration contains all the UDP configuration parameters. type UDPConfiguration struct { - Routers map[string]*UDPRouter `json:"routers,omitempty" toml:"routers,omitempty" yaml:"routers,omitempty"` - Services map[string]*UDPService `json:"services,omitempty" toml:"services,omitempty" yaml:"services,omitempty"` + Routers map[string]*UDPRouter `json:"routers,omitempty" toml:"routers,omitempty" yaml:"routers,omitempty" export:"true"` + Services map[string]*UDPService `json:"services,omitempty" toml:"services,omitempty" yaml:"services,omitempty" export:"true"` } // +k8s:deepcopy-gen=true // UDPService defines the configuration for a UDP service. All fields are mutually exclusive. type UDPService struct { - LoadBalancer *UDPServersLoadBalancer `json:"loadBalancer,omitempty" toml:"loadBalancer,omitempty" yaml:"loadBalancer,omitempty"` - Weighted *UDPWeightedRoundRobin `json:"weighted,omitempty" toml:"weighted,omitempty" yaml:"weighted,omitempty" label:"-"` + LoadBalancer *UDPServersLoadBalancer `json:"loadBalancer,omitempty" toml:"loadBalancer,omitempty" yaml:"loadBalancer,omitempty" export:"true"` + Weighted *UDPWeightedRoundRobin `json:"weighted,omitempty" toml:"weighted,omitempty" yaml:"weighted,omitempty" label:"-" export:"true"` } // +k8s:deepcopy-gen=true // UDPWeightedRoundRobin is a weighted round robin UDP load-balancer of services. type UDPWeightedRoundRobin struct { - Services []UDPWRRService `json:"services,omitempty" toml:"services,omitempty" yaml:"services,omitempty"` + Services []UDPWRRService `json:"services,omitempty" toml:"services,omitempty" yaml:"services,omitempty" export:"true"` } // +k8s:deepcopy-gen=true // UDPWRRService is a reference to a UDP service load-balanced with weighted round robin. type UDPWRRService struct { - Name string `json:"name,omitempty" toml:"name,omitempty" yaml:"name,omitempty"` - Weight *int `json:"weight,omitempty" toml:"weight,omitempty" yaml:"weight,omitempty"` + Name string `json:"name,omitempty" toml:"name,omitempty" yaml:"name,omitempty" export:"true"` + Weight *int `json:"weight,omitempty" toml:"weight,omitempty" yaml:"weight,omitempty" export:"true"` } // SetDefaults sets the default values for a UDPWRRService. @@ -45,15 +45,15 @@ func (w *UDPWRRService) SetDefaults() { // UDPRouter defines the configuration for an UDP router. type UDPRouter struct { - EntryPoints []string `json:"entryPoints,omitempty" toml:"entryPoints,omitempty" yaml:"entryPoints,omitempty"` - Service string `json:"service,omitempty" toml:"service,omitempty" yaml:"service,omitempty"` + EntryPoints []string `json:"entryPoints,omitempty" toml:"entryPoints,omitempty" yaml:"entryPoints,omitempty" export:"true"` + Service string `json:"service,omitempty" toml:"service,omitempty" yaml:"service,omitempty" export:"true"` } // +k8s:deepcopy-gen=true // UDPServersLoadBalancer defines the configuration for a load-balancer of UDP servers. type UDPServersLoadBalancer struct { - Servers []UDPServer `json:"servers,omitempty" toml:"servers,omitempty" yaml:"servers,omitempty" label-slice-as-struct:"server"` + Servers []UDPServer `json:"servers,omitempty" toml:"servers,omitempty" yaml:"servers,omitempty" label-slice-as-struct:"server" export:"true"` } // Mergeable reports whether the given load-balancer can be merged with the receiver. diff --git a/pkg/config/dynamic/zz_generated.deepcopy.go b/pkg/config/dynamic/zz_generated.deepcopy.go index 30d60dc88..43da15299 100644 --- a/pkg/config/dynamic/zz_generated.deepcopy.go +++ b/pkg/config/dynamic/zz_generated.deepcopy.go @@ -49,37 +49,6 @@ func (in *AddPrefix) DeepCopy() *AddPrefix { 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 diff --git a/pkg/pilot/pilot.go b/pkg/pilot/pilot.go index eafdd6176..73d6876bb 100644 --- a/pkg/pilot/pilot.go +++ b/pkg/pilot/pilot.go @@ -5,57 +5,56 @@ import ( "context" "encoding/json" "fmt" + "hash/fnv" "io/ioutil" "net/http" "time" "github.com/cenkalti/backoff/v4" - "github.com/traefik/traefik/v2/pkg/config/runtime" + "github.com/traefik/traefik/v2/pkg/anonymize" + "github.com/traefik/traefik/v2/pkg/config/dynamic" "github.com/traefik/traefik/v2/pkg/log" "github.com/traefik/traefik/v2/pkg/metrics" "github.com/traefik/traefik/v2/pkg/safe" "github.com/traefik/traefik/v2/pkg/version" ) -const baseURL = "https://instance-info.pilot.traefik.io/public" - -const tokenHeader = "X-Token" - const ( - pilotTimer = 5 * time.Minute - maxElapsedTime = 4 * time.Minute + baseInstanceInfoURL = "https://instance-info.pilot.traefik.io/public" + baseGatewayURL = "https://gateway.pilot.traefik.io" ) -// RunTimeRepresentation is the configuration information exposed by the API handler. -type RunTimeRepresentation struct { - Routers map[string]*runtime.RouterInfo `json:"routers,omitempty"` - Middlewares map[string]*runtime.MiddlewareInfo `json:"middlewares,omitempty"` - Services map[string]*serviceInfoRepresentation `json:"services,omitempty"` - TCPRouters map[string]*runtime.TCPRouterInfo `json:"tcpRouters,omitempty"` - TCPServices map[string]*runtime.TCPServiceInfo `json:"tcpServices,omitempty"` - UDPRouters map[string]*runtime.UDPRouterInfo `json:"udpRouters,omitempty"` - UDPServices map[string]*runtime.UDPServiceInfo `json:"udpServices,omitempty"` -} +const ( + tokenHeader = "X-Token" + tokenHashHeader = "X-Token-Hash" +) -type serviceInfoRepresentation struct { - *runtime.ServiceInfo - ServerStatus map[string]string `json:"serverStatus,omitempty"` -} +const ( + pilotInstanceInfoTimer = 5 * time.Minute + pilotDynConfTimer = 12 * time.Hour + maxElapsedTime = 4 * time.Minute +) type instanceInfo struct { - ID string `json:"id,omitempty"` - Configuration RunTimeRepresentation `json:"configuration,omitempty"` - Metrics []metrics.PilotMetric `json:"metrics,omitempty"` + ID string `json:"id,omitempty"` + Metrics []metrics.PilotMetric `json:"metrics,omitempty"` } // New creates a new Pilot. func New(token string, metricsRegistry *metrics.PilotRegistry, pool *safe.Pool) *Pilot { + tokenHash := fnv.New64a() + + // the `sum64a` implementation of the `Write` method never returns an error. + _, _ = tokenHash.Write([]byte(token)) + return &Pilot{ - rtConfChan: make(chan *runtime.Configuration), + dynamicConfigCh: make(chan dynamic.Configuration), client: &client{ - token: token, - httpClient: &http.Client{Timeout: 5 * time.Second}, - baseURL: baseURL, + token: token, + tokenHash: fmt.Sprintf("%x", tokenHash.Sum64()), + httpClient: &http.Client{Timeout: 5 * time.Second}, + baseInstanceInfoURL: baseInstanceInfoURL, + baseGatewayURL: baseGatewayURL, }, routinesPool: pool, metricsRegistry: metricsRegistry, @@ -67,44 +66,25 @@ type Pilot struct { routinesPool *safe.Pool client *client - rtConf *runtime.Configuration - rtConfChan chan *runtime.Configuration + dynamicConfig dynamic.Configuration + dynamicConfigCh chan dynamic.Configuration metricsRegistry *metrics.PilotRegistry } -// SetRuntimeConfiguration stores the runtime configuration. -func (p *Pilot) SetRuntimeConfiguration(rtConf *runtime.Configuration) { - p.rtConfChan <- rtConf +// SetDynamicConfiguration stores the dynamic configuration. +func (p *Pilot) SetDynamicConfiguration(dynamicConfig dynamic.Configuration) { + p.dynamicConfigCh <- dynamicConfig } -func (p *Pilot) getRepresentation() RunTimeRepresentation { - if p.rtConf == nil { - return RunTimeRepresentation{} +func (p *Pilot) sendAnonDynConf(ctx context.Context, config dynamic.Configuration) { + err := p.client.SendAnonDynConf(ctx, config) + if err != nil { + log.WithoutContext().Error(err) } - - siRepr := make(map[string]*serviceInfoRepresentation, len(p.rtConf.Services)) - for k, v := range p.rtConf.Services { - siRepr[k] = &serviceInfoRepresentation{ - ServiceInfo: v, - ServerStatus: v.GetAllStatus(), - } - } - - result := RunTimeRepresentation{ - Routers: p.rtConf.Routers, - Middlewares: p.rtConf.Middlewares, - Services: siRepr, - TCPRouters: p.rtConf.TCPRouters, - TCPServices: p.rtConf.TCPServices, - UDPRouters: p.rtConf.UDPRouters, - UDPServices: p.rtConf.UDPServices, - } - - return result } -func (p *Pilot) sendData(ctx context.Context, conf RunTimeRepresentation, pilotMetrics []metrics.PilotMetric) { - err := p.client.SendData(ctx, conf, pilotMetrics) +func (p *Pilot) sendInstanceInfo(ctx context.Context, pilotMetrics []metrics.PilotMetric) { + err := p.client.SendInstanceInfo(ctx, pilotMetrics) if err != nil { log.WithoutContext().Error(err) } @@ -112,35 +92,33 @@ func (p *Pilot) sendData(ctx context.Context, conf RunTimeRepresentation, pilotM // Tick sends data periodically. func (p *Pilot) Tick(ctx context.Context) { - select { - case rtConf := <-p.rtConfChan: - p.rtConf = rtConf - break - case <-ctx.Done(): - return - } - - conf := p.getRepresentation() pilotMetrics := p.metricsRegistry.Data() p.routinesPool.GoCtx(func(ctxRt context.Context) { - p.sendData(ctxRt, conf, pilotMetrics) + p.sendInstanceInfo(ctxRt, pilotMetrics) }) - ticker := time.NewTicker(pilotTimer) + instanceInfoTicker := time.NewTicker(pilotInstanceInfoTimer) + dynConfTicker := time.NewTicker(pilotDynConfTimer) + for { select { - case tick := <-ticker.C: - log.WithoutContext().Debugf("Send to pilot: %s", tick) + case tick := <-instanceInfoTicker.C: + log.WithoutContext().Debugf("Send instance info to pilot: %s", tick) - conf := p.getRepresentation() pilotMetrics := p.metricsRegistry.Data() p.routinesPool.GoCtx(func(ctxRt context.Context) { - p.sendData(ctxRt, conf, pilotMetrics) + p.sendInstanceInfo(ctxRt, pilotMetrics) }) - case rtConf := <-p.rtConfChan: - p.rtConf = rtConf + case tick := <-dynConfTicker.C: + log.WithoutContext().Debugf("Send anonymized dynamic configuration to pilot: %s", tick) + + p.routinesPool.GoCtx(func(ctxRt context.Context) { + p.sendAnonDynConf(ctxRt, p.dynamicConfig) + }) + case dynamicConfig := <-p.dynamicConfigCh: + p.dynamicConfig = dynamicConfig case <-ctx.Done(): return } @@ -148,15 +126,17 @@ func (p *Pilot) Tick(ctx context.Context) { } type client struct { - httpClient *http.Client - baseURL string - token string - uuid string + httpClient *http.Client + baseInstanceInfoURL string + baseGatewayURL string + token string + tokenHash string + uuid string } func (c *client) createUUID() (string, error) { data := []byte(`{"version":"` + version.Version + `","codeName":"` + version.Codename + `"}`) - req, err := http.NewRequest(http.MethodPost, c.baseURL+"/", bytes.NewBuffer(data)) + req, err := http.NewRequest(http.MethodPost, c.baseInstanceInfoURL+"/", bytes.NewBuffer(data)) if err != nil { return "", fmt.Errorf("failed to create request: %w", err) } @@ -189,22 +169,23 @@ func (c *client) createUUID() (string, error) { return created.ID, nil } -// SendData sends data to Pilot. -func (c *client) SendData(ctx context.Context, rtConf RunTimeRepresentation, pilotMetrics []metrics.PilotMetric) error { - exponentialBackOff := backoff.NewExponentialBackOff() - exponentialBackOff.MaxElapsedTime = maxElapsedTime +// SendAnonDynConf sends anonymized dynamic configuration to Pilot. +func (c *client) SendAnonDynConf(ctx context.Context, config dynamic.Configuration) error { + anonConfig, err := anonymize.Do(&config, false) + if err != nil { + return fmt.Errorf("unable to anonymize dynamic configuration: %w", err) + } - return backoff.RetryNotify( - func() error { - return c.sendData(rtConf, pilotMetrics) - }, - backoff.WithContext(exponentialBackOff, ctx), - func(err error, duration time.Duration) { - log.WithoutContext().Errorf("retry in %s due to: %v ", duration, err) - }) + req, err := http.NewRequest(http.MethodPost, c.baseGatewayURL+"/collect", bytes.NewReader([]byte(anonConfig))) + if err != nil { + return fmt.Errorf("failed to create request: %w", err) + } + + return c.sendDataRetryable(ctx, req) } -func (c *client) sendData(_ RunTimeRepresentation, pilotMetrics []metrics.PilotMetric) error { +// SendInstanceInfo sends instance information to Pilot. +func (c *client) SendInstanceInfo(ctx context.Context, pilotMetrics []metrics.PilotMetric) error { if len(c.uuid) == 0 { var err error c.uuid, err = c.createUUID() @@ -225,29 +206,45 @@ func (c *client) sendData(_ RunTimeRepresentation, pilotMetrics []metrics.PilotM return fmt.Errorf("failed to marshall request body: %w", err) } - request, err := http.NewRequest(http.MethodPost, c.baseURL+"/command", bytes.NewBuffer(b)) + req, err := http.NewRequest(http.MethodPost, c.baseInstanceInfoURL+"/command", bytes.NewReader(b)) if err != nil { - return fmt.Errorf("failed to create request: %w", err) + return fmt.Errorf("failed to create instance info request: %w", err) } - request.Header.Set("Content-Type", "application/json") - request.Header.Set(tokenHeader, c.token) + req.Header.Set(tokenHeader, c.token) - resp, err := c.httpClient.Do(request) - if err != nil { - return fmt.Errorf("failed to call Pilot: %w", err) - } - - defer resp.Body.Close() - - body, err := ioutil.ReadAll(resp.Body) - if err != nil { - return fmt.Errorf("failed to read response body: %w", err) - } - - if resp.StatusCode != http.StatusOK { - return fmt.Errorf("wrong status code while sending configuration: %d: %s", resp.StatusCode, body) - } - - return nil + return c.sendDataRetryable(ctx, req) +} + +func (c *client) sendDataRetryable(ctx context.Context, req *http.Request) error { + exponentialBackOff := backoff.NewExponentialBackOff() + exponentialBackOff.MaxElapsedTime = maxElapsedTime + + req.Header.Set("Content-Type", "application/json") + req.Header.Set(tokenHashHeader, c.tokenHash) + + return backoff.RetryNotify( + func() error { + resp, err := c.httpClient.Do(req) + if err != nil { + return fmt.Errorf("failed to call Pilot: %w", err) + } + + defer func() { _ = resp.Body.Close() }() + + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return fmt.Errorf("failed to read response body: %w", err) + } + + if resp.StatusCode != http.StatusOK { + return fmt.Errorf("wrong status code while sending configuration: %d: %s", resp.StatusCode, body) + } + + return nil + }, + backoff.WithContext(exponentialBackOff, ctx), + func(err error, duration time.Duration) { + log.WithoutContext().Errorf("retry in %s due to: %v ", duration, err) + }) } diff --git a/pkg/pilot/pilot_test.go b/pkg/pilot/pilot_test.go index 6661ccb69..4076175ca 100644 --- a/pkg/pilot/pilot_test.go +++ b/pkg/pilot/pilot_test.go @@ -4,13 +4,16 @@ import ( "context" "encoding/json" "fmt" + "hash/fnv" "net/http" "net/http/httptest" + "reflect" "testing" "time" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/traefik/traefik/v2/pkg/config/runtime" + "github.com/traefik/traefik/v2/pkg/config/dynamic" "github.com/traefik/traefik/v2/pkg/metrics" "github.com/traefik/traefik/v2/pkg/safe" ) @@ -45,15 +48,16 @@ func TestTick(t *testing.T) { }) pilot := New("token", metrics.RegisterPilot(), safe.NewPool(context.Background())) - pilot.client.baseURL = server.URL + + pilot.client.baseInstanceInfoURL = server.URL ctx, cancel := context.WithCancel(context.Background()) t.Cleanup(cancel) go pilot.Tick(ctx) - pilot.SetRuntimeConfiguration(&runtime.Configuration{}) - pilot.SetRuntimeConfiguration(&runtime.Configuration{}) + pilot.SetDynamicConfiguration(dynamic.Configuration{}) + pilot.SetDynamicConfiguration(dynamic.Configuration{}) select { case <-time.Tick(10 * time.Second): @@ -63,9 +67,12 @@ func TestTick(t *testing.T) { } } -func TestClient_SendConfiguration(t *testing.T) { +func TestClient_SendInstanceInfo(t *testing.T) { myToken := "myToken" + myTokenHash, err := hashToken(myToken) + require.NoError(t, err) + mux := http.NewServeMux() server := httptest.NewServer(mux) t.Cleanup(server.Close) @@ -79,6 +86,7 @@ func TestClient_SendConfiguration(t *testing.T) { tk := req.Header.Get(tokenHeader) if tk != myToken { http.Error(rw, fmt.Sprintf("invalid token: %s", tk), http.StatusUnauthorized) + return } err := json.NewEncoder(rw).Encode(instanceInfo{ID: "123"}) @@ -97,9 +105,16 @@ func TestClient_SendConfiguration(t *testing.T) { tk := req.Header.Get(tokenHeader) if tk != myToken { http.Error(rw, fmt.Sprintf("invalid token: %s", tk), http.StatusUnauthorized) + return } - defer req.Body.Close() + tkh := req.Header.Get(tokenHashHeader) + if tkh != myTokenHash { + http.Error(rw, fmt.Sprintf("invalid token hash: %s", tkh), http.StatusBadRequest) + return + } + + defer func() { _ = req.Body.Close() }() info := &instanceInfo{} err := json.NewDecoder(req.Body).Decode(info) @@ -114,11 +129,97 @@ func TestClient_SendConfiguration(t *testing.T) { }) client := client{ - baseURL: server.URL, - httpClient: http.DefaultClient, - token: myToken, + baseInstanceInfoURL: server.URL, + httpClient: http.DefaultClient, + token: myToken, + tokenHash: myTokenHash, } - err := client.SendData(context.Background(), RunTimeRepresentation{}, []metrics.PilotMetric{}) + err = client.SendInstanceInfo(context.Background(), []metrics.PilotMetric{}) require.NoError(t, err) } + +func TestClient_SendAnonDynConf(t *testing.T) { + myToken := "myToken" + + myTokenHash, err := hashToken(myToken) + require.NoError(t, err) + + var count int + mux := http.NewServeMux() + mux.HandleFunc("/collect", func(rw http.ResponseWriter, req *http.Request) { + count++ + if count == 1 { + http.Error(rw, "OOPS", http.StatusBadRequest) + return + } + + if req.Method != http.MethodPost { + http.Error(rw, "invalid method", http.StatusMethodNotAllowed) + return + } + + tkh := req.Header.Get(tokenHashHeader) + if tkh != myTokenHash { + http.Error(rw, fmt.Sprintf("invalid token hash: %s", tkh), http.StatusBadRequest) + return + } + + defer func() { _ = req.Body.Close() }() + + config := &dynamic.Configuration{} + err := json.NewDecoder(req.Body).Decode(config) + if err != nil { + http.Error(rw, err.Error(), http.StatusInternalServerError) + return + } + + router, exists := config.HTTP.Routers["foo"] + if !exists { + http.Error(rw, "router configuration is missing", http.StatusBadRequest) + return + } + + if !reflect.DeepEqual(router, &dynamic.Router{Service: "foo", Rule: "xxxx"}) { + http.Error(rw, fmt.Sprintf("configuration is not anonymized: %+v", router), http.StatusBadRequest) + return + } + }) + + server := httptest.NewServer(mux) + t.Cleanup(server.Close) + + client := client{ + baseGatewayURL: server.URL, + httpClient: http.DefaultClient, + token: myToken, + tokenHash: myTokenHash, + } + + config := dynamic.Configuration{ + HTTP: &dynamic.HTTPConfiguration{ + Routers: map[string]*dynamic.Router{ + "foo": { + Service: "foo", + Rule: "foo.com", + }, + }, + }, + } + + err = client.SendAnonDynConf(context.Background(), config) + require.NoError(t, err) + + assert.Equal(t, 2, count) +} + +func hashToken(token string) (string, error) { + tokenHash := fnv.New64a() + + _, err := tokenHash.Write([]byte(token)) + if err != nil { + return "", err + } + + return fmt.Sprintf("%x", tokenHash.Sum64()), nil +} diff --git a/pkg/tls/tls.go b/pkg/tls/tls.go index 62e114329..8e2e3f884 100644 --- a/pkg/tls/tls.go +++ b/pkg/tls/tls.go @@ -9,7 +9,7 @@ type ClientAuth struct { CAFiles []FileOrContent `json:"caFiles,omitempty" toml:"caFiles,omitempty" yaml:"caFiles,omitempty"` // ClientAuthType defines the client authentication type to apply. // The available values are: "NoClientCert", "RequestClientCert", "VerifyClientCertIfGiven" and "RequireAndVerifyClientCert". - ClientAuthType string `json:"clientAuthType,omitempty" toml:"clientAuthType,omitempty" yaml:"clientAuthType,omitempty"` + ClientAuthType string `json:"clientAuthType,omitempty" toml:"clientAuthType,omitempty" yaml:"clientAuthType,omitempty" export:"true"` } // +k8s:deepcopy-gen=true @@ -18,8 +18,8 @@ type ClientAuth struct { type Options struct { MinVersion string `json:"minVersion,omitempty" toml:"minVersion,omitempty" yaml:"minVersion,omitempty" export:"true"` MaxVersion string `json:"maxVersion,omitempty" toml:"maxVersion,omitempty" yaml:"maxVersion,omitempty" export:"true"` - CipherSuites []string `json:"cipherSuites,omitempty" toml:"cipherSuites,omitempty" yaml:"cipherSuites,omitempty"` - CurvePreferences []string `json:"curvePreferences,omitempty" toml:"curvePreferences,omitempty" yaml:"curvePreferences,omitempty"` + CipherSuites []string `json:"cipherSuites,omitempty" toml:"cipherSuites,omitempty" yaml:"cipherSuites,omitempty" export:"true"` + CurvePreferences []string `json:"curvePreferences,omitempty" toml:"curvePreferences,omitempty" yaml:"curvePreferences,omitempty" export:"true"` ClientAuth ClientAuth `json:"clientAuth,omitempty" toml:"clientAuth,omitempty" yaml:"clientAuth,omitempty"` SniStrict bool `json:"sniStrict,omitempty" toml:"sniStrict,omitempty" yaml:"sniStrict,omitempty" export:"true"` PreferServerCipherSuites bool `json:"preferServerCipherSuites,omitempty" toml:"preferServerCipherSuites,omitempty" yaml:"preferServerCipherSuites,omitempty" export:"true"` @@ -29,13 +29,13 @@ type Options struct { // Store holds the options for a given Store. type Store struct { - DefaultCertificate *Certificate `json:"defaultCertificate,omitempty" toml:"defaultCertificate,omitempty" yaml:"defaultCertificate,omitempty"` + DefaultCertificate *Certificate `json:"defaultCertificate,omitempty" toml:"defaultCertificate,omitempty" yaml:"defaultCertificate,omitempty" export:"true"` } // +k8s:deepcopy-gen=true // CertAndStores allows mapping a TLS certificate to a list of entry points. type CertAndStores struct { - Certificate `yaml:",inline"` - Stores []string `json:"stores,omitempty" toml:"stores,omitempty" yaml:"stores,omitempty"` + Certificate `yaml:",inline" export:"true"` + Stores []string `json:"stores,omitempty" toml:"stores,omitempty" yaml:"stores,omitempty" export:"true"` }