Skip to content

Commit

Permalink
Kamelet - Inject secret in Vaults - Hashicorp Vault (#4799)
Browse files Browse the repository at this point in the history
Signed-off-by: Andrea Cosentino <[email protected]>
  • Loading branch information
oscerd authored Oct 5, 2023
1 parent 062b21a commit f5c86b7
Show file tree
Hide file tree
Showing 4 changed files with 108 additions and 6 deletions.
20 changes: 18 additions & 2 deletions addons/vault/hashicorp/hashicorp_vault.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)

Expand All @@ -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"`
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down
84 changes: 82 additions & 2 deletions addons/vault/hashicorp/hashicorp_vault_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"

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 TestHashicorpVaultTraitApply(t *testing.T) {
Expand Down Expand Up @@ -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
}
4 changes: 3 additions & 1 deletion docs/modules/traits/pages/hashicorp-vault.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
6 changes: 5 additions & 1 deletion resources/traits.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit f5c86b7

Please sign in to comment.