This is an automated email from the ASF dual-hosted git repository. acosentino pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/camel-k.git
The following commit(s) were added to refs/heads/main by this push: new 09d149a1c Kamelet - Inject secret in Vaults - AWS Secret Manager (#4794) 09d149a1c is described below commit 09d149a1c565c340e237d65989cdb904a08d73ad Author: Andrea Cosentino <anco...@gmail.com> AuthorDate: Thu Oct 5 12:57:26 2023 +0200 Kamelet - Inject secret in Vaults - AWS Secret Manager (#4794) Signed-off-by: Andrea Cosentino <anco...@gmail.com> --- addons/vault/aws/aws_secrets_manager.go | 36 +++++++- addons/vault/aws/aws_secrets_manager_test.go | 96 +++++++++++++++++++++- docs/modules/traits/pages/aws-secrets-manager.adoc | 8 +- pkg/resources/resources.go | 4 +- resources/traits.yaml | 12 ++- 5 files changed, 144 insertions(+), 12 deletions(-) diff --git a/addons/vault/aws/aws_secrets_manager.go b/addons/vault/aws/aws_secrets_manager.go index 8ad20e026..81aa15c1b 100644 --- a/addons/vault/aws/aws_secrets_manager.go +++ b/addons/vault/aws/aws_secrets_manager.go @@ -18,8 +18,11 @@ limitations under the License. package aws import ( + "regexp" "strconv" + "github.com/apache/camel-k/v2/pkg/util/kubernetes" + v1 "github.com/apache/camel-k/v2/pkg/apis/camel/v1" traitv1 "github.com/apache/camel-k/v2/pkg/apis/camel/v1/trait" "github.com/apache/camel-k/v2/pkg/trait" @@ -46,9 +49,13 @@ type Trait struct { traitv1.Trait `property:",squash"` // Enables automatic configuration of the trait. Auto *bool `property:"auto" json:"auto,omitempty"` - // The AWS Access Key to use + // The AWS Access Key to use. This could be a plain text or a configmap/secret + // The content of the aws access key is expected to be a text containing a valid AWS access key. + // Syntax: [configmap|secret]:name[/key], where name represents the resource name, key optionally represents the resource key to be filtered (default key value = aws-access-key). AccessKey string `property:"access-key" json:"accessKey,omitempty"` - // The AWS Secret Key to use + // The AWS Secret Key to use. This could be a plain text or a configmap/secret + // The content of the aws secret key is expected to be a text containing a valid AWS secret key. + // Syntax: [configmap|secret]:name[/key], where name represents the resource name, key optionally represents the resource key to be filtered (default key value = aws-secret-key). SecretKey string `property:"secret-key" json:"secretKey,omitempty"` // The AWS Region to use Region string `property:"region" json:"region,omitempty"` @@ -98,6 +105,7 @@ func (t *awsSecretsManagerTrait) Configure(environment *trait.Environment) (bool } func (t *awsSecretsManagerTrait) Apply(environment *trait.Environment) error { + rex := regexp.MustCompile(`^(configmap|secret):([a-zA-Z0-9][a-zA-Z0-9-]*)(/([a-zA-Z0-9].*))?$`) if environment.IntegrationInPhase(v1.IntegrationPhaseInitialization) { util.StringSliceUniqueAdd(&environment.Integration.Status.Capabilities, v1.CapabilityAwsSecretsManager) // Deprecated @@ -106,8 +114,28 @@ func (t *awsSecretsManagerTrait) Apply(environment *trait.Environment) error { } if environment.IntegrationInRunningPhases() { - environment.ApplicationProperties["camel.vault.aws.accessKey"] = t.AccessKey - environment.ApplicationProperties["camel.vault.aws.secretKey"] = t.SecretKey + hits := rex.FindAllStringSubmatch(t.AccessKey, -1) + if len(hits) >= 1 { + var res, _ = v1.DecodeValueSource(t.AccessKey, "aws-access-key", "The access Key provided is not valid") + if secretValue, err := kubernetes.ResolveValueSource(environment.Ctx, environment.Client, environment.Platform.Namespace, &res); err != nil { + return err + } else if secretValue != "" { + environment.ApplicationProperties["camel.vault.aws.accessKey"] = string([]byte(secretValue)) + } + } else { + environment.ApplicationProperties["camel.vault.aws.accessKey"] = t.AccessKey + } + hits = rex.FindAllStringSubmatch(t.SecretKey, -1) + if len(hits) >= 1 { + var res, _ = v1.DecodeValueSource(t.SecretKey, "aws-secret-key", "The secret Key provided is not valid") + if secretValue, err := kubernetes.ResolveValueSource(environment.Ctx, environment.Client, environment.Platform.Namespace, &res); err != nil { + return err + } else if secretValue != "" { + environment.ApplicationProperties["camel.vault.aws.secretKey"] = string([]byte(secretValue)) + } + } else { + environment.ApplicationProperties["camel.vault.aws.secretKey"] = t.SecretKey + } environment.ApplicationProperties["camel.vault.aws.region"] = t.Region environment.ApplicationProperties["camel.vault.aws.defaultCredentialsProvider"] = strconv.FormatBool(*t.UseDefaultCredentialsProvider) environment.ApplicationProperties["camel.vault.aws.refreshEnabled"] = strconv.FormatBool(*t.RefreshEnabled) diff --git a/addons/vault/aws/aws_secrets_manager_test.go b/addons/vault/aws/aws_secrets_manager_test.go index a1084c3b0..aeb1e86f4 100644 --- a/addons/vault/aws/aws_secrets_manager_test.go +++ b/addons/vault/aws/aws_secrets_manager_test.go @@ -20,6 +20,9 @@ package aws import ( "testing" + "github.com/apache/camel-k/v2/pkg/util/test" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/utils/pointer" @@ -28,6 +31,7 @@ import ( "github.com/apache/camel-k/v2/pkg/util/camel" "github.com/stretchr/testify/assert" + "k8s.io/apimachinery/pkg/runtime" ) func TestAwsSecretsManagerTraitApply(t *testing.T) { @@ -75,25 +79,113 @@ func TestAwsSecretsManagerTraitNoDefaultCreds(t *testing.T) { assert.Equal(t, "false", e.ApplicationProperties["camel.vault.aws.defaultCredentialsProvider"]) } -func createEnvironment(t *testing.T, catalogGen func() (*camel.RuntimeCatalog, error)) *trait.Environment { +func TestAwsSecretsManagerTraitWithSecrets(t *testing.T) { + e := createEnvironment(t, camel.QuarkusCatalog, &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "test", + Name: "my-secret1", + }, + Data: map[string][]byte{ + "aws-secret-key": []byte("my-secret-key"), + }, + }, &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "test", + Name: "my-secret2", + }, + Data: map[string][]byte{ + "aws-access-key": []byte("my-access-key"), + }, + }) + + aws := NewAwsSecretsManagerTrait() + secrets, _ := aws.(*awsSecretsManagerTrait) + secrets.Enabled = pointer.Bool(true) + secrets.Region = "eu-west-1" + secrets.AccessKey = "secret:my-secret2/aws-access-key" + secrets.SecretKey = "secret:my-secret1/aws-secret-key" + ok, err := secrets.Configure(e) + assert.Nil(t, err) + assert.True(t, ok) + + err = secrets.Apply(e) + assert.Nil(t, err) + + assert.Empty(t, e.ApplicationProperties["quarkus.jaeger.enabled"]) + assert.Equal(t, "eu-west-1", e.ApplicationProperties["camel.vault.aws.region"]) + assert.Equal(t, "my-access-key", e.ApplicationProperties["camel.vault.aws.accessKey"]) + assert.Equal(t, "my-secret-key", e.ApplicationProperties["camel.vault.aws.secretKey"]) + assert.Equal(t, "false", e.ApplicationProperties["camel.vault.aws.defaultCredentialsProvider"]) +} + +func TestAwsSecretsManagerTraitWithConfigMap(t *testing.T) { + e := createEnvironment(t, camel.QuarkusCatalog, &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "test", + Name: "my-configmap1", + }, + Data: map[string]string{ + "aws-secret-key": "my-secret-key", + }, + }, &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "test", + Name: "my-configmap2", + }, + Data: map[string]string{ + "aws-access-key": "my-access-key", + }, + }) + + aws := NewAwsSecretsManagerTrait() + secrets, _ := aws.(*awsSecretsManagerTrait) + secrets.Enabled = pointer.Bool(true) + secrets.Region = "eu-west-1" + secrets.AccessKey = "configmap:my-configmap2/aws-access-key" + secrets.SecretKey = "configmap:my-configmap1/aws-secret-key" + ok, err := secrets.Configure(e) + assert.Nil(t, err) + assert.True(t, ok) + + err = secrets.Apply(e) + assert.Nil(t, err) + + assert.Empty(t, e.ApplicationProperties["quarkus.jaeger.enabled"]) + assert.Equal(t, "eu-west-1", e.ApplicationProperties["camel.vault.aws.region"]) + assert.Equal(t, "my-access-key", e.ApplicationProperties["camel.vault.aws.accessKey"]) + assert.Equal(t, "my-secret-key", e.ApplicationProperties["camel.vault.aws.secretKey"]) + assert.Equal(t, "false", e.ApplicationProperties["camel.vault.aws.defaultCredentialsProvider"]) +} + +func createEnvironment(t *testing.T, catalogGen func() (*camel.RuntimeCatalog, error), objects ...runtime.Object) *trait.Environment { t.Helper() catalog, err := catalogGen() + client, _ := test.NewFakeClient(objects...) assert.Nil(t, err) e := trait.Environment{ CamelCatalog: catalog, ApplicationProperties: make(map[string]string), + Client: client, } it := v1.Integration{ ObjectMeta: metav1.ObjectMeta{ - Name: "test", + Namespace: "test", + Name: "test", }, Status: v1.IntegrationStatus{ Phase: v1.IntegrationPhaseDeploying, }, } + platform := v1.IntegrationPlatform{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "test", + Name: "test", + }, + } e.Integration = &it + e.Platform = &platform return &e } diff --git a/docs/modules/traits/pages/aws-secrets-manager.adoc b/docs/modules/traits/pages/aws-secrets-manager.adoc index 03383744a..4a67be3c1 100644 --- a/docs/modules/traits/pages/aws-secrets-manager.adoc +++ b/docs/modules/traits/pages/aws-secrets-manager.adoc @@ -43,11 +43,15 @@ The following configuration options are available: | aws-secrets-manager.access-key | string -| The AWS Access Key to use +| The AWS Access Key to use. This could be a plain text or a configmap/secret +The content of the aws access key is expected to be a text containing a valid AWS access key. +Syntax: [configmap\|secret]:name[/key], where name represents the resource name, key optionally represents the resource key to be filtered (default key value = aws-access-key). | aws-secrets-manager.secret-key | string -| The AWS Secret Key to use +| The AWS Secret Key to use. This could be a plain text or a configmap/secret + // The content of the aws secret key is expected to be a text containing a valid AWS secret key. + // Syntax: [configmap\|secret]:name[/key], where name represents the resource name, key optionally represents the resource key to be filtered (default key value = aws-secret-key). | aws-secrets-manager.region | string diff --git a/pkg/resources/resources.go b/pkg/resources/resources.go index 3cc8b4665..ebf4104c9 100644 --- a/pkg/resources/resources.go +++ b/pkg/resources/resources.go @@ -625,9 +625,9 @@ var assets = func() http.FileSystem { "/traits.yaml": &vfsgen۰CompressedFileInfo{ name: "traits.yaml", modTime: time.Time{}, - uncompressedSize: 70800, + uncompressedSize: 71502, - compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xec\xbd\xfb\x77\x1b\xb9\x91\x30\xfa\xbb\xff\x0a\x1c\xce\xdd\x23\xc9\x97\x0f\x79\xb2\xc9\xce\x6a\xe3\xec\xd5\xd8\x9e\x44\x19\x3f\x74\x2d\xcd\x64\x73\xfc\xf9\x84\x60\x37\x48\x62\xd4\x04\x3a\x00\x5a\x32\xe7\xcb\xf7\xbf\x7f\x07\x55\x85\x47\x37\x9b\x22\x65\x4b\xb3\xd1\x26\x93\x73\x62\x91\xec\x2e\x14\x0a\x85\x42\xa1\x9e\x5f\xb1\xd1\xfd\xfd\xf7\xe4\x2b\xf6\x5a\x16\x42\x59\x51\x32\xa7\x99\x5b\x0a\x76\x5a\xf3\x62\x29\xd8\x85\x [...] + compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xec\xbd\x7b\x77\x1b\xb9\x91\x38\xfa\xbf\x3f\x05\x0e\xe7\xee\x91\xe4\xcb\x87\x3c\xd9\x64\x67\xb5\x71\xf6\x6a\x6c\x4f\xa2\x8c\x1f\xba\x96\x66\xb2\x39\x5e\x9f\x10\xec\x06\x49\x8c\x9a\x40\x07\x40\x4b\xe6\xdc\xdc\xef\xfe\x3b\xa8\x2a\x3c\xba\xd9\x14\x29\x5b\x9a\x8d\x36\x99\x9c\x13\x8b\x64\x37\x50\x28\x14\x0a\xf5\xae\xaf\xd8\xe8\xfe\xfe\x7b\xf2\x15\x7b\x2d\x0b\xa1\xac\x28\x99\xd3\xcc\x2d\x05\x3b\xad\x79\xb1\x14\xec\x42\xcf\x [...] }, } fs["/"].(*vfsgen۰DirInfo).entries = []os.FileInfo{ diff --git a/resources/traits.yaml b/resources/traits.yaml index d2d95bed7..826996269 100755 --- a/resources/traits.yaml +++ b/resources/traits.yaml @@ -109,10 +109,18 @@ traits: description: Enables automatic configuration of the trait. - name: access-key type: string - description: The AWS Access Key to use + description: 'The AWS Access Key to use. This could be a plain text or a configmap/secret + The content of the aws access key is expected to be a text containing a valid + AWS access key. Syntax: [configmap|secret]:name[/key], where name represents + the resource name, key optionally represents the resource key to be filtered + (default key value = aws-access-key).' - name: secret-key type: string - description: The AWS Secret Key to use + description: "The AWS Secret Key to use. This could be a plain text or a configmap/secret + \t// The content of the aws secret key is expected to be a text containing a + valid AWS secret key. \t// Syntax: [configmap|secret]:name[/key], where name + represents the resource name, key optionally represents the resource key to + be filtered (default key value = aws-secret-key)." - name: region type: string description: The AWS Region to use