From e025b8382e511d8923d29ad1b96d63f902906ca6 Mon Sep 17 00:00:00 2001 From: Miguel Martinez Trivino Date: Wed, 30 Aug 2023 15:39:56 +0200 Subject: [PATCH 1/4] fix(secretmanager): add role and fix secretPrefix Signed-off-by: Miguel Martinez Trivino --- app/artifact-cas/cmd/main.go | 2 +- app/controlplane/cmd/main.go | 2 +- app/controlplane/configs/config.devel.yaml | 2 +- app/controlplane/configs/samples/config.yaml | 6 +- .../credentials/api/credentials/v1/config.go | 33 ++++--- .../api/credentials/v1/config.pb.go | 86 +++++++++++-------- .../api/credentials/v1/config.pb.validate.go | 2 + .../api/credentials/v1/config.proto | 3 + internal/credentials/vault/keyval.go | 31 +++++-- 9 files changed, 104 insertions(+), 63 deletions(-) diff --git a/app/artifact-cas/cmd/main.go b/app/artifact-cas/cmd/main.go index 80099dc18..b9e17b4b3 100644 --- a/app/artifact-cas/cmd/main.go +++ b/app/artifact-cas/cmd/main.go @@ -103,7 +103,7 @@ func main() { panic(err) } - credentialsReader, err := credsConfig.NewFromConfig(bc.GetCredentialsService(), logger) + credentialsReader, err := credsConfig.NewFromConfig(bc.GetCredentialsService(), credsConfig.Reader, logger) if err != nil { panic(err) } diff --git a/app/controlplane/cmd/main.go b/app/controlplane/cmd/main.go index 441ee6a9e..741d2c93e 100644 --- a/app/controlplane/cmd/main.go +++ b/app/controlplane/cmd/main.go @@ -105,7 +105,7 @@ func main() { panic(err) } - credsWriter, err := credsConfig.NewFromConfig(bc.GetCredentialsService(), logger) + credsWriter, err := credsConfig.NewFromConfig(bc.GetCredentialsService(), credsConfig.Writer, logger) if err != nil { panic(err) } diff --git a/app/controlplane/configs/config.devel.yaml b/app/controlplane/configs/config.devel.yaml index 3e76ae64e..36d1f1076 100644 --- a/app/controlplane/configs/config.devel.yaml +++ b/app/controlplane/configs/config.devel.yaml @@ -27,7 +27,7 @@ credentials_service: vault: address: ${VAULT_ADDRESS:http://0.0.0.0:8200} token: ${VAULT_TOKEN:notasecret} - secret_prefix: chainloop-devel + secret_prefix: chainloop-devel data: database: diff --git a/app/controlplane/configs/samples/config.yaml b/app/controlplane/configs/samples/config.yaml index a2affd050..e27e64762 100644 --- a/app/controlplane/configs/samples/config.yaml +++ b/app/controlplane/configs/samples/config.yaml @@ -25,15 +25,13 @@ credentials_service: vault: address: ${VAULT_ADDRESS:http://0.0.0.0:8200} token: ${VAULT_TOKEN:notasecret} - secret_prefix: chainloop-devel + secret_prefix: chainloop-devel # aws_secret_manager: # creds: # access_key: not-a-key # secret_key: not-a-secret # region: us-east-1 - # secret_prefix: i-e chainloop-devel # gcp_secret_manager: # project_id: 522312304548 - # auth_key: "./configs/gcp_auth_key.json" - # secret_prefix: "pre-" \ No newline at end of file + # auth_key: "./configs/gcp_auth_key.json" \ No newline at end of file diff --git a/internal/credentials/api/credentials/v1/config.go b/internal/credentials/api/credentials/v1/config.go index bb47f2e05..30b0de8ca 100644 --- a/internal/credentials/api/credentials/v1/config.go +++ b/internal/credentials/api/credentials/v1/config.go @@ -27,27 +27,34 @@ import ( "github.com/go-kratos/kratos/v2/log" ) -func NewFromConfig(conf *Credentials, l log.Logger) (credentials.ReaderWriter, error) { +type Role int64 + +const ( + Reader Role = iota + Writer +) + +func NewFromConfig(conf *Credentials, role Role, l log.Logger) (credentials.ReaderWriter, error) { if l == nil { l = log.NewStdLogger(io.Discard) } if awsc := conf.GetAwsSecretManager(); awsc != nil { - return newAWSCredentialsManager(awsc, l) + return newAWSCredentialsManager(awsc, conf.SecretPrefix, role, l) } if gcpc := conf.GetGcpSecretManager(); gcpc != nil { - return newGCPCredentialsManager(gcpc, l) + return newGCPCredentialsManager(gcpc, conf.SecretPrefix, role, l) } if vaultc := conf.GetVault(); vaultc != nil { - return newVaultCredentialsManager(vaultc, l) + return newVaultCredentialsManager(vaultc, conf.SecretPrefix, role, l) } return nil, errors.New("no credentials manager configuration found") } -func newAWSCredentialsManager(conf *Credentials_AWSSecretManager, l log.Logger) (*aws.Manager, error) { +func newAWSCredentialsManager(conf *Credentials_AWSSecretManager, prefix string, _ Role, l log.Logger) (*aws.Manager, error) { if err := conf.ValidateAll(); err != nil { return nil, fmt.Errorf("uncompleted configuration for AWS secret manager: %w", err) } @@ -55,7 +62,8 @@ func newAWSCredentialsManager(conf *Credentials_AWSSecretManager, l log.Logger) opts := &aws.NewManagerOpts{ Region: conf.Region, AccessKey: conf.GetCreds().GetAccessKey(), SecretKey: conf.GetCreds().GetSecretKey(), - Logger: l, + Logger: l, + SecretPrefix: prefix, } m, err := aws.NewManager(opts) @@ -63,12 +71,10 @@ func newAWSCredentialsManager(conf *Credentials_AWSSecretManager, l log.Logger) return nil, fmt.Errorf("configuring the secrets manager: %w", err) } - _ = l.Log(log.LevelInfo, "msg", "secrets manager configured", "backend", "AWS secret manager") - return m, nil } -func newVaultCredentialsManager(conf *Credentials_Vault, l log.Logger) (*vault.Manager, error) { +func newVaultCredentialsManager(conf *Credentials_Vault, prefix string, r Role, l log.Logger) (*vault.Manager, error) { if err := conf.ValidateAll(); err != nil { return nil, fmt.Errorf("uncompleted configuration for Vault secret manager: %w", err) } @@ -76,6 +82,8 @@ func newVaultCredentialsManager(conf *Credentials_Vault, l log.Logger) (*vault.M opts := &vault.NewManagerOpts{ AuthToken: conf.Token, Address: conf.Address, MountPath: conf.MountPath, Logger: l, + SecretPrefix: prefix, + Role: vault.Role(r), } m, err := vault.NewManager(opts) @@ -83,12 +91,10 @@ func newVaultCredentialsManager(conf *Credentials_Vault, l log.Logger) (*vault.M return nil, fmt.Errorf("configuring vault: %w", err) } - _ = l.Log(log.LevelInfo, "msg", "secrets manager configured", "backend", "Vault") - return m, nil } -func newGCPCredentialsManager(conf *Credentials_GCPSecretManager, l log.Logger) (*gcp.Manager, error) { +func newGCPCredentialsManager(conf *Credentials_GCPSecretManager, prefix string, _ Role, l log.Logger) (*gcp.Manager, error) { if err := conf.ValidateAll(); err != nil { return nil, fmt.Errorf("uncompleted configuration for GCP secret manager: %w", err) } @@ -97,6 +103,7 @@ func newGCPCredentialsManager(conf *Credentials_GCPSecretManager, l log.Logger) ProjectID: conf.ProjectId, ServiceAccountKey: conf.ServiceAccountKey, Logger: l, + SecretPrefix: prefix, } m, err := gcp.NewManager(opts) @@ -104,7 +111,5 @@ func newGCPCredentialsManager(conf *Credentials_GCPSecretManager, l log.Logger) return nil, fmt.Errorf("configuring the GCP secret manager: %w", err) } - _ = l.Log(log.LevelInfo, "msg", "secrets manager configured", "backend", "GCP secret manager") - return m, nil } diff --git a/internal/credentials/api/credentials/v1/config.pb.go b/internal/credentials/api/credentials/v1/config.pb.go index 162930978..b28a3908e 100644 --- a/internal/credentials/api/credentials/v1/config.pb.go +++ b/internal/credentials/api/credentials/v1/config.pb.go @@ -15,7 +15,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.30.0 +// protoc-gen-go v1.31.0 // protoc (unknown) // source: credentials/v1/config.proto @@ -48,6 +48,8 @@ type Credentials struct { // *Credentials_Vault_ // *Credentials_GcpSecretManager Backend isCredentials_Backend `protobuf_oneof:"backend"` + // prefix used while writing a new secret + SecretPrefix string `protobuf:"bytes,4,opt,name=secret_prefix,json=secretPrefix,proto3" json:"secret_prefix,omitempty"` } func (x *Credentials) Reset() { @@ -110,6 +112,13 @@ func (x *Credentials) GetGcpSecretManager() *Credentials_GCPSecretManager { return nil } +func (x *Credentials) GetSecretPrefix() string { + if x != nil { + return x.SecretPrefix + } + return "" +} + type isCredentials_Backend interface { isCredentials_Backend() } @@ -374,7 +383,7 @@ var file_credentials_v1_config_proto_rawDesc = []byte{ 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0e, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x2e, 0x76, 0x31, 0x1a, 0x17, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, - 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xcd, 0x05, 0x0a, 0x0b, 0x43, 0x72, 0x65, 0x64, 0x65, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xf2, 0x05, 0x0a, 0x0b, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x12, 0x5c, 0x0a, 0x12, 0x61, 0x77, 0x73, 0x5f, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x5f, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, @@ -390,41 +399,44 @@ var file_credentials_v1_config_proto_rawDesc = []byte{ 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x2e, 0x47, 0x43, 0x50, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x48, 0x00, 0x52, 0x10, 0x67, 0x63, 0x70, - 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x1a, 0xe0, 0x01, - 0x0a, 0x10, 0x41, 0x57, 0x53, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x4d, 0x61, 0x6e, 0x61, 0x67, - 0x65, 0x72, 0x12, 0x52, 0x0a, 0x05, 0x63, 0x72, 0x65, 0x64, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x32, 0x2e, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x2e, - 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x2e, 0x41, - 0x57, 0x53, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x2e, - 0x43, 0x72, 0x65, 0x64, 0x73, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, 0x52, - 0x05, 0x63, 0x72, 0x65, 0x64, 0x73, 0x12, 0x1f, 0x0a, 0x06, 0x72, 0x65, 0x67, 0x69, 0x6f, 0x6e, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, - 0x06, 0x72, 0x65, 0x67, 0x69, 0x6f, 0x6e, 0x1a, 0x57, 0x0a, 0x05, 0x43, 0x72, 0x65, 0x64, 0x73, - 0x12, 0x26, 0x0a, 0x0a, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x09, 0x61, - 0x63, 0x63, 0x65, 0x73, 0x73, 0x4b, 0x65, 0x79, 0x12, 0x26, 0x0a, 0x0a, 0x73, 0x65, 0x63, 0x72, - 0x65, 0x74, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, - 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x09, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x4b, 0x65, 0x79, - 0x1a, 0x68, 0x0a, 0x05, 0x56, 0x61, 0x75, 0x6c, 0x74, 0x12, 0x1d, 0x0a, 0x05, 0x74, 0x6f, 0x6b, - 0x65, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, 0x10, - 0x01, 0x52, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x21, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, - 0x65, 0x73, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, - 0x10, 0x01, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x6d, - 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x09, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x50, 0x61, 0x74, 0x68, 0x1a, 0x6a, 0x0a, 0x10, 0x47, 0x43, - 0x50, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x12, 0x26, - 0x0a, 0x0a, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x09, 0x70, 0x72, 0x6f, - 0x6a, 0x65, 0x63, 0x74, 0x49, 0x64, 0x12, 0x2e, 0x0a, 0x13, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, - 0x65, 0x5f, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x11, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x41, 0x63, 0x63, 0x6f, - 0x75, 0x6e, 0x74, 0x4b, 0x65, 0x79, 0x42, 0x0e, 0x0a, 0x07, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, - 0x64, 0x12, 0x03, 0xf8, 0x42, 0x01, 0x42, 0x4f, 0x5a, 0x4d, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, - 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x6c, 0x6f, 0x6f, 0x70, 0x2d, 0x64, - 0x65, 0x76, 0x2f, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x6c, 0x6f, 0x6f, 0x70, 0x2f, 0x69, 0x6e, 0x74, - 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, - 0x73, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, - 0x73, 0x2f, 0x76, 0x31, 0x3b, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x12, 0x23, 0x0a, + 0x0d, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x5f, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x50, 0x72, 0x65, 0x66, + 0x69, 0x78, 0x1a, 0xe0, 0x01, 0x0a, 0x10, 0x41, 0x57, 0x53, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, + 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x12, 0x52, 0x0a, 0x05, 0x63, 0x72, 0x65, 0x64, 0x73, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x32, 0x2e, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, + 0x69, 0x61, 0x6c, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, + 0x61, 0x6c, 0x73, 0x2e, 0x41, 0x57, 0x53, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x4d, 0x61, 0x6e, + 0x61, 0x67, 0x65, 0x72, 0x2e, 0x43, 0x72, 0x65, 0x64, 0x73, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, + 0x01, 0x02, 0x10, 0x01, 0x52, 0x05, 0x63, 0x72, 0x65, 0x64, 0x73, 0x12, 0x1f, 0x0a, 0x06, 0x72, + 0x65, 0x67, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, + 0x72, 0x02, 0x10, 0x01, 0x52, 0x06, 0x72, 0x65, 0x67, 0x69, 0x6f, 0x6e, 0x1a, 0x57, 0x0a, 0x05, + 0x43, 0x72, 0x65, 0x64, 0x73, 0x12, 0x26, 0x0a, 0x0a, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, + 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, + 0x10, 0x01, 0x52, 0x09, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x4b, 0x65, 0x79, 0x12, 0x26, 0x0a, + 0x0a, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x09, 0x73, 0x65, 0x63, 0x72, + 0x65, 0x74, 0x4b, 0x65, 0x79, 0x1a, 0x68, 0x0a, 0x05, 0x56, 0x61, 0x75, 0x6c, 0x74, 0x12, 0x1d, + 0x0a, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, + 0x42, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x21, 0x0a, + 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, + 0xfa, 0x42, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, + 0x12, 0x1d, 0x0a, 0x0a, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x50, 0x61, 0x74, 0x68, 0x1a, + 0x6a, 0x0a, 0x10, 0x47, 0x43, 0x50, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x4d, 0x61, 0x6e, 0x61, + 0x67, 0x65, 0x72, 0x12, 0x26, 0x0a, 0x0a, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x69, + 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, 0x10, 0x01, + 0x52, 0x09, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x49, 0x64, 0x12, 0x2e, 0x0a, 0x13, 0x73, + 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x6b, + 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, + 0x65, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x4b, 0x65, 0x79, 0x42, 0x0e, 0x0a, 0x07, 0x62, + 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x12, 0x03, 0xf8, 0x42, 0x01, 0x42, 0x4f, 0x5a, 0x4d, 0x67, + 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x6c, + 0x6f, 0x6f, 0x70, 0x2d, 0x64, 0x65, 0x76, 0x2f, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x6c, 0x6f, 0x6f, + 0x70, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x63, 0x72, 0x65, 0x64, 0x65, + 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x63, 0x72, 0x65, 0x64, 0x65, + 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x2f, 0x76, 0x31, 0x3b, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/internal/credentials/api/credentials/v1/config.pb.validate.go b/internal/credentials/api/credentials/v1/config.pb.validate.go index cba6996f8..68e3bf48b 100644 --- a/internal/credentials/api/credentials/v1/config.pb.validate.go +++ b/internal/credentials/api/credentials/v1/config.pb.validate.go @@ -57,6 +57,8 @@ func (m *Credentials) validate(all bool) error { var errors []error + // no validation rules for SecretPrefix + oneofBackendPresent := false switch v := m.Backend.(type) { case *Credentials_AwsSecretManager: diff --git a/internal/credentials/api/credentials/v1/config.proto b/internal/credentials/api/credentials/v1/config.proto index 3968b6e45..ed70f45fa 100644 --- a/internal/credentials/api/credentials/v1/config.proto +++ b/internal/credentials/api/credentials/v1/config.proto @@ -29,6 +29,9 @@ message Credentials { GCPSecretManager gcp_secret_manager = 3; } + // prefix used while writing a new secret + string secret_prefix = 4; + // Top level is deprecated now message AWSSecretManager { Creds creds = 1 [(validate.rules).message.required = true]; diff --git a/internal/credentials/vault/keyval.go b/internal/credentials/vault/keyval.go index 5cacc3ffd..bc052be98 100644 --- a/internal/credentials/vault/keyval.go +++ b/internal/credentials/vault/keyval.go @@ -41,8 +41,16 @@ type Manager struct { type NewManagerOpts struct { AuthToken, Address, MountPath, SecretPrefix string Logger log.Logger + Role Role } +type Role int64 + +const ( + Reader Role = iota + Writer +) + const defaultKVMountPath = "secret" const healthCheckSecret = "chainloop-healthcheck" @@ -76,20 +84,23 @@ func NewManager(opts *NewManagerOpts) (*Manager, error) { } logger := servicelogger.ScopedHelper(l, "credentials/vault") - logger.Infow("msg", "configuring vault", "address", opts.Address, "mount_path", mountPath) + logger.Infow("msg", "configuring vault", "address", opts.Address, "mount_path", mountPath, "prefix", opts.SecretPrefix, "role", opts.Role) // Check address, token validity and mount path kv := client.KVv2(mountPath) - if err := validateClient(kv, opts.SecretPrefix); err != nil { - return nil, fmt.Errorf("validating client: %w", err) + // only run the validation if we are being used as a writer + if opts.Role != Reader { + if err := validateWriterClient(kv, opts.SecretPrefix); err != nil { + return nil, fmt.Errorf("validating client: %w", err) + } } return &Manager{kv, opts.SecretPrefix, logger}, nil } -// validateClient checks if the client is valid by writing and deleting a secret +// validateWriterClient checks if the client is valid by writing and deleting a secret // in the provided mount path. -func validateClient(kv *vault.KVv2, pathPrefix string) error { +func validateWriterClient(kv *vault.KVv2, pathPrefix string) error { ctx := context.Background() keyPath := strings.Join([]string{pathPrefix, healthCheckSecret}, "/") if _, err := kv.Put(ctx, keyPath, nil); err != nil { @@ -173,3 +184,13 @@ func mapToStruct(i map[string]interface{}, o interface{}) error { return nil } + +func (r Role) String() string { + switch r { + case Reader: + return "reader" + case Writer: + return "writer" + } + return "unknown" +} From 22b7f2c216fe67be69c67ea54c3bc8bc958e68c8 Mon Sep 17 00:00:00 2001 From: Miguel Martinez Trivino Date: Wed, 30 Aug 2023 23:34:04 +0200 Subject: [PATCH 2/4] feat(vault): enable reader healthcheck Signed-off-by: Miguel Martinez Trivino --- app/artifact-cas/cmd/main.go | 5 ++-- app/controlplane/cmd/main.go | 4 +-- deployment/chainloop/templates/_helpers.tpl | 4 +-- internal/credentials/aws/secretmanager.go | 3 +- internal/credentials/credentials.go | 7 +++++ internal/credentials/gcp/secretmanager.go | 6 ++-- .../v1/config.go => manager/manager.go} | 22 ++++++--------- .../manager_test.go} | 10 ++++--- internal/credentials/vault/keyval.go | 28 +++++++++++++++++-- 9 files changed, 59 insertions(+), 30 deletions(-) rename internal/credentials/{api/credentials/v1/config.go => manager/manager.go} (79%) rename internal/credentials/{api/credentials/v1/config_test.go => manager/manager_test.go} (95%) diff --git a/app/artifact-cas/cmd/main.go b/app/artifact-cas/cmd/main.go index b9e17b4b3..82f9f26a1 100644 --- a/app/artifact-cas/cmd/main.go +++ b/app/artifact-cas/cmd/main.go @@ -20,11 +20,12 @@ import ( "os" "time" - credsConfig "github.com/chainloop-dev/chainloop/internal/credentials/api/credentials/v1" "github.com/getsentry/sentry-go" "github.com/chainloop-dev/chainloop/app/artifact-cas/internal/conf" "github.com/chainloop-dev/chainloop/app/artifact-cas/internal/server" + "github.com/chainloop-dev/chainloop/internal/credentials" + "github.com/chainloop-dev/chainloop/internal/credentials/manager" "github.com/chainloop-dev/chainloop/internal/servicelogger" "github.com/go-kratos/kratos/v2" @@ -103,7 +104,7 @@ func main() { panic(err) } - credentialsReader, err := credsConfig.NewFromConfig(bc.GetCredentialsService(), credsConfig.Reader, logger) + credentialsReader, err := manager.NewFromConfig(bc.GetCredentialsService(), credentials.RoleReader, logger) if err != nil { panic(err) } diff --git a/app/controlplane/cmd/main.go b/app/controlplane/cmd/main.go index 741d2c93e..68427050e 100644 --- a/app/controlplane/cmd/main.go +++ b/app/controlplane/cmd/main.go @@ -31,7 +31,7 @@ import ( backends "github.com/chainloop-dev/chainloop/internal/blobmanager" "github.com/chainloop-dev/chainloop/internal/blobmanager/oci" "github.com/chainloop-dev/chainloop/internal/credentials" - credsConfig "github.com/chainloop-dev/chainloop/internal/credentials/api/credentials/v1" + "github.com/chainloop-dev/chainloop/internal/credentials/manager" "github.com/chainloop-dev/chainloop/internal/servicelogger" "github.com/go-kratos/kratos/v2" @@ -105,7 +105,7 @@ func main() { panic(err) } - credsWriter, err := credsConfig.NewFromConfig(bc.GetCredentialsService(), credsConfig.Writer, logger) + credsWriter, err := manager.NewFromConfig(bc.GetCredentialsService(), credentials.RoleWriter, logger) if err != nil { panic(err) } diff --git a/deployment/chainloop/templates/_helpers.tpl b/deployment/chainloop/templates/_helpers.tpl index 1bc172334..65b3eb9a2 100644 --- a/deployment/chainloop/templates/_helpers.tpl +++ b/deployment/chainloop/templates/_helpers.tpl @@ -59,9 +59,9 @@ WBiBSPaJtz6JYk/fye4= {{- define "chainloop.credentials_service_settings" -}} {{- with .Values.secretsBackend }} +secretPrefix: {{ required "secret prefix required" .secretPrefix | quote }} {{- if eq .backend "vault" }} vault: - secretPrefix: {{ required "secret prefix required" .secretPrefix | quote }} {{- if and $.Values.development (or (not .vault) not .vault.address) }} address: {{ printf "http://%s:8200" (include "chainloop.vault.fullname" $) | quote }} token: {{ $.Values.vault.server.dev.devRootToken | quote }} @@ -72,7 +72,6 @@ vault: {{- else if eq .backend "awsSecretManager" }} awsSecretManager: - secretPrefix: {{ required "secret prefix required" .secretPrefix | quote }} region: {{ required "region required" .awsSecretManager.region | quote }} creds: accessKey: {{ required "access key required" .awsSecretManager.accessKey | quote }} @@ -80,7 +79,6 @@ awsSecretManager: {{- else if eq .backend "gcpSecretManager" }} gcpSecretManager: - secretPrefix: {{ required "secret prefix required" .secretPrefix | quote }} projectId: {{ required "project id required" .gcpSecretManager.projectId | quote }} serviceAccountKey: "/gcp-secrets/serviceAccountKey.json" {{- if eq .gcpSecretManager.serviceAccountKey "" }} diff --git a/internal/credentials/aws/secretmanager.go b/internal/credentials/aws/secretmanager.go index e6ab1e235..a53b1f8d7 100644 --- a/internal/credentials/aws/secretmanager.go +++ b/internal/credentials/aws/secretmanager.go @@ -51,6 +51,7 @@ type Manager struct { type NewManagerOpts struct { Region, AccessKey, SecretKey, SecretPrefix string Logger log.Logger + Role credentials.Role } func NewManager(opts *NewManagerOpts) (*Manager, error) { @@ -64,7 +65,7 @@ func NewManager(opts *NewManagerOpts) (*Manager, error) { } logger := servicelogger.ScopedHelper(l, "credentials/aws-secrets-manager") - logger.Infow("msg", "configuring secrets-manager", "region", opts.Region) + logger.Infow("msg", "configuring secrets-manager", "region", opts.Region, "role", opts.Role, "prefix", opts.SecretPrefix) config, err := config.LoadDefaultConfig( context.TODO(), diff --git a/internal/credentials/credentials.go b/internal/credentials/credentials.go index f8de8d78a..ffb3ba94a 100644 --- a/internal/credentials/credentials.go +++ b/internal/credentials/credentials.go @@ -43,6 +43,13 @@ type Reader interface { ReadCredentials(ctx context.Context, secretName string, credentials any) error } +type Role int64 + +const ( + RoleReader Role = iota + RoleWriter +) + var ErrNotFound = errors.New("credentials not found") var ErrValidation = errors.New("credentials validation error") diff --git a/internal/credentials/gcp/secretmanager.go b/internal/credentials/gcp/secretmanager.go index 0d06fcbdf..6f76e22ed 100644 --- a/internal/credentials/gcp/secretmanager.go +++ b/internal/credentials/gcp/secretmanager.go @@ -52,6 +52,7 @@ type Manager struct { type NewManagerOpts struct { ProjectID, ServiceAccountKey, SecretPrefix string Logger log.Logger + Role credentials.Role } func NewManager(opts *NewManagerOpts) (*Manager, error) { @@ -65,13 +66,14 @@ func NewManager(opts *NewManagerOpts) (*Manager, error) { } logger := servicelogger.ScopedHelper(l, "credentials/gcp-secrets-manager") - logger.Infow("msg", "configuring gcp secrets-manager", "projectID", opts.ProjectID) + logger.Infow("msg", "configuring gcp secrets-manager", "projectID", opts.ProjectID, "role", opts.Role, "prefix", opts.SecretPrefix) cli, err := secretmanager.NewClient(context.TODO(), option.WithCredentialsFile(opts.ServiceAccountKey)) if err != nil { return nil, fmt.Errorf("error while creating the client: %w", err) } - logger.Infow("msg", "created GCP connection", "projectID", opts.ProjectID) + + logger.Infow("msg", "created GCP connection", "projectID", opts.ProjectID, "role", opts.Role, "prefix", opts.SecretPrefix) return &Manager{ projectID: opts.ProjectID, diff --git a/internal/credentials/api/credentials/v1/config.go b/internal/credentials/manager/manager.go similarity index 79% rename from internal/credentials/api/credentials/v1/config.go rename to internal/credentials/manager/manager.go index 30b0de8ca..66fff3e5f 100644 --- a/internal/credentials/api/credentials/v1/config.go +++ b/internal/credentials/manager/manager.go @@ -13,7 +13,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package v1 +package manager import ( "errors" @@ -21,20 +21,14 @@ import ( "io" "github.com/chainloop-dev/chainloop/internal/credentials" + api "github.com/chainloop-dev/chainloop/internal/credentials/api/credentials/v1" "github.com/chainloop-dev/chainloop/internal/credentials/aws" "github.com/chainloop-dev/chainloop/internal/credentials/gcp" "github.com/chainloop-dev/chainloop/internal/credentials/vault" "github.com/go-kratos/kratos/v2/log" ) -type Role int64 - -const ( - Reader Role = iota - Writer -) - -func NewFromConfig(conf *Credentials, role Role, l log.Logger) (credentials.ReaderWriter, error) { +func NewFromConfig(conf *api.Credentials, role credentials.Role, l log.Logger) (credentials.ReaderWriter, error) { if l == nil { l = log.NewStdLogger(io.Discard) } @@ -54,7 +48,7 @@ func NewFromConfig(conf *Credentials, role Role, l log.Logger) (credentials.Read return nil, errors.New("no credentials manager configuration found") } -func newAWSCredentialsManager(conf *Credentials_AWSSecretManager, prefix string, _ Role, l log.Logger) (*aws.Manager, error) { +func newAWSCredentialsManager(conf *api.Credentials_AWSSecretManager, prefix string, r credentials.Role, l log.Logger) (*aws.Manager, error) { if err := conf.ValidateAll(); err != nil { return nil, fmt.Errorf("uncompleted configuration for AWS secret manager: %w", err) } @@ -64,6 +58,7 @@ func newAWSCredentialsManager(conf *Credentials_AWSSecretManager, prefix string, AccessKey: conf.GetCreds().GetAccessKey(), SecretKey: conf.GetCreds().GetSecretKey(), Logger: l, SecretPrefix: prefix, + Role: r, } m, err := aws.NewManager(opts) @@ -74,7 +69,7 @@ func newAWSCredentialsManager(conf *Credentials_AWSSecretManager, prefix string, return m, nil } -func newVaultCredentialsManager(conf *Credentials_Vault, prefix string, r Role, l log.Logger) (*vault.Manager, error) { +func newVaultCredentialsManager(conf *api.Credentials_Vault, prefix string, r credentials.Role, l log.Logger) (*vault.Manager, error) { if err := conf.ValidateAll(); err != nil { return nil, fmt.Errorf("uncompleted configuration for Vault secret manager: %w", err) } @@ -83,7 +78,7 @@ func newVaultCredentialsManager(conf *Credentials_Vault, prefix string, r Role, AuthToken: conf.Token, Address: conf.Address, MountPath: conf.MountPath, Logger: l, SecretPrefix: prefix, - Role: vault.Role(r), + Role: r, } m, err := vault.NewManager(opts) @@ -94,7 +89,7 @@ func newVaultCredentialsManager(conf *Credentials_Vault, prefix string, r Role, return m, nil } -func newGCPCredentialsManager(conf *Credentials_GCPSecretManager, prefix string, _ Role, l log.Logger) (*gcp.Manager, error) { +func newGCPCredentialsManager(conf *api.Credentials_GCPSecretManager, prefix string, r credentials.Role, l log.Logger) (*gcp.Manager, error) { if err := conf.ValidateAll(); err != nil { return nil, fmt.Errorf("uncompleted configuration for GCP secret manager: %w", err) } @@ -104,6 +99,7 @@ func newGCPCredentialsManager(conf *Credentials_GCPSecretManager, prefix string, ServiceAccountKey: conf.ServiceAccountKey, Logger: l, SecretPrefix: prefix, + Role: r, } m, err := gcp.NewManager(opts) diff --git a/internal/credentials/api/credentials/v1/config_test.go b/internal/credentials/manager/manager_test.go similarity index 95% rename from internal/credentials/api/credentials/v1/config_test.go rename to internal/credentials/manager/manager_test.go index dfdd16444..432e94ea6 100644 --- a/internal/credentials/api/credentials/v1/config_test.go +++ b/internal/credentials/manager/manager_test.go @@ -13,7 +13,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package v1_test +package manager_test import ( "context" @@ -22,7 +22,9 @@ import ( "testing" "time" + "github.com/chainloop-dev/chainloop/internal/credentials" v1 "github.com/chainloop-dev/chainloop/internal/credentials/api/credentials/v1" + "github.com/chainloop-dev/chainloop/internal/credentials/manager" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" @@ -43,7 +45,7 @@ var validGCPConfig = &v1.Credentials{ Backend: &v1.Credentials_GcpSecretManager{ GcpSecretManager: &v1.Credentials_GCPSecretManager{ ProjectId: "project", - ServiceAccountKey: "../../../gcp/testdata/test_gcp_key.json", + ServiceAccountKey: "../gcp/testdata/test_gcp_key.json", }, }, } @@ -136,7 +138,7 @@ func (s *testSuite) TestNewFromConfig() { conf: &v1.Credentials{ Backend: &v1.Credentials_GcpSecretManager{ GcpSecretManager: &v1.Credentials_GCPSecretManager{ - ServiceAccountKey: "../../../gcp/testdata/test_gcp_key.json", + ServiceAccountKey: "../gcp/testdata/test_gcp_key.json", }, }, }, @@ -208,7 +210,7 @@ func (s *testSuite) TestNewFromConfig() { for _, tc := range testCases { s.Run(tc.name, func() { - _, err := v1.NewFromConfig(tc.conf, nil) + _, err := manager.NewFromConfig(tc.conf, credentials.RoleReader, nil) if tc.wantErr { assert.Error(s.T(), err) } else { diff --git a/internal/credentials/vault/keyval.go b/internal/credentials/vault/keyval.go index bc052be98..3b73b8b42 100644 --- a/internal/credentials/vault/keyval.go +++ b/internal/credentials/vault/keyval.go @@ -41,7 +41,7 @@ type Manager struct { type NewManagerOpts struct { AuthToken, Address, MountPath, SecretPrefix string Logger log.Logger - Role Role + Role credentials.Role } type Role int64 @@ -53,6 +53,7 @@ const ( const defaultKVMountPath = "secret" const healthCheckSecret = "chainloop-healthcheck" +const healthCheckNonExisting = "chainloop-non-existing" // NewManager creates a new credentials manager that uses Hashicorp Vault as backend // Configured to write secrets in the KVv2 engine referenced by the provided mount path. @@ -88,8 +89,11 @@ func NewManager(opts *NewManagerOpts) (*Manager, error) { // Check address, token validity and mount path kv := client.KVv2(mountPath) - // only run the validation if we are being used as a writer - if opts.Role != Reader { + if opts.Role == credentials.RoleReader { + if err := validateReaderClient(kv, opts.SecretPrefix); err != nil { + return nil, fmt.Errorf("validating client: %w", err) + } + } else { if err := validateWriterClient(kv, opts.SecretPrefix); err != nil { return nil, fmt.Errorf("validating client: %w", err) } @@ -114,6 +118,24 @@ func validateWriterClient(kv *vault.KVv2, pathPrefix string) error { return nil } +func validateReaderClient(kv *vault.KVv2, pathPrefix string) error { + ctx := context.Background() + // try to retrieve a non-existing key + // if we get 404 means that we have permissions to read in that path + keyPath := strings.Join([]string{pathPrefix, healthCheckNonExisting}, "/") + _, err := kv.Get(ctx, keyPath) + if err != nil { + if errors.Is(err, vault.ErrSecretNotFound) { + // Everything is ok + return nil + } + + return err + } + + return nil +} + func (m *Manager) SaveCredentials(ctx context.Context, orgID string, creds any) (string, error) { credsM, err := structToMap(creds) if err != nil { From f00fed257e891576245db9d7c1fedd9230992b43 Mon Sep 17 00:00:00 2001 From: Miguel Martinez Trivino Date: Wed, 30 Aug 2023 23:37:49 +0200 Subject: [PATCH 3/4] feat(vault): enable reader healthcheck Signed-off-by: Miguel Martinez Trivino --- app/artifact-cas/configs/config.devel.yaml | 2 ++ app/artifact-cas/configs/samples/config.yaml | 2 ++ 2 files changed, 4 insertions(+) diff --git a/app/artifact-cas/configs/config.devel.yaml b/app/artifact-cas/configs/config.devel.yaml index 9812807e5..3225502f9 100644 --- a/app/artifact-cas/configs/config.devel.yaml +++ b/app/artifact-cas/configs/config.devel.yaml @@ -13,6 +13,8 @@ server: addr: 0.0.0.0:5001 credentials_service: + # we will check that we can read there + secret_prefix: chainloop-devel # Remember to run vault via docker compose up vault: address: ${VAULT_ADDRESS:http://0.0.0.0:8200} diff --git a/app/artifact-cas/configs/samples/config.yaml b/app/artifact-cas/configs/samples/config.yaml index d81032a6a..b3cb311bd 100644 --- a/app/artifact-cas/configs/samples/config.yaml +++ b/app/artifact-cas/configs/samples/config.yaml @@ -13,6 +13,8 @@ server: addr: 0.0.0.0:5001 credentials_service: + # We use the prefix to check that we can read from it on initialization + secret_prefix: chainloop-devel # Remember to run vault via docker compose up vault: address: ${VAULT_ADDRESS:http://0.0.0.0:8200} From e399a0d36654437b0fa02496ba0cb6624e28a25c Mon Sep 17 00:00:00 2001 From: Miguel Martinez Trivino Date: Wed, 30 Aug 2023 23:54:06 +0200 Subject: [PATCH 4/4] feat(vault): enable reader healthcheck Signed-off-by: Miguel Martinez Trivino --- internal/credentials/vault/keyval_test.go | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/internal/credentials/vault/keyval_test.go b/internal/credentials/vault/keyval_test.go index bb855ad5c..4f681a07d 100644 --- a/internal/credentials/vault/keyval_test.go +++ b/internal/credentials/vault/keyval_test.go @@ -43,17 +43,20 @@ func (s *testSuite) TestNewManager() { token string path string expectedError bool + role credentials.Role }{ {name: "missing token", connection: s.connectionString, expectedError: true}, {name: "missing address", token: defaultToken, expectedError: true}, - {name: "invalid address", token: defaultToken, connection: "http://non-existing:5000", expectedError: true}, - {name: "invalid mount path", token: defaultToken, connection: s.connectionString, path: "non-existing", expectedError: true}, + {name: "invalid address reader", token: defaultToken, connection: "http://non-existing:5000", expectedError: true, role: credentials.RoleReader}, + {name: "invalid address writer", token: defaultToken, connection: "http://non-existing:5000", expectedError: true}, + {name: "invalid mount path", token: defaultToken, connection: s.connectionString, path: "non-existing", expectedError: true, role: credentials.RoleWriter}, + {name: "valid connection reader", connection: s.connectionString, token: defaultToken, role: credentials.RoleReader}, {name: "valid connection", connection: s.connectionString, token: defaultToken}, } for _, tc := range testCases { s.Run(tc.name, func() { - opts := &vault.NewManagerOpts{AuthToken: tc.token, Address: tc.connection, MountPath: tc.path} + opts := &vault.NewManagerOpts{AuthToken: tc.token, Address: tc.connection, MountPath: tc.path, Role: tc.role} _, err := vault.NewManager(opts) if tc.expectedError { assert.Error(err)