Exemplo n.º 1
0
 def _register_client(self, client, account_key):
     """Register this Letsencrypt client."""
     new_reg = acme.messages.NewRegistration(key=account_key.public_key(),
                                             terms_of_service_agreed=True)
     try:
         client.new_account(new_reg)
         _logger.info("Successfully registered.")
     except acme.errors.ConflictError as err:
         reg = acme.messages.Registration(key=account_key.public_key())
         reg_res = acme.messages.RegistrationResource(body=reg,
                                                      uri=err.location)
         client.query_registration(reg_res)
         _logger.info("Reusing existing account.")
Exemplo n.º 2
0
 def __register(self, client):
     tos = client.directory.meta.terms_of_service
     tos = input(
         "Are you agree with Let's Encrypt terms of service available at {}? [y/N] "
         .format(tos)).lower()
     if tos != 'y':
         Logger.error("Terms of service not accepted, aborting...")
         sys.exit(-1)
     email = input("Email address for Let's Encrypt account registration: ")
     account = acme.messages.NewRegistration.from_data(
         email=email, terms_of_service_agreed=True)
     Logger.info("Register account to Let's Encrypt")
     client.new_account(account)
Exemplo n.º 3
0
def request_new_certificate(hostname, accnt_key, priv_key, tmp_chall_dict,
                            directory_url):
    """Runs the entire process of ACME registration and certificate request"""

    client = create_v2_client(directory_url, accnt_key)

    try:
        client.net.account = client.new_account(
            messages.NewRegistration.from_data(terms_of_service_agreed=True))

    except errors.ConflictError as error:
        existing_reg = messages.RegistrationResource(uri=error.location)
        existing_reg = client.query_registration(existing_reg)
        client.update_registration(existing_reg)

    csr = crypto_util.make_csr(priv_key, [hostname], False)
    order = client.new_order(csr)

    log.info('Created a new order for the issuance of a certificate for %s',
             hostname)

    challb = select_http01_chall(order)

    _, chall_tok = challb.response_and_validation(client.net.key)
    v = challb.chall.encode("token")
    log.info('Exposing challenge on %s', v)
    tmp_chall_dict.set(v, ChallTok(chall_tok))

    cr = client.answer_challenge(challb, challb.response(client.net.key))
    log.debug('Acme CA responded to challenge request with: %s', cr)

    order = client.poll_and_finalize(order)

    return split_certificate_chain(order.fullchain_pem)
Exemplo n.º 4
0
def run_acme_reg_to_finish(domain, accnt_key, priv_key, hostname, tmp_chall_dict, directory_url):
    """Runs the entire process of ACME registeration"""

    client = create_v2_client(directory_url, accnt_key)

    # First we need to create a registration with the email address provided
    # and accept the terms of service
    log.info("Using boulder server %s", directory_url)

    client.net.account = client.new_account(
        messages.NewRegistration.from_data(
            terms_of_service_agreed=True
        )
    )

    # Now we need to open an order and request our certificate

    # NOTE: We'll let ACME generate a CSR for our private key as there's
    # a lot of utility code it uses to generate the CSR in a specific
    # fashion. Better to use what LE provides than to roll our own as we
    # we doing with the v1 code
    #
    # This will also let us support multi-domain certificat requests in the
    # future, as well as mandate OCSP-Must-Staple if/when GL's HTTPS server
    # supports it
    csr = crypto_util.make_csr(priv_key, [hostname], False)
    order = client.new_order(csr)
    authzr = order.authorizations

    log.info('Created a new order for %s', hostname)

    # authrz is a list of Authorization resources, we need to find the
    # HTTP-01 challenge and use it
    for auth_req in authzr: # pylint: disable=not-an-iterable
       for chall_body in auth_req.body.challenges:
            if isinstance(chall_body.chall, challenges.HTTP01):
                challb = chall_body
                break

    if challb is None:
        raise Exception("HTTP01 challenge unavailable!")

    response, chall_tok = challb.response_and_validation(client.net.key)
    v = chall_body.chall.encode("token")
    log.info('Exposing challenge on %s', v)
    tmp_chall_dict.set(v, ChallTok(chall_tok))

    cr = client.answer_challenge(challb, challb.response(client.net.key))
    log.debug('Acme CA responded to challenge request with: %s', cr)

    order = client.poll_and_finalize(order)

    return split_certificate_chain(order.fullchain_pem)
