示例#1
0
    def _new_order_v2(self):
        '''
        Start a new certificate order (ACME v2 protocol).
        https://tools.ietf.org/html/draft-ietf-acme-acme-12#section-7.4
        '''
        identifiers = []
        for domain in self.domains:
            identifiers.append({
                'type': 'dns',
                'value': domain,
            })
        new_order = {"identifiers": identifiers}
        result, info = self.account.send_signed_request(
            self.directory['newOrder'], new_order)

        if info['status'] not in [201]:
            raise ModuleFailException(
                "Error new order: CODE: {0} RESULT: {1}".format(
                    info['status'], result))

        for auth_uri in result['authorizations']:
            auth_data = simple_get(self.module, auth_uri)
            auth_data['uri'] = auth_uri
            domain = auth_data['identifier']['value']
            if auth_data.get('wildcard', False):
                domain = '*.{0}'.format(domain)
            self.authorizations[domain] = auth_data

        self.order_uri = info['location']
        self.finalize_uri = result['finalize']
示例#2
0
    def _finalize_cert(self):
        '''
        Create a new certificate based on the csr.
        Return the certificate object as dict
        https://tools.ietf.org/html/draft-ietf-acme-acme-12#section-7.4
        '''
        csr = pem_to_der(self.csr)
        new_cert = {
            "csr": nopad_b64(csr),
        }
        result, info = self.account.send_signed_request(
            self.finalize_uri, new_cert)
        if info['status'] not in [200]:
            raise ModuleFailException(
                "Error new cert: CODE: {0} RESULT: {1}".format(
                    info['status'], result))

        order = info['location']

        status = result['status']
        while status not in ['valid', 'invalid']:
            time.sleep(2)
            result = simple_get(self.module, order)
            status = result['status']

        if status != 'valid':
            raise ModuleFailException(
                "Error new cert: CODE: {0} STATUS: {1} RESULT: {2}".format(
                    info['status'], status, result))

        return result['certificate']
示例#3
0
    def _finalize_cert(self):
        '''
        Create a new certificate based on the csr.
        Return the certificate object as dict
        https://tools.ietf.org/html/draft-ietf-acme-acme-09#section-7.4
        '''
        openssl_csr_cmd = [self._openssl_bin, "req", "-in", self.csr, "-outform", "DER"]
        dummy, out, dummy = self.module.run_command(openssl_csr_cmd, check_rc=True)

        new_cert = {
            "csr": nopad_b64(to_bytes(out)),
        }
        result, info = self.account.send_signed_request(self.finalize_uri, new_cert)
        if info['status'] not in [200]:
            raise ModuleFailException("Error new cert: CODE: {0} RESULT: {1}".format(info['status'], result))

        order = info['location']

        status = result['status']
        while status not in ['valid', 'invalid']:
            time.sleep(2)
            result = simple_get(self.module, order)
            status = result['status']

        if status != 'valid':
            raise ModuleFailException("Error new cert: CODE: {0} STATUS: {1} RESULT: {2}".format(info['status'], status, result))

        return result['certificate']
示例#4
0
    def finish_challenges(self):
        '''
        Verify challenges for all domains of the CSR.
        '''
        self.authorizations = {}

        # Step 1: obtain challenge information
        if self.version == 1:
            # For ACME v1, we attempt to create new authzs. Existing ones
            # will be returned instead.
            for domain in self.domains:
                new_auth = self._new_authz_v1(domain)
                self._add_or_update_auth(domain, new_auth)
        else:
            # For ACME v2, we obtain the order object by fetching the
            # order URI, and extract the information from there.
            resp, info = fetch_url(self.module, self.order_uri)
            try:
                result = resp.read()
            except AttributeError:
                result = info.get('body')

            if not result:
                raise ModuleFailException(
                    "Cannot download order from {0}: {1} (headers: {2})".
                    format(self.order_uri, result, info))

            if info['status'] not in [200]:
                raise ModuleFailException(
                    "Error on downloading order: CODE: {0} RESULT: {1}".format(
                        info['status'], result))

            result = self.module.from_json(result.decode('utf8'))
            for auth_uri in result['authorizations']:
                auth_data = simple_get(self.module, auth_uri)
                auth_data['uri'] = auth_uri
                domain = auth_data['identifier']['value']
                if auth_data.get('wildcard', False):
                    domain = '*.{0}'.format(domain)
                self.authorizations[domain] = auth_data

            self.finalize_uri = result['finalize']

        # Step 2: validate challenges
        for domain, auth in self.authorizations.items():
            if auth['status'] == 'pending':
                self._validate_challenges(domain, auth)
示例#5
0
    def _validate_challenges(self, domain, auth):
        '''
        Validate the authorization provided in the auth dict. Returns True
        when the validation was successful and False when it was not.
        '''
        for challenge in auth['challenges']:
            if self.challenge != challenge['type']:
                continue

            uri = challenge['uri'] if self.version == 1 else challenge['url']

            challenge_response = {}
            if self.version == 1:
                token = re.sub(r"[^A-Za-z0-9_\-]", "_", challenge['token'])
                keyauthorization = self.account.get_keyauthorization(token)
                challenge_response["resource"] = "challenge"
                challenge_response["keyAuthorization"] = keyauthorization
            result, info = self.account.send_signed_request(
                uri, challenge_response)
            if info['status'] not in [200, 202]:
                raise ModuleFailException(
                    "Error validating challenge: CODE: {0} RESULT: {1}".format(
                        info['status'], result))

        status = ''

        while status not in ['valid', 'invalid', 'revoked']:
            result = simple_get(self.module, auth['uri'])
            result['uri'] = auth['uri']
            if self._add_or_update_auth(domain, result):
                self.changed = True
            # https://tools.ietf.org/html/draft-ietf-acme-acme-02#section-6.1.2
            # "status (required, string): ...
            # If this field is missing, then the default value is "pending"."
            if self.version == 1 and 'status' not in result:
                status = 'pending'
            else:
                status = result['status']
            time.sleep(2)

        if status == 'invalid':
            self._fail_challenge(domain, result,
                                 'Authorization for {0} returned invalid')

        return status == 'valid'