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 - Azure Key Vault #4798

Merged
merged 2 commits 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/azure/azure_key_vault.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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"`
Expand All @@ -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"`
Expand Down Expand Up @@ -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
Expand All @@ -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)
Expand All @@ -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
Expand Down
110 changes: 108 additions & 2 deletions addons/vault/azure/azure_key_vault_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"

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 TestAzureKeyVaultTraitApply(t *testing.T) {
Expand All @@ -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
}
4 changes: 2 additions & 2 deletions docs/modules/traits/pages/aws-secrets-manager.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
8 changes: 6 additions & 2 deletions docs/modules/traits/pages/azure-key-vault.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
25 changes: 17 additions & 8 deletions resources/traits.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
Loading