util: add vaultAuthNamespace option for Vault KMS

The new `vaultAuthNamespace` configuration parameter can be set to the
Vault Namespace where the authentication is setup in the service. Some
Hashicorp Vault deployments use sub-namespaces for their users/tenants,
with a 'root' namespace where the authentication is configured. This
requires passing of different Vault namespaces for different operations.

Example:
 - the Kubernetes Auth mechanism is configured for in the Vault
   Namespace called 'devops'
 - a user/tenant has a sub-namespace called 'devops/website' where the
   encryption passphrases can be placed in the key-value store

The configuration for this, then looks like:

    vaultAuthNamespace: devops
    vaultNamespace: devops/homepage

Note that Vault Namespaces are a feature of the Hashicorp Vault
Enterprise product, and not part of the Open Source version. This
prevents adding e2e tests that validate the Vault Namespace
configuration.

Signed-off-by: Niels de Vos <ndevos@redhat.com>
(cherry picked from commit f2d5c2e0df)
This commit is contained in:
Niels de Vos 2021-07-30 09:53:27 +02:00 committed by mergify[bot]
parent a962cccd0a
commit b866bd491c
4 changed files with 43 additions and 4 deletions

View File

@ -52,6 +52,30 @@ data:
} }
} }
}, },
"vault-tenant-sa-ns-test": {
"encryptionKMSType": "vaulttenantsa",
"vaultAddress": "http://vault.default.svc.cluster.local:8200",
"vaultBackend": "kv-v2",
"vaultBackendPath": "shared-secrets",
"vaultAuthNamespace": "devops",
"vaultNamespace": "devops/homepage",
"vaultTLSServerName": "vault.default.svc.cluster.local",
"vaultCAVerify": "false",
"tenantConfigName": "ceph-csi-kms-config",
"tenantSAName": "ceph-csi-vault-sa",
"tenants": {
"webservers": {
"vaultAddress": "https://vault.example.com",
"vaultAuthNamespace": "webservers",
"vaultNamespace": "webservers/homepage",
"vaultCAVerify": "true"
},
"homepage-db": {
"vaultNamespace": "devops/homepage/database",
"tenantSAName": "storage-encryption-sa"
}
}
},
"secrets-metadata-test": { "secrets-metadata-test": {
"encryptionKMSType": "metadata" "encryptionKMSType": "metadata"
}, },

View File

