va: add specific error for broken HTTP-01 redirects. (#4171)

Often folks will mis-configure their webserver to send an HTTP redirect missing
a `/' between the FQDN and the path.

E.g. in Apache using:

Redirect / https://bad-redirect.org

Instead of:

Redirect / https://bad-redirect.org/

Will produce an invalid HTTP-01 redirect target like:

https://bad-redirect.org.well-known/acme-challenge/xxxx

This happens frequently enough we want to return a distinct error message
for this case by detecting the redirect targets ending in ".well-known".

After the "Simple HTTP-01" code landed this case was previously getting an error
message of the form:
> "Invalid hostname in redirect target, must end in IANA registered TLD"

Resolves https://github.com/letsencrypt/boulder/issues/3606
This commit is contained in:
Daniel McCarney 2019-04-23 13:50:47 -04:00 committed by Jacob Hoffman-Andrews
parent 7b49849f87
commit 2f3c703a72
3 changed files with 60 additions and 0 deletions

View File

@ -63,6 +63,43 @@ def rand_http_chall(client):
return d, c.chall
raise Exception("No HTTP-01 challenge found for random domain authz")
def test_http_challenge_broken_redirect():
"""
test_http_challenge_broken_redirect tests that a common webserver
mis-configuration receives the correct specialized error message when attempting
an HTTP-01 challenge.
"""
client = chisel2.make_client()
# Create an authz for a random domain and get its HTTP-01 challenge token
d, chall = rand_http_chall(client)
token = chall.encode("token")
# Create a broken HTTP redirect similar to a sort we see frequently "in the wild"
challengePath = "/.well-known/acme-challenge/{0}".format(token)
redirect = "http://{0}.well-known/acme-challenge/bad-bad-bad".format(d)
challSrv.add_http_redirect(
challengePath,
redirect)
# Expect the specialized error message
expectedError = "Fetching {0}: Invalid host in redirect target \"{1}.well-known\". Check webserver config for missing '/' in redirect target.".format(redirect, d)
# NOTE(@cpu): Can't use chisel2.expect_problem here because it doesn't let
# us interrogate the detail message easily.
try:
chisel2.auth_and_issue([d], client=client, chall_type="http-01")
except acme_errors.ValidationError as e:
for authzr in e.failed_authzrs:
c = chisel2.get_chall(authzr, challenges.HTTP01)
error = c.error
if error is None or error.typ != "urn:ietf:params:acme:error:connection":
raise Exception("Expected connection prob, got %s" % (error.__str__()))
if error.detail != expectedError:
raise Exception("Expected prob detail %s, got %s" % (expectedError, error.detail))
challSrv.remove_http_redirect(challengePath)
def test_http_challenge_loop_redirect():
client = chisel2.make_client()

View File

@ -314,6 +314,22 @@ func (va *ValidationAuthorityImpl) extractRequestTarget(req *http.Request) (stri
"Only domain names are supported, not IP addresses", reqHost)
}
// Often folks will misconfigure their webserver to send an HTTP redirect
// missing a `/' between the FQDN and the path. E.g. in Apache using:
// Redirect / https://bad-redirect.org
// Instead of
// Redirect / https://bad-redirect.org/
// Will produce an invalid HTTP-01 redirect target like:
// https://bad-redirect.org.well-known/acme-challenge/xxxx
// This happens frequently enough we want to return a distinct error message
// for this case by detecting the reqHost ending in ".well-known".
if strings.HasSuffix(reqHost, ".well-known") {
return "", 0, berrors.ConnectionFailureError(
"Invalid host in redirect target %q. Check webserver config for missing '/' in redirect target.",
reqHost,
)
}
if _, err := iana.ExtractSuffix(reqHost); err != nil {
return "", 0, berrors.ConnectionFailureError(
"Invalid hostname in redirect target, must end in IANA registered TLD")

View File

@ -232,6 +232,13 @@ func TestExtractRequestTarget(t *testing.T) {
},
ExpectedError: errors.New("Invalid empty hostname in redirect target"),
},
{
Name: "invalid .well-known hostname",
Req: &http.Request{
URL: mustURL(t, "https://my.webserver.is.misconfigured.well-known/acme-challenge/xxx"),
},
ExpectedError: errors.New(`Invalid host in redirect target "my.webserver.is.misconfigured.well-known". Check webserver config for missing '/' in redirect target.`),
},
{
Name: "invalid non-iana hostname",
Req: &http.Request{