Пример #1
0
    def can_provision_for_domain(domain):
        from status_checks import normalize_ip
        # Let's Encrypt doesn't yet support IDNA domains.
        # We store domains in IDNA (ASCII). To see if this domain is IDNA,
        # we'll see if its IDNA-decoded form is different.
        if idna.decode(domain.encode("ascii")) != domain:
            problems[
                domain] = "Let's Encrypt does not yet support provisioning certificates for internationalized domains."
            return False

        # Does the domain resolve to this machine in public DNS? If not,
        # we can't do domain control validation. For IPv6 is configured,
        # make sure both IPv4 and IPv6 are correct because we don't know
        # how Let's Encrypt will connect.
        import dns.resolver
        for rtype, value in [("A", env["PUBLIC_IP"]),
                             ("AAAA", env.get("PUBLIC_IPV6"))]:
            if not value: continue  # IPv6 is not configured
            try:
                # Must make the qname absolute to prevent a fall-back lookup with a
                # search domain appended, by adding a period to the end.
                response = dns.resolver.query(domain + ".", rtype)
            except (dns.resolver.NoNameservers, dns.resolver.NXDOMAIN,
                    dns.resolver.NoAnswer) as e:
                problems[
                    domain] = "DNS isn't configured properly for this domain: DNS resolution failed (%s: %s)." % (
                        rtype, str(e) or repr(e))  # NoAnswer's str is empty
                return False
            except Exception as e:
                problems[
                    domain] = "DNS isn't configured properly for this domain: DNS lookup had an error: %s." % str(
                        e)
                return False

            # Unfortunately, the response.__str__ returns bytes
            # instead of string, if it resulted from an AAAA-query.
            # We need to convert manually, until this is fixed:
            # https://github.com/rthalley/dnspython/issues/204
            #
            # BEGIN HOTFIX
            def rdata__str__(r):
                s = r.to_text()
                if isinstance(s, bytes):
                    s = s.decode('utf-8')
                return s

            # END HOTFIX

            if len(response) != 1 or normalize_ip(rdata__str__(
                    response[0])) != normalize_ip(value):
                problems[
                    domain] = "Domain control validation cannot be performed for this domain because DNS points the domain to another machine (%s %s)." % (
                        rtype, ", ".join(rdata__str__(r) for r in response))
                return False

        return True
Пример #2
0
	def can_provision_for_domain(domain):
		from status_checks import normalize_ip

		# Does the domain resolve to this machine in public DNS? If not,
		# we can't do domain control validation. For IPv6 is configured,
		# make sure both IPv4 and IPv6 are correct because we don't know
		# how Let's Encrypt will connect.
		import dns.resolver
		for rtype, value in [("A", env["PUBLIC_IP"]), ("AAAA", env.get("PUBLIC_IPV6"))]:
			if not value: continue # IPv6 is not configured
			try:
				# Must make the qname absolute to prevent a fall-back lookup with a
				# search domain appended, by adding a period to the end.
				response = dns.resolver.query(domain + ".", rtype)
			except (dns.resolver.NoNameservers, dns.resolver.NXDOMAIN, dns.resolver.NoAnswer) as e:
				problems[domain] = "DNS isn't configured properly for this domain: DNS resolution failed (%s: %s)." % (rtype, str(e) or repr(e)) # NoAnswer's str is empty
				return False
			except Exception as e:
				problems[domain] = "DNS isn't configured properly for this domain: DNS lookup had an error: %s." % str(e)
				return False

			# Unfortunately, the response.__str__ returns bytes
			# instead of string, if it resulted from an AAAA-query.
			# We need to convert manually, until this is fixed:
			# https://github.com/rthalley/dnspython/issues/204
			#
			# BEGIN HOTFIX
			def rdata__str__(r):
				s = r.to_text()
				if isinstance(s, bytes):
					 s = s.decode('utf-8')
				return s
			# END HOTFIX

			if len(response) != 1 or normalize_ip(rdata__str__(response[0])) != normalize_ip(value):
				problems[domain] = "Domain control validation cannot be performed for this domain because DNS points the domain to another machine (%s %s)." % (rtype, ", ".join(rdata__str__(r) for r in response))
				return False

		return True
