def z2_disable_setup(): global z2_disable_client global z2_disable_authz global z2_disable_order z2_disable_client = chisel2.make_client() z2_disable_order = chisel2.auth_and_issue([random_domain()]) z2_disable_authz = z2_disable_order.authorizations[0]
def test_revoke_by_privkey(): client = chisel2.make_client(None) domains = [random_domain()] key = OpenSSL.crypto.PKey() key.generate_key(OpenSSL.crypto.TYPE_RSA, 2048) key_pem = OpenSSL.crypto.dump_privatekey(OpenSSL.crypto.FILETYPE_PEM, key) csr_pem = chisel2.make_csr(domains) order = client.new_order(csr_pem) cleanup = chisel2.do_http_challenges(client, order.authorizations) try: order = client.poll_and_finalize(order) finally: cleanup() # Create a new client with the JWK as the cert private key jwk = josepy.JWKRSA(key=key) net = acme_client.ClientNetwork(key, user_agent="Boulder integration tester") directory = Directory.from_json(net.get(chisel2.DIRECTORY_V2).json()) new_client = acme_client.ClientV2(directory, net) cert = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, order.fullchain_pem) reset_akamai_purges() client.revoke(josepy.ComparableX509(cert), 0) cert_file_pem = os.path.join(tempdir, "revokeme.pem") with open(cert_file_pem, "w") as f: f.write( OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_PEM, cert).decode()) ee_ocsp_url = "http://localhost:4002" verify_ocsp(cert_file_pem, "test/test-ca2.pem", ee_ocsp_url, "revoked") verify_akamai_purge()
def test_http_multiva_threshold_fail(): # Only config-next has remote VAs configured and is appropriate for this # integration test. if not CONFIG_NEXT: return client = chisel2.make_client() # Configure a guestlist that will fail the multiVA threshold test by # only allowing the primary VA. guestlist = {"boulder": 1} hostname, cleanup = multiva_setup(client, guestlist) try: chisel2.auth_and_issue([hostname], client=client, chall_type="http-01") except acme_errors.ValidationError as e: # NOTE(@cpu): Chisel2's expect_problem doesn't work in this case so this # test needs to unpack an `acme_errors.ValidationError` on its own. It # might be possible to clean this up in the future. if len(e.failed_authzrs) != 1: raise Exception("expected one failed authz, found {0}".format( len(e.failed_authzrs))) challs = e.failed_authzrs[0].body.challenges httpChall = None for chall_body in challs: if isinstance(chall_body.chall, challenges.HTTP01): httpChall = chall_body if httpChall is None: raise Exception("no HTTP-01 challenge in failed authz") if httpChall.error.typ != "urn:ietf:params:acme:error:unauthorized": raise Exception("expected unauthorized prob, found {0}".format( httpChall.error.typ)) finally: cleanup()
def test_wildcard_authz_reuse(): """ Test that an authorization for a base domain obtained via HTTP-01 isn't reused when issuing a wildcard for that base domain later on. """ # Create one client to reuse across multiple issuances client = chisel2.make_client(None) # Pick a random domain to issue for domains = [random_domain()] csr_pem = chisel2.make_csr(domains) # Submit an order for the name order = client.new_order(csr_pem) # Complete the order via an HTTP-01 challenge cleanup = chisel2.do_http_challenges(client, order.authorizations) try: order = client.poll_and_finalize(order) finally: cleanup() # Now try to issue a wildcard for the random domain domains[0] = "*." + domains[0] csr_pem = chisel2.make_csr(domains) order = client.new_order(csr_pem) # We expect all of the returned authorizations to be pending status for authz in order.authorizations: if authz.body.status != Status("pending"): raise Exception( "order for %s included a non-pending authorization (status: %s) from a previous HTTP-01 order" % ((domains), str(authz.body.status)))
def test_failed_validation_limit(): """ Fail a challenge repeatedly for the same domain, with the same account. Once we reach the rate limit we should get a rateLimitedError. Note that this depends on the specific threshold configured in rate-limit-policies.yml. This also incidentally tests a fix for https://github.com/letsencrypt/boulder/issues/4329. We expect to get ValidationErrors, eventually followed by a rate limit error. """ domain = "fail." + random_domain() csr_pem = chisel2.make_csr([domain]) client = chisel2.make_client() threshold = 3 for _ in range(threshold): order = client.new_order(csr_pem) chall = order.authorizations[0].body.challenges[0] client.answer_challenge(chall, chall.response(client.net.key)) try: client.poll_and_finalize(order) except errors.ValidationError as e: pass chisel2.expect_problem( "urn:ietf:params:acme:error:rateLimited", lambda: chisel2.auth_and_issue([domain], client=client))
def test_http_multiva_threshold_fail(): # Only config-next has remote VAs configured and is appropriate for this # integration test. if not CONFIG_NEXT: return client = chisel2.make_client() # Configure a guestlist that will fail the multiVA threshold test by # only allowing the primary VA. guestlist = {"boulder": 1} hostname, cleanup = multiva_setup(client, guestlist) try: chisel2.auth_and_issue([hostname], client=client, chall_type="http-01") except acme_errors.ValidationError as e: # NOTE(@cpu): Chisel2's expect_problem doesn't work in this case so this # test needs to unpack an `acme_errors.ValidationError` on its own. It # might be possible to clean this up in the future. if len(e.failed_authzrs) != 1: raise Exception("expected one failed authz, found {0}".format(len(e.failed_authzrs))) challs = e.failed_authzrs[0].body.challenges httpChall = None for chall_body in challs: if isinstance(chall_body.chall, challenges.HTTP01): httpChall = chall_body if httpChall is None: raise Exception("no HTTP-01 challenge in failed authz") if httpChall.error.typ != "urn:ietf:params:acme:error:unauthorized": raise Exception("expected unauthorized prob, found {0}".format(httpChall.error.typ)) finally: cleanup()
def test_revoke_by_issuer(): client = make_client(None) order = auth_and_issue([random_domain()], client=client) cert = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, order.fullchain_pem) client.revoke(jose.ComparableX509(cert), 0)
def test_wildcard_authz_reuse(): """ Test that an authorization for a base domain obtained via HTTP-01 isn't reused when issuing a wildcard for that base domain later on. """ # Create one client to reuse across multiple issuances client = chisel2.make_client(None) # Pick a random domain to issue for domains = [ random_domain() ] csr_pem = chisel2.make_csr(domains) # Submit an order for the name order = client.new_order(csr_pem) # Complete the order via an HTTP-01 challenge cleanup = chisel2.do_http_challenges(client, order.authorizations) try: order = client.poll_and_finalize(order) finally: cleanup() # Now try to issue a wildcard for the random domain domains[0] = "*." + domains[0] csr_pem = chisel2.make_csr(domains) order = client.new_order(csr_pem) # We expect all of the returned authorizations to be pending status for authz in order.authorizations: if authz.body.status != Status("pending"): raise Exception("order for %s included a non-pending authorization (status: %s) from a previous HTTP-01 order" % ((domains), str(authz.body.status)))
def test_new_order_policy_errs(): """ Test that creating an order with policy blocked identifiers returns a problem with subproblems. """ client = chisel2.make_client(None) # 'in-addr.arpa' is present in `test/hostname-policy.yaml`'s # HighRiskBlockedNames list. csr_pem = chisel2.make_csr( ["out-addr.in-addr.arpa", "between-addr.in-addr.arpa"]) # With two policy blocked names in the order we expect to get back a top # level rejectedIdentifier with a detail message that references # subproblems. # # TODO(@cpu): After https://github.com/certbot/certbot/issues/7046 is # implemented in the upstream `acme` module this test should also ensure the # subproblems are properly represented. ok = False try: order = client.new_order(csr_pem) except messages.Error as e: ok = True if e.typ != "urn:ietf:params:acme:error:rejectedIdentifier": raise (Exception( 'Expected rejectedIdentifier type problem, got {0}'.format( e.typ))) if e.detail != 'Error creating new order :: Cannot issue for "out-addr.in-addr.arpa": Policy forbids issuing for name (and 1 more problems. Refer to sub-problems for more information.)': raise (Exception('Order problem detail did not match expected')) if not ok: raise Exception('Expected problem, got no error')
def test_revoke_by_privkey(): client = make_client(None) domains = [random_domain()] key = OpenSSL.crypto.PKey() key.generate_key(OpenSSL.crypto.TYPE_RSA, 2048) key_pem = OpenSSL.crypto.dump_privatekey(OpenSSL.crypto.FILETYPE_PEM, key) csr_pem = acme_crypto_util.make_csr(key_pem, domains, False) order = client.new_order(csr_pem) cleanup = do_http_challenges(client, order.authorizations) try: order = client.poll_order_and_request_issuance(order) finally: cleanup() # Create a new client with the JWK as the cert private key jwk = jose.JWKRSA(key=key) net = acme_client.ClientNetwork(key, acme_version=2, user_agent="Boulder integration tester") new_client = acme_client.Client(os.getenv( 'DIRECTORY', 'http://localhost:4001/directory'), key=jwk, net=net, acme_version=2) cert = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, order.fullchain_pem) client.revoke(jose.ComparableX509(cert), 0)
def test_delete_unused_challenges(): order = chisel2.auth_and_issue([random_domain()], chall_type="dns-01") a = order.authorizations[0] if len(a.body.challenges) != 1: raise Exception("too many challenges (%d) left after validation" % len(a.body.challenges)) if not isinstance(a.body.challenges[0].chall, challenges.DNS01): raise Exception("wrong challenge type left after validation") # intentionally fail a challenge client = chisel2.make_client() csr_pem = chisel2.make_csr([random_domain()]) order = client.new_order(csr_pem) c = chisel2.get_chall(order.authorizations[0], challenges.DNS01) client.answer_challenge(c, c.response(client.net.key)) for _ in range(5): a, _ = client.poll(order.authorizations[0]) if a.body.status == Status("invalid"): break time.sleep(1) if len(a.body.challenges) != 1: raise Exception( "too many challenges (%d) left after failed validation" % len(a.body.challenges)) if not isinstance(a.body.challenges[0].chall, challenges.DNS01): raise Exception("wrong challenge type left after validation")
def test_revoke_by_privkey(): client = chisel2.make_client(None) domains = [random_domain()] key = OpenSSL.crypto.PKey() key.generate_key(OpenSSL.crypto.TYPE_RSA, 2048) key_pem = OpenSSL.crypto.dump_privatekey(OpenSSL.crypto.FILETYPE_PEM, key) csr_pem = chisel2.make_csr(domains) order = client.new_order(csr_pem) cleanup = chisel2.do_http_challenges(client, order.authorizations) try: order = client.poll_and_finalize(order) finally: cleanup() # Create a new client with the JWK as the cert private key jwk = josepy.JWKRSA(key=key) net = acme_client.ClientNetwork(key, user_agent="Boulder integration tester") directory = Directory.from_json(net.get(chisel2.DIRECTORY_V2).json()) new_client = acme_client.ClientV2(directory, net) cert = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, order.fullchain_pem) reset_akamai_purges() client.revoke(josepy.ComparableX509(cert), 0) cert_file_pem = os.path.join(tempdir, "revokeme.pem") with open(cert_file_pem, "w") as f: f.write(OpenSSL.crypto.dump_certificate( OpenSSL.crypto.FILETYPE_PEM, cert).decode()) ee_ocsp_url = "http://localhost:4002" verify_revocation(cert_file_pem, "test/test-ca2.pem", ee_ocsp_url) verify_akamai_purge()
def test_revoke_by_privkey(): client = chisel2.make_client(None) domains = [random_domain()] key = OpenSSL.crypto.PKey() key.generate_key(OpenSSL.crypto.TYPE_RSA, 2048) key_pem = OpenSSL.crypto.dump_privatekey(OpenSSL.crypto.FILETYPE_PEM, key) csr_pem = chisel2.make_csr(domains) order = client.new_order(csr_pem) cleanup = chisel2.do_http_challenges(client, order.authorizations) try: order = client.poll_and_finalize(order) finally: cleanup() # Create a new client with the JWK as the cert private key jwk = josepy.JWKRSA(key=key) net = acme_client.ClientNetwork(key, user_agent="Boulder integration tester") directory = Directory.from_json(net.get(chisel2.DIRECTORY_V2).json()) new_client = acme_client.ClientV2(directory, net) cert = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, order.fullchain_pem) client.revoke(josepy.ComparableX509(cert), 0)
def z1_reuse_setup(): """Runs during "setup_twenty_days_ago" phase.""" global z1_reuse_client global z1_reuse_authzs z1_reuse_client = chisel2.make_client() order = chisel2.auth_and_issue([random_domain(), random_domain()], client=z1_reuse_client) for a in order.authorizations: z1_reuse_authzs.append(a)
def test_duplicate_orders(): """ Test that the same client issuing for the same domain names twice in a row works without error. """ client = chisel2.make_client(None) domains = [ random_domain() ] chisel2.auth_and_issue(domains, client=client) chisel2.auth_and_issue(domains, client=client)
def test_http_challenge_http_redirect(): 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") # Calculate its keyauth so we can add it in a special non-standard location # for the redirect result resp = chall.response(client.net.key) keyauth = resp.key_authorization challSrv.add_http01_response("http-redirect", keyauth) # Create a HTTP redirect from the challenge's validation path to some other # token path where we have registered the key authorization. challengePath = "/.well-known/acme-challenge/{0}".format(token) redirectPath = "/.well-known/acme-challenge/http-redirect?params=are&important=to¬=lose" challSrv.add_http_redirect( challengePath, "http://{0}{1}".format(d, redirectPath)) chisel2.auth_and_issue([d], client=client, chall_type="http-01") challSrv.remove_http_redirect(challengePath) challSrv.remove_http01_response("http-redirect") history = challSrv.http_request_history(d) challSrv.clear_http_request_history(d) # There should have been at least two GET requests made to the # challtestsrv. There may have been more if remote VAs were configured. if len(history) < 2: raise Exception("Expected at least 2 HTTP request events on challtestsrv, found {1}".format(len(history))) initialRequests = [] redirectedRequests = [] for request in history: # All requests should have been over HTTP if request['HTTPS'] is True: raise Exception("Expected all requests to be HTTP") # Initial requests should have the expected initial HTTP-01 URL for the challenge if request['URL'] == challengePath: initialRequests.append(request) # Redirected requests should have the expected redirect path URL with all # its parameters elif request['URL'] == redirectPath: redirectedRequests.append(request) else: raise Exception("Unexpected request URL {0} in challtestsrv history: {1}".format(request['URL'], request)) # There should have been at least 1 initial HTTP-01 validation request. if len(initialRequests) < 1: raise Exception("Expected {0} initial HTTP-01 request events on challtestsrv, found {1}".format(validation_attempts, len(initialRequests))) # There should have been at least 1 redirected HTTP request for each VA if len(redirectedRequests) < 1: raise Exception("Expected {0} redirected HTTP-01 request events on challtestsrv, found {1}".format(validation_attempts, len(redirectedRequests)))
def test_duplicate_orders(): """ Test that the same client issuing for the same domain names twice in a row works without error. """ client = chisel2.make_client(None) domains = [random_domain()] chisel2.auth_and_issue(domains, client=client) chisel2.auth_and_issue(domains, client=client)
def test_order_reuse_failed_authz(): """ Test that creating an order for a domain name, failing an authorization in that order, and submitting another new order request for the same name doesn't reuse a failed authorizaton in the new order. """ client = chisel2.make_client(None) domains = [random_domain()] csr_pem = chisel2.make_csr(domains) order = client.new_order(csr_pem) firstOrderURI = order.uri # Pick the first authz's first challenge, doesn't matter what type it is chall_body = order.authorizations[0].body.challenges[0] # Answer it, but with nothing set up to solve the challenge request client.answer_challenge(chall_body, chall_body.response(client.net.key)) deadline = datetime.datetime.now() + datetime.timedelta(seconds=60) authzFailed = False try: # Poll the order's authorizations until they are non-pending, a timeout # occurs, or there is an invalid authorization status. client.poll_authorizations(order, deadline) except acme_errors.ValidationError as e: # We expect there to be a ValidationError from one of the authorizations # being invalid. authzFailed = True # If the poll ended and an authz's status isn't invalid then we reached the # deadline, fail the test if not authzFailed: raise Exception("timed out waiting for order %s to become invalid" % firstOrderURI) # Make another order with the same domains order = client.new_order(csr_pem) # It should not be the same order as before if order.uri == firstOrderURI: raise Exception("new-order for %s returned a , now-invalid, order" % domains) # We expect all of the returned authorizations to be pending status for authz in order.authorizations: if authz.body.status != Status("pending"): raise Exception( "order for %s included a non-pending authorization (status: %s) from a previous order" % ((domains), str(authz.body.status))) # We expect the new order can be fulfilled cleanup = chisel2.do_http_challenges(client, order.authorizations) try: order = client.poll_and_finalize(order) finally: cleanup()
def test_http_challenge_http_redirect(): 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") # Calculate its keyauth so we can add it in a special non-standard location # for the redirect result resp = chall.response(client.net.key) keyauth = resp.key_authorization challSrv.add_http01_response("http-redirect", keyauth) # Create a HTTP redirect from the challenge's validation path to some other # token path where we have registered the key authorization. challengePath = "/.well-known/acme-challenge/{0}".format(token) redirectPath = "/.well-known/acme-challenge/http-redirect?params=are&important=to¬=lose" challSrv.add_http_redirect( challengePath, "http://{0}{1}".format(d, redirectPath)) chisel2.auth_and_issue([d], client=client, chall_type="http-01") challSrv.remove_http_redirect(challengePath) challSrv.remove_http01_response("http-redirect") history = challSrv.http_request_history(d) challSrv.clear_http_request_history(d) # There should have been at least two GET requests made to the # challtestsrv. There may have been more if remote VAs were configured. if len(history) < 2: raise Exception("Expected at least 2 HTTP request events on challtestsrv, found {1}".format(len(history))) initialRequests = [] redirectedRequests = [] for request in history: # All requests should have been over HTTP if request['HTTPS'] is True: raise Exception("Expected all requests to be HTTP") # Initial requests should have the expected initial HTTP-01 URL for the challenge if request['URL'] == challengePath: initialRequests.append(request) # Redirected requests should have the expected redirect path URL with all # its parameters elif request['URL'] == redirectPath: redirectedRequests.append(request) else: raise Exception("Unexpected request URL {0} in challtestsrv history: {1}".format(request['URL'], request)) # There should have been at least 1 initial HTTP-01 validation request. if len(initialRequests) < 1: raise Exception("Expected {0} initial HTTP-01 request events on challtestsrv, found {1}".format(validation_attempts, len(initialRequests))) # There should have been at least 1 redirected HTTP request for each VA if len(redirectedRequests) < 1: raise Exception("Expected {0} redirected HTTP-01 request events on challtestsrv, found {1}".format(validation_attempts, len(redirectedRequests)))
def check_challenge_dns_err(chalType): """ check_challenge_dns_err tests that performing an ACME challenge of the specified type to a hostname that is configured to return SERVFAIL for all queries produces the correct problem type and detail message. """ client = chisel2.make_client() # Create a random domains. d = random_domain() # Configure the chall srv to SERVFAIL all queries for that domain. challSrv.add_servfail_response(d) # Expect a DNS problem with a detail that matches a regex expectedProbType = "dns" expectedProbRegex = re.compile( r"DNS problem: SERVFAIL looking up (A|AAAA|TXT|CAA) for {0}".format(d)) # Try and issue for the domain with the given challenge type. failed = False try: chisel2.auth_and_issue([d], client=client, chall_type=chalType) except acme_errors.ValidationError as e: # Mark that the auth_and_issue failed failed = True # Extract the failed challenge from each failed authorization for authzr in e.failed_authzrs: c = None if chalType == "http-01": c = chisel2.get_chall(authzr, challenges.HTTP01) elif chalType == "dns-01": c = chisel2.get_chall(authzr, challenges.DNS01) elif chalType == "tls-alpn-01": c = chisel2.get_chall(authzr, challenges.TLSALPN01) else: raise Exception( "Invalid challenge type requested: {0}".format(challType)) # The failed challenge's error should match expected error = c.error if error is None or error.typ != "urn:ietf:params:acme:error:{0}".format( expectedProbType): raise Exception("Expected {0} prob, got {1}".format( expectedProbType, error.typ)) if not expectedProbRegex.match(error.detail): raise Exception( "Prob detail did not match expectedProbRegex, got \"{0}\"". format(error.detail)) finally: challSrv.remove_servfail_response(d) # If there was no exception that means something went wrong. The test should fail. if failed is False: raise Exception( "No problem generated issuing for broken DNS identifier")
def test_revoke_by_authz(): domains = [random_domain()] order = chisel2.auth_and_issue(domains) # create a new client and re-authz client = chisel2.make_client(None) chisel2.auth_and_issue(domains, client=client) cert = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, order.fullchain_pem) client.revoke(josepy.ComparableX509(cert), 0)
def test_revoke_by_authz(): domains = [random_domain()] order = chisel2.auth_and_issue(domains) # create a new client and re-authz client = chisel2.make_client(None) chisel2.auth_and_issue(domains, client=client) cert = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, order.fullchain_pem) client.revoke(josepy.ComparableX509(cert), 0)
def test_order_reuse_failed_authz(): """ Test that creating an order for a domain name, failing an authorization in that order, and submitting another new order request for the same name doesn't reuse a failed authorizaton in the new order. """ client = make_client(None) domains = [random_domain()] csr_pem = make_csr(domains) order = client.new_order(csr_pem) firstOrderURI = order.uri # Pick the first authz's first challenge, doesn't matter what type it is chall_body = order.authorizations[0].body.challenges[0] # Answer it, but with nothing set up to solve the challenge request client.answer_challenge(chall_body, chall_body.response(client.key)) # Poll for a fixed amount of time checking for the order to become invalid # from the authorization attempt initiated above failing deadline = datetime.datetime.now() + datetime.timedelta(seconds=60) while datetime.datetime.now() < deadline: time.sleep(1) updatedOrder = requests.get(firstOrderURI).json() if updatedOrder['status'] == "invalid": break # If the loop ended and the status isn't invalid then we reached the # deadline waiting for the order to become invalid, fail the test if updatedOrder['status'] != "invalid": raise Exception("timed out waiting for order %s to become invalid" % firstOrderURI) # Make another order with the same domains order = client.new_order(csr_pem) # It should not be the same order as before if order.uri == firstOrderURI: raise Exception("new-order for %s returned a , now-invalid, order" % domains) # We expect all of the returned authorizations to be pending status for authz in order.authorizations: if authz.body.status != Status("pending"): raise Exception( "order for %s included a non-pending authorization (status: %s) from a previous order" % ((domains), str(authz.body.status))) # We expect the new order can be fulfilled cleanup = do_http_challenges(client, order.authorizations) try: order = client.poll_order_and_request_issuance(order) finally: cleanup()
def ocsp_exp_unauth_setup(): client = chisel2.make_client(None) order = chisel2.auth_and_issue([random_domain()], client=client) cert = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, order.fullchain_pem) cert_file_pem = os.path.join(tempdir, "to-expire.pem") with open(cert_file_pem, "w") as f: f.write(OpenSSL.crypto.dump_certificate( OpenSSL.crypto.FILETYPE_PEM, cert).decode()) verify_ocsp(cert_file_pem, "test/test-ca2.pem", "http://localhost:4002", "good") global expired_cert_name expired_cert_name = cert_file_pem
def test_auth_deactivation_v2(): client = chisel2.make_client(None) csr_pem = chisel2.make_csr([random_domain()]) order = client.new_order(csr_pem) resp = client.deactivate_authorization(order.authorizations[0]) if resp.body.status is not messages.STATUS_DEACTIVATED: raise Exception("unexpected authorization status") order = chisel2.auth_and_issue([random_domain()], client=client) resp = client.deactivate_authorization(order.authorizations[0]) if resp.body.status is not messages.STATUS_DEACTIVATED: raise Exception("unexpected authorization status")
def test_order_reuse_failed_authz(): """ Test that creating an order for a domain name, failing an authorization in that order, and submitting another new order request for the same name doesn't reuse a failed authorizaton in the new order. """ client = chisel2.make_client(None) domains = [ random_domain() ] csr_pem = chisel2.make_csr(domains) order = client.new_order(csr_pem) firstOrderURI = order.uri # Pick the first authz's first challenge, doesn't matter what type it is chall_body = order.authorizations[0].body.challenges[0] # Answer it, but with nothing set up to solve the challenge request client.answer_challenge(chall_body, chall_body.response(client.net.key)) # Poll for a fixed amount of time checking for the order to become invalid # from the authorization attempt initiated above failing deadline = datetime.datetime.now() + datetime.timedelta(seconds=60) while datetime.datetime.now() < deadline: time.sleep(1) updatedOrder = requests.get(firstOrderURI).json() if updatedOrder['status'] == "invalid": break # If the loop ended and the status isn't invalid then we reached the # deadline waiting for the order to become invalid, fail the test if updatedOrder['status'] != "invalid": raise Exception("timed out waiting for order %s to become invalid" % firstOrderURI) # Make another order with the same domains order = client.new_order(csr_pem) # It should not be the same order as before if order.uri == firstOrderURI: raise Exception("new-order for %s returned a , now-invalid, order" % domains) # We expect all of the returned authorizations to be pending status for authz in order.authorizations: if authz.body.status != Status("pending"): raise Exception("order for %s included a non-pending authorization (status: %s) from a previous order" % ((domains), str(authz.body.status))) # We expect the new order can be fulfilled cleanup = chisel2.do_http_challenges(client, order.authorizations) try: order = client.poll_and_finalize(order) finally: cleanup()
def test_http_challenge_https_redirect(): 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 HTTP redirect from the challenge's validation path to an HTTPS # address with the same path. challengePath = "/.well-known/acme-challenge/{0}".format(token) add_http_redirect(challengePath, "https://{0}{1}".format(d, challengePath)) auth_and_issue([d], client=client, chall_type="http-01") remove_http_redirect(challengePath)
def test_revoke_by_issuer(): client = chisel2.make_client(None) order = chisel2.auth_and_issue([random_domain()], client=client) cert = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, order.fullchain_pem) reset_akamai_purges() client.revoke(josepy.ComparableX509(cert), 0) cert_file_pem = os.path.join(tempdir, "revokeme.pem") with open(cert_file_pem, "w") as f: f.write(OpenSSL.crypto.dump_certificate( OpenSSL.crypto.FILETYPE_PEM, cert).decode()) ee_ocsp_url = "http://localhost:4002" verify_revocation(cert_file_pem, "test/test-ca2.pem", ee_ocsp_url) verify_akamai_purge()
def test_revoke_by_issuer(): client = chisel2.make_client(None) order = chisel2.auth_and_issue([random_domain()], client=client) cert = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, order.fullchain_pem) reset_akamai_purges() client.revoke(josepy.ComparableX509(cert), 0) cert_file_pem = os.path.join(tempdir, "revokeme.pem") with open(cert_file_pem, "w") as f: f.write(OpenSSL.crypto.dump_certificate( OpenSSL.crypto.FILETYPE_PEM, cert).decode()) ee_ocsp_url = "http://localhost:4002" verify_revocation(cert_file_pem, "test/test-ca2.pem", ee_ocsp_url) verify_akamai_purge()
def test_http2_http01_challenge(): """ test_http2_http01_challenge tests that an HTTP-01 challenge made to a HTTP/2 server fails with a specific error message for this case. """ client = chisel2.make_client() hostname = "fake.h2.example.com" # Add an A record for the test server to ensure the VA's requests are directed # to the interface that we bind the FakeH2ServerHandler to. challSrv.add_a_record(hostname, ["10.88.88.88"]) # Allow socket address reuse on the base TCPServer class. Failing to do this # causes subsequent integration tests to fail with "Address in use" errors even # though this test _does_ call shutdown() and server_close(). Even though the # server was shut-down Python's socket will be in TIME_WAIT because of prev. client # connections. Having the TCPServer set SO_REUSEADDR on the socket solves # the problem. socketserver.TCPServer.allow_reuse_address = True # Create, start, and wait for a fake HTTP/2 server. server = socketserver.TCPServer(('10.88.88.88', 5002), FakeH2ServerHandler) thread = threading.Thread(target=server.serve_forever) thread.daemon = False thread.start() wait_for_tcp_server('10.88.88.88', 5002) # Issuing an HTTP-01 challenge for this hostname should produce a connection # problem with an error specific to the HTTP/2 misconfiguration. expectedError = "Server is speaking HTTP/2 over HTTP" try: chisel2.auth_and_issue([hostname], 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 not error.detail.endswith(expectedError): raise Exception("Expected prob detail ending in %s, got %s" % (expectedError, error.detail)) finally: server.shutdown() server.server_close() thread.join()
def test_http_challenge_loop_redirect(): 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 HTTP redirect from the challenge's validation path to itself challengePath = "/.well-known/acme-challenge/{0}".format(token) add_http_redirect(challengePath, "http://{0}{1}".format(d, challengePath)) # Issuing for the the name should fail because of the challenge domains's # redirect loop. chisel2.expect_problem( "urn:acme:error:connection", lambda: auth_and_issue([d], client=client, chall_type="http-01")) remove_http_redirect(challengePath)
def test_http2_http01_challenge(): """ test_http2_http01_challenge tests that an HTTP-01 challenge made to a HTTP/2 server fails with a specific error message for this case. """ client = chisel2.make_client() hostname = "fake.h2.example.com" # Add an A record for the test server to ensure the VA's requests are directed # to the interface that we bind the FakeH2ServerHandler to. challSrv.add_a_record(hostname, ["10.88.88.88"]) # Allow socket address reuse on the base TCPServer class. Failing to do this # causes subsequent integration tests to fail with "Address in use" errors even # though this test _does_ call shutdown() and server_close(). Even though the # server was shut-down Python's socket will be in TIME_WAIT because of prev. client # connections. Having the TCPServer set SO_REUSEADDR on the socket solves # the problem. socketserver.TCPServer.allow_reuse_address = True # Create, start, and wait for a fake HTTP/2 server. server = socketserver.TCPServer(('10.88.88.88', 5002), FakeH2ServerHandler) thread = threading.Thread(target = server.serve_forever) thread.daemon = False thread.start() wait_for_tcp_server('10.88.88.88', 5002) # Issuing an HTTP-01 challenge for this hostname should produce a connection # problem with an error specific to the HTTP/2 misconfiguration. expectedError = "Server is speaking HTTP/2 over HTTP" try: chisel2.auth_and_issue([hostname], 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 not error.detail.endswith(expectedError): raise Exception("Expected prob detail ending in %s, got %s" % (expectedError, error.detail)) finally: server.shutdown() server.server_close() thread.join()
def test_http_challenge_loop_redirect(): 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 HTTP redirect from the challenge's validation path to itself challengePath = "/.well-known/acme-challenge/{0}".format(token) challSrv.add_http_redirect( challengePath, "http://{0}{1}".format(d, challengePath)) # Issuing for the the name should fail because of the challenge domains's # redirect loop. chisel2.expect_problem("urn:ietf:params:acme:error:connection", lambda: chisel2.auth_and_issue([d], client=client, chall_type="http-01")) challSrv.remove_http_redirect(challengePath)
def test_order_finalize_early(): """ Test that finalizing an order before its fully authorized results in the order having an error set and the status being invalid. """ # Create a client client = chisel2.make_client(None) # Create a random domain and a csr domains = [ random_domain() ] csr_pem = chisel2.make_csr(domains) # Create an order for the domain order = client.new_order(csr_pem) deadline = datetime.datetime.now() + datetime.timedelta(seconds=5) # Finalizing an order early should generate an orderNotReady error. chisel2.expect_problem("urn:ietf:params:acme:error:orderNotReady", lambda: client.finalize_order(order, deadline))
def test_http_multiva_threshold_pass(): # Only config-next has remote VAs configured and is appropriate for this # integration test. if not CONFIG_NEXT: return client = chisel2.make_client() # Configure a guestlist that will pass the multiVA threshold test by # allowing the primary VA and one remote. guestlist = {"boulder": 1, "boulder-remote-b": 1} hostname, cleanup = multiva_setup(client, guestlist) try: # With the maximum number of allowed remote VA failures the overall # challenge should still succeed. chisel2.auth_and_issue([hostname], client=client, chall_type="http-01") finally: cleanup()
def test_http_challenge_badhost_redirect(): 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 HTTP redirect from the challenge's validation path to a bare IP # hostname. challengePath = "/.well-known/acme-challenge/{0}".format(token) add_http_redirect(challengePath, "https://127.0.0.1{0}".format(challengePath)) # Issuing for the name should cause a connection error because the redirect # domain name is an IP address. chisel2.expect_problem( "urn:acme:error:connection", lambda: auth_and_issue([d], client=client, chall_type="http-01")) remove_http_redirect(challengePath)
def test_http_challenge_badhost_redirect(): 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 HTTP redirect from the challenge's validation path to a bare IP # hostname. challengePath = "/.well-known/acme-challenge/{0}".format(token) challSrv.add_http_redirect( challengePath, "https://127.0.0.1{0}".format(challengePath)) # Issuing for the name should cause a connection error because the redirect # domain name is an IP address. chisel2.expect_problem("urn:acme:error:connection", lambda: auth_and_issue([d], client=client, chall_type="http-01")) challSrv.remove_http_redirect(challengePath)
def test_http_challenge_badport_redirect(): 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 HTTP redirect from the challenge's validation path to a host with # an invalid port. challengePath = "/.well-known/acme-challenge/{0}".format(token) challSrv.add_http_redirect(challengePath, "http://{0}:1337{1}".format(d, challengePath)) # Issuing for the name should fail because of the challenge domain's # invalid port redirect. chisel2.expect_problem( "urn:ietf:params:acme:error:connection", lambda: chisel2. auth_and_issue([d], client=client, chall_type="http-01")) challSrv.remove_http_redirect(challengePath)
def test_http_multiva_threshold_pass(): # Only config-next has remote VAs configured and is appropriate for this # integration test. if not CONFIG_NEXT: return client = chisel2.make_client() # Configure a guestlist that will pass the multiVA threshold test by # allowing the primary VA and one remote. guestlist = {"boulder": 1, "boulder-remote-b": 1} hostname, cleanup = multiva_setup(client, guestlist) try: # With the maximum number of allowed remote VA failures the overall # challenge should still succeed. chisel2.auth_and_issue([hostname], client=client, chall_type="http-01") finally: cleanup()
def test_order_finalize_early(): """ Test that finalizing an order before its fully authorized results in the order having an error set and the status being invalid. """ # Create a client client = chisel2.make_client(None) # Create a random domain and a csr domains = [random_domain()] csr_pem = chisel2.make_csr(domains) # Create an order for the domain order = client.new_order(csr_pem) deadline = datetime.datetime.now() + datetime.timedelta(seconds=5) # Finalizing an order early should generate an orderNotReady error. chisel2.expect_problem("urn:ietf:params:acme:error:orderNotReady", lambda: client.finalize_order(order, deadline))
def test_http_challenge_badproto_redirect(): 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 HTTP redirect from the challenge's validation path to whacky # non-http/https protocol URL. challengePath = "/.well-known/acme-challenge/{0}".format(token) challSrv.add_http_redirect(challengePath, "gopher://{0}{1}".format(d, challengePath)) # Issuing for the name should cause a connection error because the redirect # domain name is an IP address. chisel2.expect_problem( "urn:ietf:params:acme:error:connection", lambda: chisel2. auth_and_issue([d], client=client, chall_type="http-01")) challSrv.remove_http_redirect(challengePath)
def test_http_challenge_badproto_redirect(): 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 HTTP redirect from the challenge's validation path to whacky # non-http/https protocol URL. challengePath = "/.well-known/acme-challenge/{0}".format(token) challSrv.add_http_redirect( challengePath, "gopher://{0}{1}".format(d, challengePath)) # Issuing for the name should cause a connection error because the redirect # domain name is an IP address. chisel2.expect_problem("urn:ietf:params:acme:error:connection", lambda: chisel2.auth_and_issue([d], client=client, chall_type="http-01")) challSrv.remove_http_redirect(challengePath)
def test_order_finalize_early(): """ Test that finalizing an order before its fully authorized results in the order having an error set and the status being invalid. """ # Create a client client = make_client(None) # Create a random domain and a csr domains = [random_domain()] csr_pem = make_csr(domains) # Create an order for the domain order = client.new_order(csr_pem) # Finalize the order without doing anything with the authorizations. YOLO # We expect this to generate an unauthorized error. chisel2.expect_problem( "urn:ietf:params:acme:error:unauthorized", lambda: client.net.post( order.body.finalize, CertificateRequest(csr=order.csr))) # Poll for a fixed amount of time checking for the order to become invalid # from the early finalization attempt initiated above failing deadline = datetime.datetime.now() + datetime.timedelta(seconds=5) while datetime.datetime.now() < deadline: time.sleep(1) updatedOrder = requests.get(order.uri).json() if updatedOrder['status'] == "invalid": break # If the loop ended and the status isn't invalid then we reached the # deadline waiting for the order to become invalid, fail the test if updatedOrder['status'] != "invalid": raise Exception("timed out waiting for order %s to become invalid" % order.uri) # The order should have an error with the expected type if updatedOrder['error'][ 'type'] != 'urn:ietf:params:acme:error:unauthorized': raise Exception("order %s has incorrect error field type: \"%s\"" % (order.uri, updatedOrder['error']['type']))
def test_overlapping_wildcard(): """ Test issuance for a random domain and a wildcard version of the same domain using DNS-01. This should result in *two* distinct authorizations. """ domain = random_domain() domains = [domain, "*." + domain] client = chisel2.make_client(None) csr_pem = chisel2.make_csr(domains) order = client.new_order(csr_pem) authzs = order.authorizations if len(authzs) != 2: raise Exception("order for %s had %d authorizations, expected 2" % (domains, len(authzs))) cleanup = chisel2.do_dns_challenges(client, authzs) try: order = client.poll_and_finalize(order) finally: cleanup()
def test_overlapping_wildcard(): """ Test issuance for a random domain and a wildcard version of the same domain using DNS-01. This should result in *two* distinct authorizations. """ domain = random_domain() domains = [ domain, "*."+domain ] client = chisel2.make_client(None) csr_pem = chisel2.make_csr(domains) order = client.new_order(csr_pem) authzs = order.authorizations if len(authzs) != 2: raise Exception("order for %s had %d authorizations, expected 2" % (domains, len(authzs))) cleanup = chisel2.do_dns_challenges(client, authzs) try: order = client.poll_and_finalize(order) finally: cleanup()
def test_order_finalize_early(): """ Test that finalizing an order before its fully authorized results in the order having an error set and the status being invalid. """ # Create a client client = chisel2.make_client(None) # Create a random domain and a csr domains = [ random_domain() ] csr_pem = chisel2.make_csr(domains) # Create an order for the domain order = client.new_order(csr_pem) deadline = datetime.datetime.now() + datetime.timedelta(seconds=5) # Finalizing an order early should generate an unauthorized error and we # should check that the order is invalidated. chisel2.expect_problem("urn:ietf:params:acme:error:unauthorized", lambda: client.finalize_order(order, deadline)) # Poll for a fixed amount of time checking for the order to become invalid # from the early finalization attempt initiated above failing while datetime.datetime.now() < deadline: time.sleep(1) updatedOrder = requests.get(order.uri).json() if updatedOrder['status'] == "invalid": break # If the loop ended and the status isn't invalid then we reached the # deadline waiting for the order to become invalid, fail the test if updatedOrder['status'] != "invalid": raise Exception("timed out waiting for order %s to become invalid" % order.uri) # The order should have an error with the expected type if updatedOrder['error']['type'] != 'urn:ietf:params:acme:error:unauthorized': raise Exception("order %s has incorrect error field type: \"%s\"" % (order.uri, updatedOrder['error']['type']))
def test_revoke_by_privkey(): client = chisel2.make_client(None) domains = [random_domain()] key = OpenSSL.crypto.PKey() key.generate_key(OpenSSL.crypto.TYPE_RSA, 2048) key_pem = OpenSSL.crypto.dump_privatekey(OpenSSL.crypto.FILETYPE_PEM, key) csr_pem = chisel2.make_csr(domains) order = client.new_order(csr_pem) cleanup = chisel2.do_http_challenges(client, order.authorizations) try: order = client.poll_and_finalize(order) finally: cleanup() # Create a new client with the JWK as the cert private key jwk = josepy.JWKRSA(key=key) net = acme_client.ClientNetwork(key, user_agent="Boulder integration tester") directory = Directory.from_json(net.get(chisel2.DIRECTORY_V2).json()) new_client = acme_client.ClientV2(directory, net) cert = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, order.fullchain_pem) client.revoke(josepy.ComparableX509(cert), 0)
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_revoke_by_issuer(): client = chisel2.make_client(None) order = chisel2.auth_and_issue([random_domain()], client=client) cert = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, order.fullchain_pem) client.revoke(josepy.ComparableX509(cert), 0)
def test_http_challenge_https_redirect(): 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 HTTP redirect from the challenge's validation path to an HTTPS # address with the same path. challengePath = "/.well-known/acme-challenge/{0}".format(token) challSrv.add_http_redirect( challengePath, "https://{0}{1}".format(d, challengePath)) # Also add an A record for the domain pointing to the interface that the # HTTPS HTTP-01 challtestsrv is bound. challSrv.add_a_record(d, ["10.77.77.77"]) auth_and_issue([d], client=client, chall_type="http-01") challSrv.remove_http_redirect(challengePath) challSrv.remove_a_record(d) # There should have been at least two GET requests made to the challtestsrv by the VA if len(history) < 2: raise Exception("Expected 2 HTTP request events on challtestsrv, found {0}".format(len(history))) initialRequests = [] redirectedRequests = [] for request in history: # Initial requests should have the expected initial HTTP-01 URL for the challenge if request['URL'] == challengePath: initialRequests.append(request) # Redirected requests should have the expected redirect path URL with all # its parameters elif request['URL'] == redirectPath: redirectedRequests.append(request) else: raise Exception("Unexpected request URL {0} in challtestsrv history: {1}".format(request['URL'], request)) # There should have been at least 1 initial HTTP-01 validation request. if len(initialRequests) < 1: raise Exception("Expected {0} initial HTTP-01 request events on challtestsrv, found {1}".format(validation_attempts, len(initialRequests))) # All initial requests should have been over HTTP for r in initialRequests: if r['HTTPS'] is True: raise Exception("Expected all initial requests to be HTTP") # There should have been at least 1 redirected HTTP request for each VA if len(redirectedRequests) < 1: raise Exception("Expected {0} redirected HTTP-01 request events on challtestsrv, found {1}".format(validation_attempts, len(redirectedRequests))) # All the redirected requests should have been over HTTPS with the correct # SNI value for r in redirectedRequests: if r['HTTPS'] is False: raise Exception("Expected all redirected requests to be HTTPS") # TODO(@cpu): The following ServerName test will fail with config-next # until https://github.com/letsencrypt/boulder/issues/3969 is fixed. if default_config_dir.startswith("test/config-next"): return elif r['ServerName'] != d: raise Exception("Expected all redirected requests to have ServerName {0} got \"{1}\"".format(d, r['ServerName']))
def test_http_challenge_https_redirect(): 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") # Calculate its keyauth so we can add it in a special non-standard location # for the redirect result resp = chall.response(client.net.key) keyauth = resp.key_authorization challSrv.add_http01_response("https-redirect", keyauth) # Create a HTTP redirect from the challenge's validation path to an HTTPS # path with some parameters challengePath = "/.well-known/acme-challenge/{0}".format(token) redirectPath = "/.well-known/acme-challenge/https-redirect?params=are&important=to¬=lose" challSrv.add_http_redirect( challengePath, "https://{0}{1}".format(d, redirectPath)) # Also add an A record for the domain pointing to the interface that the # HTTPS HTTP-01 challtestsrv is bound. challSrv.add_a_record(d, ["10.77.77.77"]) try: chisel2.auth_and_issue([d], client=client, chall_type="http-01") except errors.ValidationError as e: problems = [] for authzr in e.failed_authzrs: for chall in authzr.body.challenges: error = chall.error if error: problems.append(error.__str__()) raise Exception("validation problem: %s" % "; ".join(problems)) challSrv.remove_http_redirect(challengePath) challSrv.remove_a_record(d) history = challSrv.http_request_history(d) challSrv.clear_http_request_history(d) # There should have been at least two GET requests made to the challtestsrv by the VA if len(history) < 2: raise Exception("Expected 2 HTTP request events on challtestsrv, found {0}".format(len(history))) initialRequests = [] redirectedRequests = [] for request in history: # Initial requests should have the expected initial HTTP-01 URL for the challenge if request['URL'] == challengePath: initialRequests.append(request) # Redirected requests should have the expected redirect path URL with all # its parameters elif request['URL'] == redirectPath: redirectedRequests.append(request) else: raise Exception("Unexpected request URL {0} in challtestsrv history: {1}".format(request['URL'], request)) # There should have been at least 1 initial HTTP-01 validation request. if len(initialRequests) < 1: raise Exception("Expected {0} initial HTTP-01 request events on challtestsrv, found {1}".format(validation_attempts, len(initialRequests))) # All initial requests should have been over HTTP for r in initialRequests: if r['HTTPS'] is True: raise Exception("Expected all initial requests to be HTTP, got %s" % r) # There should have been at least 1 redirected HTTP request for each VA if len(redirectedRequests) < 1: raise Exception("Expected {0} redirected HTTP-01 request events on challtestsrv, found {1}".format(validation_attempts, len(redirectedRequests))) # All the redirected requests should have been over HTTPS with the correct # SNI value for r in redirectedRequests: if r['HTTPS'] is False: raise Exception("Expected all redirected requests to be HTTPS") # TODO(@cpu): The following ServerName test will fail with config-next # until https://github.com/letsencrypt/boulder/issues/3969 is fixed. if CONFIG_NEXT: return elif r['ServerName'] != d: raise Exception("Expected all redirected requests to have ServerName {0} got \"{1}\"".format(d, r['ServerName']))