Integration test for ACMEv2 (#3298)

This commit is contained in:
Jacob Hoffman-Andrews 2017-12-21 10:27:32 -08:00 committed by Daniel McCarney
parent bdad6ddc4e
commit b369818ad6
4 changed files with 79 additions and 15 deletions

View File

@ -39,6 +39,7 @@ matrix:
- env: RUN="unit"
- env: RUN="unit-next" BOULDER_CONFIG_DIR="test/config-next"
- env: RUN="coverage"
- env: RUN="acme-v2" BOULDER_CONFIG_DIR="test/config-next"
fast_finish: true
allow_failures:
- env: RUN="coverage"

12
test.sh
View File

@ -188,6 +188,18 @@ if [[ "$RUN" =~ "integration" ]] ; then
end_context #integration
fi
if [[ "$RUN" =~ "acme-v2" ]] ; then
# If you're developing against a local Certbot repo, edit docker-compose.yml
# to mount it as a volume under /certbot, and run tests with
# docker-compose run -e RUN=acme-v2 -e CERTBOT_REPO=/certbot boulder ./test.sh
CERTBOT_REPO=${CERTBOT_REPO:-https://github.com/certbot/certbot}
CERTBOT_DIR=$(mktemp -d -t certbotXXXX)
git clone $CERTBOT_REPO $CERTBOT_DIR
(cd $CERTBOT_DIR ; git checkout acme-v2-integration; ./tools/venv.sh)
source $CERTBOT_DIR/venv/bin/activate
REQUESTS_CA_BUNDLE=test/wfe.pem DIRECTORY=https://boulder:4431/directory run python2 test/integration-test-v2.py
fi
# Run godep-restore (happens only in Travis) to check that the hashes in
# Godeps.json really exist in the remote repo and match what we have.
if [[ "$RUN" =~ "godep-restore" ]] ; then

View File

@ -23,11 +23,12 @@ from cryptography import x509
from cryptography.hazmat.primitives import hashes
import OpenSSL
import josepy
from acme import challenges
from acme import client as acme_client
from acme import crypto_util as acme_crypto_util
from acme import errors as acme_errors
from acme import jose
from acme import messages
from acme import standalone
@ -36,17 +37,18 @@ logger = logging.getLogger()
logger.setLevel(int(os.getenv('LOGLEVEL', 0)))
DIRECTORY = os.getenv('DIRECTORY', 'http://localhost:4001/directory')
ACCEPTABLE_TOS = "https://boulder:4431/terms/v7"
def make_client(email=None):
"""Build an acme.Client and register a new account with a random key."""
key = jose.JWKRSA(key=rsa.generate_private_key(65537, 2048, default_backend()))
key = josepy.JWKRSA(key=rsa.generate_private_key(65537, 2048, default_backend()))
net = acme_client.ClientNetwork(key, verify_ssl=False,
net = acme_client.ClientNetwork(key, acme_version=2,
user_agent="Boulder integration tester")
client = acme_client.Client(DIRECTORY, key=key, net=net)
client = acme_client.Client(DIRECTORY, key=key, net=net, acme_version=2)
tos = client.directory.meta.terms_of_service
if tos is not None and "Do%20what%20thou%20wilt" in tos:
if tos == ACCEPTABLE_TOS:
net.account = client.register(messages.NewRegistration.from_data(email=email,
terms_of_service_agreed=True))
else:
@ -70,10 +72,10 @@ class ValidationError(Exception):
return "%s: %s: %s" % (self.domain, self.problem_type, self.detail)
def make_csr(domains):
key = rsa.generate_private_key(public_exponent=65537, key_size=2048, backend=default_backend())
return x509.CertificateSigningRequestBuilder().add_extension(
x509.SubjectAlternativeName([x509.DNSName(d) for d in domains], critical=False)
).sign(key, hashes.SHA256(), default_backend()).public_bytes(serialization.Encoding.PEM)
key = OpenSSL.crypto.PKey()
key.generate_key(OpenSSL.crypto.TYPE_RSA, 2048)
pem = OpenSSL.crypto.dump_privatekey(OpenSSL.crypto.FILETYPE_PEM, key)
return acme_crypto_util.make_csr(pem, domains, False)
def issue(client, authzs, cert_output=None):
"""Given a list of authzs that are being processed by the server,
@ -129,12 +131,7 @@ def auth_and_issue(domains, chall_type="http-01", email=None, cert_output=None,
raise Exception("invalid challenge type %s" % chall_type)
try:
while True:
order, response = client.poll_order(order)
print order.to_json()
if order.body.status != "pending":
break
time.sleep(1)
order = client.poll_order_and_request_issuance(order)
finally:
cleanup()

View File

@ -0,0 +1,54 @@
#!/usr/bin/env python2.7
"""
Integration test for ACMEv2 as implemented by boulder-wfe2.
Currently (December 2017) this depends on the acme-v2-integration branch of
Certbot, while we wait on landing some of our changes in master.
"""
import atexit
import random
import shutil
import subprocess
import tempfile
import startservers
import chisel2
from chisel2 import auth_and_issue
exit_status = 1
tempdir = tempfile.mkdtemp()
def random_domain():
"""Generate a random domain for testing (to avoid rate limiting)."""
return "rand.%x.xyz" % random.randrange(2**32)
def main():
if not startservers.start(race_detection=True):
raise Exception("startservers failed")
test_multidomain()
if not startservers.check():
raise Exception("startservers.check failed")
global exit_status
exit_status = 0
def test_multidomain():
auth_and_issue([random_domain(), random_domain()])
if __name__ == "__main__":
try:
main()
except subprocess.CalledProcessError as e:
raise Exception("%s. Output:\n%s" % (e, e.output))
@atexit.register
def stop():
import shutil
shutil.rmtree(tempdir)
if exit_status == 0:
print("\n\nSUCCESS")
else:
print("\n\nFAILURE")