Revert "integration: move to Python3 (#4313)" (#4323)

This reverts commit 796a7aa2f4.

People's tests have been breaking on `docker-compose up` with the following output:

```
ImportError: No module named requests
```

Fixes #4322
This commit is contained in:
Jacob Hoffman-Andrews 2019-07-03 11:35:45 -07:00 committed by GitHub
parent 0d9b48e280
commit 3af49a16be
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 164 additions and 162 deletions

View File

@ -2,7 +2,7 @@ version: '3'
services: services:
boulder: boulder:
# To minimize fetching this should be the same version used below # To minimize fetching this should be the same version used below
image: letsencrypt/boulder-tools-go${TRAVIS_GO_VERSION:-1.12}:2019-06-28 image: letsencrypt/boulder-tools-go${TRAVIS_GO_VERSION:-1.12}:2019-04-08
environment: environment:
FAKE_DNS: 10.77.77.77 FAKE_DNS: 10.77.77.77
PKCS11_PROXY_SOCKET: tcp://boulder-hsm:5657 PKCS11_PROXY_SOCKET: tcp://boulder-hsm:5657
@ -55,7 +55,7 @@ services:
working_dir: /go/src/github.com/letsencrypt/boulder working_dir: /go/src/github.com/letsencrypt/boulder
bhsm: bhsm:
# To minimize fetching this should be the same version used above # To minimize fetching this should be the same version used above
image: letsencrypt/boulder-tools-go${TRAVIS_GO_VERSION:-1.12}:2019-06-28 image: letsencrypt/boulder-tools-go${TRAVIS_GO_VERSION:-1.12}:2019-04-08
environment: environment:
PKCS11_DAEMON_SOCKET: tcp://0.0.0.0:5657 PKCS11_DAEMON_SOCKET: tcp://0.0.0.0:5657
command: /usr/local/bin/pkcs11-daemon /usr/lib/softhsm/libsofthsm2.so command: /usr/local/bin/pkcs11-daemon /usr/lib/softhsm/libsofthsm2.so
@ -77,7 +77,7 @@ services:
logging: logging:
driver: none driver: none
netaccess: netaccess:
image: letsencrypt/boulder-tools-go${TRAVIS_GO_VERSION:-1.12}:2019-06-28 image: letsencrypt/boulder-tools-go${TRAVIS_GO_VERSION:-1.12}:2019-04-08
environment: environment:
GO111MODULE: "on" GO111MODULE: "on"
GOFLAGS: "-mod=vendor" GOFLAGS: "-mod=vendor"

View File

@ -110,9 +110,9 @@ if [[ "$RUN" =~ "integration" ]] ; then
args+=("--skip-setup") args+=("--skip-setup")
fi fi
source ${CERTBOT_PATH:-/certbot}/${VENV_NAME:-venv3}/bin/activate source ${CERTBOT_PATH:-/certbot}/${VENV_NAME:-venv}/bin/activate
DIRECTORY=http://boulder:4000/directory \ DIRECTORY=http://boulder:4000/directory \
python3 test/integration-test.py "${args[@]}" python2 test/integration-test.py "${args[@]}"
fi fi
# Test that just ./start.py works, which is a proxy for testing that # Test that just ./start.py works, which is a proxy for testing that

View File

@ -9,5 +9,4 @@ RUN mkdir -p "$GOPATH/src" "$GOPATH/bin" && chmod -R 777 "$GOPATH"
WORKDIR $GOPATH WORKDIR $GOPATH
ADD build.sh /tmp/build.sh ADD build.sh /tmp/build.sh
ADD requirements.txt /tmp/requirements.txt
RUN /tmp/build.sh RUN /tmp/build.sh

View File

@ -51,8 +51,6 @@ git clone https://github.com/certbot/certbot /certbot
cd /certbot cd /certbot
./letsencrypt-auto --os-packages-only ./letsencrypt-auto --os-packages-only
./tools/venv3.py ./tools/venv3.py
source venv3/bin/activate
pip install -r /tmp/requirements.txt
cd - cd -
# Install pkcs11-proxy. Checked out commit was master HEAD at time # Install pkcs11-proxy. Checked out commit was master HEAD at time

View File

@ -1,4 +1,4 @@
import requests import urllib2
import json import json
class ChallTestServer: class ChallTestServer:
@ -39,14 +39,14 @@ class ChallTestServer:
self._baseURL = url self._baseURL = url
def _postURL(self, url, body): def _postURL(self, url, body):
return requests.post( return urllib2.urlopen(
url, url,
data=json.dumps(body)).text data=json.dumps(body)).read()
def _URL(self, path): def _URL(self, path):
urlPath = self._paths.get(path, None) urlPath = self._paths.get(path, None)
if urlPath is None: if urlPath is None:
raise(Exception("No challenge test server URL path known for {0}".format(path))) raise Exception("No challenge test server URL path known for {0}".format(path))
return self._baseURL + urlPath return self._baseURL + urlPath
def _clear_request_history(self, host, typ): def _clear_request_history(self, host, typ):

View File

@ -14,7 +14,7 @@ import socket
import sys import sys
import threading import threading
import time import time
import requests import urllib2
from cryptography import x509 from cryptography import x509
from cryptography.hazmat.backends import default_backend from cryptography.hazmat.backends import default_backend
@ -73,9 +73,9 @@ def update_email(client, email):
email. email.
""" """
if client is None: if client is None:
raise(NoClientError("update_email requires a valid acme.Client argument")) raise NoClientError("update_email requires a valid acme.Client argument")
if email is None: if email is None:
raise(EmailRequiredError("update_email requires an email argument")) raise EmailRequiredError("update_email requires an email argument")
if not email.startswith("mailto:"): if not email.startswith("mailto:"):
email = "mailto:"+ email email = "mailto:"+ email
acct = client.account acct = client.account
@ -86,7 +86,7 @@ def get_chall(authz, typ):
for chall_body in authz.body.challenges: for chall_body in authz.body.challenges:
if isinstance(chall_body.chall, typ): if isinstance(chall_body.chall, typ):
return chall_body return chall_body
raise(Exception("No %s challenge found" % typ)) raise Exception("No %s challenge found" % typ)
class ValidationError(Exception): class ValidationError(Exception):
"""An error that occurs during challenge validation.""" """An error that occurs during challenge validation."""
@ -110,9 +110,9 @@ def issue(client, authzs, cert_output=None):
csr = OpenSSL.crypto.X509Req() csr = OpenSSL.crypto.X509Req()
csr.add_extensions([ csr.add_extensions([
OpenSSL.crypto.X509Extension( OpenSSL.crypto.X509Extension(
'subjectAltName'.encode(), 'subjectAltName',
critical=False, critical=False,
value=(', '.join('DNS:' + d for d in domains)).encode() value=', '.join('DNS:' + d for d in domains).encode()
), ),
]) ])
csr.set_pubkey(pkey) csr.set_pubkey(pkey)
@ -126,21 +126,19 @@ def issue(client, authzs, cert_output=None):
# If we get a PollError, pick the first failed authz and turn it into a more # If we get a PollError, pick the first failed authz and turn it into a more
# useful ValidationError that contains details we can look for in tests. # useful ValidationError that contains details we can look for in tests.
for authz in error.updated: for authz in error.updated:
r = requests.get(authz.uri) updated_authz = json.loads(urllib2.urlopen(authz.uri).read())
r.raise_for_status()
updated_authz = r.json()
domain = authz.body.identifier.value, domain = authz.body.identifier.value,
for c in updated_authz['challenges']: for c in updated_authz['challenges']:
if 'error' in c: if 'error' in c:
err = c['error'] err = c['error']
raise(ValidationError(domain, err['type'], err['detail'])) raise ValidationError(domain, err['type'], err['detail'])
# If none of the authz's had an error, just re-raise. # If none of the authz's had an error, just re-raise.
raise raise
if cert_output is not None: if cert_output is not None:
pem = OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_PEM, pem = OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_PEM,
cert_resource.body) cert_resource.body)
with open(cert_output, 'w') as f: with open(cert_output, 'w') as f:
f.write(pem.decode()) f.write(pem)
return cert_resource return cert_resource
def http_01_answer(client, chall_body): def http_01_answer(client, chall_body):
@ -225,7 +223,7 @@ def auth_and_issue(domains, chall_type="dns-01", email=None, cert_output=None, c
elif chall_type == "tls-alpn-01": elif chall_type == "tls-alpn-01":
cleanup = do_tlsalpn_challenges(client, authzs) cleanup = do_tlsalpn_challenges(client, authzs)
else: else:
raise(Exception("invalid challenge type %s" % chall_type)) raise Exception("invalid challenge type %s" % chall_type)
try: try:
cert_resource = issue(client, authzs, cert_output) cert_resource = issue(client, authzs, cert_output)
@ -252,15 +250,15 @@ def expect_problem(problem_type, func):
else: else:
raise raise
if not ok: if not ok:
raise(Exception('Expected %s, got no error' % problem_type)) raise Exception('Expected %s, got no error' % problem_type)
if __name__ == "__main__": if __name__ == "__main__":
domains = sys.argv[1:] domains = sys.argv[1:]
if len(domains) == 0: if len(domains) == 0:
print(__doc__) print __doc__
sys.exit(0) sys.exit(0)
try: try:
auth_and_issue(domains) auth_and_issue(domains)
except messages.Error as e: except messages.Error, e:
print(e) print e
sys.exit(1) sys.exit(1)

View File

@ -15,6 +15,7 @@ import sys
import signal import signal
import threading import threading
import time import time
import urllib2
from cryptography.hazmat.backends import default_backend from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.asymmetric import rsa from cryptography.hazmat.primitives.asymmetric import rsa
@ -59,14 +60,14 @@ def make_client(email=None):
client.net.account = client.new_account(messages.NewRegistration.from_data(email=email, client.net.account = client.new_account(messages.NewRegistration.from_data(email=email,
terms_of_service_agreed=True)) terms_of_service_agreed=True))
else: else:
raise(Exception("Unrecognized terms of service URL %s" % tos)) raise Exception("Unrecognized terms of service URL %s" % tos)
return client return client
def get_chall(authz, typ): def get_chall(authz, typ):
for chall_body in authz.body.challenges: for chall_body in authz.body.challenges:
if isinstance(chall_body.chall, typ): if isinstance(chall_body.chall, typ):
return chall_body return chall_body
raise(Exception("No %s challenge found" % typ.typ)) raise Exception("No %s challenge found" % typ.typ)
def make_csr(domains): def make_csr(domains):
key = OpenSSL.crypto.PKey() key = OpenSSL.crypto.PKey()
@ -99,7 +100,7 @@ def auth_and_issue(domains, chall_type="dns-01", email=None, cert_output=None, c
elif chall_type == "tls-alpn-01": elif chall_type == "tls-alpn-01":
cleanup = do_tlsalpn_challenges(client, authzs) cleanup = do_tlsalpn_challenges(client, authzs)
else: else:
raise(Exception("invalid challenge type %s" % chall_type)) raise Exception("invalid challenge type %s" % chall_type)
try: try:
order = client.poll_and_finalize(order) order = client.poll_and_finalize(order)
@ -171,7 +172,7 @@ def expect_problem(problem_type, func):
if e.typ == problem_type: if e.typ == problem_type:
ok = True ok = True
else: else:
raise(Exception("Expected %s, got %s" % (problem_type, error.__str__()))) raise Exception("Expected %s, got %s" % (problem_type, error.__str__()))
except acme_errors.ValidationError as e: except acme_errors.ValidationError as e:
for authzr in e.failed_authzrs: for authzr in e.failed_authzrs:
for chall in authzr.body.challenges: for chall in authzr.body.challenges:
@ -179,19 +180,19 @@ def expect_problem(problem_type, func):
if error and error.typ == problem_type: if error and error.typ == problem_type:
ok = True ok = True
elif error: elif error:
raise(Exception("Expected %s, got %s" % (problem_type, error.__str__()))) raise Exception("Expected %s, got %s" % (problem_type, error.__str__()))
if not ok: if not ok:
raise(Exception('Expected %s, got no error' % problem_type)) raise Exception('Expected %s, got no error' % problem_type)
if __name__ == "__main__": if __name__ == "__main__":
# Die on SIGINT # Die on SIGINT
signal.signal(signal.SIGINT, signal.SIG_DFL) signal.signal(signal.SIGINT, signal.SIG_DFL)
domains = sys.argv[1:] domains = sys.argv[1:]
if len(domains) == 0: if len(domains) == 0:
print(__doc__) print __doc__
sys.exit(0) sys.exit(0)
try: try:
auth_and_issue(domains) auth_and_issue(domains)
except messages.Error as e: except messages.Error, e:
print(e) print e
sys.exit(1) sys.exit(1)

View File

@ -1,11 +1,11 @@
#!/usr/bin/env python2.7
import base64 import base64
import os import os
import urllib import urllib2
import time import time
import re import re
import random import random
import json import json
import requests
import socket import socket
import tempfile import tempfile
import shutil import shutil
@ -38,20 +38,20 @@ def random_domain():
return "rand.%x.xyz" % random.randrange(2**32) return "rand.%x.xyz" % random.randrange(2**32)
def run(cmd, **kwargs): def run(cmd, **kwargs):
return subprocess.check_output(cmd, shell=True, stderr=subprocess.STDOUT, **kwargs).decode() return subprocess.check_output(cmd, shell=True, stderr=subprocess.STDOUT, **kwargs)
def fetch_ocsp(request_bytes, url): def fetch_ocsp(request_bytes, url):
"""Fetch an OCSP response using POST, GET, and GET with URL encoding. """Fetch an OCSP response using POST, GET, and GET with URL encoding.
Returns a tuple of the responses. Returns a tuple of the responses.
""" """
ocsp_req_b64 = base64.b64encode(request_bytes).decode() ocsp_req_b64 = base64.b64encode(request_bytes)
# Make the OCSP request three different ways: by POST, by GET, and by GET with # Make the OCSP request three different ways: by POST, by GET, and by GET with
# URL-encoded parameters. All three should have an identical response. # URL-encoded parameters. All three should have an identical response.
get_response = requests.get("%s/%s" % (url, ocsp_req_b64)).content get_response = urllib2.urlopen("%s/%s" % (url, ocsp_req_b64)).read()
get_encoded_response = requests.get("%s/%s" % (url, urllib.parse.quote(ocsp_req_b64, safe = ""))).content get_encoded_response = urllib2.urlopen("%s/%s" % (url, urllib2.quote(ocsp_req_b64, safe = ""))).read()
post_response = requests.post("%s/" % (url), data=request_bytes).content post_response = urllib2.urlopen("%s/" % (url), request_bytes).read()
return (post_response, get_response, get_encoded_response) return (post_response, get_response, get_encoded_response)
@ -61,13 +61,13 @@ def make_ocsp_req(cert_file, issuer_file):
# First generate the OCSP request in DER form # First generate the OCSP request in DER form
run("openssl ocsp -no_nonce -issuer %s -cert %s -reqout %s" % ( run("openssl ocsp -no_nonce -issuer %s -cert %s -reqout %s" % (
issuer_file, cert_file, ocsp_req_file)) issuer_file, cert_file, ocsp_req_file))
with open(ocsp_req_file, mode='rb') as f: with open(ocsp_req_file) as f:
ocsp_req = f.read() ocsp_req = f.read()
return ocsp_req return ocsp_req
def ocsp_verify(cert_file, issuer_file, ocsp_response): def ocsp_verify(cert_file, issuer_file, ocsp_response):
ocsp_resp_file = os.path.join(tempdir, "ocsp.resp") ocsp_resp_file = os.path.join(tempdir, "ocsp.resp")
with open(ocsp_resp_file, "wb") as f: with open(ocsp_resp_file, "w") as f:
f.write(ocsp_response) f.write(ocsp_response)
output = run("openssl ocsp -no_nonce -issuer %s -cert %s \ output = run("openssl ocsp -no_nonce -issuer %s -cert %s \
-verify_other %s -CAfile test/test-root.pem \ -verify_other %s -CAfile test/test-root.pem \
@ -76,8 +76,8 @@ def ocsp_verify(cert_file, issuer_file, ocsp_response):
# also look for the string "Response Verify Failure" # also look for the string "Response Verify Failure"
verify_failure = "Response Verify Failure" verify_failure = "Response Verify Failure"
if re.search(verify_failure, output): if re.search(verify_failure, output):
print(output) print output
raise(Exception("OCSP verify failure")) raise Exception("OCSP verify failure")
return output return output
def verify_ocsp(cert_file, issuer_file, url, status): def verify_ocsp(cert_file, issuer_file, url, status):
@ -87,28 +87,28 @@ def verify_ocsp(cert_file, issuer_file, url, status):
# Verify all responses are the same # Verify all responses are the same
for resp in responses: for resp in responses:
if resp != responses[0]: if resp != responses[0]:
raise(Exception("OCSP responses differed: %s vs %s" %( raise Exception("OCSP responses differed: %s vs %s" %(
base64.b64encode(responses[0]), base64.b64encode(resp)))) base64.b64encode(responses[0]), base64.b64encode(resp)))
# Check response is for the correct certificate and is correct # Check response is for the correct certificate and is correct
# status # status
resp = responses[0] resp = responses[0]
verify_output = ocsp_verify(cert_file, issuer_file, resp) verify_output = ocsp_verify(cert_file, issuer_file, resp)
if not re.search("%s: %s" % (cert_file, status), verify_output): if not re.search("%s: %s" % (cert_file, status), verify_output):
print(verify_output) print verify_output
raise(Exception("OCSP response wasn't '%s'" % status)) raise Exception("OCSP response wasn't '%s'" % status)
def reset_akamai_purges(): def reset_akamai_purges():
requests.post("http://localhost:6789/debug/reset-purges", data="{}") urllib2.urlopen("http://localhost:6789/debug/reset-purges", "{}")
def verify_akamai_purge(): def verify_akamai_purge():
deadline = time.time() + 0.25 deadline = time.time() + 0.25
while True: while True:
time.sleep(0.05) time.sleep(0.05)
if time.time() > deadline: if time.time() > deadline:
raise(Exception("Timed out waiting for Akamai purge")) raise Exception("Timed out waiting for Akamai purge")
response = requests.get("http://localhost:6789/debug/get-purges") response = urllib2.urlopen("http://localhost:6789/debug/get-purges")
purgeData = response.json() purgeData = json.load(response)
if len(purgeData["V3"]) is not 1: if len(purgeData["V3"]) is not 1:
continue continue
break break
@ -142,7 +142,7 @@ def waitport(port, prog, perTickCheck=None):
return True return True
except socket.error as e: except socket.error as e:
if e.errno == errno.ECONNREFUSED: if e.errno == errno.ECONNREFUSED:
print("Waiting for debug port %d (%s)" % (port, prog)) print "Waiting for debug port %d (%s)" % (port, prog)
else: else:
raise raise
raise(Exception("timed out waiting for debug port %d (%s)" % (port, prog))) raise Exception("timed out waiting for debug port %d (%s)" % (port, prog))

View File

@ -1,4 +1,4 @@
#!/usr/bin/env python3 #!/usr/bin/env python2.7
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
""" """
This file contains basic infrastructure for running the integration test cases. This file contains basic infrastructure for running the integration test cases.
@ -53,7 +53,7 @@ def run_expired_authz_purger():
tool = "expired-authz-purger" tool = "expired-authz-purger"
out = get_future_output("./bin/expired-authz-purger --config cmd/expired-authz-purger/config.json", target_time) out = get_future_output("./bin/expired-authz-purger --config cmd/expired-authz-purger/config.json", target_time)
if 'via FAKECLOCK' not in out: if 'via FAKECLOCK' not in out:
raise(Exception("%s was not built with `integration` build tag" % (tool))) raise Exception("%s was not built with `integration` build tag" % (tool))
if num is None: if num is None:
return return
if CONFIG_NEXT: if CONFIG_NEXT:
@ -61,8 +61,8 @@ def run_expired_authz_purger():
else: else:
expected_output = 'Deleted a total of %d expired authorizations from %s' % (num, table) expected_output = 'Deleted a total of %d expired authorizations from %s' % (num, table)
if expected_output not in out: if expected_output not in out:
raise(Exception("%s did not print '%s'. Output:\n%s" % ( raise Exception("%s did not print '%s'. Output:\n%s" % (
tool, expected_output, out))) tool, expected_output, out))
now = datetime.datetime.utcnow() now = datetime.datetime.utcnow()
@ -121,9 +121,9 @@ def test_stats():
def expect_stat(port, stat): def expect_stat(port, stat):
url = "http://localhost:%d/metrics" % port url = "http://localhost:%d/metrics" % port
response = requests.get(url) response = requests.get(url)
if not stat in response.text: if not stat in response.content:
print(response.content) print(response.content)
raise(Exception("%s not present in %s" % (stat, url))) raise Exception("%s not present in %s" % (stat, url))
expect_stat(8000, "\nresponse_time_count{") expect_stat(8000, "\nresponse_time_count{")
expect_stat(8000, "\ngo_goroutines ") expect_stat(8000, "\ngo_goroutines ")
expect_stat(8000, '\ngrpc_client_handling_seconds_count{grpc_method="NewRegistration",grpc_service="ra.RegistrationAuthority",grpc_type="unary"} ') expect_stat(8000, '\ngrpc_client_handling_seconds_count{grpc_method="NewRegistration",grpc_service="ra.RegistrationAuthority",grpc_type="unary"} ')
@ -156,7 +156,7 @@ def main():
args = parser.parse_args() args = parser.parse_args()
if not (args.run_all or args.run_certbot or args.run_chisel or args.run_loadtest or args.custom is not None): if not (args.run_all or args.run_certbot or args.run_chisel or args.run_loadtest or args.custom is not None):
raise(Exception("must run at least one of the letsencrypt or chisel tests with --all, --certbot, --chisel, --load or --custom")) raise Exception("must run at least one of the letsencrypt or chisel tests with --all, --certbot, --chisel, --load or --custom")
if not args.skip_setup: if not args.skip_setup:
now = datetime.datetime.utcnow() now = datetime.datetime.utcnow()
@ -169,13 +169,13 @@ def main():
now = datetime.datetime.utcnow() now = datetime.datetime.utcnow()
twenty_days_ago = now+datetime.timedelta(days=-20) twenty_days_ago = now+datetime.timedelta(days=-20)
if not startservers.start(race_detection=True, fakeclock=fakeclock(twenty_days_ago), config_dir=config): if not startservers.start(race_detection=True, fakeclock=fakeclock(twenty_days_ago), config_dir=config):
raise(Exception("startservers failed (mocking twenty days ago)")) raise Exception("startservers failed (mocking twenty days ago)")
v1_integration.caa_client = caa_client = chisel.make_client() v1_integration.caa_client = caa_client = chisel.make_client()
setup_twenty_days_ago() setup_twenty_days_ago()
startservers.stop() startservers.stop()
if not startservers.start(race_detection=True): if not startservers.start(race_detection=True):
raise(Exception("startservers failed")) raise Exception("startservers failed")
if args.run_all or args.run_chisel: if args.run_all or args.run_chisel:
run_chisel(args.test_case_filter) run_chisel(args.test_case_filter)
@ -202,7 +202,7 @@ def main():
run_loadtest() run_loadtest()
if not startservers.check(): if not startservers.check():
raise(Exception("startservers.check failed")) raise Exception("startservers.check failed")
global exit_status global exit_status
exit_status = 0 exit_status = 0
@ -253,7 +253,7 @@ def check_balance():
for address in addresses: for address in addresses:
metrics = requests.get("http://%s/metrics" % address) metrics = requests.get("http://%s/metrics" % address)
if not "grpc_server_handled_total" in metrics.text: if not "grpc_server_handled_total" in metrics.text:
raise(Exception("no gRPC traffic processed by %s; load balancing problem?") raise Exception("no gRPC traffic processed by %s; load balancing problem?"
% address) % address)
def run_cert_checker(): def run_cert_checker():
@ -263,7 +263,7 @@ if __name__ == "__main__":
try: try:
main() main()
except subprocess.CalledProcessError as e: except subprocess.CalledProcessError as e:
raise(Exception("%s. Output:\n%s" % (e, e.output))) raise Exception("%s. Output:\n%s" % (e, e.output))
@atexit.register @atexit.register
def stop(): def stop():

View File

@ -1,4 +1,5 @@
import atexit import atexit
import BaseHTTPServer
import os import os
import shutil import shutil
import signal import signal
@ -101,7 +102,7 @@ def start(race_detection, fakeclock=None, config_dir=default_config_dir):
print(e) print(e)
return False return False
print("All servers running. Hit ^C to kill.") print "All servers running. Hit ^C to kill."
return True return True
def check(): def check():
@ -119,9 +120,9 @@ def check():
else: else:
busted.append(p) busted.append(p)
if busted: if busted:
print("\n\nThese processes exited early (check above for their output):") print "\n\nThese processes exited early (check above for their output):"
for p in busted: for p in busted:
print("\t'%s' with pid %d exited %d" % (p.cmd, p.pid, p.returncode)) print "\t'%s' with pid %d exited %d" % (p.cmd, p.pid, p.returncode)
processes = stillok processes = stillok
return not busted return not busted
@ -132,7 +133,7 @@ def startChallSrv():
""" """
global challSrvProcess global challSrvProcess
if challSrvProcess is not None: if challSrvProcess is not None:
raise(Exception("startChallSrv called more than once")) raise Exception("startChallSrv called more than once")
# NOTE(@cpu): We specify explicit bind addresses for -https01 and # NOTE(@cpu): We specify explicit bind addresses for -https01 and
# --tlsalpn01 here to allow HTTPS HTTP-01 responses on 5001 for on interface # --tlsalpn01 here to allow HTTPS HTTP-01 responses on 5001 for on interface

View File

@ -1,3 +1,4 @@
#!/usr/bin/env python2.7
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import datetime import datetime
import json import json
@ -6,6 +7,7 @@ import random
import re import re
import requests import requests
import time import time
import urllib2
import startservers import startservers
@ -38,7 +40,7 @@ def rand_http_chall(client):
for c in authz.body.challenges: for c in authz.body.challenges:
if isinstance(c.chall, challenges.HTTP01): if isinstance(c.chall, challenges.HTTP01):
return d, c.chall return d, c.chall
raise(Exception("No HTTP-01 challenge found for random domain authz")) raise Exception("No HTTP-01 challenge found for random domain authz")
def test_http_challenge_loop_redirect(): def test_http_challenge_loop_redirect():
client = chisel.make_client() client = chisel.make_client()
@ -156,7 +158,7 @@ def test_http_challenge_http_redirect():
# There should have been at least two GET requests made to the # There should have been at least two GET requests made to the
# challtestsrv. There may have been more if remote VAs were configured. # challtestsrv. There may have been more if remote VAs were configured.
if len(history) < 2: if len(history) < 2:
raise(Exception("Expected at least 2 HTTP request events on challtestsrv, found {1}".format(len(history)))) raise Exception("Expected at least 2 HTTP request events on challtestsrv, found {1}".format(len(history)))
initialRequests = [] initialRequests = []
redirectedRequests = [] redirectedRequests = []
@ -164,7 +166,7 @@ def test_http_challenge_http_redirect():
for request in history: for request in history:
# All requests should have been over HTTP # All requests should have been over HTTP
if request['HTTPS'] is True: if request['HTTPS'] is True:
raise(Exception("Expected all requests to be HTTP")) raise Exception("Expected all requests to be HTTP")
# Initial requests should have the expected initial HTTP-01 URL for the challenge # Initial requests should have the expected initial HTTP-01 URL for the challenge
if request['URL'] == challengePath: if request['URL'] == challengePath:
initialRequests.append(request) initialRequests.append(request)
@ -173,15 +175,15 @@ def test_http_challenge_http_redirect():
elif request['URL'] == redirectPath: elif request['URL'] == redirectPath:
redirectedRequests.append(request) redirectedRequests.append(request)
else: else:
raise(Exception("Unexpected request URL {0} in challtestsrv history: {1}".format(request['URL'], request))) 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. # There should have been at least 1 initial HTTP-01 validation request.
if len(initialRequests) < 1: if len(initialRequests) < 1:
raise(Exception("Expected {0} initial HTTP-01 request events on challtestsrv, found {1}".format(validation_attempts, len(initialRequests)))) 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 # There should have been at least 1 redirected HTTP request for each VA
if len(redirectedRequests) < 1: if len(redirectedRequests) < 1:
raise(Exception("Expected {0} redirected HTTP-01 request events on challtestsrv, found {1}".format(validation_attempts, len(redirectedRequests)))) raise Exception("Expected {0} redirected HTTP-01 request events on challtestsrv, found {1}".format(validation_attempts, len(redirectedRequests)))
def test_http_challenge_https_redirect(): def test_http_challenge_https_redirect():
client = chisel.make_client() client = chisel.make_client()
@ -218,7 +220,7 @@ def test_http_challenge_https_redirect():
# There should have been at least two GET requests made to the challtestsrv by the VA # There should have been at least two GET requests made to the challtestsrv by the VA
if len(history) < 2: if len(history) < 2:
raise(Exception("Expected 2 HTTP request events on challtestsrv, found {0}".format(len(history)))) raise Exception("Expected 2 HTTP request events on challtestsrv, found {0}".format(len(history)))
initialRequests = [] initialRequests = []
redirectedRequests = [] redirectedRequests = []
@ -232,26 +234,26 @@ def test_http_challenge_https_redirect():
elif request['URL'] == redirectPath: elif request['URL'] == redirectPath:
redirectedRequests.append(request) redirectedRequests.append(request)
else: else:
raise(Exception("Unexpected request URL {0} in challtestsrv history: {1}".format(request['URL'], request))) 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. # There should have been at least 1 initial HTTP-01 validation request.
if len(initialRequests) < 1: if len(initialRequests) < 1:
raise(Exception("Expected {0} initial HTTP-01 request events on challtestsrv, found {1}".format(validation_attempts, len(initialRequests)))) 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 # All initial requests should have been over HTTP
for r in initialRequests: for r in initialRequests:
if r['HTTPS'] is True: if r['HTTPS'] is True:
raise(Exception("Expected all initial requests to be HTTP")) raise Exception("Expected all initial requests to be HTTP")
# There should have been at least 1 redirected HTTP request for each VA # There should have been at least 1 redirected HTTP request for each VA
if len(redirectedRequests) < 1: if len(redirectedRequests) < 1:
raise(Exception("Expected {0} redirected HTTP-01 request events on challtestsrv, found {1}".format(validation_attempts, len(redirectedRequests)))) 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 # All the redirected requests should have been over HTTPS with the correct
# SNI value # SNI value
for r in redirectedRequests: for r in redirectedRequests:
if r['HTTPS'] is False: if r['HTTPS'] is False:
raise(Exception("Expected all redirected requests to be HTTPS")) raise Exception("Expected all redirected requests to be HTTPS")
elif r['ServerName'] != d: elif r['ServerName'] != d:
raise(Exception("Expected all redirected requests to have ServerName {0} got \"{1}\"".format(d, r['ServerName']))) raise Exception("Expected all redirected requests to have ServerName {0} got \"{1}\"".format(d, r['ServerName']))
class SlowHTTPRequestHandler(BaseHTTPRequestHandler): class SlowHTTPRequestHandler(BaseHTTPRequestHandler):
def do_GET(self): def do_GET(self):
@ -307,7 +309,7 @@ def test_http_challenge_timeout():
# present the timeout is 20s so adding 2s of padding = 22s) # present the timeout is 20s so adding 2s of padding = 22s)
expectedDuration = 22 expectedDuration = 22
if delta.total_seconds() == 0 or delta.total_seconds() > expectedDuration: if delta.total_seconds() == 0 or delta.total_seconds() > expectedDuration:
raise(Exception("expected timeout to occur in under {0} seconds. Took {1}".format(expectedDuration, delta.total_seconds()))) raise Exception("expected timeout to occur in under {0} seconds. Took {1}".format(expectedDuration, delta.total_seconds()))
def test_tls_alpn_challenge(): def test_tls_alpn_challenge():
# Pick two random domains # Pick two random domains
@ -331,7 +333,7 @@ def test_issuer():
of length exactly 1. of length exactly 1.
""" """
certr, authzs = auth_and_issue([random_domain()]) certr, authzs = auth_and_issue([random_domain()])
cert = requests.get(certr.uri).content cert = urllib2.urlopen(certr.uri).read()
# In the future the chain URI will use HTTPS so include the root certificate # In the future the chain URI will use HTTPS so include the root certificate
# for the WFE's PKI. Note: We use the requests library here so we honor the # for the WFE's PKI. Note: We use the requests library here so we honor the
# REQUESTS_CA_BUNDLE passed by test.sh. # REQUESTS_CA_BUNDLE passed by test.sh.
@ -374,7 +376,7 @@ def test_ct_submission():
def submissions(group): def submissions(group):
count = 0 count = 0
for log in group: for log in group:
count += int(requests.get(log + "?hostnames=%s" % hostname).text) count += int(urllib2.urlopen(log + "?hostnames=%s" % hostname).read())
return count return count
auth_and_issue([hostname]) auth_and_issue([hostname])
@ -384,29 +386,29 @@ def test_ct_submission():
for i in range(len(log_groups)): for i in range(len(log_groups)):
if got[i] < expected[i]: if got[i] < expected[i]:
raise(Exception("For log group %d, got %d submissions, expected %d." % raise Exception("For log group %d, got %d submissions, expected %d." %
(i, got[i], expected[i]))) (i, got[i], expected[i]))
def test_expiration_mailer(): def test_expiration_mailer():
email_addr = "integration.%x@letsencrypt.org" % random.randrange(2**16) email_addr = "integration.%x@letsencrypt.org" % random.randrange(2**16)
cert, _ = auth_and_issue([random_domain()], email=email_addr) cert, _ = auth_and_issue([random_domain()], email=email_addr)
# Check that the expiration mailer sends a reminder # Check that the expiration mailer sends a reminder
expiry = datetime.datetime.strptime(cert.body.get_notAfter().decode(), '%Y%m%d%H%M%SZ') expiry = datetime.datetime.strptime(cert.body.get_notAfter(), '%Y%m%d%H%M%SZ')
no_reminder = expiry + datetime.timedelta(days=-31) no_reminder = expiry + datetime.timedelta(days=-31)
first_reminder = expiry + datetime.timedelta(days=-13) first_reminder = expiry + datetime.timedelta(days=-13)
last_reminder = expiry + datetime.timedelta(days=-2) last_reminder = expiry + datetime.timedelta(days=-2)
requests.post("http://localhost:9381/clear", data='') urllib2.urlopen("http://localhost:9381/clear", data='')
print(get_future_output('./bin/expiration-mailer --config %s/expiration-mailer.json' % print get_future_output('./bin/expiration-mailer --config %s/expiration-mailer.json' %
default_config_dir, no_reminder)) default_config_dir, no_reminder)
print(get_future_output('./bin/expiration-mailer --config %s/expiration-mailer.json' % print get_future_output('./bin/expiration-mailer --config %s/expiration-mailer.json' %
default_config_dir, first_reminder)) default_config_dir, first_reminder)
print(get_future_output('./bin/expiration-mailer --config %s/expiration-mailer.json' % print get_future_output('./bin/expiration-mailer --config %s/expiration-mailer.json' %
default_config_dir, last_reminder)) default_config_dir, last_reminder)
resp = requests.get("http://localhost:9381/count?to=%s" % email_addr) resp = urllib2.urlopen("http://localhost:9381/count?to=%s" % email_addr)
mailcount = int(resp.text) mailcount = int(resp.read())
if mailcount != 2: if mailcount != 2:
raise(Exception("\nExpiry mailer failed: expected 2 emails, got %d" % mailcount)) raise Exception("\nExpiry mailer failed: expected 2 emails, got %d" % mailcount)
def test_revoke_by_account(): def test_revoke_by_account():
client = chisel.make_client() client = chisel.make_client()
@ -442,13 +444,13 @@ def test_recheck_caa():
recheck CAA and reject the request. recheck CAA and reject the request.
""" """
if len(caa_recheck_authzs) == 0: if len(caa_recheck_authzs) == 0:
raise(Exception("CAA authzs not prepared for test_caa")) raise Exception("CAA authzs not prepared for test_caa")
domains = [] domains = []
for a in caa_recheck_authzs: for a in caa_recheck_authzs:
response = requests.get(a.uri) response = requests.get(a.uri)
if response.status_code != 200: if response.status_code != 200:
raise(Exception("Unexpected response for CAA authz: ", raise Exception("Unexpected response for CAA authz: ",
response.status_code)) response.status_code)
domain = a.body.identifier.value domain = a.body.identifier.value
domains.append(domain) domains.append(domain)
challSrv.add_caa_issue(domain, ";") challSrv.add_caa_issue(domain, ";")
@ -516,11 +518,11 @@ def test_account_update():
result = chisel.update_email(client, email=email) result = chisel.update_email(client, email=email)
# We expect one contact in the result # We expect one contact in the result
if len(result.body.contact) != 1: if len(result.body.contact) != 1:
raise(Exception("\nUpdate account failed: expected one contact in result, got 0")) raise Exception("\nUpdate account failed: expected one contact in result, got 0")
# We expect it to be the email we just updated to # We expect it to be the email we just updated to
actual = result.body.contact[0] actual = result.body.contact[0]
if actual != "mailto:"+email: if actual != "mailto:"+email:
raise(Exception("\nUpdate account failed: expected contact %s, got %s" % (email, actual))) raise Exception("\nUpdate account failed: expected contact %s, got %s" % (email, actual))
def test_renewal_exemption(): def test_renewal_exemption():
""" """
@ -576,13 +578,13 @@ def test_admin_revoker_cert():
def test_sct_embedding(): def test_sct_embedding():
certr, authzs = auth_and_issue([random_domain()]) certr, authzs = auth_and_issue([random_domain()])
certBytes = requests.get(certr.uri).content certBytes = urllib2.urlopen(certr.uri).read()
cert = x509.load_der_x509_certificate(certBytes, default_backend()) cert = x509.load_der_x509_certificate(certBytes, default_backend())
# make sure there is no poison extension # make sure there is no poison extension
try: try:
cert.extensions.get_extension_for_oid(x509.ObjectIdentifier("1.3.6.1.4.1.11129.2.4.3")) cert.extensions.get_extension_for_oid(x509.ObjectIdentifier("1.3.6.1.4.1.11129.2.4.3"))
raise(Exception("certificate contains CT poison extension")) raise Exception("certificate contains CT poison extension")
except x509.ExtensionNotFound: except x509.ExtensionNotFound:
# do nothing # do nothing
pass pass
@ -591,15 +593,15 @@ def test_sct_embedding():
try: try:
sctList = cert.extensions.get_extension_for_oid(x509.ObjectIdentifier("1.3.6.1.4.1.11129.2.4.2")) sctList = cert.extensions.get_extension_for_oid(x509.ObjectIdentifier("1.3.6.1.4.1.11129.2.4.2"))
except x509.ExtensionNotFound: except x509.ExtensionNotFound:
raise(Exception("certificate doesn't contain SCT list extension")) raise Exception("certificate doesn't contain SCT list extension")
if len(sctList.value) != 2: if len(sctList.value) != 2:
raise(Exception("SCT list contains wrong number of SCTs")) raise Exception("SCT list contains wrong number of SCTs")
for sct in sctList.value: for sct in sctList.value:
if sct.version != x509.certificate_transparency.Version.v1: if sct.version != x509.certificate_transparency.Version.v1:
raise(Exception("SCT contains wrong version")) raise Exception("SCT contains wrong version")
if sct.entry_type != x509.certificate_transparency.LogEntryType.PRE_CERTIFICATE: if sct.entry_type != x509.certificate_transparency.LogEntryType.PRE_CERTIFICATE:
raise(Exception("SCT contains wrong entry type")) raise Exception("SCT contains wrong entry type")
delta = sct.timestamp - datetime.datetime.now() delta = sct.timestamp - datetime.datetime.now()
if abs(delta) > datetime.timedelta(hours=1): if abs(delta) > datetime.timedelta(hours=1):
raise(Exception("Delta between SCT timestamp and now was too great " raise Exception("Delta between SCT timestamp and now was too great "
"%s vs %s (%s)" % (sct.timestamp, datetime.datetime.now(), delta))) "%s vs %s (%s)" % (sct.timestamp, datetime.datetime.now(), delta))

View File

@ -1,3 +1,4 @@
#!/usr/bin/env python2.7
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
""" """
Integration test cases for ACMEv2 as implemented by boulder-wfe2. Integration test cases for ACMEv2 as implemented by boulder-wfe2.
@ -64,7 +65,7 @@ def rand_http_chall(client):
for c in a.body.challenges: for c in a.body.challenges:
if isinstance(c.chall, challenges.HTTP01): if isinstance(c.chall, challenges.HTTP01):
return d, c.chall return d, c.chall
raise(Exception("No HTTP-01 challenge found for random domain authz")) raise Exception("No HTTP-01 challenge found for random domain authz")
def test_http_challenge_broken_redirect(): def test_http_challenge_broken_redirect():
""" """
@ -97,9 +98,9 @@ def test_http_challenge_broken_redirect():
c = chisel2.get_chall(authzr, challenges.HTTP01) c = chisel2.get_chall(authzr, challenges.HTTP01)
error = c.error error = c.error
if error is None or error.typ != "urn:ietf:params:acme:error:connection": if error is None or error.typ != "urn:ietf:params:acme:error:connection":
raise(Exception("Expected connection prob, got %s" % (error.__str__()))) raise Exception("Expected connection prob, got %s" % (error.__str__()))
if error.detail != expectedError: if error.detail != expectedError:
raise(Exception("Expected prob detail %s, got %s" % (expectedError, error.detail))) raise Exception("Expected prob detail %s, got %s" % (expectedError, error.detail))
challSrv.remove_http_redirect(challengePath) challSrv.remove_http_redirect(challengePath)
@ -217,7 +218,7 @@ def test_http_challenge_http_redirect():
# There should have been at least two GET requests made to the # There should have been at least two GET requests made to the
# challtestsrv. There may have been more if remote VAs were configured. # challtestsrv. There may have been more if remote VAs were configured.
if len(history) < 2: if len(history) < 2:
raise(Exception("Expected at least 2 HTTP request events on challtestsrv, found {1}".format(len(history)))) raise Exception("Expected at least 2 HTTP request events on challtestsrv, found {1}".format(len(history)))
initialRequests = [] initialRequests = []
redirectedRequests = [] redirectedRequests = []
@ -225,7 +226,7 @@ def test_http_challenge_http_redirect():
for request in history: for request in history:
# All requests should have been over HTTP # All requests should have been over HTTP
if request['HTTPS'] is True: if request['HTTPS'] is True:
raise(Exception("Expected all requests to be HTTP")) raise Exception("Expected all requests to be HTTP")
# Initial requests should have the expected initial HTTP-01 URL for the challenge # Initial requests should have the expected initial HTTP-01 URL for the challenge
if request['URL'] == challengePath: if request['URL'] == challengePath:
initialRequests.append(request) initialRequests.append(request)
@ -234,15 +235,15 @@ def test_http_challenge_http_redirect():
elif request['URL'] == redirectPath: elif request['URL'] == redirectPath:
redirectedRequests.append(request) redirectedRequests.append(request)
else: else:
raise(Exception("Unexpected request URL {0} in challtestsrv history: {1}".format(request['URL'], request))) 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. # There should have been at least 1 initial HTTP-01 validation request.
if len(initialRequests) < 1: if len(initialRequests) < 1:
raise(Exception("Expected {0} initial HTTP-01 request events on challtestsrv, found {1}".format(validation_attempts, len(initialRequests)))) 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 # There should have been at least 1 redirected HTTP request for each VA
if len(redirectedRequests) < 1: if len(redirectedRequests) < 1:
raise(Exception("Expected {0} redirected HTTP-01 request events on challtestsrv, found {1}".format(validation_attempts, len(redirectedRequests)))) raise Exception("Expected {0} redirected HTTP-01 request events on challtestsrv, found {1}".format(validation_attempts, len(redirectedRequests)))
def test_http_challenge_https_redirect(): def test_http_challenge_https_redirect():
client = chisel2.make_client() client = chisel2.make_client()
@ -277,7 +278,7 @@ def test_http_challenge_https_redirect():
error = chall.error error = chall.error
if error: if error:
problems.append(error.__str__()) problems.append(error.__str__())
raise(Exception("validation problem: %s" % "; ".join(problems))) raise Exception("validation problem: %s" % "; ".join(problems))
challSrv.remove_http_redirect(challengePath) challSrv.remove_http_redirect(challengePath)
challSrv.remove_a_record(d) challSrv.remove_a_record(d)
@ -287,7 +288,7 @@ def test_http_challenge_https_redirect():
# There should have been at least two GET requests made to the challtestsrv by the VA # There should have been at least two GET requests made to the challtestsrv by the VA
if len(history) < 2: if len(history) < 2:
raise(Exception("Expected 2 HTTP request events on challtestsrv, found {0}".format(len(history)))) raise Exception("Expected 2 HTTP request events on challtestsrv, found {0}".format(len(history)))
initialRequests = [] initialRequests = []
redirectedRequests = [] redirectedRequests = []
@ -301,30 +302,30 @@ def test_http_challenge_https_redirect():
elif request['URL'] == redirectPath: elif request['URL'] == redirectPath:
redirectedRequests.append(request) redirectedRequests.append(request)
else: else:
raise(Exception("Unexpected request URL {0} in challtestsrv history: {1}".format(request['URL'], request))) 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. # There should have been at least 1 initial HTTP-01 validation request.
if len(initialRequests) < 1: if len(initialRequests) < 1:
raise(Exception("Expected {0} initial HTTP-01 request events on challtestsrv, found {1}".format(validation_attempts, len(initialRequests)))) 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 # All initial requests should have been over HTTP
for r in initialRequests: for r in initialRequests:
if r['HTTPS'] is True: if r['HTTPS'] is True:
raise(Exception("Expected all initial requests to be HTTP, got %s" % r)) 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 # There should have been at least 1 redirected HTTP request for each VA
if len(redirectedRequests) < 1: if len(redirectedRequests) < 1:
raise(Exception("Expected {0} redirected HTTP-01 request events on challtestsrv, found {1}".format(validation_attempts, len(redirectedRequests)))) 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 # All the redirected requests should have been over HTTPS with the correct
# SNI value # SNI value
for r in redirectedRequests: for r in redirectedRequests:
if r['HTTPS'] is False: if r['HTTPS'] is False:
raise(Exception("Expected all redirected requests to be HTTPS")) raise Exception("Expected all redirected requests to be HTTPS")
# TODO(@cpu): The following ServerName test will fail with config-next # TODO(@cpu): The following ServerName test will fail with config-next
# until https://github.com/letsencrypt/boulder/issues/3969 is fixed. # until https://github.com/letsencrypt/boulder/issues/3969 is fixed.
if CONFIG_NEXT: if CONFIG_NEXT:
return return
elif r['ServerName'] != d: elif r['ServerName'] != d:
raise(Exception("Expected all redirected requests to have ServerName {0} got \"{1}\"".format(d, r['ServerName']))) raise Exception("Expected all redirected requests to have ServerName {0} got \"{1}\"".format(d, r['ServerName']))
def test_tls_alpn_challenge(): def test_tls_alpn_challenge():
# Pick two random domains # Pick two random domains
@ -353,8 +354,8 @@ def test_overlapping_wildcard():
authzs = order.authorizations authzs = order.authorizations
if len(authzs) != 2: if len(authzs) != 2:
raise(Exception("order for %s had %d authorizations, expected 2" % raise Exception("order for %s had %d authorizations, expected 2" %
(domains, len(authzs)))) (domains, len(authzs)))
cleanup = chisel2.do_dns_challenges(client, authzs) cleanup = chisel2.do_dns_challenges(client, authzs)
try: try:
@ -419,8 +420,8 @@ def test_wildcard_authz_reuse():
# We expect all of the returned authorizations to be pending status # We expect all of the returned authorizations to be pending status
for authz in order.authorizations: for authz in order.authorizations:
if authz.body.status != Status("pending"): if authz.body.status != Status("pending"):
raise(Exception("order for %s included a non-pending authorization (status: %s) from a previous HTTP-01 order" % raise Exception("order for %s included a non-pending authorization (status: %s) from a previous HTTP-01 order" %
((domains), str(authz.body.status)))) ((domains), str(authz.body.status)))
def test_bad_overlap_wildcard(): def test_bad_overlap_wildcard():
chisel2.expect_problem("urn:ietf:params:acme:error:malformed", chisel2.expect_problem("urn:ietf:params:acme:error:malformed",
@ -469,20 +470,20 @@ def test_order_reuse_failed_authz():
# If the poll ended and an authz's status isn't invalid then we reached the # If the poll ended and an authz's status isn't invalid then we reached the
# deadline, fail the test # deadline, fail the test
if not authzFailed: if not authzFailed:
raise(Exception("timed out waiting for order %s to become invalid" % firstOrderURI)) raise Exception("timed out waiting for order %s to become invalid" % firstOrderURI)
# Make another order with the same domains # Make another order with the same domains
order = client.new_order(csr_pem) order = client.new_order(csr_pem)
# It should not be the same order as before # It should not be the same order as before
if order.uri == firstOrderURI: if order.uri == firstOrderURI:
raise(Exception("new-order for %s returned a , now-invalid, order" % domains)) raise Exception("new-order for %s returned a , now-invalid, order" % domains)
# We expect all of the returned authorizations to be pending status # We expect all of the returned authorizations to be pending status
for authz in order.authorizations: for authz in order.authorizations:
if authz.body.status != Status("pending"): if authz.body.status != Status("pending"):
raise(Exception("order for %s included a non-pending authorization (status: %s) from a previous order" % raise Exception("order for %s included a non-pending authorization (status: %s) from a previous order" %
((domains), str(authz.body.status)))) ((domains), str(authz.body.status)))
# We expect the new order can be fulfilled # We expect the new order can be fulfilled
cleanup = chisel2.do_http_challenges(client, order.authorizations) cleanup = chisel2.do_http_challenges(client, order.authorizations)
@ -583,13 +584,12 @@ def test_revoke_by_privkey():
def test_sct_embedding(): def test_sct_embedding():
order = chisel2.auth_and_issue([random_domain()]) order = chisel2.auth_and_issue([random_domain()])
print(order.fullchain_pem.encode()) cert = x509.load_pem_x509_certificate(str(order.fullchain_pem), default_backend())
cert = x509.load_pem_x509_certificate(order.fullchain_pem.encode(), default_backend())
# make sure there is no poison extension # make sure there is no poison extension
try: try:
cert.extensions.get_extension_for_oid(x509.ObjectIdentifier("1.3.6.1.4.1.11129.2.4.3")) cert.extensions.get_extension_for_oid(x509.ObjectIdentifier("1.3.6.1.4.1.11129.2.4.3"))
raise(Exception("certificate contains CT poison extension")) raise Exception("certificate contains CT poison extension")
except x509.ExtensionNotFound: except x509.ExtensionNotFound:
# do nothing # do nothing
pass pass
@ -598,14 +598,14 @@ def test_sct_embedding():
try: try:
sctList = cert.extensions.get_extension_for_oid(x509.ObjectIdentifier("1.3.6.1.4.1.11129.2.4.2")) sctList = cert.extensions.get_extension_for_oid(x509.ObjectIdentifier("1.3.6.1.4.1.11129.2.4.2"))
except x509.ExtensionNotFound: except x509.ExtensionNotFound:
raise(Exception("certificate doesn't contain SCT list extension")) raise Exception("certificate doesn't contain SCT list extension")
if len(sctList.value) != 2: if len(sctList.value) != 2:
raise(Exception("SCT list contains wrong number of SCTs")) raise Exception("SCT list contains wrong number of SCTs")
for sct in sctList.value: for sct in sctList.value:
if sct.version != x509.certificate_transparency.Version.v1: if sct.version != x509.certificate_transparency.Version.v1:
raise(Exception("SCT contains wrong version")) raise Exception("SCT contains wrong version")
if sct.entry_type != x509.certificate_transparency.LogEntryType.PRE_CERTIFICATE: if sct.entry_type != x509.certificate_transparency.LogEntryType.PRE_CERTIFICATE:
raise(Exception("SCT contains wrong entry type")) raise Exception("SCT contains wrong entry type")
def test_only_return_existing_reg(): def test_only_return_existing_reg():
client = chisel2.uninitialized_client() client = chisel2.uninitialized_client()
@ -624,7 +624,7 @@ def test_only_return_existing_reg():
}) })
resp = client.net.post(client.directory['newAccount'], acct, acme_version=2) resp = client.net.post(client.directory['newAccount'], acct, acme_version=2)
if resp.status_code != 200: if resp.status_code != 200:
raise(Exception("incorrect response returned for onlyReturnExisting")) raise Exception("incorrect response returned for onlyReturnExisting")
other_client = chisel2.uninitialized_client() other_client = chisel2.uninitialized_client()
newAcct = extendedAcct({ newAcct = extendedAcct({
@ -671,7 +671,7 @@ def BouncerHTTPRequestHandler(redirect, guestlist):
self.log_message("BouncerHandler UA {0} has no requests on the Guestlist. Sending request to the curb".format(ua)) self.log_message("BouncerHandler UA {0} has no requests on the Guestlist. Sending request to the curb".format(ua))
self.send_response(200) self.send_response(200)
self.end_headers() self.end_headers()
self.wfile.write(u'(• ◡ •) <( VIPs only! )') self.wfile.write(b'(• ◡ •) <( VIPs only! )')
BouncerHandler.guestlist = guestlist BouncerHandler.guestlist = guestlist
BouncerHandler.redirect = redirect BouncerHandler.redirect = redirect
@ -707,7 +707,7 @@ def multiva_setup(client, guestlist):
if isinstance(c.chall, challenges.HTTP01): if isinstance(c.chall, challenges.HTTP01):
chall = c.chall chall = c.chall
if chall is None: if chall is None:
raise(Exception("No HTTP-01 challenge found for random domain authz")) raise Exception("No HTTP-01 challenge found for random domain authz")
token = chall.encode("token") token = chall.encode("token")
@ -791,16 +791,16 @@ def test_http_multiva_threshold_fail():
# test needs to unpack an `acme_errors.ValidationError` on its own. It # test needs to unpack an `acme_errors.ValidationError` on its own. It
# might be possible to clean this up in the future. # might be possible to clean this up in the future.
if len(e.failed_authzrs) != 1: if len(e.failed_authzrs) != 1:
raise(Exception("expected one failed authz, found {0}".format(len(e.failed_authzrs)))) raise Exception("expected one failed authz, found {0}".format(len(e.failed_authzrs)))
challs = e.failed_authzrs[0].body.challenges challs = e.failed_authzrs[0].body.challenges
httpChall = None httpChall = None
for chall_body in challs: for chall_body in challs:
if isinstance(chall_body.chall, challenges.HTTP01): if isinstance(chall_body.chall, challenges.HTTP01):
httpChall = chall_body httpChall = chall_body
if httpChall is None: if httpChall is None:
raise(Exception("no HTTP-01 challenge in failed authz")) raise Exception("no HTTP-01 challenge in failed authz")
if httpChall.error.typ != "urn:ietf:params:acme:error:unauthorized": if httpChall.error.typ != "urn:ietf:params:acme:error:unauthorized":
raise(Exception("expected unauthorized prob, found {0}".format(httpChall.error.typ))) raise Exception("expected unauthorized prob, found {0}".format(httpChall.error.typ))
finally: finally:
cleanup() cleanup()
@ -827,7 +827,7 @@ def wait_for_tcp_server(addr, port):
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try: try:
sock.connect((addr, port)) sock.connect((addr, port))
sock.sendall(b"\n") sock.sendall("\n")
return return
except socket.error: except socket.error:
time.sleep(0.5) time.sleep(0.5)
@ -869,9 +869,9 @@ def test_http2_http01_challenge():
c = chisel2.get_chall(authzr, challenges.HTTP01) c = chisel2.get_chall(authzr, challenges.HTTP01)
error = c.error error = c.error
if error is None or error.typ != "urn:ietf:params:acme:error:connection": if error is None or error.typ != "urn:ietf:params:acme:error:connection":
raise(Exception("Expected connection prob, got %s" % (error.__str__()))) raise Exception("Expected connection prob, got %s" % (error.__str__()))
if not error.detail.endswith(expectedError): if not error.detail.endswith(expectedError):
raise(Exception("Expected prob detail ending in %s, got %s" % (expectedError, error.detail))) raise Exception("Expected prob detail ending in %s, got %s" % (expectedError, error.detail))
finally: finally:
server.shutdown() server.shutdown()
server.server_close() server.server_close()
@ -908,7 +908,7 @@ def test_z1_reuse():
if a.uri in authz_uris: if a.uri in authz_uris:
authz_uris.remove(a.uri) authz_uris.remove(a.uri)
if len(authz_uris) != 0: if len(authz_uris) != 0:
raise(Exception("Failed to reuse all authzs. Remaining: %s" % authz_uris)) raise Exception("Failed to reuse all authzs. Remaining: %s" % authz_uris)
def test_new_order_policy_errs(): def test_new_order_policy_errs():
""" """
@ -938,15 +938,18 @@ def test_new_order_policy_errs():
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.)': 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')) raise(Exception('Order problem detail did not match expected'))
if not ok: if not ok:
raise(Exception('Expected problem, got no error')) raise Exception('Expected problem, got no error')
def test_long_san_no_cn(): def test_long_san_no_cn():
try: try:
chisel2.auth_and_issue([''.join(random.choice(string.ascii_uppercase) for x in range(61)) + ".com"]) chisel2.auth_and_issue([''.join(random.choice(string.ascii_uppercase) for x in range(61)) + ".com"])
# if we get to this raise(the auth_and_issue call didn't fail, so fail the test) # if we get to this raise the auth_and_issue call didn't fail, so fail the test
raise(Exception("Issuance didn't fail when the only SAN in a certificate was longer than the max CN length")) raise Exception("Issuance didn't fail when the only SAN in a certificate was longer than the max CN length")
except messages.Error as e: except messages.Error as e:
if e.typ != "urn:ietf:params:acme:error:malformed": if e.typ != "urn:ietf:params:acme:error:malformed":
raise(Exception('Expected malformed type problem, got {0}'.format(e.typ))) raise Exception('Expected malformed type problem, got {0}'.format(e.typ))
if e.detail != 'Error finalizing order :: issuing precertificate: CSR doesn\'t contain a SAN short enough to fit in CN': if e.detail != 'Error finalizing order :: issuing precertificate: CSR doesn\'t contain a SAN short enough to fit in CN':
raise(Exception('Problem detail did not match expected')) raise Exception('Problem detail did not match expected')
def run(cmd, **kwargs):
return subprocess.check_output(cmd, shell=True, stderr=subprocess.STDOUT, **kwargs)