oauth2: add allowed_redirect_domains and original_request_uri#44282
oauth2: add allowed_redirect_domains and original_request_uri#44282MohammedShetaya wants to merge 1 commit intoenvoyproxy:mainfrom
Conversation
|
Hi @MohammedShetaya, welcome and thank you for your contribution. We will try to review your Pull Request as quickly as possible. In the meantime, please take a look at the contribution guidelines if you have not done so already. |
|
CC @envoyproxy/api-shepherds: Your approval is needed for changes made to |
|
@MohammedShetaya needs main merge @wbpcode gentle ping |
wbpcode
left a comment
There was a problem hiding this comment.
thanks for your great contribution. Sounds reasonable feature to me. One comment added to API.
| // When set to true, the host and scheme encoded in the OAuth2 state parameter | ||
| // will be derived from the configured redirect_uri instead of from the | ||
| // request's :authority and :scheme headers. This is useful when Envoy is | ||
| // deployed behind an external load balancer whose hostname differs from | ||
| // Envoy's internal hostname. | ||
| // Default is false. | ||
| bool match_redirect_url_to_redirect_uri = 28; |
There was a problem hiding this comment.
I see your requirements now. But I will preferred to add an optional original_uri configuration directly which would be much easier to understand the feature.
Although it brings minor additional overhead. But for oauth2, the performance, when redirect happens, is not that important.
There was a problem hiding this comment.
@wbpcode Thanks!
I applied the first approach because I didn't want to have a drift between the two values, but they service different purposes anyway. I believe it might cause some confusion, so I updated the code to implment original_request_uri
|
/wait |
0e603e6 to
f600983
Compare
wbpcode
left a comment
There was a problem hiding this comment.
LGTM to me now. Could you also add a change log for this new feature and also update the PR title.
I will inclined to merge this after the release of 1.38
f600983 to
f483264
Compare
f483264 to
5d54095
Compare
Adds two new fields to the OAuth2 filter config: * `original_request_uri`: an optional formatter-supported base URI used to build the original request URL that is encoded into the OAuth2 `state` parameter. Useful when Envoy sits behind a gateway that terminates the user-facing hostname, so the post-authentication redirect uses the public host rather than Envoy's internal `:authority`. * `allowed_redirect_domains`: an optional case-insensitive allow-list (exact match or `*.` wildcard) applied to the host of the formatted `redirect_uri`, the formatted `original_request_uri`, and the URL decoded from the `state` parameter on callback. Mitigates open-redirect attacks via injected `x-forwarded-host` headers or forged `state` values. Empty list (default) disables the check for backward compatibility. The formatted `redirect_uri` and `original_request_uri` are now also required to be parseable absolute URLs: an unparseable template output is rejected with 401 rather than silently passing through the allow-list. Authority parsing uses `Http::Utility::parseAuthority` so IPv6 literals are handled consistently. Signed-off-by: Mohammed Shetaya <mohammed.shetaya@procore.com>
decebe7 to
7bcd97e
Compare
|
Sorry for the noise. It automatically requested a review from almost everyone after a dirty commit. |
wbpcode
left a comment
There was a problem hiding this comment.
Thanks so much for the update/contribution. The 1.38 was out and I only have one last comment. Thanks for update and waiting. 🙇
/wait
| Http::Utility::Url parsed_redirect_uri; | ||
| if (!parsed_redirect_uri.initialize(redirect_uri, false)) { | ||
| sendUnauthorizedResponse(fmt::format("redirect_uri is not a valid URL: {}", redirect_uri)); | ||
| return; | ||
| } |
There was a problem hiding this comment.
From code's perspective, this is potential behavior change. Could you move this uri validation to the isHostAllowedDomain and only validate it when the allowed_redirect_domains is not empty? Then this how PR won't bring any un-guarded behavior change. Thanks.
There was a problem hiding this comment.
And you may could keep the following code in the previous position to make reviewing easier.
// Format redirect_uri — needed for the query param sent to the identity provider
auto formatter = THROW_OR_RETURN_VALUE(Formatter::FormatterImpl::create(config_->redirectUri()),
Formatter::FormatterPtr);
const auto redirect_uri = formatter->format({&headers}, decoder_callbacks_->streamInfo());
When Envoy sits behind an external load balancer, the internal :authority and :scheme headers don't match the externally-visible hostname. The redirect_uri can already be templated with formatters to handle this, but the URL encoded in the OAuth2 state parameter is always derived from the request's :authority/:scheme — causing a mismatch that breaks the post-login redirect.
This PR adds two new config fields:
original_request_uri: an optional formatter-supported base URI (scheme + host)used to build the original request URL encoded into the OAuth2 state parameter.
Operators can template it from
x-forwarded-host/x-forwarded-protoso thepost-auth redirect uses the public host rather than Envoy's internal
:authority.allowed_redirect_domains: a case-insensitive allowlist (exact or*.wildcard)applied to the host of the formatted
redirect_uri, the formattedoriginal_request_uri, and the URL decoded from thestateparameter on callback.Mitigates open-redirect attacks via injected
x-forwarded-hostheaders or forgedstatevalues. Empty list (default) disables the check for backward compatibility.Risk Level: Low
Testing: Unit tests added for the allowlist (exact, wildcard, empty-list no-op), forwarded-header-driven redirect and state, and rejection on mismatch and on unparseable URLs.
Docs Changes: Proto field documentation added inline.
Release Notes: Added
original_request_uriandallowed_redirect_domainsoptions to the OAuth2 filter to support deployments behind external load balancers and prevent open redirects via the state parameter.Platform Specific Features: N/A
[Optional Fixes #41034]
[Optional API Considerations: Two new fields added to OAuth2Config (field numbers 28 and 29). No breaking changes to existing configs.]