Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Backport of Allow connections through Terminating Gateways from peered clusters NET-3463 into release/1.15.x #19091

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .changelog/18959.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:bug
gateways: Fix a bug where a service in a peered datacenter could not access an external node service through a terminating gateway
```
22 changes: 22 additions & 0 deletions agent/proxycfg/proxycfg.deepcopy.go
Original file line number Diff line number Diff line change
Expand Up @@ -752,5 +752,27 @@ func (o *configSnapshotTerminatingGateway) DeepCopy() *configSnapshotTerminating
cp.HostnameServices[k2] = cp_HostnameServices_v2
}
}
if o.WatchedInboundPeerTrustBundles != nil {
cp.WatchedInboundPeerTrustBundles = make(map[structs.ServiceName]context.CancelFunc, len(o.WatchedInboundPeerTrustBundles))
for k2, v2 := range o.WatchedInboundPeerTrustBundles {
cp.WatchedInboundPeerTrustBundles[k2] = v2
}
}
if o.InboundPeerTrustBundles != nil {
cp.InboundPeerTrustBundles = make(map[structs.ServiceName][]*pbpeering.PeeringTrustBundle, len(o.InboundPeerTrustBundles))
for k2, v2 := range o.InboundPeerTrustBundles {
var cp_InboundPeerTrustBundles_v2 []*pbpeering.PeeringTrustBundle
if v2 != nil {
cp_InboundPeerTrustBundles_v2 = make([]*pbpeering.PeeringTrustBundle, len(v2))
copy(cp_InboundPeerTrustBundles_v2, v2)
for i3 := range v2 {
if v2[i3] != nil {
cp_InboundPeerTrustBundles_v2[i3] = v2[i3].DeepCopy()
}
}
}
cp.InboundPeerTrustBundles[k2] = cp_InboundPeerTrustBundles_v2
}
}
return &cp
}
9 changes: 9 additions & 0 deletions agent/proxycfg/snapshot.go
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,15 @@ type configSnapshotTerminatingGateway struct {
// HostnameServices is a map of service name to service instances with a hostname as the address.
// If hostnames are configured they must be provided to Envoy via CDS not EDS.
HostnameServices map[structs.ServiceName]structs.CheckServiceNodes

// WatchedInboundPeerTrustBundles is a map of service name to a cancel function. This cancel
// function is tied to the watch of the inbound peer trust bundles for the gateway.
WatchedInboundPeerTrustBundles map[structs.ServiceName]context.CancelFunc

// InboundPeerTrustBundles is a map of service name to a list of peering trust bundles.
// These bundles are used to configure RBAC policies for inbound filter chains on the gateway
// from services that are in a cluster-peered datacenter.
InboundPeerTrustBundles map[structs.ServiceName][]*pbpeering.PeeringTrustBundle
}

// ValidServices returns the list of service keys that have enough data to be emitted.
Expand Down
46 changes: 46 additions & 0 deletions agent/proxycfg/terminating_gateway.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (

cachetype "github.com/hashicorp/consul/agent/cache-types"
"github.com/hashicorp/consul/agent/structs"
"github.com/hashicorp/consul/proto/pbpeering"
)

type handlerTerminatingGateway struct {
Expand Down Expand Up @@ -65,6 +66,8 @@ func (s *handlerTerminatingGateway) initialize(ctx context.Context) (ConfigSnaps
snap.TerminatingGateway.GatewayServices = make(map[structs.ServiceName]structs.GatewayService)
snap.TerminatingGateway.DestinationServices = make(map[structs.ServiceName]structs.GatewayService)
snap.TerminatingGateway.HostnameServices = make(map[structs.ServiceName]structs.CheckServiceNodes)
snap.TerminatingGateway.WatchedInboundPeerTrustBundles = make(map[structs.ServiceName]context.CancelFunc)
snap.TerminatingGateway.InboundPeerTrustBundles = make(map[structs.ServiceName][]*pbpeering.PeeringTrustBundle)
return snap, nil
}

Expand Down Expand Up @@ -165,6 +168,29 @@ func (s *handlerTerminatingGateway) handleUpdate(ctx context.Context, u UpdateEv
snap.TerminatingGateway.WatchedIntentions[svc.Service] = cancel
}

if _, ok := snap.TerminatingGateway.WatchedInboundPeerTrustBundles[svc.Service]; !ok {
ctx, cancel := context.WithCancel(ctx)

err := s.dataSources.TrustBundleList.Notify(ctx, &cachetype.TrustBundleListRequest{
Request: &pbpeering.TrustBundleListByServiceRequest{
ServiceName: svc.Service.Name,
Namespace: svc.Service.EnterpriseMeta.NamespaceOrDefault(),
Partition: svc.Service.EnterpriseMeta.PartitionOrDefault(),
},
QueryOptions: structs.QueryOptions{Token: s.token},
}, peerTrustBundleIDPrefix+svc.Service.String(), s.ch)

if err != nil {
logger.Error("failed to register watch for peer trust bundles",
"service", svc.Service.String(),
"error", err,
)
cancel()
return err
}
snap.TerminatingGateway.WatchedInboundPeerTrustBundles[svc.Service] = cancel
}

// Watch leaf certificate for the service
// This cert is used to terminate mTLS connections on the service's behalf
if _, ok := snap.TerminatingGateway.WatchedLeaves[svc.Service]; !ok {
Expand Down Expand Up @@ -296,6 +322,16 @@ func (s *handlerTerminatingGateway) handleUpdate(ctx context.Context, u UpdateEv
}
}

// Cancel watches for peered trust bundle that were not in the update
for sn, cancelFn := range snap.TerminatingGateway.WatchedInboundPeerTrustBundles {
if _, ok := svcMap[sn]; !ok {
logger.Debug("canceling watch for peered trust bundle", "service", sn.String())
delete(snap.TerminatingGateway.WatchedInboundPeerTrustBundles, sn)
delete(snap.TerminatingGateway.InboundPeerTrustBundles, sn)
cancelFn()
}
}

// Cancel intention watches for services that were not in the update
for sn, cancelFn := range snap.TerminatingGateway.WatchedIntentions {
if _, ok := svcMap[sn]; !ok {
Expand Down Expand Up @@ -371,6 +407,16 @@ func (s *handlerTerminatingGateway) handleUpdate(ctx context.Context, u UpdateEv
sn := structs.ServiceNameFromString(strings.TrimPrefix(u.CorrelationID, serviceIntentionsIDPrefix))
snap.TerminatingGateway.Intentions[sn] = resp

case strings.HasPrefix(u.CorrelationID, peerTrustBundleIDPrefix):
resp, ok := u.Result.(*pbpeering.TrustBundleListByServiceResponse)
if !ok {
return fmt.Errorf("invalid type for response: %T", u.Result)
}
if len(resp.Bundles) > 0 {
sn := structs.ServiceNameFromString(strings.TrimPrefix(u.CorrelationID, peerTrustBundleIDPrefix))
snap.TerminatingGateway.InboundPeerTrustBundles[sn] = resp.Bundles
}

default:
// do nothing
}
Expand Down
38 changes: 21 additions & 17 deletions agent/xds/listeners.go
Original file line number Diff line number Diff line change
Expand Up @@ -1575,6 +1575,7 @@ func (s *ResourceGenerator) makeTerminatingGatewayListener(

intentions := cfgSnap.TerminatingGateway.Intentions[svc]
svcConfig := cfgSnap.TerminatingGateway.ServiceConfigs[svc]
peerTrustBundles := cfgSnap.TerminatingGateway.InboundPeerTrustBundles[svc]

cfg, err := ParseProxyConfig(svcConfig.ProxyConfig)
if err != nil {
Expand All @@ -1588,10 +1589,11 @@ func (s *ResourceGenerator) makeTerminatingGatewayListener(
}

opts := terminatingGatewayFilterChainOpts{
cluster: clusterName,
service: svc,
intentions: intentions,
protocol: cfg.Protocol,
cluster: clusterName,
service: svc,
intentions: intentions,
protocol: cfg.Protocol,
peerTrustBundles: peerTrustBundles,
}

clusterChain, err := s.makeFilterChainTerminatingGateway(cfgSnap, opts)
Expand Down Expand Up @@ -1619,6 +1621,7 @@ func (s *ResourceGenerator) makeTerminatingGatewayListener(
for _, svc := range cfgSnap.TerminatingGateway.ValidDestinations() {
intentions := cfgSnap.TerminatingGateway.Intentions[svc]
svcConfig := cfgSnap.TerminatingGateway.ServiceConfigs[svc]
peerTrustBundles := cfgSnap.TerminatingGateway.InboundPeerTrustBundles[svc]

cfg, err := ParseProxyConfig(svcConfig.ProxyConfig)
if err != nil {
Expand All @@ -1635,10 +1638,11 @@ func (s *ResourceGenerator) makeTerminatingGatewayListener(
dest = &svcConfig.Destination

opts := terminatingGatewayFilterChainOpts{
service: svc,
intentions: intentions,
protocol: cfg.Protocol,
port: dest.Port,
service: svc,
intentions: intentions,
protocol: cfg.Protocol,
port: dest.Port,
peerTrustBundles: peerTrustBundles,
}
for _, address := range dest.Addresses {
clusterName := clusterNameForDestination(cfgSnap, svc.Name, address, svc.NamespaceOrDefault(), svc.PartitionOrDefault())
Expand Down Expand Up @@ -1695,12 +1699,13 @@ func (s *ResourceGenerator) makeTerminatingGatewayListener(
}

type terminatingGatewayFilterChainOpts struct {
cluster string
service structs.ServiceName
intentions structs.Intentions
protocol string
address string // only valid for destination listeners
port int // only valid for destination listeners
cluster string
service structs.ServiceName
intentions structs.Intentions
protocol string
address string // only valid for destination listeners
port int // only valid for destination listeners
peerTrustBundles []*pbpeering.PeeringTrustBundle
}

func (s *ResourceGenerator) makeFilterChainTerminatingGateway(cfgSnap *proxycfg.ConfigSnapshot, tgtwyOpts terminatingGatewayFilterChainOpts) (*envoy_listener_v3.FilterChain, error) {
Expand Down Expand Up @@ -1736,7 +1741,7 @@ func (s *ResourceGenerator) makeFilterChainTerminatingGateway(cfgSnap *proxycfg.
datacenter: cfgSnap.Datacenter,
partition: cfgSnap.ProxyID.PartitionOrDefault(),
},
nil, // TODO(peering): verify intentions w peers don't apply to terminatingGateway
tgtwyOpts.peerTrustBundles,
)
if err != nil {
return nil, err
Expand Down Expand Up @@ -1782,7 +1787,7 @@ func (s *ResourceGenerator) makeFilterChainTerminatingGateway(cfgSnap *proxycfg.
datacenter: cfgSnap.Datacenter,
partition: cfgSnap.ProxyID.PartitionOrDefault(),
},
nil, // TODO(peering): verify intentions w peers don't apply to terminatingGateway
tgtwyOpts.peerTrustBundles,
)
if err != nil {
return nil, err
Expand Down Expand Up @@ -2051,7 +2056,6 @@ func (s *ResourceGenerator) makeMeshGatewayPeerFilterChain(
// RDS, Envoy's Route Discovery Service, is only used for HTTP services.
useRDS = useHTTPFilter
)

if useHTTPFilter && cfgSnap.MeshGateway.Leaf == nil {
return nil, nil // ignore; not ready
}
Expand Down
39 changes: 38 additions & 1 deletion agent/xds/listeners_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,18 @@ import (
"testing"
"text/template"

"github.com/hashicorp/consul/agent/xds/testcommon"
"github.com/stretchr/testify/assert"

"github.com/hashicorp/consul/agent/xds/testcommon"

envoy_listener_v3 "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3"
testinf "github.com/mitchellh/go-testing-interface"
"github.com/stretchr/testify/require"

"github.com/hashicorp/consul/agent/proxycfg"
"github.com/hashicorp/consul/agent/structs"
"github.com/hashicorp/consul/envoyextensions/xdscommon"
"github.com/hashicorp/consul/proto/pbpeering"
"github.com/hashicorp/consul/sdk/testutil"
"github.com/hashicorp/consul/types"
)
Expand Down Expand Up @@ -921,6 +923,41 @@ func TestListenersFromSnapshot(t *testing.T) {
})
},
},
{
name: "terminating-gateway-with-peer-trust-bundle",
create: func(t testinf.T) *proxycfg.ConfigSnapshot {
roots, _ := proxycfg.TestCerts(t)
return proxycfg.TestConfigSnapshotTerminatingGateway(t, true, nil, []proxycfg.UpdateEvent{
{
CorrelationID: "peer-trust-bundle:web",
Result: &pbpeering.TrustBundleListByServiceResponse{
Bundles: []*pbpeering.PeeringTrustBundle{
{
TrustDomain: "foo.bar.gov",
PeerName: "dc1",
Partition: "default",
RootPEMs: []string{
roots.Roots[0].RootCert,
},
ExportedPartition: "dc1",
CreateIndex: 0,
ModifyIndex: 0,
},
},
},
},
{
CorrelationID: "service-intentions:web",
Result: structs.Intentions{
{
SourceName: "*",
DestinationName: "web",
},
},
},
})
},
},
{
name: "ingress-with-tls-listener",
create: func(t testinf.T) *proxycfg.ConfigSnapshot {
Expand Down
Loading