Example #1
0
def verify_cert_chain(certs, error_class=ValidationError):
    """
    Verifies that the certificates in the chain are correct.

    We don't bother with full cert validation but just check that certs in the chain are signed by the next, to avoid
    basic human errors -- such as pasting the wrong certificate.

    :param certs: List of parsed certificates, use parse_cert_chain()
    :param error_class: Exception class to raise on error
    """
    cert = certs[0]
    for issuer in certs[1:]:
        # Use the current cert's public key to verify the previous signature.
        # "certificate validation is a complex problem that involves much more than just signature checks"
        try:
            check_cert_signature(cert, issuer.public_key())

        except InvalidSignature:
            # Avoid circular import.
            from lemur.common import defaults

            raise error_class("Incorrect chain certificate(s) provided: '%s' is not signed by '%s'"
                              % (defaults.common_name(cert) or 'Unknown', defaults.common_name(issuer)))

        except UnsupportedAlgorithm as err:
            current_app.logger.warning("Skipping chain validation: %s", err)

        # Next loop will validate that *this issuer* cert is signed by the next chain cert.
        cert = issuer
Example #2
0
def verify_cert_chain(certs, error_class=ValidationError):
    """
    Verifies that the certificates in the chain are correct.

    We don't bother with full cert validation but just check that certs in the chain are signed by the next, to avoid
    basic human errors -- such as pasting the wrong certificate.

    :param certs: List of parsed certificates, use parse_cert_chain()
    :param error_class: Exception class to raise on error
    """
    cert = certs[0]
    for issuer in certs[1:]:
        # Use the current cert's public key to verify the previous signature.
        # "certificate validation is a complex problem that involves much more than just signature checks"
        try:
            check_cert_signature(cert, issuer.public_key())

        except InvalidSignature:
            # Avoid circular import.
            from lemur.common import defaults

            raise error_class(
                "Incorrect chain certificate(s) provided: '%s' is not signed by '%s'"
                % (
                    defaults.common_name(cert) or "Unknown",
                    defaults.common_name(issuer),
                ))

        except UnsupportedAlgorithm as err:
            current_app.logger.warning("Skipping chain validation: %s", err)

        # Next loop will validate that *this issuer* cert is signed by the next chain cert.
        cert = issuer
Example #3
0
    def upload(self, name, body, private_key, cert_chain, options, **kwargs):

        try:
            k8_base_uri = self.get_option('kubernetesURL', options)
            secret_format = self.get_option('secretFormat', options)
            k8s_api = K8sSession(
                self.k8s_bearer(options),
                self.k8s_cert(options)
            )
            cn = common_name(parse_certificate(body))
            secret_name_format = self.get_option('secretNameFormat', options)
            secret_name = secret_name_format.format(common_name=cn)
            secret = build_secret(secret_format, secret_name, body, private_key, cert_chain)
            err = ensure_resource(
                k8s_api,
                k8s_base_uri=k8_base_uri,
                namespace=self.k8s_namespace(options),
                kind="secret",
                name=secret_name,
                data=secret
            )

        except Exception as e:
            current_app.logger.exception("Exception in upload: {}".format(e), exc_info=True)
            raise

        if err is not None:
            current_app.logger.error("Error deploying resource: %s", err)
            raise Exception("Error uploading secret: " + err)
Example #4
0
    def upload(self, name, body, private_key, cert_chain, options, **kwargs):

        try:
            k8_base_uri = self.get_option("kubernetesURL", options)
            secret_format = self.get_option("secretFormat", options)
            k8s_api = K8sSession(self.k8s_bearer(options),
                                 self.k8s_cert(options))
            cn = common_name(parse_certificate(body))
            secret_name_format = self.get_option("secretNameFormat", options)
            secret_name = secret_name_format.format(common_name=cn)
            secret = build_secret(secret_format, secret_name, body,
                                  private_key, cert_chain)
            err = ensure_resource(
                k8s_api,
                k8s_base_uri=k8_base_uri,
                namespace=self.k8s_namespace(options),
                kind="secret",
                name=secret_name,
                data=secret,
            )

        except Exception as e:
            current_app.logger.exception("Exception in upload: {}".format(e),
                                         exc_info=True)
            raise

        if err is not None:
            current_app.logger.error("Error deploying resource: %s", err)
            raise Exception("Error uploading secret: " + err)
