def install_cert(domain, ssl_cert, ssl_chain, env):
	# Write the combined cert+chain to a temporary path and validate that it is OK.
	# The certificate always goes above the chain.
	import tempfile
	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.
	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?
	# Make a unique path for the certificate.
	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.pem" % (
		safe_domain_name(cn), # common name, which should be filename safe because it is IDNA-encoded, but in case of a malformed cert make sure it's ok to use as a filename
		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))

	# 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, symlink it from the system
	# certificate path, which is hard-coded for various purposes, and then
	# restart postfix and dovecot.
	if domain == env['PRIMARY_HOSTNAME']:
		# Update symlink.
		system_ssl_certificate = os.path.join(os.path.join(env["STORAGE_ROOT"], 'ssl', 'ssl_certificate.pem'))
		os.unlink(system_ssl_certificate)
		os.symlink(ssl_certificate, system_ssl_certificate)

		# Restart postfix and dovecot so they pick up the new file.
		shell('check_call', ["/usr/sbin/service", "postfix", "restart"])
		shell('check_call', ["/usr/sbin/service", "dovecot", "restart"])
		ret.append("mail services restarted")

		# The DANE TLSA record will remain valid so long as the private key
		# hasn't changed. We don't ever change the private key automatically.
		# If the user does it, they must manually update DNS.

	# Update the web configuration so nginx picks up the new certificate file.
	from web_update import do_web_update
	ret.append( do_web_update(env) )
	return "\n".join(ret)
def kick(env, mail_result=None):
    results = []

    # Include the current operation's result in output.

    if mail_result is not None:
        results.append(mail_result + "\n")

        # Ensure every required alias exists.

    existing_users = get_mail_users(env)
    existing_aliases = get_mail_aliases(env)
    required_aliases = get_required_aliases(env)

    def ensure_admin_alias_exists(source):
        # If a user account exists with that address, we're good.
        if source in existing_users:
            return

            # Does this alias exists?
        for s, t in existing_aliases:
            if s == source:
                return

                # Doesn't exist.
        administrator = get_system_administrator(env)
        if source == administrator:
            return  # don't make an alias from the administrator to itself --- this alias must be created manually
        add_mail_alias(source, administrator, env, do_kick=False)
        results.append("added alias %s (=> %s)\n" % (source, administrator))

    for alias in required_aliases:
        ensure_admin_alias_exists(alias)

        # Remove auto-generated postmaster/admin on domains we no
        # longer have any other email addresses for.
    for source, target in existing_aliases:
        user, domain = source.split("@")
        if (
            user in ("postmaster", "admin")
            and source not in required_aliases
            and target == get_system_administrator(env)
        ):
            remove_mail_alias(source, env, do_kick=False)
            results.append("removed alias %s (was to %s; domain no longer used for email)\n" % (source, target))

            # Update DNS and nginx in case any domains are added/removed.

    from dns_update import do_dns_update

    results.append(do_dns_update(env))

    from web_update import do_web_update

    results.append(do_web_update(env))

    return "".join(s for s in results if s != "")
Exemple #3
0
def kick(env, mail_result):
	# Update DNS and nginx in case any domains are added/removed.
	from dns_update import do_dns_update
	from web_update import do_web_update
	results = [
		do_dns_update(env),
		mail_result + "\n",
		do_web_update(env),
	]
	return "".join(s for s in results if s != "")
Exemple #4
0
def kick(env, mail_result=None):
	results = []

	# Include the current operation's result in output.

	if mail_result is not None:
		results.append(mail_result + "\n")

	# Ensure every required alias exists.

	existing_users = get_mail_users(env)
	existing_alias_records = get_mail_aliases(env)
	existing_aliases = set(a for a, *_ in existing_alias_records) # just first entry in tuple
	required_aliases = get_required_aliases(env)

	def ensure_admin_alias_exists(address):
		# If a user account exists with that address, we're good.
		if address in existing_users:
			return

		# If the alias already exists, we're good.
		if address in existing_aliases:
			return

		# Doesn't exist.
		administrator = get_system_administrator(env)
		if address == administrator: return # don't make an alias from the administrator to itself --- this alias must be created manually
		add_mail_alias(address, administrator, "", env, do_kick=False)
		if administrator not in existing_aliases: return # don't report the alias in output if the administrator alias isn't in yet -- this is a hack to supress confusing output on initial setup
		results.append("added alias %s (=> %s)\n" % (address, administrator))

	for address in required_aliases:
		ensure_admin_alias_exists(address)

	# Remove auto-generated postmaster/admin on domains we no
	# longer have any other email addresses for.
	for address, forwards_to, *_ in existing_alias_records:
		user, domain = address.split("@")
		if user in ("postmaster", "admin", "abuse") \
			and address not in required_aliases \
			and forwards_to == get_system_administrator(env):
			remove_mail_alias(address, env, do_kick=False)
			results.append("removed alias %s (was to %s; domain no longer used for email)\n" % (address, forwards_to))

	# Update DNS and nginx in case any domains are added/removed.

	from dns_update import do_dns_update
	results.append( do_dns_update(env) )

	from web_update import do_web_update
	results.append( do_web_update(env) )

	return "".join(s for s in results if s != "")
