Skip to content

Commit

Permalink
cmd/contour: add EnableXRateLimitHeaders config file setting
Browse files Browse the repository at this point in the history
Adds the EnableXRateLimitHeaders field to the config file's
RateLimitService block. When set to true, adds the X-RateLimit
headers to responses that required checking the RLS.

Closes #3431.

Signed-off-by: Steve Kriss <[email protected]>
  • Loading branch information
skriss committed Mar 16, 2021
1 parent 7b18f01 commit f852d5c
Show file tree
Hide file tree
Showing 9 changed files with 548 additions and 40 deletions.
9 changes: 5 additions & 4 deletions cmd/contour/serve.go
Original file line number Diff line number Diff line change
Expand Up @@ -350,10 +350,11 @@ func doServe(log logrus.FieldLogger, ctx *serveContext) error {
}

listenerConfig.RateLimitConfig = &xdscache_v3.RateLimitConfig{
ExtensionService: namespacedName,
Domain: ctx.Config.RateLimitService.Domain,
Timeout: responseTimeout,
FailOpen: ctx.Config.RateLimitService.FailOpen,
ExtensionService: namespacedName,
Domain: ctx.Config.RateLimitService.Domain,
Timeout: responseTimeout,
FailOpen: ctx.Config.RateLimitService.FailOpen,
EnableXRateLimitHeaders: ctx.Config.RateLimitService.EnableXRateLimitHeaders,
}
}

Expand Down
6 changes: 6 additions & 0 deletions examples/contour/01-contour-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,12 @@ data:
# service fails to respond with a valid rate limit decision within
# the timeout defined on the extension service.
# failOpen: false
# Defines whether to include the X-RateLimit headers X-RateLimit-Limit,
# X-RateLimit-Remaining, and X-RateLimit-Reset (as defined by the IETF
# Internet-Draft linked below), on responses to clients when the Rate
# Limit Service is consulted for a request.
# ref. https://tools.ietf.org/id/draft-polli-ratelimit-headers-03.html
# enableXRateLimitHeaders: false
#
# Global Policy settings.
# policy:
Expand Down
6 changes: 6 additions & 0 deletions examples/render/contour.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,12 @@ data:
# service fails to respond with a valid rate limit decision within
# the timeout defined on the extension service.
# failOpen: false
# Defines whether to include the X-RateLimit headers X-RateLimit-Limit,
# X-RateLimit-Remaining, and X-RateLimit-Reset (as defined by the IETF
# Internet-Draft linked below), on responses to clients when the Rate
# Limit Service is consulted for a request.
# ref. https://tools.ietf.org/id/draft-polli-ratelimit-headers-03.html
# enableXRateLimitHeaders: false
#
# Global Policy settings.
# policy:
Expand Down
17 changes: 13 additions & 4 deletions internal/envoy/v3/ratelimit.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,10 +111,11 @@ func GlobalRateLimits(descriptors []*dag.RateLimitDescriptor) []*envoy_route_v3.
// GlobalRateLimitConfig stores configuration for
// an HTTP global rate limiting filter.
type GlobalRateLimitConfig struct {
ExtensionService types.NamespacedName
FailOpen bool
Timeout timeout.Setting
Domain string
ExtensionService types.NamespacedName
FailOpen bool
Timeout timeout.Setting
Domain string
EnableXRateLimitHeaders bool
}

// GlobalRateLimitFilter returns a configured HTTP global rate limit filter,
Expand All @@ -141,7 +142,15 @@ func GlobalRateLimitFilter(config *GlobalRateLimitConfig) *http.HttpFilter {
},
TransportApiVersion: envoy_core_v3.ApiVersion_V3,
},
EnableXRatelimitHeaders: enableXRateLimitHeaders(config.EnableXRateLimitHeaders),
}),
},
}
}

func enableXRateLimitHeaders(enable bool) ratelimit_filter_v3.RateLimit_XRateLimitHeadersRFCVersion {
if enable {
return ratelimit_filter_v3.RateLimit_DRAFT_VERSION_03
}
return ratelimit_filter_v3.RateLimit_OFF
}
118 changes: 94 additions & 24 deletions internal/envoy/v3/ratelimit_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -180,35 +180,105 @@ func TestGlobalRateLimits(t *testing.T) {
}