Example #5
0
    def __init__(self, **kwargs):
        cert = defaults.parse_certificate(kwargs['body'])

        self.issuer = defaults.issuer(cert)
        self.cn = defaults.common_name(cert)
        self.san = defaults.san(cert)
        self.not_before = defaults.not_before(cert)
        self.not_after = defaults.not_after(cert)

        # when destinations are appended they require a valid name.
        if kwargs.get('name'):
            self.name = kwargs['name']
        else:
            self.name = get_or_increase_name(defaults.certificate_name(self.cn, self.issuer, self.not_before, self.not_after, self.san))

        self.owner = kwargs['owner']
        self.body = kwargs['body']
        self.private_key = kwargs.get('private_key')
        self.chain = kwargs.get('chain')
        self.destinations = kwargs.get('destinations', [])
        self.notifications = kwargs.get('notifications', [])
        self.description = kwargs.get('description')
        self.roles = list(set(kwargs.get('roles', [])))
        self.replaces = kwargs.get('replacements', [])
        self.signing_algorithm = defaults.signing_algorithm(cert)
        self.bits = defaults.bitstrength(cert)
        self.serial = defaults.serial(cert)

        for domain in defaults.domains(cert):
            self.domains.append(Domain(name=domain))
Example #6
0
    def __init__(self, **kwargs):
        cert = defaults.parse_certificate(kwargs['body'])

        self.issuer = defaults.issuer(cert)
        self.cn = defaults.common_name(cert)
        self.san = defaults.san(cert)
        self.not_before = defaults.not_before(cert)
        self.not_after = defaults.not_after(cert)

        # when destinations are appended they require a valid name.
        if kwargs.get('name'):
            self.name = kwargs['name']
        else:
            self.name = get_or_increase_name(
                defaults.certificate_name(self.cn, self.issuer,
                                          self.not_before, self.not_after,
                                          self.san))

        self.owner = kwargs['owner']
        self.body = kwargs['body']
        self.private_key = kwargs.get('private_key')
        self.chain = kwargs.get('chain')
        self.destinations = kwargs.get('destinations', [])
        self.notifications = kwargs.get('notifications', [])
        self.description = kwargs.get('description')
        self.roles = list(set(kwargs.get('roles', [])))
        self.replaces = kwargs.get('replacements', [])
        self.signing_algorithm = defaults.signing_algorithm(cert)
        self.bits = defaults.bitstrength(cert)
        self.serial = defaults.serial(cert)

        for domain in defaults.domains(cert):
            self.domains.append(Domain(name=domain))
Example #7
0
    def __init__(self, **kwargs):
        self.body = kwargs["body"].strip()
        cert = self.parsed_cert

        self.issuer = defaults.issuer(cert)
        self.cn = defaults.common_name(cert)
        self.san = defaults.san(cert)
        self.not_before = defaults.not_before(cert)
        self.not_after = defaults.not_after(cert)
        self.serial = defaults.serial(cert)

        self.owner = kwargs["owner"]

        if kwargs.get("private_key"):
            self.private_key = kwargs["private_key"].strip()

        if kwargs.get("chain"):
            self.chain = kwargs["chain"].strip()

        if kwargs.get("csr"):
            self.csr = kwargs["csr"].strip()

        self.notify = kwargs.get("notify", True)
        self.notifications = kwargs.get("notifications", [])
        self.description = kwargs.get("description")
        self.roles = list(set(kwargs.get("roles", [])))
        self.replaces = kwargs.get("replaces", [])
        self.rotation = kwargs.get("rotation")
        self.rotation_policy = kwargs.get("rotation_policy")
        self.key_type = kwargs.get("key_type")
        self.signing_algorithm = defaults.signing_algorithm(cert)
        self.bits = defaults.bitstrength(cert)
        self.external_id = kwargs.get("external_id")
        self.authority_id = kwargs.get("authority_id")
        self.dns_provider_id = kwargs.get("dns_provider_id")

        for domain in defaults.domains(cert):
            self.domains.append(Domain(name=domain))

        # when destinations are appended they require a valid name
        # do not attempt to modify self.destinations before this step
        if kwargs.get("name"):
            self.name = get_or_increase_name(
                defaults.text_to_slug(kwargs["name"]), self.serial)
        else:
            self.name = get_or_increase_name(
                defaults.certificate_name(self.cn, self.issuer,
                                          self.not_before, self.not_after,
                                          self.san, self.domains),
                self.serial,
            )

        self.destinations = kwargs.get("destinations", [])

        # Check integrity before saving anything into the database.
        # For user-facing API calls, validation should also be done in schema validators.
        self.check_integrity()