Exemplo n.º 5
0
def run_acme_reg_to_finish(domain, accnt_key, priv_key, hostname, tmp_chall_dict, directory_url):
    """Runs the entire process of ACME registeration"""

    client = create_v2_client(directory_url, accnt_key)

    # First we need to create a registration with the email address provided
    # and accept the terms of service
    log.info("Using boulder server %s", directory_url)

    client.net.account = client.new_account(
        messages.NewRegistration.from_data(
            terms_of_service_agreed=True
        )
    )

    # Now we need to open an order and request our certificate

    # NOTE: We'll let ACME generate a CSR for our private key as there's
    # a lot of utility code it uses to generate the CSR in a specific
    # fashion. Better to use what LE provides than to roll our own as we
    # we doing with the v1 code
    #
    # This will also let us support multi-domain certificat requests in the
    # future, as well as mandate OCSP-Must-Staple if/when GL's HTTPS server
    # supports it
    csr = crypto_util.make_csr(priv_key, [hostname], False)
    order = client.new_order(csr)
    authzr = order.authorizations

    log.info('Created a new order for %s', hostname)

    # authrz is a list of Authorization resources, we need to find the
    # HTTP-01 challenge and use it
    for auth_req in authzr:  # pylint: disable=not-an-iterable
        for chall_body in auth_req.body.challenges:
            if isinstance(chall_body.chall, challenges.HTTP01):
                challb = chall_body
                break

    if challb is None:
        raise Exception("HTTP01 challenge unavailable!")

    _, chall_tok = challb.response_and_validation(client.net.key)
    v = chall_body.chall.encode("token")
    log.info('Exposing challenge on %s', v)
    tmp_chall_dict.set(v, ChallTok(chall_tok))

    cr = client.answer_challenge(challb, challb.response(client.net.key))
    log.debug('Acme CA responded to challenge request with: %s', cr)

    order = client.poll_and_finalize(order)

    return split_certificate_chain(order.fullchain_pem)
Exemplo n.º 6
0
def setup_client(storage: "StorageProtocol", account_email: str,
                 directory_url: str) -> acme.client.ClientV2:
    account = storage.get_account()
    if account:
        client = build_client(account, directory_url)
    else:
        new_account = Account()
        client = build_client(new_account, directory_url)
        new_account.regr = client.new_account(
            messages.NewRegistration.from_data(email=account_email,
                                               terms_of_service_agreed=True))
        storage.set_account(new_account)
    return client
Exemplo n.º 7
0
    def _cron(self):
        ir_config_parameter = self.env['ir.config_parameter']
        domains = self._get_altnames()
        domain = domains[0]
        cert_file = os.path.join(_get_data_dir(), 'domain.crt')

        domains = self._cascade_domains(domains)
        for dom in domains:
            self._validate_domain(dom)

        if not self._should_run(cert_file, domains):
            return

        account_key = josepy.JWKRSA.load(self._get_key('account.key'))
        domain_key = self._get_key('domain.key')

        client = self._create_client(account_key)
        new_reg = acme.messages.NewRegistration(key=account_key.public_key(),
                                                terms_of_service_agreed=True)
        try:
            client.new_account(new_reg)
            _logger.info("Successfully registered.")
        except acme.errors.ConflictError as err:
            reg = acme.messages.Registration(key=account_key.public_key())
            reg_res = acme.messages.RegistrationResource(body=reg,
                                                         uri=err.location)
            client.query_registration(reg_res)
            _logger.info("Reusing existing account.")

        _logger.info('Making CSR for the following domains: %s', domains)
        csr = acme.crypto_util.make_csr(private_key_pem=domain_key,
                                        domains=domains)
        authzr = client.new_order(csr)

        # For each requested domain name we receive a list of challenges.
        # We only have to do one from each list.
        # HTTP challenges are the easiest, so do one of those if possible.
        # We can do DNS challenges too. There are other types that we don't
        # support.
        pending_responses = []

        prefer_dns = (self.env["ir.config_parameter"].get_param(
            "letsencrypt.prefer_dns") == "True")
        for authorizations in authzr.authorizations:
            http_challenges = [
                challenge for challenge in authorizations.body.challenges
                if challenge.chall.typ == TYPE_CHALLENGE_HTTP
            ]
            other_challenges = [
                challenge for challenge in authorizations.body.challenges
                if challenge.chall.typ != TYPE_CHALLENGE_HTTP
            ]
            if prefer_dns:
                ordered_challenges = other_challenges + http_challenges
            else:
                ordered_challenges = http_challenges + other_challenges
            for challenge in ordered_challenges:
                if challenge.chall.typ == TYPE_CHALLENGE_HTTP:
                    self._respond_challenge_http(challenge, account_key)
                    client.answer_challenge(challenge,
                                            acme.challenges.HTTP01Response())
                    break
                elif challenge.chall.typ == TYPE_CHALLENGE_DNS:
                    domain = authorizations.body.identifier.value
                    token = challenge.validation(account_key)
                    self._respond_challenge_dns(domain, token)
                    # We delay this because we wait for each domain.
                    # That takes less time if they've all already been changed.
                    pending_responses.append(
                        DNSUpdate(challenge=challenge,
                                  domain=domain,
                                  token=token))
                    break
            else:
                raise UserError(
                    _('Could not respond to letsencrypt challenges.'))

        if pending_responses:
            for update in pending_responses:
                self._wait_for_record(update.domain, update.token)
            # 1 minute was not always enough during testing, even once records
            # were visible locally
            _logger.info(
                "All TXT records found, waiting 5 minutes more to make sure.")
            time.sleep(300)
            for update in pending_responses:
                client.answer_challenge(update.challenge,
                                        acme.challenges.DNSResponse())

        # let them know we are done and they should check
        backoff = int(ir_config_parameter.get_param('letsencrypt.backoff', 3))
        deadline = datetime.now() + timedelta(minutes=backoff)
        try:
            order_resource = client.poll_and_finalize(authzr, deadline)
        except acme.errors.ValidationError as error:
            _logger.error("Let's Encrypt validation failed!")
            for authz in error.failed_authzrs:
                for challenge in authz.body.challenges:
                    _logger.error(str(challenge.error))
            raise

        with open(cert_file, 'w') as crt:
            crt.write(order_resource.fullchain_pem)
        _logger.info('SUCCESS: Certificate saved: %s', cert_file)
        reload_cmd = ir_config_parameter.get_param(
            'letsencrypt.reload_command', '')
        if reload_cmd.strip():
            self._call_cmdline(reload_cmd)
        else:
            _logger.warning("No reload command defined.")
