From a09c7766456cc84ed07b122d6f3e6c04c60cd6bc Mon Sep 17 00:00:00 2001 From: Freddy Date: Wed, 1 Jun 2022 14:31:37 -0600 Subject: [PATCH] Update public listener with SPIFFE Validator Envoy's SPIFFE certificate validation extension allows for us to validate against different root certificates depending on the trust domain of the dialing proxy. If there are any trust bundles from peers in the config snapshot then we use the SPIFFE validator as the validation context, rather than the usual TrustedCA. The injected validation config includes the local root certificates as well. --- agent/agent.go | 1 + agent/proxycfg-glue/glue.go | 8 ++ agent/proxycfg/connect_proxy.go | 20 ++++ agent/proxycfg/data_sources.go | 10 ++ agent/proxycfg/snapshot.go | 8 +- agent/proxycfg/state.go | 1 + agent/proxycfg/state_test.go | 21 +++- agent/proxycfg/testing.go | 8 +- agent/xds/listeners.go | 103 +++++++++++++++++- agent/xds/listeners_test.go | 16 ++- ...nect-proxy-exported-to-peers.latest.golden | 92 ++++++++++++++++ proto/prototest/testing.go | 2 +- 12 files changed, 279 insertions(+), 11 deletions(-) create mode 100644 agent/xds/testdata/listeners/connect-proxy-exported-to-peers.latest.golden diff --git a/agent/agent.go b/agent/agent.go index ebaa339f13..8fee06587d 100644 --- a/agent/agent.go +++ b/agent/agent.go @@ -646,6 +646,7 @@ func (a *Agent) Start(ctx context.Context) error { ResolvedServiceConfig: proxycfgglue.CacheResolvedServiceConfig(a.cache), ServiceList: proxycfgglue.CacheServiceList(a.cache), TrustBundle: proxycfgglue.CacheTrustBundle(a.cache), + TrustBundleList: proxycfgglue.CacheTrustBundleList(a.cache), } a.fillEnterpriseProxyDataSources(&proxyDataSources) a.proxyConfig, err = proxycfg.NewManager(proxycfg.ManagerConfig{ diff --git a/agent/proxycfg-glue/glue.go b/agent/proxycfg-glue/glue.go index b18c6487f4..5538aab5be 100644 --- a/agent/proxycfg-glue/glue.go +++ b/agent/proxycfg-glue/glue.go @@ -101,10 +101,18 @@ func CacheServiceList(c *cache.Cache) proxycfg.ServiceList { return &cacheProxyDataSource[*structs.DCSpecificRequest]{c, cachetype.CatalogServiceListName} } +// CacheTrustBundle satisfies the proxycfg.TrustBundle interface by sourcing +// data from the agent cache. func CacheTrustBundle(c *cache.Cache) proxycfg.TrustBundle { return &cacheProxyDataSource[*pbpeering.TrustBundleReadRequest]{c, cachetype.TrustBundleReadName} } +// CacheTrustBundleList satisfies the proxycfg.TrustBundleList interface by sourcing +// data from the agent cache. +func CacheTrustBundleList(c *cache.Cache) proxycfg.TrustBundleList { + return &cacheProxyDataSource[*pbpeering.TrustBundleListByServiceRequest]{c, cachetype.TrustBundleListName} +} + // cacheProxyDataSource implements a generic wrapper around the agent cache to // provide data to the proxycfg.Manager. type cacheProxyDataSource[ReqType cache.Request] struct { diff --git a/agent/proxycfg/connect_proxy.go b/agent/proxycfg/connect_proxy.go index 923b15b73b..d2931cf27e 100644 --- a/agent/proxycfg/connect_proxy.go +++ b/agent/proxycfg/connect_proxy.go @@ -44,6 +44,16 @@ func (s *handlerConnectProxy) initialize(ctx context.Context) (ConfigSnapshot, e return snap, err } + err = s.dataSources.TrustBundleList.Notify(ctx, &pbpeering.TrustBundleListByServiceRequest{ + // TODO(peering): Pass ACL token + ServiceName: s.proxyCfg.DestinationServiceName, + Namespace: s.proxyID.NamespaceOrDefault(), + Partition: s.proxyID.PartitionOrDefault(), + }, peeringTrustBundlesWatchID, s.ch) + if err != nil { + return snap, err + } + // Watch the leaf cert err = s.dataSources.LeafCertificate.Notify(ctx, &cachetype.ConnectCALeafRequest{ Datacenter: s.source.Datacenter, @@ -259,6 +269,16 @@ func (s *handlerConnectProxy) handleUpdate(ctx context.Context, u UpdateEvent, s snap.ConnectProxy.PeerTrustBundles[peer] = resp.Bundle } + case u.CorrelationID == peeringTrustBundlesWatchID: + resp, ok := u.Result.(*pbpeering.TrustBundleListByServiceResponse) + if !ok { + return fmt.Errorf("invalid type for response: %T", u.Result) + } + if len(resp.Bundles) > 0 { + snap.ConnectProxy.PeeringTrustBundles = resp.Bundles + } + snap.ConnectProxy.PeeringTrustBundlesSet = true + case u.CorrelationID == intentionsWatchID: resp, ok := u.Result.(*structs.IndexedIntentionMatches) if !ok { diff --git a/agent/proxycfg/data_sources.go b/agent/proxycfg/data_sources.go index 1f48c15d89..c454c2052e 100644 --- a/agent/proxycfg/data_sources.go +++ b/agent/proxycfg/data_sources.go @@ -82,6 +82,10 @@ type DataSources struct { // TrustBundle provides updates about the trust bundle for a single peer. TrustBundle TrustBundle + // TrustBundleList provides updates about the list of trust bundles for + // peered clusters that the given proxy is exported to. + TrustBundleList TrustBundleList + DataSourcesEnterprise } @@ -185,3 +189,9 @@ type ServiceList interface { type TrustBundle interface { Notify(ctx context.Context, req *pbpeering.TrustBundleReadRequest, correlationID string, ch chan<- UpdateEvent) error } + +// TrustBundleList is the interface used to consume updates about trust bundles +// for peered clusters that the given proxy is exported to. +type TrustBundleList interface { + Notify(ctx context.Context, req *pbpeering.TrustBundleListByServiceRequest, correlationID string, ch chan<- UpdateEvent) error +} diff --git a/agent/proxycfg/snapshot.go b/agent/proxycfg/snapshot.go index cd8afd2cef..45a250b486 100644 --- a/agent/proxycfg/snapshot.go +++ b/agent/proxycfg/snapshot.go @@ -6,12 +6,12 @@ import ( "sort" "strings" - "github.com/hashicorp/consul/lib" - "github.com/hashicorp/consul/proto/pbpeering" "github.com/mitchellh/copystructure" "github.com/hashicorp/consul/acl" "github.com/hashicorp/consul/agent/structs" + "github.com/hashicorp/consul/lib" + "github.com/hashicorp/consul/proto/pbpeering" ) // TODO(ingress): Can we think of a better for this bag of data? @@ -122,6 +122,9 @@ func gatewayKeyFromString(s string) GatewayKey { type configSnapshotConnectProxy struct { ConfigSnapshotUpstreams + PeeringTrustBundlesSet bool + PeeringTrustBundles []*pbpeering.PeeringTrustBundle + WatchedServiceChecks map[structs.ServiceID][]structs.CheckType // TODO: missing garbage collection PreparedQueryEndpoints map[UpstreamID]structs.CheckServiceNodes // DEPRECATED:see:WatchedUpstreamEndpoints @@ -152,6 +155,7 @@ func (c *configSnapshotConnectProxy) isEmpty() bool { len(c.UpstreamConfig) == 0 && len(c.PassthroughUpstreams) == 0 && len(c.IntentionUpstreams) == 0 && + !c.PeeringTrustBundlesSet && !c.MeshConfigSet } diff --git a/agent/proxycfg/state.go b/agent/proxycfg/state.go index de0eec2368..bce6510d47 100644 --- a/agent/proxycfg/state.go +++ b/agent/proxycfg/state.go @@ -19,6 +19,7 @@ import ( const ( coalesceTimeout = 200 * time.Millisecond rootsWatchID = "roots" + peeringTrustBundlesWatchID = "peering-trust-bundles" leafWatchID = "leaf" peerTrustBundleIDPrefix = "peer-trust-bundle:" intentionsWatchID = "intentions" diff --git a/agent/proxycfg/state_test.go b/agent/proxycfg/state_test.go index 7ca93da661..20446b2dae 100644 --- a/agent/proxycfg/state_test.go +++ b/agent/proxycfg/state_test.go @@ -7,7 +7,6 @@ import ( "testing" "time" - "github.com/hashicorp/consul/proto/pbpeering" "github.com/hashicorp/go-hclog" "github.com/stretchr/testify/require" @@ -15,6 +14,8 @@ import ( cachetype "github.com/hashicorp/consul/agent/cache-types" "github.com/hashicorp/consul/agent/consul/discoverychain" "github.com/hashicorp/consul/agent/structs" + "github.com/hashicorp/consul/proto/pbpeering" + "github.com/hashicorp/consul/proto/prototest" "github.com/hashicorp/consul/sdk/testutil" ) @@ -134,6 +135,7 @@ func recordWatches(sc *stateConfig) *watchRecorder { ResolvedServiceConfig: typedWatchRecorder[*structs.ServiceConfigRequest]{wr}, ServiceList: typedWatchRecorder[*structs.DCSpecificRequest]{wr}, TrustBundle: typedWatchRecorder[*pbpeering.TrustBundleReadRequest]{wr}, + TrustBundleList: typedWatchRecorder[*pbpeering.TrustBundleListByServiceRequest]{wr}, } recordWatchesEnterprise(sc, wr) @@ -217,6 +219,14 @@ func genVerifyLeafWatch(expectedService string, expectedDatacenter string) verif return genVerifyLeafWatchWithDNSSANs(expectedService, expectedDatacenter, nil) } +func genVerifyTrustBundleListWatch(service string) verifyWatchRequest { + return func(t testing.TB, request any) { + reqReal, ok := request.(*pbpeering.TrustBundleListByServiceRequest) + require.True(t, ok) + require.Equal(t, service, reqReal.ServiceName) + } +} + func genVerifyResolverWatch(expectedService, expectedDatacenter, expectedKind string) verifyWatchRequest { return func(t testing.TB, request any) { reqReal, ok := request.(*structs.ConfigEntryQuery) @@ -2492,6 +2502,7 @@ func TestState_WatchesAndUpdates(t *testing.T) { }), rootsWatchID: genVerifyDCSpecificWatch("dc1"), leafWatchID: genVerifyLeafWatch("web", "dc1"), + peeringTrustBundlesWatchID: genVerifyTrustBundleListWatch("web"), peerTrustBundleIDPrefix + "peer-a": genVerifyTrustBundleReadWatch("peer-a"), // No Peering watch }, @@ -2514,12 +2525,18 @@ func TestState_WatchesAndUpdates(t *testing.T) { require.Len(t, snap.ConnectProxy.WatchedServiceChecks, 0, "%+v", snap.ConnectProxy.WatchedServiceChecks) require.Len(t, snap.ConnectProxy.PreparedQueryEndpoints, 0, "%+v", snap.ConnectProxy.PreparedQueryEndpoints) + require.Len(t, snap.ConnectProxy.PeeringTrustBundles, 0, "%+v", snap.ConnectProxy.PeeringTrustBundles) + require.False(t, snap.ConnectProxy.PeeringTrustBundlesSet) }, }, { // This time add the events events: []UpdateEvent{ rootWatchEvent(), + { + CorrelationID: peeringTrustBundlesWatchID, + Result: peerTrustBundles, + }, { CorrelationID: leafWatchID, Result: issuedCert, @@ -2551,8 +2568,10 @@ func TestState_WatchesAndUpdates(t *testing.T) { verifySnapshot: func(t testing.TB, snap *ConfigSnapshot) { require.True(t, snap.Valid()) require.True(t, snap.MeshGateway.isEmpty()) + require.Equal(t, indexedRoots, snap.Roots) require.Equal(t, issuedCert, snap.ConnectProxy.Leaf) + prototest.AssertDeepEqual(t, peerTrustBundles.Bundles, snap.ConnectProxy.PeeringTrustBundles) require.Len(t, snap.ConnectProxy.DiscoveryChain, 2, "%+v", snap.ConnectProxy.DiscoveryChain) require.Len(t, snap.ConnectProxy.WatchedUpstreams, 2, "%+v", snap.ConnectProxy.WatchedUpstreams) diff --git a/agent/proxycfg/testing.go b/agent/proxycfg/testing.go index 8f12ec8f39..77eb84eb08 100644 --- a/agent/proxycfg/testing.go +++ b/agent/proxycfg/testing.go @@ -24,8 +24,6 @@ import ( ) func TestPeerTrustBundles(t testing.T) *pbpeering.TrustBundleListByServiceResponse { - t.Helper() - return &pbpeering.TrustBundleListByServiceResponse{ Bundles: []*pbpeering.PeeringTrustBundle{ { @@ -722,6 +720,7 @@ func testConfigSnapshotFixture( ResolvedServiceConfig: &noopDataSource[*structs.ServiceConfigRequest]{}, ServiceList: &noopDataSource[*structs.DCSpecificRequest]{}, TrustBundle: &noopDataSource[*pbpeering.TrustBundleReadRequest]{}, + TrustBundleList: &noopDataSource[*pbpeering.TrustBundleListByServiceRequest]{}, }, dnsConfig: DNSConfig{ // TODO: make configurable Domain: "consul", @@ -922,6 +921,7 @@ func NewTestDataSources() *TestDataSources { ResolvedServiceConfig: NewTestDataSource[*structs.ServiceConfigRequest, *structs.ServiceConfigResponse](), ServiceList: NewTestDataSource[*structs.DCSpecificRequest, *structs.IndexedServiceList](), TrustBundle: NewTestDataSource[*pbpeering.TrustBundleReadRequest, *pbpeering.TrustBundleReadResponse](), + TrustBundleList: NewTestDataSource[*pbpeering.TrustBundleListByServiceRequest, *pbpeering.TrustBundleListByServiceResponse](), } srcs.buildEnterpriseSources() return srcs @@ -945,6 +945,9 @@ type TestDataSources struct { ResolvedServiceConfig *TestDataSource[*structs.ServiceConfigRequest, *structs.ServiceConfigResponse] ServiceList *TestDataSource[*structs.DCSpecificRequest, *structs.IndexedServiceList] TrustBundle *TestDataSource[*pbpeering.TrustBundleReadRequest, *pbpeering.TrustBundleReadResponse] + TrustBundleList *TestDataSource[*pbpeering.TrustBundleListByServiceRequest, *pbpeering.TrustBundleListByServiceResponse] + + TestDataSourcesEnterprise } func (t *TestDataSources) ToDataSources() DataSources { @@ -965,6 +968,7 @@ func (t *TestDataSources) ToDataSources() DataSources { ResolvedServiceConfig: t.ResolvedServiceConfig, ServiceList: t.ServiceList, TrustBundle: t.TrustBundle, + TrustBundleList: t.TrustBundleList, } t.fillEnterpriseDataSources(&ds) return ds diff --git a/agent/xds/listeners.go b/agent/xds/listeners.go index 01df376410..62d70a7f60 100644 --- a/agent/xds/listeners.go +++ b/agent/xds/listeners.go @@ -11,9 +11,6 @@ import ( "strings" "time" - "github.com/hashicorp/consul/acl" - "github.com/hashicorp/consul/lib" - "github.com/hashicorp/consul/types" "google.golang.org/protobuf/types/known/durationpb" "google.golang.org/protobuf/types/known/wrapperspb" @@ -39,10 +36,14 @@ import ( "github.com/golang/protobuf/ptypes/any" "github.com/golang/protobuf/ptypes/wrappers" + "github.com/hashicorp/consul/acl" "github.com/hashicorp/consul/agent/connect" "github.com/hashicorp/consul/agent/proxycfg" "github.com/hashicorp/consul/agent/structs" + "github.com/hashicorp/consul/lib" + "github.com/hashicorp/consul/proto/pbpeering" "github.com/hashicorp/consul/sdk/iptables" + "github.com/hashicorp/consul/types" ) const virtualIPTag = "virtual" @@ -777,6 +778,100 @@ func (s *ResourceGenerator) injectConnectTLSOnFilterChains(cfgSnap *proxycfg.Con return nil } +// +// NOTE: This method MUST only be used for connect proxy public listeners, +// since TLS validation will be done against root certs for all peers +// that might dial this proxy. +func (s *ResourceGenerator) injectConnectTLSForPublicListener(cfgSnap *proxycfg.ConfigSnapshot, listener *envoy_listener_v3.Listener) error { + if cfgSnap.Kind != structs.ServiceKindConnectProxy { + return fmt.Errorf("cannot inject peering trust bundles for kind %q", cfgSnap.Kind) + } + + // Create TLS validation context for mTLS with leaf certificate and root certs. + tlsContext := makeCommonTLSContext( + cfgSnap.Leaf(), + cfgSnap.RootPEMs(), + makeTLSParametersFromProxyTLSConfig(cfgSnap.MeshConfigTLSIncoming()), + ) + + // Inject peering trust bundles if this service is exported to peered clusters. + if len(cfgSnap.ConnectProxy.PeeringTrustBundles) > 0 { + spiffeConfig, err := makeSpiffeValidatorConfig(cfgSnap.Roots.TrustDomain, cfgSnap.RootPEMs(), cfgSnap.ConnectProxy.PeeringTrustBundles) + if err != nil { + return err + } + + typ, ok := tlsContext.ValidationContextType.(*envoy_tls_v3.CommonTlsContext_ValidationContext) + if !ok { + return fmt.Errorf("unexpected type for TLS context validation: %T", tlsContext.ValidationContextType) + } + + // makeCommonTLSFromLead injects the local trust domain's CA root certs as the TrustedCA. + // We nil it out here since the local roots are included in the SPIFFE validator config. + typ.ValidationContext.TrustedCa = nil + typ.ValidationContext.CustomValidatorConfig = &envoy_core_v3.TypedExtensionConfig{ + // The typed config name is hard-coded because it is not available as a wellknown var in the control plane lib. + Name: "envoy.tls.cert_validator.spiffe", + TypedConfig: spiffeConfig, + } + } + + transportSocket, err := makeDownstreamTLSTransportSocket(&envoy_tls_v3.DownstreamTlsContext{ + CommonTlsContext: tlsContext, + RequireClientCertificate: &wrappers.BoolValue{Value: true}, + }) + if err != nil { + return err + } + + for idx := range listener.FilterChains { + listener.FilterChains[idx].TransportSocket = transportSocket + } + return nil +} + +// SPIFFECertValidatorConfig is used to validate certificates from trust domains other than our own. +// With cluster peering we expect peered clusters to have independent certificate authorities. +// This means that we cannot use a single set of root CA certificates to validate client certificates for mTLS, +// but rather we need to validate against different roots depending on the trust domain of the certificate presented. +func makeSpiffeValidatorConfig(trustDomain, roots string, peerBundles []*pbpeering.PeeringTrustBundle) (*any.Any, error) { + // Store the trust bundle for the local trust domain. + bundles := map[string]string{trustDomain: roots} + + // Store the trust bundle for each trust domain of the peers this proxy is exported to. + // This allows us to validate traffic from other trust domains. + for _, b := range peerBundles { + var pems string + for _, pem := range b.RootPEMs { + pems += lib.EnsureTrailingNewline(pem) + } + bundles[b.TrustDomain] = pems + } + + cfg := &envoy_tls_v3.SPIFFECertValidatorConfig{ + TrustDomains: make([]*envoy_tls_v3.SPIFFECertValidatorConfig_TrustDomain, 0, len(bundles)), + } + + for domain, bundle := range bundles { + cfg.TrustDomains = append(cfg.TrustDomains, &envoy_tls_v3.SPIFFECertValidatorConfig_TrustDomain{ + Name: domain, + TrustBundle: &envoy_core_v3.DataSource{ + Specifier: &envoy_core_v3.DataSource_InlineString{ + InlineString: bundle, + }, + }, + }) + } + + // Sort the trust domains so that the output is stable. + // This benefits tests but also prevents Envoy from mistakenly thinking the listener + // changed and needs to be drained only because this ordering is different. + sort.Slice(cfg.TrustDomains, func(i int, j int) bool { + return cfg.TrustDomains[i].Name < cfg.TrustDomains[j].Name + }) + return ptypes.MarshalAny(cfg) +} + func (s *ResourceGenerator) makeInboundListener(cfgSnap *proxycfg.ConfigSnapshot, name string) (proto.Message, error) { var l *envoy_listener_v3.Listener var err error @@ -899,7 +994,7 @@ func (s *ResourceGenerator) finalizePublicListenerFromConfig(l *envoy_listener_v } // Always apply TLS certificates - if err := s.injectConnectTLSOnFilterChains(cfgSnap, l); err != nil { + if err := s.injectConnectTLSForPublicListener(cfgSnap, l); err != nil { return nil } diff --git a/agent/xds/listeners_test.go b/agent/xds/listeners_test.go index 8fb1cbddc9..b43b5c0b27 100644 --- a/agent/xds/listeners_test.go +++ b/agent/xds/listeners_test.go @@ -9,7 +9,6 @@ import ( "time" 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" @@ -42,6 +41,21 @@ func TestListenersFromSnapshot(t *testing.T) { return proxycfg.TestConfigSnapshot(t, nil, nil) }, }, + { + name: "connect-proxy-exported-to-peers", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshot(t, func(ns *structs.NodeService) { + // This test is only concerned about the SPIFFE cert validator config in the public listener + // so we empty out the upstreams to avoid generating unnecessary upstream listeners. + ns.Proxy.Upstreams = structs.Upstreams{} + }, []proxycfg.UpdateEvent{ + { + CorrelationID: "peering-trust-bundles", + Result: proxycfg.TestPeerTrustBundles(t), + }, + }) + }, + }, { name: "connect-proxy-with-tls-outgoing-min-version-auto", create: func(t testinf.T) *proxycfg.ConfigSnapshot { diff --git a/agent/xds/testdata/listeners/connect-proxy-exported-to-peers.latest.golden b/agent/xds/testdata/listeners/connect-proxy-exported-to-peers.latest.golden new file mode 100644 index 0000000000..c61cb12d71 --- /dev/null +++ b/agent/xds/testdata/listeners/connect-proxy-exported-to-peers.latest.golden @@ -0,0 +1,92 @@ +{ + "versionInfo": "00000001", + "resources": [ + { + "@type": "type.googleapis.com/envoy.config.listener.v3.Listener", + "name": "public_listener:0.0.0.0:9999", + "address": { + "socketAddress": { + "address": "0.0.0.0", + "portValue": 9999 + } + }, + "filterChains": [ + { + "filters": [ + { + "name": "envoy.filters.network.rbac", + "typedConfig": { + "@type": "type.googleapis.com/envoy.extensions.filters.network.rbac.v3.RBAC", + "rules": { + + }, + "statPrefix": "connect_authz" + } + }, + { + "name": "envoy.filters.network.tcp_proxy", + "typedConfig": { + "@type": "type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy", + "statPrefix": "public_listener", + "cluster": "local_app" + } + } + ], + "transportSocket": { + "name": "tls", + "typedConfig": { + "@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext", + "commonTlsContext": { + "tlsParams": { + + }, + "tlsCertificates": [ + { + "certificateChain": { + "inlineString": "-----BEGIN CERTIFICATE-----\nMIICjDCCAjKgAwIBAgIIC5llxGV1gB8wCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowDjEMMAoG\nA1UEAxMDd2ViMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEADPv1RHVNRfa2VKR\nAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Favq5E0ivpNtv1QnFhxtPd7d5k4e+T7\nSkW1TaOCAXIwggFuMA4GA1UdDwEB/wQEAwIDuDAdBgNVHSUEFjAUBggrBgEFBQcD\nAgYIKwYBBQUHAwEwDAYDVR0TAQH/BAIwADBoBgNVHQ4EYQRfN2Q6MDc6ODc6M2E6\nNDA6MTk6NDc6YzM6NWE6YzA6YmE6NjI6ZGY6YWY6NGI6ZDQ6MDU6MjU6NzY6M2Q6\nNWE6OGQ6MTY6OGQ6Njc6NWU6MmU6YTA6MzQ6N2Q6ZGM6ZmYwagYDVR0jBGMwYYBf\nZDE6MTE6MTE6YWM6MmE6YmE6OTc6YjI6M2Y6YWM6N2I6YmQ6ZGE6YmU6YjE6OGE6\nZmM6OWE6YmE6YjU6YmM6ODM6ZTc6NWU6NDE6NmY6ZjI6NzM6OTU6NTg6MGM6ZGIw\nWQYDVR0RBFIwUIZOc3BpZmZlOi8vMTExMTExMTEtMjIyMi0zMzMzLTQ0NDQtNTU1\nNTU1NTU1NTU1LmNvbnN1bC9ucy9kZWZhdWx0L2RjL2RjMS9zdmMvd2ViMAoGCCqG\nSM49BAMCA0gAMEUCIGC3TTvvjj76KMrguVyFf4tjOqaSCRie3nmHMRNNRav7AiEA\npY0heYeK9A6iOLrzqxSerkXXQyj5e9bE4VgUnxgPU6g=\n-----END CERTIFICATE-----\n" + }, + "privateKey": { + "inlineString": "-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIMoTkpRggp3fqZzFKh82yS4LjtJI+XY+qX/7DefHFrtdoAoGCCqGSM49\nAwEHoUQDQgAEADPv1RHVNRfa2VKRAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Fav\nq5E0ivpNtv1QnFhxtPd7d5k4e+T7SkW1TQ==\n-----END EC PRIVATE KEY-----\n" + } + } + ], + "validationContext": { + "customValidatorConfig": { + "name": "envoy.tls.cert_validator.spiffe", + "typedConfig": { + "@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.SPIFFECertValidatorConfig", + "trustDomains": [ + { + "name": "11111111-2222-3333-4444-555555555555.consul", + "trustBundle": { + "inlineString": "-----BEGIN CERTIFICATE-----\nMIICXDCCAgKgAwIBAgIICpZq70Z9LyUwCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowFDESMBAG\nA1UEAxMJVGVzdCBDQSAyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEIhywH1gx\nAsMwuF3ukAI5YL2jFxH6Usnma1HFSfVyxbXX1/uoZEYrj8yCAtdU2yoHETyd+Zx2\nThhRLP79pYegCaOCATwwggE4MA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTAD\nAQH/MGgGA1UdDgRhBF9kMToxMToxMTphYzoyYTpiYTo5NzpiMjozZjphYzo3Yjpi\nZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1ZTo0MTo2ZjpmMjo3\nMzo5NTo1ODowYzpkYjBqBgNVHSMEYzBhgF9kMToxMToxMTphYzoyYTpiYTo5Nzpi\nMjozZjphYzo3YjpiZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1\nZTo0MTo2ZjpmMjo3Mzo5NTo1ODowYzpkYjA/BgNVHREEODA2hjRzcGlmZmU6Ly8x\nMTExMTExMS0yMjIyLTMzMzMtNDQ0NC01NTU1NTU1NTU1NTUuY29uc3VsMAoGCCqG\nSM49BAMCA0gAMEUCICOY0i246rQHJt8o8Oya0D5PLL1FnmsQmQqIGCi31RwnAiEA\noR5f6Ku+cig2Il8T8LJujOp2/2A72QcHZA57B13y+8o=\n-----END CERTIFICATE-----\n" + } + }, + { + "name": "1c053652-8512-4373-90cf-5a7f6263a994.consul", + "trustBundle": { + "inlineString": "-----BEGIN CERTIFICATE-----\nMIICczCCAdwCCQC3BLnEmLCrSjANBgkqhkiG9w0BAQsFADB+MQswCQYDVQQGEwJV\nUzELMAkGA1UECAwCQVoxEjAQBgNVBAcMCUZsYWdzdGFmZjEMMAoGA1UECgwDRm9v\nMRAwDgYDVQQLDAdleGFtcGxlMQ8wDQYDVQQDDAZwZWVyLWExHTAbBgkqhkiG9w0B\nCQEWDmZvb0BwZWVyLWEuY29tMB4XDTIyMDUyNjAxMDQ0NFoXDTIzMDUyNjAxMDQ0\nNFowfjELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkFaMRIwEAYDVQQHDAlGbGFnc3Rh\nZmYxDDAKBgNVBAoMA0ZvbzEQMA4GA1UECwwHZXhhbXBsZTEPMA0GA1UEAwwGcGVl\nci1hMR0wGwYJKoZIhvcNAQkBFg5mb29AcGVlci1hLmNvbTCBnzANBgkqhkiG9w0B\nAQEFAAOBjQAwgYkCgYEA2zFYGTbXDAntT5pLTpZ2+VTiqx4J63VRJH1kdu11f0FV\nc2jl1pqCuYDbQXknDU0Pv1Q5y0+nSAihD2KqGS571r+vHQiPtKYPYRqPEe9FzAhR\n2KhWH6v/tk5DG1HqOjV9/zWRKB12gdFNZZqnw/e7NjLNq3wZ2UAwxXip5uJ8uwMC\nAwEAATANBgkqhkiG9w0BAQsFAAOBgQC/CJ9Syf4aL91wZizKTejwouRYoWv4gRAk\nyto45ZcNMHfJ0G2z+XAMl9ZbQsLgXmzAx4IM6y5Jckq8pKC4PEijCjlKTktLHlEy\n0ggmFxtNB1tid2NC8dOzcQ3l45+gDjDqdILhAvLDjlAIebdkqVqb2CfFNW/I2CQH\nZAuKN1aoKA==\n-----END CERTIFICATE-----\n" + } + }, + { + "name": "d89ac423-e95a-475d-94f2-1c557c57bf31.consul", + "trustBundle": { + "inlineString": "-----BEGIN CERTIFICATE-----\nMIICcTCCAdoCCQDyGxC08cD0BDANBgkqhkiG9w0BAQsFADB9MQswCQYDVQQGEwJV\nUzELMAkGA1UECAwCQ0ExETAPBgNVBAcMCENhcmxzYmFkMQwwCgYDVQQKDANGb28x\nEDAOBgNVBAsMB2V4YW1wbGUxDzANBgNVBAMMBnBlZXItYjEdMBsGCSqGSIb3DQEJ\nARYOZm9vQHBlZXItYi5jb20wHhcNMjIwNTI2MDExNjE2WhcNMjMwNTI2MDExNjE2\nWjB9MQswCQYDVQQGEwJVUzELMAkGA1UECAwCQ0ExETAPBgNVBAcMCENhcmxzYmFk\nMQwwCgYDVQQKDANGb28xEDAOBgNVBAsMB2V4YW1wbGUxDzANBgNVBAMMBnBlZXIt\nYjEdMBsGCSqGSIb3DQEJARYOZm9vQHBlZXItYi5jb20wgZ8wDQYJKoZIhvcNAQEB\nBQADgY0AMIGJAoGBAL4i5erdZ5vKk3mzW9Qt6Wvw/WN/IpMDlL0a28wz9oDCtMLN\ncD/XQB9yT5jUwb2s4mD1lCDZtee8MHeD8zygICozufWVB+u2KvMaoA50T9GMQD0E\nz/0nz/Z703I4q13VHeTpltmEpYcfxw/7nJ3leKA34+Nj3zteJ70iqvD/TNBBAgMB\nAAEwDQYJKoZIhvcNAQELBQADgYEAbL04gicH+EIznDNhZJEb1guMBtBBJ8kujPyU\nao8xhlUuorDTLwhLpkKsOhD8619oSS8KynjEBichidQRkwxIaze0a2mrGT+tGBMf\npVz6UeCkqpde6bSJ/ozEe/2seQzKqYvRT1oUjLwYvY7OIh2DzYibOAxh6fewYAmU\n5j5qNLc=\n-----END CERTIFICATE-----\n" + } + } + ] + } + } + } + }, + "requireClientCertificate": true + } + } + } + ], + "trafficDirection": "INBOUND" + } + ], + "typeUrl": "type.googleapis.com/envoy.config.listener.v3.Listener", + "nonce": "00000001" +} \ No newline at end of file diff --git a/proto/prototest/testing.go b/proto/prototest/testing.go index b17f359b37..c196d77b3a 100644 --- a/proto/prototest/testing.go +++ b/proto/prototest/testing.go @@ -7,7 +7,7 @@ import ( "google.golang.org/protobuf/testing/protocmp" ) -func AssertDeepEqual(t *testing.T, x, y interface{}, opts ...cmp.Option) { +func AssertDeepEqual(t testing.TB, x, y interface{}, opts ...cmp.Option) { t.Helper() opts = append(opts, protocmp.Transform())