Skip to content

Commit

Permalink
feat(translator): support disabling X-RateLimit headers (#3397)
Browse files Browse the repository at this point in the history
* feat(translator): support disabling X-RateLimit headers

Signed-off-by: Edoardo Vacchi <evacchi@users.noreply.github.com>

* bad copypasta

Signed-off-by: Edoardo Vacchi <evacchi@users.noreply.github.com>

* use ClientTrafficPolicy.Spec.Headers

Signed-off-by: Edoardo Vacchi <evacchi@users.noreply.github.com>

* add test case

Signed-off-by: Edoardo Vacchi <evacchi@users.noreply.github.com>

* add test case in internal/gatewayapi/testdata

Signed-off-by: Edoardo Vacchi <evacchi@users.noreply.github.com>

* missing translation from CTP to IR

Signed-off-by: Edoardo Vacchi <evacchi@users.noreply.github.com>

* fix docs

Signed-off-by: Edoardo Vacchi <evacchi@users.noreply.github.com>

* e2e test

Signed-off-by: Edoardo Vacchi <evacchi@users.noreply.github.com>

* e2e test

Signed-off-by: Edoardo Vacchi <evacchi@users.noreply.github.com>

---------

Signed-off-by: Edoardo Vacchi <evacchi@users.noreply.github.com>
Co-authored-by: Guy Daich <guy.daich@sap.com>
  • Loading branch information
evacchi and guydc authored May 30, 2024
1 parent 6021f9f commit bcaa1b0
Show file tree
Hide file tree
Showing 16 changed files with 666 additions and 17 deletions.
5 changes: 5 additions & 0 deletions api/v1alpha1/clienttrafficpolicy_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,11 @@ type HeaderSettings struct {
// +optional
EnableEnvoyHeaders *bool `json:"enableEnvoyHeaders,omitempty"`

// DisableRateLimitHeaders configures Envoy Proxy to omit the "X-RateLimit-" response headers
// when rate limiting is enabled.
// +optional
DisableRateLimitHeaders *bool `json:"disableRateLimitHeaders,omitempty"`

// XForwardedClientCert configures how Envoy Proxy handle the x-forwarded-client-cert (XFCC) HTTP header.
//
// x-forwarded-client-cert (XFCC) is an HTTP header used to forward the certificate
Expand Down
5 changes: 5 additions & 0 deletions api/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,11 @@ spec:
headers:
description: HeaderSettings provides configuration for header management.
properties:
disableRateLimitHeaders:
description: |-
DisableRateLimitHeaders configures Envoy Proxy to omit the "X-RateLimit-" response headers
when rate limiting is enabled.
type: boolean
enableEnvoyHeaders:
description: |-
EnableEnvoyHeaders configures Envoy Proxy to add the "X-Envoy-" headers to requests
Expand Down
7 changes: 4 additions & 3 deletions internal/gatewayapi/clienttrafficpolicy.go
Original file line number Diff line number Diff line change
Expand Up @@ -614,9 +614,10 @@ func translateListenerHeaderSettings(headerSettings *egv1a1.HeaderSettings, http
return
}
httpIR.Headers = &ir.HeaderSettings{
EnableEnvoyHeaders: ptr.Deref(headerSettings.EnableEnvoyHeaders, false),
WithUnderscoresAction: ir.WithUnderscoresAction(ptr.Deref(headerSettings.WithUnderscoresAction, egv1a1.WithUnderscoresActionRejectRequest)),
PreserveXRequestID: ptr.Deref(headerSettings.PreserveXRequestID, false),
EnableEnvoyHeaders: ptr.Deref(headerSettings.EnableEnvoyHeaders, false),
DisableRateLimitHeaders: ptr.Deref(headerSettings.DisableRateLimitHeaders, false),
WithUnderscoresAction: ir.WithUnderscoresAction(ptr.Deref(headerSettings.WithUnderscoresAction, egv1a1.WithUnderscoresActionRejectRequest)),
PreserveXRequestID: ptr.Deref(headerSettings.PreserveXRequestID, false),
}

if headerSettings.XForwardedClientCert != nil {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
clientTrafficPolicies:
- apiVersion: gateway.envoyproxy.io/v1alpha1
kind: ClientTrafficPolicy
metadata:
namespace: envoy-gateway
name: target-gateway-1
spec:
headers:
enableEnvoyHeaders: true
disableRateLimitHeaders: true
targetRef:
group: gateway.networking.k8s.io
kind: Gateway
name: gateway-1
gateways:
- apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
namespace: envoy-gateway
name: gateway-1
spec:
gatewayClassName: envoy-gateway-class
listeners:
- name: http-1
protocol: HTTP
port: 80
allowedRoutes:
namespaces:
from: Same
- name: http-2
protocol: HTTP
port: 8080
allowedRoutes:
namespaces:
from: Same
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
clientTrafficPolicies:
- apiVersion: gateway.envoyproxy.io/v1alpha1
kind: ClientTrafficPolicy
metadata:
creationTimestamp: null
name: target-gateway-1
namespace: envoy-gateway
spec:
headers:
disableRateLimitHeaders: true
enableEnvoyHeaders: true
targetRef:
group: gateway.networking.k8s.io
kind: Gateway
name: gateway-1
status:
ancestors:
- ancestorRef:
group: gateway.networking.k8s.io
kind: Gateway
name: gateway-1
namespace: envoy-gateway
conditions:
- lastTransitionTime: null
message: Policy has been accepted.
reason: Accepted
status: "True"
type: Accepted
controllerName: gateway.envoyproxy.io/gatewayclass-controller
gateways:
- apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
creationTimestamp: null
name: gateway-1
namespace: envoy-gateway
spec:
gatewayClassName: envoy-gateway-class
listeners:
- allowedRoutes:
namespaces:
from: Same
name: http-1
port: 80
protocol: HTTP
- allowedRoutes:
namespaces:
from: Same
name: http-2
port: 8080
protocol: HTTP
status:
listeners:
- attachedRoutes: 0
conditions:
- lastTransitionTime: null
message: Sending translated listener configuration to the data plane
reason: Programmed
status: "True"
type: Programmed
- lastTransitionTime: null
message: Listener has been successfully translated
reason: Accepted
status: "True"
type: Accepted
- lastTransitionTime: null
message: Listener references have been resolved
reason: ResolvedRefs
status: "True"
type: ResolvedRefs
name: http-1
supportedKinds:
- group: gateway.networking.k8s.io
kind: HTTPRoute
- group: gateway.networking.k8s.io
kind: GRPCRoute
- attachedRoutes: 0
conditions:
- lastTransitionTime: null
message: Sending translated listener configuration to the data plane
reason: Programmed
status: "True"
type: Programmed
- lastTransitionTime: null
message: Listener has been successfully translated
reason: Accepted
status: "True"
type: Accepted
- lastTransitionTime: null
message: Listener references have been resolved
reason: ResolvedRefs
status: "True"
type: ResolvedRefs
name: http-2
supportedKinds:
- group: gateway.networking.k8s.io
kind: HTTPRoute
- group: gateway.networking.k8s.io
kind: GRPCRoute
infraIR:
envoy-gateway/gateway-1:
proxy:
listeners:
- address: null
name: envoy-gateway/gateway-1/http-1
ports:
- containerPort: 10080
name: http-80
protocol: HTTP
servicePort: 80
- address: null
name: envoy-gateway/gateway-1/http-2
ports:
- containerPort: 8080
name: http-8080
protocol: HTTP
servicePort: 8080
metadata:
labels:
gateway.envoyproxy.io/owning-gateway-name: gateway-1
gateway.envoyproxy.io/owning-gateway-namespace: envoy-gateway
name: envoy-gateway/gateway-1
xdsIR:
envoy-gateway/gateway-1:
accessLog:
text:
- path: /dev/stdout
http:
- address: 0.0.0.0
headers:
disableRateLimitHeaders: true
enableEnvoyHeaders: true
withUnderscoresAction: RejectRequest
hostnames:
- '*'
isHTTP2: false
name: envoy-gateway/gateway-1/http-1
path:
escapedSlashesAction: UnescapeAndRedirect
mergeSlashes: true
port: 10080
- address: 0.0.0.0
headers:
disableRateLimitHeaders: true
enableEnvoyHeaders: true
withUnderscoresAction: RejectRequest
hostnames:
- '*'
isHTTP2: false
name: envoy-gateway/gateway-1/http-2
path:
escapedSlashesAction: UnescapeAndRedirect
mergeSlashes: true
port: 8080
5 changes: 5 additions & 0 deletions internal/ir/xds.go
Original file line number Diff line number Diff line change
Expand Up @@ -430,6 +430,11 @@ type HeaderSettings struct {
// Refer to https://www.envoyproxy.io/docs/envoy/latest/api-v3/extensions/filters/http/router/v3/router.proto#extensions-filters-http-router-v3-router
EnableEnvoyHeaders bool `json:"enableEnvoyHeaders,omitempty" yaml:"enableEnvoyHeaders,omitempty"`

// DisableRateLimitHeaders controls if "x-ratelimit-" headers are added by the HTTP Router filter.
// The default is to emit these headers.
// https://www.envoyproxy.io/docs/envoy/latest/api-v3/extensions/filters/http/ratelimit/v3/rate_limit.proto#extensions-filters-http-ratelimit-v3-ratelimit
DisableRateLimitHeaders bool `json:"disableRateLimitHeaders,omitempty" yaml:"disableRateLimitHeaders,omitempty"`

// Configure Envoy proxy how to handle the x-forwarded-client-cert (XFCC) HTTP header.
// refer to https://www.envoyproxy.io/docs/envoy/latest/api-v3/extensions/filters/network/http_connection_manager/v3/http_connection_manager.proto#envoy-v3-api-enum-extensions-filters-network-http-connection-manager-v3-httpconnectionmanager-forwardclientcertdetails
XForwardedClientCert *XForwardedClientCert `json:"xForwardedClientCert,omitempty" yaml:"xForwardedClientCert,omitempty"`
Expand Down
20 changes: 6 additions & 14 deletions internal/xds/translator/ratelimit.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,19 +40,6 @@ const (
rateLimitClientTLSCACertFilename = "/certs/ca.crt"
)

const (
// Use `draft RFC Version 03 <https://tools.ietf.org/id/draft-polli-ratelimit-headers-03.html>` by default,
// where 3 headers will be added:
// * ``X-RateLimit-Limit`` - indicates the request-quota associated to the
// client in the current time-window followed by the description of the
// quota policy. The value is returned by the maximum tokens of the token bucket.
// * ``X-RateLimit-Remaining`` - indicates the remaining requests in the
// current time-window. The value is returned by the remaining tokens in the token bucket.
// * ``X-RateLimit-Reset`` - indicates the number of seconds until reset of
// the current time-window. The value is returned by the remaining fill interval of the token bucket.
xRateLimitHeadersRfcVersion = 1
)

// patchHCMWithRateLimit builds and appends the Rate Limit Filter to the HTTP connection manager
// if applicable and it does not already exist.
func (t *Translator) patchHCMWithRateLimit(mgr *hcmv3.HttpConnectionManager, irListener *ir.HTTPListener) {
Expand Down Expand Up @@ -111,12 +98,17 @@ func (t *Translator) buildRateLimitFilter(irListener *ir.HTTPListener) *hcmv3.Ht
},
TransportApiVersion: corev3.ApiVersion_V3,
},
EnableXRatelimitHeaders: ratelimitfilterv3.RateLimit_XRateLimitHeadersRFCVersion(xRateLimitHeadersRfcVersion),
}
if t.GlobalRateLimit.Timeout > 0 {
rateLimitFilterProto.Timeout = durationpb.New(t.GlobalRateLimit.Timeout)
}

if irListener.Headers != nil && irListener.Headers.DisableRateLimitHeaders {
rateLimitFilterProto.EnableXRatelimitHeaders = ratelimitfilterv3.RateLimit_OFF
} else {
rateLimitFilterProto.EnableXRatelimitHeaders = ratelimitfilterv3.RateLimit_DRAFT_VERSION_03
}

if t.GlobalRateLimit.FailClosed {
rateLimitFilterProto.FailureModeDeny = t.GlobalRateLimit.FailClosed
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
http:
- name: "first-listener"
address: "0.0.0.0"
port: 10080
hostnames:
- "*"
path:
mergeSlashes: true
escapedSlashesAction: UnescapeAndRedirect
headers:
disableRateLimitHeaders: true
routes:
- name: "first-route"
hostname: "*"
traffic:
rateLimit:
global:
rules:
- headerMatches:
- name: "x-user-id"
exact: "one"
limit:
requests: 5
unit: second
pathMatch:
exact: "foo/bar"
destination:
name: "first-route-dest"
settings:
- endpoints:
- host: "1.2.3.4"
port: 50000
- name: "second-route"
hostname: "*"
traffic:
rateLimit:
global:
rules:
- headerMatches:
- name: "x-user-id"
distinct: true
limit:
requests: 5
unit: second
pathMatch:
exact: "example"
destination:
name: "second-route-dest"
settings:
- endpoints:
- host: "1.2.3.4"
port: 50000
- name: "third-route"
hostname: "*"
traffic:
rateLimit:
global:
rules:
- limit:
requests: 5
unit: second
pathMatch:
exact: "test"
destination:
name: "third-route-dest"
settings:
- endpoints:
- host: "1.2.3.4"
port: 50000
Loading

0 comments on commit bcaa1b0

Please sign in to comment.