From a4b354b33f56763f4c56a0fbdae134672347673a Mon Sep 17 00:00:00 2001 From: Ali <59630460+ibrahimalihc@users.noreply.github.com> Date: Mon, 24 Jan 2022 05:08:05 -0500 Subject: [PATCH] Redact credentials before logging Co-authored-by: Tom Moulard Co-authored-by: Mathieu Lonjaret --- docs/scripts/verify.sh | 2 +- pkg/anonymize/anonymize_doOnStruct_test.go | 177 ------- pkg/collector/collector.go | 4 +- pkg/config/dynamic/middlewares.go | 4 +- pkg/config/static/pilot.go | 2 +- pkg/config/static/static_config.go | 2 +- pkg/pilot/pilot.go | 4 +- pkg/ping/ping.go | 2 +- pkg/provider/acme/provider.go | 4 +- pkg/provider/aggregator/aggregator.go | 8 +- pkg/provider/consulcatalog/consul_catalog.go | 6 +- pkg/provider/ecs/ecs.go | 4 +- pkg/provider/kubernetes/crd/kubernetes.go | 2 +- pkg/provider/kubernetes/gateway/kubernetes.go | 2 +- pkg/provider/kubernetes/ingress/kubernetes.go | 2 +- pkg/provider/kv/kv.go | 10 +- pkg/provider/marathon/marathon.go | 6 +- .../anonymize.go => redactor/redactor.go} | 88 ++-- .../redactor_config_test.go} | 37 +- .../redactor_doOnJSON_test.go} | 4 +- pkg/redactor/redactor_doOnStruct_test.go | 382 ++++++++++++++ .../testdata/anonymized-dynamic-config.json | 0 .../testdata/anonymized-static-config.json | 8 +- .../testdata/example.json | 0 .../testdata/expected.json | 0 .../testdata/secured-dynamic-config.json | 487 ++++++++++++++++++ pkg/server/configurationwatcher.go | 6 +- pkg/tls/certificate.go | 2 +- pkg/tracing/elastic/elastic.go | 2 +- pkg/tracing/jaeger/jaeger.go | 4 +- pkg/types/metrics.go | 6 +- pkg/types/tls.go | 2 +- 32 files changed, 1007 insertions(+), 262 deletions(-) delete mode 100644 pkg/anonymize/anonymize_doOnStruct_test.go rename pkg/{anonymize/anonymize.go => redactor/redactor.go} (60%) rename pkg/{anonymize/anonymize_config_test.go => redactor/redactor_config_test.go} (96%) rename pkg/{anonymize/anonymize_doOnJSON_test.go => redactor/redactor_doOnJSON_test.go} (97%) create mode 100644 pkg/redactor/redactor_doOnStruct_test.go rename pkg/{anonymize => redactor}/testdata/anonymized-dynamic-config.json (100%) rename pkg/{anonymize => redactor}/testdata/anonymized-static-config.json (98%) rename pkg/{anonymize => redactor}/testdata/example.json (100%) rename pkg/{anonymize => redactor}/testdata/expected.json (100%) create mode 100644 pkg/redactor/testdata/secured-dynamic-config.json diff --git a/docs/scripts/verify.sh b/docs/scripts/verify.sh index 86b9d7f6e..d1d367c2b 100755 --- a/docs/scripts/verify.sh +++ b/docs/scripts/verify.sh @@ -22,7 +22,7 @@ find "${PATH_TO_SITE}" -type f -not -path "/app/site/theme/*" \ --alt_ignore="/traefikproxy-vertical-logo-color.svg/" \ --http_status_ignore="0,500,501,503" \ --file_ignore="/404.html/" \ - --url_ignore="/https://groups.google.com/a/traefik.io/forum/#!forum/security/,/localhost:/,/127.0.0.1:/,/fonts.gstatic.com/,/.minikube/,/github.com\/traefik\/traefik\/*edit*/,/github.com\/traefik\/traefik/,/doc.traefik.io/,/github\.com\/golang\/oauth2\/blob\/36a7019397c4c86cf59eeab3bc0d188bac444277\/.+/,/www.akamai.com/,/pilot.traefik.io\/profile/,/traefik.io/,/doc.traefik.io\/traefik-mesh/,/www.mkdocs.org/,/squidfunk.github.io/,/ietf.org/,/www.namesilo.com/,/www.youtube.com/,/www.linode.com/,/www.alibabacloud.com/,/www.cloudxns.net/" \ + --url_ignore="/https://groups.google.com/a/traefik.io/forum/#!forum/security/,/localhost:/,/127.0.0.1:/,/fonts.gstatic.com/,/.minikube/,/github.com\/traefik\/traefik\/*edit*/,/github.com\/traefik\/traefik/,/doc.traefik.io/,/github\.com\/golang\/oauth2\/blob\/36a7019397c4c86cf59eeab3bc0d188bac444277\/.+/,/www.akamai.com/,/pilot.traefik.io\/profile/,/traefik.io/,/doc.traefik.io\/traefik-mesh/,/www.mkdocs.org/,/squidfunk.github.io/,/ietf.org/,/www.namesilo.com/,/www.youtube.com/,/www.linode.com/,/www.alibabacloud.com/,/www.cloudxns.net/,/www.vultr.com/" \ '{}' 1>/dev/null ## HTML-proofer options at https://github.com/gjtorikian/html-proofer#configuration diff --git a/pkg/anonymize/anonymize_doOnStruct_test.go b/pkg/anonymize/anonymize_doOnStruct_test.go deleted file mode 100644 index 2f72f21f6..000000000 --- a/pkg/anonymize/anonymize_doOnStruct_test.go +++ /dev/null @@ -1,177 +0,0 @@ -package anonymize - -import ( - "reflect" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -type Courgette struct { - Ji string - Ho string -} - -type Tomate struct { - Ji string - Ho string -} - -type Carotte struct { - Name string - Value int - List []string - EList []string `export:"true"` - Courgette Courgette - ECourgette Courgette `export:"true"` - Pourgette *Courgette - EPourgette *Courgette `export:"true"` - Aubergine map[string]string - EAubergine map[string]string `export:"true"` - SAubergine map[string]Tomate - ESAubergine map[string]Tomate `export:"true"` - PSAubergine map[string]*Tomate - EPAubergine map[string]*Tomate `export:"true"` -} - -func Test_doOnStruct(t *testing.T) { - testCase := []struct { - name string - base *Carotte - expected *Carotte - }{ - { - name: "primitive", - base: &Carotte{ - Name: "koko", - Value: 666, - List: []string{"test"}, - EList: []string{"test"}, - }, - expected: &Carotte{ - Name: "xxxx", - List: []string{"xxxx"}, - EList: []string{"test"}, - }, - }, - { - name: "struct", - base: &Carotte{ - Name: "koko", - Courgette: Courgette{ - Ji: "huu", - }, - }, - expected: &Carotte{ - Name: "xxxx", - }, - }, - { - name: "pointer", - base: &Carotte{ - Name: "koko", - Pourgette: &Courgette{ - Ji: "hoo", - }, - }, - expected: &Carotte{ - Name: "xxxx", - Pourgette: nil, - }, - }, - { - name: "export struct", - base: &Carotte{ - Name: "koko", - ECourgette: Courgette{ - Ji: "huu", - }, - }, - expected: &Carotte{ - Name: "xxxx", - ECourgette: Courgette{ - Ji: "xxxx", - }, - }, - }, - { - name: "export pointer struct", - base: &Carotte{ - Name: "koko", - ECourgette: Courgette{ - Ji: "huu", - }, - }, - expected: &Carotte{ - Name: "xxxx", - ECourgette: Courgette{ - Ji: "xxxx", - }, - }, - }, - { - name: "export map string/string", - base: &Carotte{ - Name: "koko", - EAubergine: map[string]string{ - "foo": "bar", - }, - }, - expected: &Carotte{ - Name: "xxxx", - EAubergine: map[string]string{ - "foo": "bar", - }, - }, - }, - { - name: "export map string/pointer", - base: &Carotte{ - Name: "koko", - EPAubergine: map[string]*Tomate{ - "foo": { - Ji: "fdskljf", - }, - }, - }, - expected: &Carotte{ - Name: "xxxx", - EPAubergine: map[string]*Tomate{ - "foo": { - Ji: "xxxx", - }, - }, - }, - }, - { - name: "export map string/struct", - base: &Carotte{ - Name: "koko", - ESAubergine: map[string]Tomate{ - "foo": { - Ji: "JiJiJi", - }, - }, - }, - expected: &Carotte{ - Name: "xxxx", - ESAubergine: map[string]Tomate{ - "foo": { - Ji: "xxxx", - }, - }, - }, - }, - } - - for _, test := range testCase { - t.Run(test.name, func(t *testing.T) { - val := reflect.ValueOf(test.base).Elem() - err := doOnStruct(val) - require.NoError(t, err) - - assert.EqualValues(t, test.expected, test.base) - }) - } -} diff --git a/pkg/collector/collector.go b/pkg/collector/collector.go index 32c0ea02a..0fec4f061 100644 --- a/pkg/collector/collector.go +++ b/pkg/collector/collector.go @@ -10,9 +10,9 @@ import ( "time" "github.com/mitchellh/hashstructure" - "github.com/traefik/traefik/v2/pkg/anonymize" "github.com/traefik/traefik/v2/pkg/config/static" "github.com/traefik/traefik/v2/pkg/log" + "github.com/traefik/traefik/v2/pkg/redactor" "github.com/traefik/traefik/v2/pkg/version" ) @@ -30,7 +30,7 @@ type data struct { // Collect anonymous data. func Collect(staticConfiguration *static.Configuration) error { - anonConfig, err := anonymize.Do(staticConfiguration, false) + anonConfig, err := redactor.Anonymize(staticConfiguration) if err != nil { return err } diff --git a/pkg/config/dynamic/middlewares.go b/pkg/config/dynamic/middlewares.go index 6f82df3a8..66f58d39f 100644 --- a/pkg/config/dynamic/middlewares.go +++ b/pkg/config/dynamic/middlewares.go @@ -64,7 +64,7 @@ type AddPrefix struct { // BasicAuth holds the HTTP basic authentication configuration. type BasicAuth struct { - Users Users `json:"users,omitempty" toml:"users,omitempty" yaml:"users,omitempty"` + Users Users `json:"users,omitempty" toml:"users,omitempty" yaml:"users,omitempty" loggable:"false"` 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" export:"true"` @@ -108,7 +108,7 @@ type Compress struct { // DigestAuth holds the Digest HTTP authentication configuration. type DigestAuth struct { - Users Users `json:"users,omitempty" toml:"users,omitempty" yaml:"users,omitempty"` + Users Users `json:"users,omitempty" toml:"users,omitempty" yaml:"users,omitempty" loggable:"false"` UsersFile string `json:"usersFile,omitempty" toml:"usersFile,omitempty" yaml:"usersFile,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"` diff --git a/pkg/config/static/pilot.go b/pkg/config/static/pilot.go index 9b977e790..147c8917b 100644 --- a/pkg/config/static/pilot.go +++ b/pkg/config/static/pilot.go @@ -2,7 +2,7 @@ package static // Pilot Configuration related to Traefik Pilot. type Pilot struct { - Token string `description:"Traefik Pilot token." json:"token,omitempty" toml:"token,omitempty" yaml:"token,omitempty"` + Token string `description:"Traefik Pilot token." json:"token,omitempty" toml:"token,omitempty" yaml:"token,omitempty" loggable:"false"` Dashboard bool `description:"Enable Traefik Pilot in the dashboard." json:"dashboard,omitempty" toml:"dashboard,omitempty" yaml:"dashboard,omitempty"` } diff --git a/pkg/config/static/static_config.go b/pkg/config/static/static_config.go index b55d6182a..0b2cfcda8 100644 --- a/pkg/config/static/static_config.go +++ b/pkg/config/static/static_config.go @@ -106,7 +106,7 @@ type API struct { Dashboard bool `description:"Activate dashboard." json:"dashboard,omitempty" toml:"dashboard,omitempty" yaml:"dashboard,omitempty" export:"true"` Debug bool `description:"Enable additional endpoints for debugging and profiling." json:"debug,omitempty" toml:"debug,omitempty" yaml:"debug,omitempty" export:"true"` // TODO: Re-enable statistics - // Statistics *types.Statistics `description:"Enable more detailed statistics." json:"statistics,omitempty" toml:"statistics,omitempty" yaml:"statistics,omitempty" export:"true" label:"allowEmpty" file:"allowEmpty"` + // Statistics *types.Statistics `description:"Enable more detailed statistics." json:"statistics,omitempty" toml:"statistics,omitempty" yaml:"statistics,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"` } // SetDefaults sets the default values. diff --git a/pkg/pilot/pilot.go b/pkg/pilot/pilot.go index 7869bd4b5..2cf03326f 100644 --- a/pkg/pilot/pilot.go +++ b/pkg/pilot/pilot.go @@ -11,10 +11,10 @@ import ( "time" "github.com/cenkalti/backoff/v4" - "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/redactor" "github.com/traefik/traefik/v2/pkg/safe" "github.com/traefik/traefik/v2/pkg/version" ) @@ -173,7 +173,7 @@ func (c *client) createUUID() (string, error) { // SendAnonDynConf sends anonymized dynamic configuration to Pilot. func (c *client) SendAnonDynConf(ctx context.Context, config dynamic.Configuration) error { - anonConfig, err := anonymize.Do(&config, false) + anonConfig, err := redactor.Anonymize(&config) if err != nil { return fmt.Errorf("unable to anonymize dynamic configuration: %w", err) } diff --git a/pkg/ping/ping.go b/pkg/ping/ping.go index a7a27a991..a05311fd1 100644 --- a/pkg/ping/ping.go +++ b/pkg/ping/ping.go @@ -8,7 +8,7 @@ import ( // Handler expose ping routes. type Handler struct { - EntryPoint string `description:"EntryPoint" export:"true" json:"entryPoint,omitempty" toml:"entryPoint,omitempty" yaml:"entryPoint,omitempty"` + EntryPoint string `description:"EntryPoint" json:"entryPoint,omitempty" toml:"entryPoint,omitempty" yaml:"entryPoint,omitempty" export:"true"` ManualRouting bool `description:"Manual routing" json:"manualRouting,omitempty" toml:"manualRouting,omitempty" yaml:"manualRouting,omitempty" export:"true"` TerminatingStatusCode int `description:"Terminating status code" json:"terminatingStatusCode,omitempty" toml:"terminatingStatusCode,omitempty" yaml:"terminatingStatusCode,omitempty" export:"true"` terminating bool diff --git a/pkg/provider/acme/provider.go b/pkg/provider/acme/provider.go index 3a9aedada..bbc95d516 100644 --- a/pkg/provider/acme/provider.go +++ b/pkg/provider/acme/provider.go @@ -69,8 +69,8 @@ type Certificate struct { // EAB contains External Account Binding configuration. type EAB struct { - Kid string `description:"Key identifier from External CA." json:"kid,omitempty" toml:"kid,omitempty" yaml:"kid,omitempty"` - HmacEncoded string `description:"Base64 encoded HMAC key from External CA." json:"hmacEncoded,omitempty" toml:"hmacEncoded,omitempty" yaml:"hmacEncoded,omitempty"` + Kid string `description:"Key identifier from External CA." json:"kid,omitempty" toml:"kid,omitempty" yaml:"kid,omitempty" loggable:"false"` + HmacEncoded string `description:"Base64 encoded HMAC key from External CA." json:"hmacEncoded,omitempty" toml:"hmacEncoded,omitempty" yaml:"hmacEncoded,omitempty" loggable:"false"` } // DNSChallenge contains DNS challenge configuration. diff --git a/pkg/provider/aggregator/aggregator.go b/pkg/provider/aggregator/aggregator.go index 89bcae4d5..0d58f76af 100644 --- a/pkg/provider/aggregator/aggregator.go +++ b/pkg/provider/aggregator/aggregator.go @@ -1,14 +1,13 @@ package aggregator import ( - "encoding/json" - "github.com/traefik/traefik/v2/pkg/config/dynamic" "github.com/traefik/traefik/v2/pkg/config/static" "github.com/traefik/traefik/v2/pkg/log" "github.com/traefik/traefik/v2/pkg/provider" "github.com/traefik/traefik/v2/pkg/provider/file" "github.com/traefik/traefik/v2/pkg/provider/traefik" + "github.com/traefik/traefik/v2/pkg/redactor" "github.com/traefik/traefik/v2/pkg/safe" ) @@ -140,12 +139,13 @@ func (p ProviderAggregator) Provide(configurationChan chan<- dynamic.Message, po } func launchProvider(configurationChan chan<- dynamic.Message, pool *safe.Pool, prd provider.Provider) { - jsonConf, err := json.Marshal(prd) + jsonConf, err := redactor.RemoveCredentials(prd) if err != nil { log.WithoutContext().Debugf("Cannot marshal the provider configuration %T: %v", prd, err) } - log.WithoutContext().Infof("Starting provider %T %s", prd, jsonConf) + log.WithoutContext().Infof("Starting provider %T", prd) + log.WithoutContext().Debugf("%T provider configuration: %s", prd, jsonConf) currentProvider := prd err = currentProvider.Provide(configurationChan, pool) diff --git a/pkg/provider/consulcatalog/consul_catalog.go b/pkg/provider/consulcatalog/consul_catalog.go index f7af2bc06..86beef546 100644 --- a/pkg/provider/consulcatalog/consul_catalog.go +++ b/pkg/provider/consulcatalog/consul_catalog.go @@ -67,7 +67,7 @@ type EndpointConfig struct { Address string `description:"The address of the Consul server" json:"address,omitempty" toml:"address,omitempty" yaml:"address,omitempty"` Scheme string `description:"The URI scheme for the Consul server" json:"scheme,omitempty" toml:"scheme,omitempty" yaml:"scheme,omitempty"` DataCenter string `description:"Data center to use. If not provided, the default agent data center is used" json:"datacenter,omitempty" toml:"datacenter,omitempty" yaml:"datacenter,omitempty"` - Token string `description:"Token is used to provide a per-request ACL token which overrides the agent's default token" json:"token,omitempty" toml:"token,omitempty" yaml:"token,omitempty"` + Token string `description:"Token is used to provide a per-request ACL token which overrides the agent's default token" json:"token,omitempty" toml:"token,omitempty" yaml:"token,omitempty" loggable:"false"` TLS *types.ClientTLS `description:"Enable TLS support." json:"tls,omitempty" toml:"tls,omitempty" yaml:"tls,omitempty" export:"true"` HTTPAuth *EndpointHTTPAuthConfig `description:"Auth info to use for http access" json:"httpAuth,omitempty" toml:"httpAuth,omitempty" yaml:"httpAuth,omitempty" export:"true"` EndpointWaitTime ptypes.Duration `description:"WaitTime limits how long a Watch will block. If not provided, the agent default values will be used" json:"endpointWaitTime,omitempty" toml:"endpointWaitTime,omitempty" yaml:"endpointWaitTime,omitempty" export:"true"` @@ -75,8 +75,8 @@ type EndpointConfig struct { // EndpointHTTPAuthConfig holds configurations of the authentication. type EndpointHTTPAuthConfig struct { - Username string `description:"Basic Auth username" json:"username,omitempty" toml:"username,omitempty" yaml:"username,omitempty"` - Password string `description:"Basic Auth password" json:"password,omitempty" toml:"password,omitempty" yaml:"password,omitempty"` + Username string `description:"Basic Auth username" json:"username,omitempty" toml:"username,omitempty" yaml:"username,omitempty" loggable:"false"` + Password string `description:"Basic Auth password" json:"password,omitempty" toml:"password,omitempty" yaml:"password,omitempty" loggable:"false"` } // SetDefaults sets the default values. diff --git a/pkg/provider/ecs/ecs.go b/pkg/provider/ecs/ecs.go index 73bfe47f8..e2711bc9e 100644 --- a/pkg/provider/ecs/ecs.go +++ b/pkg/provider/ecs/ecs.go @@ -34,8 +34,8 @@ type Provider struct { Clusters []string `description:"ECS Clusters name" json:"clusters,omitempty" toml:"clusters,omitempty" yaml:"clusters,omitempty" export:"true"` AutoDiscoverClusters bool `description:"Auto discover cluster" json:"autoDiscoverClusters,omitempty" toml:"autoDiscoverClusters,omitempty" yaml:"autoDiscoverClusters,omitempty" export:"true"` Region string `description:"The AWS region to use for requests" json:"region,omitempty" toml:"region,omitempty" yaml:"region,omitempty" export:"true"` - AccessKeyID string `description:"The AWS credentials access key to use for making requests" json:"accessKeyID,omitempty" toml:"accessKeyID,omitempty" yaml:"accessKeyID,omitempty"` - SecretAccessKey string `description:"The AWS credentials access key to use for making requests" json:"secretAccessKey,omitempty" toml:"secretAccessKey,omitempty" yaml:"secretAccessKey,omitempty"` + AccessKeyID string `description:"The AWS credentials access key to use for making requests" json:"accessKeyID,omitempty" toml:"accessKeyID,omitempty" yaml:"accessKeyID,omitempty" loggable:"false"` + SecretAccessKey string `description:"The AWS credentials access key to use for making requests" json:"secretAccessKey,omitempty" toml:"secretAccessKey,omitempty" yaml:"secretAccessKey,omitempty" loggable:"false"` defaultRuleTpl *template.Template } diff --git a/pkg/provider/kubernetes/crd/kubernetes.go b/pkg/provider/kubernetes/crd/kubernetes.go index 5cae68fd4..d35271063 100644 --- a/pkg/provider/kubernetes/crd/kubernetes.go +++ b/pkg/provider/kubernetes/crd/kubernetes.go @@ -45,7 +45,7 @@ const ( // Provider holds configurations of the provider. type Provider struct { Endpoint string `description:"Kubernetes server endpoint (required for external cluster client)." json:"endpoint,omitempty" toml:"endpoint,omitempty" yaml:"endpoint,omitempty"` - Token string `description:"Kubernetes bearer token (not needed for in-cluster client)." json:"token,omitempty" toml:"token,omitempty" yaml:"token,omitempty"` + Token string `description:"Kubernetes bearer token (not needed for in-cluster client)." json:"token,omitempty" toml:"token,omitempty" yaml:"token,omitempty" loggable:"false"` CertAuthFilePath string `description:"Kubernetes certificate authority file path (not needed for in-cluster client)." json:"certAuthFilePath,omitempty" toml:"certAuthFilePath,omitempty" yaml:"certAuthFilePath,omitempty"` Namespaces []string `description:"Kubernetes namespaces." json:"namespaces,omitempty" toml:"namespaces,omitempty" yaml:"namespaces,omitempty" export:"true"` AllowCrossNamespace bool `description:"Allow cross namespace resource reference." json:"allowCrossNamespace,omitempty" toml:"allowCrossNamespace,omitempty" yaml:"allowCrossNamespace,omitempty" export:"true"` diff --git a/pkg/provider/kubernetes/gateway/kubernetes.go b/pkg/provider/kubernetes/gateway/kubernetes.go index 30a0622a0..05698f5f9 100644 --- a/pkg/provider/kubernetes/gateway/kubernetes.go +++ b/pkg/provider/kubernetes/gateway/kubernetes.go @@ -44,7 +44,7 @@ const ( // Provider holds configurations of the provider. type Provider struct { Endpoint string `description:"Kubernetes server endpoint (required for external cluster client)." json:"endpoint,omitempty" toml:"endpoint,omitempty" yaml:"endpoint,omitempty"` - Token string `description:"Kubernetes bearer token (not needed for in-cluster client)." json:"token,omitempty" toml:"token,omitempty" yaml:"token,omitempty"` + Token string `description:"Kubernetes bearer token (not needed for in-cluster client)." json:"token,omitempty" toml:"token,omitempty" yaml:"token,omitempty" loggable:"false"` CertAuthFilePath string `description:"Kubernetes certificate authority file path (not needed for in-cluster client)." json:"certAuthFilePath,omitempty" toml:"certAuthFilePath,omitempty" yaml:"certAuthFilePath,omitempty"` Namespaces []string `description:"Kubernetes namespaces." json:"namespaces,omitempty" toml:"namespaces,omitempty" yaml:"namespaces,omitempty" export:"true"` LabelSelector string `description:"Kubernetes label selector to select specific GatewayClasses." json:"labelSelector,omitempty" toml:"labelSelector,omitempty" yaml:"labelSelector,omitempty" export:"true"` diff --git a/pkg/provider/kubernetes/ingress/kubernetes.go b/pkg/provider/kubernetes/ingress/kubernetes.go index 2e1869fa8..3f7011730 100644 --- a/pkg/provider/kubernetes/ingress/kubernetes.go +++ b/pkg/provider/kubernetes/ingress/kubernetes.go @@ -37,7 +37,7 @@ const ( // Provider holds configurations of the provider. type Provider struct { Endpoint string `description:"Kubernetes server endpoint (required for external cluster client)." json:"endpoint,omitempty" toml:"endpoint,omitempty" yaml:"endpoint,omitempty"` - Token string `description:"Kubernetes bearer token (not needed for in-cluster client)." json:"token,omitempty" toml:"token,omitempty" yaml:"token,omitempty"` + Token string `description:"Kubernetes bearer token (not needed for in-cluster client)." json:"token,omitempty" toml:"token,omitempty" yaml:"token,omitempty" loggable:"false"` CertAuthFilePath string `description:"Kubernetes certificate authority file path (not needed for in-cluster client)." json:"certAuthFilePath,omitempty" toml:"certAuthFilePath,omitempty" yaml:"certAuthFilePath,omitempty"` Namespaces []string `description:"Kubernetes namespaces." json:"namespaces,omitempty" toml:"namespaces,omitempty" yaml:"namespaces,omitempty" export:"true"` LabelSelector string `description:"Kubernetes Ingress label selector to use." json:"labelSelector,omitempty" toml:"labelSelector,omitempty" yaml:"labelSelector,omitempty" export:"true"` diff --git a/pkg/provider/kv/kv.go b/pkg/provider/kv/kv.go index a25fc5f6a..50279e6bb 100644 --- a/pkg/provider/kv/kv.go +++ b/pkg/provider/kv/kv.go @@ -24,14 +24,14 @@ import ( // Provider holds configurations of the provider. type Provider struct { - RootKey string `description:"Root key used for KV store" export:"true" json:"rootKey,omitempty" toml:"rootKey,omitempty" yaml:"rootKey,omitempty"` + RootKey string `description:"Root key used for KV store" json:"rootKey,omitempty" toml:"rootKey,omitempty" yaml:"rootKey,omitempty"` Endpoints []string `description:"KV store endpoints" json:"endpoints,omitempty" toml:"endpoints,omitempty" yaml:"endpoints,omitempty"` - Username string `description:"KV Username" json:"username,omitempty" toml:"username,omitempty" yaml:"username,omitempty"` - Password string `description:"KV Password" json:"password,omitempty" toml:"password,omitempty" yaml:"password,omitempty"` - Token string `description:"KV Token" json:"token,omitempty" toml:"token,omitempty" yaml:"token,omitempty"` + Username string `description:"KV Username" json:"username,omitempty" toml:"username,omitempty" yaml:"username,omitempty" loggable:"false"` + Password string `description:"KV Password" json:"password,omitempty" toml:"password,omitempty" yaml:"password,omitempty" loggable:"false"` + Token string `description:"KV Token" json:"token,omitempty" toml:"token,omitempty" yaml:"token,omitempty" loggable:"false"` Namespace string `description:"KV Namespace" json:"namespace,omitempty" toml:"namespace,omitempty" yaml:"namespace,omitempty"` - TLS *types.ClientTLS `description:"Enable TLS support" export:"true" json:"tls,omitempty" toml:"tls,omitempty" yaml:"tls,omitempty"` + TLS *types.ClientTLS `description:"Enable TLS support" json:"tls,omitempty" toml:"tls,omitempty" yaml:"tls,omitempty" export:"true" ` storeType store.Backend kvClient store.Store diff --git a/pkg/provider/marathon/marathon.go b/pkg/provider/marathon/marathon.go index c78413ad7..b15bb5f2a 100644 --- a/pkg/provider/marathon/marathon.go +++ b/pkg/provider/marathon/marathon.go @@ -52,7 +52,7 @@ type Provider struct { Endpoint string `description:"Marathon server endpoint. You can also specify multiple endpoint for Marathon." json:"endpoint,omitempty" toml:"endpoint,omitempty" yaml:"endpoint,omitempty"` DefaultRule string `description:"Default rule." json:"defaultRule,omitempty" toml:"defaultRule,omitempty" yaml:"defaultRule,omitempty"` ExposedByDefault bool `description:"Expose Marathon apps by default." json:"exposedByDefault,omitempty" toml:"exposedByDefault,omitempty" yaml:"exposedByDefault,omitempty" export:"true"` - DCOSToken string `description:"DCOSToken for DCOS environment, This will override the Authorization header." json:"dcosToken,omitempty" toml:"dcosToken,omitempty" yaml:"dcosToken,omitempty"` + DCOSToken string `description:"DCOSToken for DCOS environment, This will override the Authorization header." json:"dcosToken,omitempty" toml:"dcosToken,omitempty" yaml:"dcosToken,omitempty" loggable:"false"` TLS *types.ClientTLS `description:"Enable TLS support." json:"tls,omitempty" toml:"tls,omitempty" yaml:"tls,omitempty" export:"true"` DialerTimeout ptypes.Duration `description:"Set a dialer timeout for Marathon." json:"dialerTimeout,omitempty" toml:"dialerTimeout,omitempty" yaml:"dialerTimeout,omitempty" export:"true"` ResponseHeaderTimeout ptypes.Duration `description:"Set a response header timeout for Marathon." json:"responseHeaderTimeout,omitempty" toml:"responseHeaderTimeout,omitempty" yaml:"responseHeaderTimeout,omitempty" export:"true"` @@ -80,8 +80,8 @@ func (p *Provider) SetDefaults() { // Basic holds basic authentication specific configurations. type Basic struct { - HTTPBasicAuthUser string `description:"Basic authentication User." json:"httpBasicAuthUser,omitempty" toml:"httpBasicAuthUser,omitempty" yaml:"httpBasicAuthUser,omitempty"` - HTTPBasicPassword string `description:"Basic authentication Password." json:"httpBasicPassword,omitempty" toml:"httpBasicPassword,omitempty" yaml:"httpBasicPassword,omitempty"` + HTTPBasicAuthUser string `description:"Basic authentication User." json:"httpBasicAuthUser,omitempty" toml:"httpBasicAuthUser,omitempty" yaml:"httpBasicAuthUser,omitempty" loggable:"false"` + HTTPBasicPassword string `description:"Basic authentication Password." json:"httpBasicPassword,omitempty" toml:"httpBasicPassword,omitempty" yaml:"httpBasicPassword,omitempty" loggable:"false"` } // Init the provider. diff --git a/pkg/anonymize/anonymize.go b/pkg/redactor/redactor.go similarity index 60% rename from pkg/anonymize/anonymize.go rename to pkg/redactor/redactor.go index ca7b87d12..c122e307c 100644 --- a/pkg/anonymize/anonymize.go +++ b/pkg/redactor/redactor.go @@ -1,4 +1,4 @@ -package anonymize +package redactor import ( "encoding/json" @@ -13,12 +13,39 @@ import ( ) const ( - maskShort = "xxxx" - maskLarge = maskShort + maskShort + maskShort + maskShort + maskShort + maskShort + maskShort + maskShort + maskShort = "xxxx" + maskLarge = maskShort + maskShort + maskShort + maskShort + maskShort + maskShort + maskShort + maskShort + tagLoggable = "loggable" + tagExport = "export" ) -// Do sends configuration. -func Do(baseConfig interface{}, indent bool) (string, error) { +// Anonymize redacts the configuration fields that do not have an export=true struct tag. +// It returns the resulting marshaled configuration. +func Anonymize(baseConfig interface{}) (string, error) { + return anonymize(baseConfig, false) +} + +func anonymize(baseConfig interface{}, indent bool) (string, error) { + conf, err := do(baseConfig, tagExport, true, indent) + if err != nil { + return "", err + } + return doOnJSON(conf), nil +} + +// RemoveCredentials redacts the configuration fields that have a loggable=false struct tag. +// It returns the resulting marshaled configuration. +func RemoveCredentials(baseConfig interface{}) (string, error) { + return removeCredentials(baseConfig, false) +} + +func removeCredentials(baseConfig interface{}, indent bool) (string, error) { + return do(baseConfig, tagLoggable, false, indent) +} + +// do marshals the given configuration, while redacting some of the fields +// respectively to the given tag. +func do(baseConfig interface{}, tag string, redactByDefault, indent bool) (string, error) { anomConfig, err := copystructure.Copy(baseConfig) if err != nil { return "", err @@ -26,7 +53,7 @@ func Do(baseConfig interface{}, indent bool) (string, error) { val := reflect.ValueOf(anomConfig) - err = doOnStruct(val) + err = doOnStruct(val, tag, redactByDefault) if err != nil { return "", err } @@ -36,7 +63,7 @@ func Do(baseConfig interface{}, indent bool) (string, error) { return "", err } - return doOnJSON(string(configJSON)), nil + return string(configJSON), nil } func doOnJSON(input string) string { @@ -44,7 +71,7 @@ func doOnJSON(input string) string { return xurls.Relaxed().ReplaceAllString(mailExp.ReplaceAllString(input, maskLarge+"\""), maskLarge) } -func doOnStruct(field reflect.Value) error { +func doOnStruct(field reflect.Value, tag string, redactByDefault bool) error { if field.Type().AssignableTo(reflect.TypeOf(dynamic.PluginConf{})) { resetPlugin(field) return nil @@ -53,7 +80,7 @@ func doOnStruct(field reflect.Value) error { switch field.Kind() { case reflect.Ptr: if !field.IsNil() { - if err := doOnStruct(field.Elem()); err != nil { + if err := doOnStruct(field.Elem(), tag, redactByDefault); err != nil { return err } } @@ -65,25 +92,28 @@ func doOnStruct(field reflect.Value) error { 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 { + if stField.Tag.Get(tag) == "false" || stField.Tag.Get(tag) != "true" && redactByDefault { + if err := reset(fld, stField.Name); err != nil { return err } - } else if err := reset(fld, stField.Name); err != nil { + continue + } + + // 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, tag, redactByDefault); err != nil { + return err + } + + fld.Set(fldPtr.Elem()) + + continue + } + + if err := doOnStruct(fld, tag, redactByDefault); err != nil { return err } } @@ -96,7 +126,7 @@ func doOnStruct(field reflect.Value) error { valPtr := reflect.New(val.Type()) valPtr.Elem().Set(val) - if err := doOnStruct(valPtr); err != nil { + if err := doOnStruct(valPtr, tag, redactByDefault); err != nil { return err } @@ -105,13 +135,13 @@ func doOnStruct(field reflect.Value) error { continue } - if err := doOnStruct(val); err != nil { + if err := doOnStruct(val, tag, redactByDefault); err != nil { return err } } case reflect.Slice: for j := 0; j < field.Len(); j++ { - if err := doOnStruct(field.Index(j)); err != nil { + if err := doOnStruct(field.Index(j), tag, redactByDefault); err != nil { return err } } diff --git a/pkg/anonymize/anonymize_config_test.go b/pkg/redactor/redactor_config_test.go similarity index 96% rename from pkg/anonymize/anonymize_config_test.go rename to pkg/redactor/redactor_config_test.go index c7c0c9bba..1241a4f5f 100644 --- a/pkg/anonymize/anonymize_config_test.go +++ b/pkg/redactor/redactor_config_test.go @@ -1,4 +1,4 @@ -package anonymize +package redactor import ( "flag" @@ -43,7 +43,9 @@ import ( var updateExpected = flag.Bool("update_expected", false, "Update expected files in fixtures") -func TestDo_dynamicConfiguration(t *testing.T) { +var fullDynConf *dynamic.Configuration + +func init() { config := &dynamic.Configuration{} config.HTTP = &dynamic.HTTPConfiguration{ Routers: map[string]*dynamic.Router{ @@ -242,7 +244,7 @@ func TestDo_dynamicConfiguration(t *testing.T) { SourceCriterion: &dynamic.SourceCriterion{ IPStrategy: &dynamic.IPStrategy{ Depth: 42, - ExcludedIPs: []string{"foo"}, + ExcludedIPs: []string{"127.0.0.1"}, }, RequestHeaderName: "foo", RequestHost: true, @@ -291,7 +293,7 @@ func TestDo_dynamicConfiguration(t *testing.T) { SourceCriterion: &dynamic.SourceCriterion{ IPStrategy: &dynamic.IPStrategy{ Depth: 42, - ExcludedIPs: []string{"foo"}, + ExcludedIPs: []string{"127.0.0.1"}, }, RequestHeaderName: "foo", RequestHost: true, @@ -463,10 +465,16 @@ func TestDo_dynamicConfiguration(t *testing.T) { }, } + fullDynConf = config +} + +func TestAnonymize_dynamicConfiguration(t *testing.T) { + config := fullDynConf + expectedConfiguration, err := os.ReadFile("./testdata/anonymized-dynamic-config.json") require.NoError(t, err) - cleanJSON, err := Do(config, true) + cleanJSON, err := anonymize(config, true) require.NoError(t, err) if *updateExpected { @@ -477,6 +485,23 @@ func TestDo_dynamicConfiguration(t *testing.T) { assert.Equal(t, expected, cleanJSON) } +func TestSecure_dynamicConfiguration(t *testing.T) { + config := fullDynConf + + expectedConfiguration, err := os.ReadFile("./testdata/secured-dynamic-config.json") + require.NoError(t, err) + + cleanJSON, err := removeCredentials(config, true) + require.NoError(t, err) + + if *updateExpected { + require.NoError(t, os.WriteFile("testdata/secured-dynamic-config.json", []byte(cleanJSON), 0o666)) + } + + expected := strings.TrimSuffix(string(expectedConfiguration), "\n") + assert.Equal(t, expected, cleanJSON) +} + func TestDo_staticConfiguration(t *testing.T) { config := &static.Configuration{} @@ -962,7 +987,7 @@ func TestDo_staticConfiguration(t *testing.T) { expectedConfiguration, err := os.ReadFile("./testdata/anonymized-static-config.json") require.NoError(t, err) - cleanJSON, err := Do(config, true) + cleanJSON, err := anonymize(config, true) require.NoError(t, err) if *updateExpected { diff --git a/pkg/anonymize/anonymize_doOnJSON_test.go b/pkg/redactor/redactor_doOnJSON_test.go similarity index 97% rename from pkg/anonymize/anonymize_doOnJSON_test.go rename to pkg/redactor/redactor_doOnJSON_test.go index 85248954b..e61e1975f 100644 --- a/pkg/anonymize/anonymize_doOnJSON_test.go +++ b/pkg/redactor/redactor_doOnJSON_test.go @@ -1,4 +1,4 @@ -package anonymize +package redactor import ( "os" @@ -57,7 +57,9 @@ func Test_doOnJSON_simple(t *testing.T) { } for _, test := range testCases { + test := test t.Run(test.name, func(t *testing.T) { + t.Parallel() output := doOnJSON(test.input) assert.Equal(t, test.expectedOutput, output) }) diff --git a/pkg/redactor/redactor_doOnStruct_test.go b/pkg/redactor/redactor_doOnStruct_test.go new file mode 100644 index 000000000..96fe5edc9 --- /dev/null +++ b/pkg/redactor/redactor_doOnStruct_test.go @@ -0,0 +1,382 @@ +package redactor + +import ( + "reflect" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +type Courgette struct { + Ji string + Ho string +} + +type Tomate struct { + Ji string + Ho string +} + +type Carotte struct { + Name string + EName string `export:"true"` + EFName string `export:"false"` + Value int + EValue int `export:"true"` + EFValue int `export:"false"` + List []string + EList []string `export:"true"` + EFList []string `export:"false"` + Courgette Courgette + ECourgette Courgette `export:"true"` + EFCourgette Courgette `export:"false"` + Pourgette *Courgette + EPourgette *Courgette `export:"true"` + EFPourgette *Courgette `export:"false"` + Aubergine map[string]string + EAubergine map[string]string `export:"true"` + EFAubergine map[string]string `export:"false"` + SAubergine map[string]Tomate + ESAubergine map[string]Tomate `export:"true"` + EFSAubergine map[string]Tomate `export:"false"` + PSAubergine map[string]*Tomate + EPAubergine map[string]*Tomate `export:"true"` + EFPAubergine map[string]*Tomate `export:"false"` +} + +func Test_doOnStruct(t *testing.T) { + testCase := []struct { + name string + base *Carotte + expected *Carotte + redactByDefault bool + }{ + { + name: "primitive", + base: &Carotte{ + Name: "koko", + EName: "kiki", + Value: 666, + EValue: 666, + List: []string{"test"}, + EList: []string{"test"}, + }, + expected: &Carotte{ + Name: "xxxx", + EName: "kiki", + EValue: 666, + List: []string{"xxxx"}, + EList: []string{"test"}, + }, + redactByDefault: true, + }, + { + name: "primitive2", + base: &Carotte{ + Name: "koko", + EFName: "keke", + Value: 666, + EFValue: 777, + List: []string{"test"}, + EFList: []string{"test"}, + }, + expected: &Carotte{ + Name: "koko", + EFName: "xxxx", + Value: 666, + List: []string{"test"}, + EFList: []string{"xxxx"}, + }, + redactByDefault: false, + }, + { + name: "struct", + base: &Carotte{ + Name: "koko", + Courgette: Courgette{ + Ji: "huu", + }, + }, + expected: &Carotte{ + Name: "xxxx", + }, + redactByDefault: true, + }, + { + name: "struct2", + base: &Carotte{ + Name: "koko", + EFName: "keke", + Courgette: Courgette{ + Ji: "huu", + }, + EFCourgette: Courgette{ + Ji: "huu", + }, + }, + expected: &Carotte{ + Name: "koko", + EFName: "xxxx", + Courgette: Courgette{ + Ji: "huu", + Ho: "", + }, + }, + redactByDefault: false, + }, + { + name: "pointer", + base: &Carotte{ + Name: "koko", + Pourgette: &Courgette{ + Ji: "hoo", + }, + }, + expected: &Carotte{ + Name: "xxxx", + Pourgette: nil, + }, + redactByDefault: true, + }, + { + name: "pointer2", + base: &Carotte{ + Name: "koko", + EFName: "keke", + Pourgette: &Courgette{ + Ji: "hoo", + }, + EFPourgette: &Courgette{ + Ji: "hoo", + }, + }, + expected: &Carotte{ + Name: "koko", + EFName: "xxxx", + Pourgette: &Courgette{ + Ji: "hoo", + }, + EFPourgette: nil, + }, + redactByDefault: false, + }, + { + name: "export struct", + base: &Carotte{ + Name: "koko", + ECourgette: Courgette{ + Ji: "huu", + }, + }, + expected: &Carotte{ + Name: "xxxx", + ECourgette: Courgette{ + Ji: "xxxx", + }, + }, + redactByDefault: true, + }, + { + name: "export struct 2", + base: &Carotte{ + Name: "koko", + EFName: "keke", + ECourgette: Courgette{ + Ji: "huu", + }, + EFCourgette: Courgette{ + Ji: "huu", + }, + }, + expected: &Carotte{ + Name: "koko", + EFName: "xxxx", + ECourgette: Courgette{ + Ji: "huu", + }, + }, + redactByDefault: false, + }, + { + name: "export pointer struct", + base: &Carotte{ + Name: "koko", + EPourgette: &Courgette{ + Ji: "huu", + }, + }, + expected: &Carotte{ + Name: "xxxx", + EPourgette: &Courgette{ + Ji: "xxxx", + }, + }, + redactByDefault: true, + }, + { + name: "export pointer struct 2", + base: &Carotte{ + Name: "koko", + EFName: "keke", + EPourgette: &Courgette{ + Ji: "huu", + }, + EFPourgette: &Courgette{ + Ji: "huu", + }, + }, + expected: &Carotte{ + Name: "koko", + EFName: "xxxx", + EPourgette: &Courgette{ + Ji: "huu", + }, + EFPourgette: nil, + }, + redactByDefault: false, + }, + { + name: "export map string/string", + base: &Carotte{ + Name: "koko", + EAubergine: map[string]string{ + "foo": "bar", + }, + }, + expected: &Carotte{ + Name: "xxxx", + EAubergine: map[string]string{ + "foo": "bar", + }, + }, + redactByDefault: true, + }, + { + name: "export map string/string 2", + base: &Carotte{ + Name: "koko", + EFName: "keke", + EAubergine: map[string]string{ + "foo": "bar", + }, + EFAubergine: map[string]string{ + "foo": "bar", + }, + }, + expected: &Carotte{ + Name: "koko", + EFName: "xxxx", + EAubergine: map[string]string{ + "foo": "bar", + }, + EFAubergine: map[string]string{}, + }, + redactByDefault: false, + }, + { + name: "export map string/pointer", + base: &Carotte{ + Name: "koko", + EPAubergine: map[string]*Tomate{ + "foo": { + Ji: "fdskljf", + }, + }, + }, + expected: &Carotte{ + Name: "xxxx", + EPAubergine: map[string]*Tomate{ + "foo": { + Ji: "xxxx", + }, + }, + }, + redactByDefault: true, + }, + { + name: "export map string/pointer 2", + base: &Carotte{ + Name: "koko", + EPAubergine: map[string]*Tomate{ + "foo": { + Ji: "fdskljf", + }, + }, + EFPAubergine: map[string]*Tomate{ + "foo": { + Ji: "fdskljf", + }, + }, + }, + expected: &Carotte{ + Name: "koko", + EPAubergine: map[string]*Tomate{ + "foo": { + Ji: "fdskljf", + }, + }, + EFPAubergine: map[string]*Tomate{}, + }, + redactByDefault: false, + }, + { + name: "export map string/struct", + base: &Carotte{ + Name: "koko", + ESAubergine: map[string]Tomate{ + "foo": { + Ji: "JiJiJi", + }, + }, + }, + expected: &Carotte{ + Name: "xxxx", + ESAubergine: map[string]Tomate{ + "foo": { + Ji: "xxxx", + }, + }, + }, + redactByDefault: true, + }, + { + name: "export map string/struct 2", + base: &Carotte{ + Name: "koko", + ESAubergine: map[string]Tomate{ + "foo": { + Ji: "JiJiJi", + }, + }, + EFSAubergine: map[string]Tomate{ + "foo": { + Ji: "JiJiJi", + }, + }, + }, + expected: &Carotte{ + Name: "koko", + ESAubergine: map[string]Tomate{ + "foo": { + Ji: "JiJiJi", + }, + }, + EFSAubergine: map[string]Tomate{}, + }, + redactByDefault: false, + }, + } + + for _, test := range testCase { + test := test + t.Run(test.name, func(t *testing.T) { + t.Parallel() + val := reflect.ValueOf(test.base).Elem() + err := doOnStruct(val, tagExport, test.redactByDefault) + require.NoError(t, err) + + assert.EqualValues(t, test.expected, test.base) + }) + } +} diff --git a/pkg/anonymize/testdata/anonymized-dynamic-config.json b/pkg/redactor/testdata/anonymized-dynamic-config.json similarity index 100% rename from pkg/anonymize/testdata/anonymized-dynamic-config.json rename to pkg/redactor/testdata/anonymized-dynamic-config.json diff --git a/pkg/anonymize/testdata/anonymized-static-config.json b/pkg/redactor/testdata/anonymized-static-config.json similarity index 98% rename from pkg/anonymize/testdata/anonymized-static-config.json rename to pkg/redactor/testdata/anonymized-static-config.json index f5582f6f2..a525c388a 100644 --- a/pkg/anonymize/testdata/anonymized-static-config.json +++ b/pkg/redactor/testdata/anonymized-static-config.json @@ -223,7 +223,7 @@ "secretAccessKey": "xxxx" }, "consul": { - "rootKey": "RootKey", + "rootKey": "xxxx", "username": "xxxx", "password": "xxxx", "tls": { @@ -235,7 +235,7 @@ } }, "etcd": { - "rootKey": "RootKey", + "rootKey": "xxxx", "username": "xxxx", "password": "xxxx", "tls": { @@ -247,7 +247,7 @@ } }, "zooKeeper": { - "rootKey": "RootKey", + "rootKey": "xxxx", "username": "xxxx", "password": "xxxx", "tls": { @@ -259,7 +259,7 @@ } }, "redis": { - "rootKey": "RootKey", + "rootKey": "xxxx", "username": "xxxx", "password": "xxxx", "tls": { diff --git a/pkg/anonymize/testdata/example.json b/pkg/redactor/testdata/example.json similarity index 100% rename from pkg/anonymize/testdata/example.json rename to pkg/redactor/testdata/example.json diff --git a/pkg/anonymize/testdata/expected.json b/pkg/redactor/testdata/expected.json similarity index 100% rename from pkg/anonymize/testdata/expected.json rename to pkg/redactor/testdata/expected.json diff --git a/pkg/redactor/testdata/secured-dynamic-config.json b/pkg/redactor/testdata/secured-dynamic-config.json new file mode 100644 index 000000000..df5dc3795 --- /dev/null +++ b/pkg/redactor/testdata/secured-dynamic-config.json @@ -0,0 +1,487 @@ +{ + "http": { + "routers": { + "foo": { + "entryPoints": [ + "foo" + ], + "middlewares": [ + "foo" + ], + "service": "foo", + "rule": "foo", + "priority": 42, + "tls": { + "options": "foo", + "certResolver": "foo", + "domains": [ + { + "main": "foo", + "sans": [ + "foo" + ] + } + ] + } + } + }, + "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": "http://127.0.0.1:8080" + } + ], + "healthCheck": { + "scheme": "foo", + "path": "foo", + "port": 42, + "interval": "foo", + "timeout": "foo", + "hostname": "foo", + "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": [ + "foo" + ], + "ipStrategy": { + "depth": 42, + "excludedIPs": [ + "127.0.0.1" + ] + } + }, + "headers": { + "customRequestHeaders": { + "foo": "bar" + }, + "customResponseHeaders": { + "foo": "bar" + }, + "accessControlAllowCredentials": true, + "accessControlAllowHeaders": [ + "foo" + ], + "accessControlAllowMethods": [ + "foo" + ], + "accessControlAllowOriginList": [ + "foo" + ], + "accessControlAllowOriginListRegex": [ + "foo" + ], + "accessControlExposeHeaders": [ + "foo" + ], + "accessControlMaxAge": 42, + "addVaryHeader": true, + "allowedHosts": [ + "foo" + ], + "hostsProxyHeaders": [ + "foo" + ], + "sslRedirect": true, + "sslTemporaryRedirect": true, + "sslHost": "foo", + "sslProxyHeaders": { + "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", + "permissionsPolicy": "foo", + "isDevelopment": true + }, + "errors": { + "status": [ + "foo" + ], + "service": "foo", + "query": "foo" + }, + "rateLimit": { + "average": 42, + "period": "42ns", + "burst": 42, + "sourceCriterion": { + "ipStrategy": { + "depth": 42, + "excludedIPs": [ + "127.0.0.1" + ] + }, + "requestHeaderName": "foo", + "requestHost": true + } + }, + "redirectRegex": { + "regex": "foo", + "replacement": "foo", + "permanent": true + }, + "redirectScheme": { + "scheme": "foo", + "port": "foo", + "permanent": true + }, + "basicAuth": { + "users": [ + "xxxx" + ], + "usersFile": "foo", + "realm": "foo", + "removeHeader": true, + "headerField": "foo" + }, + "digestAuth": { + "users": [ + "xxxx" + ], + "usersFile": "foo", + "removeHeader": true, + "realm": "foo", + "headerField": "foo" + }, + "forwardAuth": { + "address": "127.0.0.1", + "tls": { + "ca": "ca.pem", + "caOptional": true, + "cert": "cert.pem", + "key": "xxxx", + "insecureSkipVerify": true + }, + "trustForwardHeader": true, + "authResponseHeaders": [ + "foo" + ], + "authResponseHeadersRegex": "foo", + "authRequestHeaders": [ + "foo" + ] + }, + "inFlightReq": { + "amount": 42, + "sourceCriterion": { + "ipStrategy": { + "depth": 42, + "excludedIPs": [ + "127.0.0.1" + ] + }, + "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, + "organizationalUnit": 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": "42ns" + }, + "contentType": { + "autoDetect": true + }, + "plugin": { + "foo": { + "answer": {} + } + } + } + }, + "models": { + "foo": { + "middlewares": [ + "foo" + ], + "tls": { + "options": "foo", + "certResolver": "foo", + "domains": [ + { + "main": "foo", + "sans": [ + "foo" + ] + } + ] + } + } + }, + "serversTransports": { + "foo": { + "serverName": "foo", + "insecureSkipVerify": true, + "rootCAs": [ + "rootca.pem" + ], + "certificates": [ + { + "certFile": "cert.pem", + "keyFile": "xxxx" + } + ], + "maxIdleConnsPerHost": 42, + "forwardingTimeouts": { + "dialTimeout": "42ns", + "responseHeaderTimeout": "42ns", + "idleConnTimeout": "42ns", + "readIdleTimeout": "42ns", + "pingTimeout": "42ns" + } + } + } + }, + "tcp": { + "routers": { + "foo": { + "entryPoints": [ + "foo" + ], + "service": "foo", + "rule": "foo", + "tls": { + "passthrough": true, + "options": "foo", + "certResolver": "foo", + "domains": [ + { + "main": "foo", + "sans": [ + "foo" + ] + } + ] + } + } + }, + "services": { + "bar": { + "weighted": { + "services": [ + { + "name": "foo", + "weight": 42 + } + ] + } + }, + "foo": { + "loadBalancer": { + "terminationDelay": 42, + "proxyProtocol": { + "version": 42 + }, + "servers": [ + { + "address": "127.0.0.1:8080" + } + ] + } + } + } + }, + "udp": { + "routers": { + "foo": { + "entryPoints": [ + "foo" + ], + "service": "foo" + } + }, + "services": { + "bar": { + "weighted": { + "services": [ + { + "name": "foo", + "weight": 42 + } + ] + } + }, + "foo": { + "loadBalancer": { + "servers": [ + { + "address": "127.0.0.1:8080" + } + ] + } + } + } + }, + "tls": { + "certificates": [ + { + "certFile": "cert.pem", + "keyFile": "xxxx", + "stores": [ + "foo" + ] + } + ], + "options": { + "foo": { + "minVersion": "foo", + "maxVersion": "foo", + "cipherSuites": [ + "foo" + ], + "curvePreferences": [ + "foo" + ], + "clientAuth": { + "caFiles": [ + "ca.pem" + ], + "clientAuthType": "RequireAndVerifyClientCert" + }, + "sniStrict": true, + "preferServerCipherSuites": true + } + }, + "stores": { + "foo": { + "defaultCertificate": { + "certFile": "cert.pem", + "keyFile": "xxxx" + } + } + } + } +} \ No newline at end of file diff --git a/pkg/server/configurationwatcher.go b/pkg/server/configurationwatcher.go index c589f6457..f302d7571 100644 --- a/pkg/server/configurationwatcher.go +++ b/pkg/server/configurationwatcher.go @@ -84,12 +84,8 @@ func (c *ConfigurationWatcher) AddListener(listener func(dynamic.Configuration)) func (c *ConfigurationWatcher) startProvider() { logger := log.WithoutContext() - jsonConf, err := json.Marshal(c.provider) - if err != nil { - logger.Debugf("Unable to marshal provider configuration %T: %v", c.provider, err) - } + logger.Infof("Starting provider %T", c.provider) - logger.Infof("Starting provider %T %s", c.provider, jsonConf) currentProvider := c.provider safe.Go(func() { diff --git a/pkg/tls/certificate.go b/pkg/tls/certificate.go index 6862145a2..8d2c8b61d 100644 --- a/pkg/tls/certificate.go +++ b/pkg/tls/certificate.go @@ -50,7 +50,7 @@ var ( // Certs and Key could be either a file path, or the file content itself. type Certificate struct { CertFile FileOrContent `json:"certFile,omitempty" toml:"certFile,omitempty" yaml:"certFile,omitempty"` - KeyFile FileOrContent `json:"keyFile,omitempty" toml:"keyFile,omitempty" yaml:"keyFile,omitempty"` + KeyFile FileOrContent `json:"keyFile,omitempty" toml:"keyFile,omitempty" yaml:"keyFile,omitempty" loggable:"false"` } // Certificates defines traefik certificates type diff --git a/pkg/tracing/elastic/elastic.go b/pkg/tracing/elastic/elastic.go index 5fc018628..fa385376b 100644 --- a/pkg/tracing/elastic/elastic.go +++ b/pkg/tracing/elastic/elastic.go @@ -25,7 +25,7 @@ func init() { // Config provides configuration settings for a elastic.co tracer. type Config struct { ServerURL string `description:"Sets the URL of the Elastic APM server." json:"serverURL,omitempty" toml:"serverURL,omitempty" yaml:"serverURL,omitempty"` - SecretToken string `description:"Sets the token used to connect to Elastic APM Server." json:"secretToken,omitempty" toml:"secretToken,omitempty" yaml:"secretToken,omitempty"` + SecretToken string `description:"Sets the token used to connect to Elastic APM Server." json:"secretToken,omitempty" toml:"secretToken,omitempty" yaml:"secretToken,omitempty" loggable:"false"` ServiceEnvironment string `description:"Sets the name of the environment Traefik is deployed in, e.g. 'production' or 'staging'." json:"serviceEnvironment,omitempty" toml:"serviceEnvironment,omitempty" yaml:"serviceEnvironment,omitempty" export:"true"` } diff --git a/pkg/tracing/jaeger/jaeger.go b/pkg/tracing/jaeger/jaeger.go index 7e2858cc7..9f5b90026 100644 --- a/pkg/tracing/jaeger/jaeger.go +++ b/pkg/tracing/jaeger/jaeger.go @@ -43,8 +43,8 @@ func (c *Config) SetDefaults() { // Collector provides configuration settings for jaeger collector. type Collector struct { Endpoint string `description:"Instructs reporter to send spans to jaeger-collector at this URL." json:"endpoint,omitempty" toml:"endpoint,omitempty" yaml:"endpoint,omitempty"` - User string `description:"User for basic http authentication when sending spans to jaeger-collector." json:"user,omitempty" toml:"user,omitempty" yaml:"user,omitempty"` - Password string `description:"Password for basic http authentication when sending spans to jaeger-collector." json:"password,omitempty" toml:"password,omitempty" yaml:"password,omitempty"` + User string `description:"User for basic http authentication when sending spans to jaeger-collector." json:"user,omitempty" toml:"user,omitempty" yaml:"user,omitempty" loggable:"false"` + Password string `description:"Password for basic http authentication when sending spans to jaeger-collector." json:"password,omitempty" toml:"password,omitempty" yaml:"password,omitempty" loggable:"false"` } // SetDefaults sets the default values. diff --git a/pkg/types/metrics.go b/pkg/types/metrics.go index 003097cff..3b4c6463c 100644 --- a/pkg/types/metrics.go +++ b/pkg/types/metrics.go @@ -22,7 +22,7 @@ type Prometheus struct { AddEntryPointsLabels bool `description:"Enable metrics on entry points." json:"addEntryPointsLabels,omitempty" toml:"addEntryPointsLabels,omitempty" yaml:"addEntryPointsLabels,omitempty" export:"true"` AddRoutersLabels bool `description:"Enable metrics on routers." json:"addRoutersLabels,omitempty" toml:"addRoutersLabels,omitempty" yaml:"addRoutersLabels,omitempty" export:"true"` AddServicesLabels bool `description:"Enable metrics on services." json:"addServicesLabels,omitempty" toml:"addServicesLabels,omitempty" yaml:"addServicesLabels,omitempty" export:"true"` - EntryPoint string `description:"EntryPoint" export:"true" json:"entryPoint,omitempty" toml:"entryPoint,omitempty" yaml:"entryPoint,omitempty"` + EntryPoint string `description:"EntryPoint" json:"entryPoint,omitempty" toml:"entryPoint,omitempty" yaml:"entryPoint,omitempty" export:"true"` ManualRouting bool `description:"Manual routing" json:"manualRouting,omitempty" toml:"manualRouting,omitempty" yaml:"manualRouting,omitempty" export:"true"` } @@ -88,8 +88,8 @@ type InfluxDB struct { PushInterval types.Duration `description:"InfluxDB push interval." json:"pushInterval,omitempty" toml:"pushInterval,omitempty" yaml:"pushInterval,omitempty" export:"true"` Database string `description:"InfluxDB database used when protocol is http." json:"database,omitempty" toml:"database,omitempty" yaml:"database,omitempty" export:"true"` RetentionPolicy string `description:"InfluxDB retention policy used when protocol is http." json:"retentionPolicy,omitempty" toml:"retentionPolicy,omitempty" yaml:"retentionPolicy,omitempty" export:"true"` - Username string `description:"InfluxDB username (only with http)." json:"username,omitempty" toml:"username,omitempty" yaml:"username,omitempty"` - Password string `description:"InfluxDB password (only with http)." json:"password,omitempty" toml:"password,omitempty" yaml:"password,omitempty"` + Username string `description:"InfluxDB username (only with http)." json:"username,omitempty" toml:"username,omitempty" yaml:"username,omitempty" loggable:"false"` + Password string `description:"InfluxDB password (only with http)." json:"password,omitempty" toml:"password,omitempty" yaml:"password,omitempty" loggable:"false"` AddEntryPointsLabels bool `description:"Enable metrics on entry points." json:"addEntryPointsLabels,omitempty" toml:"addEntryPointsLabels,omitempty" yaml:"addEntryPointsLabels,omitempty" export:"true"` AddRoutersLabels bool `description:"Enable metrics on routers." json:"addRoutersLabels,omitempty" toml:"addRoutersLabels,omitempty" yaml:"addRoutersLabels,omitempty" export:"true"` AddServicesLabels bool `description:"Enable metrics on services." json:"addServicesLabels,omitempty" toml:"addServicesLabels,omitempty" yaml:"addServicesLabels,omitempty" export:"true"` diff --git a/pkg/types/tls.go b/pkg/types/tls.go index c701b6b7e..f064f4e4b 100644 --- a/pkg/types/tls.go +++ b/pkg/types/tls.go @@ -19,7 +19,7 @@ type ClientTLS struct { CA string `description:"TLS CA" json:"ca,omitempty" toml:"ca,omitempty" yaml:"ca,omitempty"` CAOptional bool `description:"TLS CA.Optional" json:"caOptional,omitempty" toml:"caOptional,omitempty" yaml:"caOptional,omitempty" export:"true"` Cert string `description:"TLS cert" json:"cert,omitempty" toml:"cert,omitempty" yaml:"cert,omitempty"` - Key string `description:"TLS key" json:"key,omitempty" toml:"key,omitempty" yaml:"key,omitempty"` + Key string `description:"TLS key" json:"key,omitempty" toml:"key,omitempty" yaml:"key,omitempty" loggable:"false"` InsecureSkipVerify bool `description:"TLS insecure skip verify" json:"insecureSkipVerify,omitempty" toml:"insecureSkipVerify,omitempty" yaml:"insecureSkipVerify,omitempty" export:"true"` }