Skip to content
This repository was archived by the owner on Jul 11, 2023. It is now read-only.

Commit ac27868

Browse files
authored
rate-limit: implement connection level local rate limiting (#4823)
Adds support for rate limiting L4 TCP connections. Part of #2018 Signed-off-by: Shashank Ram <[email protected]>
1 parent 327b5b0 commit ac27868

File tree

2 files changed

+116
-1
lines changed

2 files changed

+116
-1
lines changed

pkg/envoy/lds/inmesh.go

+63
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,22 @@ package lds
33
import (
44
"fmt"
55
"strings"
6+
"time"
67

78
xds_core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
89
xds_listener "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3"
10+
xds_local_ratelimit "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/local_ratelimit/v3"
911
xds_tcp_proxy "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/tcp_proxy/v3"
12+
xds_type "github.com/envoyproxy/go-control-plane/envoy/type/v3"
1013
"github.com/envoyproxy/go-control-plane/pkg/wellknown"
1114
"github.com/golang/protobuf/ptypes/any"
1215
"github.com/pkg/errors"
1316
"google.golang.org/protobuf/types/known/anypb"
17+
"google.golang.org/protobuf/types/known/durationpb"
1418
"google.golang.org/protobuf/types/known/wrapperspb"
1519

20+
policyv1alpha1 "github.com/openservicemesh/osm/pkg/apis/policy/v1alpha1"
21+
1622
"github.com/openservicemesh/osm/pkg/constants"
1723
"github.com/openservicemesh/osm/pkg/envoy"
1824
"github.com/openservicemesh/osm/pkg/envoy/rds/route"
@@ -75,6 +81,15 @@ func (lb *listenerBuilder) getInboundHTTPFilters(trafficMatch *trafficpolicy.Tra
7581
filters = append(filters, rbacFilter)
7682
}
7783

84+
// Apply the network level local rate limit filter if configured for the TrafficMatch
85+
if trafficMatch.RateLimit != nil && trafficMatch.RateLimit.Local != nil && trafficMatch.RateLimit.Local.TCP != nil {
86+
rateLimitFilter, err := buildTCPLocalRateLimitFilter(trafficMatch.RateLimit.Local.TCP, trafficMatch.Name)
87+
if err != nil {
88+
return nil, err
89+
}
90+
filters = append(filters, rateLimitFilter)
91+
}
92+
7893
// Build the HTTP Connection Manager filter from its options
7994
inboundConnManager, err := httpConnManagerOptions{
8095
direction: inbound,
@@ -228,6 +243,15 @@ func (lb *listenerBuilder) getInboundTCPFilters(trafficMatch *trafficpolicy.Traf
228243
filters = append(filters, rbacFilter)
229244
}
230245

246+
// Apply the network level local rate limit filter if configured for the TrafficMatch
247+
if trafficMatch.RateLimit != nil && trafficMatch.RateLimit.Local != nil && trafficMatch.RateLimit.Local.TCP != nil {
248+
rateLimitFilter, err := buildTCPLocalRateLimitFilter(trafficMatch.RateLimit.Local.TCP, trafficMatch.Name)
249+
if err != nil {
250+
return nil, err
251+
}
252+
filters = append(filters, rateLimitFilter)
253+
}
254+
231255
// Apply the TCP Proxy Filter
232256
tcpProxy := &xds_tcp_proxy.TcpProxy{
233257
StatPrefix: fmt.Sprintf("%s.%s", inboundMeshTCPProxyStatPrefix, trafficMatch.Cluster),
@@ -248,6 +272,45 @@ func (lb *listenerBuilder) getInboundTCPFilters(trafficMatch *trafficpolicy.Traf
248272
return filters, nil
249273
}
250274

275+
func buildTCPLocalRateLimitFilter(config *policyv1alpha1.TCPLocalRateLimitSpec, statPrefix string) (*xds_listener.Filter, error) {
276+
if config == nil {
277+
return nil, nil
278+
}
279+
280+
var fillInterval time.Duration
281+
switch config.Unit {
282+
case "second":
283+
fillInterval = time.Second
284+
case "minute":
285+
fillInterval = time.Minute
286+
case "hour":
287+
fillInterval = time.Hour
288+
default:
289+
return nil, errors.Errorf("invalid unit %q for TCP connection rate limiting", config.Unit)
290+
}
291+
292+
rateLimit := &xds_local_ratelimit.LocalRateLimit{
293+
StatPrefix: statPrefix,
294+
TokenBucket: &xds_type.TokenBucket{
295+
MaxTokens: config.Connections + config.Burst,
296+
TokensPerFill: wrapperspb.UInt32(config.Connections),
297+
FillInterval: durationpb.New(fillInterval),
298+
},
299+
}
300+
301+
marshalledConfig, err := anypb.New(rateLimit)
302+
if err != nil {
303+
return nil, err
304+
}
305+
306+
filter := &xds_listener.Filter{
307+
Name: wellknown.RateLimit,
308+
ConfigType: &xds_listener.Filter_TypedConfig{TypedConfig: marshalledConfig},
309+
}
310+
311+
return filter, nil
312+
}
313+
251314
// getOutboundHTTPFilter returns an HTTP connection manager network filter used to filter outbound HTTP traffic for the given route configuration
252315
func (lb *listenerBuilder) getOutboundHTTPFilter(routeConfigName string) (*xds_listener.Filter, error) {
253316
var marshalledFilter *any.Any

pkg/envoy/lds/inmesh_test.go

+53-1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313
"google.golang.org/protobuf/types/known/wrapperspb"
1414

1515
configv1alpha2 "github.com/openservicemesh/osm/pkg/apis/config/v1alpha2"
16+
policyv1alpha1 "github.com/openservicemesh/osm/pkg/apis/policy/v1alpha1"
1617

1718
"github.com/openservicemesh/osm/pkg/auth"
1819
"github.com/openservicemesh/osm/pkg/catalog"
@@ -271,6 +272,32 @@ func TestGetInboundMeshHTTPFilterChain(t *testing.T) {
271272
expectedFilterNames: []string{wellknown.HTTPConnectionManager},
272273
expectError: false,
273274
},
275+
{
276+
name: "inbound HTTP filter chain with rate limiting enabled",
277+
permissiveMode: true,
278+
trafficMatch: &trafficpolicy.TrafficMatch{
279+
Name: "inbound_ns1/svc1_90_http",
280+
DestinationPort: 90,
281+
DestinationProtocol: "http",
282+
ServerNames: []string{"svc1.ns1.svc.cluster.local"},
283+
RateLimit: &policyv1alpha1.RateLimitSpec{
284+
Local: &policyv1alpha1.LocalRateLimitSpec{
285+
TCP: &policyv1alpha1.TCPLocalRateLimitSpec{
286+
Connections: 100,
287+
Unit: "minute",
288+
},
289+
},
290+
},
291+
},
292+
expectedFilterChainMatch: &xds_listener.FilterChainMatch{
293+
DestinationPort: &wrapperspb.UInt32Value{Value: 90},
294+
ServerNames: []string{"svc1.ns1.svc.cluster.local"},
295+
TransportProtocol: "tls",
296+
ApplicationProtocols: []string{"osm"},
297+
},
298+
expectedFilterNames: []string{wellknown.RateLimit, wellknown.HTTPConnectionManager},
299+
expectError: false,
300+
},
274301
}
275302

276303
trafficTargets := []trafficpolicy.TrafficTargetWithRoutes{
@@ -358,7 +385,6 @@ func TestGetInboundMeshTCPFilterChain(t *testing.T) {
358385
expectedFilterNames: []string{wellknown.RoleBasedAccessControl, wellknown.TCPProxy},
359386
expectError: false,
360387
},
361-
362388
{
363389
name: "inbound TCP filter chain with permissive mode enabled",
364390
permissiveMode: true,
@@ -377,6 +403,32 @@ func TestGetInboundMeshTCPFilterChain(t *testing.T) {
377403
expectedFilterNames: []string{wellknown.TCPProxy},
378404
expectError: false,
379405
},
406+
{
407+
name: "inbound TCP filter chain with local TCP rate limiting enabled",
408+
permissiveMode: true,
409+
trafficMatch: &trafficpolicy.TrafficMatch{
410+
Name: "inbound_ns1/svc1_90_http",
411+
DestinationPort: 90,
412+
DestinationProtocol: "tcp",
413+
ServerNames: []string{"svc1.ns1.svc.cluster.local"},
414+
RateLimit: &policyv1alpha1.RateLimitSpec{
415+
Local: &policyv1alpha1.LocalRateLimitSpec{
416+
TCP: &policyv1alpha1.TCPLocalRateLimitSpec{
417+
Connections: 100,
418+
Unit: "minute",
419+
},
420+
},
421+
},
422+
},
423+
expectedFilterChainMatch: &xds_listener.FilterChainMatch{
424+
DestinationPort: &wrapperspb.UInt32Value{Value: 90},
425+
ServerNames: []string{"svc1.ns1.svc.cluster.local"},
426+
TransportProtocol: "tls",
427+
ApplicationProtocols: []string{"osm"},
428+
},
429+
expectedFilterNames: []string{wellknown.RateLimit, wellknown.TCPProxy},
430+
expectError: false,
431+
},
380432
}
381433

382434
trafficTargets := []trafficpolicy.TrafficTargetWithRoutes{

0 commit comments

Comments
 (0)