Skip to content

Commit

Permalink
vault: allow setting TriggerAuthentication defaults via ENV variables
Browse files Browse the repository at this point in the history
Signed-off-by: Bojan Zelic <[email protected]>
  • Loading branch information
BojanZelic committed Nov 21, 2023
1 parent 1c560ae commit 9c024a3
Show file tree
Hide file tree
Showing 7 changed files with 105 additions and 8 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ Here is an overview of all new **experimental** features:
- **General**: Support TriggerAuthentication properties from ConfigMap ([#4830](https://github.com/kedacore/keda/issues/4830))
- **Hashicorp Vault**: Add support to get secret that needs write operation (e.g. pki) ([#5067](https://github.com/kedacore/keda/issues/5067))
- **Hashicorp Vault**: Fix operator panic when spec.hashiCorpVault.credential.serviceAccount is not set ([#4964](https://github.com/kedacore/keda/issues/4964))
- **Hashicorp Vault**: Allow setting vault TriggerAuthentication defaults via ENV variables ([#4965](https://github.com/kedacore/keda/issues/4965))
- **Kafka Scaler**: Ability to set upper bound to the number of partitions with lag ([#3997](https://github.com/kedacore/keda/issues/3997))
- **Kafka Scaler**: Add more logging to check Sarama DescribeTopics method ([#5102](https://github.com/kedacore/keda/issues/5102))
- **Kafka Scaler**: Add support for Kerberos authentication (SASL / GSSAPI) ([#4836](https://github.com/kedacore/keda/issues/4836))
Expand Down
8 changes: 6 additions & 2 deletions apis/keda/v1alpha1/triggerauthentication_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -170,9 +170,13 @@ type AuthEnvironment struct {

// HashiCorpVault is used to authenticate using Hashicorp Vault
type HashiCorpVault struct {
Address string `json:"address"`
Secrets []VaultSecret `json:"secrets"`

// +optional
Address string `json:"address"`

// +optional
Authentication VaultAuthentication `json:"authentication"`
Secrets []VaultSecret `json:"secrets"`

// +optional
Namespace string `json:"namespace,omitempty"`
Expand Down
2 changes: 0 additions & 2 deletions config/crd/bases/keda.sh_clustertriggerauthentications.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -233,8 +233,6 @@ spec:
type: object
type: array
required:
- address
- authentication
- secrets
type: object
podIdentity:
Expand Down
2 changes: 0 additions & 2 deletions config/crd/bases/keda.sh_triggerauthentications.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -232,8 +232,6 @@ spec:
type: object
type: array
required:
- address
- authentication
- secrets
type: object
podIdentity:
Expand Down
38 changes: 38 additions & 0 deletions pkg/scaling/resolver/hashicorpvault_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import (
vaultapi "github.com/hashicorp/vault/api"

kedav1alpha1 "github.com/kedacore/keda/v2/apis/keda/v1alpha1"
"github.com/kedacore/keda/v2/pkg/util"
)

// HashicorpVaultHandler is specification of Hashi Corp Vault
Expand All @@ -43,9 +44,46 @@ func NewHashicorpVaultHandler(v *kedav1alpha1.HashiCorpVault) *HashicorpVaultHan
}
}

func (vh *HashicorpVaultHandler) SetFromEnvironment() {
if vh.vault.Address == "" {
vh.vault.Address = util.ResolveOsString("VAULT_ADDR", "")
}

if vh.vault.Authentication == "" {
vh.vault.Authentication = kedav1alpha1.VaultAuthentication(util.ResolveOsString("VAULT_AUTH", ""))
}

if vh.vault.Role == "" {
vh.vault.Role = util.ResolveOsString("VAULT_ROLE", "")
}

if vh.vault.Mount == "" {
vh.vault.Mount = util.ResolveOsString("VAULT_MOUNT", "")
}

if vh.vault.Credential == nil {
serviceAccount := util.ResolveOsString("VAULT_JWT_PATH", "")
vaultToken := util.ResolveOsString("VAULT_TOKEN", "")
if vaultToken != "" {
vh.vault.Credential = &kedav1alpha1.Credential{}
vh.vault.Credential.Token = vaultToken
}
if serviceAccount != "" {
vh.vault.Credential = &kedav1alpha1.Credential{}
vh.vault.Credential.ServiceAccount = serviceAccount
}
}

if vh.vault.Namespace == "" {
vh.vault.Namespace = util.ResolveOsString("VAULT_NAMESPACE", "")
}
}

// Initialize the Vault client
func (vh *HashicorpVaultHandler) Initialize(logger logr.Logger) error {
config := vaultapi.DefaultConfig()
vh.SetFromEnvironment()

client, err := vaultapi.NewClient(config)
if err != nil {
return err
Expand Down
52 changes: 50 additions & 2 deletions pkg/scaling/resolver/hashicorpvault_handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
"math"
"net/http"
"net/http/httptest"
"os"
"testing"

vaultapi "github.com/hashicorp/vault/api"
Expand Down Expand Up @@ -120,9 +121,13 @@ func TestGetPkiRequest(t *testing.T) {
func mockVault(t *testing.T) *httptest.Server {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
var data map[string]interface{}
var auth *vaultapi.SecretAuth
switch r.URL.Path {
case "/v1/auth/kubernetes/login":
auth = &vaultapi.SecretAuth{
ClientToken: "38fe9691-e623-7238-f618-c94d4e7bc674",
}
case "/v1/auth/token/lookup-self":

data = vaultTokenSelf
case "/v1/kv_v2/data/keda": //todo: more generic
data = kvV2SecretDataKeda
Expand Down Expand Up @@ -160,7 +165,7 @@ func mockVault(t *testing.T) *httptest.Server {
Data: data,
Renewable: false,
Warnings: nil,
Auth: nil,
Auth: auth,
WrapInfo: nil,
}
var out, _ = json.Marshal(secret)
Expand All @@ -169,6 +174,49 @@ func mockVault(t *testing.T) *httptest.Server {
return server
}

func TestHashicorpVaultLoadFromENVVariablesToken(t *testing.T) {
server := mockVault(t)
defer server.Close()

t.Setenv("VAULT_ADDR", server.URL)
t.Setenv("VAULT_MOUNT", "kubernetes")
t.Setenv("VAULT_AUTH", "token")
t.Setenv("VAULT_ROLE", "my-role")
t.Setenv("VAULT_NAMESPACE", "")
t.Setenv("VAULT_TOKEN", vaultTestToken)

vault := kedav1alpha1.HashiCorpVault{}
vaultHandler := NewHashicorpVaultHandler(&vault)

err := vaultHandler.Initialize(logf.Log.WithName("test"))
defer vaultHandler.Stop()
assert.Nil(t, err)
}

func TestHashicorpVaultLoadFromENVVariablesKubernetes(t *testing.T) {
server := mockVault(t)
defer server.Close()

t.Setenv("VAULT_ADDR", server.URL)
t.Setenv("VAULT_MOUNT", "kubernetes")
t.Setenv("VAULT_AUTH", "kubernetes")
t.Setenv("VAULT_ROLE", "my-role")
t.Setenv("VAULT_NAMESPACE", "")

tmpServiceToken, err := os.CreateTemp("", "token")
assert.Nil(t, err)
defer tmpServiceToken.Close()

t.Setenv("VAULT_JWT_PATH", tmpServiceToken.Name())

vault := kedav1alpha1.HashiCorpVault{}
vaultHandler := NewHashicorpVaultHandler(&vault)

err = vaultHandler.Initialize(logf.Log.WithName("test"))
defer vaultHandler.Stop()
assert.Nil(t, err)
}

func TestHashicorpVaultHandler_getSecretValue_specify_secret_type(t *testing.T) {
server := mockVault(t)
defer server.Close()
Expand Down
10 changes: 10 additions & 0 deletions pkg/util/env_resolver.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,16 @@ func ResolveOsEnvDuration(envName string) (*time.Duration, error) {
return nil, nil
}

func ResolveOsString(envName string, defaultvalue string) string {
valueStr, found := os.LookupEnv(envName)

if found && valueStr != "" {
return valueStr
}

return defaultvalue
}

// GetClusterObjectNamespace retrieves the cluster object namespace of KEDA, default is the namespace of KEDA Operator & Metrics Server
func GetClusterObjectNamespace() (string, error) {
// Check if a cached value is available.
Expand Down

0 comments on commit 9c024a3

Please sign in to comment.