def post_install_func(env):
	ret = []

	# Get the certificate to use for PRIMARY_HOSTNAME.
	ssl_certificates = get_ssl_certificates(env)
	cert = get_domain_ssl_files(env['PRIMARY_HOSTNAME'], ssl_certificates, env, use_main_cert=False)
	if not cert:
		# Ruh-row, we don't have any certificate usable
		# for the primary hostname.
		ret.append("there is no valid certificate for " + env['PRIMARY_HOSTNAME'])

	# Symlink the best cert for PRIMARY_HOSTNAME to the system
	# certificate path, which is hard-coded for various purposes, and then
	# restart postfix and dovecot.
	system_ssl_certificate = os.path.join(os.path.join(env["STORAGE_ROOT"], 'ssl', 'ssl_certificate.pem'))
	if cert and os.readlink(system_ssl_certificate) != cert['certificate']:
		# Update symlink.
		ret.append("updating primary certificate")
		ssl_certificate = cert['certificate']
		os.unlink(system_ssl_certificate)
		os.symlink(ssl_certificate, system_ssl_certificate)

		# Restart postfix and dovecot so they pick up the new file.
		shell('check_call', ["/usr/sbin/service", "postfix", "restart"])
		shell('check_call', ["/usr/sbin/service", "dovecot", "restart"])
		ret.append("mail services restarted")

		# The DANE TLSA record will remain valid so long as the private key
		# hasn't changed. We don't ever change the private key automatically.
		# If the user does it, they must manually update DNS.

	# Update the web configuration so nginx picks up the new certificate file.
	from web_update import do_web_update
	ret.append( do_web_update(env) )

	return ret
Exemple #6
0
def kick(env, mail_result=None):
	results = []

	# Inclde the current operation's result in output.

	if mail_result is not None:
		results.append(mail_result + "\n")

	# Create hostmaster@ for the primary domain if it does not already exist.
	# Default the target to administrator@ which the user is responsible for
	# setting and keeping up to date.

	existing_aliases = get_mail_aliases(env)

	administrator = "administrator@" + env['PRIMARY_HOSTNAME']

	def ensure_admin_alias_exists(source):
		# Does this alias exists?
		for s, t in existing_aliases:
			if s == source:
				return

		# Doesn't exist.
		add_mail_alias(source, administrator, env, do_kick=False)
		results.append("added alias %s (=> %s)\n" % (source, administrator))

	ensure_admin_alias_exists("hostmaster@" + env['PRIMARY_HOSTNAME'])

	# Get a list of domains we serve mail for, except ones for which the only
	# email on that domain is a postmaster/admin alias to the administrator.

	real_mail_domains = get_mail_domains(env,
		filter_aliases = lambda alias : \
			(not alias[0].startswith("postmaster@") \
			 and not alias[0].startswith("admin@")) \
			or alias[1] != administrator \
			)

	# Create postmaster@ and admin@ for all domains we serve mail on.
	# postmaster@ is assumed to exist by our Postfix configuration. admin@
	# isn't anything, but it might save the user some trouble e.g. when
	# buying an SSL certificate.
	for domain in real_mail_domains:
		ensure_admin_alias_exists("postmaster@" + domain)
		ensure_admin_alias_exists("admin@" + domain)

	# Remove auto-generated hostmaster/postmaster/admin on domains we no
	# longer have any other email addresses for.
	for source, target in existing_aliases:
		user, domain = source.split("@")
		if user in ("postmaster", "admin") and domain not in real_mail_domains \
			and target == administrator:
			remove_mail_alias(source, env, do_kick=False)
			results.append("removed alias %s (was to %s; domain no longer used for email)\n" % (source, target))

	# Update DNS and nginx in case any domains are added/removed.

	from dns_update import do_dns_update
	results.append( do_dns_update(env) )

	from web_update import do_web_update
	results.append( do_web_update(env) )

	return "".join(s for s in results if s != "")
Exemple #7
0
def web_update():
	from web_update import do_web_update
	return do_web_update(env)
Exemple #8
0
def web_update():
    from web_update import do_web_update
    try:
        return do_web_update(env)
    except Exception as e:
        return (str(e), 500)
Exemple #9
0
def web_update():
    from web_update import do_web_update
    return do_web_update(env)
def install_cert(domain, ssl_cert, ssl_chain, env, raw=False):
    # Write the combined cert+chain to a temporary path and validate that it is OK.
    # The certificate always goes above the chain.
    import tempfile
    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.
    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?
    # Make a unique path for the certificate.
    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.pem" % (
        safe_domain_name(cn),
        # common name, which should be filename safe because it is IDNA-encoded, but in case of a malformed cert make sure it's ok to use as a filename
        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))

    # 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, symlink it from the system
    # certificate path, which is hard-coded for various purposes, and then
    # restart postfix and dovecot.
    if domain == env['PRIMARY_HOSTNAME']:
        # Update symlink.
        system_ssl_certificate = os.path.join(
            os.path.join(env["STORAGE_ROOT"], 'ssl', 'ssl_certificate.pem'))
        os.unlink(system_ssl_certificate)
        os.symlink(ssl_certificate, system_ssl_certificate)

        # Restart postfix and dovecot so they pick up the new file.
        shell('check_call', ["/usr/sbin/service", "postfix", "restart"])
        shell('check_call', ["/usr/sbin/service", "dovecot", "restart"])
        ret.append("mail services restarted")

    # The DANE TLSA record will remain valid so long as the private key
    # hasn't changed. We don't ever change the private key automatically.
    # If the user does it, they must manually update DNS.

    # Update the web configuration so nginx picks up the new certificate file.
    from web_update import do_web_update
    ret.append(do_web_update(env))
    if raw: return ret
    return "\n".join(ret)