Skip to content

Commit

Permalink
Kamelet - Inject secret in Vaults - AWS Secret Manager (apache#4794)
Browse files Browse the repository at this point in the history
Signed-off-by: Andrea Cosentino <[email protected]>
  • Loading branch information
oscerd authored Oct 5, 2023
1 parent c924a32 commit 09d149a
Show file tree
Hide file tree
Showing 5 changed files with 144 additions and 12 deletions.
36 changes: 32 additions & 4 deletions addons/vault/aws/aws_secrets_manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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"`
Expand Down Expand Up @@ -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
Expand All @@ -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)
Expand Down
96 changes: 94 additions & 2 deletions addons/vault/aws/aws_secrets_manager_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand All @@ -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) {
Expand Down Expand Up @@ -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
}
8 changes: 6 additions & 2 deletions docs/modules/traits/pages/aws-secrets-manager.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions pkg/resources/resources.go

Large diffs are not rendered by default.

12 changes: 10 additions & 2 deletions resources/traits.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit 09d149a

Please sign in to comment.