Exemple #1
0
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)
Exemple #4
0
	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)
Exemple #5
0
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
Exemple #6
0
	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)
Exemple #7
0
	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)
Exemple #8
0
	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)
Exemple #9
0
	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)
Exemple #10
0
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
Exemple #11
0
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)
Exemple #12
0
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() != "")
Exemple #13
0
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
Exemple #14
0
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'])
Exemple #16
0
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)