func TestGlobalRateLimitFilter(t *testing.T) {
assert.Nil(t, GlobalRateLimitFilter(nil))

want := &http.HttpFilter{
Name: wellknown.HTTPRateLimit,
ConfigType: &http.HttpFilter_TypedConfig{
TypedConfig: protobuf.MustMarshalAny(&ratelimit_filter_v3.RateLimit{
Domain: "domain",
Timeout: protobuf.Duration(time.Second),
FailureModeDeny: false,
RateLimitService: &ratelimit_config_v3.RateLimitServiceConfig{
GrpcService: &envoy_core_v3.GrpcService{
TargetSpecifier: &envoy_core_v3.GrpcService_EnvoyGrpc_{
EnvoyGrpc: &envoy_core_v3.GrpcService_EnvoyGrpc{
ClusterName: "extension/projectcontour/ratelimit",
tests := map[string]struct {
cfg *GlobalRateLimitConfig
want *http.HttpFilter
}{
"nil config produces nil filter": {
cfg: nil,
want: nil,
},
"all fields configured correctly with FailOpen=false": {
cfg: &GlobalRateLimitConfig{
ExtensionService: k8s.NamespacedNameFrom("projectcontour/ratelimit"),
Timeout: timeout.DurationSetting(7 * time.Second),
Domain: "domain",
FailOpen: false,
},
want: &http.HttpFilter{
Name: wellknown.HTTPRateLimit,
ConfigType: &http.HttpFilter_TypedConfig{
TypedConfig: protobuf.MustMarshalAny(&ratelimit_filter_v3.RateLimit{
Domain: "domain",
Timeout: protobuf.Duration(7 * time.Second),
FailureModeDeny: true,
RateLimitService: &ratelimit_config_v3.RateLimitServiceConfig{
GrpcService: &envoy_core_v3.GrpcService{
TargetSpecifier: &envoy_core_v3.GrpcService_EnvoyGrpc_{
EnvoyGrpc: &envoy_core_v3.GrpcService_EnvoyGrpc{
ClusterName: "extension/projectcontour/ratelimit",
},
},
},
TransportApiVersion: envoy_core_v3.ApiVersion_V3,
},
},
TransportApiVersion: envoy_core_v3.ApiVersion_V3,
}),
},
},
},
"all fields configured correctly with FailOpen=true": {
cfg: &GlobalRateLimitConfig{
ExtensionService: k8s.NamespacedNameFrom("projectcontour/ratelimit"),
Timeout: timeout.DurationSetting(7 * time.Second),
Domain: "domain",
FailOpen: true,
},
want: &http.HttpFilter{
Name: wellknown.HTTPRateLimit,
ConfigType: &http.HttpFilter_TypedConfig{
TypedConfig: protobuf.MustMarshalAny(&ratelimit_filter_v3.RateLimit{
Domain: "domain",
Timeout: protobuf.Duration(7 * time.Second),
FailureModeDeny: false,
RateLimitService: &ratelimit_config_v3.RateLimitServiceConfig{
GrpcService: &envoy_core_v3.GrpcService{
TargetSpecifier: &envoy_core_v3.GrpcService_EnvoyGrpc_{
EnvoyGrpc: &envoy_core_v3.GrpcService_EnvoyGrpc{
ClusterName: "extension/projectcontour/ratelimit",
},
},
},
TransportApiVersion: envoy_core_v3.ApiVersion_V3,
},
}),
},
}),
},
},
"EnableXRateLimitHeaders=true is configured correctly": {
cfg: &GlobalRateLimitConfig{
ExtensionService: k8s.NamespacedNameFrom("projectcontour/ratelimit"),
Timeout: timeout.DurationSetting(7 * time.Second),
Domain: "domain",
FailOpen: true,
EnableXRateLimitHeaders: true,
},
want: &http.HttpFilter{
Name: wellknown.HTTPRateLimit,
ConfigType: &http.HttpFilter_TypedConfig{
TypedConfig: protobuf.MustMarshalAny(&ratelimit_filter_v3.RateLimit{
Domain: "domain",
Timeout: protobuf.Duration(7 * time.Second),
FailureModeDeny: false,
RateLimitService: &ratelimit_config_v3.RateLimitServiceConfig{
GrpcService: &envoy_core_v3.GrpcService{
TargetSpecifier: &envoy_core_v3.GrpcService_EnvoyGrpc_{
EnvoyGrpc: &envoy_core_v3.GrpcService_EnvoyGrpc{
ClusterName: "extension/projectcontour/ratelimit",
},
},
},
TransportApiVersion: envoy_core_v3.ApiVersion_V3,
},
EnableXRatelimitHeaders: ratelimit_filter_v3.RateLimit_DRAFT_VERSION_03,
}),
},
},
},
}

cfg := &GlobalRateLimitConfig{
ExtensionService: k8s.NamespacedNameFrom("projectcontour/ratelimit"),
FailOpen: true,
Timeout: timeout.DurationSetting(time.Second),
Domain: "domain",
for name, tc := range tests {
t.Run(name, func(t *testing.T) {
assert.Equal(t, tc.want, GlobalRateLimitFilter(tc.cfg))
})
}

assert.Equal(t, want, GlobalRateLimitFilter(cfg))
}
18 changes: 10 additions & 8 deletions internal/xdscache/v3/listener.go
Original file line number Diff line number Diff line change
Expand Up @@ -141,10 +141,11 @@ type ListenerConfig struct {
}

type RateLimitConfig struct {
ExtensionService types.NamespacedName
Domain string
Timeout timeout.Setting
FailOpen bool
ExtensionService types.NamespacedName
Domain string
Timeout timeout.Setting
FailOpen bool
EnableXRateLimitHeaders bool
}

// httpAddress returns the port for the HTTP (non TLS)
Expand Down Expand Up @@ -406,10 +407,11 @@ func envoyGlobalRateLimitConfig(config *RateLimitConfig) *envoy_v3.GlobalRateLim
}

return &envoy_v3.GlobalRateLimitConfig{
ExtensionService: config.ExtensionService,
FailOpen: config.FailOpen,
Timeout: config.Timeout,
Domain: config.Domain,
ExtensionService: config.ExtensionService,
FailOpen: config.FailOpen,
Timeout: config.Timeout,
Domain: config.Domain,
EnableXRateLimitHeaders: config.EnableXRateLimitHeaders,
}
}

Expand Down
Loading

0 comments on commit f852d5c

Please sign in to comment.