Example #8
0
    def upload(self, name, body, private_key, cert_chain, options, **kwargs):
        """
        Upload certificate and private key

        :param private_key:
        :param cert_chain:
        :return:
        """

        # we use the common name to identify the certificate
        # Azure does not allow "." in the certificate name we replace them with "-"
        cert = parse_certificate(body)
        certificate_name = common_name(cert).replace(".", "-")

        vault_URI = self.get_option("vaultUrl", options)
        tenant = self.get_option("azureTenant", options)
        app_id = self.get_option("appID", options)
        password = self.get_option("azurePassword", options)

        access_token = get_access_token(tenant, app_id, password, self)

        cert_url = f"{vault_URI}/certificates/{certificate_name}/import?api-version=7.1"
        post_header = {"Authorization": f"Bearer {access_token}"}
        key_pkcs8 = parse_private_key(private_key).private_bytes(
            encoding=serialization.Encoding.PEM,
            format=serialization.PrivateFormat.PKCS8,
            encryption_algorithm=serialization.NoEncryption(),
        )
        key_pkcs8 = key_pkcs8.decode("utf-8").replace('\\n', '\n')
        cert_package = f"{body}\n{key_pkcs8}"

        post_body = {
            "value": cert_package,
            "policy": {
                "key_props": {
                    "exportable": True,
                    "kty": "RSA",
                    "key_size": bitstrength(cert),
                    "reuse_key": True
                },
                "secret_props": {
                    "contentType": "application/x-pem-file"
                }
            }
        }

        try:
            response = self.session.post(cert_url,
                                         headers=post_header,
                                         json=post_body)
        except requests.exceptions.RequestException as e:
            current_app.logger.exception(f"AZURE: Error for POST {e}")
        return_value = handle_response(response)
Example #9
0
    def __init__(self, **kwargs):
        self.body = kwargs['body'].strip()
        cert = self.parsed_cert

        self.issuer = defaults.issuer(cert)
        self.cn = defaults.common_name(cert)
        self.san = defaults.san(cert)
        self.not_before = defaults.not_before(cert)
        self.not_after = defaults.not_after(cert)
        self.serial = defaults.serial(cert)

        # when destinations are appended they require a valid name.
        if kwargs.get('name'):
            self.name = get_or_increase_name(
                defaults.text_to_slug(kwargs['name']), self.serial)
        else:
            self.name = get_or_increase_name(
                defaults.certificate_name(self.cn, self.issuer,
                                          self.not_before, self.not_after,
                                          self.san), self.serial)

        self.owner = kwargs['owner']

        if kwargs.get('private_key'):
            self.private_key = kwargs['private_key'].strip()

        if kwargs.get('chain'):
            self.chain = kwargs['chain'].strip()

        if kwargs.get('csr'):
            self.csr = kwargs['csr'].strip()

        self.notify = kwargs.get('notify', True)
        self.destinations = kwargs.get('destinations', [])
        self.notifications = kwargs.get('notifications', [])
        self.description = kwargs.get('description')
        self.roles = list(set(kwargs.get('roles', [])))
        self.replaces = kwargs.get('replaces', [])
        self.rotation = kwargs.get('rotation')
        self.rotation_policy = kwargs.get('rotation_policy')
        self.signing_algorithm = defaults.signing_algorithm(cert)
        self.bits = defaults.bitstrength(cert)
        self.external_id = kwargs.get('external_id')
        self.authority_id = kwargs.get('authority_id')
        self.dns_provider_id = kwargs.get('dns_provider_id')

        for domain in defaults.domains(cert):
            self.domains.append(Domain(name=domain))

        # Check integrity before saving anything into the database.
        # For user-facing API calls, validation should also be done in schema validators.
        self.check_integrity()
Example #10
0
    def upload(self, name, body, private_key, cert_chain, options, **kwargs):
        """
        Upload certificate and private key

        :param private_key:
        :param cert_chain:
        :return:
        """
        cname = common_name(parse_certificate(body))

        url = self.get_option('vaultUrl', options)
        token_file = self.get_option('vaultAuthTokenFile', options)
        mount = self.get_option('vaultMount', options)
        path = self.get_option('vaultPath', options)
        bundle = self.get_option('bundleChain', options)
        obj_name = self.get_option('objectName', options)
        api_version = self.get_option('vaultKvApiVersion', options)

        with open(token_file, 'r') as file:
            token = file.readline().rstrip('\n')

        client = hvac.Client(url=url, token=token)
        client.secrets.kv.default_kv_version = api_version

        if obj_name:
            path = '{0}/{1}'.format(path, obj_name)
        else:
            path = '{0}/{1}'.format(path, cname)

        secret = get_secret(client, mount, path)
        secret['data'][cname] = {}

        if bundle == 'Nginx' and cert_chain:
            secret['data'][cname]['crt'] = '{0}\n{1}'.format(body, cert_chain)
        elif bundle == 'Apache' and cert_chain:
            secret['data'][cname]['crt'] = body
            secret['data'][cname]['chain'] = cert_chain
        else:
            secret['data'][cname]['crt'] = body
        secret['data'][cname]['key'] = private_key
        san_list = get_san_list(body)
        if isinstance(san_list, list):
            secret['data'][cname]['san'] = san_list
        try:
            client.secrets.kv.create_or_update_secret(path=path,
                                                      mount_point=mount,
                                                      secret=secret['data'])
        except ConnectionError as err:
            current_app.logger.exception(
                "Exception uploading secret to vault: {0}".format(err),
                exc_info=True)
