From 3b4ac3456df302859781a26874036bf741dc3b08 Mon Sep 17 00:00:00 2001 From: FZambia Date: Thu, 26 Sep 2024 21:46:52 +0300 Subject: [PATCH] avoid showing full basic auth creds in proxy endpoint logs --- internal/tools/address.go | 30 --------------- internal/tools/logging.go | 59 ++++++++++++++++++++++++++++++ internal/tools/logging_test.go | 67 ++++++++++++++++++++++++++++++++++ main.go | 14 +++---- 4 files changed, 133 insertions(+), 37 deletions(-) delete mode 100644 internal/tools/address.go create mode 100644 internal/tools/logging.go create mode 100644 internal/tools/logging_test.go diff --git a/internal/tools/address.go b/internal/tools/address.go deleted file mode 100644 index b1c8fb83c3..0000000000 --- a/internal/tools/address.go +++ /dev/null @@ -1,30 +0,0 @@ -package tools - -import ( - "net/url" - "strings" -) - -// StripPassword from URL address. -func StripPassword(address string) string { - u, err := url.Parse(address) - if err != nil { - return address - } - pass, passSet := u.User.Password() - if passSet { - return strings.Replace(u.String(), pass+"@", "***@", 1) - } - return u.String() -} - -// GetLogAddresses returns a string with addresses (concatenated with comma) -// with password stripped from each address. -func GetLogAddresses(addresses []string) string { - cleanedAddresses := make([]string, 0, len(addresses)) - for _, a := range addresses { - cleanedAddress := StripPassword(a) - cleanedAddresses = append(cleanedAddresses, cleanedAddress) - } - return strings.Join(cleanedAddresses, ", ") -} diff --git a/internal/tools/logging.go b/internal/tools/logging.go new file mode 100644 index 0000000000..8ce514fad7 --- /dev/null +++ b/internal/tools/logging.go @@ -0,0 +1,59 @@ +package tools + +import ( + "net/url" + "strings" +) + +// StripPassword from URL address. +func StripPassword(address string) string { + u, err := url.Parse(address) + if err != nil { + return address + } + pass, passSet := u.User.Password() + if passSet { + return strings.Replace(u.String(), pass+"@", "***@", 1) + } + return u.String() +} + +// GetLogAddresses returns a string with addresses (concatenated with comma) +// with password stripped from each address. +func GetLogAddresses(addresses []string) string { + cleanedAddresses := make([]string, 0, len(addresses)) + for _, a := range addresses { + cleanedAddress := StripPassword(a) + cleanedAddresses = append(cleanedAddresses, cleanedAddress) + } + return strings.Join(cleanedAddresses, ", ") +} + +// RedactedLogURLs prepares URLs to be logged or shown in UI stripping auth info from them. +func RedactedLogURLs(urls ...string) []string { + var result []string + + for _, input := range urls { + // Split the input by commas to handle comma-separated URLs. + urlParts := strings.Split(input, ",") + var cleanedParts []string + + for _, urlString := range urlParts { + parsedURL, err := url.Parse(strings.TrimSpace(urlString)) + var cleanedURL string + if err != nil { + cleanedURL = "" + } else { + cleanedURL = parsedURL.Redacted() + } + cleanedParts = append(cleanedParts, cleanedURL) + } + + // Combine the cleaned URLs back into a comma-separated string. + if len(cleanedParts) > 0 { + result = append(result, strings.Join(cleanedParts, ",")) + } + } + + return result +} diff --git a/internal/tools/logging_test.go b/internal/tools/logging_test.go new file mode 100644 index 0000000000..d76e492719 --- /dev/null +++ b/internal/tools/logging_test.go @@ -0,0 +1,67 @@ +package tools + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +// TestGetLogURLs tests the RedactedLogURLs function using Redacted method. +func TestGetLogURLs(t *testing.T) { + t.Run("Single URL with auth info", func(t *testing.T) { + input := "https://user:password@domain.com/resource" + expected := []string{"https://user:xxxxx@domain.com/resource"} + actual := RedactedLogURLs(input) + require.Equal(t, expected, actual) + }) + + t.Run("Multiple URLs with mixed auth info", func(t *testing.T) { + input := "https://user:pass@domain.com/resource,https://another.com" + expected := []string{"https://user:xxxxx@domain.com/resource,https://another.com"} + actual := RedactedLogURLs(input) + require.Equal(t, expected, actual) + }) + + t.Run("Multiple URLs with mixed spaces", func(t *testing.T) { + input := "https://user:pass@domain.com/resource, https://another.com" + expected := []string{"https://user:xxxxx@domain.com/resource,https://another.com"} + actual := RedactedLogURLs(input) + require.Equal(t, expected, actual) + }) + + t.Run("Single URL without auth info", func(t *testing.T) { + input := "https://domain.com/resource" + expected := []string{"https://domain.com/resource"} + actual := RedactedLogURLs(input) + require.Equal(t, expected, actual) + }) + + t.Run("Invalid URL", func(t *testing.T) { + input := "://invalid-url" + expected := []string{""} + actual := RedactedLogURLs(input) + require.Equal(t, expected, actual) + }) + + t.Run("Mixed valid and invalid URLs", func(t *testing.T) { + input := "https://user:pass@domain.com/resource, ://invalid-url, https://valid.com" + expected := []string{"https://user:xxxxx@domain.com/resource,,https://valid.com"} + actual := RedactedLogURLs(input) + require.Equal(t, expected, actual) + }) + + t.Run("Multiple comma-separated URLs with auth", func(t *testing.T) { + input := "https://user:pass@domain.com, https://admin:admin@another.com, httpss://example.com/resource" + expected := []string{"https://user:xxxxx@domain.com,https://admin:xxxxx@another.com,httpss://example.com/resource"} + actual := RedactedLogURLs(input) + require.Equal(t, expected, actual) + }) + + t.Run("GRPC addresses work correctly", func(t *testing.T) { + // We use such format for GRPC proxy config. + input := []string{"grpc://user:pass@127.0.0.1:9000", "grpc://127.0.0.1:10000"} + expected := []string{"grpc://user:xxxxx@127.0.0.1:9000", "grpc://127.0.0.1:10000"} + actual := RedactedLogURLs(input...) + require.Equal(t, expected, actual) + }) +} diff --git a/main.go b/main.go index 2777247681..0decf5fd05 100644 --- a/main.go +++ b/main.go @@ -2090,7 +2090,7 @@ func proxyMapConfig() (*client.ProxyMap, bool) { if err != nil { log.Fatal().Msgf("error creating connect proxy: %v", err) } - log.Info().Str("endpoint", connectEndpoint).Msg("connect proxy enabled") + log.Info().Str("endpoint", tools.RedactedLogURLs(connectEndpoint)[0]).Msg("connect proxy enabled") } if refreshEndpoint != "" { @@ -2101,7 +2101,7 @@ func proxyMapConfig() (*client.ProxyMap, bool) { if err != nil { log.Fatal().Msgf("error creating refresh proxy: %v", err) } - log.Info().Str("endpoint", refreshEndpoint).Msg("refresh proxy enabled") + log.Info().Str("endpoint", tools.RedactedLogURLs(refreshEndpoint)[0]).Msg("refresh proxy enabled") } if subscribeEndpoint != "" { @@ -2112,7 +2112,7 @@ func proxyMapConfig() (*client.ProxyMap, bool) { log.Fatal().Msgf("error creating subscribe proxy: %v", err) } proxyMap.SubscribeProxies[""] = sp - log.Info().Str("endpoint", subscribeEndpoint).Msg("subscribe proxy enabled") + log.Info().Str("endpoint", tools.RedactedLogURLs(subscribeEndpoint)[0]).Msg("subscribe proxy enabled") } if publishEndpoint != "" { @@ -2123,7 +2123,7 @@ func proxyMapConfig() (*client.ProxyMap, bool) { log.Fatal().Msgf("error creating publish proxy: %v", err) } proxyMap.PublishProxies[""] = pp - log.Info().Str("endpoint", publishEndpoint).Msg("publish proxy enabled") + log.Info().Str("endpoint", tools.RedactedLogURLs(publishEndpoint)[0]).Msg("publish proxy enabled") } if rpcEndpoint != "" { @@ -2134,7 +2134,7 @@ func proxyMapConfig() (*client.ProxyMap, bool) { log.Fatal().Msgf("error creating rpc proxy: %v", err) } proxyMap.RpcProxies[""] = rp - log.Info().Str("endpoint", rpcEndpoint).Msg("RPC proxy enabled") + log.Info().Str("endpoint", tools.RedactedLogURLs(rpcEndpoint)[0]).Msg("RPC proxy enabled") } if subRefreshEndpoint != "" { @@ -2145,7 +2145,7 @@ func proxyMapConfig() (*client.ProxyMap, bool) { log.Fatal().Msgf("error creating sub refresh proxy: %v", err) } proxyMap.SubRefreshProxies[""] = srp - log.Info().Str("endpoint", subRefreshEndpoint).Msg("sub refresh proxy enabled") + log.Info().Str("endpoint", tools.RedactedLogURLs(subRefreshEndpoint)[0]).Msg("sub refresh proxy enabled") } if proxyStreamSubscribeEndpoint != "" { @@ -2156,7 +2156,7 @@ func proxyMapConfig() (*client.ProxyMap, bool) { log.Fatal().Msgf("error creating subscribe stream proxy: %v", err) } proxyMap.SubscribeStreamProxies[""] = streamProxy - log.Info().Str("endpoint", proxyStreamSubscribeEndpoint).Msg("subscribe stream proxy enabled") + log.Info().Str("endpoint", tools.RedactedLogURLs(proxyStreamSubscribeEndpoint)[0]).Msg("subscribe stream proxy enabled") } keepHeadersInContext := connectEndpoint != "" || refreshEndpoint != "" ||