From 1fe86f6482f3966bd0dfdf5a92c4ea8efd674799 Mon Sep 17 00:00:00 2001 From: Petr Vobornik Date: Thu, 3 Oct 2024 18:11:31 +0200 Subject: [PATCH] fix(HMS-4769): log configuration on startup Log also secrets but obfuscate them so that it is visible if they are set or not but not their value. Signed-off-by: Petr Vobornik --- cmd/service/main.go | 2 + internal/config/config.go | 69 ++++++++++++++++++++++++++++++++++ internal/config/config_test.go | 60 +++++++++++++++++++++++++++++ 3 files changed, 131 insertions(+) diff --git a/cmd/service/main.go b/cmd/service/main.go index 83b659d0..2ebbdc66 100644 --- a/cmd/service/main.go +++ b/cmd/service/main.go @@ -2,6 +2,7 @@ package main import ( "context" + "log/slog" "os" "os/signal" "sync" @@ -47,6 +48,7 @@ func main() { logger.LogBuildInfo("idmscv-backend") cfg := config.Get() logger.InitLogger(cfg) + cfg.Log(slog.Default()) db := datastore.NewDB(cfg) defer datastore.Close(db) diff --git a/internal/config/config.go b/internal/config/config.go index 13e932da..510adc36 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -509,6 +509,75 @@ func Load(cfg *Config) *viper.Viper { return v } +// Log logs the configuration +func (c *Config) Log(logger *slog.Logger) { + logger.Info( + "Configuration", + slog.Group("Web", + slog.Int("Port", int(c.Web.Port)), + ), + slog.Group("Database", + slog.String("Host", c.Database.Host), + slog.Int("Port", c.Database.Port), + slog.String("User", c.Database.User), + slog.String("Password", obfuscateSecret(c.Database.Password)), + slog.String("Name", c.Database.Name), + slog.String("CACertPath", c.Database.CACertPath), + slog.Int("MaxOpenConns", c.Database.MaxOpenConns), + ), + slog.Group("Logging", + slog.String("Level", c.Logging.Level), + slog.Bool("Console", c.Logging.Console), + slog.Bool("Location", c.Logging.Location), + slog.String("Type", c.Logging.Type), + slog.Group("Cloudwatch", + slog.String("Region", c.Logging.Cloudwatch.Region), + slog.String("Key", c.Logging.Cloudwatch.Key), + slog.String("Secret", obfuscateSecret(c.Logging.Cloudwatch.Secret)), + slog.String("Session", c.Logging.Cloudwatch.Session), + slog.String("Group", c.Logging.Cloudwatch.Group), + slog.String("Stream", c.Logging.Cloudwatch.Stream), + ), + ), + slog.Group("Metrics", + slog.String("Path", c.Metrics.Path), + slog.Int("Port", c.Metrics.Port), + ), + slog.Group("Clients", + slog.String("RbacBaseURL", c.Clients.RbacBaseURL), + slog.String("PendoBaseURL", c.Clients.PendoBaseURL), + slog.String("PendoAPIKey", obfuscateSecret(c.Clients.PendoAPIKey)), + slog.String("PendoTrackEventKey", obfuscateSecret(c.Clients.PendoTrackEventKey)), + slog.Int("PendoRequestTimeoutSecs", c.Clients.PendoRequestTimeoutSecs), + ), + slog.Group("Application", + slog.String("Name", c.Application.Name), + slog.String("PathPrefix", c.Application.PathPrefix), + slog.Int("TokenExpirationTimeSeconds", c.Application.TokenExpirationTimeSeconds), + slog.Duration("HostconfJwkValidity", c.Application.HostconfJwkValidity), + slog.Duration("HostconfJwkRenewalThreshold", c.Application.HostconfJwkRenewalThreshold), + slog.Int("PaginationDefaultLimit", c.Application.PaginationDefaultLimit), + slog.Int("PaginationMaxLimit", c.Application.PaginationMaxLimit), + slog.Bool("AcceptXRHFakeIdentity", c.Application.AcceptXRHFakeIdentity), + slog.Bool("ValidateAPI", c.Application.ValidateAPI), + slog.String("MainSecret", obfuscateSecret(c.Application.MainSecret)), + slog.Bool("EnableRBAC", c.Application.EnableRBAC), + slog.Duration("IdleTimeout", c.Application.IdleTimeout), + slog.Duration("ReadTimeout", c.Application.ReadTimeout), + slog.Duration("WriteTimeout", c.Application.WriteTimeout), + slog.Int("SizeLimitRequestHeader", c.Application.SizeLimitRequestHeader), + slog.Int("SizeLimitRequestBody", c.Application.SizeLimitRequestBody), + ), + ) +} + +func obfuscateSecret(value string) string { + if value == "" { + return "" + } + return "***" +} + func reportError(err error) { for _, err := range err.(validator.ValidationErrors) { slog.Error( diff --git a/internal/config/config_test.go b/internal/config/config_test.go index d1fcdb80..b08e835b 100644 --- a/internal/config/config_test.go +++ b/internal/config/config_test.go @@ -1,6 +1,8 @@ package config import ( + "bytes" + "log/slog" "os" "strconv" "testing" @@ -153,6 +155,64 @@ func TestLoad(t *testing.T) { }) } +func TestLog(t *testing.T) { + // Given + buf := &bytes.Buffer{} + logger := slog.New(slog.NewTextHandler(buf, nil)) + + cfg := Config{ + Web: Web{ + Port: 8080, + }, + Database: Database{ + User: "testdbuser", + Password: "testdbpassword", + }, + Logging: Logging{ + Level: "debug", + Cloudwatch: Cloudwatch{ + Secret: "testcloudwatchsecret", + }, + }, + Metrics: Metrics{ + Path: "/metrics", + }, + Clients: Clients{ + RbacBaseURL: "http://localhost:8080/api/rbac/v1", + PendoAPIKey: "testpendoapikey", + PendoTrackEventKey: "testpendotrackeventkey", + }, + Application: Application{ + Name: "appname", + MainSecret: "testmainsecret", + }, + } + + // When + cfg.Log(logger) + loggedStr := buf.String() + + // Then + assert.Contains(t, loggedStr, "Web.Port=8080") + assert.Contains(t, loggedStr, "Database.User=testdbuser") + assert.Contains(t, loggedStr, "Database.Password=***") + assert.Contains(t, loggedStr, "Logging.Level=debug") + assert.Contains(t, loggedStr, "Logging.Cloudwatch.Secret=***") + assert.Contains(t, loggedStr, "Metrics.Path=/metrics") + assert.Contains(t, loggedStr, "Clients.RbacBaseURL=http://localhost:8080/api/rbac/v1") + assert.Contains(t, loggedStr, "Clients.PendoAPIKey=***") + assert.Contains(t, loggedStr, "Clients.PendoTrackEventKey=***") + assert.Contains(t, loggedStr, "Application.Name=appname") + assert.Contains(t, loggedStr, "Application.MainSecret=***") + + // No password in the log + assert.NotContains(t, loggedStr, "testdbpassword") + assert.NotContains(t, loggedStr, "testcloudwatchsecret") + assert.NotContains(t, loggedStr, "testpendoapikey") + assert.NotContains(t, loggedStr, "testpendotrackeventkey") + assert.NotContains(t, loggedStr, "testmainsecret") +} + func TestValidateConfig(t *testing.T) { cfg := Config{ Application: Application{