Commit b8d6226a authored by Joshua Tauberer's avatar Joshua Tauberer

when provisioning tls certs from the command line, specify domain names as...

when provisioning tls certs from the command line, specify domain names as command line arguments to force getting certs for those domains
parent bac15d39
...@@ -335,7 +335,7 @@ def ssl_get_status(): ...@@ -335,7 +335,7 @@ def ssl_get_status():
provision, cant_provision = get_certificates_to_provision(env, ok_as_problem=False) provision, cant_provision = get_certificates_to_provision(env, ok_as_problem=False)
domains_status = get_web_domains_info(env) domains_status = get_web_domains_info(env)
return json_response({ 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) ], "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": [{ "domain": d["domain"], "status": d["ssl_certificate"][0], "text": d["ssl_certificate"][1] } for d in domains_status ],
}) })
......
...@@ -156,7 +156,7 @@ def get_domain_ssl_files(domain, ssl_certificates, env, allow_missing_cert=False ...@@ -156,7 +156,7 @@ def get_domain_ssl_files(domain, ssl_certificates, env, allow_missing_cert=False
# PROVISIONING CERTIFICATES FROM LETSENCRYPT # 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 # 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 # for. Provision if a domain name has no valid certificate or if any
# certificate is expiring in 14 days. If provisioning anything, also # certificate is expiring in 14 days. If provisioning anything, also
...@@ -175,6 +175,13 @@ def get_certificates_to_provision(env, ok_as_problem=True): ...@@ -175,6 +175,13 @@ def get_certificates_to_provision(env, ok_as_problem=True):
domains_if_any = set() domains_if_any = set()
problems = { } problems = { }
for domain in get_web_domains(env): 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: try:
cert = get_domain_ssl_files(domain, certs, env, allow_missing_cert=True) cert = get_domain_ssl_files(domain, certs, env, allow_missing_cert=True)
except FileNotFoundError as e: except FileNotFoundError as e:
...@@ -201,8 +208,9 @@ def get_certificates_to_provision(env, ok_as_problem=True): ...@@ -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." problems[domain] = "The certificate is valid for at least another 30 days --- no need to replace."
# Warn the user about domains hosted elsewhere. # Warn the user about domains hosted elsewhere.
for domain in set(get_web_domains(env, exclude_dns_elsewhere=False)) - set(get_web_domains(env)): if force_domains is None:
problems[domain] = "The domain's DNS is pointed elsewhere, so a TLS certificate is not necessary here and cannot be provisioned automatically anyway." 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. # Filter out domains that we can't provision a certificate for.
def can_provision_for_domain(domain): def can_provision_for_domain(domain):
...@@ -245,7 +253,7 @@ def get_certificates_to_provision(env, ok_as_problem=True): ...@@ -245,7 +253,7 @@ def get_certificates_to_provision(env, ok_as_problem=True):
return (domains, problems) 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 requests.exceptions
import acme.messages import acme.messages
...@@ -253,7 +261,7 @@ def provision_certificates(env, agree_to_tos_url=None, logger=None): ...@@ -253,7 +261,7 @@ def provision_certificates(env, agree_to_tos_url=None, logger=None):
# What domains should we provision certificates for? And what # What domains should we provision certificates for? And what
# errors prevent provisioning for other domains. # 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. # Exit fast if there is nothing to do.
if len(domains) == 0: if len(domains) == 0:
...@@ -395,6 +403,24 @@ def provision_certificates_cmdline(): ...@@ -395,6 +403,24 @@ def provision_certificates_cmdline():
exclusive_process("update_tls_certificates") exclusive_process("update_tls_certificates")
env = load_environment() 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 agree_to_tos_url = None
while True: while True:
# Run the provisioning script. This installs certificates. If there are # Run the provisioning script. This installs certificates. If there are
...@@ -402,14 +428,14 @@ def provision_certificates_cmdline(): ...@@ -402,14 +428,14 @@ def provision_certificates_cmdline():
# certificates for groups of domains. We have to check the result for # certificates for groups of domains. We have to check the result for
# each group. # each group.
def my_logger(message): def my_logger(message):
if "-v" in sys.argv: if verbose:
print(">", message) 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 agree_to_tos_url = None # reset to prevent infinite looping
if not status["requests"]: if not status["requests"]:
# No domains need certificates. # 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: if len(status["problems"]) == 0:
print("No domains hosted on this box need a new TLS certificate at this time.") print("No domains hosted on this box need a new TLS certificate at this time.")
elif len(status["problems"]) > 0: elif len(status["problems"]) > 0:
...@@ -430,7 +456,7 @@ def provision_certificates_cmdline(): ...@@ -430,7 +456,7 @@ def provision_certificates_cmdline():
continue continue
# Can't ask the user a question in this mode. # 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.") print("Can't issue TLS certficate until user has agreed to Let's Encrypt TOS.")
sys.exit(1) sys.exit(1)
......
...@@ -3,9 +3,9 @@ ...@@ -3,9 +3,9 @@
<h2>TLS (SSL) Certificates</h2> <h2>TLS (SSL) Certificates</h2>
<p>An SSL certificate is a cryptographic file that proves to anyone connecting to this machine (like you right now) that the connection is secure.</p> <p>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.</p>
<p>You need an SSL certificate for this box&rsquo;s hostname ({{hostname}}), and although optional you should also get one for every domain name and subdomain managed by this box (unless you&rsquo;ve directed DNS for a domain elsewhere through custom or external DNS).</p> <p>You need a TLS certificate for this box&rsquo;s hostname ({{hostname}}) and every other domain name and subdomain that this box is hosting a website for (see the list below).</p>
<h3>Provision a Certificate</h3> <h3>Provision a Certificate</h3>
...@@ -87,12 +87,17 @@ function show_tls() { ...@@ -87,12 +87,17 @@ function show_tls() {
function(res) { function(res) {
// provisioning status // provisioning status
if (res.can_provision.length > 0) { 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(", ")); .text("Domains: " + res.can_provision.join(", "));
} else if (res.cant_provision.length == 0) { } 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 { } 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').toggle(res.cant_provision.length > 0);
$('#ssl_provision_problems tbody').text(""); $('#ssl_provision_problems tbody').text("");
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment