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 f5c86b79d Kamelet - Inject secret in Vaults - Hashicorp Vault (#4799) f5c86b79d is described below commit f5c86b79d320ea719b645545d4923c1c9e43c6f5 Author: Andrea Cosentino <anco...@gmail.com> AuthorDate: Thu Oct 5 17:04:29 2023 +0200 Kamelet - Inject secret in Vaults - Hashicorp Vault (#4799) Signed-off-by: Andrea Cosentino <anco...@gmail.com> --- addons/vault/hashicorp/hashicorp_vault.go | 20 +++++- addons/vault/hashicorp/hashicorp_vault_test.go | 84 +++++++++++++++++++++++++- docs/modules/traits/pages/hashicorp-vault.adoc | 4 +- resources/traits.yaml | 6 +- 4 files changed, 108 insertions(+), 6 deletions(-) diff --git a/addons/vault/hashicorp/hashicorp_vault.go b/addons/vault/hashicorp/hashicorp_vault.go index e3eefed8e..058b18cc9 100644 --- a/addons/vault/hashicorp/hashicorp_vault.go +++ b/addons/vault/hashicorp/hashicorp_vault.go @@ -18,10 +18,13 @@ limitations under the License. package hashicorp import ( + "regexp" + 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" "github.com/apache/camel-k/v2/pkg/util" + "github.com/apache/camel-k/v2/pkg/util/kubernetes" "k8s.io/utils/pointer" ) @@ -46,7 +49,9 @@ type Trait struct { Port string `property:"port" json:"port,omitempty"` // The Hashicorp engine to use Engine string `property:"engine" json:"engine,omitempty"` - // The token to access Hashicorp Vault + // The token to access Hashicorp Vault. This could be a plain text or a configmap/secret + // The content of the hashicorp vault token is expected to be a text containing a valid Hashicorp Vault Token. + // Syntax: [configmap|secret]:name[/key], where name represents the resource name, key optionally represents the resource key to be filtered (default key value = hashicorp-vault-token). Token string `property:"token" json:"token,omitempty"` // The scheme to access Hashicorp Vault Scheme string `property:"scheme" json:"scheme,omitempty"` @@ -76,6 +81,7 @@ func (t *hashicorpVaultTrait) Configure(environment *trait.Environment) (bool, e } func (t *hashicorpVaultTrait) 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.CapabilityHashicorpVault) // Deprecated @@ -84,7 +90,17 @@ func (t *hashicorpVaultTrait) Apply(environment *trait.Environment) error { } if environment.IntegrationInRunningPhases() { - environment.ApplicationProperties["camel.vault.hashicorp.token"] = t.Token + hits := rex.FindAllStringSubmatch(t.Token, -1) + if len(hits) >= 1 { + var res, _ = v1.DecodeValueSource(t.Token, "hashicorp-vault-token", "The Hashicorp Vault Token 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.hashicorp.token"] = string([]byte(secretValue)) + } + } else { + environment.ApplicationProperties["camel.vault.hashicorp.token"] = t.Token + } environment.ApplicationProperties["camel.vault.hashicorp.host"] = t.Host environment.ApplicationProperties["camel.vault.hashicorp.port"] = t.Port environment.ApplicationProperties["camel.vault.hashicorp.engine"] = t.Engine diff --git a/addons/vault/hashicorp/hashicorp_vault_test.go b/addons/vault/hashicorp/hashicorp_vault_test.go index 7333dffe1..86cb70229 100644 --- a/addons/vault/hashicorp/hashicorp_vault_test.go +++ b/addons/vault/hashicorp/hashicorp_vault_test.go @@ -20,6 +20,9 @@ package hashicorp 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 TestHashicorpVaultTraitApply(t *testing.T) { @@ -55,25 +59,101 @@ func TestHashicorpVaultTraitApply(t *testing.T) { assert.Equal(t, "http", e.ApplicationProperties["camel.vault.hashicorp.scheme"]) } -func createEnvironment(t *testing.T, catalogGen func() (*camel.RuntimeCatalog, error)) *trait.Environment { +func TestHashicorpVaultTraitWithSecretApply(t *testing.T) { + e := createEnvironment(t, camel.QuarkusCatalog, &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "test", + Name: "my-secret1", + }, + Data: map[string][]byte{ + "hashicorp-vault-token": []byte("my-hashicorp-vault-token"), + }, + }) + hashicorp := NewHashicorpVaultTrait() + secrets, _ := hashicorp.(*hashicorpVaultTrait) + secrets.Enabled = pointer.Bool(true) + secrets.Engine = "test" + secrets.Token = "secret:my-secret1/hashicorp-vault-token" + secrets.Host = "localhost" + secrets.Port = "9091" + secrets.Scheme = "http" + 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, "test", e.ApplicationProperties["camel.vault.hashicorp.engine"]) + assert.Equal(t, "my-hashicorp-vault-token", e.ApplicationProperties["camel.vault.hashicorp.token"]) + assert.Equal(t, "localhost", e.ApplicationProperties["camel.vault.hashicorp.host"]) + assert.Equal(t, "9091", e.ApplicationProperties["camel.vault.hashicorp.port"]) + assert.Equal(t, "http", e.ApplicationProperties["camel.vault.hashicorp.scheme"]) +} + +func TestHashicorpVaultTraitWithConfigMapApply(t *testing.T) { + e := createEnvironment(t, camel.QuarkusCatalog, &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "test", + Name: "my-configmap1", + }, + Data: map[string]string{ + "hashicorp-vault-token": "my-hashicorp-vault-token", + }, + }) + hashicorp := NewHashicorpVaultTrait() + secrets, _ := hashicorp.(*hashicorpVaultTrait) + secrets.Enabled = pointer.Bool(true) + secrets.Engine = "test" + secrets.Token = "configmap:my-configmap1/hashicorp-vault-token" + secrets.Host = "localhost" + secrets.Port = "9091" + secrets.Scheme = "http" + 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, "test", e.ApplicationProperties["camel.vault.hashicorp.engine"]) + assert.Equal(t, "my-hashicorp-vault-token", e.ApplicationProperties["camel.vault.hashicorp.token"]) + assert.Equal(t, "localhost", e.ApplicationProperties["camel.vault.hashicorp.host"]) + assert.Equal(t, "9091", e.ApplicationProperties["camel.vault.hashicorp.port"]) + assert.Equal(t, "http", e.ApplicationProperties["camel.vault.hashicorp.scheme"]) +} + +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/hashicorp-vault.adoc b/docs/modules/traits/pages/hashicorp-vault.adoc index ea64b34e8..10c0cb89d 100644 --- a/docs/modules/traits/pages/hashicorp-vault.adoc +++ b/docs/modules/traits/pages/hashicorp-vault.adoc @@ -51,7 +51,9 @@ The following configuration options are available: | hashicorp-vault.token | string -| The token to access Hashicorp Vault +| The token to access Hashicorp Vault. This could be a plain text or a configmap/secret +The content of the hashicorp vault token is expected to be a text containing a valid Hashicorp Vault Token. +Syntax: [configmap\|secret]:name[/key], where name represents the resource name, key optionally represents the resource key to be filtered (default key value = hashicorp-vault-token). | hashicorp-vault.scheme | string diff --git a/resources/traits.yaml b/resources/traits.yaml index 5fdd4f75e..7484e147e 100755 --- a/resources/traits.yaml +++ b/resources/traits.yaml @@ -650,7 +650,11 @@ traits: description: The Hashicorp engine to use - name: token type: string - description: The token to access Hashicorp Vault + description: 'The token to access Hashicorp Vault. This could be a plain text + or a configmap/secret The content of the hashicorp vault token is expected to + be a text containing a valid Hashicorp Vault Token. Syntax: [configmap|secret]:name[/key], + where name represents the resource name, key optionally represents the resource + key to be filtered (default key value = hashicorp-vault-token).' - name: scheme type: string description: The scheme to access Hashicorp Vault