Skip to content

Commit 60ed370

Browse files
Aias00claude
andcommitted
feat(clienttrafficpolicy): support forward_proto_config to infer x-forwarded-proto from PROXY protocol
Add a forwardProtoConfig field to proxyProtocol in ClientTrafficPolicy that exposes Envoy's HttpConnectionManager.forward_proto_config feature. When PROXY protocol is enabled on a listener, this infers the x-forwarded-proto header from the PROXY protocol destination port, so the correct scheme is set for requests forwarded to backends. This is useful when a Layer 4 load balancer (such as AWS NLB) terminates TLS and forwards traffic to Envoy over PROXY protocol, where Envoy would otherwise not know whether the original client request was HTTP or HTTPS. The new field nests httpsDestinationPorts and httpDestinationPorts (each a list of ports in [1, 65535]). At least one of the two lists must be set (enforced via a CEL XValidation). forward_proto_config is an HCM field, so it applies to HTTP listeners only. Closes #9219 Co-Authored-By: Claude <noreply@anthropic.com> Signed-off-by: liuhy <liuhongyu@apache.org>
1 parent 69e072f commit 60ed370

16 files changed

Lines changed: 956 additions & 3 deletions

api/v1alpha1/clienttrafficpolicy_types.go

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -455,6 +455,45 @@ type ProxyProtocolSettings struct {
455455
//
456456
// +optional
457457
Optional *bool `json:"optional,omitempty"`
458+
// ForwardProtoConfig infers the x-forwarded-proto header from the PROXY protocol
459+
// destination port. Only takes effect when PROXY protocol is enabled.
460+
//
461+
// This is useful when a Layer 4 load balancer (such as AWS NLB) terminates TLS and
462+
// forwards traffic to Envoy over PROXY protocol, so that Envoy can set the correct
463+
// scheme for the forwarded request.
464+
//
465+
// +optional
466+
ForwardProtoConfig *ForwardProtoConfig `json:"forwardProtoConfig,omitempty"`
467+
}
468+
469+
// ForwardProtoConfig configures the ports used to infer the x-forwarded-proto
470+
// header from the PROXY protocol destination port. When the restored local address
471+
// (populated by the PROXY protocol listener filter) has a destination port matching
472+
// one of these lists, the x-forwarded-proto header is set to the corresponding scheme.
473+
// At least one of httpsDestinationPorts or httpDestinationPorts must be set.
474+
//
475+
// +kubebuilder:validation:XValidation:rule="(has(self.httpsDestinationPorts) ? self.httpsDestinationPorts.size() : 0) + (has(self.httpDestinationPorts) ? self.httpDestinationPorts.size() : 0) >= 1",message="at least one of httpsDestinationPorts or httpDestinationPorts must be set"
476+
type ForwardProtoConfig struct {
477+
// HTTPSDestinationPorts are the destination ports treated as HTTPS.
478+
// When the PROXY protocol destination port matches one of these, the
479+
// x-forwarded-proto header is set to "https".
480+
// Common values: 443, 8443.
481+
//
482+
// +optional
483+
// +kubebuilder:validation:MinItems=1
484+
// +kubebuilder:validation:MaxItems=16
485+
// +kubebuilder:validation:items:XValidation:rule="self >= 1 && self <= 65535",message="port must be in the range [1, 65535]"
486+
HTTPSDestinationPorts []int32 `json:"httpsDestinationPorts,omitempty"`
487+
// HTTPDestinationPorts are the destination ports treated as HTTP.
488+
// When the PROXY protocol destination port matches one of these, the
489+
// x-forwarded-proto header is set to "http".
490+
// Common values: 80, 8080.
491+
//
492+
// +optional
493+
// +kubebuilder:validation:MinItems=1
494+
// +kubebuilder:validation:MaxItems=16
495+
// +kubebuilder:validation:items:XValidation:rule="self >= 1 && self <= 65535",message="port must be in the range [1, 65535]"
496+
HTTPDestinationPorts []int32 `json:"httpDestinationPorts,omitempty"`
458497
}
459498

460499
//+kubebuilder:object:root=true

api/v1alpha1/zz_generated.deepcopy.go

