-
Notifications
You must be signed in to change notification settings - Fork 17
Fix: ACME directory URL redirects are not followed #77
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
|
Did not have time to take a good look at this; some general comments:
Something I would want to see here is a simple loop in pub async fn get(&self, url: &Uri) -> Result<http::Response<Bytes>, RequestError> {
let mut u = url.clone();
for _ in 0..MAX_REDIRECTS {
let req = ...;
let res = self.http.request(req).await?;
if res.status().is_redirection() {
u = ...;
continue;
}
return Ok(res);
}
Err(...)
} |
|
Thanks for your input. Your suggestion makes a lot of sense, compared to my previously overengineered solution. I have added some new error options, some input if that works for you, or if I should rather compress them into existing ones would be appreciated. |
src/acme.rs
Outdated
|
|
||
| if res.status().is_redirection() { | ||
| if let Some(location) = try_get_header(res.headers(), http::header::LOCATION) { | ||
| u = Uri::try_from(location).map_err(RequestError::UrlParse)?; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's still necessary to handle relative URIs in the Location header.
I don't like the idea of reimplementing RFC3986 § 5 reference resolution algorithm (resolve_location in the previous revision didn't look quite right), so I suggest to use the same dependency as reqwest and tower-http do, iri-string. Here's how it may look: https://docs.rs/tower-http/0.6.6/src/tower_http/follow_redirect/mod.rs.html#394.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have adjusted the code to match your suggestion.
I also split out the error logic to a custom RedirectError, similar to NewAccountError, as the potential issues kind of started to pollute RequestError.
In addition I could also extend it into the Problem and extend some logic there:
impl From<RequestError> for NewAccountError {
fn from(value: RequestError) -> Self {
match value {
RequestError::Protocol(problem) => Self::Protocol(problem),
_ => Self::Request(value),
}
}
}Line 326 in d8d7a08
| impl Problem { |
Not sure what you'd prefer here though?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you mind extracting URI resolution code into a function? I suspect we may need it elsewhere in the future, because RFC8555 does not require any of the directory or resource location URIs to be absolute.
I also don't believe we need that many errors:
UriAbsoluteString::try_from()should not fail, becauseself.http.request()already fails on invalid URI. Still need to check the result, but don't expect anything useful.UriReferenceStr::new()returns a rather useless error "Invalid IRI".Uri::try_from()should succeed, because it always receives a valid absolute URI.
With that in mind, fn resolve_uri(base, relative) -> Option<Uri> is a reasonable interface, and the None can be later converted to a single InvalidUri.
"invalid redirect URI"/"missing redirect URI"/"too many redirects" is a sufficient set of errors to handle this, and it will look acceptable both as a separate RedirectError or as members of RequestError. Just ensure that you maintain the sorting order of the enum members.
In addition I could also extend it into the Problem and extend some logic there:
Problem is reserved for ACME protocol errors received from the server. Redirect errors don't belong there.
The conversion you quoted exists to simplify handling Problems that point to a permanent configuration error, i.e. cases when we should stop attempting to process the current item. Also, to make logs prettier.
Partial fix for #75
Technically this PR fixes the general redirect aspects for the GET call in
acme.rs.As of now the POST from
acme.rswould still not be covered:https://github.com/nginx/nginx-acme/blob/main/src/acme.rs#L181
Based on my testing this should only affect use-cases where URLs (relative, or absolute) are returned, that cause another redirect, which should be rarely the case (at least not in the environments I tested with). Reason being, that the URLs provided by
https://acme.example.com/directoryshould normally be OK not requiring redirects.Also I'm not completely sure how to best implement the requirements for
RFC 8555 §6.2.I can add those as well if needed, though it might be best to extract part of the redirect functionality to it's own (reusable) unit I assume.
Any opinions on this matter?
IMO this basic PR should already cover a few additional standard use cases, such as:
https://acme.example.com/directory->https://acme.example.com/some-id/directory(from ACME directory URL redirects are not followed #75)