Exemplo n.º 8
0
def create_or_update_cert(
        kv_cert_name,
        *domains,
        use_prod=False,
        keyvault_url='https://ponti-certs-kvjwxwal2p6n.vault.azure.net/',
        dns_zone_resource_group='damienpontifex.com-rg',
        dns_zone_name='damienpontifex.com',
        registration_email='*****@*****.**',
        dns_subscription_id="fa2cbf67-1293-4f9c-8884-a0379a9e0c64"):

    # Get directory
    if use_prod:
        directory_url = 'https://acme-v02.api.letsencrypt.org/directory'
        user_key_name = 'acme'
        issuance_period_months = 3
    else:
        directory_url = 'https://acme-staging-v02.api.letsencrypt.org/directory'
        user_key_name = 'acme-staging'
        issuance_period_months = 1

    credential = DefaultAzureCredential()

    challenge_handler = functools.partial(
        dns_challenge_handler,
        credential=credential,
        subscription_id=dns_subscription_id,
        dns_zone_resource_group=dns_zone_resource_group,
        dns_zone_name=dns_zone_name)

    cert_client = CertificateClient(vault_url=keyvault_url,
                                    credential=credential)

    #%%
    key = KeyVaultRSAKey(credential, keyvault_url, user_key_name)

    account_key = josepy.JWKRSA(key=key)
    client_network = acme.client.ClientNetwork(account_key)

    directory = messages.Directory.from_json(
        client_network.get(directory_url).json())

    client = acme.client.ClientV2(directory, client_network)

    new_regr = acme.messages.Registration.from_data(
        key=account_key,
        email=registration_email,
        terms_of_service_agreed=True)

    # Register or fetch account
    try:
        regr = client.new_account(new_regr)
        logger.info('Created new account')
    except acme.errors.ConflictError as e:
        regr = acme.messages.RegistrationResource(uri=e.location,
                                                  body=new_regr)
        regr = client.query_registration(regr)
        logger.info('Got existing account')

    cert_policy = CertificatePolicy(
        issuer_name='Unknown',
        subject_name=f'CN={domains[0]}',
        exportable=True,
        key_type=KeyType.rsa,
        key_size=2048,
        content_type=CertificateContentType.pkcs12,
        san_dns_names=domains[1:] if len(domains) > 1 else [],
        validity_in_months=issuance_period_months)

    try:
        # Check an existing certificate operation isn't in progress
        cert_op = cert_client.get_certificate_operation(
            certificate_name=kv_cert_name)
        logger.info('Existing cert operation in progress')
    except ResourceNotFoundError:
        cert_op = cert_client.begin_create_certificate(
            certificate_name=kv_cert_name, policy=cert_policy)
        logger.info('New cert operation')

    # cert_op = kvclient.create_certificate(KEYVAULT_URL, certificate_name=kv_cert_name, certificate_policy=cert_policy)
    cert_op_res = cert_op.result()
    cert_op_r = cert_client.get_certificate_operation(kv_cert_name)

    logger.info('Created certificate request in key vault')

    # Wrap with header and footer for pem to show certificate request
    csr_pem = "-----BEGIN CERTIFICATE REQUEST-----\n" + base64.b64encode(
        cert_op_r.csr).decode() + "\n-----END CERTIFICATE REQUEST-----\n"

    # Submit order
    order_resource = client.new_order(csr_pem)
    logger.info('Submitted order')

    # Challenges from order
    # Respond to challenges
    challenges_to_respond_to = list(
        challenge_handler(authorizations=order_resource.authorizations,
                          account_key=account_key))

    for dns_challenge in challenges_to_respond_to:
        # Perform challenge
        auth_response = client.answer_challenge(
            dns_challenge, dns_challenge.chall.response(account_key))

    logger.info('Answered challenges')

    # Poll for status
    # Finalize order
    # Download certificate
    final_order = client.poll_and_finalize(order_resource)

    logger.info('Finalised order')

    # Strip header and footer of BEGIN/END CERTIFICATE
    # with open('cert.pem', 'w') as f:
    #     f.write(final_order.fullchain_pem)

    certificate_vals = [
        val.replace('\n', '').encode()
        for val in final_order.fullchain_pem.split('-----')
        if 'CERTIFICATE' not in val and len(val.replace('\n', '')) != 0
    ]

    cert_client.merge_certificate(name=kv_cert_name,
                                  x509_certificates=certificate_vals)

    logger.info('Merged certificate back to key vault')