Example #11
0
    def __init__(self, **kwargs):
        self.body = kwargs['body'].strip()
        cert = self.parsed_cert

        self.issuer = defaults.issuer(cert)
        self.cn = defaults.common_name(cert)
        self.san = defaults.san(cert)
        self.not_before = defaults.not_before(cert)
        self.not_after = defaults.not_after(cert)
        self.serial = defaults.serial(cert)

        # when destinations are appended they require a valid name.
        if kwargs.get('name'):
            self.name = get_or_increase_name(defaults.text_to_slug(kwargs['name']), self.serial)
        else:
            self.name = get_or_increase_name(
                defaults.certificate_name(self.cn, self.issuer, self.not_before, self.not_after, self.san), self.serial)

        self.owner = kwargs['owner']

        if kwargs.get('private_key'):
            self.private_key = kwargs['private_key'].strip()

        if kwargs.get('chain'):
            self.chain = kwargs['chain'].strip()

        if kwargs.get('csr'):
            self.csr = kwargs['csr'].strip()

        self.notify = kwargs.get('notify', True)
        self.destinations = kwargs.get('destinations', [])
        self.notifications = kwargs.get('notifications', [])
        self.description = kwargs.get('description')
        self.roles = list(set(kwargs.get('roles', [])))
        self.replaces = kwargs.get('replaces', [])
        self.rotation = kwargs.get('rotation')
        self.rotation_policy = kwargs.get('rotation_policy')
        self.signing_algorithm = defaults.signing_algorithm(cert)
        self.bits = defaults.bitstrength(cert)
        self.external_id = kwargs.get('external_id')
        self.authority_id = kwargs.get('authority_id')
        self.dns_provider_id = kwargs.get('dns_provider_id')

        for domain in defaults.domains(cert):
            self.domains.append(Domain(name=domain))

        # Check integrity before saving anything into the database.
        # For user-facing API calls, validation should also be done in schema validators.
        self.check_integrity()
Example #12
0
    def upload(self, name, body, private_key, cert_chain, options, **kwargs):
        """
        Upload certificate and private key

        :param private_key:
        :param cert_chain:
        :return:
        """
        priv_id = None
        cert_id = None
        name_found = False
        cname = common_name(parse_certificate(body))
        priv_keys = get_all_private_keys()
        cert_keys = get_all_certificates()
        log_data = {
            "function": inspect.currentframe().f_code.co_name
        }
        for each in priv_keys:
            if each['name'] == cname:
                name_found = True
                if each['sha1'] == get_public_key_sha1(private_key):
                    priv_id = None
                    break
                else:
                    priv_id = each['id']
                    for cert in cert_keys:
                        if cert['name'] == cname:
                            cert_id = cert['id']
        if priv_id or not name_found:
            post_private_key(private_key, name=cname)
            time.sleep(2)
            new_cert_id = post_certificate(body, cert_chain, name=cname)
            log_data["message"] = f"Certificate updated: {cname}"
            act_id = get_activation(cert_id)
            if len(act_id) > 0 and new_cert_id:
                for each in act_id:
                    patch_activation(each, new_cert_id)
                    log_data["message"] = f"Certificate updated: {new_cert_id} activated: {act_id}"
            if cert_id:
                delete_certificate(cert_id)
            if priv_id:
                delete_private_key(priv_id)
        else:
            log_data["message"] = f"Certificate up to data, no changes made"
        current_app.logger.debug(log_data)
