Skip to content

Commit

Permalink
Adding acceptance test for Vault Namespaces (#1171)
Browse files Browse the repository at this point in the history
* Adding acceptance test for Vault Namespaces

* Removing unused function that was added during development

* fixing linting

* removing remnant from test

* Apply suggestions from code review

Co-authored-by: Iryna Shustava <[email protected]>

* adding consul-ci context to acceptance and acceptance-tproxy jobs so that VAULT_LICENSE is available.

* FIXING vault license to be at VAULT_LICENSE instead of VAULT_ENT_LICENSE

Co-authored-by: Iryna Shustava <[email protected]>
  • Loading branch information
jmurret and ishustava authored Apr 21, 2022
1 parent 3e7ce3a commit 880846e
Show file tree
Hide file tree
Showing 8 changed files with 204 additions and 10 deletions.
2 changes: 2 additions & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -900,9 +900,11 @@ workflows:
- build-distros-amd64
# Run acceptance tests using the docker image built for the control plane
- acceptance:
context: consul-ci
requires:
- dev-upload-docker
- acceptance-tproxy:
context: consul-ci
requires:
- dev-upload-docker
nightly-acceptance-tests:
Expand Down
16 changes: 13 additions & 3 deletions acceptance/framework/vault/vault_cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,12 +129,16 @@ func (v *VaultCluster) SetupVaultClient(t *testing.T) *vapi.Client {
}

// bootstrap sets up Kubernetes auth method and enables secrets engines.
func (v *VaultCluster) bootstrap(t *testing.T) {
func (v *VaultCluster) bootstrap(t *testing.T, vaultNamespace string) {
if !v.serverEnabled() {
v.logger.Logf(t, "skipping bootstrapping Vault because Vault server is not enabled")
return
}
v.vaultClient = v.SetupVaultClient(t)
if vaultNamespace != "" {
createNamespace(t, v.vaultClient, vaultNamespace)
v.vaultClient.SetNamespace(vaultNamespace)
}

// Enable the KV-V2 Secrets engine.
err := v.vaultClient.Sys().Mount("consul", &vapi.MountInput{
Expand Down Expand Up @@ -188,7 +192,7 @@ func (v *VaultCluster) ConfigureAuthMethod(t *testing.T, vaultClient *vapi.Clien
}

// Create installs Vault via Helm and then calls bootstrap to initialize it.
func (v *VaultCluster) Create(t *testing.T, ctx environment.TestContext) {
func (v *VaultCluster) Create(t *testing.T, ctx environment.TestContext, vaultNamespace string) {
t.Helper()

// Make sure we delete the cluster if we receive an interrupt signal and
Expand All @@ -211,7 +215,7 @@ func (v *VaultCluster) Create(t *testing.T, ctx environment.TestContext) {
k8s.WaitForAllPodsToBeReady(t, v.kubernetesClient, v.helmOptions.KubectlOptions.Namespace, v.releaseLabelSelector())

// Now call bootstrap().
v.bootstrap(t)
v.bootstrap(t, vaultNamespace)
}

// Destroy issues a helm delete and deletes the PVC + any helm secrets related to the release that are leftover.
Expand Down Expand Up @@ -403,3 +407,9 @@ func (v *VaultCluster) initAndUnseal(t *testing.T) {
}, metav1.CreateOptions{})
require.NoError(t, err)
}

// CreateNamespace creates a Vault namespace given the namespacePath.
func createNamespace(t *testing.T, vaultClient *vapi.Client, namespacePath string) {
_, err := vaultClient.Logical().Write(fmt.Sprintf("sys/namespaces/%s", namespacePath), nil)
require.NoError(t, err)
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ func TestSnapshotAgent_Vault(t *testing.T) {
vaultReleaseName := helpers.RandomName()

vaultCluster := vault.NewVaultCluster(t, ctx, cfg, vaultReleaseName, nil)
vaultCluster.Create(t, ctx)
vaultCluster.Create(t, ctx, "")
// Vault is now installed in the cluster.

// Now fetch the Vault client so we can create the policies and secrets.
Expand Down
182 changes: 182 additions & 0 deletions acceptance/tests/vault/vault_namespaces_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
package vault

import (
"fmt"
"os"
"testing"

terratestLogger "github.com/gruntwork-io/terratest/modules/logger"
"github.com/hashicorp/consul-k8s/acceptance/framework/consul"
"github.com/hashicorp/consul-k8s/acceptance/framework/environment"
"github.com/hashicorp/consul-k8s/acceptance/framework/helpers"
"github.com/hashicorp/consul-k8s/acceptance/framework/k8s"
"github.com/hashicorp/consul-k8s/acceptance/framework/logger"
"github.com/hashicorp/consul-k8s/acceptance/framework/vault"
"github.com/stretchr/testify/require"
)

// TestVault_VaultNamespace installs Vault, configures a Vault namespace, and then bootstraps it
// with secrets, policies, and Kube Auth Method.
// It then configures Consul to use vault as the backend and checks that it works
// with the vault namespace.
func TestVault_VaultNamespace(t *testing.T) {
cfg := suite.Config()
ctx := suite.Environment().DefaultContext(t)
ns := ctx.KubectlOptions(t).Namespace
vaultNamespacePath := "test-namespace"
consulReleaseName := helpers.RandomName()
vaultReleaseName := helpers.RandomName()

k8sClient := environment.KubernetesClientFromOptions(t, ctx.KubectlOptions(t))
vaultLicenseSecretName := fmt.Sprintf("%s-enterprise-license", vaultReleaseName)
vaultLicenseSecretKey := "license"

vaultEnterpriseLicense := os.Getenv("VAULT_LICENSE")

logger.Log(t, "Creating secret for Vault license")
consul.CreateK8sSecret(t, k8sClient, cfg, ns, vaultLicenseSecretName, vaultLicenseSecretKey, vaultEnterpriseLicense)
vaultHelmvalues := map[string]string{
"server.image.repository": "docker.mirror.hashicorp.services/hashicorp/vault-enterprise",
"server.image.tag": "1.9.4-ent",
"server.enterpriseLicense.secretName": vaultLicenseSecretName,
"server.enterpriseLicense.secretKey": vaultLicenseSecretKey,
}
vaultCluster := vault.NewVaultCluster(t, ctx, cfg, vaultReleaseName, vaultHelmvalues)
vaultCluster.Create(t, ctx, vaultNamespacePath)
// Vault is now installed in the cluster.

// Now fetch the Vault client so we can create the policies and secrets.
vaultClient := vaultCluster.VaultClient(t)

gossipKey := vault.ConfigureGossipVaultSecret(t, vaultClient)

vault.CreateConnectCAPolicy(t, vaultClient, "dc1")
if cfg.EnableEnterprise {
vault.ConfigureEnterpriseLicenseVaultSecret(t, vaultClient, cfg)
}

bootstrapToken := vault.ConfigureACLTokenVaultSecret(t, vaultClient, "bootstrap")

serverPolicies := "gossip,connect-ca-dc1,server-cert-dc1,bootstrap-token"
if cfg.EnableEnterprise {
serverPolicies += ",license"
}
vault.ConfigureKubernetesAuthRole(t, vaultClient, consulReleaseName, ns, "kubernetes", "server", serverPolicies)
vault.ConfigureKubernetesAuthRole(t, vaultClient, consulReleaseName, ns, "kubernetes", "client", "gossip")
vault.ConfigureKubernetesAuthRole(t, vaultClient, consulReleaseName, ns, "kubernetes", "server-acl-init", "bootstrap-token")
vault.ConfigureConsulCAKubernetesAuthRole(t, vaultClient, ns, "kubernetes")

vault.ConfigurePKICA(t, vaultClient)
certPath := vault.ConfigurePKICertificates(t, vaultClient, consulReleaseName, ns, "dc1", "1h")

vaultCASecret := vault.CASecretName(vaultReleaseName)

consulHelmValues := map[string]string{
"server.extraVolumes[0].type": "secret",
"server.extraVolumes[0].name": vaultCASecret,
"server.extraVolumes[0].load": "false",

"connectInject.enabled": "true",
"connectInject.replicas": "1",
"controller.enabled": "true",

"global.secretsBackend.vault.enabled": "true",
"global.secretsBackend.vault.consulServerRole": "server",
"global.secretsBackend.vault.consulClientRole": "client",
"global.secretsBackend.vault.consulCARole": "consul-ca",
"global.secretsBackend.vault.manageSystemACLsRole": "server-acl-init",

"global.secretsBackend.vault.ca.secretName": vaultCASecret,
"global.secretsBackend.vault.ca.secretKey": "tls.crt",

"global.secretsBackend.vault.connectCA.address": vaultCluster.Address(),
"global.secretsBackend.vault.connectCA.rootPKIPath": "connect_root",
"global.secretsBackend.vault.connectCA.intermediatePKIPath": "dc1/connect_inter",
"global.secretsBackend.vault.connectCA.additionalConfig": fmt.Sprintf(`"{\"connect\": [{ \"ca_config\": [{ \"namespace\": \"%s\"}]}]}"`, vaultNamespacePath),

"global.secretsBackend.vault.agentAnnotations": fmt.Sprintf("\"vault.hashicorp.com/namespace\": \"%s\"", vaultNamespacePath),

"global.acls.manageSystemACLs": "true",
"global.acls.bootstrapToken.secretName": "consul/data/secret/bootstrap",
"global.acls.bootstrapToken.secretKey": "token",
"global.tls.enabled": "true",
"global.gossipEncryption.secretName": "consul/data/secret/gossip",
"global.gossipEncryption.secretKey": "gossip",

"ingressGateways.enabled": "true",
"ingressGateways.defaults.replicas": "1",
"terminatingGateways.enabled": "true",
"terminatingGateways.defaults.replicas": "1",

"server.serverCert.secretName": certPath,
"global.tls.caCert.secretName": "pki/cert/ca",
"global.tls.enableAutoEncrypt": "true",

// For sync catalog, it is sufficient to check that the deployment is running and ready
// because we only care that get-auto-encrypt-client-ca init container was able
// to talk to the Consul server using the CA from Vault. For this reason,
// we don't need any services to be synced in either direction.
"syncCatalog.enabled": "true",
"syncCatalog.toConsul": "false",
"syncCatalog.toK8S": "false",
}

if cfg.EnableEnterprise {
consulHelmValues["global.enterpriseLicense.secretName"] = "consul/data/secret/license"
consulHelmValues["global.enterpriseLicense.secretKey"] = "license"
}

logger.Log(t, "Installing Consul")
consulCluster := consul.NewHelmCluster(t, consulHelmValues, ctx, cfg, consulReleaseName)
consulCluster.Create(t)

// Validate that the gossip encryption key is set correctly.
logger.Log(t, "Validating the gossip key has been set correctly.")
consulCluster.ACLToken = bootstrapToken
consulClient, _ := consulCluster.SetupConsulClient(t, true)
keys, err := consulClient.Operator().KeyringList(nil)
require.NoError(t, err)
// There are two identical keys for LAN and WAN since there is only 1 dc.
require.Len(t, keys, 2)
require.Equal(t, 1, keys[0].PrimaryKeys[gossipKey])

// Confirm that the Vault Connect CA has been bootstrapped correctly.
caConfig, _, err := consulClient.Connect().CAGetConfig(nil)
require.NoError(t, err)
require.Equal(t, caConfig.Provider, "vault")

// Validate that consul sever is running correctly and the consul members command works
logger.Log(t, "Confirming that we can run Consul commands when exec'ing into server container")
membersOutput, err := k8s.RunKubectlAndGetOutputWithLoggerE(t, ctx.KubectlOptions(t), terratestLogger.Discard, "exec", fmt.Sprintf("%s-consul-server-0", consulReleaseName), "-c", "consul", "--", "sh", "-c", fmt.Sprintf("CONSUL_HTTP_TOKEN=%s consul members", bootstrapToken))
logger.Logf(t, "Members: \n%s", membersOutput)
require.NoError(t, err)
require.Contains(t, membersOutput, fmt.Sprintf("%s-consul-server-0", consulReleaseName))

if cfg.EnableEnterprise {
// Validate that the enterprise license is set correctly.
logger.Log(t, "Validating the enterprise license has been set correctly.")
license, licenseErr := consulClient.Operator().LicenseGet(nil)
require.NoError(t, licenseErr)
require.True(t, license.Valid)
}

// Deploy two services and check that they can talk to each other.
logger.Log(t, "creating static-server and static-client deployments")
k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-server-inject")
if cfg.EnableTransparentProxy {
k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-client-tproxy")
} else {
k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-client-inject")
}
helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() {
k8s.KubectlDeleteK(t, ctx.KubectlOptions(t), "../fixtures/bases/intention")
})
k8s.KubectlApplyK(t, ctx.KubectlOptions(t), "../fixtures/bases/intention")

logger.Log(t, "checking that connection is successful")
if cfg.EnableTransparentProxy {
k8s.CheckStaticServerConnectionSuccessful(t, ctx.KubectlOptions(t), staticClientName, "http://static-server")
} else {
k8s.CheckStaticServerConnectionSuccessful(t, ctx.KubectlOptions(t), staticClientName, "http://localhost:1234")
}
}
4 changes: 2 additions & 2 deletions acceptance/tests/vault/vault_partitions_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ func TestVault_Partitions(t *testing.T) {
serverClusterVaultHelmValues["server.service.nodePort"] = "31000"
}
serverClusterVault := vault.NewVaultCluster(t, serverClusterCtx, cfg, vaultReleaseName, serverClusterVaultHelmValues)
serverClusterVault.Create(t, serverClusterCtx)
serverClusterVault.Create(t, serverClusterCtx, "")

externalVaultAddress := vaultAddress(t, cfg, serverClusterCtx, vaultReleaseName)

Expand All @@ -59,7 +59,7 @@ func TestVault_Partitions(t *testing.T) {
}

secondaryVaultCluster := vault.NewVaultCluster(t, clientClusterCtx, cfg, vaultReleaseName, clientClusterVaultHelmValues)
secondaryVaultCluster.Create(t, clientClusterCtx)
secondaryVaultCluster.Create(t, clientClusterCtx, "")

vaultClient := serverClusterVault.VaultClient(t)

Expand Down
2 changes: 1 addition & 1 deletion acceptance/tests/vault/vault_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ func TestVault(t *testing.T) {
vaultReleaseName := helpers.RandomName()

vaultCluster := vault.NewVaultCluster(t, ctx, cfg, vaultReleaseName, nil)
vaultCluster.Create(t, ctx)
vaultCluster.Create(t, ctx, "")
// Vault is now installed in the cluster.

// Now fetch the Vault client so we can create the policies and secrets.
Expand Down
2 changes: 1 addition & 1 deletion acceptance/tests/vault/vault_tls_auto_reload_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ func TestVault_TlsAutoReload(t *testing.T) {
vaultReleaseName := helpers.RandomName()

vaultCluster := vault.NewVaultCluster(t, ctx, cfg, vaultReleaseName, nil)
vaultCluster.Create(t, ctx)
vaultCluster.Create(t, ctx, "")
// Vault is now installed in the cluster.

// Now fetch the Vault client so we can create the policies and secrets.
Expand Down
4 changes: 2 additions & 2 deletions acceptance/tests/vault/vault_wan_fed_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ func TestVault_WANFederationViaGateways(t *testing.T) {
}

primaryVaultCluster := vault.NewVaultCluster(t, primaryCtx, cfg, vaultReleaseName, primaryVaultHelmValues)
primaryVaultCluster.Create(t, primaryCtx)
primaryVaultCluster.Create(t, primaryCtx, "")

externalVaultAddress := vaultAddress(t, cfg, primaryCtx, vaultReleaseName)

Expand All @@ -62,7 +62,7 @@ func TestVault_WANFederationViaGateways(t *testing.T) {
}

secondaryVaultCluster := vault.NewVaultCluster(t, secondaryCtx, cfg, vaultReleaseName, secondaryVaultHelmValues)
secondaryVaultCluster.Create(t, secondaryCtx)
secondaryVaultCluster.Create(t, secondaryCtx, "")

vaultClient := primaryVaultCluster.VaultClient(t)

Expand Down

0 comments on commit 880846e

Please sign in to comment.