Merge pull request #12676 from hashicorp/implement-lambda-patching
Implement Lambda Patching in the Serverless Plugin
This commit is contained in:
commit
9b291d362a
|
@ -642,13 +642,19 @@ func TestConfigSnapshotTerminatingGatewayIgnoreExtraResolvers(t testing.T) *Conf
|
|||
})
|
||||
}
|
||||
|
||||
func TestConfigSnapshotTerminatingGatewayWithServiceDefaultsMeta(t testing.T) *ConfigSnapshot {
|
||||
func TestConfigSnapshotTerminatingGatewayWithLambdaService(t testing.T) *ConfigSnapshot {
|
||||
web := structs.NewServiceName("web", nil)
|
||||
return TestConfigSnapshotTerminatingGateway(t, true, nil, []agentcache.UpdateEvent{
|
||||
{
|
||||
CorrelationID: serviceConfigIDPrefix + web.String(),
|
||||
Result: &structs.ServiceConfigResponse{
|
||||
Meta: map[string]string{"a": "b"},
|
||||
ProxyConfig: map[string]interface{}{"protocol": "http"},
|
||||
Meta: map[string]string{
|
||||
"serverless.consul.hashicorp.com/v1alpha1/lambda/enabled": "true",
|
||||
"serverless.consul.hashicorp.com/v1alpha1/lambda/arn": "lambda-arn",
|
||||
"serverless.consul.hashicorp.com/v1alpha1/lambda/payload-passthrough": "true",
|
||||
"serverless.consul.hashicorp.com/v1alpha1/lambda/region": "us-east-1",
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
|
|
@ -0,0 +1,107 @@
|
|||
//go:build !consulent
|
||||
// +build !consulent
|
||||
|
||||
package xds
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"testing"
|
||||
|
||||
envoy_cluster_v3 "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3"
|
||||
envoy_listener_v3 "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3"
|
||||
"github.com/golang/protobuf/proto"
|
||||
testinf "github.com/mitchellh/go-testing-interface"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/hashicorp/consul/agent/proxycfg"
|
||||
"github.com/hashicorp/consul/agent/xds/proxysupport"
|
||||
"github.com/hashicorp/consul/agent/xds/serverlessplugin"
|
||||
"github.com/hashicorp/consul/agent/xds/xdscommon"
|
||||
"github.com/hashicorp/consul/sdk/testutil"
|
||||
)
|
||||
|
||||
func TestServerlessPluginFromSnapshot(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
create func(t testinf.T) *proxycfg.ConfigSnapshot
|
||||
}{
|
||||
{
|
||||
name: "lambda-terminating-gateway",
|
||||
create: proxycfg.TestConfigSnapshotTerminatingGatewayWithLambdaService,
|
||||
},
|
||||
}
|
||||
|
||||
latestEnvoyVersion := proxysupport.EnvoyVersions[0]
|
||||
for _, envoyVersion := range proxysupport.EnvoyVersions {
|
||||
sf, err := determineSupportedProxyFeaturesFromString(envoyVersion)
|
||||
require.NoError(t, err)
|
||||
t.Run("envoy-"+envoyVersion, func(t *testing.T) {
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
// Sanity check default with no overrides first
|
||||
snap := tt.create(t)
|
||||
|
||||
// We need to replace the TLS certs with deterministic ones to make golden
|
||||
// files workable. Note we don't update these otherwise they'd change
|
||||
// golden files for every test case and so not be any use!
|
||||
setupTLSRootsAndLeaf(t, snap)
|
||||
|
||||
g := newResourceGenerator(testutil.Logger(t), nil, nil, false)
|
||||
g.ProxyFeatures = sf
|
||||
|
||||
res, err := g.allResourcesFromSnapshot(snap)
|
||||
require.NoError(t, err)
|
||||
|
||||
indexedResources := indexResources(g.Logger, res)
|
||||
newResourceMap, err := serverlessplugin.MutateIndexedResources(indexedResources, xdscommon.MakePluginConfiguration(snap))
|
||||
require.NoError(t, err)
|
||||
|
||||
entities := []struct {
|
||||
name string
|
||||
key string
|
||||
sorter func([]proto.Message) func(int, int) bool
|
||||
}{
|
||||
{
|
||||
name: "clusters",
|
||||
key: xdscommon.ClusterType,
|
||||
sorter: func(msgs []proto.Message) func(int, int) bool {
|
||||
return func(i, j int) bool {
|
||||
return msgs[i].(*envoy_cluster_v3.Cluster).Name < msgs[j].(*envoy_cluster_v3.Cluster).Name
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "listeners",
|
||||
key: xdscommon.ListenerType,
|
||||
sorter: func(msgs []proto.Message) func(int, int) bool {
|
||||
return func(i, j int) bool {
|
||||
return msgs[i].(*envoy_listener_v3.Listener).Name < msgs[j].(*envoy_listener_v3.Listener).Name
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, entity := range entities {
|
||||
var msgs []proto.Message
|
||||
for _, e := range newResourceMap.Index[entity.key] {
|
||||
msgs = append(msgs, e)
|
||||
}
|
||||
|
||||
sort.Slice(msgs, entity.sorter(msgs))
|
||||
r, err := createResponse(entity.key, "00000001", "00000001", msgs)
|
||||
require.NoError(t, err)
|
||||
|
||||
t.Run(entity.name, func(t *testing.T) {
|
||||
gotJSON := protoToJSON(t, r)
|
||||
|
||||
require.JSONEq(t, goldenEnvoy(t,
|
||||
filepath.Join("serverless_plugin", entity.name, tt.name),
|
||||
envoyVersion, latestEnvoyVersion, gotJSON), gotJSON)
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
package serverlessplugin
|
||||
|
||||
import (
|
||||
envoy_core_v3 "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
|
||||
envoy_listener_v3 "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3"
|
||||
envoy_http_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3"
|
||||
envoy_tls_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3"
|
||||
|
||||
"github.com/golang/protobuf/ptypes"
|
||||
|
||||
"github.com/golang/protobuf/proto"
|
||||
)
|
||||
|
||||
// This is copied from xds and not put into the shared package because I'm not
|
||||
// convinced it should be shared.
|
||||
|
||||
func makeUpstreamTLSTransportSocket(tlsContext *envoy_tls_v3.UpstreamTlsContext) (*envoy_core_v3.TransportSocket, error) {
|
||||
if tlsContext == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return makeTransportSocket("tls", tlsContext)
|
||||
}
|
||||
|
||||
func makeTransportSocket(name string, config proto.Message) (*envoy_core_v3.TransportSocket, error) {
|
||||
any, err := ptypes.MarshalAny(config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &envoy_core_v3.TransportSocket{
|
||||
Name: name,
|
||||
ConfigType: &envoy_core_v3.TransportSocket_TypedConfig{
|
||||
TypedConfig: any,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func makeEnvoyHTTPFilter(name string, cfg proto.Message) (*envoy_http_v3.HttpFilter, error) {
|
||||
any, err := ptypes.MarshalAny(cfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &envoy_http_v3.HttpFilter{
|
||||
Name: name,
|
||||
ConfigType: &envoy_http_v3.HttpFilter_TypedConfig{TypedConfig: any},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func makeFilter(name string, cfg proto.Message) (*envoy_listener_v3.Filter, error) {
|
||||
any, err := ptypes.MarshalAny(cfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &envoy_listener_v3.Filter{
|
||||
Name: name,
|
||||
ConfigType: &envoy_listener_v3.Filter_TypedConfig{TypedConfig: any},
|
||||
}, nil
|
||||
}
|
|
@ -0,0 +1,170 @@
|
|||
package serverlessplugin
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
envoy_cluster_v3 "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3"
|
||||
envoy_core_v3 "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
|
||||
envoy_endpoint_v3 "github.com/envoyproxy/go-control-plane/envoy/config/endpoint/v3"
|
||||
envoy_listener_v3 "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3"
|
||||
envoy_lambda_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/aws_lambda/v3"
|
||||
envoy_http_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3"
|
||||
envoy_tls_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3"
|
||||
envoy_resource_v3 "github.com/envoyproxy/go-control-plane/pkg/resource/v3"
|
||||
pstruct "github.com/golang/protobuf/ptypes/struct"
|
||||
|
||||
"github.com/hashicorp/consul/agent/xds/xdscommon"
|
||||
"github.com/hashicorp/consul/api"
|
||||
)
|
||||
|
||||
const (
|
||||
lambdaPrefix string = "serverless.consul.hashicorp.com/v1alpha1"
|
||||
lambdaEnabledTag string = lambdaPrefix + "/lambda/enabled"
|
||||
lambdaArnTag string = lambdaPrefix + "/lambda/arn"
|
||||
lambdaPayloadPassthroughTag string = lambdaPrefix + "/lambda/payload-passhthrough"
|
||||
lambdaRegionTag string = lambdaPrefix + "/lambda/region"
|
||||
lambdaInvocationMode string = lambdaPrefix + "/lambda/invocation-mode"
|
||||
)
|
||||
|
||||
type lambdaPatcher struct {
|
||||
arn string
|
||||
payloadPassthrough bool
|
||||
region string
|
||||
kind api.ServiceKind
|
||||
invocationMode envoy_lambda_v3.Config_InvocationMode
|
||||
}
|
||||
|
||||
var _ patcher = (*lambdaPatcher)(nil)
|
||||
|
||||
func makeLambdaPatcher(serviceConfig xdscommon.ServiceConfig) (patcher, bool) {
|
||||
var patcher lambdaPatcher
|
||||
if !isStringTrue(serviceConfig.Meta[lambdaEnabledTag]) {
|
||||
return patcher, true
|
||||
}
|
||||
|
||||
arn := serviceConfig.Meta[lambdaArnTag]
|
||||
if arn == "" {
|
||||
return patcher, false
|
||||
}
|
||||
|
||||
region := serviceConfig.Meta[lambdaRegionTag]
|
||||
if region == "" {
|
||||
return patcher, false
|
||||
}
|
||||
|
||||
payloadPassthrough := isStringTrue(serviceConfig.Meta[lambdaPayloadPassthroughTag])
|
||||
|
||||
invocationModeStr := serviceConfig.Meta[lambdaInvocationMode]
|
||||
invocationMode := envoy_lambda_v3.Config_SYNCHRONOUS
|
||||
if invocationModeStr == "asynchronous" {
|
||||
invocationMode = envoy_lambda_v3.Config_ASYNCHRONOUS
|
||||
}
|
||||
|
||||
return lambdaPatcher{
|
||||
arn: arn,
|
||||
payloadPassthrough: payloadPassthrough,
|
||||
region: region,
|
||||
kind: serviceConfig.Kind,
|
||||
invocationMode: invocationMode,
|
||||
}, true
|
||||
}
|
||||
|
||||
func isStringTrue(v string) bool {
|
||||
return v == "true"
|
||||
}
|
||||
|
||||
func (p lambdaPatcher) CanPatch(kind api.ServiceKind) bool {
|
||||
return kind == p.kind
|
||||
}
|
||||
|
||||
func (p lambdaPatcher) PatchCluster(c *envoy_cluster_v3.Cluster) (*envoy_cluster_v3.Cluster, bool, error) {
|
||||
transportSocket, err := makeUpstreamTLSTransportSocket(&envoy_tls_v3.UpstreamTlsContext{
|
||||
Sni: "*.amazonaws.com",
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return c, false, fmt.Errorf("failed to make transport socket: %w", err)
|
||||
}
|
||||
|
||||
cluster := &envoy_cluster_v3.Cluster{
|
||||
Name: c.Name,
|
||||
ConnectTimeout: c.ConnectTimeout,
|
||||
ClusterDiscoveryType: &envoy_cluster_v3.Cluster_Type{Type: envoy_cluster_v3.Cluster_LOGICAL_DNS},
|
||||
DnsLookupFamily: envoy_cluster_v3.Cluster_V4_ONLY,
|
||||
LbPolicy: envoy_cluster_v3.Cluster_ROUND_ROBIN,
|
||||
Metadata: &envoy_core_v3.Metadata{
|
||||
FilterMetadata: map[string]*pstruct.Struct{
|
||||
"com.amazonaws.lambda": {
|
||||
Fields: map[string]*pstruct.Value{
|
||||
"egress_gateway": {Kind: &pstruct.Value_BoolValue{BoolValue: true}},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
LoadAssignment: &envoy_endpoint_v3.ClusterLoadAssignment{
|
||||
ClusterName: c.Name,
|
||||
Endpoints: []*envoy_endpoint_v3.LocalityLbEndpoints{
|
||||
{
|
||||
LbEndpoints: []*envoy_endpoint_v3.LbEndpoint{
|
||||
{
|
||||
HostIdentifier: &envoy_endpoint_v3.LbEndpoint_Endpoint{
|
||||
Endpoint: &envoy_endpoint_v3.Endpoint{
|
||||
Address: &envoy_core_v3.Address{
|
||||
Address: &envoy_core_v3.Address_SocketAddress{
|
||||
SocketAddress: &envoy_core_v3.SocketAddress{
|
||||
Address: fmt.Sprintf("lambda.%s.amazonaws.com", p.region),
|
||||
PortSpecifier: &envoy_core_v3.SocketAddress_PortValue{
|
||||
PortValue: 443,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
TransportSocket: transportSocket,
|
||||
}
|
||||
return cluster, true, nil
|
||||
}
|
||||
|
||||
func (p lambdaPatcher) PatchFilter(filter *envoy_listener_v3.Filter) (*envoy_listener_v3.Filter, bool, error) {
|
||||
if filter.Name != "envoy.filters.network.http_connection_manager" {
|
||||
return filter, false, nil
|
||||
}
|
||||
if typedConfig := filter.GetTypedConfig(); typedConfig == nil {
|
||||
return filter, false, errors.New("error getting typed config for http filter")
|
||||
}
|
||||
|
||||
config := envoy_resource_v3.GetHTTPConnectionManager(filter)
|
||||
if config == nil {
|
||||
return filter, false, errors.New("error unmarshalling filter")
|
||||
}
|
||||
httpFilter, err := makeEnvoyHTTPFilter(
|
||||
"envoy.filters.http.aws_lambda",
|
||||
&envoy_lambda_v3.Config{
|
||||
Arn: p.arn,
|
||||
PayloadPassthrough: p.payloadPassthrough,
|
||||
InvocationMode: p.invocationMode,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return filter, false, err
|
||||
}
|
||||
|
||||
config.HttpFilters = []*envoy_http_v3.HttpFilter{
|
||||
httpFilter,
|
||||
{Name: "envoy.filters.http.router"},
|
||||
}
|
||||
config.StripMatchingHostPort = true
|
||||
newFilter, err := makeFilter("envoy.filters.network.http_connection_manager", config)
|
||||
if err != nil {
|
||||
return filter, false, errors.New("error making new filter")
|
||||
}
|
||||
|
||||
return newFilter, true, nil
|
||||
}
|
|
@ -0,0 +1,83 @@
|
|||
package serverlessplugin
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/hashicorp/consul/agent/xds/xdscommon"
|
||||
"github.com/hashicorp/consul/api"
|
||||
)
|
||||
|
||||
func TestMakeLambdaPatcher(t *testing.T) {
|
||||
kind := api.ServiceKindTerminatingGateway
|
||||
cases := []struct {
|
||||
name string
|
||||
enabled bool
|
||||
arn string
|
||||
payloadPassthrough bool
|
||||
region string
|
||||
expected lambdaPatcher
|
||||
ok bool
|
||||
}{
|
||||
{
|
||||
name: "no meta",
|
||||
ok: true,
|
||||
},
|
||||
{
|
||||
name: "lambda disabled",
|
||||
enabled: false,
|
||||
ok: true,
|
||||
},
|
||||
{
|
||||
name: "missing arn",
|
||||
enabled: true,
|
||||
region: "blah",
|
||||
ok: false,
|
||||
},
|
||||
{
|
||||
name: "missing region",
|
||||
enabled: true,
|
||||
region: "arn",
|
||||
ok: false,
|
||||
},
|
||||
{
|
||||
name: "including payload passthrough",
|
||||
enabled: true,
|
||||
arn: "arn",
|
||||
region: "blah",
|
||||
payloadPassthrough: true,
|
||||
expected: lambdaPatcher{
|
||||
arn: "arn",
|
||||
payloadPassthrough: true,
|
||||
region: "blah",
|
||||
kind: kind,
|
||||
},
|
||||
ok: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
config := xdscommon.ServiceConfig{
|
||||
Kind: kind,
|
||||
Meta: map[string]string{
|
||||
lambdaEnabledTag: strconv.FormatBool(tc.enabled),
|
||||
lambdaArnTag: tc.arn,
|
||||
lambdaRegionTag: tc.region,
|
||||
},
|
||||
}
|
||||
|
||||
if tc.payloadPassthrough {
|
||||
config.Meta[lambdaPayloadPassthroughTag] = strconv.FormatBool(tc.payloadPassthrough)
|
||||
}
|
||||
|
||||
patcher, ok := makeLambdaPatcher(config)
|
||||
|
||||
require.Equal(t, tc.ok, ok)
|
||||
|
||||
require.Equal(t, tc.expected, patcher)
|
||||
})
|
||||
}
|
||||
}
|
|
@ -0,0 +1,81 @@
|
|||
package serverlessplugin
|
||||
|
||||
import (
|
||||
envoy_cluster_v3 "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3"
|
||||
envoy_listener_v3 "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3"
|
||||
|
||||
"github.com/hashicorp/consul/agent/xds/xdscommon"
|
||||
"github.com/hashicorp/consul/api"
|
||||
)
|
||||
|
||||
// patcher is the interface that each serverless integration must implement. It
|
||||
// is responsible for modifying the xDS structures based on only the state of
|
||||
// the patcher.
|
||||
type patcher interface {
|
||||
// CanPatch determines if the patcher can mutate resources for the given api.ServiceKind
|
||||
CanPatch(api.ServiceKind) bool
|
||||
|
||||
// PatchCluster patches a cluster to include the custom Envoy configuration
|
||||
// required to integrate with the serverless integration.
|
||||
PatchCluster(*envoy_cluster_v3.Cluster) (*envoy_cluster_v3.Cluster, bool, error)
|
||||
|
||||
// PatchFilter patches an Envoy filter to include the custom Envoy
|
||||
// configuration required to integrate with the serverless integration.
|
||||
PatchFilter(*envoy_listener_v3.Filter) (*envoy_listener_v3.Filter, bool, error)
|
||||
}
|
||||
|
||||
type patchers map[api.CompoundServiceName]patcher
|
||||
|
||||
func getPatcher(patchers patchers, kind api.ServiceKind, name api.CompoundServiceName) patcher {
|
||||
patcher, ok := patchers[name]
|
||||
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
if !patcher.CanPatch(kind) {
|
||||
return nil
|
||||
}
|
||||
|
||||
return patcher
|
||||
}
|
||||
|
||||
// getPatcherBySNI gets the patcher for the associated SNI.
|
||||
func getPatcherBySNI(config xdscommon.PluginConfiguration, kind api.ServiceKind, sni string) patcher {
|
||||
serviceName, ok := config.SNIToServiceName[sni]
|
||||
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
serviceConfig, ok := config.ServiceConfigs[serviceName]
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
p := makePatcher(serviceConfig)
|
||||
if p == nil || !p.CanPatch(kind) {
|
||||
return nil
|
||||
}
|
||||
|
||||
return p
|
||||
}
|
||||
|
||||
func makePatcher(serviceConfig xdscommon.ServiceConfig) patcher {
|
||||
for _, constructor := range patchConstructors {
|
||||
patcher, ok := constructor(serviceConfig)
|
||||
if ok {
|
||||
return patcher
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// patchConstructor is used to construct patchers based on
|
||||
// xdscommon.ServiceConfig. This function contains all of the logic around
|
||||
// turning Meta data into the patcher.
|
||||
type patchConstructor func(xdscommon.ServiceConfig) (patcher, bool)
|
||||
|
||||
// patchConstructors contains all patchers that getPatchers tries to create.
|
||||
var patchConstructors = []patchConstructor{makeLambdaPatcher}
|
|
@ -0,0 +1,100 @@
|
|||
package serverlessplugin
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/hashicorp/consul/agent/xds/xdscommon"
|
||||
"github.com/hashicorp/consul/api"
|
||||
)
|
||||
|
||||
func TestGetPatcherBySNI(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
sni string
|
||||
kind api.ServiceKind
|
||||
expected patcher
|
||||
config *xdscommon.PluginConfiguration
|
||||
}{
|
||||
{
|
||||
name: "no sni match",
|
||||
sni: "not-matching",
|
||||
},
|
||||
{
|
||||
name: "no patcher",
|
||||
config: &xdscommon.PluginConfiguration{},
|
||||
sni: "lambda-sni",
|
||||
},
|
||||
{
|
||||
name: "no kind match",
|
||||
kind: api.ServiceKindIngressGateway,
|
||||
sni: "lambda-sni",
|
||||
},
|
||||
{
|
||||
name: "full match",
|
||||
sni: "lambda-sni",
|
||||
kind: api.ServiceKindTerminatingGateway,
|
||||
expected: lambdaPatcher{
|
||||
arn: "arn",
|
||||
region: "region",
|
||||
payloadPassthrough: false,
|
||||
kind: api.ServiceKindTerminatingGateway,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
config := sampleConfig()
|
||||
if tc.config != nil {
|
||||
config = *tc.config
|
||||
}
|
||||
patcher := getPatcherBySNI(config, tc.kind, tc.sni)
|
||||
|
||||
if tc.expected == nil {
|
||||
require.Empty(t, patcher)
|
||||
} else {
|
||||
require.Equal(t, tc.expected, patcher)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
lambdaService = api.CompoundServiceName{Name: "lambda"}
|
||||
disabledLambdaService = api.CompoundServiceName{Name: "disabled-lambda"}
|
||||
invalidLambdaService = api.CompoundServiceName{Name: "invalid-lambda"}
|
||||
)
|
||||
|
||||
func sampleConfig() xdscommon.PluginConfiguration {
|
||||
return xdscommon.PluginConfiguration{
|
||||
ServiceConfigs: map[api.CompoundServiceName]xdscommon.ServiceConfig{
|
||||
lambdaService: {
|
||||
Kind: api.ServiceKindTerminatingGateway,
|
||||
Meta: map[string]string{
|
||||
lambdaEnabledTag: "true",
|
||||
lambdaArnTag: "arn",
|
||||
lambdaRegionTag: "region",
|
||||
},
|
||||
},
|
||||
disabledLambdaService: {
|
||||
Kind: api.ServiceKindTerminatingGateway,
|
||||
Meta: map[string]string{
|
||||
lambdaEnabledTag: "false",
|
||||
lambdaArnTag: "arn",
|
||||
lambdaRegionTag: "region",
|
||||
},
|
||||
},
|
||||
invalidLambdaService: {
|
||||
Kind: api.ServiceKindTerminatingGateway,
|
||||
Meta: map[string]string{
|
||||
lambdaEnabledTag: "true",
|
||||
},
|
||||
},
|
||||
},
|
||||
SNIToServiceName: map[string]api.CompoundServiceName{
|
||||
"lambda-sni": lambdaService,
|
||||
},
|
||||
}
|
||||
}
|
|
@ -1,9 +1,122 @@
|
|||
package serverlessplugin
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
envoy_cluster_v3 "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3"
|
||||
envoy_listener_v3 "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3"
|
||||
"github.com/golang/protobuf/proto"
|
||||
"github.com/hashicorp/go-multierror"
|
||||
|
||||
"github.com/hashicorp/consul/agent/xds/xdscommon"
|
||||
"github.com/hashicorp/consul/api"
|
||||
)
|
||||
|
||||
// MutateIndexedResources updates indexed xDS structures to include patches for
|
||||
// serverless integrations. It is responsible for constructing all of the
|
||||
// patchers and forwarding xDS structs onto the appropriate patcher. If any
|
||||
// portion of this function fails, it will record the error and continue. The
|
||||
// behavior is appropriate since the unpatched xDS structures this receives are
|
||||
// typically invalid.
|
||||
func MutateIndexedResources(resources *xdscommon.IndexedResources, config xdscommon.PluginConfiguration) (*xdscommon.IndexedResources, error) {
|
||||
return resources, nil
|
||||
var resultErr error
|
||||
|
||||
// The serverless plugin only supports terminating gateays for now, but will
|
||||
// likely add connect proxies soon.
|
||||
if config.Kind != api.ServiceKindTerminatingGateway {
|
||||
return resources, resultErr
|
||||
}
|
||||
|
||||
for _, indexType := range []string{
|
||||
xdscommon.ClusterType,
|
||||
xdscommon.ListenerType,
|
||||
} {
|
||||
for nameOrSNI, msg := range resources.Index[indexType] {
|
||||
switch resource := msg.(type) {
|
||||
case *envoy_cluster_v3.Cluster:
|
||||
patcher := getPatcherBySNI(config, config.Kind, nameOrSNI)
|
||||
if patcher == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
newCluster, patched, err := patcher.PatchCluster(resource)
|
||||
if err != nil {
|
||||
resultErr = multierror.Append(resultErr, fmt.Errorf("error patching cluster: %w", err))
|
||||
continue
|
||||
}
|
||||
if patched {
|
||||
resources.Index[xdscommon.ClusterType][nameOrSNI] = newCluster
|
||||
}
|
||||
|
||||
case *envoy_listener_v3.Listener:
|
||||
newListener, patched, err := patchTerminatingGatewayListener(resource, config)
|
||||
if err != nil {
|
||||
resultErr = multierror.Append(resultErr, fmt.Errorf("error patching listener: %w", err))
|
||||
continue
|
||||
}
|
||||
if patched {
|
||||
resources.Index[xdscommon.ListenerType][nameOrSNI] = newListener
|
||||
}
|
||||
|
||||
default:
|
||||
resultErr = multierror.Append(resultErr, fmt.Errorf("unsupported type was skipped: %T", resource))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return resources, resultErr
|
||||
}
|
||||
|
||||
func patchTerminatingGatewayListener(l *envoy_listener_v3.Listener, config xdscommon.PluginConfiguration) (proto.Message, bool, error) {
|
||||
var resultErr error
|
||||
patched := false
|
||||
for _, filterChain := range l.FilterChains {
|
||||
sni := getSNI(filterChain)
|
||||
|
||||
if sni == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
patcher := getPatcherBySNI(config, config.Kind, sni)
|
||||
|
||||
if patcher == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
var filters []*envoy_listener_v3.Filter
|
||||
|
||||
for _, filter := range filterChain.Filters {
|
||||
newFilter, ok, err := patcher.PatchFilter(filter)
|
||||
|
||||
if err != nil {
|
||||
resultErr = multierror.Append(resultErr, fmt.Errorf("error patching listener filter: %w", err))
|
||||
filters = append(filters, filter)
|
||||
}
|
||||
if ok {
|
||||
filters = append(filters, newFilter)
|
||||
patched = true
|
||||
}
|
||||
}
|
||||
filterChain.Filters = filters
|
||||
}
|
||||
|
||||
return l, patched, resultErr
|
||||
}
|
||||
|
||||
func getSNI(chain *envoy_listener_v3.FilterChain) string {
|
||||
var sni string
|
||||
|
||||
if chain == nil {
|
||||
return sni
|
||||
}
|
||||
|
||||
if chain.FilterChainMatch == nil {
|
||||
return sni
|
||||
}
|
||||
|
||||
if len(chain.FilterChainMatch.ServerNames) == 0 {
|
||||
return sni
|
||||
}
|
||||
|
||||
return chain.FilterChainMatch.ServerNames[0]
|
||||
}
|
||||
|
|
169
agent/xds/testdata/serverless_plugin/clusters/lambda-terminating-gateway.envoy-1-20-x.golden
vendored
Normal file
169
agent/xds/testdata/serverless_plugin/clusters/lambda-terminating-gateway.envoy-1-20-x.golden
vendored
Normal file
|
@ -0,0 +1,169 @@
|
|||
{
|
||||
"versionInfo": "00000001",
|
||||
"resources": [
|
||||
{
|
||||
"@type": "type.googleapis.com/envoy.config.cluster.v3.Cluster",
|
||||
"name": "api.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
|
||||
"type": "LOGICAL_DNS",
|
||||
"connectTimeout": "5s",
|
||||
"loadAssignment": {
|
||||
"clusterName": "api.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
|
||||
"endpoints": [
|
||||
{
|
||||
"lbEndpoints": [
|
||||
{
|
||||
"endpoint": {
|
||||
"address": {
|
||||
"socketAddress": {
|
||||
"address": "api.altdomain",
|
||||
"portValue": 8081
|
||||
}
|
||||
}
|
||||
},
|
||||
"healthStatus": "HEALTHY",
|
||||
"loadBalancingWeight": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"dnsRefreshRate": "10s",
|
||||
"dnsLookupFamily": "V4_ONLY",
|
||||
"outlierDetection": {
|
||||
|
||||
},
|
||||
"transportSocket": {
|
||||
"name": "tls",
|
||||
"typedConfig": {
|
||||
"@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext",
|
||||
"commonTlsContext": {
|
||||
"tlsParams": {
|
||||
|
||||
},
|
||||
"tlsCertificates": [
|
||||
{
|
||||
"certificateChain": {
|
||||
"filename": "api.cert.pem"
|
||||
},
|
||||
"privateKey": {
|
||||
"filename": "api.key.pem"
|
||||
}
|
||||
}
|
||||
],
|
||||
"validationContext": {
|
||||
"trustedCa": {
|
||||
"filename": "ca.cert.pem"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"@type": "type.googleapis.com/envoy.config.cluster.v3.Cluster",
|
||||
"name": "cache.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
|
||||
"type": "LOGICAL_DNS",
|
||||
"connectTimeout": "5s",
|
||||
"loadAssignment": {
|
||||
"clusterName": "cache.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
|
||||
"endpoints": [
|
||||
{
|
||||
"lbEndpoints": [
|
||||
{
|
||||
"endpoint": {
|
||||
"address": {
|
||||
"socketAddress": {
|
||||
"address": "cache.mydomain",
|
||||
"portValue": 8081
|
||||
}
|
||||
}
|
||||
},
|
||||
"healthStatus": "HEALTHY",
|
||||
"loadBalancingWeight": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"dnsRefreshRate": "10s",
|
||||
"dnsLookupFamily": "V4_ONLY",
|
||||
"outlierDetection": {
|
||||
|
||||
}
|
||||
},
|
||||
{
|
||||
"@type": "type.googleapis.com/envoy.config.cluster.v3.Cluster",
|
||||
"name": "db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
|
||||
"type": "LOGICAL_DNS",
|
||||
"connectTimeout": "5s",
|
||||
"loadAssignment": {
|
||||
"clusterName": "db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
|
||||
"endpoints": [
|
||||
{
|
||||
"lbEndpoints": [
|
||||
{
|
||||
"endpoint": {
|
||||
"address": {
|
||||
"socketAddress": {
|
||||
"address": "db.mydomain",
|
||||
"portValue": 8081
|
||||
}
|
||||
}
|
||||
},
|
||||
"healthStatus": "UNHEALTHY",
|
||||
"loadBalancingWeight": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"dnsRefreshRate": "10s",
|
||||
"dnsLookupFamily": "V4_ONLY",
|
||||
"outlierDetection": {
|
||||
|
||||
}
|
||||
},
|
||||
{
|
||||
"@type": "type.googleapis.com/envoy.config.cluster.v3.Cluster",
|
||||
"name": "web.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
|
||||
"type": "LOGICAL_DNS",
|
||||
"connectTimeout": "5s",
|
||||
"loadAssignment": {
|
||||
"clusterName": "web.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
|
||||
"endpoints": [
|
||||
{
|
||||
"lbEndpoints": [
|
||||
{
|
||||
"endpoint": {
|
||||
"address": {
|
||||
"socketAddress": {
|
||||
"address": "lambda.us-east-1.amazonaws.com",
|
||||
"portValue": 443
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"dnsLookupFamily": "V4_ONLY",
|
||||
"transportSocket": {
|
||||
"name": "tls",
|
||||
"typedConfig": {
|
||||
"@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext",
|
||||
"sni": "*.amazonaws.com"
|
||||
}
|
||||
},
|
||||
"metadata": {
|
||||
"filterMetadata": {
|
||||
"com.amazonaws.lambda": {
|
||||
"egress_gateway": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"typeUrl": "type.googleapis.com/envoy.config.cluster.v3.Cluster",
|
||||
"nonce": "00000001"
|
||||
}
|
272
agent/xds/testdata/serverless_plugin/listeners/lambda-terminating-gateway.envoy-1-20-x.golden
vendored
Normal file
272
agent/xds/testdata/serverless_plugin/listeners/lambda-terminating-gateway.envoy-1-20-x.golden
vendored
Normal file
|
@ -0,0 +1,272 @@
|
|||
{
|
||||
"versionInfo": "00000001",
|
||||
"resources": [
|
||||
{
|
||||
"@type": "type.googleapis.com/envoy.config.listener.v3.Listener",
|
||||
"name": "default:1.2.3.4:8443",
|
||||
"address": {
|
||||
"socketAddress": {
|
||||
"address": "1.2.3.4",
|
||||
"portValue": 8443
|
||||
}
|
||||
},
|
||||
"filterChains": [
|
||||
{
|
||||
"filterChainMatch": {
|
||||
"serverNames": [
|
||||
"api.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul"
|
||||
]
|
||||
},
|
||||
"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": "upstream.api.default.default.dc1",
|
||||
"cluster": "api.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul"
|
||||
}
|
||||
}
|
||||
],
|
||||
"transportSocket": {
|
||||
"name": "tls",
|
||||
"typedConfig": {
|
||||
"@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext",
|
||||
"commonTlsContext": {
|
||||
"tlsParams": {
|
||||
|
||||
},
|
||||
"tlsCertificates": [
|
||||
{
|
||||
"certificateChain": {
|
||||
"inlineString": "-----BEGIN CERTIFICATE-----\nMIICnTCCAkKgAwIBAgIRAJrvEdaRAkSltrotd/l/j2cwCgYIKoZIzj0EAwIwgbgx\nCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTEWMBQGA1UEBxMNU2FuIEZyYW5jaXNj\nbzEaMBgGA1UECRMRMTAxIFNlY29uZCBTdHJlZXQxDjAMBgNVBBETBTk0MTA1MRcw\nFQYDVQQKEw5IYXNoaUNvcnAgSW5jLjE/MD0GA1UEAxM2Q29uc3VsIEFnZW50IENB\nIDk2NjM4NzM1MDkzNTU5NTIwNDk3MTQwOTU3MDY1MTc0OTg3NDMxMB4XDTIwMDQx\nNDIyMzE1MloXDTIxMDQxNDIyMzE1MlowHDEaMBgGA1UEAxMRc2VydmVyLmRjMS5j\nb25zdWwwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAQ4v0FoIYI0OWmxE2MR6w5l\n0pWGhc02RpsOPj/6RS1fmXMMu7JzPzwCmkGcR16RlwwhNFKCZsWpvAjVRHf/pTp+\no4HHMIHEMA4GA1UdDwEB/wQEAwIFoDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYB\nBQUHAwIwDAYDVR0TAQH/BAIwADApBgNVHQ4EIgQgk7kABFitAy3PluyNtmzYiC7H\njSN8W/K/OXNJQAQAscMwKwYDVR0jBCQwIoAgNKbPPepvRHXSAPTc+a/BXBzFX1qJ\ny+Zi7qtjlFX7qtUwLQYDVR0RBCYwJIIRc2VydmVyLmRjMS5jb25zdWyCCWxvY2Fs\naG9zdIcEfwAAATAKBggqhkjOPQQDAgNJADBGAiEAhP4HmN5BWysWTbQWClXaWUah\nLpBGFrvc/2cCQuyEZKsCIQD6JyYCYMArtWwZ4G499zktxrFlqfX14bqyONrxtA5I\nDw==\n-----END CERTIFICATE-----\n"
|
||||
},
|
||||
"privateKey": {
|
||||
"inlineString": "-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIE3KbKXHdsa0vvC1fysQaGdoJRgjRALIolI4XJanie+coAoGCCqGSM49\nAwEHoUQDQgAEOL9BaCGCNDlpsRNjEesOZdKVhoXNNkabDj4/+kUtX5lzDLuycz88\nAppBnEdekZcMITRSgmbFqbwI1UR3/6U6fg==\n-----END EC PRIVATE KEY-----\n"
|
||||
}
|
||||
}
|
||||
],
|
||||
"validationContext": {
|
||||
"trustedCa": {
|
||||
"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"
|
||||
}
|
||||
}
|
||||
},
|
||||
"requireClientCertificate": true
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"filterChainMatch": {
|
||||
"serverNames": [
|
||||
"cache.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul"
|
||||
]
|
||||
},
|
||||
"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": "upstream.cache.default.default.dc1",
|
||||
"cluster": "cache.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul"
|
||||
}
|
||||
}
|
||||
],
|
||||
"transportSocket": {
|
||||
"name": "tls",
|
||||
"typedConfig": {
|
||||
"@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext",
|
||||
"commonTlsContext": {
|
||||
"tlsParams": {
|
||||
|
||||
},
|
||||
"tlsCertificates": [
|
||||
{
|
||||
"certificateChain": {
|
||||
"inlineString": "-----BEGIN CERTIFICATE-----\nMIICmjCCAkGgAwIBAgIQe1ZmC0rzRwer6jaH1YIUIjAKBggqhkjOPQQDAjCBuDEL\nMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMRYwFAYDVQQHEw1TYW4gRnJhbmNpc2Nv\nMRowGAYDVQQJExExMDEgU2Vjb25kIFN0cmVldDEOMAwGA1UEERMFOTQxMDUxFzAV\nBgNVBAoTDkhhc2hpQ29ycCBJbmMuMT8wPQYDVQQDEzZDb25zdWwgQWdlbnQgQ0Eg\nODE5ODAwNjg0MDM0MTM3ODkyNDYxNTA1MDk0NDU3OTU1MTQxNjEwHhcNMjAwNjE5\nMTU1MjAzWhcNMjEwNjE5MTU1MjAzWjAcMRowGAYDVQQDExFzZXJ2ZXIuZGMxLmNv\nbnN1bDBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABH2aWaaa3fpQLBayheHiKlrH\n+z53m0frfGknKjOhOPVYDVHV8x0OE01negswVQbKHAtxPf1M8Zy+WbI9rK7Ua1mj\ngccwgcQwDgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEF\nBQcDAjAMBgNVHRMBAf8EAjAAMCkGA1UdDgQiBCDf9CPBSUwwZvpeW73oJLTmgQE2\ntW1NKpL5t1uq9WFcqDArBgNVHSMEJDAigCCPPd/NxgZB0tq2M8pdVpPj3Cr79iTv\ni4/T1ysodfMb7zAtBgNVHREEJjAkghFzZXJ2ZXIuZGMxLmNvbnN1bIIJbG9jYWxo\nb3N0hwR/AAABMAoGCCqGSM49BAMCA0cAMEQCIFCjFZAoXq0s2ied2eIBv0i1KoW5\nIhCylnKFt6iHkyDeAiBBCByTcjHRgEQmqyPojQKoO584EFiczTub9aWdnf9tEw==\n-----END CERTIFICATE-----\n"
|
||||
},
|
||||
"privateKey": {
|
||||
"inlineString": "-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEINsen3S8xzxMrKcRZIvxXzhKDn43Tw9ttqWEFU9TqS5hoAoGCCqGSM49\nAwEHoUQDQgAEfZpZpprd+lAsFrKF4eIqWsf7PnebR+t8aScqM6E49VgNUdXzHQ4T\nTWd6CzBVBsocC3E9/UzxnL5Zsj2srtRrWQ==\n-----END EC PRIVATE KEY-----\n"
|
||||
}
|
||||
}
|
||||
],
|
||||
"validationContext": {
|
||||
"trustedCa": {
|
||||
"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"
|
||||
}
|
||||
}
|
||||
},
|
||||
"requireClientCertificate": true
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"filterChainMatch": {
|
||||
"serverNames": [
|
||||
"db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul"
|
||||
]
|
||||
},
|
||||
"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": "upstream.db.default.default.dc1",
|
||||
"cluster": "db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul"
|
||||
}
|
||||
}
|
||||
],
|
||||
"transportSocket": {
|
||||
"name": "tls",
|
||||
"typedConfig": {
|
||||
"@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext",
|
||||
"commonTlsContext": {
|
||||
"tlsParams": {
|
||||
|
||||
},
|
||||
"tlsCertificates": [
|
||||
{
|
||||
"certificateChain": {
|
||||
"inlineString": "-----BEGIN CERTIFICATE-----\nMIICnTCCAkOgAwIBAgIRAKF+qDJbaOULNL1TIatrsBowCgYIKoZIzj0EAwIwgbkx\nCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTEWMBQGA1UEBxMNU2FuIEZyYW5jaXNj\nbzEaMBgGA1UECRMRMTAxIFNlY29uZCBTdHJlZXQxDjAMBgNVBBETBTk0MTA1MRcw\nFQYDVQQKEw5IYXNoaUNvcnAgSW5jLjFAMD4GA1UEAxM3Q29uc3VsIEFnZW50IENB\nIDE4Nzg3MDAwNjUzMDcxOTYzNTk1ODkwNTE1ODY1NjEzMDA2MTU0NDAeFw0yMDA2\nMTkxNTMxMzRaFw0yMTA2MTkxNTMxMzRaMBwxGjAYBgNVBAMTEXNlcnZlci5kYzEu\nY29uc3VsMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEdQ8Igci5f7ZvvCVsxXt9\ntLfvczD+60XHg0OC0+Aka7ZjQfbEjQwZbz/82EwPoS7Dqo3LTK4IuelOimoNNxuk\nkaOBxzCBxDAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsG\nAQUFBwMCMAwGA1UdEwEB/wQCMAAwKQYDVR0OBCIEILzTLkfJcdWQnTMKUcai/YJq\n0RqH1pjCqtY7SOU4gGOTMCsGA1UdIwQkMCKAIMa2vNcTEC5AGfHIYARJ/4sodX0o\nLzCj3lpw7BcEzPTcMC0GA1UdEQQmMCSCEXNlcnZlci5kYzEuY29uc3Vsgglsb2Nh\nbGhvc3SHBH8AAAEwCgYIKoZIzj0EAwIDSAAwRQIgBZ/Z4GSLEc98WvT/qjTVCNTG\n1WNaAaesVbkRx+J0yl8CIQDAVoqY9ByA5vKHjnQrxWlc/JUtJz8wudg7e/OCRriP\nSg==\n-----END CERTIFICATE-----\n"
|
||||
},
|
||||
"privateKey": {
|
||||
"inlineString": "-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIN1v14FaNxgY4MgjDOOWthen8dgwB0lNMs9/j2TfrnxzoAoGCCqGSM49\nAwEHoUQDQgAEdQ8Igci5f7ZvvCVsxXt9tLfvczD+60XHg0OC0+Aka7ZjQfbEjQwZ\nbz/82EwPoS7Dqo3LTK4IuelOimoNNxukkQ==\n-----END EC PRIVATE KEY-----\n"
|
||||
}
|
||||
}
|
||||
],
|
||||
"validationContext": {
|
||||
"trustedCa": {
|
||||
"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"
|
||||
}
|
||||
}
|
||||
},
|
||||
"requireClientCertificate": true
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"filterChainMatch": {
|
||||
"serverNames": [
|
||||
"web.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul"
|
||||
]
|
||||
},
|
||||
"filters": [
|
||||
{
|
||||
"name": "envoy.filters.network.http_connection_manager",
|
||||
"typedConfig": {
|
||||
"@type": "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager",
|
||||
"statPrefix": "upstream.web.default.default.dc1",
|
||||
"rds": {
|
||||
"configSource": {
|
||||
"ads": {
|
||||
|
||||
},
|
||||
"resourceApiVersion": "V3"
|
||||
},
|
||||
"routeConfigName": "web.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul"
|
||||
},
|
||||
"httpFilters": [
|
||||
{
|
||||
"name": "envoy.filters.http.aws_lambda",
|
||||
"typedConfig": {
|
||||
"@type": "type.googleapis.com/envoy.extensions.filters.http.aws_lambda.v3.Config",
|
||||
"arn": "lambda-arn"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "envoy.filters.http.router"
|
||||
}
|
||||
],
|
||||
"tracing": {
|
||||
"randomSampling": {
|
||||
|
||||
}
|
||||
},
|
||||
"stripMatchingHostPort": true
|
||||
}
|
||||
}
|
||||
],
|
||||
"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": {
|
||||
"trustedCa": {
|
||||
"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"
|
||||
}
|
||||
}
|
||||
},
|
||||
"requireClientCertificate": true
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"filters": [
|
||||
{
|
||||
"name": "envoy.filters.network.sni_cluster"
|
||||
},
|
||||
{
|
||||
"name": "envoy.filters.network.tcp_proxy",
|
||||
"typedConfig": {
|
||||
"@type": "type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy",
|
||||
"statPrefix": "terminating_gateway.default",
|
||||
"cluster": ""
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"listenerFilters": [
|
||||
{
|
||||
"name": "envoy.filters.listener.tls_inspector"
|
||||
}
|
||||
],
|
||||
"trafficDirection": "INBOUND"
|
||||
}
|
||||
],
|
||||
"typeUrl": "type.googleapis.com/envoy.config.listener.v3.Listener",
|
||||
"nonce": "00000001"
|
||||
}
|
|
@ -78,7 +78,8 @@ type PluginConfiguration struct {
|
|||
// associated service's CompoundServiceName
|
||||
EnvoyIDToServiceName map[string]api.CompoundServiceName
|
||||
|
||||
// Kind is mode the local Envoy proxy is running in
|
||||
// Kind is mode the local Envoy proxy is running in. For now, only
|
||||
// terminating gateways are supported.
|
||||
Kind api.ServiceKind
|
||||
}
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ import (
|
|||
)
|
||||
|
||||
func TestMakePluginConfiguration_TerminatingGateway(t *testing.T) {
|
||||
snap := proxycfg.TestConfigSnapshotTerminatingGatewayWithServiceDefaultsMeta(t)
|
||||
snap := proxycfg.TestConfigSnapshotTerminatingGatewayWithLambdaService(t)
|
||||
|
||||
webService := api.CompoundServiceName{
|
||||
Name: "web",
|
||||
|
@ -41,7 +41,12 @@ func TestMakePluginConfiguration_TerminatingGateway(t *testing.T) {
|
|||
ServiceConfigs: map[api.CompoundServiceName]ServiceConfig{
|
||||
webService: {
|
||||
Kind: api.ServiceKindTerminatingGateway,
|
||||
Meta: map[string]string{"a": "b"},
|
||||
Meta: map[string]string{
|
||||
"serverless.consul.hashicorp.com/v1alpha1/lambda/enabled": "true",
|
||||
"serverless.consul.hashicorp.com/v1alpha1/lambda/arn": "lambda-arn",
|
||||
"serverless.consul.hashicorp.com/v1alpha1/lambda/payload-passthrough": "true",
|
||||
"serverless.consul.hashicorp.com/v1alpha1/lambda/region": "us-east-1",
|
||||
},
|
||||
},
|
||||
apiService: {
|
||||
Kind: api.ServiceKindTerminatingGateway,
|
||||
|
|
Loading…
Reference in New Issue