Example #13
0
    def __init__(self, **kwargs):
        self.csr = kwargs.get("csr")
        self.private_key = kwargs.get("private_key", "")
        if self.private_key:
            # If the request does not send private key, the key exists but the value is None
            self.private_key = self.private_key.strip()
        self.external_id = kwargs.get("external_id")

        # when destinations are appended they require a valid name.
        if kwargs.get("name"):
            self.name = get_or_increase_name(defaults.text_to_slug(kwargs["name"]), 0)
            self.rename = False
        else:
            # TODO: Fix auto-generated name, it should be renamed on creation
            self.name = get_or_increase_name(
                defaults.certificate_name(
                    kwargs["common_name"],
                    kwargs["authority"].name,
                    dt.now(),
                    dt.now(),
                    False,
                ),
                self.external_id,
            )
            self.rename = True

        self.cn = defaults.common_name(utils.parse_csr(self.csr))
        self.owner = kwargs["owner"]
        self.number_attempts = 0

        if kwargs.get("chain"):
            self.chain = kwargs["chain"].strip()

        self.notify = kwargs.get("notify", True)
        self.destinations = kwargs.get("destinations", [])
        self.notifications = kwargs.get("notifications", [])
        self.description = kwargs.get("description")
        self.roles = list(set(kwargs.get("roles", [])))
        self.replaces = kwargs.get("replaces", [])
        self.rotation = kwargs.get("rotation")
        self.rotation_policy = kwargs.get("rotation_policy")
        try:
            self.dns_provider_id = kwargs.get("dns_provider").id
        except (AttributeError, KeyError, TypeError, Exception):
            pass
Example #14
0
    def __init__(self, **kwargs):
        cert = lemur.common.utils.parse_certificate(kwargs['body'])

        self.issuer = defaults.issuer(cert)
        self.cn = defaults.common_name(cert)
        self.san = defaults.san(cert)
        self.not_before = defaults.not_before(cert)
        self.not_after = defaults.not_after(cert)
        self.serial = defaults.serial(cert)

        # when destinations are appended they require a valid name.
        if kwargs.get('name'):
            self.name = get_or_increase_name(
                defaults.text_to_slug(kwargs['name']), self.serial)
        else:
            self.name = get_or_increase_name(
                defaults.certificate_name(self.cn, self.issuer,
                                          self.not_before, self.not_after,
                                          self.san), self.serial)

        self.owner = kwargs['owner']
        self.body = kwargs['body'].strip()

        if kwargs.get('private_key'):
            self.private_key = kwargs['private_key'].strip()

        if kwargs.get('chain'):
            self.chain = kwargs['chain'].strip()

        self.notify = kwargs.get('notify', True)
        self.destinations = kwargs.get('destinations', [])
        self.notifications = kwargs.get('notifications', [])
        self.description = kwargs.get('description')
        self.roles = list(set(kwargs.get('roles', [])))
        self.replaces = kwargs.get('replaces', [])
        self.rotation = kwargs.get('rotation')
        self.rotation_policy = kwargs.get('rotation_policy')
        self.signing_algorithm = defaults.signing_algorithm(cert)
        self.bits = defaults.bitstrength(cert)
        self.external_id = kwargs.get('external_id')
        self.authority_id = kwargs.get('authority_id')
        self.dns_provider_id = kwargs.get('dns_provider_id')

        for domain in defaults.domains(cert):
            self.domains.append(Domain(name=domain))
Example #15
0
    def export(self, body, chain, key, options, **kwargs):
        """
        Generates a Java Truststore
        """

        if self.get_option("alias", options):
            alias = self.get_option("alias", options)
        else:
            alias = common_name(parse_certificate(body))

        if self.get_option("passphrase", options):
            passphrase = self.get_option("passphrase", options)
        else:
            passphrase = Fernet.generate_key().decode("utf-8")

        raw = create_truststore(body, chain, alias, passphrase)

        return "jks", passphrase, raw
Example #16
0
    def export(self, body, chain, key, options, **kwargs):
        """
        Generates a Java Keystore
        """

        if self.get_option('passphrase', options):
            passphrase = self.get_option('passphrase', options)
        else:
            passphrase = Fernet.generate_key().decode('utf-8')

        if self.get_option('alias', options):
            alias = self.get_option('alias', options)
        else:
            alias = common_name(parse_certificate(body))

        raw = create_keystore(body, chain, key, alias, passphrase)

        return 'jks', passphrase, raw
Example #17
0
    def export(self, body, chain, key, options, **kwargs):
        """
        Generates a Java Truststore
        """

        if self.get_option('alias', options):
            alias = self.get_option('alias', options)
        else:
            alias = common_name(parse_certificate(body))

        if self.get_option('passphrase', options):
            passphrase = self.get_option('passphrase', options)
        else:
            passphrase = Fernet.generate_key().decode('utf-8')

        raw = create_truststore(body, chain, alias, passphrase)

        return 'jks', passphrase, raw
Example #18
0
    def __init__(self, **kwargs):
        self.csr = kwargs.get('csr')
        self.private_key = kwargs.get('private_key', "")
        if self.private_key:
            # If the request does not send private key, the key exists but the value is None
            self.private_key = self.private_key.strip()
        self.external_id = kwargs.get('external_id')

        # when destinations are appended they require a valid name.
        if kwargs.get('name'):
            self.name = get_or_increase_name(
                defaults.text_to_slug(kwargs['name']), 0)
            self.rename = False
        else:
            # TODO: Fix auto-generated name, it should be renamed on creation
            self.name = get_or_increase_name(
                defaults.certificate_name(kwargs['common_name'],
                                          kwargs['authority'].name, dt.now(),
                                          dt.now(), False), self.external_id)
            self.rename = True

        self.cn = defaults.common_name(utils.parse_csr(self.csr))
        self.owner = kwargs['owner']
        self.number_attempts = 0

        if kwargs.get('chain'):
            self.chain = kwargs['chain'].strip()

        self.notify = kwargs.get('notify', True)
        self.destinations = kwargs.get('destinations', [])
        self.notifications = kwargs.get('notifications', [])
        self.description = kwargs.get('description')
        self.roles = list(set(kwargs.get('roles', [])))
        self.replaces = kwargs.get('replaces', [])
        self.rotation = kwargs.get('rotation')
        self.rotation_policy = kwargs.get('rotation_policy')
        try:
            self.dns_provider_id = kwargs.get('dns_provider').id
        except (AttributeError, KeyError, TypeError, Exception):
            pass
Example #19
0
    def upload(self, name, body, private_key, cert_chain, options, **kwargs):

        current_app.logger.debug("SFTP destination plugin is started")

        cn = common_name(parse_certificate(body))
        dst_path = self.get_option("destinationPath", options)
        dst_path_cn = dst_path + "/" + cn
        export_format = self.get_option("exportFormat", options)

        # prepare files for upload
        files = {cn + ".key": private_key, cn + ".pem": body}

        if cert_chain:
            if export_format == "NGINX":
                # assemble body + chain in the single file
                files[cn + ".pem"] += "\n" + cert_chain

            elif export_format == "Apache":
                # store chain in the separate file
                files[cn + ".ca.bundle.pem"] = cert_chain

        self.upload_file(dst_path_cn, files, options)
Example #20
0
    def __init__(self, **kwargs):
        self.csr = kwargs.get('csr')
        self.private_key = kwargs.get('private_key', "")
        if self.private_key:
            # If the request does not send private key, the key exists but the value is None
            self.private_key = self.private_key.strip()
        self.external_id = kwargs.get('external_id')

        # when destinations are appended they require a valid name.
        if kwargs.get('name'):
            self.name = get_or_increase_name(defaults.text_to_slug(kwargs['name']), 0)
            self.rename = False
        else:
            # TODO: Fix auto-generated name, it should be renamed on creation
            self.name = get_or_increase_name(
                defaults.certificate_name(kwargs['common_name'], kwargs['authority'].name,
                                          dt.now(), dt.now(), False), self.external_id)
            self.rename = True

        self.cn = defaults.common_name(utils.parse_csr(self.csr))
        self.owner = kwargs['owner']
        self.number_attempts = 0

        if kwargs.get('chain'):
            self.chain = kwargs['chain'].strip()

        self.notify = kwargs.get('notify', True)
        self.destinations = kwargs.get('destinations', [])
        self.notifications = kwargs.get('notifications', [])
        self.description = kwargs.get('description')
        self.roles = list(set(kwargs.get('roles', [])))
        self.replaces = kwargs.get('replaces', [])
        self.rotation = kwargs.get('rotation')
        self.rotation_policy = kwargs.get('rotation_policy')
        try:
            self.dns_provider_id = kwargs.get('dns_provider').id
        except (AttributeError, KeyError, TypeError, Exception):
            pass
Example #21
0
    def export(self, body, chain, key, options, **kwargs):
        """
        Generates a PKCS#12 archive.

        :param key:
        :param chain:
        :param body:
        :param options:
        :param kwargs:
        """
        if self.get_option('passphrase', options):
            passphrase = self.get_option('passphrase', options)
        else:
            passphrase = get_psuedo_random_string()

        if self.get_option('alias', options):
            alias = self.get_option('alias', options)
        else:
            alias = common_name(parse_certificate(body))

        type = self.get_option('type', options)

        with mktemppath() as output_tmp:
            if type == 'PKCS12 (.p12)':
                if not key:
                    raise Exception("Private Key required by {0}".format(type))

                create_pkcs12(body, chain, output_tmp, key, alias, passphrase)
                extension = "p12"
            else:
                raise Exception(
                    "Unable to export, unsupported type: {0}".format(type))

            with open(output_tmp, 'rb') as f:
                raw = f.read()

        return extension, passphrase, raw