Lines changed: 30 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_clienttrafficpolicies.yaml

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1012,6 +1012,52 @@ spec:
10121012
If both EnableProxyProtocol and ProxyProtocol are set, ProxyProtocol takes precedence.
10131013
minProperties: 0
10141014
properties:
1015+
forwardProtoConfig:
1016+
description: |-
1017+
ForwardProtoConfig infers the x-forwarded-proto header from the PROXY protocol
1018+
destination port. Only takes effect when PROXY protocol is enabled.
1019+
1020+
This is useful when a Layer 4 load balancer (such as AWS NLB) terminates TLS and
1021+
forwards traffic to Envoy over PROXY protocol, so that Envoy can set the correct
1022+
scheme for the forwarded request.
1023+
properties:
1024+
httpDestinationPorts:
1025+
description: |-
1026+
HTTPDestinationPorts are the destination ports treated as HTTP.
1027+
When the PROXY protocol destination port matches one of these, the
1028+
x-forwarded-proto header is set to "http".
1029+
Common values: 80, 8080.
1030+
items:
1031+
format: int32
1032+
type: integer
1033+
x-kubernetes-validations:
1034+
- message: port must be in the range [1, 65535]
1035+
rule: self >= 1 && self <= 65535
1036+
maxItems: 16
1037+
minItems: 1
1038+
type: array
1039+
httpsDestinationPorts:
1040+
description: |-
1041+
HTTPSDestinationPorts are the destination ports treated as HTTPS.
1042+
When the PROXY protocol destination port matches one of these, the
1043+
x-forwarded-proto header is set to "https".
1044+
Common values: 443, 8443.
1045+
items:
1046+
format: int32
1047+
type: integer
1048+
x-kubernetes-validations:
1049+
- message: port must be in the range [1, 65535]
1050+
rule: self >= 1 && self <= 65535
1051+
maxItems: 16
1052+
minItems: 1
1053+
type: array
1054+
type: object
1055+
x-kubernetes-validations:
1056+
- message: at least one of httpsDestinationPorts or httpDestinationPorts
1057+
must be set
1058+
rule: '(has(self.httpsDestinationPorts) ? self.httpsDestinationPorts.size()
1059+
: 0) + (has(self.httpDestinationPorts) ? self.httpDestinationPorts.size()
1060+
: 0) >= 1'
10151061
optional:
10161062
description: |-
10171063
Optional allows requests without a Proxy Protocol header to be proxied.

charts/gateway-helm/charts/crds/crds/generated/gateway.envoyproxy.io_clienttrafficpolicies.yaml

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1011,6 +1011,52 @@ spec:
10111011
If both EnableProxyProtocol and ProxyProtocol are set, ProxyProtocol takes precedence.
10121012
minProperties: 0
10131013
properties:
1014+
forwardProtoConfig:
1015+
description: |-
1016+
ForwardProtoConfig infers the x-forwarded-proto header from the PROXY protocol
1017+
destination port. Only takes effect when PROXY protocol is enabled.
1018+
1019+
This is useful when a Layer 4 load balancer (such as AWS NLB) terminates TLS and
1020+
forwards traffic to Envoy over PROXY protocol, so that Envoy can set the correct
1021+
scheme for the forwarded request.
1022+
properties:
1023+
httpDestinationPorts:
1024+
description: |-
1025+
HTTPDestinationPorts are the destination ports treated as HTTP.
1026+
When the PROXY protocol destination port matches one of these, the
1027+
x-forwarded-proto header is set to "http".
1028+
Common values: 80, 8080.
1029+
items:
1030+
format: int32
1031+
type: integer
1032+
x-kubernetes-validations:
1033+
- message: port must be in the range [1, 65535]
1034+
rule: self >= 1 && self <= 65535
1035+
maxItems: 16
1036+
minItems: 1
1037+
type: array
1038+
httpsDestinationPorts:
1039+
description: |-
1040+
HTTPSDestinationPorts are the destination ports treated as HTTPS.
1041+
When the PROXY protocol destination port matches one of these, the
1042+
x-forwarded-proto header is set to "https".
1043+
Common values: 443, 8443.
1044+
items:
1045+
format: int32
1046+
type: integer
1047+
x-kubernetes-validations:
1048+
- message: port must be in the range [1, 65535]
1049+
rule: self >= 1 && self <= 65535
1050+
maxItems: 16
1051+
minItems: 1
1052+
type: array
1053+
type: object
1054+
x-kubernetes-validations:
1055+
- message: at least one of httpsDestinationPorts or httpDestinationPorts
1056+
must be set
1057+
rule: '(has(self.httpsDestinationPorts) ? self.httpsDestinationPorts.size()
1058+
: 0) + (has(self.httpDestinationPorts) ? self.httpDestinationPorts.size()
1059+
: 0) >= 1'
10141060
optional:
10151061
description: |-
10161062
Optional allows requests without a Proxy Protocol header to be proxied.

