diff --git a/docs/backends.md b/docs/backends.md index d59e1d63..903590ee 100644 --- a/docs/backends.md +++ b/docs/backends.md @@ -176,7 +176,7 @@ The path for IBM Cloud Secret Manager secrets can be specified in two ways: 2. `ibmcloud//secrets/groups//#` Where: -* `` can be one of the following: `arbitrary`, `iam_credentials`, `imported_cert`, `kv`, `private_cert`, `public_cert`, or `username_password`. +* `` can be one of the following: `arbitrary`, `iam_credentials`, `imported_cert`, `kv`, `private_cert`, `public_cert`, `username_password`, or `service_credentials`. * `` can be a secret group ID or name. * `` is the name of the secret. * `` is the key name within the secret. Specifically, the following keys are available for extraction: @@ -184,6 +184,7 @@ Where: * `username` and `password` for the `username_password` secret type * `certificate`, `private_key`, `intermediate` for the `imported_cert` or `public_cert` secret types * `certificate`, `private_key`, `issuing_ca`, `ca_chain` for the `private_cert` secret type + * `apikey` or/and any top-level key of the actual credential object for the `service_credentials` secret type * any key of the `kv` secret type `` is not supported for the `arbitrary` secret type. diff --git a/go.mod b/go.mod index 19c2472d..674de324 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azsecrets v1.0.1 github.com/DelineaXPM/tss-sdk-go/v2 v2.0.0 github.com/IBM/go-sdk-core/v5 v5.14.1 - github.com/IBM/secrets-manager-go-sdk v1.2.0 + github.com/IBM/secrets-manager-go-sdk/v2 v2.0.4 github.com/aws/aws-sdk-go-v2 v1.27.1 github.com/aws/aws-sdk-go-v2/config v1.27.17 github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.29.2 diff --git a/go.sum b/go.sum index a8c974f3..a91e6323 100644 --- a/go.sum +++ b/go.sum @@ -154,8 +154,8 @@ github.com/HdrHistogram/hdrhistogram-go v1.1.2 h1:5IcZpTvzydCQeHzK4Ef/D5rrSqwxob github.com/HdrHistogram/hdrhistogram-go v1.1.2/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo= github.com/IBM/go-sdk-core/v5 v5.14.1 h1:WR1r0zz+gDW++xzZjF41r9ueY4JyjS2vgZjiYs8lO3c= github.com/IBM/go-sdk-core/v5 v5.14.1/go.mod h1:MUvIr/1mgGh198ZXL+ByKz9Qs1JoEh80v/96x8jPXNY= -github.com/IBM/secrets-manager-go-sdk v1.2.0 h1:bgFfBF+LjHLtUfV3hTLkfgE8EjFsJaeU2icA2Hg+M50= -github.com/IBM/secrets-manager-go-sdk v1.2.0/go.mod h1:qv+tQg8Z3Vb11DQYxDjEGeROHDtTLQxUWuOIrIdWg6E= +github.com/IBM/secrets-manager-go-sdk/v2 v2.0.4 h1:xa9e+POVqaXxXHXkSMCOVAbKdUNEu86jQmo5hcpd+L4= +github.com/IBM/secrets-manager-go-sdk/v2 v2.0.4/go.mod h1:5gq8D8uWOIbqOm1uztay6lpOysgJaxxEsaVZLWGWb40= github.com/Jeffail/gabs/v2 v2.1.0 h1:6dV9GGOjoQgzWTQEltZPXlJdFloxvIq7DwqgxMCbq30= github.com/Jeffail/gabs/v2 v2.1.0/go.mod h1:xCn81vdHKxFUuWWAaD5jCTQDNPBMh5pPs9IJ+NcziBI= github.com/JohnCGriffin/overflow v0.0.0-20211019200055-46fa312c352c h1:RGWPOewvKIROun94nF7v2cua9qP+thov/7M50KEoeSU= diff --git a/pkg/backends/ibmsecretsmanager.go b/pkg/backends/ibmsecretsmanager.go index c6d34f8c..41bd5775 100644 --- a/pkg/backends/ibmsecretsmanager.go +++ b/pkg/backends/ibmsecretsmanager.go @@ -6,7 +6,7 @@ import ( "sync" "github.com/IBM/go-sdk-core/v5/core" - ibmsm "github.com/IBM/secrets-manager-go-sdk/secretsmanagerv2" + ibmsm "github.com/IBM/secrets-manager-go-sdk/v2/secretsmanagerv2" "github.com/argoproj-labs/argocd-vault-plugin/pkg/types" "github.com/argoproj-labs/argocd-vault-plugin/pkg/utils" ) @@ -86,6 +86,15 @@ func (m IBMSecretMetadata) GetMetadata() (map[string]string, error) { "type": *v.SecretType, }, nil } + case *ibmsm.ServiceCredentialsSecretMetadata: + { + return map[string]string{ + "name": *v.Name, + "id": *v.ID, + "groupId": *v.SecretGroupID, + "type": *v.SecretType, + }, nil + } default: return nil, fmt.Errorf("Unknown secret type %T encountered", v) } @@ -165,6 +174,17 @@ func (d IBMSecretData) GetSecret() (map[string]interface{}, error) { result[k] = v } } + case *ibmsm.ServiceCredentialsSecret: + { + if v.Credentials != nil { + if v.Credentials.Apikey != nil { + result["apikey"] = *v.Credentials.Apikey + } + for k, v := range v.Credentials.GetProperties() { + result[k] = v + } + } + } default: { return nil, fmt.Errorf("Unsupported secret type %T encountered. This should be impossible", v) @@ -252,7 +272,6 @@ func (d IBMVersionedSecretData) GetSecret() (map[string]interface{}, error) { if *v.PayloadAvailable { result["api_key"] = *v.ApiKey } - return nil, fmt.Errorf("Payload unavailable for secret %s", *v.ID) } case *ibmsm.KVSecretVersion: { @@ -262,6 +281,17 @@ func (d IBMVersionedSecretData) GetSecret() (map[string]interface{}, error) { } } } + case *ibmsm.ServiceCredentialsSecretVersion: + { + if *v.PayloadAvailable { + if v.Credentials.Apikey != nil { + result["apikey"] = *v.Credentials.Apikey + } + for k, v := range v.Credentials.GetProperties() { + result[k] = v + } + } + } default: { return nil, fmt.Errorf("Unsupported secret type %T encountered. This should be impossible", v) diff --git a/pkg/backends/ibmsecretsmanager_test.go b/pkg/backends/ibmsecretsmanager_test.go index 613f3352..1472823a 100644 --- a/pkg/backends/ibmsecretsmanager_test.go +++ b/pkg/backends/ibmsecretsmanager_test.go @@ -8,7 +8,7 @@ import ( "testing" "github.com/IBM/go-sdk-core/v5/core" - ibmsm "github.com/IBM/secrets-manager-go-sdk/secretsmanagerv2" + ibmsm "github.com/IBM/secrets-manager-go-sdk/v2/secretsmanagerv2" "github.com/argoproj-labs/argocd-vault-plugin/pkg/backends" "github.com/argoproj-labs/argocd-vault-plugin/pkg/types" ) @@ -108,6 +108,7 @@ func (m *MockIBMSMClient) ListSecrets(listAllSecretsOptions *ibmsm.ListSecretsOp ctype := "public_cert" itype := "iam_credentials" ktype := "kv" + sctype := "service_credentials" smallGroupSecrets := []ibmsm.SecretMetadataIntf{ &ibmsm.ArbitrarySecretMetadata{ Name: &name, @@ -139,6 +140,12 @@ func (m *MockIBMSMClient) ListSecrets(listAllSecretsOptions *ibmsm.ListSecretsOp SecretGroupID: &smallGroup, ID: &ktype, }, + &ibmsm.ServiceCredentialsSecretMetadata{ + Name: &name, + SecretType: &sctype, + SecretGroupID: &smallGroup, + ID: &sctype, + }, } defaultGroup := "default" @@ -231,6 +238,22 @@ func (m *MockIBMSMClient) GetSecret(getSecretOptions *ibmsm.GetSecretOptions) (r ID: &id, Data: payload, }, nil, nil + } else if *getSecretOptions.ID == "service_credentials" { + name := "my-secret" + id := "service_credentials" + api_key := "123456" + credentials := &ibmsm.ServiceCredentialsSecretCredentials{ + Apikey: &api_key, + } + credentials.SetProperty("authentication", map[string]interface{}{ + "username": "user", + "password": "pass", + }) + return &ibmsm.ServiceCredentialsSecret{ + Name: &name, + ID: &id, + Credentials: credentials, + }, nil, nil } else { name := "my-secret" id := "username_password" @@ -250,18 +273,37 @@ func (m *MockIBMSMClient) GetSecretVersion(getSecretOptions *ibmsm.GetSecretVers m.GetSecretVersionCalledWith = getSecretOptions m.GetSecretVersionCallCount += 1 m.GetSecretLock.Unlock() - cert1 := "dummy certificate" - key := "dummy private key" - cert2 := "dummy intermediate certificate" - id := "public_cert" - yes := true - return &ibmsm.PublicCertificateVersion{ - ID: &id, - PayloadAvailable: &yes, - Certificate: &cert1, - PrivateKey: &key, - Intermediate: &cert2, - }, nil, nil + if *getSecretOptions.SecretID == "service_credentials" { + id := "service_credentials" + payload := true + api_key := "old-123456" + credentials := &ibmsm.ServiceCredentialsSecretCredentials{ + Apikey: &api_key, + } + credentials.SetProperty("authentication", map[string]interface{}{ + "username": "old-user", + "password": "old-pass", + }) + return &ibmsm.ServiceCredentialsSecretVersion{ + ID: &id, + Credentials: credentials, + PayloadAvailable: &payload, + }, nil, nil + } else { + cert1 := "dummy certificate" + key := "dummy private key" + cert2 := "dummy intermediate certificate" + id := "public_cert" + yes := true + return &ibmsm.PublicCertificateVersion{ + ID: &id, + PayloadAvailable: &yes, + Certificate: &cert1, + PrivateKey: &key, + Intermediate: &cert2, + }, nil, nil + } + } func TestIBMSecretsManagerGetSecrets(t *testing.T) { @@ -741,6 +783,32 @@ func TestIBMSecretsManagerSecretLookup(t *testing.T) { GetIndividualSecretTest(t, "ibmcloud/iam_credentials/secrets/groups/small-group/my-secret", "doesnotexist", "", nil) }) + t.Run("Retrieves payload of service credentials secret", func(t *testing.T) { + expected := map[string]interface{}{ + "apikey": "123456", + "authentication": map[string]interface{}{ + "username": "user", + "password": "pass", + }, + } + GetSecretsTest(t, "ibmcloud/service_credentials/secrets/groups/small-group/my-secret", "", expected) + GetIndividualSecretTest(t, "ibmcloud/service_credentials/secrets/groups/small-group/my-secret", "credentials", "", expected["credentials"]) + GetIndividualSecretTest(t, "ibmcloud/service_credentials/secrets/groups/small-group/my-secret", "doesnotexist", "", nil) + }) + + t.Run("Retrieves payload of service credentials secret (versioned)", func(t *testing.T) { + expected := map[string]interface{}{ + "apikey": "old-123456", + "authentication": map[string]interface{}{ + "username": "old-user", + "password": "old-pass", + }, + } + GetSecretsTest(t, "ibmcloud/service_credentials/secrets/groups/small-group/my-secret", "123", expected) + GetIndividualSecretTest(t, "ibmcloud/service_credentials/secrets/groups/small-group/my-secret", "credentials", "123", expected["credentials"]) + GetIndividualSecretTest(t, "ibmcloud/service_credentials/secrets/groups/small-group/my-secret", "doesnotexist", "123", nil) + }) + t.Run("Retrieves payload of arbitrary secret", func(t *testing.T) { mock := MockIBMSMClient{} sm := backends.NewIBMSecretsManagerBackend(&mock) diff --git a/pkg/config/config.go b/pkg/config/config.go index bc896701..42f1cb66 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -15,7 +15,7 @@ import ( "github.com/Azure/azure-sdk-for-go/sdk/azidentity" delineasecretserver "github.com/DelineaXPM/tss-sdk-go/v2/server" "github.com/IBM/go-sdk-core/v5/core" - ibmsm "github.com/IBM/secrets-manager-go-sdk/secretsmanagerv2" + ibmsm "github.com/IBM/secrets-manager-go-sdk/v2/secretsmanagerv2" "github.com/argoproj-labs/argocd-vault-plugin/pkg/auth/vault" "github.com/argoproj-labs/argocd-vault-plugin/pkg/backends" "github.com/argoproj-labs/argocd-vault-plugin/pkg/kube"