Example #22
0
    def upload(self, name, body, private_key, cert_chain, options, **kwargs):
        """
        Upload certificate and private key

        :param private_key:
        :param cert_chain:
        :return:
        """
        cname = common_name(parse_certificate(body))

        url = self.get_option("vaultUrl", options)
        auth_method = self.get_option("authenticationMethod", options)
        auth_key = self.get_option("tokenFile/vaultRole", options)
        mount = self.get_option("vaultMount", options)
        path = self.get_option("vaultPath", options)
        bundle = self.get_option("bundleChain", options)
        obj_name = self.get_option("objectName", options)
        api_version = self.get_option("vaultKvApiVersion", options)
        san_filter = self.get_option("sanFilter", options)

        san_list = get_san_list(body)
        if san_filter:
            for san in san_list:
                try:
                    if not re.match(san_filter, san, flags=re.IGNORECASE):
                        current_app.logger.exception(
                            "Exception uploading secret to vault: invalid SAN: {}"
                            .format(san),
                            exc_info=True,
                        )
                        os._exit(1)
                except re.error:
                    current_app.logger.exception(
                        "Exception compiling regex filter: invalid filter",
                        exc_info=True,
                    )

        client = hvac.Client(url=url)
        if auth_method == 'token':
            with open(auth_key, "r") as tfile:
                token = tfile.readline().rstrip("\n")
            client.token = token

        if auth_method == 'kubernetes':
            token_path = '/var/run/secrets/kubernetes.io/serviceaccount/token'
            with open(token_path, 'r') as f:
                jwt = f.read()
            client.auth_kubernetes(auth_key, jwt)

        client.secrets.kv.default_kv_version = api_version

        if obj_name:
            path = "{0}/{1}".format(path, obj_name)
        else:
            path = "{0}/{1}".format(path, cname)

        secret = get_secret(client, mount, path)
        secret["data"][cname] = {}

        if not cert_chain:
            chain = ''
        else:
            chain = cert_chain

        if bundle == "Nginx":
            secret["data"][cname]["crt"] = "{0}\n{1}".format(body, chain)
            secret["data"][cname]["key"] = private_key
        elif bundle == "Apache":
            secret["data"][cname]["crt"] = body
            secret["data"][cname]["chain"] = chain
            secret["data"][cname]["key"] = private_key
        elif bundle == "PEM":
            secret["data"][cname]["pem"] = "{0}\n{1}\n{2}".format(
                body, chain, private_key)
        else:
            secret["data"][cname]["crt"] = body
            secret["data"][cname]["key"] = private_key
        if isinstance(san_list, list):
            secret["data"][cname]["san"] = san_list
        try:
            client.secrets.kv.create_or_update_secret(path=path,
                                                      mount_point=mount,
                                                      secret=secret["data"])
        except ConnectionError as err:
            current_app.logger.exception(
                "Exception uploading secret to vault: {0}".format(err),
                exc_info=True)
Example #23
0
def test_cert_get_cn(client):
    from lemur.common.defaults import common_name

    assert common_name(SAN_CERT) == 'san.example.org'
Example #24
0
def test_cert_get_cn(client):
    from .vectors import INTERNAL_VALID_LONG_CERT
    from lemur.common.defaults import common_name

    assert common_name(INTERNAL_VALID_LONG_CERT) == 'long.lived.com'
Example #25
0
def test_cert_get_cn(client):
    from .vectors import INTERNAL_VALID_LONG_CERT
    from lemur.common.defaults import common_name

    assert common_name(INTERNAL_VALID_LONG_CERT) == 'long.lived.com'
Example #26
0
def test_cert_get_cn(client):
    from lemur.common.defaults import common_name

    assert common_name(SAN_CERT) == 'san.example.org'