internal/gatewayapi/clienttrafficpolicy.go

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -667,7 +667,8 @@ func (t *Translator) translateClientTrafficPolicyForListener(
667667
// ProxyProtocol field takes precedence when configured
668668
// Even if it's an empty object {}, we should enable proxy protocol with default settings
669669
proxyProtocol = &ir.ProxyProtocolSettings{
670-
Optional: ptr.Deref(policy.Spec.ProxyProtocol.Optional, false),
670+
Optional: ptr.Deref(policy.Spec.ProxyProtocol.Optional, false),
671+
ForwardProtoConfig: buildIRForwardProtoConfig(policy.Spec.ProxyProtocol.ForwardProtoConfig),
671672
}
672673
} else if ptr.Deref(policy.Spec.EnableProxyProtocol, false) {
673674
// Fallback to legacy EnableProxyProtocol field
@@ -796,6 +797,29 @@ func (t *Translator) translateClientTrafficPolicyForListener(
796797
return nil
797798
}
798799

800+
// buildIRForwardProtoConfig translates the ProxyProtocol ForwardProtoConfig API type into the IR
801+
// type, converting the int32 port lists to uint32 to match the Envoy proto field types.
802+
func buildIRForwardProtoConfig(cfg *egv1a1.ForwardProtoConfig) *ir.ForwardProtoConfig {
803+
// Return early if not set.
804+
if cfg == nil {
805+
return nil
806+
}
807+
808+
irCfg := &ir.ForwardProtoConfig{}
809+
810+
irCfg.HTTPSDestinationPorts = make([]uint32, len(cfg.HTTPSDestinationPorts))
811+
for i, p := range cfg.HTTPSDestinationPorts {
812+
irCfg.HTTPSDestinationPorts[i] = uint32(p)
813+
}
814+
815+
irCfg.HTTPDestinationPorts = make([]uint32, len(cfg.HTTPDestinationPorts))
816+
for i, p := range cfg.HTTPDestinationPorts {
817+
irCfg.HTTPDestinationPorts[i] = uint32(p)
818+
}
819+
820+
return irCfg
821+
}
822+
799823
func buildKeepAlive(tcpKeepAlive *egv1a1.TCPKeepalive) (*ir.TCPKeepalive, error) {
800824
// Return early if not set
801825
if tcpKeepAlive == nil {
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
clientTrafficPolicies:
2+
- apiVersion: gateway.envoyproxy.io/v1alpha1
3+
kind: ClientTrafficPolicy
4+
metadata:
5+
namespace: envoy-gateway
6+
name: target-gateway-1-section-http-1
7+
spec:
8+
proxyProtocol:
9+
optional: true
10+
forwardProtoConfig:
11+
httpsDestinationPorts:
12+
- 443
13+
- 8443
14+
httpDestinationPorts:
15+
- 80
16+
- 8080
17+
targetRef:
18+
group: gateway.networking.k8s.io
19+
kind: Gateway
20+
name: gateway-1
21+
sectionName: http-1
22+
gateways:
23+
- apiVersion: gateway.networking.k8s.io/v1
24+
kind: Gateway
25+
metadata:
26+
namespace: envoy-gateway
27+
name: gateway-1
28+
spec:
29+
gatewayClassName: envoy-gateway-class
30+
listeners:
31+
- name: http-1
32+
protocol: HTTP
33+
port: 80
34+
allowedRoutes:
35+
namespaces:
36+
from: Same
37+
- name: http-2
38+
protocol: HTTP
39+
port: 8080
40+
allowedRoutes:
41+
namespaces:
42+
from: Same

0 commit comments

Comments
 (0)