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 062b21a09 Kamelet - Inject secret in Vaults - Azure Key Vault (#4798) 062b21a09 is described below commit 062b21a09a96d734e886d1e9a0e49f4769dbce72 Author: Andrea Cosentino <anco...@gmail.com> AuthorDate: Thu Oct 5 16:20:42 2023 +0200 Kamelet - Inject secret in Vaults - Azure Key Vault (#4798) * Kamelet - Inject secret in Vaults - Azure Key Vault Signed-off-by: Andrea Cosentino <anco...@gmail.com> * Kamelet - Inject secret in Vaults - Azure Key Vault Signed-off-by: Andrea Cosentino <anco...@gmail.com> --------- Signed-off-by: Andrea Cosentino <anco...@gmail.com> --- addons/vault/azure/azure_key_vault.go | 36 ++++++- addons/vault/azure/azure_key_vault_test.go | 110 ++++++++++++++++++++- docs/modules/traits/pages/aws-secrets-manager.adoc | 4 +- docs/modules/traits/pages/azure-key-vault.adoc | 8 +- resources/traits.yaml | 25 +++-- 5 files changed, 165 insertions(+), 18 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 } diff --git a/docs/modules/traits/pages/aws-secrets-manager.adoc b/docs/modules/traits/pages/aws-secrets-manager.adoc index 4a67be3c1..d221f8280 100644 --- a/docs/modules/traits/pages/aws-secrets-manager.adoc +++ b/docs/modules/traits/pages/aws-secrets-manager.adoc @@ -50,8 +50,8 @@ Syntax: [configmap\|secret]:name[/key], where name represents the resource name, | aws-secrets-manager.secret-key | string | 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). +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/docs/modules/traits/pages/azure-key-vault.adoc b/docs/modules/traits/pages/azure-key-vault.adoc index 48303a037..a5d8ffc69 100644 --- a/docs/modules/traits/pages/azure-key-vault.adoc +++ b/docs/modules/traits/pages/azure-key-vault.adoc @@ -51,7 +51,9 @@ The following configuration options are available: | azure-key-vault.client-secret | string -| 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). | azure-key-vault.vault-name | string @@ -83,7 +85,9 @@ The following configuration options are available: | azure-key-vault.blob-access-key | string -| 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). | azure-key-vault.blob-container-name | string diff --git a/resources/traits.yaml b/resources/traits.yaml index 826996269..5fdd4f75e 100755 --- a/resources/traits.yaml +++ b/resources/traits.yaml @@ -116,11 +116,11 @@ traits: (default key value = aws-access-key).' - name: secret-key type: string - 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)." + description: '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).' - name: region type: string description: The AWS Region to use @@ -179,7 +179,11 @@ traits: description: The Azure Client Id for accessing Key Vault - name: client-secret type: string - description: The Azure Client Secret for accessing Key Vault + description: '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).' - name: vault-name type: string description: The Azure Vault Name for accessing Key Vault @@ -207,8 +211,13 @@ traits: used to save checkpoint while consuming from Eventhub - name: blob-access-key type: string - description: If Refresh is enabled, the access key for Azure Storage Blob service - used to save checkpoint while consuming from Eventhub + description: '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).' - name: blob-container-name type: string description: If Refresh is enabled, the container name for Azure Storage Blob