예제 #1
0
class AcmeDnsChallenge(AcmeChallenge):
    challengeType = challenges.DNS01

    def create_certificate(self, csr, issuer_options):
        """
        Creates an ACME certificate.

        :param csr:
        :param issuer_options:
        :return: :raise Exception:
        """
        self.acme = AcmeDnsHandler()
        authority = issuer_options.get("authority")
        create_immediately = issuer_options.get("create_immediately", False)
        acme_client, registration = self.acme.setup_acme_client(authority)
        domains = self.acme.get_domains(issuer_options)
        dns_provider = issuer_options.get("dns_provider", {})

        if dns_provider:
            for domain in domains:
                # Currently, we only support specifying one DNS provider per certificate, even if that
                # certificate has multiple SANs that may belong to different provid
                self.acme.dns_providers_for_domain[domain] = [dns_provider]

            credentials = json.loads(dns_provider.credentials)
            current_app.logger.debug("Using DNS provider: {0}".format(
                dns_provider.provider_type))
            account_number = credentials.get("account_id")
            provider_type = dns_provider.provider_type
            if provider_type == "route53" and not account_number:
                error = "Route53 DNS Provider {} does not have an account number configured.".format(
                    dns_provider.name)
                current_app.logger.error(error)
                raise InvalidConfiguration(error)
        else:
            dns_provider = {}
            account_number = None
            provider_type = None

            for domain in domains:
                self.acme.autodetect_dns_providers(domain)

        # Create pending authorizations that we'll need to do the creation
        dns_authorization = authorization_service.create(
            account_number, domains, provider_type)

        if not create_immediately:
            # Return id of the DNS Authorization
            return None, None, dns_authorization.id

        pem_certificate, pem_certificate_chain = self.create_certificate_immediately(
            acme_client, dns_authorization, csr)
        # TODO add external ID (if possible)
        return pem_certificate, pem_certificate_chain, None

    @retry(stop_max_attempt_number=ACME_ADDITIONAL_ATTEMPTS, wait_fixed=5000)
    def create_certificate_immediately(self, acme_client, order_info, csr):
        try:
            order = acme_client.new_order(csr)
        except WildcardUnsupportedError:
            metrics.send("create_certificte_immediately_wildcard_unsupported",
                         "counter", 1)
            raise Exception("The currently selected ACME CA endpoint does"
                            " not support issuing wildcard certificates.")

        try:
            authorizations = self.acme.get_authorizations(
                acme_client, order, order_info)
        except ClientError:
            capture_exception()
            metrics.send("create_certificate_immediately_error", "counter", 1)

            current_app.logger.error(
                f"Unable to resolve cert for domains: {', '.join(order_info.domains)}",
                exc_info=True)
            return False

        authorizations = self.acme.finalize_authorizations(
            acme_client, authorizations)

        return self.acme.request_certificate(acme_client, authorizations,
                                             order)

    def deploy(self, challenge, acme_client, validation_target):
        pass

    def cleanup(self, authorizations, acme_client, validation_target=None):
        """
        Best effort attempt to delete DNS challenges that may not have been deleted previously. This is usually called
        on an exception

        :param authorizations: all the authorizations to be cleaned up
        :param acme_client: an already bootstrapped acme_client, to avoid passing all issuer_options and so on
        :param validation_target: Unused right now
        :return:
        """
        acme = AcmeDnsHandler()
        acme.cleanup_dns_challenges(acme_client, authorizations)