From 5565d71ab420a51b41475a401a6265692d224ece Mon Sep 17 00:00:00 2001 From: Iryna Shustava Date: Thu, 20 Aug 2020 17:25:58 -0700 Subject: [PATCH] Add auto-encrypt and secure terminating gateway tests (#581) --- test/acceptance/helpers/helpers.go | 2 +- test/acceptance/tests/basic/basic_test.go | 9 + .../tests/connect/connect_inject_test.go | 86 +++++--- .../ingress-gateway/ingress_gateway_test.go | 45 ++-- .../tests/mesh-gateway/mesh_gateway_test.go | 16 +- .../tests/sync/sync_catalog_test.go | 10 + .../terminating_gateway_test.go | 192 +++++++++++++----- 7 files changed, 242 insertions(+), 118 deletions(-) diff --git a/test/acceptance/helpers/helpers.go b/test/acceptance/helpers/helpers.go index ac374b2b92..d399b21c1d 100644 --- a/test/acceptance/helpers/helpers.go +++ b/test/acceptance/helpers/helpers.go @@ -94,7 +94,7 @@ func Deploy(t *testing.T, options *k8s.KubectlOptions, noCleanupOnFailure bool, // to be "hello world" in a case of success. // If expectSuccess is true, it will expect connection to succeed, // otherwise it will expect failure due to intentions. -func CheckStaticServerConnection(t *testing.T, options *k8s.KubectlOptions, deploymentName string, expectSuccess bool, curlArgs ...string) { +func CheckStaticServerConnection(t *testing.T, options *k8s.KubectlOptions, expectSuccess bool, deploymentName string, curlArgs ...string) { t.Helper() retrier := &retry.Timer{Timeout: 20 * time.Second, Wait: 500 * time.Millisecond} diff --git a/test/acceptance/tests/basic/basic_test.go b/test/acceptance/tests/basic/basic_test.go index 82fd467f40..e2c9b10bf2 100644 --- a/test/acceptance/tests/basic/basic_test.go +++ b/test/acceptance/tests/basic/basic_test.go @@ -31,6 +31,15 @@ func TestBasicInstallation(t *testing.T) { }, true, }, + { + "Secure installation (with TLS with auto-encrypt and ACLs enabled)", + map[string]string{ + "global.tls.enabled": "true", + "global.tls.enableAutoEncrypt": "true", + "global.acls.manageSystemACLs": "true", + }, + true, + }, } for _, c := range cases { diff --git a/test/acceptance/tests/connect/connect_inject_test.go b/test/acceptance/tests/connect/connect_inject_test.go index 34506b951a..263d9b4d5a 100644 --- a/test/acceptance/tests/connect/connect_inject_test.go +++ b/test/acceptance/tests/connect/connect_inject_test.go @@ -9,6 +9,8 @@ import ( "github.com/stretchr/testify/require" ) +const staticClientName = "static-client" + // Test that Connect works in a default installation func TestConnectInjectDefault(t *testing.T) { cfg := suite.Config() @@ -28,43 +30,61 @@ func TestConnectInjectDefault(t *testing.T) { helpers.Deploy(t, ctx.KubectlOptions(), cfg.NoCleanupOnFailure, "fixtures/static-client.yaml") t.Log("checking that connection is successful") - helpers.CheckStaticServerConnection(t, ctx.KubectlOptions(), "static-client", true, "http://localhost:1234") + helpers.CheckStaticServerConnection(t, ctx.KubectlOptions(), true, staticClientName, "http://localhost:1234") } // Test that Connect works in a secure installation, // with ACLs and TLS enabled. func TestConnectInjectSecure(t *testing.T) { - cfg := suite.Config() - ctx := suite.Environment().DefaultContext(t) - - helmValues := map[string]string{ - "connectInject.enabled": "true", - "global.tls.enabled": "true", - "global.acls.manageSystemACLs": "true", + cases := []struct { + name string + enableAutoEncrypt string + }{ + { + "without auto-encrypt", + "false", + }, + { + "with auto-encrypt", + "true", + }, + } + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + cfg := suite.Config() + ctx := suite.Environment().DefaultContext(t) + + helmValues := map[string]string{ + "connectInject.enabled": "true", + "global.tls.enabled": "true", + "global.tls.enableAutoEncrypt": c.enableAutoEncrypt, + "global.acls.manageSystemACLs": "true", + } + + releaseName := helpers.RandomName() + consulCluster := framework.NewHelmCluster(t, helmValues, ctx, cfg, releaseName) + + consulCluster.Create(t) + + t.Log("creating static-server and static-client deployments") + helpers.Deploy(t, ctx.KubectlOptions(), cfg.NoCleanupOnFailure, "fixtures/static-server.yaml") + helpers.Deploy(t, ctx.KubectlOptions(), cfg.NoCleanupOnFailure, "fixtures/static-client.yaml") + + t.Log("checking that the connection is not successful because there's no intention") + helpers.CheckStaticServerConnection(t, ctx.KubectlOptions(), false, staticClientName, "http://localhost:1234") + + consulClient := consulCluster.SetupConsulClient(t, true) + + t.Log("creating intention") + _, _, err := consulClient.Connect().IntentionCreate(&api.Intention{ + SourceName: staticClientName, + DestinationName: "static-server", + Action: api.IntentionActionAllow, + }, nil) + require.NoError(t, err) + + t.Log("checking that connection is successful") + helpers.CheckStaticServerConnection(t, ctx.KubectlOptions(), true, staticClientName, "http://localhost:1234") + }) } - - releaseName := helpers.RandomName() - consulCluster := framework.NewHelmCluster(t, helmValues, ctx, cfg, releaseName) - - consulCluster.Create(t) - - t.Log("creating static-server and static-client deployments") - helpers.Deploy(t, ctx.KubectlOptions(), cfg.NoCleanupOnFailure, "fixtures/static-server.yaml") - helpers.Deploy(t, ctx.KubectlOptions(), cfg.NoCleanupOnFailure, "fixtures/static-client.yaml") - - t.Log("checking that the connection is not successful because there's no intention") - helpers.CheckStaticServerConnection(t, ctx.KubectlOptions(), "static-client", false, "http://localhost:1234") - - consulClient := consulCluster.SetupConsulClient(t, true) - - t.Log("creating intention") - _, _, err := consulClient.Connect().IntentionCreate(&api.Intention{ - SourceName: "static-client", - DestinationName: "static-server", - Action: api.IntentionActionAllow, - }, nil) - require.NoError(t, err) - - t.Log("checking that connection is successful") - helpers.CheckStaticServerConnection(t, ctx.KubectlOptions(), "static-client", true, "http://localhost:1234") } diff --git a/test/acceptance/tests/ingress-gateway/ingress_gateway_test.go b/test/acceptance/tests/ingress-gateway/ingress_gateway_test.go index ebdca869ba..ff2a5bffc8 100644 --- a/test/acceptance/tests/ingress-gateway/ingress_gateway_test.go +++ b/test/acceptance/tests/ingress-gateway/ingress_gateway_test.go @@ -2,6 +2,7 @@ package ingressgateway import ( "fmt" + "strconv" "testing" "github.com/hashicorp/consul-helm/test/acceptance/framework" @@ -12,9 +13,26 @@ import ( // Test that ingress gateways work in a default installation and a secure installation. func TestIngressGateway(t *testing.T) { - for _, secure := range []bool{false, true} { - testName := fmt.Sprintf("secure: %t", secure) - t.Run(testName, func(t *testing.T) { + cases := []struct { + secure bool + autoEncrypt bool + }{ + { + false, + false, + }, + { + true, + false, + }, + { + true, + true, + }, + } + for _, c := range cases { + name := fmt.Sprintf("secure: %t; auto-encrypt: %t", c.secure, c.autoEncrypt) + t.Run(name, func(t *testing.T) { ctx := suite.Environment().DefaultContext(t) cfg := suite.Config() @@ -24,9 +42,10 @@ func TestIngressGateway(t *testing.T) { "ingressGateways.gateways[0].name": "ingress-gateway", "ingressGateways.gateways[0].replicas": "1", } - if secure { + if c.secure { helmValues["global.acls.manageSystemACLs"] = "true" helmValues["global.tls.enabled"] = "true" + helmValues["global.tls.autoEncrypt"] = strconv.FormatBool(c.autoEncrypt) } releaseName := helpers.RandomName() @@ -44,7 +63,7 @@ func TestIngressGateway(t *testing.T) { // With the cluster up, we can create our ingress-gateway config entry. t.Log("creating config entry") - consulClient := consulCluster.SetupConsulClient(t, secure) + consulClient := consulCluster.SetupConsulClient(t, c.secure) // Create config entry created, _, err := consulClient.ConfigEntries().Set(&api.IngressGatewayConfigEntry{ @@ -68,17 +87,12 @@ func TestIngressGateway(t *testing.T) { k8sOptions := ctx.KubectlOptions() // If ACLs are enabled, test that intentions prevent connections. - if secure { + if c.secure { // With the ingress gateway up, we test that we can make a call to it // via the bounce pod. It should fail to connect with the // static-server pod because of intentions. t.Log("testing intentions prevent ingress") - helpers.CheckStaticServerConnection(t, - k8sOptions, - "bounce", - false, - "-H", "Host: static-server.ingress.consul", - fmt.Sprintf("http://%s-consul-ingress-gateway:8080/", releaseName)) + helpers.CheckStaticServerConnection(t, k8sOptions, false, "bounce", "-H", "Host: static-server.ingress.consul", fmt.Sprintf("http://%s-consul-ingress-gateway:8080/", releaseName)) // Now we create the allow intention. t.Log("creating ingress-gateway => static-server intention") @@ -93,12 +107,7 @@ func TestIngressGateway(t *testing.T) { // Test that we can make a call to the ingress gateway // via the bounce pod. It should route to the static-server pod. t.Log("trying calls to ingress gateway") - helpers.CheckStaticServerConnection(t, - k8sOptions, - "bounce", - true, - "-H", "Host: static-server.ingress.consul", - fmt.Sprintf("http://%s-consul-ingress-gateway:8080/", releaseName)) + helpers.CheckStaticServerConnection(t, k8sOptions, true, "bounce", "-H", "Host: static-server.ingress.consul", fmt.Sprintf("http://%s-consul-ingress-gateway:8080/", releaseName)) }) } } diff --git a/test/acceptance/tests/mesh-gateway/mesh_gateway_test.go b/test/acceptance/tests/mesh-gateway/mesh_gateway_test.go index d9aec4df49..47e07be53b 100644 --- a/test/acceptance/tests/mesh-gateway/mesh_gateway_test.go +++ b/test/acceptance/tests/mesh-gateway/mesh_gateway_test.go @@ -11,6 +11,8 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) +const staticClientName = "static-client" + // Test that Connect and wan federation over mesh gateways work in a default installation // i.e. without ACLs because TLS is required for WAN federation over mesh gateways func TestMeshGatewayDefault(t *testing.T) { @@ -93,11 +95,7 @@ func TestMeshGatewayDefault(t *testing.T) { helpers.Deploy(t, primaryContext.KubectlOptions(), cfg.NoCleanupOnFailure, "fixtures/static-client.yaml") t.Log("checking that connection is successful") - helpers.CheckStaticServerConnection(t, - primaryContext.KubectlOptions(), - "static-client", - true, - "http://localhost:1234") + helpers.CheckStaticServerConnection(t, primaryContext.KubectlOptions(), true, staticClientName, "http://localhost:1234") } // Test that Connect and wan federation over mesh gateways work in a secure installation, @@ -208,18 +206,14 @@ func TestMeshGatewaySecure(t *testing.T) { t.Log("creating intention") _, _, err = consulClient.Connect().IntentionCreate(&api.Intention{ - SourceName: "static-client", + SourceName: staticClientName, DestinationName: "static-server", Action: api.IntentionActionAllow, }, nil) require.NoError(t, err) t.Log("checking that connection is successful") - helpers.CheckStaticServerConnection(t, - primaryContext.KubectlOptions(), - "static-client", - true, - "http://localhost:1234") + helpers.CheckStaticServerConnection(t, primaryContext.KubectlOptions(), true, staticClientName, "http://localhost:1234") }) } } diff --git a/test/acceptance/tests/sync/sync_catalog_test.go b/test/acceptance/tests/sync/sync_catalog_test.go index f62226907b..12498f5842 100644 --- a/test/acceptance/tests/sync/sync_catalog_test.go +++ b/test/acceptance/tests/sync/sync_catalog_test.go @@ -37,6 +37,16 @@ func TestSyncCatalog(t *testing.T) { }, true, }, + { + "Secure installation (with TLS with auto-encrypt and ACLs enabled)", + map[string]string{ + "syncCatalog.enabled": "true", + "global.tls.enabled": "true", + "global.tls.enableAutoEncrypt": "true", + "global.acls.manageSystemACLs": "true", + }, + true, + }, } for _, c := range cases { diff --git a/test/acceptance/tests/terminating-gateway/terminating_gateway_test.go b/test/acceptance/tests/terminating-gateway/terminating_gateway_test.go index 13bd90f42b..0359ed2bec 100644 --- a/test/acceptance/tests/terminating-gateway/terminating_gateway_test.go +++ b/test/acceptance/tests/terminating-gateway/terminating_gateway_test.go @@ -1,6 +1,9 @@ package terminatinggateway import ( + "fmt" + "strconv" + "strings" "testing" "github.com/hashicorp/consul-helm/test/acceptance/framework" @@ -9,63 +12,142 @@ import ( "github.com/stretchr/testify/require" ) +const staticClientName = "static-client" +const staticServerName = "static-server" + // Test that terminating gateways work in a default installation. func TestTerminatingGateway(t *testing.T) { - ctx := suite.Environment().DefaultContext(t) - cfg := suite.Config() - - helmValues := map[string]string{ - "connectInject.enabled": "true", - "terminatingGateways.enabled": "true", - "terminatingGateways.gateways[0].name": "terminating-gateway", - "terminatingGateways.gateways[0].replicas": "1", + cases := []struct { + secure bool + autoEncrypt bool + }{ + { + false, + false, + }, + { + true, + true, + }, + { + true, + true, + }, } + for _, c := range cases { + name := fmt.Sprintf("secure: %t, auto-encrypt: %t", c.secure, c.autoEncrypt) + t.Run(name, func(t *testing.T) { + ctx := suite.Environment().DefaultContext(t) + cfg := suite.Config() - t.Log("creating consul cluster") - releaseName := helpers.RandomName() - consulCluster := framework.NewHelmCluster(t, helmValues, ctx, cfg, releaseName) - consulCluster.Create(t) - - // Deploy a static-server that will play the role of an external service - t.Log("creating static-server deployment") - helpers.Deploy(t, ctx.KubectlOptions(), cfg.NoCleanupOnFailure, "fixtures/static-server.yaml") - - // Once the cluster is up, register the external service, then create the config entry. - consulClient := consulCluster.SetupConsulClient(t, false) - - // Register the external service - t.Log("registering the external service") - _, err := consulClient.Catalog().Register(&api.CatalogRegistration{ - Node: "legacy_node", - Address: "static-server", - NodeMeta: map[string]string{"external-node": "true", "external-probe": "true"}, - Service: &api.AgentService{ - ID: "static-server", - Service: "static-server", - Port: 80, - }, - }, &api.WriteOptions{}) - require.NoError(t, err) - - // Create the config entry for the terminating gateway - t.Log("creating config entry") - created, _, err := consulClient.ConfigEntries().Set(&api.TerminatingGatewayConfigEntry{ - Kind: api.TerminatingGateway, - Name: "terminating-gateway", - Services: []api.LinkedService{{Name: "static-server"}}, - }, nil) - require.NoError(t, err) - require.True(t, created, "config entry failed") - - // Deploy the static client - t.Log("deploying static client") - helpers.Deploy(t, ctx.KubectlOptions(), cfg.NoCleanupOnFailure, "fixtures/static-client.yaml") - - // Test that we can make a call to the terminating gateway - t.Log("trying calls to terminating gateway") - helpers.CheckStaticServerConnection(t, - ctx.KubectlOptions(), - "static-client", - true, - "http://localhost:1234") + helmValues := map[string]string{ + "connectInject.enabled": "true", + "terminatingGateways.enabled": "true", + "terminatingGateways.gateways[0].name": "terminating-gateway", + "terminatingGateways.gateways[0].replicas": "1", + } + + if c.secure { + helmValues["global.acls.manageSystemACLs"] = "true" + helmValues["global.tls.enabled"] = "true" + helmValues["global.tls.autoEncrypt"] = strconv.FormatBool(c.autoEncrypt) + } + + t.Log("creating consul cluster") + releaseName := helpers.RandomName() + consulCluster := framework.NewHelmCluster(t, helmValues, ctx, cfg, releaseName) + consulCluster.Create(t) + + // Deploy a static-server that will play the role of an external service + t.Log("creating static-server deployment") + helpers.Deploy(t, ctx.KubectlOptions(), cfg.NoCleanupOnFailure, "fixtures/static-server.yaml") + + // Once the cluster is up, register the external service, then create the config entry. + consulClient := consulCluster.SetupConsulClient(t, c.secure) + + // Register the external service + t.Log("registering the external service") + _, err := consulClient.Catalog().Register(&api.CatalogRegistration{ + Node: "legacy_node", + Address: staticServerName, + NodeMeta: map[string]string{"external-node": "true", "external-probe": "true"}, + Service: &api.AgentService{ + ID: staticServerName, + Service: staticServerName, + Port: 80, + }, + }, nil) + require.NoError(t, err) + + // If ACLs are enabled we need to update the token of the terminating gateway + // with service:write permissions to the static-server service + // so that it can can request Connect certificates for it. + if c.secure { + // Create a write policy for the static-server + _, _, err := consulClient.ACL().PolicyCreate(&api.ACLPolicy{ + Name: "static-server-write-policy", + Rules: staticServerPolicyRules, + }, nil) + require.NoError(t, err) + + // Get the terminating gateway token + tokens, _, err := consulClient.ACL().TokenList(nil) + require.NoError(t, err) + var termGwTokenID string + for _, token := range tokens { + if strings.Contains(token.Description, "terminating-gateway-terminating-gateway-token") { + termGwTokenID = token.AccessorID + break + } + } + termGwToken, _, err := consulClient.ACL().TokenRead(termGwTokenID, nil) + require.NoError(t, err) + + // Add policy to the token and update it + termGwToken.Policies = append(termGwToken.Policies, &api.ACLTokenPolicyLink{Name: "static-server-write-policy"}) + _, _, err = consulClient.ACL().TokenUpdate(termGwToken, nil) + require.NoError(t, err) + } + + // Create the config entry for the terminating gateway + t.Log("creating config entry") + created, _, err := consulClient.ConfigEntries().Set(&api.TerminatingGatewayConfigEntry{ + Kind: api.TerminatingGateway, + Name: "terminating-gateway", + Services: []api.LinkedService{{Name: staticServerName}}, + }, nil) + require.NoError(t, err) + require.True(t, created, "config entry failed") + + // Deploy the static client + t.Log("deploying static client") + helpers.Deploy(t, ctx.KubectlOptions(), cfg.NoCleanupOnFailure, "fixtures/static-client.yaml") + + // If ACLs are enabled, test that intentions prevent connections. + if c.secure { + // With the terminating gateway up, we test that we can make a call to it + // via the static-server. It should fail to connect with the + // static-server pod because of intentions. + t.Log("testing intentions prevent connections through the terminating gateway") + helpers.CheckStaticServerConnection(t, ctx.KubectlOptions(), false, staticClientName, "http://localhost:1234") + + // Now we create the allow intention. + t.Log("creating static-client => static-server intention") + _, _, err = consulClient.Connect().IntentionCreate(&api.Intention{ + SourceName: staticClientName, + DestinationName: staticServerName, + Action: api.IntentionActionAllow, + }, nil) + require.NoError(t, err) + } + + // Test that we can make a call to the terminating gateway + t.Log("trying calls to terminating gateway") + helpers.CheckStaticServerConnection(t, ctx.KubectlOptions(), true, staticClientName, "http://localhost:1234") + }) + } } + +const staticServerPolicyRules = `service "static-server" { + policy = "write" +}`