From 2882e63dd8965d6048f60c458846b25a4c7c28d1 Mon Sep 17 00:00:00 2001 From: Joshua Tauberer Date: Mon, 4 Jan 2016 18:22:02 -0500 Subject: [PATCH] second part of provisioning tls certificates from the control panel --- management/daemon.py | 31 ++++++- management/ssl_certificates.py | 13 ++- management/templates/ssl.html | 147 ++++++++++++++++++++++++++++----- 3 files changed, 160 insertions(+), 31 deletions(-) diff --git a/management/daemon.py b/management/daemon.py index 926c65b3..1099a596 100755 --- a/management/daemon.py +++ b/management/daemon.py @@ -331,13 +331,27 @@ def dns_get_dump(): @authorized_personnel_only def ssl_get_status(): from ssl_certificates import get_certificates_to_provision - from web_update import get_web_domains_info - provision, cant_provision = get_certificates_to_provision(env, ok_as_problem=False) + from web_update import get_web_domains_info, get_web_domains + + # What domains can we provision certificates for? What unexpected problems do we have? + provision, cant_provision = get_certificates_to_provision(env, show_extended_problems=False) + + # What's the current status of TLS certificates on all of the domain? domains_status = get_web_domains_info(env) + domains_status = [{ "domain": d["domain"], "status": d["ssl_certificate"][0], "text": d["ssl_certificate"][1] } for d in domains_status ] + + # Warn the user about domain names not hosted here because of other settings. + for domain in set(get_web_domains(env, exclude_dns_elsewhere=False)) - set(get_web_domains(env)): + domains_status.append({ + "domain": domain, + "status": "not-applicable", + "text": "The domain's website is hosted elsewhere.", + }) + return json_response({ "can_provision": utils.sort_domains(provision, env), "cant_provision": [{ "domain": domain, "problem": cant_provision[domain] } for domain in utils.sort_domains(cant_provision, env) ], - "status": [{ "domain": d["domain"], "status": d["ssl_certificate"][0], "text": d["ssl_certificate"][1] } for d in domains_status ], + "status": domains_status, }) @app.route('/ssl/csr/', methods=['POST']) @@ -359,6 +373,17 @@ def ssl_install_cert(): return "Invalid domain name." return install_cert(domain, ssl_cert, ssl_chain, env) +@app.route('/ssl/provision', methods=['POST']) +@authorized_personnel_only +def ssl_provision_certs(): + from ssl_certificates import provision_certificates + agree_to_tos_url = request.form.get('agree_to_tos_url') + status = provision_certificates(env, + agree_to_tos_url=agree_to_tos_url, + jsonable=True) + return json_response(status) + + # WEB @app.route('/web/domains') diff --git a/management/ssl_certificates.py b/management/ssl_certificates.py index 0d72cc1c..35379198 100755 --- a/management/ssl_certificates.py +++ b/management/ssl_certificates.py @@ -156,7 +156,7 @@ def get_domain_ssl_files(domain, ssl_certificates, env, allow_missing_cert=False # PROVISIONING CERTIFICATES FROM LETSENCRYPT -def get_certificates_to_provision(env, ok_as_problem=True, force_domains=None): +def get_certificates_to_provision(env, show_extended_problems=True, force_domains=None): # Get a set of domain names that we should now provision certificates # for. Provision if a domain name has no valid certificate or if any # certificate is expiring in 14 days. If provisioning anything, also @@ -204,13 +204,13 @@ def get_certificates_to_provision(env, ok_as_problem=True, force_domains=None): domains_if_any.add(domain) # It's valid. Should we report its validness? - if ok_as_problem: + if show_extended_problems: problems[domain] = "The certificate is valid for at least another 30 days --- no need to replace." # Warn the user about domains hosted elsewhere. - if force_domains is None: + if not force_domains and show_extended_problems: for domain in set(get_web_domains(env, exclude_dns_elsewhere=False)) - set(get_web_domains(env)): - problems[domain] = "The domain's DNS is pointed elsewhere, so a TLS certificate is not necessary here and cannot be provisioned automatically anyway." + problems[domain] = "The domain's DNS is pointed elsewhere, so there is no point to installing a TLS certificate here and we could not automatically provision one anyway because provisioning requires access to the website (which isn't here)." # Filter out domains that we can't provision a certificate for. def can_provision_for_domain(domain): @@ -253,7 +253,7 @@ def get_certificates_to_provision(env, ok_as_problem=True, force_domains=None): return (domains, problems) -def provision_certificates(env, agree_to_tos_url=None, logger=None, force_domains=None): +def provision_certificates(env, agree_to_tos_url=None, logger=None, force_domains=None, jsonable=False): import requests.exceptions import acme.messages @@ -324,7 +324,6 @@ def provision_certificates(env, agree_to_tos_url=None, logger=None, force_domain except client.NeedToTakeAction as e: # Write out the ACME challenge files. - for action in e.actions: if isinstance(action, client.NeedToInstallFile): fn = os.path.join(challenges_path, action.file_name) @@ -355,7 +354,7 @@ def provision_certificates(env, agree_to_tos_url=None, logger=None, force_domain import time, datetime ret_item.update({ "result": "wait", - "until": e.until_when, #.isoformat(), + "until": e.until_when if not jsonable else e.until_when.isoformat(), "seconds": (e.until_when - datetime.datetime.now()).total_seconds() }) diff --git a/management/templates/ssl.html b/management/templates/ssl.html index 336eabd3..ca9de298 100644 --- a/management/templates/ssl.html +++ b/management/templates/ssl.html @@ -7,13 +7,22 @@

You need a TLS certificate for this box’s hostname ({{hostname}}) and every other domain name and subdomain that this box is hosting a website for (see the list below).

+

Provision a Certificate

-

We can provision an SSL certificate for you from Let’s Encrypt, a free SSL certificate provider.

+ -

+
- +
+ + @@ -23,9 +32,14 @@
Domain
+

Use the Install Certificate button below for these domains.

+
+

Certificate Status

+

Certificates expire after a period of time. All certificates will be automatically renewed through Let’s Encrypt 14 days prior to expiration.

+ @@ -38,7 +52,6 @@ -

A multi-domain or wildcard certificate will be automatically applied to any domains it is valid for.

Install Certificate

@@ -48,6 +61,8 @@

+

(A multi-domain or wildcard certificate will be automatically applied to any domains it is valid for besides the one you choose above.)

+

What country are you in? This is required by some TLS certificate providers. You may leave this blank if you know your TLS certificate provider doesn't require it.