package config import "encoding/json" const redactedSecret = "[REDACTED]" // Redacted returns a detached configuration view suitable for diagnostics. func (c *Config) Redacted() Config { if c == nil { return Config{} } redacted := cloneConfig(*c) redactAuth(&redacted.Gateway.Auth) redactAuth(&redacted.Distribution.Auth) redactAuth(&redacted.Admin.Auth) for name, upstream := range redacted.Upstreams { redactProviderAuth(&upstream.API.Auth) upstream.ProxyAuth.Password = redact(upstream.ProxyAuth.Password) redacted.Upstreams[name] = upstream } redacted.Storage.PostgresURL = redact(redacted.Storage.PostgresURL) redacted.Storage.RedisURL = redact(redacted.Storage.RedisURL) return redacted } func (c *Config) String() string { if c == nil { return "" } encoded, err := json.Marshal(c.Redacted()) if err != nil { return `{"config":"[REDACTED]"}` } return string(encoded) } func (c *Config) GoString() string { return c.String() } func redactAuth(auth *Auth) { auth.Password = redact(auth.Password) auth.Token = redact(auth.Token) for index := range auth.Methods { auth.Methods[index].Password = redact(auth.Methods[index].Password) auth.Methods[index].Value = redact(auth.Methods[index].Value) } } func redactProviderAuth(auth *ProviderAuth) { auth.Password = redact(auth.Password) auth.Token = redact(auth.Token) auth.Value = redact(auth.Value) } func redact(value string) string { if value == "" { return "" } return redactedSecret } func cloneConfig(source Config) Config { cloned := source cloned.Defaults.Check.URLs = cloneStrings(source.Defaults.Check.URLs) cloned.Gateway = cloneListener(source.Gateway) cloned.Distribution.Listener = cloneListener(source.Distribution.Listener) cloned.Admin = cloneListener(source.Admin) cloned.Routing = make([]Routing, len(source.Routing)) for index, route := range source.Routing { cloned.Routing[index] = cloneRouting(route) } cloned.Upstreams = make(map[string]Upstream, len(source.Upstreams)) for name, upstream := range source.Upstreams { cloned.Upstreams[name] = cloneUpstream(upstream) } return cloned } func cloneListener(source Listener) Listener { cloned := source cloned.Access.AllowCIDRs = cloneStrings(source.Access.AllowCIDRs) cloned.Access.TrustedProxies = cloneStrings(source.Access.TrustedProxies) cloned.Auth.CIDRs = cloneStrings(source.Auth.CIDRs) cloned.Auth.Methods = append([]AuthMethod(nil), source.Auth.Methods...) for index := range cloned.Auth.Methods { cloned.Auth.Methods[index].CIDRs = cloneStrings(source.Auth.Methods[index].CIDRs) } cloned.Retry.RetryMethods = cloneStrings(source.Retry.RetryMethods) cloned.DestinationPolicy.DenyCIDRs = cloneStrings(source.DestinationPolicy.DenyCIDRs) return cloned } func cloneRouting(source Routing) Routing { cloned := source cloned.Match.Methods = cloneStrings(source.Match.Methods) cloned.Match.Headers = cloneStringMap(source.Match.Headers) cloned.Upstreams = cloneStrings(source.Upstreams) cloned.Strategy.Weights = cloneIntMap(source.Strategy.Weights) return cloned } func cloneUpstream(source Upstream) Upstream { cloned := source cloned.Exposure = cloneStrings(source.Exposure) cloned.Provider.Protocols = cloneStrings(source.Provider.Protocols) cloned.API.Headers = cloneStringMap(source.API.Headers) cloned.API.Query = cloneStringMap(source.API.Query) cloned.API.Body.Value = cloneStringMap(source.API.Body.Value) cloned.Check.URLs = cloneStrings(source.Check.URLs) return cloned } func cloneStrings(source []string) []string { return append([]string(nil), source...) } func cloneStringMap(source map[string]string) map[string]string { if source == nil { return nil } cloned := make(map[string]string, len(source)) for key, value := range source { cloned[key] = value } return cloned } func cloneIntMap(source map[string]int) map[string]int { if source == nil { return nil } cloned := make(map[string]int, len(source)) for key, value := range source { cloned[key] = value } return cloned }