feat: envoy extension - http local rate limit (#16196)
- http local rate limit - Apply rate limit only to local_app - unit test and integ test
This commit is contained in:
parent
fc92f4f75b
commit
1c5ca0da53
|
@ -0,0 +1,58 @@
|
|||
package localratelimit
|
||||
|
||||
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"
|
||||
|
||||
"google.golang.org/protobuf/proto"
|
||||
"google.golang.org/protobuf/types/known/anypb"
|
||||
)
|
||||
|
||||
// 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 := anypb.New(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 := anypb.New(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 := anypb.New(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,198 @@
|
|||
package localratelimit
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
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_listener_v3 "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3"
|
||||
envoy_route_v3 "github.com/envoyproxy/go-control-plane/envoy/config/route/v3"
|
||||
envoy_ratelimit "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/local_ratelimit/v3"
|
||||
envoy_http_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3"
|
||||
envoy_type_v3 "github.com/envoyproxy/go-control-plane/envoy/type/v3"
|
||||
envoy_resource_v3 "github.com/envoyproxy/go-control-plane/pkg/resource/v3"
|
||||
"github.com/golang/protobuf/ptypes/wrappers"
|
||||
"github.com/hashicorp/go-multierror"
|
||||
"github.com/mitchellh/mapstructure"
|
||||
"google.golang.org/protobuf/types/known/durationpb"
|
||||
|
||||
"github.com/hashicorp/consul/api"
|
||||
"github.com/hashicorp/consul/envoyextensions/extensioncommon"
|
||||
)
|
||||
|
||||
type ratelimit struct {
|
||||
ProxyType string
|
||||
|
||||
// Token bucket of the rate limit
|
||||
MaxTokens *int
|
||||
TokensPerFill *int
|
||||
FillInterval *int
|
||||
|
||||
// Percent of requests to be rate limited
|
||||
FilterEnabled *uint32
|
||||
FilterEnforced *uint32
|
||||
}
|
||||
|
||||
var _ extensioncommon.BasicExtension = (*ratelimit)(nil)
|
||||
|
||||
// Constructor follows a specific function signature required for the extension registration.
|
||||
func Constructor(ext api.EnvoyExtension) (extensioncommon.EnvoyExtender, error) {
|
||||
var r ratelimit
|
||||
if name := ext.Name; name != api.BuiltinLocalRatelimitExtension {
|
||||
return nil, fmt.Errorf("expected extension name 'ratelimit' but got %q", name)
|
||||
}
|
||||
|
||||
if err := r.fromArguments(ext.Arguments); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &extensioncommon.BasicEnvoyExtender{
|
||||
Extension: &r,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (r *ratelimit) fromArguments(args map[string]interface{}) error {
|
||||
if err := mapstructure.Decode(args, r); err != nil {
|
||||
return fmt.Errorf("error decoding extension arguments: %v", err)
|
||||
}
|
||||
return r.validate()
|
||||
}
|
||||
|
||||
func (r *ratelimit) validate() error {
|
||||
var resultErr error
|
||||
|
||||
// NOTE: Envoy requires FillInterval value must be greater than 0.
|
||||
// If unset, it is considered as 0.
|
||||
if r.FillInterval == nil {
|
||||
resultErr = multierror.Append(resultErr, fmt.Errorf("FillInterval(in second) is missing"))
|
||||
} else if *r.FillInterval <= 0 {
|
||||
resultErr = multierror.Append(resultErr, fmt.Errorf("FillInterval(in second) must be greater than 0, got %d", *r.FillInterval))
|
||||
}
|
||||
|
||||
// NOTE: Envoy requires MaxToken value must be greater than 0.
|
||||
// If unset, it is considered as 0.
|
||||
if r.MaxTokens == nil {
|
||||
resultErr = multierror.Append(resultErr, fmt.Errorf("MaxTokens is missing"))
|
||||
} else if *r.MaxTokens <= 0 {
|
||||
resultErr = multierror.Append(resultErr, fmt.Errorf("MaxTokens must be greater than 0, got %d", r.MaxTokens))
|
||||
}
|
||||
|
||||
// TokensPerFill is allowed to unset. In this case, envoy
|
||||
// uses its default value, which is 1.
|
||||
if r.TokensPerFill != nil && *r.TokensPerFill <= 0 {
|
||||
resultErr = multierror.Append(resultErr, fmt.Errorf("TokensPerFill must be greater than 0, got %d", *r.TokensPerFill))
|
||||
}
|
||||
|
||||
if err := validateProxyType(r.ProxyType); err != nil {
|
||||
resultErr = multierror.Append(resultErr, err)
|
||||
}
|
||||
|
||||
return resultErr
|
||||
}
|
||||
|
||||
// CanApply determines if the extension can apply to the given extension configuration.
|
||||
func (p *ratelimit) CanApply(config *extensioncommon.RuntimeConfig) bool {
|
||||
// rate limit is only applied to the service itself since the limit is
|
||||
// aggregated from all downstream connections.
|
||||
return string(config.Kind) == p.ProxyType && !config.IsUpstream()
|
||||
}
|
||||
|
||||
// PatchRoute does nothing.
|
||||
func (p ratelimit) PatchRoute(_ *extensioncommon.RuntimeConfig, route *envoy_route_v3.RouteConfiguration) (*envoy_route_v3.RouteConfiguration, bool, error) {
|
||||
return route, false, nil
|
||||
}
|
||||
|
||||
// PatchCluster does nothing.
|
||||
func (p ratelimit) PatchCluster(_ *extensioncommon.RuntimeConfig, c *envoy_cluster_v3.Cluster) (*envoy_cluster_v3.Cluster, bool, error) {
|
||||
return c, false, nil
|
||||
}
|
||||
|
||||
// PatchFilter inserts a http local rate_limit filter at the head of
|
||||
// envoy.filters.network.http_connection_manager filters
|
||||
func (p ratelimit) PatchFilter(_ *extensioncommon.RuntimeConfig, 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")
|
||||
}
|
||||
|
||||
tokenBucket := envoy_type_v3.TokenBucket{}
|
||||
|
||||
if p.TokensPerFill != nil {
|
||||
tokenBucket.TokensPerFill = &wrappers.UInt32Value{
|
||||
Value: uint32(*p.TokensPerFill),
|
||||
}
|
||||
}
|
||||
if p.MaxTokens != nil {
|
||||
tokenBucket.MaxTokens = uint32(*p.MaxTokens)
|
||||
}
|
||||
|
||||
if p.FillInterval != nil {
|
||||
tokenBucket.FillInterval = durationpb.New(time.Duration(*p.FillInterval) * time.Second)
|
||||
}
|
||||
|
||||
var FilterEnabledDefault *envoy_core_v3.RuntimeFractionalPercent
|
||||
if p.FilterEnabled != nil {
|
||||
FilterEnabledDefault = &envoy_core_v3.RuntimeFractionalPercent{
|
||||
DefaultValue: &envoy_type_v3.FractionalPercent{
|
||||
Numerator: *p.FilterEnabled,
|
||||
Denominator: envoy_type_v3.FractionalPercent_HUNDRED,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
var FilterEnforcedDefault *envoy_core_v3.RuntimeFractionalPercent
|
||||
if p.FilterEnforced != nil {
|
||||
FilterEnforcedDefault = &envoy_core_v3.RuntimeFractionalPercent{
|
||||
DefaultValue: &envoy_type_v3.FractionalPercent{
|
||||
Numerator: *p.FilterEnforced,
|
||||
Denominator: envoy_type_v3.FractionalPercent_HUNDRED,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
ratelimitHttpFilter, err := makeEnvoyHTTPFilter(
|
||||
"envoy.filters.http.local_ratelimit",
|
||||
&envoy_ratelimit.LocalRateLimit{
|
||||
TokenBucket: &tokenBucket,
|
||||
StatPrefix: "local_ratelimit",
|
||||
FilterEnabled: FilterEnabledDefault,
|
||||
FilterEnforced: FilterEnforcedDefault,
|
||||
},
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return filter, false, err
|
||||
}
|
||||
|
||||
changedFilters := make([]*envoy_http_v3.HttpFilter, 0, len(config.HttpFilters)+1)
|
||||
|
||||
// The ratelimitHttpFilter is inserted as the first element of the http
|
||||
// filter chain.
|
||||
changedFilters = append(changedFilters, ratelimitHttpFilter)
|
||||
changedFilters = append(changedFilters, config.HttpFilters...)
|
||||
config.HttpFilters = changedFilters
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
func validateProxyType(t string) error {
|
||||
if t != "connect-proxy" {
|
||||
return fmt.Errorf("unexpected ProxyType %q", t)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,160 @@
|
|||
package localratelimit
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/hashicorp/consul/api"
|
||||
"github.com/hashicorp/consul/envoyextensions/extensioncommon"
|
||||
)
|
||||
|
||||
func TestConstructor(t *testing.T) {
|
||||
makeArguments := func(overrides map[string]interface{}) map[string]interface{} {
|
||||
m := map[string]interface{}{
|
||||
"ProxyType": "connect-proxy",
|
||||
}
|
||||
|
||||
for k, v := range overrides {
|
||||
m[k] = v
|
||||
}
|
||||
|
||||
return m
|
||||
}
|
||||
|
||||
cases := map[string]struct {
|
||||
extensionName string
|
||||
arguments map[string]interface{}
|
||||
expected ratelimit
|
||||
ok bool
|
||||
expectedErrMsg string
|
||||
}{
|
||||
"with no arguments": {
|
||||
arguments: nil,
|
||||
ok: false,
|
||||
},
|
||||
"with an invalid name": {
|
||||
arguments: makeArguments(map[string]interface{}{}),
|
||||
extensionName: "bad",
|
||||
ok: false,
|
||||
},
|
||||
"MaxToken is missing": {
|
||||
arguments: makeArguments(map[string]interface{}{
|
||||
"ProxyType": "connect-proxy",
|
||||
"FillInterval": 30,
|
||||
"TokensPerFill": 5,
|
||||
}),
|
||||
expectedErrMsg: "MaxTokens is missing",
|
||||
ok: false,
|
||||
},
|
||||
"MaxTokens <= 0": {
|
||||
arguments: makeArguments(map[string]interface{}{
|
||||
"ProxyType": "connect-proxy",
|
||||
"FillInterval": 30,
|
||||
"TokensPerFill": 5,
|
||||
"MaxTokens": 0,
|
||||
}),
|
||||
expectedErrMsg: "MaxTokens must be greater than 0",
|
||||
ok: false,
|
||||
},
|
||||
"FillInterval is missing": {
|
||||
arguments: makeArguments(map[string]interface{}{
|
||||
"ProxyType": "connect-proxy",
|
||||
"TokensPerFill": 5,
|
||||
"MaxTokens": 10,
|
||||
}),
|
||||
expectedErrMsg: "FillInterval(in second) is missing",
|
||||
ok: false,
|
||||
},
|
||||
"FillInterval <= 0": {
|
||||
arguments: makeArguments(map[string]interface{}{
|
||||
"ProxyType": "connect-proxy",
|
||||
"FillInterval": 0,
|
||||
"TokensPerFill": 5,
|
||||
"MaxTokens": 10,
|
||||
}),
|
||||
expectedErrMsg: "FillInterval(in second) must be greater than 0",
|
||||
ok: false,
|
||||
},
|
||||
"TokensPerFill <= 0": {
|
||||
arguments: makeArguments(map[string]interface{}{
|
||||
"ProxyType": "connect-proxy",
|
||||
"FillInterval": 30,
|
||||
"TokensPerFill": 0,
|
||||
"MaxTokens": 10,
|
||||
}),
|
||||
expectedErrMsg: "TokensPerFill must be greater than 0",
|
||||
ok: false,
|
||||
},
|
||||
"FilterEnabled < 0": {
|
||||
arguments: makeArguments(map[string]interface{}{
|
||||
"ProxyType": "connect-proxy",
|
||||
"FillInterval": 30,
|
||||
"TokensPerFill": 5,
|
||||
"MaxTokens": 10,
|
||||
"FilterEnabled": -1,
|
||||
}),
|
||||
expectedErrMsg: "cannot parse 'FilterEnabled', -1 overflows uint",
|
||||
ok: false,
|
||||
},
|
||||
"FilterEnforced < 0": {
|
||||
arguments: makeArguments(map[string]interface{}{
|
||||
"ProxyType": "connect-proxy",
|
||||
"FillInterval": 30,
|
||||
"TokensPerFill": 5,
|
||||
"MaxTokens": 10,
|
||||
"FilterEnforced": -1,
|
||||
}),
|
||||
expectedErrMsg: "cannot parse 'FilterEnforced', -1 overflows uint",
|
||||
ok: false,
|
||||
},
|
||||
"valid everything": {
|
||||
arguments: makeArguments(map[string]interface{}{
|
||||
"ProxyType": "connect-proxy",
|
||||
"FillInterval": 30,
|
||||
"MaxTokens": 20,
|
||||
"TokensPerFill": 5,
|
||||
}),
|
||||
expected: ratelimit{
|
||||
ProxyType: "connect-proxy",
|
||||
MaxTokens: intPointer(20),
|
||||
FillInterval: intPointer(30),
|
||||
TokensPerFill: intPointer(5),
|
||||
},
|
||||
ok: true,
|
||||
},
|
||||
}
|
||||
|
||||
for n, tc := range cases {
|
||||
t.Run(n, func(t *testing.T) {
|
||||
|
||||
extensionName := api.BuiltinLocalRatelimitExtension
|
||||
if tc.extensionName != "" {
|
||||
extensionName = tc.extensionName
|
||||
}
|
||||
|
||||
svc := api.CompoundServiceName{Name: "svc"}
|
||||
ext := extensioncommon.RuntimeConfig{
|
||||
ServiceName: svc,
|
||||
EnvoyExtension: api.EnvoyExtension{
|
||||
Name: extensionName,
|
||||
Arguments: tc.arguments,
|
||||
},
|
||||
}
|
||||
|
||||
e, err := Constructor(ext.EnvoyExtension)
|
||||
|
||||
if tc.ok {
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, &extensioncommon.BasicEnvoyExtender{Extension: &tc.expected}, e)
|
||||
} else {
|
||||
require.Error(t, err)
|
||||
require.Contains(t, err.Error(), tc.expectedErrMsg)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func intPointer(i int) *int {
|
||||
return &i
|
||||
}
|
|
@ -4,6 +4,7 @@ import (
|
|||
"fmt"
|
||||
|
||||
awslambda "github.com/hashicorp/consul/agent/envoyextensions/builtin/aws-lambda"
|
||||
"github.com/hashicorp/consul/agent/envoyextensions/builtin/http/localratelimit"
|
||||
"github.com/hashicorp/consul/agent/envoyextensions/builtin/lua"
|
||||
"github.com/hashicorp/consul/api"
|
||||
"github.com/hashicorp/consul/envoyextensions/extensioncommon"
|
||||
|
@ -13,8 +14,9 @@ import (
|
|||
type extensionConstructor func(api.EnvoyExtension) (extensioncommon.EnvoyExtender, error)
|
||||
|
||||
var extensionConstructors = map[string]extensionConstructor{
|
||||
api.BuiltinLuaExtension: lua.Constructor,
|
||||
api.BuiltinAWSLambdaExtension: awslambda.Constructor,
|
||||
api.BuiltinLuaExtension: lua.Constructor,
|
||||
api.BuiltinAWSLambdaExtension: awslambda.Constructor,
|
||||
api.BuiltinLocalRatelimitExtension: localratelimit.Constructor,
|
||||
}
|
||||
|
||||
// ConstructExtension attempts to lookup and build an extension from the registry with the
|
||||
|
|
|
@ -208,6 +208,27 @@ end`,
|
|||
return proxycfg.TestConfigSnapshotDiscoveryChain(t, "default", nsFunc, nil, makeLambdaServiceDefaults(true))
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "http-local-ratelimit-applyto-filter",
|
||||
create: func(t testinf.T) *proxycfg.ConfigSnapshot {
|
||||
return proxycfg.TestConfigSnapshot(t, func(ns *structs.NodeService) {
|
||||
ns.Proxy.Config["protocol"] = "http"
|
||||
ns.Proxy.EnvoyExtensions = []structs.EnvoyExtension{
|
||||
{
|
||||
Name: api.BuiltinLocalRatelimitExtension,
|
||||
Arguments: map[string]interface{}{
|
||||
"ProxyType": "connect-proxy",
|
||||
"MaxTokens": 3,
|
||||
"TokensPerFill": 2,
|
||||
"FillInterval": 10,
|
||||
"FilterEnabled": 100,
|
||||
"FilterEnforced": 100,
|
||||
},
|
||||
},
|
||||
}
|
||||
}, nil)
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
latestEnvoyVersion := xdscommon.EnvoyVersions[0]
|
||||
|
|
127
agent/xds/testdata/builtin_extension/clusters/http-local-ratelimit-applyto-filter.latest.golden
vendored
Normal file
127
agent/xds/testdata/builtin_extension/clusters/http-local-ratelimit-applyto-filter.latest.golden
vendored
Normal file
|
@ -0,0 +1,127 @@
|
|||
{
|
||||
"versionInfo": "00000001",
|
||||
"resources": [
|
||||
{
|
||||
"@type": "type.googleapis.com/envoy.config.cluster.v3.Cluster",
|
||||
"name": "db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
|
||||
"altStatName": "db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
|
||||
"type": "EDS",
|
||||
"edsClusterConfig": {
|
||||
"edsConfig": {
|
||||
"ads": {},
|
||||
"resourceApiVersion": "V3"
|
||||
}
|
||||
},
|
||||
"connectTimeout": "5s",
|
||||
"circuitBreakers": {},
|
||||
"outlierDetection": {},
|
||||
"commonLbConfig": {
|
||||
"healthyPanicThreshold": {}
|
||||
},
|
||||
"transportSocket": {
|
||||
"name": "tls",
|
||||
"typedConfig": {
|
||||
"@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext",
|
||||
"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"
|
||||
},
|
||||
"matchSubjectAltNames": [
|
||||
{
|
||||
"exact": "spiffe://11111111-2222-3333-4444-555555555555.consul/ns/default/dc/dc1/svc/db"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"sni": "db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"@type": "type.googleapis.com/envoy.config.cluster.v3.Cluster",
|
||||
"name": "geo-cache.default.dc1.query.11111111-2222-3333-4444-555555555555.consul",
|
||||
"type": "EDS",
|
||||
"edsClusterConfig": {
|
||||
"edsConfig": {
|
||||
"ads": {},
|
||||
"resourceApiVersion": "V3"
|
||||
}
|
||||
},
|
||||
"connectTimeout": "5s",
|
||||
"circuitBreakers": {},
|
||||
"outlierDetection": {},
|
||||
"transportSocket": {
|
||||
"name": "tls",
|
||||
"typedConfig": {
|
||||
"@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext",
|
||||
"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"
|
||||
},
|
||||
"matchSubjectAltNames": [
|
||||
{
|
||||
"exact": "spiffe://11111111-2222-3333-4444-555555555555.consul/ns/default/dc/dc1/svc/geo-cache-target"
|
||||
},
|
||||
{
|
||||
"exact": "spiffe://11111111-2222-3333-4444-555555555555.consul/ns/default/dc/dc2/svc/geo-cache-target"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"sni": "geo-cache.default.dc1.query.11111111-2222-3333-4444-555555555555.consul"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"@type": "type.googleapis.com/envoy.config.cluster.v3.Cluster",
|
||||
"name": "local_app",
|
||||
"type": "STATIC",
|
||||
"connectTimeout": "5s",
|
||||
"loadAssignment": {
|
||||
"clusterName": "local_app",
|
||||
"endpoints": [
|
||||
{
|
||||
"lbEndpoints": [
|
||||
{
|
||||
"endpoint": {
|
||||
"address": {
|
||||
"socketAddress": {
|
||||
"address": "127.0.0.1",
|
||||
"portValue": 8080
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"typeUrl": "type.googleapis.com/envoy.config.cluster.v3.Cluster",
|
||||
"nonce": "00000001"
|
||||
}
|
75
agent/xds/testdata/builtin_extension/endpoints/http-local-ratelimit-applyto-filter.latest.golden
vendored
Normal file
75
agent/xds/testdata/builtin_extension/endpoints/http-local-ratelimit-applyto-filter.latest.golden
vendored
Normal file
|
@ -0,0 +1,75 @@
|
|||
{
|
||||
"versionInfo": "00000001",
|
||||
"resources": [
|
||||
{
|
||||
"@type": "type.googleapis.com/envoy.config.endpoint.v3.ClusterLoadAssignment",
|
||||
"clusterName": "db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
|
||||
"endpoints": [
|
||||
{
|
||||
"lbEndpoints": [
|
||||
{
|
||||
"endpoint": {
|
||||
"address": {
|
||||
"socketAddress": {
|
||||
"address": "10.10.1.1",
|
||||
"portValue": 8080
|
||||
}
|
||||
}
|
||||
},
|
||||
"healthStatus": "HEALTHY",
|
||||
"loadBalancingWeight": 1
|
||||
},
|
||||
{
|
||||
"endpoint": {
|
||||
"address": {
|
||||
"socketAddress": {
|
||||
"address": "10.10.1.2",
|
||||
"portValue": 8080
|
||||
}
|
||||
}
|
||||
},
|
||||
"healthStatus": "HEALTHY",
|
||||
"loadBalancingWeight": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"@type": "type.googleapis.com/envoy.config.endpoint.v3.ClusterLoadAssignment",
|
||||
"clusterName": "geo-cache.default.dc1.query.11111111-2222-3333-4444-555555555555.consul",
|
||||
"endpoints": [
|
||||
{
|
||||
"lbEndpoints": [
|
||||
{
|
||||
"endpoint": {
|
||||
"address": {
|
||||
"socketAddress": {
|
||||
"address": "10.10.1.1",
|
||||
"portValue": 8080
|
||||
}
|
||||
}
|
||||
},
|
||||
"healthStatus": "HEALTHY",
|
||||
"loadBalancingWeight": 1
|
||||
},
|
||||
{
|
||||
"endpoint": {
|
||||
"address": {
|
||||
"socketAddress": {
|
||||
"address": "10.20.1.2",
|
||||
"portValue": 8080
|
||||
}
|
||||
}
|
||||
},
|
||||
"healthStatus": "HEALTHY",
|
||||
"loadBalancingWeight": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"typeUrl": "type.googleapis.com/envoy.config.endpoint.v3.ClusterLoadAssignment",
|
||||
"nonce": "00000001"
|
||||
}
|
256
agent/xds/testdata/builtin_extension/listeners/http-local-ratelimit-applyto-filter.latest.golden
vendored
Normal file
256
agent/xds/testdata/builtin_extension/listeners/http-local-ratelimit-applyto-filter.latest.golden
vendored
Normal file
|
@ -0,0 +1,256 @@
|
|||
{
|
||||
"versionInfo": "00000001",
|
||||
"resources": [
|
||||
{
|
||||
"@type": "type.googleapis.com/envoy.config.listener.v3.Listener",
|
||||
"name": "db:127.0.0.1:9191",
|
||||
"address": {
|
||||
"socketAddress": {
|
||||
"address": "127.0.0.1",
|
||||
"portValue": 9191
|
||||
}
|
||||
},
|
||||
"filterChains": [
|
||||
{
|
||||
"filters": [
|
||||
{
|
||||
"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"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"trafficDirection": "OUTBOUND"
|
||||
},
|
||||
{
|
||||
"@type": "type.googleapis.com/envoy.config.listener.v3.Listener",
|
||||
"name": "prepared_query:geo-cache:127.10.10.10:8181",
|
||||
"address": {
|
||||
"socketAddress": {
|
||||
"address": "127.10.10.10",
|
||||
"portValue": 8181
|
||||
}
|
||||
},
|
||||
"filterChains": [
|
||||
{
|
||||
"filters": [
|
||||
{
|
||||
"name": "envoy.filters.network.tcp_proxy",
|
||||
"typedConfig": {
|
||||
"@type": "type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy",
|
||||
"statPrefix": "upstream.prepared_query_geo-cache",
|
||||
"cluster": "geo-cache.default.dc1.query.11111111-2222-3333-4444-555555555555.consul"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"trafficDirection": "OUTBOUND"
|
||||
},
|
||||
{
|
||||
"@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.http_connection_manager",
|
||||
"typedConfig": {
|
||||
"@type": "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager",
|
||||
"statPrefix": "public_listener",
|
||||
"routeConfig": {
|
||||
"name": "public_listener",
|
||||
"virtualHosts": [
|
||||
{
|
||||
"name": "public_listener",
|
||||
"domains": [
|
||||
"*"
|
||||
],
|
||||
"routes": [
|
||||
{
|
||||
"match": {
|
||||
"prefix": "/"
|
||||
},
|
||||
"route": {
|
||||
"cluster": "local_app"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"httpFilters": [
|
||||
{
|
||||
"name": "envoy.filters.http.local_ratelimit",
|
||||
"typedConfig": {
|
||||
"@type": "type.googleapis.com/envoy.extensions.filters.http.local_ratelimit.v3.LocalRateLimit",
|
||||
"statPrefix": "local_ratelimit",
|
||||
"tokenBucket": {
|
||||
"maxTokens": 3,
|
||||
"tokensPerFill": 2,
|
||||
"fillInterval": "10s"
|
||||
},
|
||||
"filterEnabled": {
|
||||
"defaultValue": {
|
||||
"numerator": 100
|
||||
}
|
||||
},
|
||||
"filterEnforced": {
|
||||
"defaultValue": {
|
||||
"numerator": 100
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "envoy.filters.http.rbac",
|
||||
"typedConfig": {
|
||||
"@type": "type.googleapis.com/envoy.extensions.filters.http.rbac.v3.RBAC",
|
||||
"rules": {}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "envoy.filters.http.header_to_metadata",
|
||||
"typedConfig": {
|
||||
"@type": "type.googleapis.com/envoy.extensions.filters.http.header_to_metadata.v3.Config",
|
||||
"requestRules": [
|
||||
{
|
||||
"header": "x-forwarded-client-cert",
|
||||
"onHeaderPresent": {
|
||||
"metadataNamespace": "consul",
|
||||
"key": "trust-domain",
|
||||
"regexValueRewrite": {
|
||||
"pattern": {
|
||||
"googleRe2": {},
|
||||
"regex": ".*URI=spiffe://([^/]+.[^/]+)(?:/ap/([^/]+))?/ns/([^/]+)/dc/([^/]+)/svc/([^/;,]+).*"
|
||||
},
|
||||
"substitution": "\\1"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"header": "x-forwarded-client-cert",
|
||||
"onHeaderPresent": {
|
||||
"metadataNamespace": "consul",
|
||||
"key": "partition",
|
||||
"regexValueRewrite": {
|
||||
"pattern": {
|
||||
"googleRe2": {},
|
||||
"regex": ".*URI=spiffe://([^/]+.[^/]+)(?:/ap/([^/]+))?/ns/([^/]+)/dc/([^/]+)/svc/([^/;,]+).*"
|
||||
},
|
||||
"substitution": "\\2"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"header": "x-forwarded-client-cert",
|
||||
"onHeaderPresent": {
|
||||
"metadataNamespace": "consul",
|
||||
"key": "namespace",
|
||||
"regexValueRewrite": {
|
||||
"pattern": {
|
||||
"googleRe2": {},
|
||||
"regex": ".*URI=spiffe://([^/]+.[^/]+)(?:/ap/([^/]+))?/ns/([^/]+)/dc/([^/]+)/svc/([^/;,]+).*"
|
||||
},
|
||||
"substitution": "\\3"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"header": "x-forwarded-client-cert",
|
||||
"onHeaderPresent": {
|
||||
"metadataNamespace": "consul",
|
||||
"key": "datacenter",
|
||||
"regexValueRewrite": {
|
||||
"pattern": {
|
||||
"googleRe2": {},
|
||||
"regex": ".*URI=spiffe://([^/]+.[^/]+)(?:/ap/([^/]+))?/ns/([^/]+)/dc/([^/]+)/svc/([^/;,]+).*"
|
||||
},
|
||||
"substitution": "\\4"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"header": "x-forwarded-client-cert",
|
||||
"onHeaderPresent": {
|
||||
"metadataNamespace": "consul",
|
||||
"key": "service",
|
||||
"regexValueRewrite": {
|
||||
"pattern": {
|
||||
"googleRe2": {},
|
||||
"regex": ".*URI=spiffe://([^/]+.[^/]+)(?:/ap/([^/]+))?/ns/([^/]+)/dc/([^/]+)/svc/([^/;,]+).*"
|
||||
},
|
||||
"substitution": "\\5"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "envoy.filters.http.router",
|
||||
"typedConfig": {
|
||||
"@type": "type.googleapis.com/envoy.extensions.filters.http.router.v3.Router"
|
||||
}
|
||||
}
|
||||
],
|
||||
"tracing": {
|
||||
"randomSampling": {}
|
||||
},
|
||||
"forwardClientCertDetails": "APPEND_FORWARD",
|
||||
"setCurrentClientCertDetails": {
|
||||
"subject": true,
|
||||
"cert": true,
|
||||
"chain": true,
|
||||
"dns": true,
|
||||
"uri": 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"
|
||||
}
|
||||
},
|
||||
"alpnProtocols": [
|
||||
"http/1.1"
|
||||
]
|
||||
},
|
||||
"requireClientCertificate": true
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"trafficDirection": "INBOUND"
|
||||
}
|
||||
],
|
||||
"typeUrl": "type.googleapis.com/envoy.config.listener.v3.Listener",
|
||||
"nonce": "00000001"
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"versionInfo": "00000001",
|
||||
"typeUrl": "type.googleapis.com/envoy.config.route.v3.RouteConfiguration",
|
||||
"nonce": "00000001"
|
||||
}
|
|
@ -33,8 +33,9 @@ const (
|
|||
)
|
||||
|
||||
const (
|
||||
BuiltinAWSLambdaExtension string = "builtin/aws/lambda"
|
||||
BuiltinLuaExtension string = "builtin/lua"
|
||||
BuiltinAWSLambdaExtension string = "builtin/aws/lambda"
|
||||
BuiltinLuaExtension string = "builtin/lua"
|
||||
BuiltinLocalRatelimitExtension string = "builtin/http/localratelimit"
|
||||
)
|
||||
|
||||
type ConfigEntry interface {
|
||||
|
|
2
go.mod
2
go.mod
|
@ -28,6 +28,7 @@ require (
|
|||
github.com/fsnotify/fsnotify v1.5.1
|
||||
github.com/go-openapi/runtime v0.24.1
|
||||
github.com/go-openapi/strfmt v0.21.3
|
||||
github.com/golang/protobuf v1.5.2
|
||||
github.com/google/go-cmp v0.5.8
|
||||
github.com/google/gofuzz v1.2.0
|
||||
github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1
|
||||
|
@ -151,7 +152,6 @@ require (
|
|||
github.com/go-ozzo/ozzo-validation v3.6.0+incompatible // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e // indirect
|
||||
github.com/golang/protobuf v1.5.2 // indirect
|
||||
github.com/golang/snappy v0.0.4 // indirect
|
||||
github.com/google/btree v1.0.0 // indirect
|
||||
github.com/google/go-querystring v1.0.0 // indirect
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
#!/bin/bash
|
||||
|
||||
snapshot_envoy_admin localhost:19000 s1 primary || true
|
||||
snapshot_envoy_admin localhost:19001 s2 primary || true
|
|
@ -0,0 +1,16 @@
|
|||
services {
|
||||
name = "s1"
|
||||
port = 8080
|
||||
connect {
|
||||
sidecar_service {
|
||||
proxy {
|
||||
upstreams = [
|
||||
{
|
||||
destination_name = "s2"
|
||||
local_bind_port = 5000
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
services {
|
||||
name = "s2"
|
||||
port = 8181
|
||||
connect { sidecar_service {} }
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
#!/bin/bash
|
||||
|
||||
set -eEuo pipefail
|
||||
|
||||
upsert_config_entry primary '
|
||||
Kind = "service-defaults"
|
||||
Name = "s2"
|
||||
Protocol = "http"
|
||||
EnvoyExtensions = [
|
||||
{
|
||||
Name = "builtin/http/localratelimit",
|
||||
Arguments = {
|
||||
ProxyType = "connect-proxy"
|
||||
MaxTokens = 1,
|
||||
TokensPerFill = 1,
|
||||
FillInterval = 120,
|
||||
FilterEnabled = 100,
|
||||
FilterEnforced = 100,
|
||||
}
|
||||
}
|
||||
]
|
||||
'
|
||||
|
||||
upsert_config_entry primary '
|
||||
Kind = "service-defaults"
|
||||
Name = "s1"
|
||||
Protocol = "tcp"
|
||||
EnvoyExtensions = [
|
||||
{
|
||||
Name = "builtin/http/localratelimit",
|
||||
Arguments = {
|
||||
ProxyType = "connect-proxy"
|
||||
MaxTokens = 1,
|
||||
TokensPerFill = 1,
|
||||
FillInterval = 120,
|
||||
FilterEnabled = 100,
|
||||
FilterEnforced = 100,
|
||||
}
|
||||
}
|
||||
]
|
||||
'
|
||||
|
||||
register_services primary
|
||||
|
||||
gen_envoy_bootstrap s1 19000 primary
|
||||
gen_envoy_bootstrap s2 19001 primary
|
|
@ -0,0 +1,3 @@
|
|||
#!/bin/bash
|
||||
|
||||
export REQUIRED_SERVICES="s1 s1-sidecar-proxy s2 s2-sidecar-proxy"
|
|
@ -0,0 +1,57 @@
|
|||
#!/usr/bin/env bats
|
||||
|
||||
load helpers
|
||||
|
||||
@test "s1 proxy admin is up on :19000" {
|
||||
retry_default curl -f -s localhost:19000/stats -o /dev/null
|
||||
}
|
||||
|
||||
@test "s2 proxy admin is up on :19001" {
|
||||
retry_default curl -f -s localhost:19001/stats -o /dev/null
|
||||
}
|
||||
|
||||
@test "s1 proxy listener should be up and have right cert" {
|
||||
assert_proxy_presents_cert_uri localhost:21000 s1
|
||||
}
|
||||
|
||||
@test "s2 proxy listener should be up and have right cert" {
|
||||
assert_proxy_presents_cert_uri localhost:21001 s2
|
||||
}
|
||||
|
||||
@test "s2 proxy should be healthy" {
|
||||
assert_service_has_healthy_instances s2 1
|
||||
}
|
||||
|
||||
@test "s1 upstream should have healthy endpoints for s2" {
|
||||
assert_upstream_has_endpoints_in_status 127.0.0.1:19000 s2.default.primary HEALTHY 1
|
||||
}
|
||||
|
||||
@test "s2 proxy should have been configured with http local ratelimit filters" {
|
||||
HTTP_FILTERS=$(get_envoy_http_filters localhost:19001)
|
||||
PUB=$(echo "$HTTP_FILTERS" | grep -E "^public_listener:" | cut -f 2 -d ' ')
|
||||
|
||||
echo "HTTP_FILTERS = $HTTP_FILTERS"
|
||||
echo "PUB = $PUB"
|
||||
|
||||
[ "$PUB" = "envoy.filters.http.local_ratelimit,envoy.filters.http.rbac,envoy.filters.http.header_to_metadata,envoy.filters.http.router" ]
|
||||
}
|
||||
|
||||
@test "s1(tcp) proxy should not be changed by http/localratelimit extension" {
|
||||
TCP_FILTERS=$(get_envoy_listener_filters localhost:19000)
|
||||
PUB=$(echo "$TCP_FILTERS" | grep -E "^public_listener:" | cut -f 2 -d ' ')
|
||||
|
||||
echo "TCP_FILTERS = $TCP_FILTERS"
|
||||
echo "PUB = $PUB"
|
||||
|
||||
[ "$PUB" = "envoy.filters.network.rbac,envoy.filters.network.tcp_proxy" ]
|
||||
}
|
||||
|
||||
@test "first connection to s2 - success" {
|
||||
run retry_default curl -s -f -d hello localhost:5000
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" == *"hello"* ]]
|
||||
}
|
||||
|
||||
@test "ratelimit to s2 is in effect - return code 429" {
|
||||
retry_default must_fail_http_connection localhost:5000 429
|
||||
}
|
Loading…
Reference in New Issue