Skip to content

Commit

Permalink
testing/deployer: support tproxy in v2 for dataplane (#19094)
Browse files Browse the repository at this point in the history
This updates the testing/deployer (aka "topology test") framework to allow for a 
v2-oriented topology to opt services into enabling TransparentProxy. The restrictions 
are similar to that of #19046

The multiport Ports map that was added in #19046 was changed to allow for the 
protocol to be specified at this time, but for now the only supported protocol is TCP 
as only L4 functions currently on main.

As part of making transparent proxy work, the DNS server needed a new zonefile 
for responding to virtual.consul requests, since there is no Kubernetes DNS and 
the Consul DNS work for v2 has not happened yet. Once Consul DNS supports v2 we should switch over. For now the format of queries is:

<service>--<namespace>--<partition>.virtual.consul

Additionally:

- All transparent proxy enabled services are assigned a virtual ip in the 10.244.0/24
  range. This is something Consul will do in v2 at a later date, likely during 1.18.
- All services with exposed ports (non-mesh) are assigned a virtual port number for use
  with tproxy
- The consul-dataplane image has been made un-distroless, and gotten the necessary
  tools to execute consul connect redirect-traffic before running dataplane, thus simulating
  a kubernetes init container in plain docker.
  • Loading branch information
rboyer committed Nov 2, 2023
1 parent 7be5fd7 commit 4537d53
Show file tree
Hide file tree
Showing 20 changed files with 809 additions and 123 deletions.
28 changes: 9 additions & 19 deletions test-integ/catalogv2/explicit_destinations_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ package catalogv2

import (
"fmt"
"strings"
"testing"

pbauth "github.com/hashicorp/consul/proto-public/pbauth/v2beta1"
Expand All @@ -18,7 +17,7 @@ import (
"github.com/hashicorp/consul/test-integ/topoutil"
)

// TestBasicL4ExplicitDestination sets up the following:
// TestBasicL4ExplicitDestinations sets up the following:
//
// - 1 cluster (no peering / no wanfed)
// - 3 servers in that cluster
Expand All @@ -37,8 +36,8 @@ import (
// - part1/default
// - default/nsa
// - part1/nsa
func TestBasicL4ExplicitDestination(t *testing.T) {
cfg := testBasicL4ExplicitDestinationCreator{}.NewConfig(t)
func TestBasicL4ExplicitDestinations(t *testing.T) {
cfg := testBasicL4ExplicitDestinationsCreator{}.NewConfig(t)

sp := sprawltest.Launch(t, cfg)

Expand Down Expand Up @@ -69,20 +68,11 @@ func TestBasicL4ExplicitDestination(t *testing.T) {
for _, ship := range ships {
t.Run("relationship: "+ship.String(), func(t *testing.T) {
var (
svc = ship.Caller
u = ship.Upstream
clusterPrefix string
svc = ship.Caller
u = ship.Upstream
)

if u.Peer == "" {
if u.ID.PartitionOrDefault() == "default" {
clusterPrefix = strings.Join([]string{u.PortName, u.ID.Name, u.ID.Namespace, u.Cluster, "internal"}, ".")
} else {
clusterPrefix = strings.Join([]string{u.PortName, u.ID.Name, u.ID.Namespace, u.ID.Partition, u.Cluster, "internal-v1"}, ".")
}
} else {
clusterPrefix = strings.Join([]string{u.ID.Name, u.ID.Namespace, u.Peer, "external"}, ".")
}
clusterPrefix := clusterPrefixForUpstream(u)

asserter.UpstreamEndpointStatus(t, svc, clusterPrefix+".", "HEALTHY", 1)
asserter.HTTPServiceEchoes(t, svc, u.LocalPort, "")
Expand All @@ -91,9 +81,9 @@ func TestBasicL4ExplicitDestination(t *testing.T) {
}
}

type testBasicL4ExplicitDestinationCreator struct{}
type testBasicL4ExplicitDestinationsCreator struct{}

func (c testBasicL4ExplicitDestinationCreator) NewConfig(t *testing.T) *topology.Config {
func (c testBasicL4ExplicitDestinationsCreator) NewConfig(t *testing.T) *topology.Config {
const clusterName = "dc1"

servers := topoutil.NewTopologyServerSet(clusterName+"-server", 3, []string{clusterName, "wan"}, nil)
Expand Down Expand Up @@ -129,7 +119,7 @@ func (c testBasicL4ExplicitDestinationCreator) NewConfig(t *testing.T) *topology
}
}

func (c testBasicL4ExplicitDestinationCreator) topologyConfigAddNodes(
func (c testBasicL4ExplicitDestinationsCreator) topologyConfigAddNodes(
t *testing.T,
cluster *topology.Cluster,
nodeName func() string,
Expand Down
22 changes: 22 additions & 0 deletions test-integ/catalogv2/helpers_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1

package catalogv2

import (
"strings"

"github.com/hashicorp/consul/testing/deployer/topology"
)

func clusterPrefixForUpstream(u *topology.Upstream) string {
if u.Peer == "" {
if u.ID.PartitionOrDefault() == "default" {
return strings.Join([]string{u.PortName, u.ID.Name, u.ID.Namespace, u.Cluster, "internal"}, ".")
} else {
return strings.Join([]string{u.PortName, u.ID.Name, u.ID.Namespace, u.ID.Partition, u.Cluster, "internal-v1"}, ".")
}
} else {
return strings.Join([]string{u.ID.Name, u.ID.Namespace, u.Peer, "external"}, ".")
}
}
214 changes: 214 additions & 0 deletions test-integ/catalogv2/implicit_destinations_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,214 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1

package catalogv2

import (
"fmt"
"testing"

pbauth "github.com/hashicorp/consul/proto-public/pbauth/v2beta1"
"github.com/hashicorp/consul/proto-public/pbresource"
libassert "github.com/hashicorp/consul/test/integration/consul-container/libs/assert"
"github.com/hashicorp/consul/test/integration/consul-container/libs/utils"
"github.com/hashicorp/consul/testing/deployer/sprawl/sprawltest"
"github.com/hashicorp/consul/testing/deployer/topology"

"github.com/hashicorp/consul/test-integ/topoutil"
)

// TestBasicL4ImplicitDestinations sets up the following:
//
// - 1 cluster (no peering / no wanfed)
// - 3 servers in that cluster
// - v2 arch is activated
// - for each tenancy, only using v2 constructs:
// - a server exposing 2 tcp ports
// - a client with transparent proxy enabled and no explicit upstreams
// - a traffic permission granting the client access to the service on all ports
//
// When this test is executed in CE it will only use the default/default
// tenancy.
//
// When this test is executed in Enterprise it will additionally test the same
// things within these tenancies:
//
// - part1/default
// - default/nsa
// - part1/nsa
func TestBasicL4ImplicitDestinations(t *testing.T) {
cfg := testBasicL4ImplicitDestinationsCreator{}.NewConfig(t)

sp := sprawltest.Launch(t, cfg)

var (
asserter = topoutil.NewAsserter(sp)

topo = sp.Topology()
cluster = topo.Clusters["dc1"]

ships = topo.ComputeRelationships()
)

clientV2 := sp.ResourceServiceClientForCluster(cluster.Name)

t.Log(topology.RenderRelationships(ships))

// Make sure things are truly in v2 not v1.
for _, name := range []string{
"static-server",
"static-client",
} {
libassert.CatalogV2ServiceHasEndpointCount(t, clientV2, name, nil, 1)
}

// Check relationships
for _, ship := range ships {
t.Run("relationship: "+ship.String(), func(t *testing.T) {
var (
svc = ship.Caller
u = ship.Upstream
)

clusterPrefix := clusterPrefixForUpstream(u)

asserter.UpstreamEndpointStatus(t, svc, clusterPrefix+".", "HEALTHY", 1)
if u.LocalPort > 0 {
asserter.HTTPServiceEchoes(t, svc, u.LocalPort, "")
}
asserter.FortioFetch2FortioName(t, svc, u, cluster.Name, u.ID)
})
}
}

type testBasicL4ImplicitDestinationsCreator struct{}

func (c testBasicL4ImplicitDestinationsCreator) NewConfig(t *testing.T) *topology.Config {
const clusterName = "dc1"

servers := topoutil.NewTopologyServerSet(clusterName+"-server", 3, []string{clusterName, "wan"}, nil)

cluster := &topology.Cluster{
Enterprise: utils.IsEnterprise(),
Name: clusterName,
Nodes: servers,
}

lastNode := 0
nodeName := func() string {
lastNode++
return fmt.Sprintf("%s-box%d", clusterName, lastNode)
}

c.topologyConfigAddNodes(t, cluster, nodeName, "default", "default")
if cluster.Enterprise {
c.topologyConfigAddNodes(t, cluster, nodeName, "part1", "default")
c.topologyConfigAddNodes(t, cluster, nodeName, "part1", "nsa")
c.topologyConfigAddNodes(t, cluster, nodeName, "default", "nsa")
}

return &topology.Config{
Images: topoutil.TargetImages(),
Networks: []*topology.Network{
{Name: clusterName},
{Name: "wan", Type: "wan"},
},
Clusters: []*topology.Cluster{
cluster,
},
}
}

func (c testBasicL4ImplicitDestinationsCreator) topologyConfigAddNodes(
t *testing.T,
cluster *topology.Cluster,
nodeName func() string,
partition,
namespace string,
) {
clusterName := cluster.Name

newServiceID := func(name string) topology.ServiceID {
return topology.ServiceID{
Partition: partition,
Namespace: namespace,
Name: name,
}
}

tenancy := &pbresource.Tenancy{
Partition: partition,
Namespace: namespace,
PeerName: "local",
}

serverNode := &topology.Node{
Kind: topology.NodeKindDataplane,
Version: topology.NodeVersionV2,
Partition: partition,
Name: nodeName(),
Services: []*topology.Service{
topoutil.NewFortioServiceWithDefaults(
clusterName,
newServiceID("static-server"),
topology.NodeVersionV2,
func(svc *topology.Service) {
svc.EnableTransparentProxy = true
},
),
},
}
clientNode := &topology.Node{
Kind: topology.NodeKindDataplane,
Version: topology.NodeVersionV2,
Partition: partition,
Name: nodeName(),
Services: []*topology.Service{
topoutil.NewFortioServiceWithDefaults(
clusterName,
newServiceID("static-client"),
topology.NodeVersionV2,
func(svc *topology.Service) {
svc.EnableTransparentProxy = true
svc.ImpliedUpstreams = []*topology.Upstream{
{
ID: newServiceID("static-server"),
PortName: "http",
},
{
ID: newServiceID("static-server"),
PortName: "http-alt",
},
}
},
),
},
}
trafficPerms := sprawltest.MustSetResourceData(t, &pbresource.Resource{
Id: &pbresource.ID{
Type: pbauth.TrafficPermissionsType,
Name: "static-server-perms",
Tenancy: tenancy,
},
}, &pbauth.TrafficPermissions{
Destination: &pbauth.Destination{
IdentityName: "static-server",
},
Action: pbauth.Action_ACTION_ALLOW,
Permissions: []*pbauth.Permission{{
Sources: []*pbauth.Source{{
IdentityName: "static-client",
Namespace: namespace,
}},
}},
})

cluster.Nodes = append(cluster.Nodes,
clientNode,
serverNode,
)

cluster.InitialResources = append(cluster.InitialResources,
trafficPerms,
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,13 @@ import (
"testing"
"time"

"github.com/hashicorp/consul/api"
"github.com/hashicorp/consul/sdk/testutil/retry"
libassert "github.com/hashicorp/consul/test/integration/consul-container/libs/assert"
"github.com/hashicorp/consul/testing/deployer/topology"
"github.com/hashicorp/go-cleanhttp"
"github.com/itchyny/gojq"
"github.com/stretchr/testify/require"

"github.com/hashicorp/consul/api"
"github.com/hashicorp/consul/sdk/testutil/retry"
libassert "github.com/hashicorp/consul/test/integration/consul-container/libs/assert"
)

var ac3SvcDefaultsSuites []sharedTopoSuite = []sharedTopoSuite{
Expand Down Expand Up @@ -185,7 +184,7 @@ func (s *ac3SvcDefaultsSuite) test(t *testing.T, ct *commonTopo) {
// TODO: what is default? namespace? partition?
clusterName := fmt.Sprintf("%s.default.%s.external", s.upstream.ID.Name, s.upstream.Peer)
nonceStatus := http.StatusInsufficientStorage
url507 := fmt.Sprintf("http://localhost:%d/fortio/fetch2?url=%s", svcClient.ExposedPort,
url507 := fmt.Sprintf("http://localhost:%d/fortio/fetch2?url=%s", svcClient.ExposedPort(""),
url.QueryEscape(fmt.Sprintf("http://localhost:%d/?status=%d", s.upstream.LocalPort, nonceStatus)),
)

Expand Down Expand Up @@ -221,7 +220,7 @@ func (s *ac3SvcDefaultsSuite) test(t *testing.T, ct *commonTopo) {
require.True(r, resultAsBool)
})

url200 := fmt.Sprintf("http://localhost:%d/fortio/fetch2?url=%s", svcClient.ExposedPort,
url200 := fmt.Sprintf("http://localhost:%d/fortio/fetch2?url=%s", svcClient.ExposedPort(""),
url.QueryEscape(fmt.Sprintf("http://localhost:%d/", s.upstream.LocalPort)),
)
retry.RunWith(&retry.Timer{Timeout: time.Minute * 1, Wait: time.Millisecond * 500}, t, func(r *retry.R) {
Expand Down
4 changes: 2 additions & 2 deletions test-integ/peering_commontopo/ac4_proxy_defaults_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -180,11 +180,11 @@ func (s *ac4ProxyDefaultsSuite) test(t *testing.T, ct *commonTopo) {
})

t.Run("HTTP service fails due to connection timeout", func(t *testing.T) {
url504 := fmt.Sprintf("http://localhost:%d/fortio/fetch2?url=%s", client.ExposedPort,
url504 := fmt.Sprintf("http://localhost:%d/fortio/fetch2?url=%s", client.ExposedPort(""),
url.QueryEscape(fmt.Sprintf("http://localhost:%d/?delay=1000ms", s.upstream.LocalPort)),
)

url200 := fmt.Sprintf("http://localhost:%d/fortio/fetch2?url=%s", client.ExposedPort,
url200 := fmt.Sprintf("http://localhost:%d/fortio/fetch2?url=%s", client.ExposedPort(""),
url.QueryEscape(fmt.Sprintf("http://localhost:%d/", s.upstream.LocalPort)),
)

Expand Down
Loading

0 comments on commit 4537d53

Please sign in to comment.