def get_domain_ssl_files(domain, env, allow_shared_cert=True): # What SSL private key will we use? Allow the user to override this, but # in many cases using the same private key for all domains would be fine. # Don't allow the user to override the key for PRIMARY_HOSTNAME because # that's what's in the main file. ssl_key = os.path.join(env["STORAGE_ROOT"], 'ssl/ssl_private_key.pem') ssl_key_is_alt = False alt_key = os.path.join(env["STORAGE_ROOT"], 'ssl/%s/private_key.pem' % safe_domain_name(domain)) if domain != env['PRIMARY_HOSTNAME'] and os.path.exists(alt_key): ssl_key = alt_key ssl_key_is_alt = True # What SSL certificate will we use? ssl_certificate_primary = os.path.join(env["STORAGE_ROOT"], 'ssl/ssl_certificate.pem') ssl_via = None if domain == env['PRIMARY_HOSTNAME']: # For PRIMARY_HOSTNAME, use the one we generated at set-up time. ssl_certificate = ssl_certificate_primary else: # For other domains, we'll probably use a certificate in a different path. ssl_certificate = os.path.join( env["STORAGE_ROOT"], 'ssl/%s/ssl_certificate.pem' % safe_domain_name(domain)) # But we can be smart and reuse the main SSL certificate if is has # a Subject Alternative Name matching this domain. Don't do this if # the user has uploaded a different private key for this domain. if not ssl_key_is_alt and allow_shared_cert: from status_checks import check_certificate if check_certificate(domain, ssl_certificate_primary, None, just_check_domain=True)[0] == "OK": ssl_certificate = ssl_certificate_primary ssl_via = "Using multi/wildcard certificate of %s." % env[ 'PRIMARY_HOSTNAME'] # For a 'www.' domain, see if we can reuse the cert of the parent. elif domain.startswith('www.'): ssl_certificate_parent = os.path.join( env["STORAGE_ROOT"], 'ssl/%s/ssl_certificate.pem' % safe_domain_name(domain[4:])) if os.path.exists( ssl_certificate_parent) and check_certificate( domain, ssl_certificate_parent, None, just_check_domain=True)[0] == "OK": ssl_certificate = ssl_certificate_parent ssl_via = "Using multi/wildcard certificate of %s." % domain[ 4:] return ssl_key, ssl_certificate, ssl_via
def install_cert(domain, ssl_cert, ssl_chain, env): if domain not in get_web_domains(env): return "Invalid domain name." # Write the combined cert+chain to a temporary path and validate that it is OK. # The certificate always goes above the chain. import tempfile, os fd, fn = tempfile.mkstemp('.pem') os.write(fd, (ssl_cert + '\n' + ssl_chain).encode("ascii")) os.close(fd) # Do validation on the certificate before installing it. from status_checks import check_certificate ssl_key, ssl_certificate, ssl_csr_path = get_domain_ssl_files(domain, env) cert_status, cert_status_details = check_certificate(domain, fn, ssl_key) if cert_status != "OK": if cert_status == "SELF-SIGNED": cert_status = "This is a self-signed certificate. I can't install that." os.unlink(fn) return cert_status # Copy the certificate to its expected location. os.makedirs(os.path.dirname(ssl_certificate), exist_ok=True) shutil.move(fn, ssl_certificate) # Kick nginx so it sees the cert. return do_web_update(env, ok_status="")
def check_cert(domain): from status_checks import check_certificate ssl_key, ssl_certificate, ssl_csr_path = get_domain_ssl_files(domain, env) if not os.path.exists(ssl_certificate): return ("danger", "No Certificate Installed") cert_status, cert_status_details = check_certificate(domain, ssl_certificate, ssl_key) if cert_status == "OK": return ("success", "Signed & valid. " + cert_status_details) elif cert_status == "SELF-SIGNED": return ("warning", "Self-signed. Get a signed certificate to stop warnings.") else: return ("danger", "Certificate has a problem: " + cert_status)
def get_domain_ssl_files(domain, env, allow_shared_cert=True): # What SSL private key will we use? Allow the user to override this, but # in many cases using the same private key for all domains would be fine. # Don't allow the user to override the key for PRIMARY_HOSTNAME because # that's what's in the main file. ssl_key = os.path.join(env["STORAGE_ROOT"], 'ssl/ssl_private_key.pem') ssl_key_is_alt = False alt_key = os.path.join(env["STORAGE_ROOT"], 'ssl/%s/private_key.pem' % safe_domain_name(domain)) if domain != env['PRIMARY_HOSTNAME'] and os.path.exists(alt_key): ssl_key = alt_key ssl_key_is_alt = True # What SSL certificate will we use? ssl_certificate_primary = os.path.join(env["STORAGE_ROOT"], 'ssl/ssl_certificate.pem') ssl_via = None if domain == env['PRIMARY_HOSTNAME']: # For PRIMARY_HOSTNAME, use the one we generated at set-up time. ssl_certificate = ssl_certificate_primary else: # For other domains, we'll probably use a certificate in a different path. ssl_certificate = os.path.join(env["STORAGE_ROOT"], 'ssl/%s/ssl_certificate.pem' % safe_domain_name(domain)) # But we can be smart and reuse the main SSL certificate if is has # a Subject Alternative Name matching this domain. Don't do this if # the user has uploaded a different private key for this domain. if not ssl_key_is_alt and allow_shared_cert: from status_checks import check_certificate if check_certificate(domain, ssl_certificate_primary, None)[0] == "OK": ssl_certificate = ssl_certificate_primary ssl_via = "Using multi/wildcard certificate of %s." % env['PRIMARY_HOSTNAME'] # For a 'www.' domain, see if we can reuse the cert of the parent. elif domain.startswith('www.'): ssl_certificate_parent = os.path.join(env["STORAGE_ROOT"], 'ssl/%s/ssl_certificate.pem' % safe_domain_name(domain[4:])) if os.path.exists(ssl_certificate_parent) and check_certificate(domain, ssl_certificate_parent, None)[0] == "OK": ssl_certificate = ssl_certificate_parent ssl_via = "Using multi/wildcard certificate of %s." % domain[4:] return ssl_key, ssl_certificate, ssl_via
def check_cert(domain): from status_checks import check_certificate ssl_key, ssl_certificate, ssl_via = get_domain_ssl_files(domain, env) if not os.path.exists(ssl_certificate): return ("danger", "No Certificate Installed") cert_status, cert_status_details = check_certificate(domain, ssl_certificate, ssl_key) if cert_status == "OK": if not ssl_via: return ("success", "Signed & valid. " + cert_status_details) else: # This is an alternate domain but using the same cert as the primary domain. return ("success", "Signed & valid. " + ssl_via) elif cert_status == "SELF-SIGNED": return ("warning", "Self-signed. Get a signed certificate to stop warnings.") else: return ("danger", "Certificate has a problem: " + cert_status)
def check_cert(domain): from status_checks import check_certificate ssl_key, ssl_certificate, ssl_csr_path = get_domain_ssl_files(domain, env) if not os.path.exists(ssl_certificate): return ("danger", "No Certificate Installed") cert_status, cert_status_details = check_certificate(domain, ssl_certificate, ssl_key) if cert_status == "OK": if domain == env['PRIMARY_HOSTNAME'] or ssl_certificate != get_domain_ssl_files(env['PRIMARY_HOSTNAME'], env)[1]: return ("success", "Signed & valid. " + cert_status_details) else: # This is an alternate domain but using the same cert as the primary domain. return ("success", "Signed & valid. Using multi/wildcard certificate of %s." % env['PRIMARY_HOSTNAME']) elif cert_status == "SELF-SIGNED": return ("warning", "Self-signed. Get a signed certificate to stop warnings.") else: return ("danger", "Certificate has a problem: " + cert_status)
def get_domain_ssl_files(domain, env): # What SSL private key will we use? Allow the user to override this, but # in many cases using the same private key for all domains would be fine. # Don't allow the user to override the key for PRIMARY_HOSTNAME because # that's what's in the main file. ssl_key = os.path.join(env["STORAGE_ROOT"], 'ssl/ssl_private_key.pem') ssl_key_is_alt = False alt_key = os.path.join(env["STORAGE_ROOT"], 'ssl/%s/private_key.pem' % safe_domain_name(domain)) if domain != env['PRIMARY_HOSTNAME'] and os.path.exists(alt_key): ssl_key = alt_key ssl_key_is_alt = True # What SSL certificate will we use? ssl_certificate_primary = os.path.join(env["STORAGE_ROOT"], 'ssl/ssl_certificate.pem') if domain == env['PRIMARY_HOSTNAME']: # For PRIMARY_HOSTNAME, use the one we generated at set-up time. ssl_certificate = ssl_certificate_primary else: # For other domains, we'll probably use a certificate in a different path. ssl_certificate = os.path.join( env["STORAGE_ROOT"], 'ssl/%s/ssl_certificate.pem' % safe_domain_name(domain)) # But we can be smart and reuse the main SSL certificate if is has # a Subject Alternative Name matching this domain. Don't do this if # the user has uploaded a different private key for this domain. if not ssl_key_is_alt: from status_checks import check_certificate if check_certificate(domain, ssl_certificate_primary, None)[0] == "OK": ssl_certificate = ssl_certificate_primary # Where would the CSR go? As with the SSL cert itself, the CSR must be # different for each domain name. if domain == env['PRIMARY_HOSTNAME']: csr_path = os.path.join(env["STORAGE_ROOT"], 'ssl/ssl_cert_sign_req.csr') else: csr_path = os.path.join( env["STORAGE_ROOT"], 'ssl/%s/certificate_signing_request.csr' % safe_domain_name(domain)) return ssl_key, ssl_certificate, csr_path
def install_cert(domain, ssl_cert, ssl_chain, env): if domain not in get_web_domains(env) + get_default_www_redirects(env): return "Invalid domain name." # Write the combined cert+chain to a temporary path and validate that it is OK. # The certificate always goes above the chain. import tempfile, os fd, fn = tempfile.mkstemp('.pem') os.write(fd, (ssl_cert + '\n' + ssl_chain).encode("ascii")) os.close(fd) # Do validation on the certificate before installing it. from status_checks import check_certificate ssl_key, ssl_certificate, ssl_via = get_domain_ssl_files( domain, env, allow_shared_cert=False) cert_status, cert_status_details = check_certificate(domain, fn, ssl_key) if cert_status != "OK": if cert_status == "SELF-SIGNED": cert_status = "This is a self-signed certificate. I can't install that." os.unlink(fn) if cert_status_details is not None: cert_status += " " + cert_status_details return cert_status # Copy the certificate to its expected location. os.makedirs(os.path.dirname(ssl_certificate), exist_ok=True) shutil.move(fn, ssl_certificate) ret = ["OK"] # When updating the cert for PRIMARY_HOSTNAME, also update DNS because it is # used in the DANE TLSA record and restart postfix and dovecot which use # that certificate. if domain == env['PRIMARY_HOSTNAME']: ret.append(do_dns_update(env)) shell('check_call', ["/usr/sbin/service", "postfix", "restart"]) shell('check_call', ["/usr/sbin/service", "dovecot", "restart"]) ret.append("mail services restarted") # Kick nginx so it sees the cert. ret.append(do_web_update(env)) return "\n".join(ret)
def install_cert(domain, ssl_cert, ssl_chain, env): if domain not in get_web_domains(env): return "Invalid domain name." # Write the combined cert+chain to a temporary path and validate that it is OK. # The certificate always goes above the chain. import tempfile, os fd, fn = tempfile.mkstemp('.pem') os.write(fd, (ssl_cert + '\n' + ssl_chain).encode("ascii")) os.close(fd) # Do validation on the certificate before installing it. from status_checks import check_certificate ssl_key, ssl_certificate, ssl_via = get_domain_ssl_files(domain, env, allow_shared_cert=False) cert_status, cert_status_details = check_certificate(domain, fn, ssl_key) if cert_status != "OK": if cert_status == "SELF-SIGNED": cert_status = "This is a self-signed certificate. I can't install that." os.unlink(fn) if cert_status_details is not None: cert_status += " " + cert_status_details return cert_status # Copy the certificate to its expected location. os.makedirs(os.path.dirname(ssl_certificate), exist_ok=True) shutil.move(fn, ssl_certificate) ret = [] # When updating the cert for PRIMARY_HOSTNAME, also update DNS because it is # used in the DANE TLSA record and restart postfix and dovecot which use # that certificate. if domain == env['PRIMARY_HOSTNAME']: ret.append( do_dns_update(env) ) shell('check_call', ["/usr/sbin/service", "postfix", "restart"]) shell('check_call', ["/usr/sbin/service", "dovecot", "restart"]) ret.append("mail services restarted") # Kick nginx so it sees the cert. ret.append( do_web_update(env, ok_status="") ) return "\n".join(r for r in ret if r.strip() != "")
def get_domain_ssl_files(domain, env): # What SSL private key will we use? Allow the user to override this, but # in many cases using the same private key for all domains would be fine. # Don't allow the user to override the key for PRIMARY_HOSTNAME because # that's what's in the main file. ssl_key = os.path.join(env["STORAGE_ROOT"], 'ssl/ssl_private_key.pem') ssl_key_is_alt = False alt_key = os.path.join(env["STORAGE_ROOT"], 'ssl/%s/private_key.pem' % safe_domain_name(domain)) if domain != env['PRIMARY_HOSTNAME'] and os.path.exists(alt_key): ssl_key = alt_key ssl_key_is_alt = True # What SSL certificate will we use? ssl_certificate_primary = os.path.join(env["STORAGE_ROOT"], 'ssl/ssl_certificate.pem') if domain == env['PRIMARY_HOSTNAME']: # For PRIMARY_HOSTNAME, use the one we generated at set-up time. ssl_certificate = ssl_certificate_primary else: # For other domains, we'll probably use a certificate in a different path. ssl_certificate = os.path.join(env["STORAGE_ROOT"], 'ssl/%s/ssl_certificate.pem' % safe_domain_name(domain)) # But we can be smart and reuse the main SSL certificate if is has # a Subject Alternative Name matching this domain. Don't do this if # the user has uploaded a different private key for this domain. if not ssl_key_is_alt: from status_checks import check_certificate if check_certificate(domain, ssl_certificate_primary, None)[0] == "OK": ssl_certificate = ssl_certificate_primary # Where would the CSR go? As with the SSL cert itself, the CSR must be # different for each domain name. if domain == env['PRIMARY_HOSTNAME']: csr_path = os.path.join(env["STORAGE_ROOT"], 'ssl/ssl_cert_sign_req.csr') else: csr_path = os.path.join(env["STORAGE_ROOT"], 'ssl/%s/certificate_signing_request.csr' % safe_domain_name(domain)) return ssl_key, ssl_certificate, csr_path
def buy_ssl_certificate(api_key, domain, command, env): if domain != env['PRIMARY_HOSTNAME'] \ and domain not in get_web_domains(env): raise ValueError( "Domain is not %s or a domain we're serving a website for." % env['PRIMARY_HOSTNAME']) # Initialize. gandi = xmlrpc.client.ServerProxy('https://rpc.gandi.net/xmlrpc/') try: existing_certs = gandi.cert.list(api_key) except Exception as e: if "Invalid API key" in str(e): print( "Invalid API key. Check that you copied the API Key correctly from https://www.gandi.net/admin/api_key." ) sys.exit(1) else: raise # Where is the SSL cert stored? ssl_key, ssl_certificate, ssl_csr_path = get_domain_ssl_files(domain, env) # Have we already created a cert for this domain? for cert in existing_certs: if cert['cn'] == domain: break else: # No existing cert found. Purchase one. if command != 'purchase': print( "No certificate or order found yet. If you haven't yet purchased a certificate, run ths script again with the 'purchase' command. Otherwise wait a moment and try again." ) sys.exit(1) else: # Start an order for a single standard SSL certificate. # Use DNS validation. Web-based validation won't work because they # require a file on HTTP but not HTTPS w/o redirects and we don't # serve anything plainly over HTTP. Email might be another way but # DNS is easier to automate. op = gandi.cert.create( api_key, { "csr": open(ssl_csr_path).read(), "dcv_method": "dns", "duration": 1, # year? "package": "cert_std_1_0_0", }) print("An SSL certificate has been ordered.") print() print(op) print() print( "In a moment please run this script again with the 'setup' command." ) if cert['status'] == 'pending': # Get the information we need to update our DNS with a code so that # Gandi can verify that we own the domain. dcv = gandi.cert.get_dcv_params( api_key, { "csr": open(ssl_csr_path).read(), "cert_id": cert['id'], "dcv_method": "dns", "duration": 1, # year? "package": "cert_std_1_0_0", }) if dcv["dcv_method"] != "dns": raise Exception( "Certificate ordered with an unknown validation method.") # Update our DNS data. dns_config = env['STORAGE_ROOT'] + '/dns/custom.yaml' if os.path.exists(dns_config): dns_records = rtyaml.load(open(dns_config)) else: dns_records = {} qname = dcv['md5'] + '.' + domain value = dcv['sha1'] + '.comodoca.com.' dns_records[qname] = {"CNAME": value} with open(dns_config, 'w') as f: f.write(rtyaml.dump(dns_records)) shell('check_call', ['tools/dns_update']) # Okay, done with this step. print("DNS has been updated. Gandi will check within 60 minutes.") print() print( "See https://www.gandi.net/admin/ssl/%d/details for the status of this order." % cert['id']) elif cert['status'] == 'valid': # The certificate is ready. # Check before we overwrite something we shouldn't. if os.path.exists(ssl_certificate): cert_status = check_certificate(None, ssl_certificate, None) if cert_status != "SELF-SIGNED": print( "Please back up and delete the file %s so I can save your new certificate." % ssl_certificate) sys.exit(1) # Form the certificate. # The certificate comes as a long base64-encoded string. Break in # into lines in the usual way. pem = "-----BEGIN CERTIFICATE-----\n" pem += "\n".join(chunk for chunk in re.split(r"(.{64})", cert['cert']) if chunk != "") pem += "\n-----END CERTIFICATE-----\n\n" # Append intermediary certificates. pem += urllib.request.urlopen( "https://www.gandi.net/static/CAs/GandiStandardSSLCA.pem").read( ).decode("ascii") # Write out. with open(ssl_certificate, "w") as f: f.write(pem) print( "The certificate has been installed in %s. Restarting services..." % ssl_certificate) # Restart dovecot and if this is for PRIMARY_HOSTNAME. if domain == env['PRIMARY_HOSTNAME']: shell('check_call', ["/usr/sbin/service", "dovecot", "restart"]) shell('check_call', ["/usr/sbin/service", "postfix", "restart"]) # Restart nginx in all cases. shell('check_call', ["/usr/sbin/service", "nginx", "restart"]) else: print( "The certificate has an unknown status. Please check https://www.gandi.net/admin/ssl/%d/details for the status of this order." % cert['id'])
def buy_ssl_certificate(api_key, domain, command, env): if domain != env['PRIMARY_HOSTNAME'] \ and domain not in get_web_domains(env): raise ValueError("Domain is not %s or a domain we're serving a website for." % env['PRIMARY_HOSTNAME']) # Initialize. gandi = xmlrpc.client.ServerProxy('https://rpc.gandi.net/xmlrpc/') try: existing_certs = gandi.cert.list(api_key) except Exception as e: if "Invalid API key" in str(e): print("Invalid API key. Check that you copied the API Key correctly from https://www.gandi.net/admin/api_key.") sys.exit(1) else: raise # Where is the SSL cert stored? ssl_key, ssl_certificate, ssl_csr_path = get_domain_ssl_files(domain, env) # Have we already created a cert for this domain? for cert in existing_certs: if cert['cn'] == domain: break else: # No existing cert found. Purchase one. if command != 'purchase': print("No certificate or order found yet. If you haven't yet purchased a certificate, run ths script again with the 'purchase' command. Otherwise wait a moment and try again.") sys.exit(1) else: # Start an order for a single standard SSL certificate. # Use DNS validation. Web-based validation won't work because they # require a file on HTTP but not HTTPS w/o redirects and we don't # serve anything plainly over HTTP. Email might be another way but # DNS is easier to automate. op = gandi.cert.create(api_key, { "csr": open(ssl_csr_path).read(), "dcv_method": "dns", "duration": 1, # year? "package": "cert_std_1_0_0", }) print("An SSL certificate has been ordered.") print() print(op) print() print("In a moment please run this script again with the 'setup' command.") if cert['status'] == 'pending': # Get the information we need to update our DNS with a code so that # Gandi can verify that we own the domain. dcv = gandi.cert.get_dcv_params(api_key, { "csr": open(ssl_csr_path).read(), "cert_id": cert['id'], "dcv_method": "dns", "duration": 1, # year? "package": "cert_std_1_0_0", }) if dcv["dcv_method"] != "dns": raise Exception("Certificate ordered with an unknown validation method.") # Update our DNS data. dns_config = env['STORAGE_ROOT'] + '/dns/custom.yaml' if os.path.exists(dns_config): dns_records = rtyaml.load(open(dns_config)) else: dns_records = { } qname = dcv['md5'] + '.' + domain value = dcv['sha1'] + '.comodoca.com.' dns_records[qname] = { "CNAME": value } with open(dns_config, 'w') as f: f.write(rtyaml.dump(dns_records)) shell('check_call', ['tools/dns_update']) # Okay, done with this step. print("DNS has been updated. Gandi will check within 60 minutes.") print() print("See https://www.gandi.net/admin/ssl/%d/details for the status of this order." % cert['id']) elif cert['status'] == 'valid': # The certificate is ready. # Check before we overwrite something we shouldn't. if os.path.exists(ssl_certificate): cert_status, cert_status_details = check_certificate(None, ssl_certificate, None) if cert_status != "SELF-SIGNED": print("Please back up and delete the file %s so I can save your new certificate." % ssl_certificate) sys.exit(1) # Form the certificate. # The certificate comes as a long base64-encoded string. Break in # into lines in the usual way. pem = "-----BEGIN CERTIFICATE-----\n" pem += "\n".join(chunk for chunk in re.split(r"(.{64})", cert['cert']) if chunk != "") pem += "\n-----END CERTIFICATE-----\n\n" # Append intermediary certificates. pem += urllib.request.urlopen("https://www.gandi.net/static/CAs/GandiStandardSSLCA.pem").read().decode("ascii") # Write out. with open(ssl_certificate, "w") as f: f.write(pem) print("The certificate has been installed in %s. Restarting services..." % ssl_certificate) # Restart dovecot and if this is for PRIMARY_HOSTNAME. if domain == env['PRIMARY_HOSTNAME']: shell('check_call', ["/usr/sbin/service", "dovecot", "restart"]) shell('check_call', ["/usr/sbin/service", "postfix", "restart"]) # Restart nginx in all cases. shell('check_call', ["/usr/sbin/service", "nginx", "restart"]) else: print("The certificate has an unknown status. Please check https://www.gandi.net/admin/ssl/%d/details for the status of this order." % cert['id'])
def install_cert(domain, ssl_cert, ssl_chain, env): if domain not in get_web_domains(env) + get_default_www_redirects(env): return "Invalid domain name." # Write the combined cert+chain to a temporary path and validate that it is OK. # The certificate always goes above the chain. import tempfile, os fd, fn = tempfile.mkstemp('.pem') os.write(fd, (ssl_cert + '\n' + ssl_chain).encode("ascii")) os.close(fd) # Do validation on the certificate before installing it. from status_checks import check_certificate ssl_private_key = os.path.join( os.path.join(env["STORAGE_ROOT"], 'ssl', 'ssl_private_key.pem')) cert_status, cert_status_details = check_certificate( domain, fn, ssl_private_key) if cert_status != "OK": if cert_status == "SELF-SIGNED": cert_status = "This is a self-signed certificate. I can't install that." os.unlink(fn) if cert_status_details is not None: cert_status += " " + cert_status_details return cert_status # Where to put it? if domain == env['PRIMARY_HOSTNAME']: ssl_certificate = os.path.join( os.path.join(env["STORAGE_ROOT"], 'ssl', 'ssl_certificate.pem')) else: # Make a unique path for the certificate. from status_checks import load_cert_chain, load_pem, get_certificate_domains from cryptography.hazmat.primitives import hashes from binascii import hexlify cert = load_pem(load_cert_chain(fn)[0]) all_domains, cn = get_certificate_domains(cert) path = "%s-%s-%s" % ( cn, # common name cert.not_valid_after.date().isoformat().replace( "-", ""), # expiration date hexlify(cert.fingerprint( hashes.SHA256())).decode("ascii")[0:8], # fingerprint prefix ) ssl_certificate = os.path.join( os.path.join(env["STORAGE_ROOT"], 'ssl', path, 'ssl_certificate.pem')) # Install the certificate. os.makedirs(os.path.dirname(ssl_certificate), exist_ok=True) shutil.move(fn, ssl_certificate) ret = ["OK"] # When updating the cert for PRIMARY_HOSTNAME, also update DNS because it is # used in the DANE TLSA record and restart postfix and dovecot which use # that certificate. if domain == env['PRIMARY_HOSTNAME']: ret.append(do_dns_update(env)) shell('check_call', ["/usr/sbin/service", "postfix", "restart"]) shell('check_call', ["/usr/sbin/service", "dovecot", "restart"]) ret.append("mail services restarted") # Kick nginx so it sees the cert. ret.append(do_web_update(env)) return "\n".join(ret)