diff --git a/api/v1alpha1/clienttrafficpolicy_types.go b/api/v1alpha1/clienttrafficpolicy_types.go
index 619bbeb50f3..b95292abdd3 100644
--- a/api/v1alpha1/clienttrafficpolicy_types.go
+++ b/api/v1alpha1/clienttrafficpolicy_types.go
@@ -104,6 +104,13 @@ type HeaderSettings struct {
// is encountered. The default action is to reject the request.
// +optional
WithUnderscoresAction *WithUnderscoresAction `json:"withUnderscoresAction,omitempty"`
+
+ // PreserveExternalRequestID configures Envoy to keep the X-Request-ID header if passed for a request that is edge
+ // (Edge request is the request from external clients to front Envoy) and not reset it, which is the current Envoy behaviour.
+ // It defaults to false.
+ //
+ // +optional
+ PreserveExternalRequestID *bool `json:"preserveExternalRequestID,omitempty"`
}
// WithUnderscoresAction configures the action to take when an HTTP header with underscores
diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go
index 7dcd9227d55..b97285fbf43 100644
--- a/api/v1alpha1/zz_generated.deepcopy.go
+++ b/api/v1alpha1/zz_generated.deepcopy.go
@@ -2183,6 +2183,11 @@ func (in *HeaderSettings) DeepCopyInto(out *HeaderSettings) {
*out = new(WithUnderscoresAction)
**out = **in
}
+ if in.PreserveExternalRequestID != nil {
+ in, out := &in.PreserveExternalRequestID, &out.PreserveExternalRequestID
+ *out = new(bool)
+ **out = **in
+ }
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HeaderSettings.
diff --git a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_clienttrafficpolicies.yaml b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_clienttrafficpolicies.yaml
index 000886cf656..3638a93b36a 100644
--- a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_clienttrafficpolicies.yaml
+++ b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_clienttrafficpolicies.yaml
@@ -150,6 +150,12 @@ spec:
EnableEnvoyHeaders configures Envoy Proxy to add the "X-Envoy-" headers to requests
and responses.
type: boolean
+ preserveExternalRequestID:
+ description: |-
+ PreserveExternalRequestID configures Envoy to keep the X-Request-ID header if passed for a request that is edge
+ (Edge request is the request from external clients to front Envoy) and not reset it, which is the current Envoy behaviour.
+ It defaults to false.
+ type: boolean
withUnderscoresAction:
description: |-
WithUnderscoresAction configures the action to take when an HTTP header with underscores
diff --git a/internal/gatewayapi/clienttrafficpolicy.go b/internal/gatewayapi/clienttrafficpolicy.go
index c8f4ca7ed8f..65730737e07 100644
--- a/internal/gatewayapi/clienttrafficpolicy.go
+++ b/internal/gatewayapi/clienttrafficpolicy.go
@@ -539,8 +539,9 @@ 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)),
+ EnableEnvoyHeaders: ptr.Deref(headerSettings.EnableEnvoyHeaders, false),
+ WithUnderscoresAction: ir.WithUnderscoresAction(ptr.Deref(headerSettings.WithUnderscoresAction, egv1a1.WithUnderscoresActionRejectRequest)),
+ PreserveExternalRequestID: ptr.Deref(headerSettings.PreserveExternalRequestID, false),
}
}
diff --git a/internal/gatewayapi/testdata/clienttrafficpolicy-headers.in.yaml b/internal/gatewayapi/testdata/clienttrafficpolicy-headers.in.yaml
index 136450f2abd..903261f1d39 100644
--- a/internal/gatewayapi/testdata/clienttrafficpolicy-headers.in.yaml
+++ b/internal/gatewayapi/testdata/clienttrafficpolicy-headers.in.yaml
@@ -8,6 +8,7 @@ clientTrafficPolicies:
headers:
enableEnvoyHeaders: true
withUnderscoresAction: Allow
+ preserveExternalRequestID: true
targetRef:
group: gateway.networking.k8s.io
kind: Gateway
diff --git a/internal/gatewayapi/testdata/clienttrafficpolicy-headers.out.yaml b/internal/gatewayapi/testdata/clienttrafficpolicy-headers.out.yaml
index 4a71a59d6d0..39fdf87d7a7 100644
--- a/internal/gatewayapi/testdata/clienttrafficpolicy-headers.out.yaml
+++ b/internal/gatewayapi/testdata/clienttrafficpolicy-headers.out.yaml
@@ -8,6 +8,7 @@ clientTrafficPolicies:
spec:
headers:
enableEnvoyHeaders: true
+ preserveXRequestID: true
withUnderscoresAction: Allow
targetRef:
group: gateway.networking.k8s.io
@@ -130,6 +131,7 @@ xdsIR:
- address: 0.0.0.0
headers:
enableEnvoyHeaders: true
+ preserveExternalRequestID: true
withUnderscoresAction: Allow
hostnames:
- '*'
@@ -142,6 +144,7 @@ xdsIR:
- address: 0.0.0.0
headers:
enableEnvoyHeaders: true
+ preserveExternalRequestID: true
withUnderscoresAction: Allow
hostnames:
- '*'
diff --git a/internal/ir/xds.go b/internal/ir/xds.go
index 7db99e3b6e8..c9742c69a9f 100644
--- a/internal/ir/xds.go
+++ b/internal/ir/xds.go
@@ -406,6 +406,11 @@ type HeaderSettings struct {
// is encountered. The default action is to reject the request.
// Refer to https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/core/v3/protocol.proto#envoy-v3-api-enum-config-core-v3-httpprotocoloptions-headerswithunderscoresaction
WithUnderscoresAction WithUnderscoresAction `json:"withUnderscoresAction,omitempty" yaml:"withUnderscoresAction,omitempty"`
+
+ // PreserveExternalRequestID configures whether Envoy will keep the x-request-id header if passed for a request that is edge
+ // (Edge request is the request from external clients to front Envoy) and not reset it, which is the current Envoy behaviour.
+ // It defaults to false.
+ PreserveExternalRequestID bool `json:"preserveExternalRequestID,omitempty" yaml:"preserveExternalRequestID,omitempty"`
}
// ClientTimeout sets the timeout configuration for downstream connections
diff --git a/internal/xds/translator/listener.go b/internal/xds/translator/listener.go
index f909fd3ff8c..a0a7e564754 100644
--- a/internal/xds/translator/listener.go
+++ b/internal/xds/translator/listener.go
@@ -256,7 +256,8 @@ func (t *Translator) addHCMToXDSListener(xdsListener *listenerv3.Listener, irLis
CommonHttpProtocolOptions: &corev3.HttpProtocolOptions{
HeadersWithUnderscoresAction: buildHeadersWithUnderscoresAction(irListener.Headers),
},
- Tracing: hcmTracing,
+ Tracing: hcmTracing,
+ PreserveExternalRequestId: ptr.Deref(irListener.Headers, ir.HeaderSettings{}).PreserveExternalRequestID,
}
if irListener.Timeout != nil && irListener.Timeout.HTTP != nil {
diff --git a/internal/xds/translator/testdata/in/xds-ir/headers-with-preserve-x-request-id.yaml b/internal/xds/translator/testdata/in/xds-ir/headers-with-preserve-x-request-id.yaml
new file mode 100644
index 00000000000..a75c7edac30
--- /dev/null
+++ b/internal/xds/translator/testdata/in/xds-ir/headers-with-preserve-x-request-id.yaml
@@ -0,0 +1,32 @@
+http:
+- name: "first-listener"
+ address: "0.0.0.0"
+ port: 8081
+ hostnames:
+ - "*"
+ routes:
+ - name: "first-route"
+ hostname: "*"
+ destination:
+ name: "first-route-dest"
+ settings:
+ - endpoints:
+ - host: "1.1.1.1"
+ port: 8081
+ headers:
+ preserveExternalRequestID: true
+- name: "second-listener"
+ address: "0.0.0.0"
+ port: 8082
+ hostnames:
+ - "*"
+ routes:
+ - name: "second-route"
+ hostname: "*"
+ destination:
+ name: "second-route-dest"
+ settings:
+ - endpoints:
+ - host: "2.2.2.2"
+ port: 8082
+
diff --git a/internal/xds/translator/testdata/out/xds-ir/headers-with-preserve-x-request-id.clusters.yaml b/internal/xds/translator/testdata/out/xds-ir/headers-with-preserve-x-request-id.clusters.yaml
new file mode 100755
index 00000000000..d65e267ad7d
--- /dev/null
+++ b/internal/xds/translator/testdata/out/xds-ir/headers-with-preserve-x-request-id.clusters.yaml
@@ -0,0 +1,34 @@
+- circuitBreakers:
+ thresholds:
+ - maxRetries: 1024
+ commonLbConfig:
+ localityWeightedLbConfig: {}
+ connectTimeout: 10s
+ dnsLookupFamily: V4_ONLY
+ edsClusterConfig:
+ edsConfig:
+ ads: {}
+ resourceApiVersion: V3
+ serviceName: first-route-dest
+ lbPolicy: LEAST_REQUEST
+ name: first-route-dest
+ outlierDetection: {}
+ perConnectionBufferLimitBytes: 32768
+ type: EDS
+- circuitBreakers:
+ thresholds:
+ - maxRetries: 1024
+ commonLbConfig:
+ localityWeightedLbConfig: {}
+ connectTimeout: 10s
+ dnsLookupFamily: V4_ONLY
+ edsClusterConfig:
+ edsConfig:
+ ads: {}
+ resourceApiVersion: V3
+ serviceName: second-route-dest
+ lbPolicy: LEAST_REQUEST
+ name: second-route-dest
+ outlierDetection: {}
+ perConnectionBufferLimitBytes: 32768
+ type: EDS
diff --git a/internal/xds/translator/testdata/out/xds-ir/headers-with-preserve-x-request-id.endpoints.yaml b/internal/xds/translator/testdata/out/xds-ir/headers-with-preserve-x-request-id.endpoints.yaml
new file mode 100755
index 00000000000..9ad09dd91b0
--- /dev/null
+++ b/internal/xds/translator/testdata/out/xds-ir/headers-with-preserve-x-request-id.endpoints.yaml
@@ -0,0 +1,24 @@
+- clusterName: first-route-dest
+ endpoints:
+ - lbEndpoints:
+ - endpoint:
+ address:
+ socketAddress:
+ address: 1.1.1.1
+ portValue: 8081
+ loadBalancingWeight: 1
+ loadBalancingWeight: 1
+ locality:
+ region: first-route-dest/backend/0
+- clusterName: second-route-dest
+ endpoints:
+ - lbEndpoints:
+ - endpoint:
+ address:
+ socketAddress:
+ address: 2.2.2.2
+ portValue: 8082
+ loadBalancingWeight: 1
+ loadBalancingWeight: 1
+ locality:
+ region: second-route-dest/backend/0
diff --git a/internal/xds/translator/testdata/out/xds-ir/headers-with-preserve-x-request-id.listeners.yaml b/internal/xds/translator/testdata/out/xds-ir/headers-with-preserve-x-request-id.listeners.yaml
new file mode 100644
index 00000000000..64401c87014
--- /dev/null
+++ b/internal/xds/translator/testdata/out/xds-ir/headers-with-preserve-x-request-id.listeners.yaml
@@ -0,0 +1,63 @@
+- address:
+ socketAddress:
+ address: 0.0.0.0
+ portValue: 8081
+ defaultFilterChain:
+ filters:
+ - name: envoy.filters.network.http_connection_manager
+ typedConfig:
+ '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
+ commonHttpProtocolOptions: {}
+ http2ProtocolOptions:
+ initialConnectionWindowSize: 1048576
+ initialStreamWindowSize: 65536
+ maxConcurrentStreams: 100
+ httpFilters:
+ - name: envoy.filters.http.router
+ typedConfig:
+ '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
+ suppressEnvoyHeaders: true
+ normalizePath: true
+ preserveExternalRequestId: true
+ rds:
+ configSource:
+ ads: {}
+ resourceApiVersion: V3
+ routeConfigName: first-listener
+ serverHeaderTransformation: PASS_THROUGH
+ statPrefix: http
+ useRemoteAddress: true
+ drainType: MODIFY_ONLY
+ name: first-listener
+ perConnectionBufferLimitBytes: 32768
+- address:
+ socketAddress:
+ address: 0.0.0.0
+ portValue: 8082
+ defaultFilterChain:
+ filters:
+ - name: envoy.filters.network.http_connection_manager
+ typedConfig:
+ '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
+ commonHttpProtocolOptions: {}
+ http2ProtocolOptions:
+ initialConnectionWindowSize: 1048576
+ initialStreamWindowSize: 65536
+ maxConcurrentStreams: 100
+ httpFilters:
+ - name: envoy.filters.http.router
+ typedConfig:
+ '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
+ suppressEnvoyHeaders: true
+ normalizePath: true
+ rds:
+ configSource:
+ ads: {}
+ resourceApiVersion: V3
+ routeConfigName: second-listener
+ serverHeaderTransformation: PASS_THROUGH
+ statPrefix: http
+ useRemoteAddress: true
+ drainType: MODIFY_ONLY
+ name: second-listener
+ perConnectionBufferLimitBytes: 32768
diff --git a/internal/xds/translator/testdata/out/xds-ir/headers-with-preserve-x-request-id.routes.yaml b/internal/xds/translator/testdata/out/xds-ir/headers-with-preserve-x-request-id.routes.yaml
new file mode 100755
index 00000000000..ff93cfff360
--- /dev/null
+++ b/internal/xds/translator/testdata/out/xds-ir/headers-with-preserve-x-request-id.routes.yaml
@@ -0,0 +1,28 @@
+- ignorePortInHostMatching: true
+ name: first-listener
+ virtualHosts:
+ - domains:
+ - '*'
+ name: first-listener/*
+ routes:
+ - match:
+ prefix: /
+ name: first-route
+ route:
+ cluster: first-route-dest
+ upgradeConfigs:
+ - upgradeType: websocket
+- ignorePortInHostMatching: true
+ name: second-listener
+ virtualHosts:
+ - domains:
+ - '*'
+ name: second-listener/*
+ routes:
+ - match:
+ prefix: /
+ name: second-route
+ route:
+ cluster: second-route-dest
+ upgradeConfigs:
+ - upgradeType: websocket
diff --git a/site/content/en/latest/api/extension_types.md b/site/content/en/latest/api/extension_types.md
index 5756c6df0cc..5e97bb37b84 100644
--- a/site/content/en/latest/api/extension_types.md
+++ b/site/content/en/latest/api/extension_types.md
@@ -1614,6 +1614,7 @@ _Appears in:_
| --- | --- | --- | --- |
| `enableEnvoyHeaders` | _boolean_ | false | EnableEnvoyHeaders configures Envoy Proxy to add the "X-Envoy-" headers to requests
and responses. |
| `withUnderscoresAction` | _[WithUnderscoresAction](#withunderscoresaction)_ | false | WithUnderscoresAction configures the action to take when an HTTP header with underscores
is encountered. The default action is to reject the request. |
+| `preserveExternalRequestID` | _boolean_ | false | PreserveExternalRequestID configures Envoy to keep the X-Request-ID header if passed for a request that is edge
(Edge request is the request from external clients to front Envoy) and not reset it, which is the current Envoy behaviour.
It defaults to false. |
#### HealthCheck