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"
|
"fmt"
|
||||||
|
|
||||||
awslambda "github.com/hashicorp/consul/agent/envoyextensions/builtin/aws-lambda"
|
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/agent/envoyextensions/builtin/lua"
|
||||||
"github.com/hashicorp/consul/api"
|
"github.com/hashicorp/consul/api"
|
||||||
"github.com/hashicorp/consul/envoyextensions/extensioncommon"
|
"github.com/hashicorp/consul/envoyextensions/extensioncommon"
|
||||||
|
@ -13,8 +14,9 @@ import (
|
||||||
type extensionConstructor func(api.EnvoyExtension) (extensioncommon.EnvoyExtender, error)
|
type extensionConstructor func(api.EnvoyExtension) (extensioncommon.EnvoyExtender, error)
|
||||||
|
|
||||||
var extensionConstructors = map[string]extensionConstructor{
|
var extensionConstructors = map[string]extensionConstructor{
|
||||||
api.BuiltinLuaExtension: lua.Constructor,
|
api.BuiltinLuaExtension: lua.Constructor,
|
||||||
api.BuiltinAWSLambdaExtension: awslambda.Constructor,
|
api.BuiltinAWSLambdaExtension: awslambda.Constructor,
|
||||||
|
api.BuiltinLocalRatelimitExtension: localratelimit.Constructor,
|
||||||
}
|
}
|
||||||
|
|
||||||
// ConstructExtension attempts to lookup and build an extension from the registry with the
|
// 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))
|
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]
|
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 (
|
const (
|
||||||
BuiltinAWSLambdaExtension string = "builtin/aws/lambda"
|
BuiltinAWSLambdaExtension string = "builtin/aws/lambda"
|
||||||
BuiltinLuaExtension string = "builtin/lua"
|
BuiltinLuaExtension string = "builtin/lua"
|
||||||
|
BuiltinLocalRatelimitExtension string = "builtin/http/localratelimit"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ConfigEntry interface {
|
type ConfigEntry interface {
|
||||||
|
|
2
go.mod
2
go.mod
|
@ -28,6 +28,7 @@ require (
|
||||||
github.com/fsnotify/fsnotify v1.5.1
|
github.com/fsnotify/fsnotify v1.5.1
|
||||||
github.com/go-openapi/runtime v0.24.1
|
github.com/go-openapi/runtime v0.24.1
|
||||||
github.com/go-openapi/strfmt v0.21.3
|
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/go-cmp v0.5.8
|
||||||
github.com/google/gofuzz v1.2.0
|
github.com/google/gofuzz v1.2.0
|
||||||
github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1
|
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/go-ozzo/ozzo-validation v3.6.0+incompatible // indirect
|
||||||
github.com/gogo/protobuf v1.3.2 // indirect
|
github.com/gogo/protobuf v1.3.2 // indirect
|
||||||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e // 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/golang/snappy v0.0.4 // indirect
|
||||||
github.com/google/btree v1.0.0 // indirect
|
github.com/google/btree v1.0.0 // indirect
|
||||||
github.com/google/go-querystring 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