diff --git a/management/daemon.py b/management/daemon.py index 6ba111d7..926c65b3 100755 --- a/management/daemon.py +++ b/management/daemon.py @@ -335,7 +335,7 @@ def ssl_get_status(): provision, cant_provision = get_certificates_to_provision(env, ok_as_problem=False) domains_status = get_web_domains_info(env) return json_response({ - "can_provision": list(provision), + "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 ], }) diff --git a/management/ssl_certificates.py b/management/ssl_certificates.py index fd922ede..0d72cc1c 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): +def get_certificates_to_provision(env, ok_as_problem=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 @@ -175,6 +175,13 @@ def get_certificates_to_provision(env, ok_as_problem=True): domains_if_any = set() problems = { } for domain in get_web_domains(env): + # If the user really wants a cert for certain domains, include it. + if force_domains: + if force_domains == "ALL" or (isinstance(force_domains, list) and domain in force_domains): + domains.add(domain) + continue + + # Include this domain if its certificate is missing, self-signed, or expiring soon. try: cert = get_domain_ssl_files(domain, certs, env, allow_missing_cert=True) except FileNotFoundError as e: @@ -201,8 +208,9 @@ def get_certificates_to_provision(env, ok_as_problem=True): problems[domain] = "The certificate is valid for at least another 30 days --- no need to replace." # Warn the user about domains hosted elsewhere. - 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." + if force_domains is None: + 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." # Filter out domains that we can't provision a certificate for. def can_provision_for_domain(domain): @@ -245,7 +253,7 @@ def get_certificates_to_provision(env, ok_as_problem=True): return (domains, problems) -def provision_certificates(env, agree_to_tos_url=None, logger=None): +def provision_certificates(env, agree_to_tos_url=None, logger=None, force_domains=None): import requests.exceptions import acme.messages @@ -253,7 +261,7 @@ def provision_certificates(env, agree_to_tos_url=None, logger=None): # What domains should we provision certificates for? And what # errors prevent provisioning for other domains. - domains, problems = get_certificates_to_provision(env) + domains, problems = get_certificates_to_provision(env, force_domains=force_domains) # Exit fast if there is nothing to do. if len(domains) == 0: @@ -395,6 +403,24 @@ def provision_certificates_cmdline(): exclusive_process("update_tls_certificates") env = load_environment() + verbose = False + headless = False + force_domains = None + + args = list(sys.argv) + args.pop(0) # program name + if args and args[0] == "-v": + verbose = True + args.pop(0) + if args and args[0] == "--headless": + headless = True + args.pop(0) + if args and args[0] == "--force": + force_domains = "ALL" + args.pop(0) + else: + force_domains = args + agree_to_tos_url = None while True: # Run the provisioning script. This installs certificates. If there are @@ -402,14 +428,14 @@ def provision_certificates_cmdline(): # certificates for groups of domains. We have to check the result for # each group. def my_logger(message): - if "-v" in sys.argv: + if verbose: print(">", message) - status = provision_certificates(env, agree_to_tos_url=agree_to_tos_url, logger=my_logger) + status = provision_certificates(env, agree_to_tos_url=agree_to_tos_url, logger=my_logger, force_domains=force_domains) agree_to_tos_url = None # reset to prevent infinite looping if not status["requests"]: # No domains need certificates. - if "--headless" not in sys.argv or "-v" in sys.argv: + if not headless or verbose: if len(status["problems"]) == 0: print("No domains hosted on this box need a new TLS certificate at this time.") elif len(status["problems"]) > 0: @@ -430,7 +456,7 @@ def provision_certificates_cmdline(): continue # Can't ask the user a question in this mode. - if "--headless" in sys.argv: + if headless in sys.argv: print("Can't issue TLS certficate until user has agreed to Let's Encrypt TOS.") sys.exit(1) diff --git a/management/templates/ssl.html b/management/templates/ssl.html index 2f9a1cab..336eabd3 100644 --- a/management/templates/ssl.html +++ b/management/templates/ssl.html @@ -3,9 +3,9 @@

TLS (SSL) Certificates

-

An SSL certificate is a cryptographic file that proves to anyone connecting to this machine (like you right now) that the connection is secure.

+

A TLS (formerly called SSL) certificate is a cryptographic file that proves to anyone connecting to a web address that the connection is secure between you and the owner of that address.

-

You need an SSL certificate for this box’s hostname ({{hostname}}), and although optional you should also get one for every domain name and subdomain managed by this box (unless you’ve directed DNS for a domain elsewhere through custom or external DNS).

+

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

@@ -87,12 +87,17 @@ function show_tls() { function(res) { // provisioning status if (res.can_provision.length > 0) { - $('#ssl_provision_status').removeClass("text-warning").removeClass("text-success") + $('#ssl_provision_status') + .removeClass("text-warning").removeClass("text-success").addClass("text-danger") .text("Domains: " + res.can_provision.join(", ")); } else if (res.cant_provision.length == 0) { - $('#ssl_provision_status').addClass("text-success").text("No domains hosted on this box need a new TLS certificate at this time."); + $('#ssl_provision_status') + .addClass("text-success").removeClass("text-warning").removeClass("text-danger") + .text("No domains hosted on this box need a new TLS certificate at this time."); } else { - $('#ssl_provision_status').addClass("text-warning").text("No TLS certificates can be provisoned at this time:"); + $('#ssl_provision_status') + .removeClass("text-success").addClass("text-warning").removeClass("text-danger") + .text("No TLS certificates can be provisoned at this time:"); } $('#ssl_provision_problems').toggle(res.cant_provision.length > 0); $('#ssl_provision_problems tbody').text("");