Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Kamelet - Inject secret in Vaults - AWS Secret Manager #4794

Merged
merged 1 commit into from
Oct 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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) {
oscerd marked this conversation as resolved.
Show resolved Hide resolved
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