feat(api): add ClientTrafficPolicy.clientIPDetection.downstreamRemoteAddress for L4-transparent geoip#8956
Conversation
Today, SecurityPolicy authorization.rules[].principal.clientIPGeoLocations requires ClientTrafficPolicy.spec.clientIPDetection to be set. This makes GeoIP authorization unreachable in L4-transparent topologies where the downstream TCP peer IS the real client IP (e.g. AWS NLB with target-type=instance + externalTrafficPolicy=Local, Azure Standard LB), because there is no XFF/custom header to trust. Envoy's HTTP geoip filter (envoy.extensions.filters.http.geoip.v3.Geoip) supports three IP-source modes: xff_config, custom_header_config, and "neither set" -- in which case the filter uses "the immediate downstream connection source address" (per the proto). EG already exposes the first two modes; this PR exposes the third. Changes: * internal/gatewayapi/securitypolicy.go: validateAuthorizationGeoIP no longer rejects nil clientIPDetection. The TrustedCIDRs check is kept and guarded. * internal/xds/translator/geoip.go: comment-only update; the existing code already gated xff_config/custom_header_config on a non-nil ClientIPDetection, so no behavioral change is needed there. * Tests: unit + golden coverage for the nil-clientIPDetection case (gatewayapi translator and xDS translator). The xDS golden confirms the geoip filter is emitted with neither xff_config nor custom_header_config, and that no original_ip_detection_extensions are added to the HCM. * Docs and release notes updated. Fixes envoyproxy#8955 Signed-off-by: Salim Boulkour <salim.boulkour@algolia.com>
✅ Deploy Preview for cerulean-figolla-1f9435 ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
Per maintainer feedback on envoyproxy#8955, replace the implicit "nil clientIPDetection ⇒ use downstream remote address" behavior introduced in the previous commit with an explicit new field: ClientTrafficPolicy.spec.clientIPDetection.downstreamRemoteAddress. Triggering the new behavior on absence of clientIPDetection was a fail-open footgun: an operator forgetting to set XFF behind a NATing proxy would silently trust the wrong IP for geolocation. The explicit field makes the operator's intent unambiguous. API: * Add DownstreamRemoteAddressSettings (empty struct, room to grow) and the DownstreamRemoteAddress field on ClientIPDetectionSettings. * Update the CEL XValidation rule to enforce mutual exclusion across all three siblings (xForwardedFor, customHeader, downstreamRemoteAddress). Validator: * Restore the rejection of nil clientIPDetection in validateAuthorizationGeoIP. The new mode requires an explicit non-nil ClientIPDetection with DownstreamRemoteAddress set. Translator: * Add an explicit switch arm in buildHCMGeoIPFilter that emits the geoip filter with neither xff_config nor custom_header_config when DownstreamRemoteAddress is set, so Envoy uses its documented default (the immediate downstream connection source address). HCM-side wiring needs no change: useRemoteAddress remains true since no original_ip_detection_extensions are produced. Tests: * Restore the gatewayapi golden testdata for the rejection case (byte-identical to upstream/main). * Add positive gatewayapi + xDS golden coverage for the new mode. * Update CEL validation tests for the new mutual-exclusion message and add positive/negative cases for downstreamRemoteAddress. Docs and release notes updated to describe the explicit field. Signed-off-by: Salim Boulkour <salim.boulkour@algolia.com>
…moteAddress field These golden files render the ClientTrafficPolicy CRD inside the helm chart and need to reflect the new downstreamRemoteAddress field plus the updated CEL mutual-exclusion message. Caught by 'make gen-check'. Signed-off-by: Salim Boulkour <salim.boulkour@algolia.com>
Tighten ClientTrafficPolicy.clientIPDetection validation from "at most
one" to "exactly one" of {xForwardedFor, customHeader,
downstreamRemoteAddress}. This rejects an empty object (`{}`) and
aligns behavior with the explicit opt-in requirement for GeoIP source
selection.
Also add a defensive runtime validation in authorization GeoIP
translation so stale resources (or bypassed admission validation) are
rejected unless exactly one mode is configured.
Tests updated:
* gatewayapi unit tests: reject empty and multi-mode clientIPDetection.
* CEL validation tests: updated error message and added empty-object
rejection case.
* regenerated CRD/docs/helm golden outputs for the new CEL rule and
message.
Signed-off-by: Salim Boulkour <salim.boulkour@algolia.com>
|
Follow-up pushed in
|
|
@zhaohuabing could you please enable CI for this first-time contributor PR and review when you have a moment? I updated the PR to the explicit opt-in design with strict one-of clientIPDetection ( Thank you! |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #8956 +/- ##
==========================================
+ Coverage 74.73% 74.75% +0.02%
==========================================
Files 251 251
Lines 40394 40404 +10
==========================================
+ Hits 30188 30205 +17
+ Misses 8138 8132 -6
+ Partials 2068 2067 -1 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
|
@zhaohuabing thx for the CI run :) About the failed test, seems like the the gateway didn't get an IP within the 3min deadline. |
|
/retest |
Per reviewer feedback, DownstreamRemoteAddress is too Envoy-internal. DirectRemoteAddress better describes the user-facing intent: use the direct TCP peer address as the client IP. Also add breaking change release note entry for the strict one-of CEL validation on clientIPDetection. Signed-off-by: Salim Boulkour <salim.boulkour@algolia.com>
bedc422 to
c59c988
Compare
Signed-off-by: Salim Boulkour <salim.boulkour@algolia.com>
|
@zhaohuabing ok pushed required changes. ready for a new CI run :) |
…dress Signed-off-by: Salim Boulkour <salim.boulkour@algolia.com>
The sed-based rename accidentally changed downstreamRemoteAddressWithoutPortCelFormatter in ratelimit.go, which is a rate-limiting internal constant unrelated to the directRemoteAddress GeoIP feature. Revert to original name. Signed-off-by: Salim Boulkour <salim.boulkour@algolia.com>
|
|
||
| # Changes that are expected to cause an incompatibility with previous versions, such as deletions or modifications to existing APIs. | ||
| breaking changes: | | ||
| `ClientTrafficPolicy.spec.clientIPDetection` now requires exactly one of `xForwardedFor`, `customHeader`, or `directRemoteAddress` to be set. Previously an empty `clientIPDetection: {}` was accepted; it is now rejected by CEL validation. |
There was a problem hiding this comment.
This is an API-breaking change, but I think it should be acceptable since an empty clientIPDetection is not meaningful and is unlikely to be used in existing setups. cc @envoyproxy/gateway-maintainers
What type of PR is this?
feat
What this PR does / why we need it:
Adds
ClientTrafficPolicy.spec.clientIPDetection.downstreamRemoteAddressas an explicit third client-IP source for GeoIP authorization.When this field is set, Envoy Gateway emits the GeoIP filter without
xff_configand withoutcustom_header_config, so Envoy uses the immediate downstream connection source address.This enables
SecurityPolicy.authorization.rules[].principal.clientIPGeoLocationsin L4-transparent topologies (for example AWS NLBtarget-type: instance+externalTrafficPolicy: Local, Azure Standard Load Balancer).Per maintainer feedback on #8955, behavior is explicit opt-in (new field), not implicit fallback from missing
clientIPDetection.Changes
DownstreamRemoteAddressSettingsandclientIPDetection.downstreamRemoteAddress.xForwardedFor/customHeader/downstreamRemoteAddress) via CELsize() == 1.clientIPDetectionfor GeoIP auth and add a defensive runtime exactly-one check.DownstreamRemoteAddressbranch in GeoIP filter generation.Which issue(s) this PR fixes:
Fixes #8955
Release Notes: Yes