Пример #3
0
def get_certificates_to_provision(env,
                                  limit_domains=None,
                                  show_valid_certs=True):
    # Get a set of domain names that we can provision certificates for
    # using certbot. We start with domains that the box is serving web
    # for and subtract:
    # * domains not in limit_domains if limit_domains is not empty
    # * domains with custom "A" records, i.e. they are hosted elsewhere
    # * domains with actual "A" records that point elsewhere (misconfiguration)
    # * domains that already have certificates that will be valid for a while

    from web_update import get_web_domains
    from status_checks import query_dns, normalize_ip

    existing_certs = get_ssl_certificates(env)

    plausible_web_domains = get_web_domains(env, exclude_dns_elsewhere=False)
    actual_web_domains = get_web_domains(env)

    domains_to_provision = set()
    domains_cant_provision = {}

    for domain in plausible_web_domains:
        # Skip domains that the user doesn't want to provision now.
        if limit_domains and domain not in limit_domains:
            continue

        # Check that there isn't an explicit A/AAAA record.
        if domain not in actual_web_domains:
            domains_cant_provision[
                domain] = "The domain has a custom DNS A/AAAA record that points the domain elsewhere, so there is no point to installing a TLS certificate here and we could not automatically provision one anyway because provisioning requires access to the website (which isn't here)."

        # Check that the DNS resolves to here.
        else:

            # Does the domain resolve to this machine in public DNS? If not,
            # we can't do domain control validation. For IPv6 is configured,
            # make sure both IPv4 and IPv6 are correct because we don't know
            # how Let's Encrypt will connect.
            bad_dns = []
            for rtype, value in [("A", env["PUBLIC_IP"]),
                                 ("AAAA", env.get("PUBLIC_IPV6"))]:
                if not value: continue  # IPv6 is not configured
                response = query_dns(domain, rtype)
                if response != normalize_ip(value):
                    bad_dns.append("%s (%s)" % (response, rtype))

            if bad_dns:
                domains_cant_provision[domain] = "The domain name does not resolve to this machine: " \
                 + (", ".join(bad_dns)) \
                 + "."

            else:
                # DNS is all good.

                # Check for a good existing cert.
                existing_cert = get_domain_ssl_files(domain,
                                                     existing_certs,
                                                     env,
                                                     use_main_cert=False,
                                                     allow_missing_cert=True)
                if existing_cert:
                    existing_cert_check = check_certificate(
                        domain,
                        existing_cert['certificate'],
                        existing_cert['private-key'],
                        warn_if_expiring_soon=14)
                    if existing_cert_check[0] == "OK":
                        if show_valid_certs:
                            domains_cant_provision[
                                domain] = "The domain has a valid certificate already. ({} Certificate: {}, private key {})".format(
                                    existing_cert_check[1],
                                    existing_cert['certificate'],
                                    existing_cert['private-key'])
                        continue

                domains_to_provision.add(domain)

    return (domains_to_provision, domains_cant_provision)
Пример #4
0
def get_certificates_to_provision(env, limit_domains=None, show_valid_certs=True):
	# Get a set of domain names that we can provision certificates for
	# using certbot. We start with domains that the box is serving web
	# for and subtract:
	# * domains not in limit_domains if limit_domains is not empty
	# * domains with custom "A" records, i.e. they are hosted elsewhere
	# * domains with actual "A" records that point elsewhere
	# * domains that already have certificates that will be valid for a while

	from web_update import get_web_domains
	from status_checks import query_dns, normalize_ip

	existing_certs = get_ssl_certificates(env)

	plausible_web_domains = get_web_domains(env, exclude_dns_elsewhere=False)
	actual_web_domains = get_web_domains(env)

	domains_to_provision = set()
	domains_cant_provision = { }

	for domain in plausible_web_domains:
		# Skip domains that the user doesn't want to provision now.
		if limit_domains and domain not in limit_domains:
			continue

		# Check that there isn't an explicit A/AAAA record.
		if domain not in actual_web_domains:
			domains_cant_provision[domain] = "The domain has a custom DNS A/AAAA record that points the domain elsewhere, so there is no point to installing a TLS certificate here and we could not automatically provision one anyway because provisioning requires access to the website (which isn't here)."

		# Check that the DNS resolves to here.
		else:

			# Does the domain resolve to this machine in public DNS? If not,
			# we can't do domain control validation. For IPv6 is configured,
			# make sure both IPv4 and IPv6 are correct because we don't know
			# how Let's Encrypt will connect.
			bad_dns = []
			for rtype, value in [("A", env["PUBLIC_IP"]), ("AAAA", env.get("PUBLIC_IPV6"))]:
				if not value: continue # IPv6 is not configured
				response = query_dns(domain, rtype)
				if response != normalize_ip(value):
					bad_dns.append("%s (%s)" % (response, rtype))
	
			if bad_dns:
				domains_cant_provision[domain] = "The domain name does not resolve to this machine: " \
					+ (", ".join(bad_dns)) \
					+ "."
			
			else:
				# DNS is all good.

				# Check for a good existing cert.
				existing_cert = get_domain_ssl_files(domain, existing_certs, env, use_main_cert=False, allow_missing_cert=True)
				if existing_cert:
					existing_cert_check = check_certificate(domain, existing_cert['certificate'], existing_cert['private-key'],
						warn_if_expiring_soon=14)
					if existing_cert_check[0] == "OK":
						if show_valid_certs:
							domains_cant_provision[domain] = "The domain has a valid certificate already. ({} Certificate: {}, private key {})".format(
								existing_cert_check[1],
								existing_cert['certificate'],
								existing_cert['private-key'])
						continue

				domains_to_provision.add(domain)

	return (domains_to_provision, domains_cant_provision)