You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by ac...@apache.org on 2023/10/05 12:04:31 UTC

[camel-k] branch 4743-azure-key-vault created (now 23f1fc2fc)

This is an automated email from the ASF dual-hosted git repository.

acosentino pushed a change to branch 4743-azure-key-vault
in repository https://gitbox.apache.org/repos/asf/camel-k.git


      at 23f1fc2fc Kamelet - Inject secret in Vaults - Azure Key Vault

This branch includes the following new commits:

     new 23f1fc2fc Kamelet - Inject secret in Vaults - Azure Key Vault

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.



[camel-k] 01/01: Kamelet - Inject secret in Vaults - Azure Key Vault

Posted by ac...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

acosentino pushed a commit to branch 4743-azure-key-vault
in repository https://gitbox.apache.org/repos/asf/camel-k.git

commit 23f1fc2fcdf06464ad1994c7e5c105bd8242dde3
Author: Andrea Cosentino <an...@gmail.com>
AuthorDate: Thu Oct 5 14:04:10 2023 +0200

    Kamelet - Inject secret in Vaults - Azure Key Vault
    
    Signed-off-by: Andrea Cosentino <an...@gmail.com>
---
 addons/vault/azure/azure_key_vault.go      |  36 ++++++++--
 addons/vault/azure/azure_key_vault_test.go | 110 ++++++++++++++++++++++++++++-
 2 files changed, 140 insertions(+), 6 deletions(-)

diff --git a/addons/vault/azure/azure_key_vault.go b/addons/vault/azure/azure_key_vault.go
index 76ecee985..5ab1ab0f6 100644
--- a/addons/vault/azure/azure_key_vault.go
+++ b/addons/vault/azure/azure_key_vault.go
@@ -18,8 +18,11 @@ limitations under the License.
 package azure
 
 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"
@@ -50,7 +53,9 @@ type Trait struct {
 	TenantID string `property:"tenant-id" json:"tenantId,omitempty"`
 	// The Azure Client Id for accessing Key Vault
 	ClientID string `property:"client-id" json:"clientId,omitempty"`
-	// The Azure Client Secret for accessing Key Vault
+	// The Azure Client Secret for accessing Key Vault. This could be a plain text or a configmap/secret.
+	// The content of the azure key vault client secret is expected to be a text containing a valid Client Secret.
+	// Syntax: [configmap|secret]:name[/key], where name represents the resource name, key optionally represents the resource key to be filtered (default key value = azure-key-vault-client-secret).
 	ClientSecret string `property:"client-secret" json:"clientSecret,omitempty"`
 	// The Azure Vault Name for accessing Key Vault
 	VaultName string `property:"vault-name" json:"vaultName,omitempty"`
@@ -66,7 +71,9 @@ type Trait struct {
 	EventhubConnectionString string `property:"eventhub-connection-string" json:"eventhubConnectionString,omitempty"`
 	// If Refresh is enabled, the account name for Azure Storage Blob service used to save checkpoint while consuming from Eventhub
 	BlobAccountName string `property:"blob-account-name" json:"blobAccountName,omitempty"`
-	// If Refresh is enabled, the access key for Azure Storage Blob service used to save checkpoint while consuming from Eventhub
+	// If Refresh is enabled, the access key for Azure Storage Blob service used to save checkpoint while consuming from Eventhub. This could be a plain text or a configmap/secret.
+	// The content of the azure key vault blob access key is expected to be a text containing a valid Access Key for Azure Storage Blob.
+	// Syntax: [configmap|secret]:name[/key], where name represents the resource name, key optionally represents the resource key to be filtered (default key value = azure-storage-blob-access-key).
 	BlobAccessKey string `property:"blob-access-key" json:"blobAccessKey,omitempty"`
 	// If Refresh is enabled, the container name for Azure Storage Blob service used to save checkpoint while consuming from Eventhub
 	BlobContainerName string `property:"blob-container-name" json:"blobContainerName,omitempty"`
@@ -104,6 +111,7 @@ func (t *azureKeyVaultTrait) Configure(environment *trait.Environment) (bool, er
 }
 
 func (t *azureKeyVaultTrait) 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.CapabilityAzureKeyVault)
 		// Deprecated
@@ -112,9 +120,30 @@ func (t *azureKeyVaultTrait) Apply(environment *trait.Environment) error {
 	}
 
 	if environment.IntegrationInRunningPhases() {
+		hits := rex.FindAllStringSubmatch(t.ClientSecret, -1)
+		if len(hits) >= 1 {
+			var res, _ = v1.DecodeValueSource(t.ClientSecret, "azure-key-vault-client-secret", "The Azure Key Vault Client Secret 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.azure.clientSecret"] = string([]byte(secretValue))
+			}
+		} else {
+			environment.ApplicationProperties["camel.vault.azure.clientSecret"] = t.ClientSecret
+		}
+		hits = rex.FindAllStringSubmatch(t.BlobAccessKey, -1)
+		if len(hits) >= 1 {
+			var res, _ = v1.DecodeValueSource(t.BlobAccessKey, "azure-storage-blob-access-key", "The Azure Storage Blob 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.azure.blobAccessKey"] = string([]byte(secretValue))
+			}
+		} else {
+			environment.ApplicationProperties["camel.vault.azure.blobAccessKey"] = t.BlobAccessKey
+		}
 		environment.ApplicationProperties["camel.vault.azure.tenantId"] = t.TenantID
 		environment.ApplicationProperties["camel.vault.azure.clientId"] = t.ClientID
