diff --git a/cmd/kes/migrate.go b/cmd/kes/migrate.go index 646e5f24..95d6d51b 100644 --- a/cmd/kes/migrate.go +++ b/cmd/kes/migrate.go @@ -109,7 +109,7 @@ func migrate(args []string) { srcConf, err := kesconf.ReadFile(fromPath) cli.Assert(err == nil, err) - src, err := srcConf.KeyStore.Connect(ctx) + src, err := srcConf.KeyStore.Connect(ctx, false) cli.Assert(err == nil, err) iter := &kes.ListIter[string]{ @@ -121,7 +121,7 @@ func migrate(args []string) { dstConf, err := kesconf.ReadFile(toPath) cli.Assert(err == nil, err) - dst, err := dstConf.KeyStore.Connect(ctx) + dst, err := dstConf.KeyStore.Connect(ctx, false) cli.Assert(err == nil, err) var ( diff --git a/cmd/kes/server.go b/cmd/kes/server.go index 1e640766..6a4da861 100644 --- a/cmd/kes/server.go +++ b/cmd/kes/server.go @@ -79,6 +79,7 @@ func serverCmd(args []string) { tlsCertFlag string mtlsAuthFlag string devFlag bool + verboseFlag bool ) cmd.StringVar(&addrFlag, "addr", "", "The address of the server") cmd.StringVar(&configFlag, "config", "", "Path to the server configuration file") @@ -86,6 +87,7 @@ func serverCmd(args []string) { cmd.StringVar(&tlsCertFlag, "cert", "", "Path to the TLS certificate") cmd.StringVar(&mtlsAuthFlag, "auth", "", "Controls how the server handles mTLS authentication") cmd.BoolVar(&devFlag, "dev", false, "Start the KES server in development mode") + cmd.BoolVar(&verboseFlag, "verbose", false, "Log verbose output (Vault only)") if err := cmd.Parse(args[1:]); err != nil { if errors.Is(err, flag.ErrHelp) { os.Exit(2) @@ -122,12 +124,12 @@ func serverCmd(args []string) { return } - if err := startServer(addrFlag, configFlag); err != nil { + if err := startServer(addrFlag, configFlag, verboseFlag); err != nil { cli.Fatal(err) } } -func startServer(addrFlag, configFlag string) error { +func startServer(addrFlag, configFlag string, verbose bool) error { var memLocked bool if runtime.GOOS == "linux" { memLocked = mlockall() == nil @@ -174,7 +176,7 @@ func startServer(addrFlag, configFlag string) error { ctx, cancel := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM) defer cancel() - conf, err := rawConfig.Config(ctx) + conf, err := rawConfig.Config(ctx, verbose) if err != nil { return err } @@ -238,7 +240,7 @@ func startServer(addrFlag, configFlag string) error { fmt.Fprintf(os.Stderr, "Failed to reload server config: %v\n", err) continue } - config, err := file.Config(ctx) + config, err := file.Config(ctx, verbose) if err != nil { fmt.Fprintf(os.Stderr, "Failed to reload server config: %v\n", err) continue diff --git a/internal/http/log.go b/internal/http/log.go index 3adf3b5a..7eac16a6 100644 --- a/internal/http/log.go +++ b/internal/http/log.go @@ -3,12 +3,22 @@ package http import ( "log/slog" "net/http" + "slices" "time" ) // LoggingTransport is an http.RoundTripper that logs the request and response. type LoggingTransport struct { http.RoundTripper + skipPaths []string +} + +// NewLoggingTransport creates an http.RoundTripper that logs the request and response. +func NewLoggingTransport(rt http.RoundTripper, skipPaths ...string) *LoggingTransport { + return &LoggingTransport{ + RoundTripper: rt, + skipPaths: skipPaths, + } } // RoundTrip implements the RoundTripper interface. @@ -22,7 +32,7 @@ func (lt *LoggingTransport) RoundTrip(req *http.Request) (*http.Response, error) resp, err := rt.RoundTrip(req) // don't log health checks - if req.URL.Path != "/v1/sys/health" { + if !slices.Contains(lt.skipPaths, req.URL.Path) { switch { case err != nil: slog.Info("HTTP error", diff --git a/internal/keystore/vault/config.go b/internal/keystore/vault/config.go index 7b16fa93..c9c34904 100644 --- a/internal/keystore/vault/config.go +++ b/internal/keystore/vault/config.go @@ -220,9 +220,6 @@ type Config struct { // host's root CA set is used. CAPath string - // Flag to enable logging of all Vault HTTP requests - Verbose bool - lock sync.RWMutex } @@ -249,6 +246,5 @@ func (c *Config) Clone() *Config { PrivateKey: c.PrivateKey, Certificate: c.Certificate, CAPath: c.CAPath, - Verbose: c.Verbose, } } diff --git a/internal/keystore/vault/vault.go b/internal/keystore/vault/vault.go index 8ee80ae8..e367f4fd 100644 --- a/internal/keystore/vault/vault.go +++ b/internal/keystore/vault/vault.go @@ -27,7 +27,7 @@ import ( "aead.dev/mem" vaultapi "github.com/hashicorp/vault/api" "github.com/minio/kes" - internalhttp "github.com/minio/kes/internal/http" + xhttp "github.com/minio/kes/internal/http" "github.com/minio/kes/internal/keystore" kesdk "github.com/minio/kms-go/kes" ) @@ -41,7 +41,7 @@ type Store struct { // Connect connects to a Hashicorp Vault server with // the given configuration. -func Connect(ctx context.Context, c *Config) (*Store, error) { +func Connect(ctx context.Context, c *Config, verbose bool) (*Store, error) { c = c.Clone() if c.Engine == "" { @@ -114,8 +114,8 @@ func Connect(ctx context.Context, c *Config) (*Store, error) { tr.DisableKeepAlives = true tr.MaxIdleConnsPerHost = -1 } - if c.Verbose { - config.HttpClient.Transport = &internalhttp.LoggingTransport{RoundTripper: config.HttpClient.Transport} + if verbose { + config.HttpClient.Transport = xhttp.NewLoggingTransport(config.HttpClient.Transport, "/v1/sys/health") } vaultClient, err := vaultapi.NewClient(config) if err != nil { @@ -150,7 +150,7 @@ func Connect(ctx context.Context, c *Config) (*Store, error) { lastAuthSuccess = false } } else { - if c.Verbose { + if verbose { obfuscatedToken := secret.Auth.ClientToken if len(obfuscatedToken) > 10 { obfuscatedToken = obfuscatedToken[:2] + "***" + obfuscatedToken[len(obfuscatedToken)-4:] diff --git a/kesconf/aws_test.go b/kesconf/aws_test.go index 07c20ba7..3ca722f0 100644 --- a/kesconf/aws_test.go +++ b/kesconf/aws_test.go @@ -29,7 +29,7 @@ func TestAWS(t *testing.T) { ctx, cancel := testingContext(t) defer cancel() - store, err := config.KeyStore.Connect(ctx) + store, err := config.KeyStore.Connect(ctx, false) if err != nil { t.Fatal(err) } diff --git a/kesconf/azure_test.go b/kesconf/azure_test.go index 06d7e8cf..55cd021a 100644 --- a/kesconf/azure_test.go +++ b/kesconf/azure_test.go @@ -35,7 +35,7 @@ func TestAzure(t *testing.T) { ctx, cancel := testingContext(t) defer cancel() - store, err := config.KeyStore.Connect(ctx) + store, err := config.KeyStore.Connect(ctx, false) if err != nil { t.Fatal(err) } diff --git a/kesconf/config.go b/kesconf/config.go index eb4603d8..67860da2 100644 --- a/kesconf/config.go +++ b/kesconf/config.go @@ -121,8 +121,6 @@ type ymlFile struct { Status struct { Ping env[time.Duration] `yaml:"ping"` } `yaml:"status"` - - Verbose env[bool] `yaml:"verbose"` } `yaml:"vault"` Fortanix *struct { @@ -478,7 +476,6 @@ func ymlToKeyStore(y *ymlFile) (KeyStore, error) { Certificate: y.KeyStore.Vault.TLS.Certificate.Value, CAPath: y.KeyStore.Vault.TLS.CAPath.Value, StatusPing: y.KeyStore.Vault.Status.Ping.Value, - Verbose: y.KeyStore.Vault.Verbose.Value, } if y.KeyStore.Vault.AppRole != nil { s.AppRole = &VaultAppRoleAuth{ diff --git a/kesconf/file.go b/kesconf/file.go index 89a429f1..55e8423d 100644 --- a/kesconf/file.go +++ b/kesconf/file.go @@ -159,7 +159,7 @@ func (f *File) TLSConfig() (*tls.Config, error) { // Config returns a new KES configuration as specified by // the File. It connects to the KeyStore using the given // context. -func (f *File) Config(ctx context.Context) (*kes.Config, error) { +func (f *File) Config(ctx context.Context, verbose bool) (*kes.Config, error) { conf := &kes.Config{ Admin: f.Admin, } @@ -211,7 +211,7 @@ func (f *File) Config(ctx context.Context) (*kes.Config, error) { } if f.KeyStore != nil { - keystore, err := f.KeyStore.Connect(ctx) + keystore, err := f.KeyStore.Connect(ctx, verbose) if err != nil { return nil, err } @@ -365,7 +365,7 @@ type Key struct { type KeyStore interface { // Connect establishes and returns a new connection // to the keystore. - Connect(ctx context.Context) (kes.KeyStore, error) + Connect(ctx context.Context, verbose bool) (kes.KeyStore, error) } // FSKeyStore is a structure containing the configuration @@ -382,7 +382,7 @@ type FSKeyStore struct { } // Connect returns a kv.Store that stores key-value pairs in a path on the filesystem. -func (s *FSKeyStore) Connect(context.Context) (kes.KeyStore, error) { +func (s *FSKeyStore) Connect(context.Context, bool) (kes.KeyStore, error) { return fs.NewStore(s.Path) } @@ -455,9 +455,6 @@ type VaultKeyStore struct { // is checked. // If not set, defaults to 10s. StatusPing time.Duration - - // Verbose enables logging of all HTTP requests to Vault - Verbose bool } // VaultAppRoleAuth is a structure containing the configuration @@ -531,7 +528,7 @@ type VaultTransit struct { } // Connect returns a kv.Store that stores key-value pairs on a Hashicorp Vault server. -func (s *VaultKeyStore) Connect(ctx context.Context) (kes.KeyStore, error) { +func (s *VaultKeyStore) Connect(ctx context.Context, verbose bool) (kes.KeyStore, error) { if s.AppRole == nil && s.Kubernetes == nil { return nil, errors.New("edge: failed to connect to hashicorp vault: no authentication method specified") } @@ -548,7 +545,6 @@ func (s *VaultKeyStore) Connect(ctx context.Context) (kes.KeyStore, error) { Certificate: s.Certificate, CAPath: s.CAPath, StatusPingAfter: s.StatusPing, - Verbose: s.Verbose, } if s.AppRole != nil { c.AppRole = &vault.AppRole{ @@ -572,7 +568,7 @@ func (s *VaultKeyStore) Connect(ctx context.Context) (kes.KeyStore, error) { KeyName: s.Transit.KeyName, } } - return vault.Connect(ctx, c) + return vault.Connect(ctx, c, verbose) } // FortanixKeyStore is a structure containing the @@ -598,7 +594,7 @@ type FortanixKeyStore struct { } // Connect returns a kv.Store that stores key-value pairs on a Fortanix SDKMS server. -func (s *FortanixKeyStore) Connect(ctx context.Context) (kes.KeyStore, error) { +func (s *FortanixKeyStore) Connect(ctx context.Context, verbose bool) (kes.KeyStore, error) { return fortanix.Connect(ctx, &fortanix.Config{ Endpoint: s.Endpoint, GroupID: s.GroupID, @@ -633,7 +629,7 @@ type KeySecureKeyStore struct { } // Connect returns a kv.Store that stores key-value pairs on a Gemalto KeySecure instance. -func (s *KeySecureKeyStore) Connect(ctx context.Context) (kes.KeyStore, error) { +func (s *KeySecureKeyStore) Connect(ctx context.Context, verbose bool) (kes.KeyStore, error) { return gemalto.Connect(ctx, &gemalto.Config{ Endpoint: s.Endpoint, CAPath: s.CAPath, @@ -682,7 +678,7 @@ type GCPSecretManagerKeyStore struct { } // Connect returns a kv.Store that stores key-value pairs on GCP SecretManager. -func (s *GCPSecretManagerKeyStore) Connect(ctx context.Context) (kes.KeyStore, error) { +func (s *GCPSecretManagerKeyStore) Connect(ctx context.Context, verbose bool) (kes.KeyStore, error) { return gcp.Connect(ctx, &gcp.Config{ Endpoint: s.Endpoint, ProjectID: s.ProjectID, @@ -726,7 +722,7 @@ type AWSSecretsManagerKeyStore struct { } // Connect returns a kv.Store that stores key-value pairs on AWS SecretsManager. -func (s *AWSSecretsManagerKeyStore) Connect(ctx context.Context) (kes.KeyStore, error) { +func (s *AWSSecretsManagerKeyStore) Connect(ctx context.Context, verbose bool) (kes.KeyStore, error) { return aws.Connect(ctx, &aws.Config{ Addr: s.Endpoint, Region: s.Region, @@ -762,7 +758,7 @@ type AzureKeyVaultKeyStore struct { } // Connect returns a kv.Store that stores key-value pairs on Azure KeyVault. -func (s *AzureKeyVaultKeyStore) Connect(_ context.Context) (kes.KeyStore, error) { +func (s *AzureKeyVaultKeyStore) Connect(_ context.Context, verbose bool) (kes.KeyStore, error) { if (s.TenantID != "" || s.ClientID != "" || s.ClientSecret != "") && s.ManagedIdentityClientID != "" { return nil, errors.New("edge: failed to connect to Azure KeyVault: more than one authentication method specified") } @@ -812,7 +808,7 @@ type EntrustKeyControlKeyStore struct { } // Connect returns a kv.Store that stores key-value pairs on Entrust KeyControl. -func (s *EntrustKeyControlKeyStore) Connect(ctx context.Context) (kes.KeyStore, error) { +func (s *EntrustKeyControlKeyStore) Connect(ctx context.Context, verbose bool) (kes.KeyStore, error) { var rootCAs *x509.CertPool if s.CAPath != "" { ca, err := https.CertPoolFromFile(s.CAPath) diff --git a/kesconf/fortanix_test.go b/kesconf/fortanix_test.go index 3e8ebe85..396c3479 100644 --- a/kesconf/fortanix_test.go +++ b/kesconf/fortanix_test.go @@ -30,7 +30,7 @@ func TestFortanix(t *testing.T) { ctx, cancel := testingContext(t) defer cancel() - store, err := config.KeyStore.Connect(ctx) + store, err := config.KeyStore.Connect(ctx, false) if err != nil { t.Fatal(err) } diff --git a/kesconf/fs_test.go b/kesconf/fs_test.go index 2336b873..623b01d4 100644 --- a/kesconf/fs_test.go +++ b/kesconf/fs_test.go @@ -24,7 +24,7 @@ func TestFS(t *testing.T) { ctx, cancel := testingContext(t) defer cancel() - store, err := config.Connect(ctx) + store, err := config.Connect(ctx, false) if err != nil { t.Fatal(err) } diff --git a/kesconf/gcp_test.go b/kesconf/gcp_test.go index f3e6d131..5966b79f 100644 --- a/kesconf/gcp_test.go +++ b/kesconf/gcp_test.go @@ -30,7 +30,7 @@ func TestGCP(t *testing.T) { ctx, cancel := testingContext(t) defer cancel() - store, err := config.KeyStore.Connect(ctx) + store, err := config.KeyStore.Connect(ctx, false) if err != nil { t.Fatal(err) } diff --git a/kesconf/gemalto_test.go b/kesconf/gemalto_test.go index 41785706..01909ede 100644 --- a/kesconf/gemalto_test.go +++ b/kesconf/gemalto_test.go @@ -30,7 +30,7 @@ func TestGemalto(t *testing.T) { ctx, cancel := testingContext(t) defer cancel() - store, err := config.KeyStore.Connect(ctx) + store, err := config.KeyStore.Connect(ctx, false) if err != nil { t.Fatal(err) } diff --git a/kesconf/keycontrol_test.go b/kesconf/keycontrol_test.go index e5d10a8d..0d2e1190 100644 --- a/kesconf/keycontrol_test.go +++ b/kesconf/keycontrol_test.go @@ -30,7 +30,7 @@ func TestKeyControl(t *testing.T) { ctx, cancel := testingContext(t) defer cancel() - store, err := config.KeyStore.Connect(ctx) + store, err := config.KeyStore.Connect(ctx, false) if err != nil { t.Fatal(err) } diff --git a/kesconf/vault_test.go b/kesconf/vault_test.go index 00f15d8c..68998965 100644 --- a/kesconf/vault_test.go +++ b/kesconf/vault_test.go @@ -30,7 +30,7 @@ func TestVault(t *testing.T) { ctx, cancel := testingContext(t) defer cancel() - store, err := config.KeyStore.Connect(ctx) + store, err := config.KeyStore.Connect(ctx, false) if err != nil { t.Fatal(err) }