@ -167,9 +167,14 @@ func (vc *vaultConnection) initConnection(config map[string]interface{}) error {
if errors.Is(err, errConfigOptionInvalid) { if errors.Is(err, errConfigOptionInvalid) {
return err return err
} }
vaultAuthNamespace := vaultNamespace // optional, same as vaultNamespace
err = setConfigString(&vaultAuthNamespace, config, "vaultAuthNamespace")
if errors.Is(err, errConfigOptionInvalid) {
return err
}
// set the option if the value was not invalid // set the option if the value was not invalid
if firstInit || !errors.Is(err, errConfigOptionMissing) { if firstInit || !errors.Is(err, errConfigOptionMissing) {
vaultConfig[api.EnvVaultNamespace] = vaultNamespace vaultConfig[api.EnvVaultNamespace] = vaultAuthNamespace
keyContext[loss.KeyVaultNamespace] = vaultNamespace keyContext[loss.KeyVaultNamespace] = vaultNamespace
} }

View File

@ -63,6 +63,7 @@ type standardVault struct {
VaultTLSServerName string `json:"VAULT_TLS_SERVER_NAME"` VaultTLSServerName string `json:"VAULT_TLS_SERVER_NAME"`
VaultClientCert string `json:"VAULT_CLIENT_CERT"` VaultClientCert string `json:"VAULT_CLIENT_CERT"`
VaultClientKey string `json:"VAULT_CLIENT_KEY"` VaultClientKey string `json:"VAULT_CLIENT_KEY"`
VaultAuthNamespace string `json:"VAULT_AUTH_NAMESPACE"`
VaultNamespace string `json:"VAULT_NAMESPACE"` VaultNamespace string `json:"VAULT_NAMESPACE"`
VaultSkipVerify string `json:"VAULT_SKIP_VERIFY"` VaultSkipVerify string `json:"VAULT_SKIP_VERIFY"`
} }
@ -76,6 +77,7 @@ type vaultTokenConf struct {
VaultTLSServerName string `json:"vaultTLSServerName"` VaultTLSServerName string `json:"vaultTLSServerName"`
VaultClientCertFromSecret string `json:"vaultClientCertFromSecret"` VaultClientCertFromSecret string `json:"vaultClientCertFromSecret"`
VaultClientCertKeyFromSecret string `json:"vaultClientCertKeyFromSecret"` VaultClientCertKeyFromSecret string `json:"vaultClientCertKeyFromSecret"`
VaultAuthNamespace string `json:"vaultAuthNamespace"`
VaultNamespace string `json:"vaultNamespace"` VaultNamespace string `json:"vaultNamespace"`
VaultCAVerify string `json:"vaultCAVerify"` VaultCAVerify string `json:"vaultCAVerify"`
} }
@ -88,6 +90,7 @@ func (v *vaultTokenConf) convertStdVaultToCSIConfig(s *standardVault) {
v.VaultCAFromSecret = s.VaultCACert v.VaultCAFromSecret = s.VaultCACert
v.VaultClientCertFromSecret = s.VaultClientCert v.VaultClientCertFromSecret = s.VaultClientCert
v.VaultClientCertKeyFromSecret = s.VaultClientKey v.VaultClientCertKeyFromSecret = s.VaultClientKey
v.VaultAuthNamespace = s.VaultAuthNamespace
v.VaultNamespace = s.VaultNamespace v.VaultNamespace = s.VaultNamespace
v.VaultTLSServerName = s.VaultTLSServerName v.VaultTLSServerName = s.VaultTLSServerName
@ -521,6 +524,8 @@ func isTenantConfigOption(opt string) bool {
case "vaultAddress": case "vaultAddress":
case "vaultBackend": case "vaultBackend":
case "vaultBackendPath": case "vaultBackendPath":
case "vaultAuthNamespace":
case "vaultNamespace":
case "vaultTLSServerName": case "vaultTLSServerName":
case "vaultCAFromSecret": case "vaultCAFromSecret":
case "vaultCAVerify": case "vaultCAVerify":

View File

@ -131,7 +131,8 @@ func TestStdVaultToCSIConfig(t *testing.T) {
"VAULT_TLS_SERVER_NAME":"vault.example.com", "VAULT_TLS_SERVER_NAME":"vault.example.com",
"VAULT_CLIENT_CERT":"", "VAULT_CLIENT_CERT":"",
"VAULT_CLIENT_KEY":"", "VAULT_CLIENT_KEY":"",
"VAULT_NAMESPACE":"a-department", "VAULT_AUTH_NAMESPACE":"devops",
"VAULT_NAMESPACE":"devops/homepage",
"VAULT_SKIP_VERIFY":"true" "VAULT_SKIP_VERIFY":"true"
}` }`
@ -161,7 +162,9 @@ func TestStdVaultToCSIConfig(t *testing.T) {
t.Errorf("unexpected value for VaultClientCertFromSecret: %s", v.VaultClientCertFromSecret) t.Errorf("unexpected value for VaultClientCertFromSecret: %s", v.VaultClientCertFromSecret)
case v.VaultClientCertKeyFromSecret != "": case v.VaultClientCertKeyFromSecret != "":
t.Errorf("unexpected value for VaultClientCertKeyFromSecret: %s", v.VaultClientCertKeyFromSecret) t.Errorf("unexpected value for VaultClientCertKeyFromSecret: %s", v.VaultClientCertKeyFromSecret)
case v.VaultNamespace != "a-department": case v.VaultAuthNamespace != "devops":
t.Errorf("unexpected value for VaultAuthNamespace: %s", v.VaultAuthNamespace)
case v.VaultNamespace != "devops/homepage":
t.Errorf("unexpected value for VaultNamespace: %s", v.VaultNamespace) t.Errorf("unexpected value for VaultNamespace: %s", v.VaultNamespace)
case v.VaultTLSServerName != "vault.example.com": case v.VaultTLSServerName != "vault.example.com":
t.Errorf("unexpected value for VaultTLSServerName: %s", v.VaultTLSServerName) t.Errorf("unexpected value for VaultTLSServerName: %s", v.VaultTLSServerName)
@ -181,7 +184,8 @@ func TestTransformConfig(t *testing.T) {
cm["VAULT_TLS_SERVER_NAME"] = "vault.example.com" cm["VAULT_TLS_SERVER_NAME"] = "vault.example.com"
cm["VAULT_CLIENT_CERT"] = "" cm["VAULT_CLIENT_CERT"] = ""
cm["VAULT_CLIENT_KEY"] = "" cm["VAULT_CLIENT_KEY"] = ""
cm["VAULT_NAMESPACE"] = "a-department" cm["VAULT_AUTH_NAMESPACE"] = "devops"
cm["VAULT_NAMESPACE"] = "devops/homepage"
cm["VAULT_SKIP_VERIFY"] = "true" // inverse of "vaultCAVerify" cm["VAULT_SKIP_VERIFY"] = "true" // inverse of "vaultCAVerify"
config, err := transformConfig(cm) config, err := transformConfig(cm)
@ -194,6 +198,7 @@ func TestTransformConfig(t *testing.T) {
assert.Equal(t, config["vaultTLSServerName"], cm["VAULT_TLS_SERVER_NAME"]) assert.Equal(t, config["vaultTLSServerName"], cm["VAULT_TLS_SERVER_NAME"])
assert.Equal(t, config["vaultClientCertFromSecret"], cm["VAULT_CLIENT_CERT"]) assert.Equal(t, config["vaultClientCertFromSecret"], cm["VAULT_CLIENT_CERT"])
assert.Equal(t, config["vaultClientCertKeyFromSecret"], cm["VAULT_CLIENT_KEY"]) assert.Equal(t, config["vaultClientCertKeyFromSecret"], cm["VAULT_CLIENT_KEY"])
assert.Equal(t, config["vaultAuthNamespace"], cm["VAULT_AUTH_NAMESPACE"])
assert.Equal(t, config["vaultNamespace"], cm["VAULT_NAMESPACE"]) assert.Equal(t, config["vaultNamespace"], cm["VAULT_NAMESPACE"])
assert.Equal(t, config["vaultCAVerify"], "false") assert.Equal(t, config["vaultCAVerify"], "false")
} }