letsencrypt / boulder

An ACME-based certificate authority, written in Go.
Mozilla Public License 2.0
5.14k stars 601 forks source link

Querying old/purged authorizations does not always return RFC7807 formatted errors. #4540

Closed rmbolger closed 4 years ago

rmbolger commented 4 years ago

From RFC8555 section 6.7 :

When the server responds with an error status, it SHOULD provide additional information using a problem document [RFC7807].

I was testing some new functionality in my client and ran into some cases where old authorization objects were returning non-formatted 404 errors.

Specifically, https://acme-staging-v02.api.letsencrypt.org/acme/authz-v3/8313838 returns:

{
  "type": "urn:ietf:params:acme:error:malformed",
  "detail": "Expired authorization",
  "status": 404
}

While https://acme-staging-v02.api.letsencrypt.org/acme/authz/NBOv1Fly24bHm6SXztdbFs-3FFxX6UfUeXa1Kk-eREM returns:

404 page not found

I would expect a generic 404 to be returned as something like this:

{
  "type": "urn:ietf:params:acme:error:malformed",
  "detail": "Page not found",
  "status": 404
}
rolandshoemaker commented 4 years ago

/acme/authz is no longer a valid resource, there is nothing served under that path anymore, hence why a non-7807 error is returned, it's the same thing you'd get if you requested /acme/this-is-not-a-thing.

rmbolger commented 4 years ago

I figured that might be the case.

Would it be difficult to add a catch-all route that returns a 7807 parseable 404 result rather than the plain text one? Or at least routes for previously valid endpoints?

It’s not the end of the world if I have to add additional error handling logic for this case. But it would be nice if I didn’t have to. 😬

jsha commented 4 years ago

@rmbolger can you tell us more about your use case? The authorization objects contain an expiration date, so ideally your client could keep track of that and avoid querying authorizations that are known to be expired.

rmbolger commented 4 years ago

Currently my client doesn't store the authorization objects. It only stores the order objects which have the authorization URLs. So I can't check the expiration without re-querying the authorization object.

This came up when I was adding the ability to revoke authorizations. The idea is that you can pass one or more order objects to the function (or just a list of auth URLs) and it will attempt to revoke any that are still valid. But it tries to query their status first to avoid sending a revoke request for something that's already invalid/expired and provide additional result context.

The error handling is in a lower level function that handles the generic ACME message signing and previously it just assumed all non-200 responses would be 7807 parseable. I've since added additional error handling for 404 responses that will generate a synthetic 7807 response to pass up the stack.

Again, it's not a huge deal with the additional error handling in place. It just seemed odd at the time that a resource URL that was previously valid wouldn't still return a 7807 parseable result. Querying a URL that was previously valid seems different than querying a URL that has never been valid.

jsha commented 4 years ago

That's useful context, thanks for the additional info! I'm going to consider this a "won't fix" for now, but we may reconsider in the future.