From 067756fbe6c7e2de53a1c2d76d90c0535d53b4c5 Mon Sep 17 00:00:00 2001 From: Rudrakh Panigrahi Date: Fri, 25 Oct 2024 12:00:49 +0530 Subject: [PATCH] support setting trusted CIDRs Signed-off-by: Rudrakh Panigrahi --- api/v1alpha1/clienttrafficpolicy_types.go | 19 ++++- api/v1alpha1/zz_generated.deepcopy.go | 5 ++ ...y.envoyproxy.io_clienttrafficpolicies.yaml | 25 +++++- release-notes/current.yaml | 2 +- site/content/en/latest/api/extension_types.md | 5 +- site/content/zh/latest/api/extension_types.md | 5 +- .../clienttrafficpolicy_test.go | 83 +++++++++++++++++++ 7 files changed, 137 insertions(+), 7 deletions(-) diff --git a/api/v1alpha1/clienttrafficpolicy_types.go b/api/v1alpha1/clienttrafficpolicy_types.go index 63b2c91fb2e..6c7129da060 100644 --- a/api/v1alpha1/clienttrafficpolicy_types.go +++ b/api/v1alpha1/clienttrafficpolicy_types.go @@ -237,14 +237,29 @@ type ClientIPDetectionSettings struct { } // XForwardedForSettings provides configuration for using X-Forwarded-For headers for determining the client IP address. +// Refer to https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_conn_man/headers#x-forwarded-for +// for more details. +// +kubebuilder:validation:XValidation:rule="(has(self.numTrustedHops) && !has(self.trustedCIDRs)) || (!has(self.numTrustedHops) && has(self.trustedCIDRs))", message="only one of numTrustedHops or trustedCIDRs must be set" type XForwardedForSettings struct { // NumTrustedHops controls the number of additional ingress proxy hops from the right side of XFF HTTP // headers to trust when determining the origin client's IP address. - // Refer to https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_conn_man/headers#x-forwarded-for - // for more details. + // Only one of NumTrustedHops and TrustedCIDRs must be set. // // +optional NumTrustedHops *uint32 `json:"numTrustedHops,omitempty"` + + // TrustedCIDRs is a list of CIDR ranges to trust when evaluating + // the remote IP address to determine the original client’s IP address. + // When the remote IP address matches a trusted CIDR and the x-forwarded-for header was sent, + // each entry in the x-forwarded-for header is evaluated from right to left + // and the first public non-trusted address is used as the original client address. + // If all addresses in x-forwarded-for are within the trusted list, the first (leftmost) entry is used. + // Only one of NumTrustedHops and TrustedCIDRs must be set. + // + // +optional + // +kubebuilder:validation:MinItems=1 + // +notImplementedHide + TrustedCIDRs []CIDR `json:"trustedCIDRs,omitempty"` } // CustomHeaderExtensionSettings provides configuration for determining the client IP address for a request based on diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index 321a143df9c..3368e73dd70 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -5579,6 +5579,11 @@ func (in *XForwardedForSettings) DeepCopyInto(out *XForwardedForSettings) { *out = new(uint32) **out = **in } + if in.TrustedCIDRs != nil { + in, out := &in.TrustedCIDRs, &out.TrustedCIDRs + *out = make([]CIDR, len(*in)) + copy(*out, *in) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new XForwardedForSettings. 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 3e626f3f88a..ad17b8101d2 100644 --- a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_clienttrafficpolicies.yaml +++ b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_clienttrafficpolicies.yaml @@ -85,11 +85,32 @@ spec: description: |- NumTrustedHops controls the number of additional ingress proxy hops from the right side of XFF HTTP headers to trust when determining the origin client's IP address. - Refer to https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_conn_man/headers#x-forwarded-for - for more details. + Only one of NumTrustedHops and TrustedCIDRs must be set. format: int32 type: integer + trustedCIDRs: + description: |- + TrustedCIDRs is a list of CIDR ranges to trust when evaluating + the remote IP address to determine the original client’s IP address. + When the remote IP address matches a trusted CIDR and the x-forwarded-for header was sent, + each entry in the x-forwarded-for header is evaluated from right to left + and the first public non-trusted address is used as the original client address. + If all addresses in x-forwarded-for are within the trusted list, the first (leftmost) entry is used. + Only one of NumTrustedHops and TrustedCIDRs must be set. + items: + description: |- + CIDR defines a CIDR Address range. + A CIDR can be an IPv4 address range such as "192.168.1.0/24" or an IPv6 address range such as "2001:0db8:11a3:09d7::/64". + pattern: ((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\/([0-9]+))|((([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))\/([0-9]+)) + type: string + minItems: 1 + type: array type: object + x-kubernetes-validations: + - message: only one of numTrustedHops or trustedCIDRs must be + set + rule: (has(self.numTrustedHops) && !has(self.trustedCIDRs)) + || (!has(self.numTrustedHops) && has(self.trustedCIDRs)) type: object x-kubernetes-validations: - message: customHeader cannot be used in conjunction with xForwardedFor diff --git a/release-notes/current.yaml b/release-notes/current.yaml index cf4ea00691b..3ca1cd67d5b 100644 --- a/release-notes/current.yaml +++ b/release-notes/current.yaml @@ -10,7 +10,7 @@ security updates: | # New features or capabilities added in this release. new features: | - Add a new feature here + - Added support for trusted CIDRs in the ClientIPDetectionSettings API # Fixes for bugs identified in previous versions. bug fixes: | diff --git a/site/content/en/latest/api/extension_types.md b/site/content/en/latest/api/extension_types.md index 2999d46410c..511b707de74 100644 --- a/site/content/en/latest/api/extension_types.md +++ b/site/content/en/latest/api/extension_types.md @@ -525,6 +525,7 @@ A CIDR can be an IPv4 address range such as "192.168.1.0/24" or an IPv6 address _Appears in:_ - [Principal](#principal) +- [XForwardedForSettings](#xforwardedforsettings) @@ -4267,13 +4268,15 @@ _Appears in:_ XForwardedForSettings provides configuration for using X-Forwarded-For headers for determining the client IP address. +Refer to https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_conn_man/headers#x-forwarded-for +for more details. _Appears in:_ - [ClientIPDetectionSettings](#clientipdetectionsettings) | Field | Type | Required | Description | | --- | --- | --- | --- | -| `numTrustedHops` | _integer_ | false | NumTrustedHops controls the number of additional ingress proxy hops from the right side of XFF HTTP
headers to trust when determining the origin client's IP address.
Refer to https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_conn_man/headers#x-forwarded-for
for more details. | +| `numTrustedHops` | _integer_ | false | NumTrustedHops controls the number of additional ingress proxy hops from the right side of XFF HTTP
headers to trust when determining the origin client's IP address.
Only one of NumTrustedHops and TrustedCIDRs must be set. | #### ZipkinTracingProvider diff --git a/site/content/zh/latest/api/extension_types.md b/site/content/zh/latest/api/extension_types.md index 2999d46410c..511b707de74 100644 --- a/site/content/zh/latest/api/extension_types.md +++ b/site/content/zh/latest/api/extension_types.md @@ -525,6 +525,7 @@ A CIDR can be an IPv4 address range such as "192.168.1.0/24" or an IPv6 address _Appears in:_ - [Principal](#principal) +- [XForwardedForSettings](#xforwardedforsettings) @@ -4267,13 +4268,15 @@ _Appears in:_ XForwardedForSettings provides configuration for using X-Forwarded-For headers for determining the client IP address. +Refer to https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_conn_man/headers#x-forwarded-for +for more details. _Appears in:_ - [ClientIPDetectionSettings](#clientipdetectionsettings) | Field | Type | Required | Description | | --- | --- | --- | --- | -| `numTrustedHops` | _integer_ | false | NumTrustedHops controls the number of additional ingress proxy hops from the right side of XFF HTTP
headers to trust when determining the origin client's IP address.
Refer to https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_conn_man/headers#x-forwarded-for
for more details. | +| `numTrustedHops` | _integer_ | false | NumTrustedHops controls the number of additional ingress proxy hops from the right side of XFF HTTP
headers to trust when determining the origin client's IP address.
Only one of NumTrustedHops and TrustedCIDRs must be set. | #### ZipkinTracingProvider diff --git a/test/cel-validation/clienttrafficpolicy_test.go b/test/cel-validation/clienttrafficpolicy_test.go index 3558d1848f9..942afea1646 100644 --- a/test/cel-validation/clienttrafficpolicy_test.go +++ b/test/cel-validation/clienttrafficpolicy_test.go @@ -221,6 +221,89 @@ func TestClientTrafficPolicyTarget(t *testing.T) { "spec.clientIPDetection: Invalid value: \"object\": customHeader cannot be used in conjunction with xForwardedFor", }, }, + { + desc: "clientIPDetection numTrustedHops and trustedCIDRs", + mutate: func(ctp *egv1a1.ClientTrafficPolicy) { + ctp.Spec = egv1a1.ClientTrafficPolicySpec{ + PolicyTargetReferences: egv1a1.PolicyTargetReferences{ + TargetRef: &gwapiv1a2.LocalPolicyTargetReferenceWithSectionName{ + LocalPolicyTargetReference: gwapiv1a2.LocalPolicyTargetReference{ + Group: gwapiv1a2.Group("gateway.networking.k8s.io"), + Kind: gwapiv1a2.Kind("Gateway"), + Name: gwapiv1a2.ObjectName("eg"), + }, + }, + }, + ClientIPDetection: &egv1a1.ClientIPDetectionSettings{ + XForwardedFor: &egv1a1.XForwardedForSettings{ + NumTrustedHops: ptr.To(uint32(1)), + TrustedCIDRs: []egv1a1.CIDR{ + "192.168.1.0/24", + "10.0.0.0/16", + "172.16.0.0/12", + }, + }, + }, + } + }, + wantErrors: []string{ + "spec.clientIPDetection.xForwardedFor: Invalid value: \"object\": only one of numTrustedHops or trustedCIDRs must be set", + }, + }, + { + desc: "clientIPDetection invalid trustedCIDRs", + mutate: func(ctp *egv1a1.ClientTrafficPolicy) { + ctp.Spec = egv1a1.ClientTrafficPolicySpec{ + PolicyTargetReferences: egv1a1.PolicyTargetReferences{ + TargetRef: &gwapiv1a2.LocalPolicyTargetReferenceWithSectionName{ + LocalPolicyTargetReference: gwapiv1a2.LocalPolicyTargetReference{ + Group: gwapiv1a2.Group("gateway.networking.k8s.io"), + Kind: gwapiv1a2.Kind("Gateway"), + Name: gwapiv1a2.ObjectName("eg"), + }, + }, + }, + ClientIPDetection: &egv1a1.ClientIPDetectionSettings{ + XForwardedFor: &egv1a1.XForwardedForSettings{ + TrustedCIDRs: []egv1a1.CIDR{ + "192.0124.1.0/24", + "10.0.0.0/1645", + "17212.16.0.0/123", + }, + }, + }, + } + }, + wantErrors: []string{ + "spec.clientIPDetection.xForwardedFor.trustedCIDRs[0]: Invalid value: \"192.0124.1.0/24\": spec.clientIPDetection.xForwardedFor.trustedCIDRs[0] in body should match '((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\/([0-9]+))|((([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))\\/([0-9]+))'", + }, + }, + { + desc: "clientIPDetection valid trustedCIDRs", + mutate: func(ctp *egv1a1.ClientTrafficPolicy) { + ctp.Spec = egv1a1.ClientTrafficPolicySpec{ + PolicyTargetReferences: egv1a1.PolicyTargetReferences{ + TargetRef: &gwapiv1a2.LocalPolicyTargetReferenceWithSectionName{ + LocalPolicyTargetReference: gwapiv1a2.LocalPolicyTargetReference{ + Group: gwapiv1a2.Group("gateway.networking.k8s.io"), + Kind: gwapiv1a2.Kind("Gateway"), + Name: gwapiv1a2.ObjectName("eg"), + }, + }, + }, + ClientIPDetection: &egv1a1.ClientIPDetectionSettings{ + XForwardedFor: &egv1a1.XForwardedForSettings{ + TrustedCIDRs: []egv1a1.CIDR{ + "192.168.1.0/24", + "10.0.0.0/16", + "172.16.0.0/12", + }, + }, + }, + } + }, + wantErrors: []string{}, + }, { desc: "http3 enabled and ALPN protocols not set with other TLS parameters set", mutate: func(ctp *egv1a1.ClientTrafficPolicy) {