mirror of https://github.com/hashicorp/consul
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
766 lines
26 KiB
766 lines
26 KiB
package proxycfg |
|
|
|
import ( |
|
"context" |
|
"fmt" |
|
"sort" |
|
"strings" |
|
|
|
"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? |
|
// A shared data structure that contains information about discovered upstreams |
|
type ConfigSnapshotUpstreams struct { |
|
Leaf *structs.IssuedCert |
|
|
|
MeshConfig *structs.MeshConfigEntry |
|
MeshConfigSet bool |
|
|
|
// DiscoveryChain is a map of UpstreamID -> CompiledDiscoveryChain's, and |
|
// is used to determine what services could be targeted by this upstream. |
|
// We then instantiate watches for those targets. |
|
DiscoveryChain map[UpstreamID]*structs.CompiledDiscoveryChain |
|
|
|
// WatchedDiscoveryChains is a map of UpstreamID -> CancelFunc's |
|
// in order to cancel any watches when the proxy's configuration is |
|
// changed. Ingress gateways and transparent proxies need this because |
|
// discovery chain watches are added and removed through the lifecycle |
|
// of a single proxycfg state instance. |
|
WatchedDiscoveryChains map[UpstreamID]context.CancelFunc |
|
|
|
// WatchedUpstreams is a map of UpstreamID -> (map of TargetID -> |
|
// CancelFunc's) in order to cancel any watches when the configuration is |
|
// changed. |
|
WatchedUpstreams map[UpstreamID]map[string]context.CancelFunc |
|
|
|
// WatchedUpstreamEndpoints is a map of UpstreamID -> (map of |
|
// TargetID -> CheckServiceNodes) and is used to determine the backing |
|
// endpoints of an upstream. |
|
WatchedUpstreamEndpoints map[UpstreamID]map[string]structs.CheckServiceNodes |
|
|
|
// WatchedUpstreamPeerTrustBundles is a map of (PeerName -> CancelFunc) in order to cancel |
|
// watches for peer trust bundles any time the list of upstream peers changes. |
|
WatchedUpstreamPeerTrustBundles map[string]context.CancelFunc |
|
|
|
// UpstreamPeerTrustBundles is a map of (PeerName -> PeeringTrustBundle). |
|
// It is used to store trust bundles for upstream TLS transport sockets. |
|
UpstreamPeerTrustBundles map[string]*pbpeering.PeeringTrustBundle |
|
|
|
// WatchedGateways is a map of UpstreamID -> (map of GatewayKey.String() -> |
|
// CancelFunc) in order to cancel watches for mesh gateways |
|
WatchedGateways map[UpstreamID]map[string]context.CancelFunc |
|
|
|
// WatchedGatewayEndpoints is a map of UpstreamID -> (map of |
|
// GatewayKey.String() -> CheckServiceNodes) and is used to determine the |
|
// backing endpoints of a mesh gateway. |
|
WatchedGatewayEndpoints map[UpstreamID]map[string]structs.CheckServiceNodes |
|
|
|
// UpstreamConfig is a map to an upstream's configuration. |
|
UpstreamConfig map[UpstreamID]*structs.Upstream |
|
|
|
// PassthroughEndpoints is a map of: UpstreamID -> (map of TargetID -> |
|
// (set of IP addresses)). It contains the upstream endpoints that |
|
// can be dialed directly by a transparent proxy. |
|
PassthroughUpstreams map[UpstreamID]map[string]map[string]struct{} |
|
|
|
// PassthroughIndices is a map of: address -> indexedTarget. |
|
// It is used to track the modify index associated with a passthrough address. |
|
// Tracking this index helps break ties when a single address is shared by |
|
// more than one upstream due to a race. |
|
PassthroughIndices map[string]indexedTarget |
|
|
|
// IntentionUpstreams is a set of upstreams inferred from intentions. |
|
// |
|
// This list only applies to proxies registered in 'transparent' mode. |
|
IntentionUpstreams map[UpstreamID]struct{} |
|
|
|
// PeerUpstreamEndpoints is a map of UpstreamID -> (set of IP addresses) |
|
// and used to determine the backing endpoints of an upstream in another |
|
// peer. |
|
PeerUpstreamEndpoints map[UpstreamID]structs.CheckServiceNodes |
|
PeerUpstreamEndpointsUseHostnames map[UpstreamID]struct{} |
|
} |
|
|
|
// indexedTarget is used to associate the Raft modify index of a resource |
|
// with the corresponding upstream target. |
|
type indexedTarget struct { |
|
upstreamID UpstreamID |
|
targetID string |
|
idx uint64 |
|
} |
|
|
|
type GatewayKey struct { |
|
Datacenter string |
|
Partition string |
|
} |
|
|
|
func (k GatewayKey) String() string { |
|
resp := k.Datacenter |
|
if !acl.IsDefaultPartition(k.Partition) { |
|
resp = k.Partition + "." + resp |
|
} |
|
return resp |
|
} |
|
|
|
func (k GatewayKey) IsEmpty() bool { |
|
return k.Partition == "" && k.Datacenter == "" |
|
} |
|
|
|
func (k GatewayKey) Matches(dc, partition string) bool { |
|
return acl.EqualPartitions(k.Partition, partition) && k.Datacenter == dc |
|
} |
|
|
|
func gatewayKeyFromString(s string) GatewayKey { |
|
split := strings.SplitN(s, ".", 2) |
|
|
|
if len(split) == 1 { |
|
return GatewayKey{Datacenter: split[0], Partition: acl.DefaultPartitionName} |
|
} |
|
return GatewayKey{Partition: split[0], Datacenter: split[1]} |
|
} |
|
|
|
type configSnapshotConnectProxy struct { |
|
ConfigSnapshotUpstreams |
|
|
|
InboundPeerTrustBundlesSet bool |
|
InboundPeerTrustBundles []*pbpeering.PeeringTrustBundle |
|
|
|
WatchedServiceChecks map[structs.ServiceID][]structs.CheckType // TODO: missing garbage collection |
|
PreparedQueryEndpoints map[UpstreamID]structs.CheckServiceNodes // DEPRECATED:see:WatchedUpstreamEndpoints |
|
|
|
// NOTE: Intentions stores a list of lists as returned by the Intentions |
|
// Match RPC. So far we only use the first list as the list of matching |
|
// intentions. |
|
Intentions structs.Intentions |
|
IntentionsSet bool |
|
} |
|
|
|
// isEmpty is a test helper |
|
func (c *configSnapshotConnectProxy) isEmpty() bool { |
|
if c == nil { |
|
return true |
|
} |
|
return c.Leaf == nil && |
|
!c.IntentionsSet && |
|
len(c.DiscoveryChain) == 0 && |
|
len(c.WatchedDiscoveryChains) == 0 && |
|
len(c.WatchedUpstreams) == 0 && |
|
len(c.WatchedUpstreamEndpoints) == 0 && |
|
len(c.WatchedUpstreamPeerTrustBundles) == 0 && |
|
len(c.UpstreamPeerTrustBundles) == 0 && |
|
len(c.WatchedGateways) == 0 && |
|
len(c.WatchedGatewayEndpoints) == 0 && |
|
len(c.WatchedServiceChecks) == 0 && |
|
len(c.PreparedQueryEndpoints) == 0 && |
|
len(c.UpstreamConfig) == 0 && |
|
len(c.PassthroughUpstreams) == 0 && |
|
len(c.IntentionUpstreams) == 0 && |
|
!c.InboundPeerTrustBundlesSet && |
|
!c.MeshConfigSet && |
|
len(c.PeerUpstreamEndpoints) == 0 && |
|
len(c.PeerUpstreamEndpointsUseHostnames) == 0 |
|
} |
|
|
|
type configSnapshotTerminatingGateway struct { |
|
MeshConfig *structs.MeshConfigEntry |
|
MeshConfigSet bool |
|
|
|
// WatchedServices is a map of service name to a cancel function. This cancel |
|
// function is tied to the watch of linked service instances for the given |
|
// id. If the linked services watch would indicate the removal of |
|
// a service altogether we then cancel watching that service for its endpoints. |
|
WatchedServices map[structs.ServiceName]context.CancelFunc |
|
|
|
// WatchedIntentions is a map of service name to a cancel function. |
|
// This cancel function is tied to the watch of intentions for linked services. |
|
// As with WatchedServices, intention watches will be cancelled when services |
|
// are no longer linked to the gateway. |
|
WatchedIntentions map[structs.ServiceName]context.CancelFunc |
|
|
|
// NOTE: Intentions stores a map of list of lists as returned by the Intentions |
|
// Match RPC. So far we only use the first list as the list of matching |
|
// intentions. |
|
// |
|
// A key being present implies that we have gotten at least one watch reply for the |
|
// service. This is logically the same as ConnectProxy.IntentionsSet==true |
|
Intentions map[structs.ServiceName]structs.Intentions |
|
|
|
// WatchedLeaves is a map of ServiceName to a cancel function. |
|
// This cancel function is tied to the watch of leaf certs for linked services. |
|
// As with WatchedServices, leaf watches will be cancelled when services |
|
// are no longer linked to the gateway. |
|
WatchedLeaves map[structs.ServiceName]context.CancelFunc |
|
|
|
// ServiceLeaves is a map of ServiceName to a leaf cert. |
|
// Terminating gateways will present different certificates depending |
|
// on the service that the caller is trying to reach. |
|
ServiceLeaves map[structs.ServiceName]*structs.IssuedCert |
|
|
|
// WatchedConfigs is a map of ServiceName to a cancel function. This cancel |
|
// function is tied to the watch of service configs for linked services. As |
|
// with WatchedServices, service config watches will be cancelled when |
|
// services are no longer linked to the gateway. |
|
WatchedConfigs map[structs.ServiceName]context.CancelFunc |
|
|
|
// ServiceConfigs is a map of service name to the resolved service config |
|
// for that service. |
|
ServiceConfigs map[structs.ServiceName]*structs.ServiceConfigResponse |
|
|
|
// WatchedResolvers is a map of ServiceName to a cancel function. |
|
// This cancel function is tied to the watch of resolvers for linked services. |
|
// As with WatchedServices, resolver watches will be cancelled when services |
|
// are no longer linked to the gateway. |
|
WatchedResolvers map[structs.ServiceName]context.CancelFunc |
|
|
|
// ServiceResolvers is a map of service name to an associated |
|
// service-resolver config entry for that service. |
|
ServiceResolvers map[structs.ServiceName]*structs.ServiceResolverConfigEntry |
|
ServiceResolversSet map[structs.ServiceName]bool |
|
|
|
// ServiceGroups is a map of service name to the service instances of that |
|
// service in the local datacenter. |
|
ServiceGroups map[structs.ServiceName]structs.CheckServiceNodes |
|
|
|
// GatewayServices is a map of service name to the config entry association |
|
// between the gateway and a service. TLS configuration stored here is |
|
// used for TLS origination from the gateway to the linked service. |
|
// This map does not include GatewayServices that represent Endpoints to external |
|
// destinations. |
|
GatewayServices map[structs.ServiceName]structs.GatewayService |
|
|
|
// DestinationServices is a map of service name to GatewayServices that represent |
|
// a destination to an external destination of the service mesh. |
|
DestinationServices map[structs.ServiceName]structs.GatewayService |
|
|
|
// 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 |
|
} |
|
|
|
// ValidServices returns the list of service keys that have enough data to be emitted. |
|
func (c *configSnapshotTerminatingGateway) ValidServices() []structs.ServiceName { |
|
out := make([]structs.ServiceName, 0, len(c.ServiceGroups)) |
|
for svc := range c.ServiceGroups { |
|
// It only counts if ALL of our watches have come back (with data or not). |
|
|
|
// Skip the service if we don't know if there is a resolver or not. |
|
if _, ok := c.ServiceResolversSet[svc]; !ok { |
|
continue |
|
} |
|
|
|
// Skip the service if we don't have a cert to present for mTLS. |
|
if cert, ok := c.ServiceLeaves[svc]; !ok || cert == nil { |
|
continue |
|
} |
|
|
|
// Skip the service if we haven't gotten our intentions yet. |
|
if _, intentionsSet := c.Intentions[svc]; !intentionsSet { |
|
continue |
|
} |
|
|
|
// Skip the service if we haven't gotten our service config yet to know |
|
// the protocol. |
|
if _, ok := c.ServiceConfigs[svc]; !ok { |
|
continue |
|
} |
|
|
|
out = append(out, svc) |
|
} |
|
return out |
|
} |
|
|
|
// ValidDestinations returns the list of service keys (that represent exclusively endpoints) that have enough data to be emitted. |
|
func (c *configSnapshotTerminatingGateway) ValidDestinations() []structs.ServiceName { |
|
out := make([]structs.ServiceName, 0, len(c.DestinationServices)) |
|
for svc := range c.DestinationServices { |
|
// It only counts if ALL of our watches have come back (with data or not). |
|
|
|
// Skip the service if we don't have a cert to present for mTLS. |
|
if cert, ok := c.ServiceLeaves[svc]; !ok || cert == nil { |
|
continue |
|
} |
|
|
|
// Skip the service if we haven't gotten our intentions yet. |
|
if _, intentionsSet := c.Intentions[svc]; !intentionsSet { |
|
continue |
|
} |
|
|
|
// Skip the service if we haven't gotten our service config yet to know |
|
// the protocol. |
|
if _, ok := c.ServiceConfigs[svc]; !ok || c.ServiceConfigs[svc].Destination.Address == "" { |
|
continue |
|
} |
|
|
|
out = append(out, svc) |
|
} |
|
return out |
|
} |
|
|
|
// isEmpty is a test helper |
|
func (c *configSnapshotTerminatingGateway) isEmpty() bool { |
|
if c == nil { |
|
return true |
|
} |
|
return len(c.ServiceLeaves) == 0 && |
|
len(c.WatchedLeaves) == 0 && |
|
len(c.WatchedIntentions) == 0 && |
|
len(c.Intentions) == 0 && |
|
len(c.ServiceGroups) == 0 && |
|
len(c.WatchedServices) == 0 && |
|
len(c.ServiceResolvers) == 0 && |
|
len(c.ServiceResolversSet) == 0 && |
|
len(c.WatchedResolvers) == 0 && |
|
len(c.ServiceConfigs) == 0 && |
|
len(c.WatchedConfigs) == 0 && |
|
len(c.GatewayServices) == 0 && |
|
len(c.DestinationServices) == 0 && |
|
len(c.HostnameServices) == 0 && |
|
!c.MeshConfigSet |
|
} |
|
|
|
type configSnapshotMeshGateway struct { |
|
// WatchedServices is a map of service name to a cancel function. This cancel |
|
// function is tied to the watch of connect enabled services for the given |
|
// id. If the main datacenter services watch would indicate the removal of |
|
// a service altogether we then cancel watching that service for its |
|
// connect endpoints. |
|
WatchedServices map[structs.ServiceName]context.CancelFunc |
|
|
|
// WatchedServicesSet indicates that the watch on the datacenters services |
|
// has completed. Even when there are no connect services, this being set |
|
// (and the Connect roots being available) will be enough for the config |
|
// snapshot to be considered valid. In the case of Envoy, this allows it to |
|
// start its listeners even when no services would be proxied and allow its |
|
// health check to pass. |
|
WatchedServicesSet bool |
|
|
|
// WatchedGateways is a map of GatewayKeys to a cancel function. |
|
// This cancel function is tied to the watch of mesh-gateway services in |
|
// that datacenter/partition. |
|
WatchedGateways map[string]context.CancelFunc |
|
|
|
// ServiceGroups is a map of service name to the service instances of that |
|
// service in the local datacenter. |
|
ServiceGroups map[structs.ServiceName]structs.CheckServiceNodes |
|
|
|
// ServiceResolvers is a map of service name to an associated |
|
// service-resolver config entry for that service. |
|
ServiceResolvers map[structs.ServiceName]*structs.ServiceResolverConfigEntry |
|
|
|
// GatewayGroups is a map of datacenter names to services of kind |
|
// mesh-gateway in that datacenter. |
|
GatewayGroups map[string]structs.CheckServiceNodes |
|
|
|
// FedStateGateways is a map of datacenter names to mesh gateways in that |
|
// datacenter. |
|
FedStateGateways map[string]structs.CheckServiceNodes |
|
|
|
// ConsulServers is the list of consul servers in this datacenter. |
|
ConsulServers structs.CheckServiceNodes |
|
|
|
// HostnameDatacenters is a map of datacenters to mesh gateway instances with a hostname as the address. |
|
// If hostnames are configured they must be provided to Envoy via CDS not EDS. |
|
HostnameDatacenters map[string]structs.CheckServiceNodes |
|
|
|
// ExportedServicesSlice is a sorted slice of services that are exported to |
|
// connected peers. |
|
ExportedServicesSlice []structs.ServiceName |
|
|
|
// ExportedServicesWithPeers is a map of exported service name to a sorted |
|
// slice of peers that they are exported to. |
|
ExportedServicesWithPeers map[structs.ServiceName][]string |
|
|
|
// ExportedServicesSet indicates that the watch on the list of |
|
// peer-exported services has completed at least once. |
|
ExportedServicesSet bool |
|
|
|
// DiscoveryChain is a map of the peer-exported service names to their |
|
// local compiled discovery chain. This will be populated regardless of |
|
// L4/L7 status of the chain. |
|
DiscoveryChain map[structs.ServiceName]*structs.CompiledDiscoveryChain |
|
|
|
// WatchedDiscoveryChains is a map of peer-exported service names to a |
|
// cancel function. |
|
WatchedDiscoveryChains map[structs.ServiceName]context.CancelFunc |
|
|
|
// MeshConfig is the mesh config entry that should be used for services |
|
// fronted by this mesh gateway. |
|
MeshConfig *structs.MeshConfigEntry |
|
|
|
// MeshConfigSet indicates that the watch on the mesh config entry has |
|
// completed at least once. |
|
MeshConfigSet bool |
|
|
|
// Leaf is the leaf cert to be used by this mesh gateway. |
|
Leaf *structs.IssuedCert |
|
|
|
// LeafCertWatchCancel is a CancelFunc to use when refreshing this gateway's |
|
// leaf cert watch with different parameters. |
|
LeafCertWatchCancel context.CancelFunc |
|
|
|
// PeeringTrustBundles is the list of trust bundles for peers where |
|
// services have been exported to using this mesh gateway. |
|
PeeringTrustBundles []*pbpeering.PeeringTrustBundle |
|
|
|
// PeeringTrustBundlesSet indicates that the watch on the peer trust |
|
// bundles has completed at least once. |
|
PeeringTrustBundlesSet bool |
|
} |
|
|
|
func (c *configSnapshotMeshGateway) IsServiceExported(svc structs.ServiceName) bool { |
|
if c == nil || len(c.ExportedServicesWithPeers) == 0 { |
|
return false |
|
} |
|
|
|
_, ok := c.ExportedServicesWithPeers[svc] |
|
return ok |
|
} |
|
|
|
func (c *configSnapshotMeshGateway) GatewayKeys() []GatewayKey { |
|
sz1, sz2 := len(c.GatewayGroups), len(c.FedStateGateways) |
|
|
|
sz := sz1 |
|
if sz2 > sz1 { |
|
sz = sz2 |
|
} |
|
|
|
keys := make([]GatewayKey, 0, sz) |
|
for key := range c.FedStateGateways { |
|
keys = append(keys, gatewayKeyFromString(key)) |
|
} |
|
for key := range c.GatewayGroups { |
|
gk := gatewayKeyFromString(key) |
|
if _, ok := c.FedStateGateways[gk.Datacenter]; !ok { |
|
keys = append(keys, gk) |
|
} |
|
} |
|
|
|
// Always sort the results to ensure we generate deterministic things over |
|
// xDS, such as mesh-gateway listener filter chains. |
|
sort.Slice(keys, func(i, j int) bool { |
|
if keys[i].Datacenter != keys[j].Datacenter { |
|
return keys[i].Datacenter < keys[j].Datacenter |
|
} |
|
return keys[i].Partition < keys[j].Partition |
|
}) |
|
return keys |
|
} |
|
|
|
// isEmpty is a test helper |
|
func (c *configSnapshotMeshGateway) isEmpty() bool { |
|
if c == nil { |
|
return true |
|
} |
|
return len(c.WatchedServices) == 0 && |
|
!c.WatchedServicesSet && |
|
len(c.WatchedGateways) == 0 && |
|
len(c.ServiceGroups) == 0 && |
|
len(c.ServiceResolvers) == 0 && |
|
len(c.GatewayGroups) == 0 && |
|
len(c.FedStateGateways) == 0 && |
|
len(c.ConsulServers) == 0 && |
|
len(c.HostnameDatacenters) == 0 && |
|
c.isEmptyPeering() |
|
} |
|
|
|
// isEmptyPeering is a test helper |
|
func (c *configSnapshotMeshGateway) isEmptyPeering() bool { |
|
if c == nil { |
|
return true |
|
} |
|
|
|
return len(c.ExportedServicesSlice) == 0 && |
|
len(c.ExportedServicesWithPeers) == 0 && |
|
!c.ExportedServicesSet && |
|
len(c.DiscoveryChain) == 0 && |
|
len(c.WatchedDiscoveryChains) == 0 && |
|
!c.MeshConfigSet && |
|
c.LeafCertWatchCancel == nil && |
|
c.Leaf == nil && |
|
len(c.PeeringTrustBundles) == 0 && |
|
!c.PeeringTrustBundlesSet |
|
} |
|
|
|
type configSnapshotIngressGateway struct { |
|
ConfigSnapshotUpstreams |
|
|
|
// TLSConfig is the gateway-level TLS configuration. Listener/service level |
|
// config is preserved in the Listeners map below. |
|
TLSConfig structs.GatewayTLSConfig |
|
|
|
// GatewayConfigLoaded is used to determine if we have received the initial |
|
// ingress-gateway config entry yet. |
|
GatewayConfigLoaded bool |
|
|
|
// Hosts is the list of extra host entries to add to our leaf cert's DNS SANs. |
|
Hosts []string |
|
HostsSet bool |
|
|
|
// LeafCertWatchCancel is a CancelFunc to use when refreshing this gateway's |
|
// leaf cert watch with different parameters. |
|
LeafCertWatchCancel context.CancelFunc |
|
|
|
// Upstreams is a list of upstreams this ingress gateway should serve traffic |
|
// to. This is constructed from the ingress-gateway config entry, and uses |
|
// the GatewayServices RPC to retrieve them. |
|
Upstreams map[IngressListenerKey]structs.Upstreams |
|
|
|
// UpstreamsSet is the unique set of UpstreamID the gateway routes to. |
|
UpstreamsSet map[UpstreamID]struct{} |
|
|
|
// Listeners is the original listener config from the ingress-gateway config |
|
// entry to save us trying to pass fields through Upstreams |
|
Listeners map[IngressListenerKey]structs.IngressListener |
|
} |
|
|
|
// isEmpty is a test helper |
|
func (c *configSnapshotIngressGateway) isEmpty() bool { |
|
if c == nil { |
|
return true |
|
} |
|
return len(c.Upstreams) == 0 && |
|
len(c.UpstreamsSet) == 0 && |
|
len(c.DiscoveryChain) == 0 && |
|
len(c.WatchedUpstreams) == 0 && |
|
len(c.WatchedUpstreamEndpoints) == 0 && |
|
!c.MeshConfigSet |
|
} |
|
|
|
type IngressListenerKey struct { |
|
Protocol string |
|
Port int |
|
} |
|
|
|
func (k *IngressListenerKey) RouteName() string { |
|
return fmt.Sprintf("%d", k.Port) |
|
} |
|
|
|
func IngressListenerKeyFromGWService(s structs.GatewayService) IngressListenerKey { |
|
return IngressListenerKey{Protocol: s.Protocol, Port: s.Port} |
|
} |
|
|
|
func IngressListenerKeyFromListener(l structs.IngressListener) IngressListenerKey { |
|
return IngressListenerKey{Protocol: l.Protocol, Port: l.Port} |
|
} |
|
|
|
// ConfigSnapshot captures all the resulting config needed for a proxy instance. |
|
// It is meant to be point-in-time coherent and is used to deliver the current |
|
// config state to observers who need it to be pushed in (e.g. XDS server). |
|
type ConfigSnapshot struct { |
|
Kind structs.ServiceKind |
|
Service string |
|
ProxyID ProxyID |
|
Address string |
|
Port int |
|
ServiceMeta map[string]string |
|
TaggedAddresses map[string]structs.ServiceAddress |
|
Proxy structs.ConnectProxyConfig |
|
Datacenter string |
|
IntentionDefaultAllow bool |
|
Locality GatewayKey |
|
|
|
ServerSNIFn ServerSNIFunc |
|
Roots *structs.IndexedCARoots |
|
|
|
// connect-proxy specific |
|
ConnectProxy configSnapshotConnectProxy |
|
|
|
// terminating-gateway specific |
|
TerminatingGateway configSnapshotTerminatingGateway |
|
|
|
// mesh-gateway specific |
|
MeshGateway configSnapshotMeshGateway |
|
|
|
// ingress-gateway specific |
|
IngressGateway configSnapshotIngressGateway |
|
} |
|
|
|
// Valid returns whether or not the snapshot has all required fields filled yet. |
|
func (s *ConfigSnapshot) Valid() bool { |
|
switch s.Kind { |
|
case structs.ServiceKindConnectProxy: |
|
if s.Proxy.Mode == structs.ProxyModeTransparent && !s.ConnectProxy.MeshConfigSet { |
|
return false |
|
} |
|
return s.Roots != nil && |
|
s.ConnectProxy.Leaf != nil && |
|
s.ConnectProxy.IntentionsSet && |
|
s.ConnectProxy.MeshConfigSet |
|
|
|
case structs.ServiceKindTerminatingGateway: |
|
return s.Roots != nil && |
|
s.TerminatingGateway.MeshConfigSet |
|
|
|
case structs.ServiceKindMeshGateway: |
|
if s.ServiceMeta[structs.MetaWANFederationKey] == "1" { |
|
if len(s.MeshGateway.ConsulServers) == 0 { |
|
return false |
|
} |
|
} |
|
return s.Roots != nil && |
|
(s.MeshGateway.WatchedServicesSet || len(s.MeshGateway.ServiceGroups) > 0) && |
|
s.MeshGateway.ExportedServicesSet && |
|
s.MeshGateway.MeshConfigSet && |
|
s.MeshGateway.PeeringTrustBundlesSet |
|
|
|
case structs.ServiceKindIngressGateway: |
|
return s.Roots != nil && |
|
s.IngressGateway.Leaf != nil && |
|
s.IngressGateway.GatewayConfigLoaded && |
|
s.IngressGateway.HostsSet && |
|
s.IngressGateway.MeshConfigSet |
|
default: |
|
return false |
|
} |
|
} |
|
|
|
// Clone makes a deep copy of the snapshot we can send to other goroutines |
|
// without worrying that they will racily read or mutate shared maps etc. |
|
func (s *ConfigSnapshot) Clone() (*ConfigSnapshot, error) { |
|
snapCopy, err := copystructure.Copy(s) |
|
if err != nil { |
|
return nil, err |
|
} |
|
|
|
snap := snapCopy.(*ConfigSnapshot) |
|
|
|
// nil these out as anything receiving one of these clones does not need them and should never "cancel" our watches |
|
switch s.Kind { |
|
case structs.ServiceKindConnectProxy: |
|
// common with connect-proxy and ingress-gateway |
|
snap.ConnectProxy.WatchedUpstreams = nil |
|
snap.ConnectProxy.WatchedGateways = nil |
|
snap.ConnectProxy.WatchedDiscoveryChains = nil |
|
snap.ConnectProxy.WatchedUpstreamPeerTrustBundles = nil |
|
case structs.ServiceKindTerminatingGateway: |
|
snap.TerminatingGateway.WatchedServices = nil |
|
snap.TerminatingGateway.WatchedIntentions = nil |
|
snap.TerminatingGateway.WatchedLeaves = nil |
|
snap.TerminatingGateway.WatchedConfigs = nil |
|
snap.TerminatingGateway.WatchedResolvers = nil |
|
case structs.ServiceKindMeshGateway: |
|
snap.MeshGateway.WatchedGateways = nil |
|
snap.MeshGateway.WatchedServices = nil |
|
case structs.ServiceKindIngressGateway: |
|
// common with connect-proxy and ingress-gateway |
|
snap.IngressGateway.WatchedUpstreams = nil |
|
snap.IngressGateway.WatchedGateways = nil |
|
snap.IngressGateway.WatchedDiscoveryChains = nil |
|
snap.IngressGateway.WatchedUpstreamPeerTrustBundles = nil |
|
// only ingress-gateway |
|
snap.IngressGateway.LeafCertWatchCancel = nil |
|
} |
|
|
|
return snap, nil |
|
} |
|
|
|
func (s *ConfigSnapshot) Leaf() *structs.IssuedCert { |
|
switch s.Kind { |
|
case structs.ServiceKindConnectProxy: |
|
return s.ConnectProxy.Leaf |
|
case structs.ServiceKindIngressGateway: |
|
return s.IngressGateway.Leaf |
|
case structs.ServiceKindMeshGateway: |
|
return s.MeshGateway.Leaf |
|
default: |
|
return nil |
|
} |
|
} |
|
|
|
func (s *ConfigSnapshot) PeeringTrustBundles() []*pbpeering.PeeringTrustBundle { |
|
switch s.Kind { |
|
case structs.ServiceKindConnectProxy: |
|
return s.ConnectProxy.InboundPeerTrustBundles |
|
case structs.ServiceKindMeshGateway: |
|
return s.MeshGateway.PeeringTrustBundles |
|
default: |
|
return nil |
|
} |
|
} |
|
|
|
// RootPEMs returns all PEM-encoded public certificates for the root CA. |
|
func (s *ConfigSnapshot) RootPEMs() string { |
|
var rootPEMs string |
|
for _, root := range s.Roots.Roots { |
|
rootPEMs += lib.EnsureTrailingNewline(root.RootCert) |
|
} |
|
return rootPEMs |
|
} |
|
|
|
func (s *ConfigSnapshot) MeshConfig() *structs.MeshConfigEntry { |
|
switch s.Kind { |
|
case structs.ServiceKindConnectProxy: |
|
return s.ConnectProxy.MeshConfig |
|
case structs.ServiceKindIngressGateway: |
|
return s.IngressGateway.MeshConfig |
|
case structs.ServiceKindTerminatingGateway: |
|
return s.TerminatingGateway.MeshConfig |
|
case structs.ServiceKindMeshGateway: |
|
return s.MeshGateway.MeshConfig |
|
default: |
|
return nil |
|
} |
|
} |
|
|
|
func (s *ConfigSnapshot) MeshConfigTLSIncoming() *structs.MeshDirectionalTLSConfig { |
|
mesh := s.MeshConfig() |
|
if mesh == nil || mesh.TLS == nil { |
|
return nil |
|
} |
|
return mesh.TLS.Incoming |
|
} |
|
|
|
func (s *ConfigSnapshot) MeshConfigTLSOutgoing() *structs.MeshDirectionalTLSConfig { |
|
mesh := s.MeshConfig() |
|
if mesh == nil || mesh.TLS == nil { |
|
return nil |
|
} |
|
return mesh.TLS.Outgoing |
|
} |
|
|
|
func (u *ConfigSnapshotUpstreams) UpstreamPeerMeta(uid UpstreamID) structs.PeeringServiceMeta { |
|
nodes := u.PeerUpstreamEndpoints[uid] |
|
if len(nodes) == 0 { |
|
return structs.PeeringServiceMeta{} |
|
} |
|
|
|
// In agent/rpc/peering/subscription_manager.go we denormalize the |
|
// PeeringServiceMeta data onto each replicated service instance to convey |
|
// this information back to the importing side of the peering. |
|
// |
|
// This data is guaranteed (subject to any eventual consistency lag around |
|
// updates) to be the same across all instances, so we only need to take |
|
// the first item. |
|
// |
|
// TODO(peering): consider replicating this "common to all instances" data |
|
// using a different replication type and persist it separately in the |
|
// catalog to avoid this weird construction. |
|
csn := nodes[0] |
|
if csn.Service == nil { |
|
return structs.PeeringServiceMeta{} |
|
} |
|
return *csn.Service.Connect.PeerMeta |
|
} |
|
|
|
func (u *ConfigSnapshotUpstreams) PeeredUpstreamIDs() []UpstreamID { |
|
out := make([]UpstreamID, 0, len(u.UpstreamConfig)) |
|
for uid := range u.UpstreamConfig { |
|
if uid.Peer == "" { |
|
continue |
|
} |
|
|
|
if _, ok := u.UpstreamPeerTrustBundles[uid.Peer]; uid.Peer != "" && !ok { |
|
// The trust bundle for this upstream is not available yet, skip for now. |
|
continue |
|
} |
|
|
|
out = append(out, uid) |
|
} |
|
return out |
|
}
|
|
|