diff -Nru acme-tiny-20171115/acme_tiny.egg-info/dependency_links.txt acme-tiny-4.0.4/acme_tiny.egg-info/dependency_links.txt --- acme-tiny-20171115/acme_tiny.egg-info/dependency_links.txt 1970-01-01 01:00:00.000000000 +0100 +++ acme-tiny-4.0.4/acme_tiny.egg-info/dependency_links.txt 2018-05-17 13:49:36.000000000 +0100 @@ -0,0 +1 @@ + diff -Nru acme-tiny-20171115/acme_tiny.egg-info/entry_points.txt acme-tiny-4.0.4/acme_tiny.egg-info/entry_points.txt --- acme-tiny-20171115/acme_tiny.egg-info/entry_points.txt 1970-01-01 01:00:00.000000000 +0100 +++ acme-tiny-4.0.4/acme_tiny.egg-info/entry_points.txt 2018-05-17 13:49:36.000000000 +0100 @@ -0,0 +1,3 @@ +[console_scripts] +acme-tiny = acme_tiny:main + diff -Nru acme-tiny-20171115/acme_tiny.egg-info/PKG-INFO acme-tiny-4.0.4/acme_tiny.egg-info/PKG-INFO --- acme-tiny-20171115/acme_tiny.egg-info/PKG-INFO 1970-01-01 01:00:00.000000000 +0100 +++ acme-tiny-4.0.4/acme_tiny.egg-info/PKG-INFO 2018-05-17 13:49:36.000000000 +0100 @@ -0,0 +1,22 @@ +Metadata-Version: 1.1 +Name: acme-tiny +Version: 4.0.4 +Summary: A tiny script to issue and renew TLS certs from Let's Encrypt +Home-page: https://github.com/diafygi/acme-tiny +Author: Daniel Roesler +Author-email: diafygi@gmail.com +License: MIT +Description: UNKNOWN +Platform: UNKNOWN +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: System Administrators +Classifier: License :: OSI Approved :: MIT License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 2 +Classifier: Programming Language :: Python :: 2.7 +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.3 +Classifier: Programming Language :: Python :: 3.4 +Classifier: Programming Language :: Python :: 3.5 +Classifier: Programming Language :: Python :: 3.6 diff -Nru acme-tiny-20171115/acme_tiny.egg-info/SOURCES.txt acme-tiny-4.0.4/acme_tiny.egg-info/SOURCES.txt --- acme-tiny-20171115/acme_tiny.egg-info/SOURCES.txt 1970-01-01 01:00:00.000000000 +0100 +++ acme-tiny-4.0.4/acme_tiny.egg-info/SOURCES.txt 2018-05-17 13:49:36.000000000 +0100 @@ -0,0 +1,19 @@ +.gitignore +.travis.yml +LICENSE +README.md +acme_tiny.py +setup.cfg +setup.py +acme_tiny.egg-info/PKG-INFO +acme_tiny.egg-info/SOURCES.txt +acme_tiny.egg-info/dependency_links.txt +acme_tiny.egg-info/entry_points.txt +acme_tiny.egg-info/top_level.txt +tests/README.md +tests/__init__.py +tests/monkey.py +tests/requirements.txt +tests/server.py +tests/test_install.py +tests/test_module.py \ No newline at end of file diff -Nru acme-tiny-20171115/acme_tiny.egg-info/top_level.txt acme-tiny-4.0.4/acme_tiny.egg-info/top_level.txt --- acme-tiny-20171115/acme_tiny.egg-info/top_level.txt 1970-01-01 01:00:00.000000000 +0100 +++ acme-tiny-4.0.4/acme_tiny.egg-info/top_level.txt 2018-05-17 13:49:36.000000000 +0100 @@ -0,0 +1 @@ +acme_tiny diff -Nru acme-tiny-20171115/acme_tiny.py acme-tiny-4.0.4/acme_tiny.py --- acme-tiny-20171115/acme_tiny.py 2017-11-15 05:30:52.000000000 +0000 +++ acme-tiny-4.0.4/acme_tiny.py 2018-05-17 13:43:30.000000000 +0100 @@ -1,73 +1,94 @@ #!/usr/bin/env python +# Copyright Daniel Roesler, under MIT license, see LICENSE at github.com/diafygi/acme-tiny import argparse, subprocess, json, os, sys, base64, binascii, time, hashlib, re, copy, textwrap, logging try: - from urllib.request import urlopen # Python 3 + from urllib.request import urlopen, Request # Python 3 except ImportError: - from urllib2 import urlopen # Python 2 + from urllib2 import urlopen, Request # Python 2 -#DEFAULT_CA = "https://acme-staging.api.letsencrypt.org" -DEFAULT_CA = "https://acme-v01.api.letsencrypt.org" +DEFAULT_CA = "https://acme-v02.api.letsencrypt.org" # DEPRECATED! USE DEFAULT_DIRECTORY_URL INSTEAD +DEFAULT_DIRECTORY_URL = "https://acme-v02.api.letsencrypt.org/directory" LOGGER = logging.getLogger(__name__) LOGGER.addHandler(logging.StreamHandler()) LOGGER.setLevel(logging.INFO) -def get_crt(account_key, csr, acme_dir, log=LOGGER, CA=DEFAULT_CA): - # helper function base64 encode for jose spec +def get_crt(account_key, csr, acme_dir, log=LOGGER, CA=DEFAULT_CA, disable_check=False, directory_url=DEFAULT_DIRECTORY_URL, contact=None): + directory, acct_headers, alg, jwk = None, None, None, None # global variables + + # helper functions - base64 encode for jose spec def _b64(b): return base64.urlsafe_b64encode(b).decode('utf8').replace("=", "") + # helper function - run external commands + def _cmd(cmd_list, stdin=None, cmd_input=None, err_msg="Command Line Error"): + proc = subprocess.Popen(cmd_list, stdin=stdin, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + out, err = proc.communicate(cmd_input) + if proc.returncode != 0: + raise IOError("{0}\n{1}".format(err_msg, err)) + return out + + # helper function - make request and automatically parse json response + def _do_request(url, data=None, err_msg="Error", depth=0): + try: + resp = urlopen(Request(url, data=data, headers={"Content-Type": "application/jose+json", "User-Agent": "acme-tiny"})) + resp_data, code, headers = resp.read().decode("utf8"), resp.getcode(), resp.headers + except IOError as e: + resp_data = e.read().decode("utf8") if hasattr(e, "read") else str(e) + code, headers = getattr(e, "code", None), {} + try: + resp_data = json.loads(resp_data) # try to parse json results + except ValueError: + pass # ignore json parsing errors + if depth < 100 and code == 400 and resp_data['type'] == "urn:ietf:params:acme:error:badNonce": + raise IndexError(resp_data) # allow 100 retrys for bad nonces + if code not in [200, 201, 204]: + raise ValueError("{0}:\nUrl: {1}\nData: {2}\nResponse Code: {3}\nResponse: {4}".format(err_msg, url, data, code, resp_data)) + return resp_data, code, headers + + # helper function - make signed requests + def _send_signed_request(url, payload, err_msg, depth=0): + payload64 = _b64(json.dumps(payload).encode('utf8')) + new_nonce = _do_request(directory['newNonce'])[2]['Replay-Nonce'] + protected = {"url": url, "alg": alg, "nonce": new_nonce} + protected.update({"jwk": jwk} if acct_headers is None else {"kid": acct_headers['Location']}) + protected64 = _b64(json.dumps(protected).encode('utf8')) + protected_input = "{0}.{1}".format(protected64, payload64).encode('utf8') + out = _cmd(["openssl", "dgst", "-sha256", "-sign", account_key], stdin=subprocess.PIPE, cmd_input=protected_input, err_msg="OpenSSL Error") + data = json.dumps({"protected": protected64, "payload": payload64, "signature": _b64(out)}) + try: + return _do_request(url, data=data.encode('utf8'), err_msg=err_msg, depth=depth) + except IndexError: # retry bad nonces (they raise IndexError) + return _send_signed_request(url, payload, err_msg, depth=(depth + 1)) + + # helper function - poll until complete + def _poll_until_not(url, pending_statuses, err_msg): + while True: + result, _, _ = _do_request(url, err_msg=err_msg) + if result['status'] in pending_statuses: + time.sleep(2) + continue + return result + # parse account key to get public key log.info("Parsing account key...") - proc = subprocess.Popen(["openssl", "rsa", "-in", account_key, "-noout", "-text"], - stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - out, err = proc.communicate() - if proc.returncode != 0: - raise IOError("OpenSSL Error: {0}".format(err)) - pub_hex, pub_exp = re.search( - r"modulus:\n\s+00:([a-f0-9\:\s]+?)\npublicExponent: ([0-9]+)", - out.decode('utf8'), re.MULTILINE|re.DOTALL).groups() + out = _cmd(["openssl", "rsa", "-in", account_key, "-noout", "-text"], err_msg="OpenSSL Error") + pub_pattern = r"modulus:\n\s+00:([a-f0-9\:\s]+?)\npublicExponent: ([0-9]+)" + pub_hex, pub_exp = re.search(pub_pattern, out.decode('utf8'), re.MULTILINE|re.DOTALL).groups() pub_exp = "{0:x}".format(int(pub_exp)) pub_exp = "0{0}".format(pub_exp) if len(pub_exp) % 2 else pub_exp - header = { - "alg": "RS256", - "jwk": { - "e": _b64(binascii.unhexlify(pub_exp.encode("utf-8"))), - "kty": "RSA", - "n": _b64(binascii.unhexlify(re.sub(r"(\s|:)", "", pub_hex).encode("utf-8"))), - }, + alg = "RS256" + jwk = { + "e": _b64(binascii.unhexlify(pub_exp.encode("utf-8"))), + "kty": "RSA", + "n": _b64(binascii.unhexlify(re.sub(r"(\s|:)", "", pub_hex).encode("utf-8"))), } - accountkey_json = json.dumps(header['jwk'], sort_keys=True, separators=(',', ':')) + accountkey_json = json.dumps(jwk, sort_keys=True, separators=(',', ':')) thumbprint = _b64(hashlib.sha256(accountkey_json.encode('utf8')).digest()) - # helper function make signed requests - def _send_signed_request(url, payload): - payload64 = _b64(json.dumps(payload).encode('utf8')) - protected = copy.deepcopy(header) - protected["nonce"] = urlopen(CA + "/directory").headers['Replay-Nonce'] - protected64 = _b64(json.dumps(protected).encode('utf8')) - proc = subprocess.Popen(["openssl", "dgst", "-sha256", "-sign", account_key], - stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - out, err = proc.communicate("{0}.{1}".format(protected64, payload64).encode('utf8')) - if proc.returncode != 0: - raise IOError("OpenSSL Error: {0}".format(err)) - data = json.dumps({ - "header": header, "protected": protected64, - "payload": payload64, "signature": _b64(out), - }) - try: - resp = urlopen(url, data.encode('utf8')) - return resp.getcode(), resp.read() - except IOError as e: - return getattr(e, "code", None), getattr(e, "read", e.__str__)() - # find domains log.info("Parsing CSR...") - proc = subprocess.Popen(["openssl", "req", "-in", csr, "-noout", "-text"], - stdout=subprocess.PIPE, stderr=subprocess.PIPE) - out, err = proc.communicate() - if proc.returncode != 0: - raise IOError("Error loading {0}: {1}".format(csr, err)) + out = _cmd(["openssl", "req", "-in", csr, "-noout", "-text"], err_msg="Error loading {0}".format(csr)) domains = set([]) common_name = re.search(r"Subject:.*? CN\s?=\s?([^\s,;/]+)", out.decode('utf8')) if common_name is not None: @@ -77,34 +98,37 @@ for san in subject_alt_names.group(1).split(", "): if san.startswith("DNS:"): domains.add(san[4:]) + log.info("Found domains: {0}".format(", ".join(domains))) - # get the certificate domains and expiration - log.info("Registering account...") - code, result = _send_signed_request(CA + "/acme/new-reg", { - "resource": "new-reg", - "agreement": json.loads(urlopen(CA + "/directory").read().decode('utf8'))['meta']['terms-of-service'], - }) - if code == 201: - log.info("Registered!") - elif code == 409: - log.info("Already registered!") - else: - raise ValueError("Error registering: {0} {1}".format(code, result)) + # get the ACME directory of urls + log.info("Getting directory...") + directory_url = CA + "/directory" if CA != DEFAULT_CA else directory_url # backwards compatibility with deprecated CA kwarg + directory, _, _ = _do_request(directory_url, err_msg="Error getting directory") + log.info("Directory found!") - # verify each domain - for domain in domains: + # create account, update contact details (if any), and set the global key identifier + log.info("Registering account...") + reg_payload = {"termsOfServiceAgreed": True} + account, code, acct_headers = _send_signed_request(directory['newAccount'], reg_payload, "Error registering") + log.info("Registered!" if code == 201 else "Already registered!") + if contact is not None: + account, _, _ = _send_signed_request(acct_headers['Location'], {"contact": contact}, "Error updating contact details") + log.info("Updated contact details:\n{0}".format("\n".join(account['contact']))) + + # create a new order + log.info("Creating new order...") + order_payload = {"identifiers": [{"type": "dns", "value": d} for d in domains]} + order, _, order_headers = _send_signed_request(directory['newOrder'], order_payload, "Error creating new order") + log.info("Order created!") + + # get the authorizations that need to be completed + for auth_url in order['authorizations']: + authorization, _, _ = _do_request(auth_url, err_msg="Error getting challenges") + domain = authorization['identifier']['value'] log.info("Verifying {0}...".format(domain)) - # get new challenge - code, result = _send_signed_request(CA + "/acme/new-authz", { - "resource": "new-authz", - "identifier": {"type": "dns", "value": domain}, - }) - if code != 201: - raise ValueError("Error requesting challenges: {0} {1}".format(code, result)) - - # make the challenge file - challenge = [c for c in json.loads(result.decode('utf8'))['challenges'] if c['type'] == "http-01"][0] + # find the http-01 challenge and write the challenge file + challenge = [c for c in authorization['challenges'] if c['type'] == "http-01"][0] token = re.sub(r"[^A-Za-z0-9_\-]", "_", challenge['token']) keyauthorization = "{0}.{1}".format(token, thumbprint) wellknown_path = os.path.join(acme_dir, token) @@ -112,86 +136,62 @@ wellknown_file.write(keyauthorization) # check that the file is in place - wellknown_url = "http://{0}/.well-known/acme-challenge/{1}".format(domain, token) try: - resp = urlopen(wellknown_url) - resp_data = resp.read().decode('utf8').strip() - assert resp_data == keyauthorization - except (IOError, AssertionError): + wellknown_url = "http://{0}/.well-known/acme-challenge/{1}".format(domain, token) + assert(disable_check or _do_request(wellknown_url)[0] == keyauthorization) + except (AssertionError, ValueError) as e: os.remove(wellknown_path) - raise ValueError("Wrote file to {0}, but couldn't download {1}".format( - wellknown_path, wellknown_url)) - - # notify challenge are met - code, result = _send_signed_request(challenge['uri'], { - "resource": "challenge", - "keyAuthorization": keyauthorization, - }) - if code != 202: - raise ValueError("Error triggering challenge: {0} {1}".format(code, result)) + raise ValueError("Wrote file to {0}, but couldn't download {1}: {2}".format(wellknown_path, wellknown_url, e)) - # wait for challenge to be verified - while True: - try: - resp = urlopen(challenge['uri']) - challenge_status = json.loads(resp.read().decode('utf8')) - except IOError as e: - raise ValueError("Error checking challenge: {0} {1}".format( - e.code, json.loads(e.read().decode('utf8')))) - if challenge_status['status'] == "pending": - time.sleep(2) - elif challenge_status['status'] == "valid": - log.info("{0} verified!".format(domain)) - os.remove(wellknown_path) - break - else: - raise ValueError("{0} challenge did not pass: {1}".format( - domain, challenge_status)) + # say the challenge is done + _send_signed_request(challenge['url'], {}, "Error submitting challenges: {0}".format(domain)) + authorization = _poll_until_not(auth_url, ["pending"], "Error checking challenge status for {0}".format(domain)) + if authorization['status'] != "valid": + raise ValueError("Challenge did not pass for {0}: {1}".format(domain, authorization)) + log.info("{0} verified!".format(domain)) - # get the new certificate + # finalize the order with the csr log.info("Signing certificate...") - proc = subprocess.Popen(["openssl", "req", "-in", csr, "-outform", "DER"], - stdout=subprocess.PIPE, stderr=subprocess.PIPE) - csr_der, err = proc.communicate() - code, result = _send_signed_request(CA + "/acme/new-cert", { - "resource": "new-cert", - "csr": _b64(csr_der), - }) - if code != 201: - raise ValueError("Error signing certificate: {0} {1}".format(code, result)) + csr_der = _cmd(["openssl", "req", "-in", csr, "-outform", "DER"], err_msg="DER Export Error") + _send_signed_request(order['finalize'], {"csr": _b64(csr_der)}, "Error finalizing order") - # return signed certificate! + # poll the order to monitor when it's done + order = _poll_until_not(order_headers['Location'], ["pending", "processing"], "Error checking order status") + if order['status'] != "valid": + raise ValueError("Order failed: {0}".format(order)) + + # download the certificate + certificate_pem, _, _ = _do_request(order['certificate'], err_msg="Certificate download failed") log.info("Certificate signed!") - return """-----BEGIN CERTIFICATE-----\n{0}\n-----END CERTIFICATE-----\n""".format( - "\n".join(textwrap.wrap(base64.b64encode(result).decode('utf8'), 64))) + return certificate_pem -def main(argv): +def main(argv=None): parser = argparse.ArgumentParser( formatter_class=argparse.RawDescriptionHelpFormatter, description=textwrap.dedent("""\ - This script automates the process of getting a signed TLS certificate from - Let's Encrypt using the ACME protocol. It will need to be run on your server - and have access to your private account key, so PLEASE READ THROUGH IT! It's - only ~200 lines, so it won't take long. - - ===Example Usage=== - python acme_tiny.py --account-key ./account.key --csr ./domain.csr --acme-dir /usr/share/nginx/html/.well-known/acme-challenge/ > signed.crt - =================== - - ===Example Crontab Renewal (once per month)=== - 0 0 1 * * python /path/to/acme_tiny.py --account-key /path/to/account.key --csr /path/to/domain.csr --acme-dir /usr/share/nginx/html/.well-known/acme-challenge/ > /path/to/signed.crt 2>> /var/log/acme_tiny.log - ============================================== + This script automates the process of getting a signed TLS certificate from Let's Encrypt using + the ACME protocol. It will need to be run on your server and have access to your private + account key, so PLEASE READ THROUGH IT! It's only ~200 lines, so it won't take long. + + Example Usage: + python acme_tiny.py --account-key ./account.key --csr ./domain.csr --acme-dir /usr/share/nginx/html/.well-known/acme-challenge/ > signed_chain.crt + + Example Crontab Renewal (once per month): + 0 0 1 * * python /path/to/acme_tiny.py --account-key /path/to/account.key --csr /path/to/domain.csr --acme-dir /usr/share/nginx/html/.well-known/acme-challenge/ > /path/to/signed_chain.crt 2>> /var/log/acme_tiny.log """) ) parser.add_argument("--account-key", required=True, help="path to your Let's Encrypt account private key") parser.add_argument("--csr", required=True, help="path to your certificate signing request") parser.add_argument("--acme-dir", required=True, help="path to the .well-known/acme-challenge/ directory") parser.add_argument("--quiet", action="store_const", const=logging.ERROR, help="suppress output except for errors") - parser.add_argument("--ca", default=DEFAULT_CA, help="certificate authority, default is Let's Encrypt") + parser.add_argument("--disable-check", default=False, action="store_true", help="disable checking if the challenge file is hosted correctly before telling the CA") + parser.add_argument("--directory-url", default=DEFAULT_DIRECTORY_URL, help="certificate authority directory url, default is Let's Encrypt") + parser.add_argument("--ca", default=DEFAULT_CA, help="DEPRECATED! USE --directory-url INSTEAD!") + parser.add_argument("--contact", metavar="CONTACT", default=None, nargs="*", help="Contact details (e.g. mailto:aaa@bbb.com) for your account-key") args = parser.parse_args(argv) LOGGER.setLevel(args.quiet or LOGGER.level) - signed_crt = get_crt(args.account_key, args.csr, args.acme_dir, log=LOGGER, CA=args.ca) + signed_crt = get_crt(args.account_key, args.csr, args.acme_dir, log=LOGGER, CA=args.ca, disable_check=args.disable_check, directory_url=args.directory_url, contact=args.contact) sys.stdout.write(signed_crt) if __name__ == "__main__": # pragma: no cover diff -Nru acme-tiny-20171115/debian/acme-tiny.1 acme-tiny-4.0.4/debian/acme-tiny.1 --- acme-tiny-20171115/debian/acme-tiny.1 2018-05-26 19:35:45.000000000 +0100 +++ acme-tiny-4.0.4/debian/acme-tiny.1 2019-04-08 21:31:24.000000000 +0100 @@ -1,43 +1,56 @@ .\" Hey, EMACS: -*- nroff -*- +.\" MIT License (Expat) .\" (C) Copyright 2016 Jeremías Casteglione , -.TH ACME-TINY 1 "2016-03-08" +.\" (C) Copyright 2019 Samuel Henrique , +.TH ACME-TINY 1 "2019-04-07" .\" Please adjust this date whenever revising the manpage. .SH NAME acme-tiny \- letsencrypt tiny python client .SH SYNOPSIS .B acme-tiny ---account-key account.key ---csr domain.csr ---acme-dir /var/www/html/.well-known/acme-challenge +[-h] +--account-key ACCOUNT_KEY +--csr CSR +--acme-dir ACME_DIR [--quiet] -[--ca https://acme-v01.api.letsencrypt.org] +[--disable-check] +[--directory-url DIRECTORY_URL] +[--contact [CONTACT [CONTACT ...]]] .SH DESCRIPTION This script automates the process of getting a signed TLS certificate from Let's Encrypt using the ACME protocol. It will need to be run on your server and have access to your private account key, so PLEASE READ THROUGH IT! It's only ~200 lines, so it won't take long. -.SH EXAMPLES -.B acme-tiny ---account-key ./account.key --csr ./domain.csr --acme-dir /var/www/html/.well-known/acme-challenge/ >signed.crt .SH OPTIONS .TP .B \-h, \-\-help -Show summary of options. +Show summary of options .TP -.B \-\-account-key account.key -Path to private account.key file. For letsencrypt authentication. +.B \-\-account-key ACCOUNT_KEY +Path to your Let's Encrypt account private key .TP -.B \-\-csr domain.csr -Path to the domain(s) CSR file. +.B \-\-csr CSR +Path to your certificate signing request .TP -.B \-\-acme-dir /var/www/html/.well-known/acme-challenge -Path to webserver public acme-challenge dir for domain ownership confirmation. +.B \-\-acme-dir ACME_DIR +Path to the .well-known/acme-challenge/ directory .TP .B \-\-quiet -Suppress output except for errors. +Suppress output except for errors +.TP +.B \-\-disable-check +Disable checking if the challenge file is hosted correctly before telling the CA .TP -.B \-\-ca https://acme-v01.api.letsencrypt.org -URL to Certificate Authority. Defaults to letsencrypt CA. +.B \-\-directory-url DIRECTORY_URL +Certificate authority directory url, default is Let's Encrypt +.TP +.B \-\-contact [CONTACT [CONTACT ...]] +Contact details (e.g. mailto:aaa@bbb.com) for your account-key +.SH EXAMPLES +.B acme-tiny +--account-key ./account.key --csr ./domain.csr --acme-dir /usr/share/nginx/html/.well-known/acme-challenge/ > signed_chain.crt +.SH EXAMPLE CRONTAB RENEWAL +0 0 1 * * acme_tiny --account-key /path/to/account.key --csr /path/to/domain.csr --acme-dir /usr/share/nginx/html/.well-known/acme-challenge/ > /path/to/signed_chain.crt 2>> /var/log/acme_tiny.log .SH SEE ALSO .BR openssl (1), .BR https://letsencrypt.org/ , diff -Nru acme-tiny-20171115/debian/changelog acme-tiny-4.0.4/debian/changelog --- acme-tiny-20171115/debian/changelog 2018-05-26 19:35:45.000000000 +0100 +++ acme-tiny-4.0.4/debian/changelog 2019-04-08 21:31:24.000000000 +0100 @@ -1,3 +1,25 @@ +acme-tiny (1:4.0.4-1) unstable; urgency=medium + + * New upstream version 4.0.4 + - Support for ACMEv2 API (closes: #924393) + - Add epoch as the previous releases were using calver, now we're + using semver (closes: #905720) + * Bump DH level to 12 + * Bump Standards-Version to 4.3.0 + * Remove previous Uploader (not active since 2017) and add myself instead + * Remove legacy upstream changelog (it was a git log) + * Update manpage + * d/control: Add Build-Depends on python3-setuptools-scm + * d/copyright: + - Update entries + - Add upstream email + * debian/patches + - setuptools-support.patch: Remove patch, no more needed + - readme-replace-usr-bin-sh-by-bin-sh.patch: Update patch + * d/watch: Bump to v4 and update version detection + + -- Samuel Henrique Mon, 08 Apr 2019 21:31:24 +0100 + acme-tiny (20171115-2) unstable; urgency=medium * Team upload. @@ -37,6 +59,7 @@ acme-tiny (20160801-1) unstable; urgency=medium * new upstream release (8bb99b2) + + updated agreement URL (Closes: #833647) -- Jeremías Casteglione Thu, 11 Aug 2016 01:45:55 +0000 diff -Nru acme-tiny-20171115/debian/CHANGELOG acme-tiny-4.0.4/debian/CHANGELOG --- acme-tiny-20171115/debian/CHANGELOG 2018-05-26 19:35:45.000000000 +0100 +++ acme-tiny-4.0.4/debian/CHANGELOG 1970-01-01 01:00:00.000000000 +0100 @@ -1,529 +0,0 @@ -commit 5a7b4e79bc9bd5b51739c0d8aaf644f62cc440e6 -Merge: ecd26b1 7a5a255 -Author: Daniel Roesler -Date: Mon Aug 1 12:53:22 2016 -0700 - - Merge remote-tracking branch 'origin/master' - -commit ecd26b1e784973e5b52d5a308964a8e29a6cf207 -Author: Daniel Roesler -Date: Mon Aug 1 12:53:13 2016 -0700 - - updated agreement URL since Let's Encrypt is now rejecting the old agreement - -commit 7a5a2558c8d6e5ab2a59b9fec9633d9e63127971 -Merge: 84308ab bf5d383 -Author: Daniel Roesler -Date: Sat Mar 26 10:30:04 2016 -0700 - - Merge pull request #103 from nylen/patch-1 - - For challenges, redirects from HTTP -> HTTPS are fine too - -commit 84308ab4a5b741a06206f5e65264e603bb54d22e -Author: Daniel Roesler -Date: Sat Mar 26 10:24:30 2016 -0700 - - Let's Encrypt changed their staging server issuer common name - -commit fba3cb5263f082ce28848a87a2fc31450022ce90 -Merge: f61f72c be31020 -Author: Daniel Roesler -Date: Sat Mar 26 10:16:32 2016 -0700 - - Merge pull request #109 from maghoff/patch-1 - - Update README.md - -commit be31020b3c063cc2605c16ca483b34fed81690e1 -Author: Magnus Hoff -Date: Sat Mar 26 12:10:37 2016 +0100 - - Update README.md - - Let's Encrypt have switched to using their intermediate certificate X3 for signing, see https://letsencrypt.org/certificates/ - -commit bf5d383e257101769b003eadccdc2a650c42c6cd -Author: James Nylen -Date: Wed Mar 16 01:49:21 2016 -0500 - - For challenges, redirects from HTTP -> HTTPS are fine too - -commit f61f72c212cea27f388eb4a26ede0d65035bdb53 -Merge: fcb7cd6 32376d0 -Author: Daniel Roesler -Date: Tue Dec 29 12:43:26 2015 -0800 - - Merge remote-tracking branch 'origin/master' - -commit fcb7cd6f66e951eeae76ff2f48d8ad3e40da37ef -Author: Daniel Roesler -Date: Tue Dec 29 12:43:12 2015 -0800 - - fixed #58, apparently .reason isn't an attribute on HTTPError in python 2.6 even though it's documented as such - -commit 32376d02f45843545f38810f1c18478cdc6cb8a0 -Merge: 69a4572 7747bd1 -Author: Daniel Roesler -Date: Sat Dec 26 11:41:15 2015 -0800 - - Merge pull request #55 from jwilk/spelling - - fix typos - -commit 7747bd1eb13475e4faa41049ff5fe14648b3a302 -Author: Jakub Wilk -Date: Sat Dec 26 01:22:29 2015 +0100 - - fix typos - -commit 69a457269a6392ac31b629b4e103e8ea7dd282c9 -Author: Daniel Roesler -Date: Tue Dec 22 15:15:01 2015 -0800 - - fixed error reading for URLErrors and HTTPErrors - -commit 9e69fa7b0ffbb3e35bd374c0242941fe905c0b53 -Author: Daniel Roesler -Date: Tue Dec 22 10:32:33 2015 -0800 - - added permissions change to accomodate https://bugs.launchpad.net/ubuntu/+source/fuse/+bug/794494 - -commit d3c6451a4f5d260a2845117d4b3ff22924bf9f4e -Author: Daniel Roesler -Date: Tue Dec 22 08:38:57 2015 -0800 - - fixed #48, handle URLErrors in addition to HTTPErrors - -commit a9a1bc79a6b66dcf76c765e58365ca4c0201a830 -Author: Daniel Roesler -Date: Tue Dec 22 08:17:03 2015 -0800 - - switched form the node script to the tiny python script - -commit 33f593d0601c6e15869d793e11141578f5ca76ed -Merge: 7d8e06a 039769a -Author: Daniel Roesler -Date: Tue Dec 22 08:11:21 2015 -0800 - - Merge branch 'master' into pull_34 - -commit 039769ab3f555b220b1c502a7f5e43c8d7e1c994 -Merge: 6d30024 7f2325e -Author: Daniel Roesler -Date: Tue Dec 22 07:47:54 2015 -0800 - - Merge pull request #44 from RalfJung/master - - Fix TypeError: 'str' does not support the buffer interface - -commit 6d3002403e097c4df6aaa9fe94be70777dea4d1a -Merge: 86394c6 fd9d2a8 -Author: Daniel Roesler -Date: Tue Dec 22 07:36:33 2015 -0800 - - Merge pull request #51 from monkz/patch-1 - - Additional clarification for the creation of the account key - -commit fd9d2a8161fede8cc3d1204d2588aa1250eaacce -Author: monkz -Date: Sat Dec 19 15:10:13 2015 +0100 - - Additional clarification for the creation of the account key - - According to #50 was the wording in Step 1 a litte ambiguous. - -commit 86394c615afa9470b82c3385500941b62f69ab44 -Merge: ad59a79 81e357f -Author: Daniel Roesler -Date: Tue Dec 15 08:28:15 2015 -0800 - - Merge remote-tracking branch 'origin/master' - -commit ad59a7968173ad10e4265db369f6ebf4f072c42f -Author: Daniel Roesler -Date: Tue Dec 15 08:27:57 2015 -0800 - - fixed #45, added readme with instructions on how to test - -commit 7f2325e49fc57b304d203a651224c5264c7bfd64 -Author: Ralf Jung -Date: Sun Dec 13 12:54:04 2015 +0100 - - Fix TypeError: 'str' does not support the buffer interface - -commit 81e357fe81705b15d4ef9e0b1ba3e262e9b71527 -Merge: f7aaf19 24da0a1 -Author: Daniel Roesler -Date: Thu Dec 10 07:08:38 2015 -0800 - - Merge pull request #38 from reidrac/master - - Fixed nginx example - -commit 24da0a18385739fc049a20e911cfd63579c23b5d -Author: Juan J. Martínez -Date: Wed Dec 9 07:14:00 2015 +0000 - - Fixed nginx example - - server_name names is not a comma separated list host hostsnames. - -commit 7d8e06a80559cd9934d1b30108df7163ef0fa796 -Author: Andreas Pfohl -Date: Mon Dec 7 14:00:33 2015 +0100 - - Added description for converting an LE private key for acme-tiny. - -commit f7aaf199e4f7f8828ee2ee4cc8d7e5f2fbff26eb -Author: Daniel Roesler -Date: Sun Dec 6 02:36:32 2015 -0800 - - switched coveralls badge to use master branch - -commit b37284d218eed51b862a42a3f792162fd01f4e15 -Author: Daniel Roesler -Date: Sun Dec 6 02:26:03 2015 -0800 - - added error handling tests - -commit e6cad5f0f6fbfec1126fe3c987c0374b1d2c3cfa -Author: Daniel Roesler -Date: Sun Dec 6 00:38:10 2015 -0800 - - added argparse to requirements for python 2.6 and removed fuse from coverage - -commit e0a874203c3e7821ad04a5a5ec3a6ff667e48172 -Author: Daniel Roesler -Date: Sun Dec 6 00:23:22 2015 -0800 - - added monkey patching via fuse for full integration test - -commit dacd06f2c558b3e22526d56a65051f1c5ef4c8e3 -Author: Daniel Roesler -Date: Sat Dec 5 18:58:29 2015 -0800 - - added coveralls to travis test - -commit d3e807302b36a938c889da00cb56df83ddcb6b80 -Author: Daniel Roesler -Date: Sat Dec 5 18:40:36 2015 -0800 - - apparently coverage doesn't support python 3.2 - -commit 1bda17f1766b3125bad2b91b7666f764be88c5ae -Author: Daniel Roesler -Date: Sat Dec 5 18:38:14 2015 -0800 - - added coverage to tests - -commit 12636e5fb3ec90cd8ba69b0621a4b7ba97df51c1 -Author: Daniel Roesler -Date: Sat Dec 5 18:22:42 2015 -0800 - - added code coverage - -commit 7cbf7ecc35e9a107208c229e7028579d2bc92dae -Author: Daniel Roesler -Date: Sat Dec 5 18:19:00 2015 -0800 - - apparently python 2.6 doesn't have test discovery - -commit d866955185ca447d2647a8d4a5adf4b48ee3218a -Author: Daniel Roesler -Date: Sat Dec 5 18:11:29 2015 -0800 - - added initial test structure - -commit c3493dcba827e8fb80b5473437accec0eb9b6602 -Author: Daniel Roesler -Date: Sat Dec 5 17:44:23 2015 -0800 - - tweaked help text on default certificate authority - -commit 862175e92fbc0901e4b4a42ddd17195029bcfc62 -Author: Daniel Roesler -Date: Sat Dec 5 17:33:22 2015 -0800 - - added kwarg for log to argparse function calling - -commit 406f49aa1520354ae2c2c76cb77a9c70ca083d58 -Author: Daniel Roesler -Date: Sat Dec 5 17:31:48 2015 -0800 - - added default CA back at the module level - -commit 97d9090a10347c7e0fc876fdb6f388247bded314 -Merge: 6aa18cc 36ec4bd -Author: Daniel Roesler -Date: Sat Dec 5 17:24:43 2015 -0800 - - Merge branch 'master' into pull_32 - -commit 36ec4bdd375fc5663fb0a85e72c48d00d78611b4 -Author: Daniel Roesler -Date: Sat Dec 5 17:19:14 2015 -0800 - - no need to save the certificate to a variable in the example cron script, since it will exit before chained.pem is overwritten - -commit 6aa18cc5a2ec3e209e730583222922e4e81cc358 -Author: Tobias Heinzen -Date: Sun Dec 6 02:17:35 2015 +0100 - - adding CA parameter to command line utility - -commit a9a7170c5a68a30416586b15ae56e038a62f1c87 -Author: Benoit Garret -Date: Sun Dec 6 02:00:54 2015 +0100 - - cron example: do not write the certificate if there is an error - -commit b7ee7b8d52e02467f7403d59c7fc433c3970c5d6 -Merge: aebde7a ec7b74c -Author: Daniel Roesler -Date: Sat Dec 5 16:45:53 2015 -0800 - - Merge branch 'code-reduction' into logging-rewrite - -commit ec7b74cfc73846c695edca576e252fb33c302d3b -Author: somecoder42 -Date: Sat Dec 5 13:57:47 2015 +0100 - - simpify protected - Conflicts: - acme_tiny.py - -commit f940739ba9da541d8abb3823bfb2d477278f6a0e -Author: somecoder42 -Date: Sat Dec 5 13:27:50 2015 +0100 - - shrink pub_exp/pub_mod code - - This does not reduce readability in my opinion, but that is debatable - Conflicts: - acme_tiny.py - -commit 8d2b46519a335752a51442360b77c1536dd2da10 -Author: somecoder42 -Date: Sat Dec 5 13:10:09 2015 +0100 - - Use 'with' when opening wellknown_path - -commit 1a16509caae3fa07a903de41b5bfa2b1374cdd84 -Author: somecoder42 -Date: Sat Dec 5 13:08:24 2015 +0100 - - replace challenge['token'] by token - Conflicts: - acme_tiny.py - -commit aebde7ae1a129eab8b97dd7039f7bb6c4fe4fe10 -Author: Daniel Roesler -Date: Sat Dec 5 16:25:53 2015 -0800 - - added propper logging, but needs code reduction to bring back below 200 lines - -commit ee20af39bebbf1eb6d90b22e91f56dfadeee650c -Author: Daniel Roesler -Date: Sat Dec 5 15:07:22 2015 -0800 - - tweaked import formatting for python 2 and 3 - -commit 9ce0107fa3ed89ec77e19590d025bb580edb7cf2 -Merge: ef7a46d 59dae1d -Author: Daniel Roesler -Date: Sat Dec 5 14:57:11 2015 -0800 - - Merge remote-tracking branch 'origin/master' into pull_22 - -commit ef7a46df3f44d7beb1b1f9aa41744909594fe415 -Merge: 0c980e0 2bcf39f -Author: Daniel Roesler -Date: Sat Dec 5 14:56:58 2015 -0800 - - Merge branch 'master' into pull_22 - -commit 59dae1dc5f52b397559a2004505e4b56f516922b -Merge: 2bcf39f 15cbcc8 -Author: Daniel Roesler -Date: Sat Dec 5 14:55:56 2015 -0800 - - Merge pull request #26 from jwilk/readme-formatting - - README: enable syntax highlighting for nginx snippet - -commit 15cbcc8cfbe816b8cb726ab4cd9fcefab1db2d17 -Author: Jakub Wilk -Date: Sat Dec 5 15:41:15 2015 +0100 - - README: enable syntax highlighting for nginx snippet - -commit 0c980e00ecb6a63a43e89632dc2b3e5b37041b67 -Author: Collin Anderson -Date: Fri Dec 4 17:05:00 2015 -0500 - - Python 3 support - -commit 2bcf39f8eb0d61a99348e02646a8b2c13934239c -Author: Nicolas Dietrich -Date: Fri Dec 4 23:03:03 2015 +0100 - - Fix two missing words - -commit 8d9dc340563437717611fbc0ff5eaf08cdf7a3bc -Merge: 3d0e018 dc800f5 -Author: Daniel Roesler -Date: Fri Dec 4 12:38:39 2015 -0800 - - Merge remote-tracking branch 'maghoff/patch-1' into pull_19 - -commit 3d0e0189e0095f04000ec470150bdaec65bcc9ef -Author: Daniel Roesler -Date: Fri Dec 4 12:35:12 2015 -0800 - - consolidated helptext dedent a bit - -commit fdae5f75a13be687df352a8e8b1347abc28c0ebd -Merge: 47ebd61 64465ac -Author: Daniel Roesler -Date: Fri Dec 4 12:32:33 2015 -0800 - - Merge remote-tracking branch 'jomo/patch-indent' into pull_17 - -commit 47ebd611ab913e35a9ecb6dcc03d3308007fc35e -Author: Daniel Roesler -Date: Fri Dec 4 12:28:13 2015 -0800 - - updated urlsafe regex to use raw string - -commit fe7cb7648947dbbde03b2990fd73eb69fa5846d6 -Merge: 1775b70 3f7fe35 -Author: Daniel Roesler -Date: Fri Dec 4 12:26:35 2015 -0800 - - Merge remote-tracking branch 'jomo/patch-regex' into pull_16 - -commit 1775b7065438004322e47809e0552f2522e3bbb5 -Author: Daniel Roesler -Date: Fri Dec 4 12:23:37 2015 -0800 - - prevent malicious tokens from being anything but urlsafe base64 - -commit dc800f5e022a4ea6cd29957c3fe8df2efaa29b87 -Author: Magnus Hoff -Date: Fri Dec 4 20:44:45 2015 +0100 - - Fix typo - -commit 64465ac88e1abbfdcd7381d6e8c5a230877b1e89 -Author: jomo -Date: Fri Dec 4 17:25:08 2015 +0100 - - use textwrap.dedent - - textwrap was already imported, so why not use it? :) - From https://docs.python.org/2/library/textwrap.html#textwrap.dedent: - > This can be used to make triple-quoted strings line up with the left edge of the display, while still presenting them in the source code in indented form. - -commit 3f7fe35625223bee1c15d1f1952cc64d7a347fd8 -Author: jomo -Date: Fri Dec 4 17:20:59 2015 +0100 - - use raw regex string - - From https://docs.python.org/2/howto/regex.html#the-backslash-plague: - > backslashes are not handled in any special way in a string literal prefixed with 'r', so r"\n" is a two-character string containing '\' and 'n', while "\n" is a one-character string containing a newline. - -commit a866b255e76c51fe60e70e6d4d764d91e8d9245b -Author: Paul Jimenez -Date: Fri Dec 4 11:06:45 2015 -0500 - - Join paths platform-neutrally - - Use os.path to join paths in a more platform-neutral fashion - -commit e12cd728c8beba52c47ee72520769cc461353bd0 -Merge: 80c83f8 b2cd85d -Author: Daniel Roesler -Date: Fri Dec 4 06:43:36 2015 -0800 - - Merge pull request #7 from temporaryaccount/compat - - Make compatible with older 2.x versions of Python - -commit b2cd85d8f7db4f818ff169c60c363885c7839fb4 -Author: temp -Date: Fri Dec 4 18:29:01 2015 +0400 - - Make compatible with older 2.x versions of Python - -commit 80c83f8150c51c478cfa0ec16ece7895595343a4 -Author: Daniel Roesler -Date: Fri Dec 4 06:11:43 2015 -0800 - - added more verbose error output to openssl errors - -commit af3a2e7de70db9aaf8b4bedc63282b65c6a2d541 -Merge: cffe2a8 8c676b6 -Author: Daniel Roesler -Date: Fri Dec 4 06:08:13 2015 -0800 - - Merge remote-tracking branch 'origin/master' - -commit cffe2a8b29600473a3e20cc2b6987776ab9bc034 -Author: Daniel Roesler -Date: Fri Dec 4 06:08:01 2015 -0800 - - added error output of openssl signature errors - -commit 8c676b6169d12e10643fa5161e55f107d6870a85 -Merge: 338f7be 01919d4 -Author: Daniel Roesler -Date: Fri Dec 4 05:45:22 2015 -0800 - - Merge pull request #6 from jwilk/spelling - - fix typo - -commit 01919d4bda7201f14cd480994a780a90db75f9e5 -Author: Jakub Wilk -Date: Fri Dec 4 13:35:20 2015 +0100 - - fix typo - -commit 338f7bedaf3da0c3f049c0b9c5be6ed58a8b26ee -Author: Daniel Roesler -Date: Thu Dec 3 20:56:01 2015 -0800 - - fixed #4, specify an output file for wget to prevent clobbering - -commit c711b821af511feac0d7b7771be609685a3f800a -Author: Daniel Roesler -Date: Thu Dec 3 20:53:04 2015 -0800 - - fixed #3, cleared up confusing directory names in example helptext - -commit 3f68f50f347f957cef8425a41989e6c255b37f32 -Author: Daniel Roesler -Date: Wed Nov 25 22:54:57 2015 -0800 - - typos - -commit 7e8f7731434061496f20b56a6262ce2fbb21081e -Author: Daniel Roesler -Date: Wed Nov 25 22:48:17 2015 -0800 - - Added readme - -commit 7db428f90b06e3ef57db824b8420c821eff3ec96 -Author: Daniel Roesler -Date: Wed Nov 25 21:46:20 2015 -0800 - - added basic script - -commit 7eacef721cb9c7f1c120bff9ff03adb6e5c35b4d -Author: Daniel Roesler -Date: Wed Nov 25 21:44:00 2015 -0800 - - Initial commit diff -Nru acme-tiny-20171115/debian/compat acme-tiny-4.0.4/debian/compat --- acme-tiny-20171115/debian/compat 2018-05-26 19:35:45.000000000 +0100 +++ acme-tiny-4.0.4/debian/compat 2019-04-08 21:31:24.000000000 +0100 @@ -1 +1 @@ -11 +12 diff -Nru acme-tiny-20171115/debian/control acme-tiny-4.0.4/debian/control --- acme-tiny-20171115/debian/control 2018-05-26 19:35:45.000000000 +0100 +++ acme-tiny-4.0.4/debian/control 2019-04-08 21:31:24.000000000 +0100 @@ -1,13 +1,14 @@ Source: acme-tiny Maintainer: Debian Let's Encrypt Team -Uploaders: Jeremías Casteglione +Uploaders: Samuel Henrique Section: utils Priority: optional -Build-Depends: debhelper (>= 11~), +Build-Depends: debhelper (>= 12~), dh-python, python3 (>= 3.3.2-2~), - python3-setuptools -Standards-Version: 4.1.4 + python3-setuptools, + python3-setuptools-scm +Standards-Version: 4.3.0 Homepage: https://github.com/diafygi/acme-tiny Vcs-Git: https://salsa.debian.org/letsencrypt-team/acme-tiny.git Vcs-Browser: https://salsa.debian.org/letsencrypt-team/acme-tiny diff -Nru acme-tiny-20171115/debian/copyright acme-tiny-4.0.4/debian/copyright --- acme-tiny-20171115/debian/copyright 2018-05-26 19:35:45.000000000 +0100 +++ acme-tiny-4.0.4/debian/copyright 2019-04-08 21:31:24.000000000 +0100 @@ -3,29 +3,16 @@ Source: https://github.com/diafygi/acme-tiny Files: * -Copyright: 2015 Daniel Roesler +Copyright: 2015 Daniel Roesler License: MIT - Permission is hereby granted, free of charge, to any person obtaining a - copy of this software and associated documentation files (the "Software"), - to deal in the Software without restriction, including without limitation - the rights to use, copy, modify, merge, publish, distribute, sublicense, - and/or sell copies of the Software, and to permit persons to whom the - Software is furnished to do so, subject to the following conditions: - . - The above copyright notice and this permission notice shall be included - in all copies or substantial portions of the Software. - . - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY - CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, - TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE - SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Files: debian/* -Copyright: 2016 Jeremías Casteglione -License: Expat +Copyright: 2016-2017 Jeremías Casteglione + 2017-2018 Sebastien Badia + 2019 Samuel Henrique +License: MIT + +License: MIT Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including diff -Nru acme-tiny-20171115/debian/patches/0001-setuptools-support.patch acme-tiny-4.0.4/debian/patches/0001-setuptools-support.patch --- acme-tiny-20171115/debian/patches/0001-setuptools-support.patch 2018-05-26 19:35:45.000000000 +0100 +++ acme-tiny-4.0.4/debian/patches/0001-setuptools-support.patch 1970-01-01 01:00:00.000000000 +0100 @@ -1,89 +0,0 @@ -From 620034275172b9e63c5f3174668800d57d0bf172 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Jerem=C3=ADas=20Casteglione?= -Date: Thu, 19 May 2016 01:10:20 -0300 -Subject: setuptools support - ---- - acme_tiny.py | 13 +++++++++++++ - setup.py | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ - 2 files changed, 62 insertions(+) - create mode 100644 setup.py - -diff --git a/acme_tiny.py b/acme_tiny.py -index fa1ee4d..205d1f4 100644 ---- a/acme_tiny.py -+++ b/acme_tiny.py -@@ -194,5 +194,18 @@ def main(argv): - signed_crt = get_crt(args.account_key, args.csr, args.acme_dir, log=LOGGER, CA=args.ca) - sys.stdout.write(signed_crt) - -+def __check_deps(): -+ # check runtime dependencies (openssl command only) -+ stat, out = subprocess.getstatusoutput("openssl version") -+ if stat != 0: -+ sys.stderr.write(out) -+ sys.stderr.write('\n') -+ sys.exit(stat) -+ -+def __entry_point(): -+ # needed for setuptools support (see setup.py file) -+ __check_deps() -+ main(sys.argv[1:]) -+ - if __name__ == "__main__": # pragma: no cover - main(sys.argv[1:]) -diff --git a/setup.py b/setup.py -new file mode 100644 -index 0000000..fea578f ---- /dev/null -+++ b/setup.py -@@ -0,0 +1,49 @@ -+#!/usr/bin/env python3 -+# -*- encoding: utf-8 -*- -+ -+from setuptools import setup -+ -+long_description = """ -+acme-tiny is a tiny script to issue and renew TLS certs from Let's Encrypt -+ -+This is a tiny, auditable script that you can throw on your server to issue and -+renew Let's Encrypt certificates. Since it has to be run on your server and have -+access to your private Let's Encrypt account key, I tried to make it as tiny as -+possible (currently less than 200 lines). The only prerequisites are python and -+openssl. -+""" -+ -+setup( -+ name='acme-tiny', -+ version='20171115', -+ -+ description="acme-tiny is a tiny script to issue and renew TLS certs from Let's Encrypt", -+ long_description=long_description, -+ -+ license='MIT', -+ url='https://github.com/diafygi/acme-tiny', -+ -+ author='Daniel Roesler', -+ author_email='diafygi@gmail.com', -+ -+ maintainer='Jeremías Casteglione', -+ maintainer_email='jrmsdev@gmail.com', -+ -+ classifiers=[ -+ 'Development Status :: 3 - Alpha', -+ 'Intended Audience :: Developers', -+ 'Intended Audience :: System Administrators', -+ 'License :: OSI Approved :: MIT License', -+ 'Programming Language :: Python :: 2', -+ 'Programming Language :: Python :: 3', -+ ], -+ -+ keywords='development', -+ py_modules=['acme_tiny'], -+ -+ entry_points={ -+ 'console_scripts': [ -+ 'acme-tiny=acme_tiny:__entry_point', -+ ], -+ }, -+) diff -Nru acme-tiny-20171115/debian/patches/0002-readme-replace-usr-bin-sh-by-bin-sh.patch acme-tiny-4.0.4/debian/patches/0002-readme-replace-usr-bin-sh-by-bin-sh.patch --- acme-tiny-20171115/debian/patches/0002-readme-replace-usr-bin-sh-by-bin-sh.patch 2018-05-26 19:35:45.000000000 +0100 +++ acme-tiny-4.0.4/debian/patches/0002-readme-replace-usr-bin-sh-by-bin-sh.patch 2019-04-08 21:31:24.000000000 +0100 @@ -7,16 +7,16 @@ README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) -diff --git a/README.md b/README.md -index 6e3c844..b6e1024 100644 ---- a/README.md -+++ b/README.md -@@ -172,7 +172,7 @@ for example script). +Index: acme-tiny/README.md +=================================================================== +--- acme-tiny.orig/README.md ++++ acme-tiny/README.md +@@ -165,7 +165,7 @@ for example script). Example of a `renew_cert.sh`: ```sh -#!/usr/bin/sh -+#!/bin/sh - python /path/to/acme_tiny.py --account-key /path/to/account.key --csr /path/to/domain.csr --acme-dir /var/www/challenges/ > /tmp/signed.crt || exit - wget -O - https://letsencrypt.org/certs/lets-encrypt-x3-cross-signed.pem > intermediate.pem - cat /tmp/signed.crt intermediate.pem > /path/to/chained.pem ++#!/usr/sh + python /path/to/acme_tiny.py --account-key /path/to/account.key --csr /path/to/domain.csr --acme-dir /var/www/challenges/ > /path/to/signed_chain.crt || exit + service nginx reload + ``` diff -Nru acme-tiny-20171115/debian/patches/series acme-tiny-4.0.4/debian/patches/series --- acme-tiny-20171115/debian/patches/series 2018-05-26 19:35:45.000000000 +0100 +++ acme-tiny-4.0.4/debian/patches/series 2019-04-08 21:31:24.000000000 +0100 @@ -1,2 +1 @@ -0001-setuptools-support.patch 0002-readme-replace-usr-bin-sh-by-bin-sh.patch diff -Nru acme-tiny-20171115/debian/rules acme-tiny-4.0.4/debian/rules --- acme-tiny-20171115/debian/rules 2018-05-26 19:35:45.000000000 +0100 +++ acme-tiny-4.0.4/debian/rules 2019-04-08 21:31:24.000000000 +0100 @@ -1,7 +1,4 @@ #!/usr/bin/make -f -# This file was automatically generated by stdeb 0.8.5 at -# Fri, 19 Feb 2016 06:09:08 +0000 - export PYBUILD_NAME=acme-tiny %: @@ -9,9 +6,3 @@ override_dh_auto_test: -override_dh_installchangelogs: - if [ -e $(CURDIR)/debian/CHANGELOG ] ; then \ - dh_installchangelogs $(CURDIR)/debian/CHANGELOG ; \ - else \ - dh_installchangelogs ; \ - fi diff -Nru acme-tiny-20171115/debian/watch acme-tiny-4.0.4/debian/watch --- acme-tiny-20171115/debian/watch 2018-05-26 19:35:45.000000000 +0100 +++ acme-tiny-4.0.4/debian/watch 2019-04-08 21:31:24.000000000 +0100 @@ -1,7 +1,3 @@ -# 20151229-1 There's not an upstream release yet, so we used the latest commit -# timestamp for the package version. -# Once upstream makes it first release, we will use an 'epoch' number -# like 1: -version=2 -opts=filenamemangle=s/.+\/v?(\d\S*)\.tar\.gz/acme-tiny-$1\.tar\.gz/ \ - https://github.com/diafygi/acme-tiny/tags .*/?(\d{2}\.\d.\d)\.tar\.gz +version=4 +opts=uversionmangle=s/(rc|a|b|c)/~$1/ \ +https://pypi.debian.net/acme-tiny/acme-tiny-(.+)\.(?:zip|tgz|tbz|txz|(?:tar\.(?:gz|bz2|xz))) diff -Nru acme-tiny-20171115/PKG-INFO acme-tiny-4.0.4/PKG-INFO --- acme-tiny-20171115/PKG-INFO 1970-01-01 01:00:00.000000000 +0100 +++ acme-tiny-4.0.4/PKG-INFO 2018-05-17 13:49:36.000000000 +0100 @@ -0,0 +1,22 @@ +Metadata-Version: 1.1 +Name: acme-tiny +Version: 4.0.4 +Summary: A tiny script to issue and renew TLS certs from Let's Encrypt +Home-page: https://github.com/diafygi/acme-tiny +Author: Daniel Roesler +Author-email: diafygi@gmail.com +License: MIT +Description: UNKNOWN +Platform: UNKNOWN +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: System Administrators +Classifier: License :: OSI Approved :: MIT License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 2 +Classifier: Programming Language :: Python :: 2.7 +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.3 +Classifier: Programming Language :: Python :: 3.4 +Classifier: Programming Language :: Python :: 3.5 +Classifier: Programming Language :: Python :: 3.6 diff -Nru acme-tiny-20171115/README.md acme-tiny-4.0.4/README.md --- acme-tiny-20171115/README.md 2017-11-15 05:30:52.000000000 +0000 +++ acme-tiny-4.0.4/README.md 2018-05-17 13:43:30.000000000 +0100 @@ -9,9 +9,9 @@ key, I tried to make it as tiny as possible (currently less than 200 lines). The only prerequisites are python and openssl. -**PLEASE READ THE SOURCE CODE! YOU MUST TRUST IT WITH YOUR PRIVATE KEYS!** +**PLEASE READ THE SOURCE CODE! YOU MUST TRUST IT WITH YOUR PRIVATE ACCOUNT KEY!** -##Donate +## Donate If this script is useful to you, please donate to the EFF. I don't work there, but they do fantastic work. @@ -30,7 +30,7 @@ this script likely isn't for you! Please use the official Let's Encrypt [client](https://github.com/letsencrypt/letsencrypt). To accomplish this you need to initially create a key, that can be used by -acme-tiny, to register a account for you and sign all following requests. +acme-tiny, to register an account for you and sign all following requests. ``` openssl genrsa 4096 > account.key @@ -67,15 +67,15 @@ you can't use your account private key as your domain private key! ``` -#generate a domain private key (if you haven't already) +# Generate a domain private key (if you haven't already) openssl genrsa 4096 > domain.key ``` ``` -#for a single domain +# For a single domain openssl req -new -sha256 -key domain.key -subj "/CN=yoursite.com" > domain.csr -#for multiple domains (use this one if you want both www.yoursite.com and yoursite.com) +# For multiple domains (use this one if you want both www.yoursite.com and yoursite.com) openssl req -new -sha256 -key domain.key -subj "/" -reqexts SAN -config <(cat /etc/ssl/openssl.cnf <(printf "[SAN]\nsubjectAltName=DNS:yoursite.com,DNS:www.yoursite.com")) > domain.csr ``` @@ -89,12 +89,12 @@ must serve the challenge files via HTTP (a redirect to HTTPS is fine too). ``` -#make some challenge folder (modify to suit your needs) +# Make some challenge folder (modify to suit your needs) mkdir -p /var/www/challenges/ ``` ```nginx -#example for nginx +# Example for nginx server { listen 80; server_name yoursite.com www.yoursite.com; @@ -115,30 +115,23 @@ and read your private account key and CSR. ``` -#run the script on your server -python acme_tiny.py --account-key ./account.key --csr ./domain.csr --acme-dir /var/www/challenges/ > ./signed.crt +# Run the script on your server +python acme_tiny.py --account-key ./account.key --csr ./domain.csr --acme-dir /var/www/challenges/ > ./signed_chain.crt ``` ### Step 5: Install the certificate -The signed https certificate that is output by this script can be used along +The signed https certificate chain that is output by this script can be used along with your private key to run an https server. You need to include them in the https settings in your web server's configuration. Here's an example on how to configure an nginx server: -``` -#NOTE: For nginx, you need to append the Let's Encrypt intermediate cert to your cert -wget -O - https://letsencrypt.org/certs/lets-encrypt-x3-cross-signed.pem > intermediate.pem -cat signed.crt intermediate.pem > chained.pem -``` - ```nginx server { - listen 443; + listen 443 ssl; server_name yoursite.com, www.yoursite.com; - ssl on; - ssl_certificate /path/to/chained.pem; + ssl_certificate /path/to/signed_chain.crt; ssl_certificate_key /path/to/domain.key; ssl_session_timeout 5m; ssl_protocols TLSv1 TLSv1.1 TLSv1.2; @@ -173,17 +166,25 @@ Example of a `renew_cert.sh`: ```sh #!/usr/bin/sh -python /path/to/acme_tiny.py --account-key /path/to/account.key --csr /path/to/domain.csr --acme-dir /var/www/challenges/ > /tmp/signed.crt || exit -wget -O - https://letsencrypt.org/certs/lets-encrypt-x3-cross-signed.pem > intermediate.pem -cat /tmp/signed.crt intermediate.pem > /path/to/chained.pem +python /path/to/acme_tiny.py --account-key /path/to/account.key --csr /path/to/domain.csr --acme-dir /var/www/challenges/ > /path/to/signed_chain.crt || exit service nginx reload ``` ``` -#example line in your crontab (runs once per month) +# Example line in your crontab (runs once per month) 0 0 1 * * /path/to/renew_cert.sh 2>> /var/log/acme_tiny.log ``` +NOTE: Since Let's Encrypt's ACME v2 release (acme-tiny 4.0.0+), the intermediate +certificate is included in the issued certificate download, so you no longer have +to independently download the intermediate certificate and concatenate it to your +signed certificate. If you have an bash script using acme-tiny <4.0 (e.g. before +2018-03-17) with acme-tiny 4.0.0+, then you may be adding the intermediate +certificate to your signed_chain.crt twice (not a big deal, it should still work fine, +but just makes the certificate slightly larger than it needs to be). To fix, +simply remove the bash code where you're downloading the intermediate and adding +it to the acme-tiny certificate output. + ## Permissions The biggest problem you'll likely come across while setting up and running this @@ -191,7 +192,7 @@ challenge web folder as much as possible. I'd recommend creating a user specifically for handling this script, the account private key, and the challenge folder. Then add the ability for that user to write to your installed -certificate file (e.g. `/path/to/chained.pem`) and reload your webserver. That +certificate file (e.g. `/path/to/signed_chain.crt`) and reload your webserver. That way, the cron script will do its thing, overwrite your old certificate, and reload your webserver without having permission to do anything else. diff -Nru acme-tiny-20171115/setup.cfg acme-tiny-4.0.4/setup.cfg --- acme-tiny-20171115/setup.cfg 1970-01-01 01:00:00.000000000 +0100 +++ acme-tiny-4.0.4/setup.cfg 2018-05-17 13:49:36.000000000 +0100 @@ -0,0 +1,7 @@ +[wheel] +universal = True + +[egg_info] +tag_build = +tag_date = 0 + diff -Nru acme-tiny-20171115/setup.py acme-tiny-4.0.4/setup.py --- acme-tiny-20171115/setup.py 1970-01-01 01:00:00.000000000 +0100 +++ acme-tiny-4.0.4/setup.py 2018-05-17 13:43:30.000000000 +0100 @@ -0,0 +1,30 @@ +from setuptools import setup + +setup( + name="acme-tiny", + use_scm_version=True, + url="https://github.com/diafygi/acme-tiny", + author="Daniel Roesler", + author_email="diafygi@gmail.com", + description="A tiny script to issue and renew TLS certs from Let's Encrypt", + license="MIT", + py_modules=['acme_tiny'], + entry_points={'console_scripts': [ + 'acme-tiny = acme_tiny:main', + ]}, + setup_requires=['setuptools_scm'], + classifiers = [ + 'Development Status :: 5 - Production/Stable', + 'Intended Audience :: System Administrators', + 'License :: OSI Approved :: MIT License', + 'Operating System :: OS Independent', + 'Programming Language :: Python', + 'Programming Language :: Python :: 2', + 'Programming Language :: Python :: 2.7', + 'Programming Language :: Python :: 3', + 'Programming Language :: Python :: 3.3', + 'Programming Language :: Python :: 3.4', + 'Programming Language :: Python :: 3.5', + 'Programming Language :: Python :: 3.6', + ] +) diff -Nru acme-tiny-20171115/tests/__init__.py acme-tiny-4.0.4/tests/__init__.py --- acme-tiny-20171115/tests/__init__.py 2017-11-15 05:30:52.000000000 +0000 +++ acme-tiny-4.0.4/tests/__init__.py 2018-05-17 13:43:30.000000000 +0100 @@ -1 +1,2 @@ from .test_module import TestModule +from .test_install import TestInstall diff -Nru acme-tiny-20171115/tests/monkey.py acme-tiny-4.0.4/tests/monkey.py --- acme-tiny-20171115/tests/monkey.py 2017-11-15 05:30:52.000000000 +0000 +++ acme-tiny-4.0.4/tests/monkey.py 2018-05-17 13:43:30.000000000 +0100 @@ -29,7 +29,9 @@ # subject alt-name domain san_csr = NamedTemporaryFile() san_conf = NamedTemporaryFile() - san_conf.write(open("/etc/ssl/openssl.cnf").read().encode("utf8")) + for openssl_cnf in ['/etc/pki/tls/openssl.cnf', '/etc/ssl/openssl.cnf']: + if os.path.exists(openssl_cnf): break + san_conf.write(open(openssl_cnf).read().encode("utf8")) san_conf.write("\n[SAN]\nsubjectAltName=DNS:{0}\n".format(DOMAIN).encode("utf8")) san_conf.seek(0) Popen(["openssl", "req", "-new", "-sha256", "-key", domain_key.name, diff -Nru acme-tiny-20171115/tests/README.md acme-tiny-4.0.4/tests/README.md --- acme-tiny-20171115/tests/README.md 2017-11-15 05:30:52.000000000 +0000 +++ acme-tiny-4.0.4/tests/README.md 2018-05-17 13:43:30.000000000 +0100 @@ -9,6 +9,9 @@ 1. Make a test subdomain for a server you control. Set it as an environmental variable on your local test setup. * On your local: `export TRAVIS_DOMAIN=travis-ci.gethttpsforfree.com` + * Configure the webserver on `$TRAVIS_DOMAIN` for redirection of + `http://$TRAVIS_DOMAIN/.well-known/acme-challenge/` to + `http://localhost:8888/` 2. Generate a shared secret between your local test setup and your server. * `openssl rand -base64 32` * On your local: `export TRAVIS_SESSION=""` @@ -24,7 +27,7 @@ * `pip install -r requirements.txt` 5. Run the test suit on your local. * `cd /path/to/acme-tiny` - * `coverage run --source ./ --omit ./tests/server.py -m unittest tests` + * `coverage run --source ./ --omit ./tests/server.py,./setup.py -m unittest tests` ## Why use FUSE? diff -Nru acme-tiny-20171115/tests/test_install.py acme-tiny-4.0.4/tests/test_install.py --- acme-tiny-20171115/tests/test_install.py 1970-01-01 01:00:00.000000000 +0100 +++ acme-tiny-4.0.4/tests/test_install.py 2018-05-17 13:43:30.000000000 +0100 @@ -0,0 +1,24 @@ +import unittest +import os +import tempfile +import shutil +import subprocess + + +class TestInstall(unittest.TestCase): + def setUp(self): + self.tempdir = tempfile.mkdtemp() + subprocess.check_call(["virtualenv", self.tempdir]) + + def tearDown(self): + shutil.rmtree(self.tempdir) + + def virtualenv_bin(self, cmd): + return os.path.join(self.tempdir, "bin", cmd) + + def test_install(self): + subprocess.check_call([self.virtualenv_bin("python"), "setup.py", "install"]) + + def test_cli(self): + self.test_install() + subprocess.check_call([self.virtualenv_bin("acme-tiny"), "-h"]) diff -Nru acme-tiny-20171115/tests/test_module.py acme-tiny-4.0.4/tests/test_module.py --- acme-tiny-20171115/tests/test_module.py 2017-11-15 05:30:52.000000000 +0000 +++ acme-tiny-4.0.4/tests/test_module.py 2018-05-17 13:43:30.000000000 +0100 @@ -1,4 +1,4 @@ -import unittest, os, sys, tempfile +import unittest, os, sys, tempfile, logging from subprocess import Popen, PIPE try: from StringIO import StringIO # Python 2 @@ -14,7 +14,7 @@ "Tests for acme_tiny.get_crt()" def setUp(self): - self.CA = "https://acme-staging.api.letsencrypt.org" + self.DIR_URL = "https://acme-staging-v02.api.letsencrypt.org/directory" self.tempdir = tempfile.mkdtemp() self.fuse_proc = Popen(["python", "tests/monkey.py", self.tempdir]) @@ -31,13 +31,12 @@ "--account-key", KEYS['account_key'].name, "--csr", KEYS['domain_csr'].name, "--acme-dir", self.tempdir, - "--ca", self.CA, + "--directory-url", self.DIR_URL, ]) sys.stdout.seek(0) crt = sys.stdout.read().encode("utf8") sys.stdout = old_stdout - out, err = Popen(["openssl", "x509", "-text", "-noout"], stdin=PIPE, - stdout=PIPE, stderr=PIPE).communicate(crt) + out, err = Popen(["openssl", "x509", "-text", "-noout"], stdin=PIPE, stdout=PIPE, stderr=PIPE).communicate(crt) self.assertIn("Issuer: CN=Fake LE Intermediate", out.decode("utf8")) def test_success_san(self): @@ -48,13 +47,12 @@ "--account-key", KEYS['account_key'].name, "--csr", KEYS['san_csr'].name, "--acme-dir", self.tempdir, - "--ca", self.CA, + "--directory-url", self.DIR_URL, ]) sys.stdout.seek(0) crt = sys.stdout.read().encode("utf8") sys.stdout = old_stdout - out, err = Popen(["openssl", "x509", "-text", "-noout"], stdin=PIPE, - stdout=PIPE, stderr=PIPE).communicate(crt) + out, err = Popen(["openssl", "x509", "-text", "-noout"], stdin=PIPE, stdout=PIPE, stderr=PIPE).communicate(crt) self.assertIn("Issuer: CN=Fake LE Intermediate", out.decode("utf8")) def test_success_cli(self): @@ -64,10 +62,9 @@ "--account-key", KEYS['account_key'].name, "--csr", KEYS['domain_csr'].name, "--acme-dir", self.tempdir, - "--ca", self.CA, + "--directory-url", self.DIR_URL, ], stdout=PIPE, stderr=PIPE).communicate() - out, err = Popen(["openssl", "x509", "-text", "-noout"], stdin=PIPE, - stdout=PIPE, stderr=PIPE).communicate(crt) + out, err = Popen(["openssl", "x509", "-text", "-noout"], stdin=PIPE, stdout=PIPE, stderr=PIPE).communicate(crt) self.assertIn("Issuer: CN=Fake LE Intermediate", out.decode("utf8")) def test_missing_account_key(self): @@ -77,7 +74,7 @@ "--account-key", "/foo/bar", "--csr", KEYS['domain_csr'].name, "--acme-dir", self.tempdir, - "--ca", self.CA, + "--directory-url", self.DIR_URL, ]) except Exception as e: result = e @@ -91,7 +88,7 @@ "--account-key", KEYS['account_key'].name, "--csr", "/foo/bar", "--acme-dir", self.tempdir, - "--ca", self.CA, + "--directory-url", self.DIR_URL, ]) except Exception as e: result = e @@ -105,7 +102,7 @@ "--account-key", KEYS['weak_key'].name, "--csr", KEYS['domain_csr'].name, "--acme-dir", self.tempdir, - "--ca", self.CA, + "--directory-url", self.DIR_URL, ]) except Exception as e: result = e @@ -119,21 +116,21 @@ "--account-key", KEYS['account_key'].name, "--csr", KEYS['invalid_csr'].name, "--acme-dir", self.tempdir, - "--ca", self.CA, + "--directory-url", self.DIR_URL, ]) except Exception as e: result = e self.assertIsInstance(result, ValueError) self.assertIn("Invalid character in DNS name", result.args[0]) - def test_nonexistant_domain(self): + def test_nonexistent_domain(self): """ Should be unable verify a nonexistent domain """ try: result = acme_tiny.main([ "--account-key", KEYS['account_key'].name, "--csr", KEYS['nonexistent_csr'].name, "--acme-dir", self.tempdir, - "--ca", self.CA, + "--directory-url", self.DIR_URL, ]) except Exception as e: result = e @@ -147,10 +144,38 @@ "--account-key", KEYS['account_key'].name, "--csr", KEYS['account_csr'].name, "--acme-dir", self.tempdir, - "--ca", self.CA, + "--directory-url", self.DIR_URL, ]) except Exception as e: result = e self.assertIsInstance(result, ValueError) self.assertIn("certificate public key must be different than account key", result.args[0]) + def test_contact(self): + """ Make sure optional contact details can be set """ + # add a logging handler that captures the info log output + log_output = StringIO() + debug_handler = logging.StreamHandler(log_output) + acme_tiny.LOGGER.addHandler(debug_handler) + # call acme_tiny with new contact details + old_stdout = sys.stdout + sys.stdout = StringIO() + result = acme_tiny.main([ + "--account-key", KEYS['account_key'].name, + "--csr", KEYS['domain_csr'].name, + "--acme-dir", self.tempdir, + "--directory-url", self.DIR_URL, + "--contact", "mailto:devteam@example.com", "mailto:boss@example.com", + ]) + sys.stdout.seek(0) + crt = sys.stdout.read().encode("utf8") + sys.stdout = old_stdout + log_output.seek(0) + log_string = log_output.read().encode("utf8") + # make sure the certificate was issued and the contact details were updated + out, err = Popen(["openssl", "x509", "-text", "-noout"], stdin=PIPE, stdout=PIPE, stderr=PIPE).communicate(crt) + self.assertIn("Issuer: CN=Fake LE Intermediate", out.decode("utf8")) + self.assertIn("Updated contact details:\nmailto:devteam@example.com\nmailto:boss@example.com", log_string.decode("utf8")) + # remove logging capture + acme_tiny.LOGGER.removeHandler(debug_handler) + diff -Nru acme-tiny-20171115/.travis.yml acme-tiny-4.0.4/.travis.yml --- acme-tiny-20171115/.travis.yml 2017-11-15 05:30:52.000000000 +0000 +++ acme-tiny-4.0.4/.travis.yml 2018-05-17 13:43:30.000000000 +0100 @@ -14,6 +14,6 @@ install: - pip install -r tests/requirements.txt script: - - coverage run --source ./ --omit ./tests/server.py -m unittest tests + - coverage run --source ./ --omit ./tests/server.py,./setup.py -m unittest tests after_success: - coveralls