Exemplo n.º 9
0
def run_acme_reg_to_finish(domain, accnt_key, priv_key, hostname,
                           tmp_chall_dict, directory_url):
    '''Runs the entire process of ACME registeration'''

    client = create_v2_client(directory_url, accnt_key)

    # First we need to create a registration with the email address provided
    # and accept the terms of service
    log.info("Using boulder server %s", directory_url)

    client.net.account = client.new_account(
        messages.NewRegistration.from_data(terms_of_service_agreed=True))

    # Now we need to open an order and request our certificate

    # NOTE: We'll let ACME generate a CSR for our private key as there's
    # a lot of utility code it uses to generate the CSR in a specific
    # fashion. Better to use what LE provides than to roll our own as we
    # we doing with the v1 code
    #
    # This will also let us support multi-domain certificat requests in the
    # future, as well as mandate OCSP-Must-Staple if/when GL's HTTPS server
    # supports it
    csr = crypto_util.make_csr(priv_key, [hostname], False)
    order = client.new_order(csr)
    authzr = order.authorizations

    log.info('Created a new order for %s', hostname)

    # authrz is a list of Authorization resources, we need to find the
    # HTTP-01 challenge and use it
    for auth_req in authzr:  # pylint: disable=not-an-iterable
        for chall_body in auth_req.body.challenges:
            if isinstance(chall_body.chall, challenges.HTTP01):
                challb = chall_body
                break

    if challb is None:
        raise Exception("HTTP01 challenge unavailable!")

    response, chall_tok = challb.response_and_validation(client.net.key)
    v = chall_body.chall.encode("token")
    log.info('Exposing challenge on %s', v)
    tmp_chall_dict.set(v, ChallTok(chall_tok))

    cr = client.answer_challenge(challb, challb.response(client.net.key))
    log.debug('Acme CA responded to challenge request with: %s', cr)

    # Wrap this step and log the failure particularly here because this is
    # the expected point of failure for applications that are not reachable
    # from the public internet.
    try:
        order = client.poll_and_finalize(order)

    except messages.Error as error:
        log.err("Failed in request issuance step %s", error)
        raise

    # ACME V2 returns a full chain certificate, and ACME doesn't ship with
    # helper functions out of the box. Fortunately, searching through cerbot
    # this is easily enough to do with pyOpenSSL

    cert = load_certificate(FILETYPE_PEM, order.fullchain_pem)
    cert_str = dump_certificate(FILETYPE_PEM, cert).decode()
    chain_str = order.fullchain_pem[len(cert_str):].lstrip()

    # pylint: disable=no-member
    expr_date = convert_asn1_date(cert.get_notAfter())
    log.info('Retrieved cert using ACME that expires on %s', expr_date)

    return cert_str, chain_str