From 66710c6020c120ebdc73289107e19ac2b827124f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Mierzwa?= Date: Tue, 28 Mar 2017 11:00:16 -0700 Subject: [PATCH] Sanitize URLs to hide all passwords when logging runtime config When unsee starts it will print runtime config, but this can contain password for URL keys, replace passwords with 'xxx' string --- config/config.go | 27 +++++++++++++++++++++++++-- config/config_test.go | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+), 2 deletions(-) diff --git a/config/config.go b/config/config.go index 1abff06d4..4d5ecdda6 100644 --- a/config/config.go +++ b/config/config.go @@ -4,6 +4,7 @@ import ( "bytes" "flag" "fmt" + "net/url" "os" "reflect" "strings" @@ -11,6 +12,7 @@ import ( "unicode" log "github.com/Sirupsen/logrus" + "github.com/asaskevich/govalidator" "github.com/kelseyhightower/envconfig" ) @@ -121,12 +123,33 @@ func (config *configEnvs) Read() { } } +func hideURLPassword(s string) (string, error) { + u, err := url.Parse(s) + if err != nil { + return "", err + } + if u.User != nil { + if _, pwdSet := u.User.Password(); pwdSet { + u.User = url.UserPassword(u.User.Username(), "xxx") + } + } + return u.String(), nil +} + func (config *configEnvs) LogValues() { s := reflect.ValueOf(config).Elem() typeOfT := s.Type() for i := 0; i < s.NumField(); i++ { - f := s.Field(i) - log.Infof("%20s => %v", typeOfT.Field(i).Tag.Get("envconfig"), f.Interface()) + env := typeOfT.Field(i).Tag.Get("envconfig") + val := fmt.Sprintf("%v", s.Field(i).Interface()) + if govalidator.IsURL(val) { + var err error + val, err = hideURLPassword(val) + if err != nil { + log.Errorf("Failed to parse url value for %s: %s", env, err.Error()) + } + } + log.Infof("%20s => %v", env, val) } } diff --git a/config/config_test.go b/config/config_test.go index 84c25bb82..bf11fb480 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -61,3 +61,39 @@ func TestReadConfig(t *testing.T) { } } + +type urlSecretTest struct { + raw string + sanitized string +} + +var urlSecretTests = []urlSecretTest{ + urlSecretTest{ + raw: "http://localhost", + sanitized: "http://localhost", + }, + urlSecretTest{ + raw: "http://alertmanager.example.com/path", + sanitized: "http://alertmanager.example.com/path", + }, + urlSecretTest{ + raw: "http://user@alertmanager.example.com/path", + sanitized: "http://user@alertmanager.example.com/path", + }, + urlSecretTest{ + raw: "https://user:password@alertmanager.example.com/path", + sanitized: "https://user:xxx@alertmanager.example.com/path", + }, +} + +func TestUrlSecretTest(t *testing.T) { + for _, testCase := range urlSecretTests { + sanitized, err := hideURLPassword(testCase.raw) + if err != nil { + t.Errorf("Unexpected error when parsing '%s': %s", testCase.raw, err.Error()) + } + if sanitized != testCase.sanitized { + t.Errorf("Invalid sanitized url, expected '%s', got '%s'", testCase.sanitized, sanitized) + } + } +}