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.
197 lines
7.7 KiB
197 lines
7.7 KiB
// Copyright (c) HashiCorp, Inc. |
|
// SPDX-License-Identifier: BUSL-1.1 |
|
|
|
package extensionruntime |
|
|
|
import ( |
|
"github.com/hashicorp/consul/agent/connect" |
|
"github.com/hashicorp/consul/agent/proxycfg" |
|
"github.com/hashicorp/consul/agent/structs" |
|
"github.com/hashicorp/consul/api" |
|
"github.com/hashicorp/consul/envoyextensions/extensioncommon" |
|
) |
|
|
|
func GetRuntimeConfigurations(cfgSnap *proxycfg.ConfigSnapshot) map[api.CompoundServiceName][]extensioncommon.RuntimeConfig { |
|
extensionsMap := make(map[api.CompoundServiceName][]api.EnvoyExtension) |
|
upstreamMap := make(map[api.CompoundServiceName]*extensioncommon.UpstreamData) |
|
var kind api.ServiceKind |
|
extensionConfigurationsMap := make(map[api.CompoundServiceName][]extensioncommon.RuntimeConfig) |
|
|
|
trustDomain := "" |
|
if cfgSnap.Roots != nil { |
|
trustDomain = cfgSnap.Roots.TrustDomain |
|
} |
|
|
|
switch cfgSnap.Kind { |
|
case structs.ServiceKindConnectProxy: |
|
kind = api.ServiceKindConnectProxy |
|
outgoingKindByService := make(map[api.CompoundServiceName]api.ServiceKind) |
|
vipForService := make(map[api.CompoundServiceName]string) |
|
for uid, upstreamData := range cfgSnap.ConnectProxy.WatchedUpstreamEndpoints { |
|
sn := upstreamIDToCompoundServiceName(uid) |
|
|
|
for _, serviceNodes := range upstreamData { |
|
for _, serviceNode := range serviceNodes { |
|
if serviceNode.Service == nil { |
|
continue |
|
} |
|
vip := serviceNode.Service.TaggedAddresses[structs.TaggedAddressVirtualIP].Address |
|
if vip != "" { |
|
if _, ok := vipForService[sn]; !ok { |
|
vipForService[sn] = vip |
|
} |
|
} |
|
// Store the upstream's kind, and for ServiceKindTypical we don't do anything because we'll default |
|
// any unset upstreams to ServiceKindConnectProxy below. |
|
switch serviceNode.Service.Kind { |
|
case structs.ServiceKindTypical: |
|
default: |
|
outgoingKindByService[sn] = api.ServiceKind(serviceNode.Service.Kind) |
|
} |
|
// We only need the kind from one instance, so break once we find it. |
|
break |
|
} |
|
} |
|
} |
|
|
|
// TODO(peering): consider PeerUpstreamEndpoints in addition to DiscoveryChain |
|
// These are the discovery chains for upstreams which have the Envoy Extensions applied to the local service. |
|
for uid, chain := range cfgSnap.ConnectProxy.DiscoveryChain { |
|
compoundServiceName := upstreamIDToCompoundServiceName(uid) |
|
extensionsMap[compoundServiceName] = convertEnvoyExtensions(chain.EnvoyExtensions) |
|
|
|
primarySNI := connect.ServiceSNI(uid.Name, "", chain.Namespace, chain.Partition, cfgSnap.Datacenter, trustDomain) |
|
snis := make(map[string]struct{}) |
|
for _, t := range chain.Targets { |
|
// SNI isn't set for peered services. We don't support peered services yet. |
|
if t.SNI != "" { |
|
snis[t.SNI] = struct{}{} |
|
} |
|
} |
|
|
|
outgoingKind, ok := outgoingKindByService[compoundServiceName] |
|
if !ok { |
|
outgoingKind = api.ServiceKindConnectProxy |
|
} |
|
|
|
upstreamMap[compoundServiceName] = &extensioncommon.UpstreamData{ |
|
PrimarySNI: primarySNI, |
|
SNIs: snis, |
|
VIP: vipForService[compoundServiceName], |
|
EnvoyID: uid.EnvoyID(), |
|
OutgoingProxyKind: outgoingKind, |
|
} |
|
} |
|
// Adds extensions configured for the local service to the RuntimeConfig. This only applies to |
|
// connect-proxies because extensions are either global or tied to a specific service, so the terminating |
|
// gateway's Envoy resources for the local service (i.e not to upstreams) would never need to be modified. |
|
localSvc := api.CompoundServiceName{ |
|
Name: cfgSnap.Proxy.DestinationServiceName, |
|
Namespace: cfgSnap.ProxyID.NamespaceOrDefault(), |
|
Partition: cfgSnap.ProxyID.PartitionOrEmpty(), |
|
} |
|
extensionConfigurationsMap[localSvc] = []extensioncommon.RuntimeConfig{} |
|
cfgSnapExts := convertEnvoyExtensions(cfgSnap.Proxy.EnvoyExtensions) |
|
for _, ext := range cfgSnapExts { |
|
extCfg := extensioncommon.RuntimeConfig{ |
|
EnvoyExtension: ext, |
|
ServiceName: localSvc, |
|
IsSourcedFromUpstream: false, |
|
Upstreams: upstreamMap, |
|
Kind: kind, |
|
Protocol: proxyConfigProtocol(cfgSnap.Proxy.Config), |
|
} |
|
extensionConfigurationsMap[localSvc] = append(extensionConfigurationsMap[localSvc], extCfg) |
|
} |
|
case structs.ServiceKindTerminatingGateway: |
|
kind = api.ServiceKindTerminatingGateway |
|
for svc, c := range cfgSnap.TerminatingGateway.ServiceConfigs { |
|
compoundServiceName := serviceNameToCompoundServiceName(svc) |
|
extensionsMap[compoundServiceName] = convertEnvoyExtensions(c.EnvoyExtensions) |
|
|
|
primarySNI := connect.ServiceSNI(svc.Name, "", svc.NamespaceOrDefault(), svc.PartitionOrDefault(), cfgSnap.Datacenter, trustDomain) |
|
envoyID := proxycfg.NewUpstreamIDFromServiceName(svc) |
|
|
|
snis := map[string]struct{}{primarySNI: {}} |
|
|
|
resolver, hasResolver := cfgSnap.TerminatingGateway.ServiceResolvers[svc] |
|
if hasResolver { |
|
for subsetName := range resolver.Subsets { |
|
sni := connect.ServiceSNI(svc.Name, subsetName, svc.NamespaceOrDefault(), svc.PartitionOrDefault(), cfgSnap.Datacenter, trustDomain) |
|
snis[sni] = struct{}{} |
|
} |
|
} |
|
|
|
upstreamMap[compoundServiceName] = &extensioncommon.UpstreamData{ |
|
PrimarySNI: primarySNI, |
|
SNIs: snis, |
|
EnvoyID: envoyID.EnvoyID(), |
|
OutgoingProxyKind: api.ServiceKindTerminatingGateway, |
|
} |
|
|
|
} |
|
} |
|
|
|
// If applicable, include extension configuration for remote upstreams of the local service. |
|
// This only applies to specific extensions authorized to apply to remote proxies. |
|
for svc, exts := range extensionsMap { |
|
extensionConfigurationsMap[svc] = []extensioncommon.RuntimeConfig{} |
|
for _, ext := range exts { |
|
if appliesToRemoteDownstreams(ext) { |
|
extCfg := extensioncommon.RuntimeConfig{ |
|
EnvoyExtension: ext, |
|
Kind: kind, |
|
ServiceName: svc, |
|
IsSourcedFromUpstream: true, |
|
Upstreams: upstreamMap, |
|
Protocol: proxyConfigProtocol(cfgSnap.Proxy.Config), |
|
} |
|
extensionConfigurationsMap[svc] = append(extensionConfigurationsMap[svc], extCfg) |
|
} |
|
} |
|
} |
|
|
|
return extensionConfigurationsMap |
|
} |
|
|
|
func serviceNameToCompoundServiceName(svc structs.ServiceName) api.CompoundServiceName { |
|
return api.CompoundServiceName{ |
|
Name: svc.Name, |
|
Partition: svc.PartitionOrDefault(), |
|
Namespace: svc.NamespaceOrDefault(), |
|
} |
|
} |
|
|
|
func upstreamIDToCompoundServiceName(uid proxycfg.UpstreamID) api.CompoundServiceName { |
|
return api.CompoundServiceName{ |
|
Name: uid.Name, |
|
Partition: uid.PartitionOrDefault(), |
|
Namespace: uid.NamespaceOrDefault(), |
|
} |
|
} |
|
|
|
func convertEnvoyExtensions(structExtensions structs.EnvoyExtensions) []api.EnvoyExtension { |
|
return structExtensions.ToAPI() |
|
} |
|
|
|
func proxyConfigProtocol(cfg map[string]any) string { |
|
if p, exists := cfg["protocol"]; exists { |
|
if protocol, ok := p.(string); ok { |
|
return protocol |
|
} |
|
} |
|
return "" |
|
} |
|
|
|
// appliesToRemoteDownstreams returns true if the given extension should be applied to remote downstream proxies of the |
|
// service targeted by the extension, rather than just the local proxy. In the context of GetRuntimeConfigurations, this |
|
// determines whether the extension should apply to the local proxy (a downstream of the configured service). |
|
// |
|
// Currently, only the AWS Lambda and Validate extensions are allowed to apply to downstream proxies. |
|
// |
|
// See extensioncommon.RuntimeConfig.IsSourcedFromUpstream and UpstreamEnvoyExtender doc for more information. We make |
|
// this check here out of precaution s.t. even if an unauthorized extension is erroneously constructed with the |
|
// UpstreamEnvoyExtender, this check will not allow the upstream extension configuration to be provided. |
|
func appliesToRemoteDownstreams(extension api.EnvoyExtension) bool { |
|
return extension.Name == api.BuiltinAWSLambdaExtension || extension.Name == api.BuiltinValidateExtension |
|
}
|
|
|