annotate host/acme_tiny.py @ 2034:d718511fc69f acme-tiny

Begin work on moving to tiny-acme.
author Violet7
date Tue, 04 Nov 2025 20:28:50 -0800
parents
children
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
rev   line source
2034
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
1 #!/usr/bin/env python3
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
2 # Copyright Daniel Roesler, under MIT license, see LICENSE at github.com/diafygi/acme-tiny
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
3 import argparse, subprocess, json, os, sys, base64, binascii, time, hashlib, re, copy, textwrap, logging
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
4 try:
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
5 from urllib.request import urlopen, Request # Python 3
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
6 except ImportError: # pragma: no cover
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
7 from urllib2 import urlopen, Request # Python 2
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
8
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
9 DEFAULT_CA = "https://acme-v02.api.letsencrypt.org" # DEPRECATED! USE DEFAULT_DIRECTORY_URL INSTEAD
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
10 DEFAULT_DIRECTORY_URL = "https://acme-v02.api.letsencrypt.org/directory"
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
11
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
12 LOGGER = logging.getLogger(__name__)
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
13 LOGGER.addHandler(logging.StreamHandler())
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
14 LOGGER.setLevel(logging.INFO)
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
15
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
16 def get_crt(account_key, csr, acme_dir, log=LOGGER, CA=DEFAULT_CA, disable_check=False, directory_url=DEFAULT_DIRECTORY_URL, contact=None, check_port=None):
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
17 directory, acct_headers, alg, jwk = None, None, None, None # global variables
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
18
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
19 # helper functions - base64 encode for jose spec
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
20 def _b64(b):
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
21 return base64.urlsafe_b64encode(b).decode('utf8').replace("=", "")
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
22
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
23 # helper function - run external commands
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
24 def _cmd(cmd_list, stdin=None, cmd_input=None, err_msg="Command Line Error"):
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
25 proc = subprocess.Popen(cmd_list, stdin=stdin, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
26 out, err = proc.communicate(cmd_input)
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
27 if proc.returncode != 0:
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
28 raise IOError("{0}\n{1}".format(err_msg, err))
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
29 return out
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
30
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
31 # helper function - make request and automatically parse json response
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
32 def _do_request(url, data=None, err_msg="Error", depth=0):
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
33 try:
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
34 resp = urlopen(Request(url, data=data, headers={"Content-Type": "application/jose+json", "User-Agent": "acme-tiny"}))
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
35 resp_data, code, headers = resp.read().decode("utf8"), resp.getcode(), resp.headers
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
36 except IOError as e:
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
37 resp_data = e.read().decode("utf8") if hasattr(e, "read") else str(e)
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
38 code, headers = getattr(e, "code", None), {}
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
39 try:
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
40 resp_data = json.loads(resp_data) # try to parse json results
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
41 except ValueError:
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
42 pass # ignore json parsing errors
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
43 if depth < 100 and code == 400 and resp_data['type'] == "urn:ietf:params:acme:error:badNonce":
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
44 raise IndexError(resp_data) # allow 100 retrys for bad nonces
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
45 if code not in [200, 201, 204]:
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
46 raise ValueError("{0}:\nUrl: {1}\nData: {2}\nResponse Code: {3}\nResponse: {4}".format(err_msg, url, data, code, resp_data))
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
47 return resp_data, code, headers
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
48
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
49 # helper function - make signed requests
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
50 def _send_signed_request(url, payload, err_msg, depth=0):
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
51 payload64 = "" if payload is None else _b64(json.dumps(payload).encode('utf8'))
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
52 new_nonce = _do_request(directory['newNonce'])[2]['Replay-Nonce']
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
53 protected = {"url": url, "alg": alg, "nonce": new_nonce}
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
54 protected.update({"jwk": jwk} if acct_headers is None else {"kid": acct_headers['Location']})
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
55 protected64 = _b64(json.dumps(protected).encode('utf8'))
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
56 protected_input = "{0}.{1}".format(protected64, payload64).encode('utf8')
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
57 out = _cmd(["openssl", "dgst", "-sha256", "-sign", account_key], stdin=subprocess.PIPE, cmd_input=protected_input, err_msg="OpenSSL Error")
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
58 data = json.dumps({"protected": protected64, "payload": payload64, "signature": _b64(out)})
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
59 try:
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
60 return _do_request(url, data=data.encode('utf8'), err_msg=err_msg, depth=depth)
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
61 except IndexError: # retry bad nonces (they raise IndexError)
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
62 return _send_signed_request(url, payload, err_msg, depth=(depth + 1))
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
63
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
64 # helper function - poll until complete
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
65 def _poll_until_not(url, pending_statuses, err_msg):
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
66 result, t0 = None, time.time()
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
67 while result is None or result['status'] in pending_statuses:
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
68 assert (time.time() - t0 < 3600), "Polling timeout" # 1 hour timeout
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
69 time.sleep(0 if result is None else 2)
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
70 result, _, _ = _send_signed_request(url, None, err_msg)
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
71 return result
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
72
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
73 # parse account key to get public key
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
74 log.info("Parsing account key...")
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
75 out = _cmd(["openssl", "rsa", "-in", account_key, "-noout", "-text"], err_msg="OpenSSL Error")
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
76 pub_pattern = r"modulus:[\s]+?00:([a-f0-9\:\s]+?)\npublicExponent: ([0-9]+)"
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
77 pub_hex, pub_exp = re.search(pub_pattern, out.decode('utf8'), re.MULTILINE|re.DOTALL).groups()
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
78 pub_exp = "{0:x}".format(int(pub_exp))
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
79 pub_exp = "0{0}".format(pub_exp) if len(pub_exp) % 2 else pub_exp
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
80 alg, jwk = "RS256", {
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
81 "e": _b64(binascii.unhexlify(pub_exp.encode("utf-8"))),
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
82 "kty": "RSA",
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
83 "n": _b64(binascii.unhexlify(re.sub(r"(\s|:)", "", pub_hex).encode("utf-8"))),
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
84 }
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
85 accountkey_json = json.dumps(jwk, sort_keys=True, separators=(',', ':'))
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
86 thumbprint = _b64(hashlib.sha256(accountkey_json.encode('utf8')).digest())
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
87
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
88 # find domains
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
89 log.info("Parsing CSR...")
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
90 out = _cmd(["openssl", "req", "-in", csr, "-noout", "-text"], err_msg="Error loading {0}".format(csr))
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
91 domains = set([])
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
92 common_name = re.search(r"Subject:.*? CN\s?=\s?([^\s,;/]+)", out.decode('utf8'))
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
93 if common_name is not None:
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
94 domains.add(common_name.group(1))
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
95 subject_alt_names = re.search(r"X509v3 Subject Alternative Name: (?:critical)?\n +([^\n]+)\n", out.decode('utf8'), re.MULTILINE|re.DOTALL)
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
96 if subject_alt_names is not None:
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
97 for san in subject_alt_names.group(1).split(", "):
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
98 if san.startswith("DNS:"):
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
99 domains.add(san[4:])
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
100 log.info(u"Found domains: {0}".format(", ".join(domains)))
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
101
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
102 # get the ACME directory of urls
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
103 log.info("Getting directory...")
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
104 directory_url = CA + "/directory" if CA != DEFAULT_CA else directory_url # backwards compatibility with deprecated CA kwarg
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
105 directory, _, _ = _do_request(directory_url, err_msg="Error getting directory")
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
106 log.info("Directory found!")
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
107
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
108 # create account, update contact details (if any), and set the global key identifier
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
109 log.info("Registering account...")
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
110 reg_payload = {"termsOfServiceAgreed": True} if contact is None else {"termsOfServiceAgreed": True, "contact": contact}
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
111 account, code, acct_headers = _send_signed_request(directory['newAccount'], reg_payload, "Error registering")
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
112 log.info("{0} Account ID: {1}".format("Registered!" if code == 201 else "Already registered!", acct_headers['Location']))
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
113 if contact is not None:
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
114 account, _, _ = _send_signed_request(acct_headers['Location'], {"contact": contact}, "Error updating contact details")
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
115 log.info("Updated contact details:\n{0}".format("\n".join(account.get('contact') or [])))
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
116
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
117 # create a new order
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
118 log.info("Creating new order...")
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
119 order_payload = {"identifiers": [{"type": "dns", "value": d} for d in domains]}
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
120 order, _, order_headers = _send_signed_request(directory['newOrder'], order_payload, "Error creating new order")
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
121 log.info("Order created!")
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
122
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
123 # get the authorizations that need to be completed
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
124 for auth_url in order['authorizations']:
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
125 authorization, _, _ = _send_signed_request(auth_url, None, "Error getting challenges")
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
126 domain = authorization['identifier']['value']
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
127
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
128 # skip if already valid
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
129 if authorization['status'] == "valid":
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
130 log.info("Already verified: {0}, skipping...".format(domain))
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
131 continue
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
132 log.info("Verifying {0}...".format(domain))
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
133
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
134 # find the http-01 challenge and write the challenge file
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
135 challenge = [c for c in authorization['challenges'] if c['type'] == "http-01"][0]
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
136 token = re.sub(r"[^A-Za-z0-9_\-]", "_", challenge['token'])
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
137 keyauthorization = "{0}.{1}".format(token, thumbprint)
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
138 wellknown_path = os.path.join(acme_dir, token)
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
139 with open(wellknown_path, "w") as wellknown_file:
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
140 wellknown_file.write(keyauthorization)
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
141
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
142 # check that the file is in place
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
143 try:
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
144 wellknown_url = "http://{0}{1}/.well-known/acme-challenge/{2}".format(domain, "" if check_port is None else ":{0}".format(check_port), token)
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
145 assert (disable_check or _do_request(wellknown_url)[0] == keyauthorization)
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
146 except (AssertionError, ValueError) as e:
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
147 raise ValueError("Wrote file to {0}, but couldn't download {1}: {2}".format(wellknown_path, wellknown_url, e))
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
148
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
149 # say the challenge is done
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
150 _send_signed_request(challenge['url'], {}, "Error submitting challenges: {0}".format(domain))
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
151 authorization = _poll_until_not(auth_url, ["pending"], "Error checking challenge status for {0}".format(domain))
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
152 if authorization['status'] != "valid":
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
153 raise ValueError("Challenge did not pass for {0}: {1}".format(domain, authorization))
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
154 os.remove(wellknown_path)
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
155 log.info("{0} verified!".format(domain))
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
156
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
157 # finalize the order with the csr
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
158 log.info("Signing certificate...")
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
159 csr_der = _cmd(["openssl", "req", "-in", csr, "-outform", "DER"], err_msg="DER Export Error")
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
160 _send_signed_request(order['finalize'], {"csr": _b64(csr_der)}, "Error finalizing order")
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
161
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
162 # poll the order to monitor when it's done
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
163 order = _poll_until_not(order_headers['Location'], ["pending", "processing"], "Error checking order status")
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
164 if order['status'] != "valid":
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
165 raise ValueError("Order failed: {0}".format(order))
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
166
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
167 # download the certificate
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
168 certificate_pem, _, _ = _send_signed_request(order['certificate'], None, "Certificate download failed")
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
169 log.info("Certificate signed!")
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
170 return certificate_pem
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
171
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
172 def main(argv=None):
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
173 parser = argparse.ArgumentParser(
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
174 formatter_class=argparse.RawDescriptionHelpFormatter,
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
175 description=textwrap.dedent("""\
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
176 This script automates the process of getting a signed TLS certificate from Let's Encrypt using the ACME protocol.
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
177 It will need to be run on your server and have access to your private account key, so PLEASE READ THROUGH IT!
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
178 It's only ~200 lines, so it won't take long.
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
179
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
180 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
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
181 """)
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
182 )
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
183 parser.add_argument("--account-key", required=True, help="path to your Let's Encrypt account private key")
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
184 parser.add_argument("--csr", required=True, help="path to your certificate signing request")
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
185 parser.add_argument("--acme-dir", required=True, help="path to the .well-known/acme-challenge/ directory")
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
186 parser.add_argument("--quiet", action="store_const", const=logging.ERROR, help="suppress output except for errors")
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
187 parser.add_argument("--disable-check", default=False, action="store_true", help="disable checking if the challenge file is hosted correctly before telling the CA")
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
188 parser.add_argument("--directory-url", default=DEFAULT_DIRECTORY_URL, help="certificate authority directory url, default is Let's Encrypt")
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
189 parser.add_argument("--ca", default=DEFAULT_CA, help="DEPRECATED! USE --directory-url INSTEAD!")
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
190 parser.add_argument("--contact", metavar="CONTACT", default=None, nargs="*", help="Contact details (e.g. mailto:aaa@bbb.com) for your account-key")
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
191 parser.add_argument("--check-port", metavar="PORT", default=None, help="what port to use when self-checking the challenge file, default is port 80")
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
192
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
193 args = parser.parse_args(argv)
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
194 LOGGER.setLevel(args.quiet or LOGGER.level)
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
195 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, check_port=args.check_port)
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
196 sys.stdout.write(signed_crt)
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
197
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
198 if __name__ == "__main__": # pragma: no cover
d718511fc69f Begin work on moving to tiny-acme.
Violet7
parents:
diff changeset
199 main(sys.argv[1:])