Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions api/v1alpha1/clienttrafficpolicy_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,14 @@ type HeaderSettings struct {
// +optional
DisableRateLimitHeaders *bool `json:"disableRateLimitHeaders,omitempty"`

// DisableXForwardedForAppend configures Envoy Proxy to stop appending the downstream address
// to the X-Forwarded-For header.
//
// This only disables the automatic append behavior. It does not remove or sanitize
// an incoming X-Forwarded-For header.
// +optional
DisableXForwardedForAppend *bool `json:"disableXForwardedForAppend,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 @@ -237,6 +237,14 @@ spec:
DisableRateLimitHeaders configures Envoy Proxy to omit the "X-RateLimit-" response headers
when rate limiting is enabled.
type: boolean
disableXForwardedForAppend:
description: |-
DisableXForwardedForAppend configures Envoy Proxy to stop appending the downstream address
to the X-Forwarded-For header.

This only disables the automatic append behavior. It does not remove or sanitize
an incoming X-Forwarded-For header.
type: boolean
earlyRequestHeaders:
description: |-
EarlyRequestHeaders defines settings for early request header modification, before envoy performs
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,14 @@ spec:
DisableRateLimitHeaders configures Envoy Proxy to omit the "X-RateLimit-" response headers
when rate limiting is enabled.
type: boolean
disableXForwardedForAppend:
description: |-
DisableXForwardedForAppend configures Envoy Proxy to stop appending the downstream address
to the X-Forwarded-For header.

This only disables the automatic append behavior. It does not remove or sanitize
an incoming X-Forwarded-For header.
type: boolean
earlyRequestHeaders:
description: |-
EarlyRequestHeaders defines settings for early request header modification, before envoy performs
Expand Down
1 change: 1 addition & 0 deletions internal/gatewayapi/clienttrafficpolicy.go
Original file line number Diff line number Diff line change
Expand Up @@ -672,6 +672,7 @@ func translateListenerHeaderSettings(headerSettings *egv1a1.HeaderSettings, http
httpIR.Headers = &ir.HeaderSettings{
EnableEnvoyHeaders: ptr.Deref(headerSettings.EnableEnvoyHeaders, false),
DisableRateLimitHeaders: ptr.Deref(headerSettings.DisableRateLimitHeaders, false),
DisableXForwardedForAppend: ptr.Deref(headerSettings.DisableXForwardedForAppend, false),
WithUnderscoresAction: ir.WithUnderscoresAction(ptr.Deref(headerSettings.WithUnderscoresAction, egv1a1.WithUnderscoresActionRejectRequest)),
}
if headerSettings.RequestID != nil {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ clientTrafficPolicies:
clientIPDetection:
xForwardedFor:
numTrustedHops: 2
headers:
disableXForwardedForAppend: true
targetRef:
group: gateway.networking.k8s.io
kind: Gateway
Expand Down Expand Up @@ -43,6 +45,19 @@ clientTrafficPolicies:
kind: Gateway
name: gateway-1
sectionName: http-3
- apiVersion: gateway.envoyproxy.io/v1alpha1
kind: ClientTrafficPolicy
metadata:
namespace: envoy-gateway
name: target-gateway-1-http-4
spec:
headers:
disableXForwardedForAppend: true
targetRef:
group: gateway.networking.k8s.io
kind: Gateway
name: gateway-1
sectionName: http-4
gateways:
- apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ clientTrafficPolicies:
clientIPDetection:
xForwardedFor:
numTrustedHops: 2
headers:
disableXForwardedForAppend: true
targetRef:
group: gateway.networking.k8s.io
kind: Gateway
Expand Down Expand Up @@ -88,6 +90,34 @@ clientTrafficPolicies:
status: "True"
type: Accepted
controllerName: gateway.envoyproxy.io/gatewayclass-controller
- apiVersion: gateway.envoyproxy.io/v1alpha1
kind: ClientTrafficPolicy
metadata:
name: target-gateway-1-http-4
namespace: envoy-gateway
spec:
headers:
disableXForwardedForAppend: true
targetRef:
group: gateway.networking.k8s.io
kind: Gateway
name: gateway-1
sectionName: http-4
status:
ancestors:
- ancestorRef:
group: gateway.networking.k8s.io
kind: Gateway
name: gateway-1
namespace: envoy-gateway
sectionName: http-4
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
Expand Down Expand Up @@ -284,6 +314,9 @@ xdsIR:
xForwardedFor:
numTrustedHops: 2
externalPort: 8081
headers:
disableXForwardedForAppend: true
withUnderscoresAction: RejectRequest
hostnames:
- '*'
metadata:
Expand Down Expand Up @@ -334,6 +367,9 @@ xdsIR:
port: 8083
- address: 0.0.0.0
externalPort: 8084
headers:
disableXForwardedForAppend: true
withUnderscoresAction: RejectRequest
hostnames:
- '*'
metadata:
Expand Down
4 changes: 4 additions & 0 deletions internal/ir/xds.go
Original file line number Diff line number Diff line change
Expand Up @@ -777,6 +777,10 @@ type HeaderSettings struct {
// 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"`

// DisableXForwardedForAppend controls if Envoy should stop appending the downstream address to
// the X-Forwarded-For header. The default is to keep appending the downstream address.
DisableXForwardedForAppend bool `json:"disableXForwardedForAppend,omitempty" yaml:"disableXForwardedForAppend,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
8 changes: 4 additions & 4 deletions internal/xds/translator/listener.go
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ func xffNumTrustedHops(clientIPDetection *ir.ClientIPDetectionSettings) uint32 {
return 0
}

func originalIPDetectionExtensions(clientIPDetection *ir.ClientIPDetectionSettings) []*corev3.TypedExtensionConfig {
func originalIPDetectionExtensions(clientIPDetection *ir.ClientIPDetectionSettings, headers *ir.HeaderSettings) []*corev3.TypedExtensionConfig {
// Return early if settings are nil
if clientIPDetection == nil {
return nil
Expand Down Expand Up @@ -183,12 +183,12 @@ func originalIPDetectionExtensions(clientIPDetection *ir.ClientIPDetectionSettin
XffTrustedCidrs: &xffv3.XffTrustedCidrs{
Cidrs: trustedCidrs,
},
SkipXffAppend: wrapperspb.Bool(false),
SkipXffAppend: wrapperspb.Bool(headers != nil && headers.DisableXForwardedForAppend),
})
} else if clientIPDetection.XForwardedFor.NumTrustedHops != nil {
xffHeaderConfigAny, _ = proto.ToAnyWithValidation(&xffv3.XffConfig{
XffNumTrustedHops: xffNumTrustedHops(clientIPDetection),
Copy link
Member

@rudrakhp rudrakhp Mar 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Check if this logic in xffNumTrustedHops() is affected by change in appending behavior. For this we can write a E2E test with XFF append disabled and using num trusted hops.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

SkipXffAppend: wrapperspb.Bool(false),
SkipXffAppend: wrapperspb.Bool(headers != nil && headers.DisableXForwardedForAppend),
})
}
extensionConfig = append(extensionConfig, &corev3.TypedExtensionConfig{
Expand Down Expand Up @@ -354,7 +354,7 @@ func (t *Translator) addHCMToXDSListener(
// HTTP filter configuration
// Client IP detection
useRemoteAddress := true
originalIPDetectionExtensions := originalIPDetectionExtensions(irListener.ClientIPDetection)
originalIPDetectionExtensions := originalIPDetectionExtensions(irListener.ClientIPDetection, irListener.Headers)
if originalIPDetectionExtensions != nil {
useRemoteAddress = false
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ http:
clientIPDetection:
xForwardedFor:
numTrustedHops: 2
headers:
disableXForwardedForAppend: true
- name: "second-listener"
address: "::"
port: 8082
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
- name: envoy.extensions.http.original_ip_detection.xff
typedConfig:
'@type': type.googleapis.com/envoy.extensions.http.original_ip_detection.xff.v3.XffConfig
skipXffAppend: false
skipXffAppend: true
xffNumTrustedHops: 1
rds:
configSource:
Expand Down
1 change: 1 addition & 0 deletions release-notes/current.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ new features: |
Added support for retry budget in BackendTrafficPolicy.
Added support for BackendUtilization load balancing policy in BackendTrafficPolicy.
Added support for upstrean access log.
Added `spec.headers.disableXForwardedForAppend` to ClientTrafficPolicy to disable Envoy's automatic X-Forwarded-For append behavior when using XFF-based client IP detection.

bug fixes: |
Rejected ClientTrafficPolicy if invalid TLS cipher suites are configured.
Expand Down
1 change: 1 addition & 0 deletions site/content/en/latest/api/extension_types.md
Original file line number Diff line number Diff line change
Expand Up @@ -3048,6 +3048,7 @@ _Appears in:_
| --- | --- | --- | --- | --- |
| `enableEnvoyHeaders` | _boolean_ | false | | EnableEnvoyHeaders configures Envoy Proxy to add the "X-Envoy-" headers to requests<br />and responses. |
| `disableRateLimitHeaders` | _boolean_ | false | | DisableRateLimitHeaders configures Envoy Proxy to omit the "X-RateLimit-" response headers<br />when rate limiting is enabled. |
| `disableXForwardedForAppend` | _boolean_ | false | | DisableXForwardedForAppend configures Envoy Proxy to stop appending the downstream address<br />to the X-Forwarded-For header.<br />This only disables the automatic append behavior. It does not remove or sanitize<br />an incoming X-Forwarded-For header. |
| `xForwardedClientCert` | _[XForwardedClientCert](#xforwardedclientcert)_ | false | | XForwardedClientCert configures how Envoy Proxy handle the x-forwarded-client-cert (XFCC) HTTP header.<br />x-forwarded-client-cert (XFCC) is an HTTP header used to forward the certificate<br />information of part or all of the clients or proxies that a request has flowed through,<br />on its way from the client to the server.<br />Envoy proxy may choose to sanitize/append/forward the XFCC header before proxying the request.<br />If not set, the default behavior is sanitizing the XFCC header. |
| `withUnderscoresAction` | _[WithUnderscoresAction](#withunderscoresaction)_ | false | | WithUnderscoresAction configures the action to take when an HTTP header with underscores<br />is encountered. The default action is to reject the request. |
| `preserveXRequestID` | _boolean_ | false | | PreserveXRequestID configures Envoy to keep the X-Request-ID header if passed for a request that is edge<br />(Edge request is the request from external clients to front Envoy) and not reset it, which is the current Envoy behaviour.<br />Defaults to false and cannot be combined with RequestID.<br />Deprecated: use RequestID=PreserveOrGenerate instead |
Expand Down
11 changes: 11 additions & 0 deletions site/content/en/latest/tasks/traffic/client-traffic-policy.md
Original file line number Diff line number Diff line change
Expand Up @@ -408,6 +408,17 @@ curl -v http://$GATEWAY_HOST/get \
If `numTrustedHops` is set to N, the client IP is taken from the Nth address from the right end of the XFF header. In this example,
we set `numTrustedHops` to 2, so the client IP will be taken from the second rightmost address in the XFF header.

To stop Envoy Gateway from automatically appending the downstream client address to `X-Forwarded-For`,
set `spec.headers.disableXForwardedForAppend: true` in the same `ClientTrafficPolicy`.
This only disables the automatic append behavior and does not remove or sanitize an incoming
`X-Forwarded-For` header.

```yaml
spec:
headers:
disableXForwardedForAppend: true
```

You should expect 200 response status, see that `X-Forwarded-Proto` was preserved and `X-Envoy-External-Address` was set to the
second rightmost address in the `X-Forwarded-For` header:

Expand Down
20 changes: 20 additions & 0 deletions test/cel-validation/clienttrafficpolicy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -541,6 +541,26 @@ func TestClientTrafficPolicyTarget(t *testing.T) {
"spec.http2.initialConnectionWindowSize: Invalid value: \"15m\": spec.http2.initialConnectionWindowSize in body should match '^[1-9]+[0-9]*([EPTGMK]i|[EPTGMk])?$'",
},
},
{
desc: "valid disableXForwardedForAppend header setting",
mutate: func(ctp *egv1a1.ClientTrafficPolicy) {
ctp.Spec = egv1a1.ClientTrafficPolicySpec{
PolicyTargetReferences: egv1a1.PolicyTargetReferences{
TargetRef: &gwapiv1.LocalPolicyTargetReferenceWithSectionName{
LocalPolicyTargetReference: gwapiv1.LocalPolicyTargetReference{
Group: gwapiv1.Group("gateway.networking.k8s.io"),
Kind: gwapiv1.Kind("Gateway"),
Name: gwapiv1.ObjectName("eg"),
},
},
},
Headers: &egv1a1.HeaderSettings{
DisableXForwardedForAppend: ptr.To(true),
},
}
},
wantErrors: []string{},
},
{
desc: "invalid xffc setting",
mutate: func(ctp *egv1a1.ClientTrafficPolicy) {
Expand Down
32 changes: 32 additions & 0 deletions test/e2e/testdata/disable-xff-append.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
apiVersion: gateway.envoyproxy.io/v1alpha1
kind: ClientTrafficPolicy
metadata:
name: disable-xff-append-ctp
namespace: gateway-conformance-infra
spec:
targetRefs:
- group: gateway.networking.k8s.io
kind: Gateway
name: same-namespace
headers:
disableXForwardedForAppend: true
clientIPDetection:
xForwardedFor:
numTrustedHops: 1
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: http-with-disable-xff-append
namespace: gateway-conformance-infra
spec:
parentRefs:
- name: same-namespace
rules:
- matches:
- path:
type: PathPrefix
value: /disable-xff-append
backendRefs:
- name: infra-backend-v1
port: 8080
72 changes: 72 additions & 0 deletions test/e2e/tests/disable_xff_append.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
// Copyright Envoy Gateway Authors
// SPDX-License-Identifier: Apache-2.0
// The full text of the Apache license is available in the LICENSE file at
// the root of the repo.

//go:build e2e

package tests

import (
"testing"

"k8s.io/apimachinery/pkg/types"
gwapiv1 "sigs.k8s.io/gateway-api/apis/v1"
"sigs.k8s.io/gateway-api/conformance/utils/http"
"sigs.k8s.io/gateway-api/conformance/utils/kubernetes"
"sigs.k8s.io/gateway-api/conformance/utils/suite"

"github.com/envoyproxy/gateway/internal/gatewayapi"
"github.com/envoyproxy/gateway/internal/gatewayapi/resource"
)

func init() {
ConformanceTests = append(ConformanceTests, DisableXFFAppendTest)
}

var DisableXFFAppendTest = suite.ConformanceTest{
ShortName: "DisableXFFAppend",
Description: "Disable X-Forwarded-For append via ClientTrafficPolicy",
Manifests: []string{"testdata/disable-xff-append.yaml"},
Test: func(t *testing.T, suite *suite.ConformanceTestSuite) {
ns := "gateway-conformance-infra"
routeNN := types.NamespacedName{Name: "http-with-disable-xff-append", Namespace: ns}
gwNN := types.NamespacedName{Name: "same-namespace", Namespace: ns}
gwAddr := kubernetes.GatewayAndHTTPRoutesMustBeAccepted(t, suite.Client, suite.TimeoutConfig, suite.ControllerName, kubernetes.NewGatewayRef(gwNN), routeNN)

ancestorRef := gwapiv1.ParentReference{
Group: gatewayapi.GroupPtr(gwapiv1.GroupName),
Kind: gatewayapi.KindPtr(resource.KindGateway),
Namespace: gatewayapi.NamespacePtr(gwNN.Namespace),
Name: gwapiv1.ObjectName(gwNN.Name),
}
ClientTrafficPolicyMustBeAccepted(t, suite.Client, types.NamespacedName{Name: "disable-xff-append-ctp", Namespace: ns}, suite.ControllerName, ancestorRef)

t.Run("X-Forwarded-For header should not be modified", func(t *testing.T) {
expectedResponse := http.ExpectedResponse{
Request: http.Request{
Path: "/disable-xff-append",
Headers: map[string]string{
"X-Forwarded-For": "1.2.3.4",
},
},
ExpectedRequest: &http.ExpectedRequest{
Request: http.Request{
Path: "/disable-xff-append",
Headers: map[string]string{
// With disableXForwardedForAppend=true, Envoy should NOT append
// the downstream client IP to the X-Forwarded-For header.
"X-Forwarded-For": "1.2.3.4",
},
},
},
Response: http.Response{
StatusCodes: []int{200},
},
Namespace: ns,
}

http.MakeRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, gwAddr, expectedResponse)
})
},
}