-		environment.ApplicationProperties["camel.vault.azure.clientSecret"] = t.ClientSecret
 		environment.ApplicationProperties["camel.vault.azure.vaultName"] = t.VaultName
 		environment.ApplicationProperties["camel.vault.azure.refreshEnabled"] = strconv.FormatBool(*t.RefreshEnabled)
 		environment.ApplicationProperties["camel.main.context-reload-enabled"] = strconv.FormatBool(*t.ContextReloadEnabled)
@@ -125,7 +154,6 @@ func (t *azureKeyVaultTrait) Apply(environment *trait.Environment) error {
 		environment.ApplicationProperties["camel.vault.azure.eventhubConnectionString"] = t.EventhubConnectionString
 		environment.ApplicationProperties["camel.vault.azure.blobAccountName"] = t.BlobAccountName
 		environment.ApplicationProperties["camel.vault.azure.blobContainerName"] = t.BlobContainerName
-		environment.ApplicationProperties["camel.vault.azure.blobAccessKey"] = t.BlobAccessKey
 	}
 
 	return nil
diff --git a/addons/vault/azure/azure_key_vault_test.go b/addons/vault/azure/azure_key_vault_test.go
index 6de912212..388643605 100644
--- a/addons/vault/azure/azure_key_vault_test.go
+++ b/addons/vault/azure/azure_key_vault_test.go
@@ -20,6 +20,9 @@ package azure
 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 TestAzureKeyVaultTraitApply(t *testing.T) {
@@ -52,25 +56,127 @@ func TestAzureKeyVaultTraitApply(t *testing.T) {
 	assert.Equal(t, "my-vault", e.ApplicationProperties["camel.vault.azure.vaultName"])
 }
 
-func createEnvironment(t *testing.T, catalogGen func() (*camel.RuntimeCatalog, error)) *trait.Environment {
+func TestAzureKeyVaultTraitApplyWithConfigmapAndRefresh(t *testing.T) {
+	e := createEnvironment(t, camel.QuarkusCatalog, &corev1.ConfigMap{
+		ObjectMeta: metav1.ObjectMeta{
+			Namespace: "test",
+			Name:      "my-configmap1",
+		},
+		Data: map[string]string{
+			"azure-client-secret": "my-secret-key",
+		},
+	}, &corev1.ConfigMap{
+		ObjectMeta: metav1.ObjectMeta{
+			Namespace: "test",
+			Name:      "my-configmap2",
+		},
+		Data: map[string]string{
+			"azure-storage-blob-key": "my-access-key",
+		},
+	})
+	azure := NewAzureKeyVaultTrait()
+	secrets, _ := azure.(*azureKeyVaultTrait)
+	secrets.Enabled = pointer.Bool(true)
+	secrets.TenantID = "tenant-id"
+	secrets.ClientID = "client-id"
+	secrets.ClientSecret = "configmap:my-configmap1/azure-client-secret"
+	secrets.VaultName = "my-vault"
+	secrets.RefreshEnabled = pointer.Bool(true)
+	secrets.BlobAccessKey = "configmap:my-configmap2/azure-storage-blob-key"
+	secrets.BlobAccountName = "camel-k"
+	secrets.BlobContainerName = "camel-k-container"
+	ok, err := secrets.Configure(e)
+	assert.Nil(t, err)
+	assert.True(t, ok)
+
+	err = secrets.Apply(e)
+	assert.Nil(t, err)
+
+	assert.Equal(t, "client-id", e.ApplicationProperties["camel.vault.azure.clientId"])
+	assert.Equal(t, "my-secret-key", e.ApplicationProperties["camel.vault.azure.clientSecret"])
+	assert.Equal(t, "tenant-id", e.ApplicationProperties["camel.vault.azure.tenantId"])
+	assert.Equal(t, "my-vault", e.ApplicationProperties["camel.vault.azure.vaultName"])
+	assert.Equal(t, "camel-k", e.ApplicationProperties["camel.vault.azure.blobAccountName"])
+	assert.Equal(t, "camel-k-container", e.ApplicationProperties["camel.vault.azure.blobContainerName"])
+	assert.Equal(t, "my-access-key", e.ApplicationProperties["camel.vault.azure.blobAccessKey"])
+	assert.True(t, true, e.ApplicationProperties["camel.vault.azure.refreshEnabled"])
+}
+
+func TestAzureKeyVaultTraitApplyWithSecretAndRefresh(t *testing.T) {
+	e := createEnvironment(t, camel.QuarkusCatalog, &corev1.Secret{
+		ObjectMeta: metav1.ObjectMeta{
+			Namespace: "test",
+			Name:      "my-secret1",
+		},
+		Data: map[string][]byte{
+			"azure-client-secret": []byte("my-secret-key"),
+		},
+	}, &corev1.Secret{
+		ObjectMeta: metav1.ObjectMeta{
+			Namespace: "test",
+			Name:      "my-secret2",
+		},
+		Data: map[string][]byte{
+			"azure-storage-blob-key": []byte("my-access-key"),
+		},
+	})
+	azure := NewAzureKeyVaultTrait()
+	secrets, _ := azure.(*azureKeyVaultTrait)
+	secrets.Enabled = pointer.Bool(true)
+	secrets.TenantID = "tenant-id"
+	secrets.ClientID = "client-id"
+	secrets.ClientSecret = "secret:my-secret1/azure-client-secret"
+	secrets.VaultName = "my-vault"
+	secrets.RefreshEnabled = pointer.Bool(true)
+	secrets.BlobAccessKey = "secret:my-secret2/azure-storage-blob-key"
+	secrets.BlobAccountName = "camel-k"
+	secrets.BlobContainerName = "camel-k-container"
+	ok, err := secrets.Configure(e)
+	assert.Nil(t, err)
+	assert.True(t, ok)
+
+	err = secrets.Apply(e)
+	assert.Nil(t, err)
+
+	assert.Equal(t, "client-id", e.ApplicationProperties["camel.vault.azure.clientId"])
+	assert.Equal(t, "my-secret-key", e.ApplicationProperties["camel.vault.azure.clientSecret"])
+	assert.Equal(t, "tenant-id", e.ApplicationProperties["camel.vault.azure.tenantId"])
+	assert.Equal(t, "my-vault", e.ApplicationProperties["camel.vault.azure.vaultName"])
+	assert.Equal(t, "camel-k", e.ApplicationProperties["camel.vault.azure.blobAccountName"])
+	assert.Equal(t, "camel-k-container", e.ApplicationProperties["camel.vault.azure.blobContainerName"])
+	assert.Equal(t, "my-access-key", e.ApplicationProperties["camel.vault.azure.blobAccessKey"])
+	assert.True(t, true, e.ApplicationProperties["camel.vault.azure.refreshEnabled"])
+}
+
+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
 }