Example #27
0
    def upload(self, name, body, private_key, cert_chain, options, **kwargs):

        current_app.logger.debug('SFTP destination plugin is started')

        cn = common_name(parse_certificate(body))
        host = self.get_option('host', options)
        port = self.get_option('port', options)
        user = self.get_option('user', options)
        password = self.get_option('password', options)
        ssh_priv_key = self.get_option('privateKeyPath', options)
        ssh_priv_key_pass = self.get_option('privateKeyPass', options)
        dst_path = self.get_option('destinationPath', options)
        export_format = self.get_option('exportFormat', options)

        # prepare files for upload
        files = {cn + '.key': private_key,
                 cn + '.pem': body}

        if cert_chain:
            if export_format == 'NGINX':
                # assemble body + chain in the single file
                files[cn + '.pem'] += '\n' + cert_chain

            elif export_format == 'Apache':
                # store chain in the separate file
                files[cn + '.ca.bundle.pem'] = cert_chain

        # upload files
        try:
            current_app.logger.debug('Connecting to {0}@{1}:{2}'.format(user, host, port))
            ssh = paramiko.SSHClient()

            # allow connection to the new unknown host
            ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())

            # open the ssh connection
            if password:
                current_app.logger.debug('Using password')
                ssh.connect(host, username=user, port=port, password=password)
            elif ssh_priv_key:
                current_app.logger.debug('Using RSA private key')
                pkey = paramiko.RSAKey.from_private_key_file(ssh_priv_key, ssh_priv_key_pass)
                ssh.connect(host, username=user, port=port, pkey=pkey)
            else:
                current_app.logger.error("No password or private key provided. Can't proceed")
                raise paramiko.ssh_exception.AuthenticationException

            # open the sftp session inside the ssh connection
            sftp = ssh.open_sftp()

            # make sure that the destination path exist
            try:
                current_app.logger.debug('Creating {0}'.format(dst_path))
                sftp.mkdir(dst_path)
            except IOError:
                current_app.logger.debug('{0} already exist, resuming'.format(dst_path))
            try:
                dst_path_cn = dst_path + '/' + cn
                current_app.logger.debug('Creating {0}'.format(dst_path_cn))
                sftp.mkdir(dst_path_cn)
            except IOError:
                current_app.logger.debug('{0} already exist, resuming'.format(dst_path_cn))

            # upload certificate files to the sftp destination
            for filename, data in files.items():
                current_app.logger.debug('Uploading {0} to {1}'.format(filename, dst_path_cn))
                with sftp.open(dst_path_cn + '/' + filename, 'w') as f:
                    f.write(data)
                # read only for owner, -r--------
                sftp.chmod(dst_path_cn + '/' + filename, 0o400)

            ssh.close()

        except Exception as e:
            current_app.logger.error('ERROR in {0}: {1}'.format(e.__class__, e))
            try:
                ssh.close()
            except BaseException:
                pass
Example #28
0
    def upload(self, name, body, private_key, cert_chain, options, **kwargs):

        current_app.logger.debug("SFTP destination plugin is started")

        cn = common_name(parse_certificate(body))
        host = self.get_option("host", options)
        port = self.get_option("port", options)
        user = self.get_option("user", options)
        password = self.get_option("password", options)
        ssh_priv_key = self.get_option("privateKeyPath", options)
        ssh_priv_key_pass = self.get_option("privateKeyPass", options)
        dst_path = self.get_option("destinationPath", options)
        export_format = self.get_option("exportFormat", options)

        # prepare files for upload
        files = {cn + ".key": private_key, cn + ".pem": body}

        if cert_chain:
            if export_format == "NGINX":
                # assemble body + chain in the single file
                files[cn + ".pem"] += "\n" + cert_chain

            elif export_format == "Apache":
                # store chain in the separate file
                files[cn + ".ca.bundle.pem"] = cert_chain

        # upload files
        try:
            current_app.logger.debug("Connecting to {0}@{1}:{2}".format(
                user, host, port))
            ssh = paramiko.SSHClient()

            # allow connection to the new unknown host
            ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())

            # open the ssh connection
            if password:
                current_app.logger.debug("Using password")
                ssh.connect(host, username=user, port=port, password=password)
            elif ssh_priv_key:
                current_app.logger.debug("Using RSA private key")
                pkey = paramiko.RSAKey.from_private_key_file(
                    ssh_priv_key, ssh_priv_key_pass)
                ssh.connect(host, username=user, port=port, pkey=pkey)
            else:
                current_app.logger.error(
                    "No password or private key provided. Can't proceed")
                raise paramiko.ssh_exception.AuthenticationException

            # open the sftp session inside the ssh connection
            sftp = ssh.open_sftp()

            # make sure that the destination path exist
            try:
                current_app.logger.debug("Creating {0}".format(dst_path))
                sftp.mkdir(dst_path)
            except IOError:
                current_app.logger.debug(
                    "{0} already exist, resuming".format(dst_path))
            try:
                dst_path_cn = dst_path + "/" + cn
                current_app.logger.debug("Creating {0}".format(dst_path_cn))
                sftp.mkdir(dst_path_cn)
            except IOError:
                current_app.logger.debug(
                    "{0} already exist, resuming".format(dst_path_cn))

            # upload certificate files to the sftp destination
            for filename, data in files.items():
                current_app.logger.debug("Uploading {0} to {1}".format(
                    filename, dst_path_cn))
                with sftp.open(dst_path_cn + "/" + filename, "w") as f:
                    f.write(data)
                # read only for owner, -r--------
                sftp.chmod(dst_path_cn + "/" + filename, 0o400)

            ssh.close()

        except Exception as e:
            current_app.logger.error("ERROR in {0}: {1}".format(
                e.__class__, e))
            try:
                ssh.close()
            except BaseException:
                pass