Esempio n. 1
0
def self_enroll(skip_notify=False):
    assert os.getuid() == 0 and os.getgid(
    ) == 0, "Can self-enroll only as root"

    from certidude import const, config
    common_name = const.FQDN
    os.umask(0o0177)

    try:
        path, buf, cert, signed, expires = get_signed(common_name)
        self_public_key = asymmetric.load_public_key(path)
        private_key = asymmetric.load_private_key(config.SELF_KEY_PATH)
    except FileNotFoundError:  # certificate or private key not found
        click.echo("Generating private key for frontend: %s" %
                   config.SELF_KEY_PATH)
        with open(config.SELF_KEY_PATH, 'wb') as fh:
            if public_key.algorithm == "ec":
                self_public_key, private_key = asymmetric.generate_pair(
                    "ec", curve=public_key.curve)
            elif public_key.algorithm == "rsa":
                self_public_key, private_key = asymmetric.generate_pair(
                    "rsa", bit_size=public_key.bit_size)
            else:
                raise NotImplemented(
                    "CA certificate public key algorithm %s not supported" %
                    public_key.algorithm)
            fh.write(asymmetric.dump_private_key(private_key, None))
    else:
        now = datetime.utcnow()
        if now + timedelta(days=1) < expires:
            click.echo(
                "Certificate %s still valid, delete to self-enroll again" %
                path)
            return

    builder = CSRBuilder({"common_name": common_name}, self_public_key)
    request = builder.build(private_key)
    pid = os.fork()
    if not pid:
        from certidude import authority, config
        from certidude.common import drop_privileges
        drop_privileges()
        assert os.getuid() != 0 and os.getgid() != 0
        path = os.path.join(config.REQUESTS_DIR, common_name + ".pem")
        click.echo("Writing certificate signing request for frontend: %s" %
                   path)
        with open(path, "wb") as fh:
            fh.write(
                pem_armor_csr(request))  # Write CSR with certidude permissions
        authority.sign(common_name,
                       skip_notify=skip_notify,
                       skip_push=True,
                       overwrite=True,
                       profile=config.PROFILES["srv"])
        click.echo("Frontend certificate signed")
        sys.exit(0)
    else:
        os.waitpid(pid, 0)
        os.system("systemctl reload nginx")
Esempio n. 2
0
def self_enroll():
    assert os.getuid() == 0 and os.getgid(
    ) == 0, "Can self-enroll only as root"

    from certidude import const
    common_name = const.FQDN
    directory = os.path.join("/var/lib/certidude", const.FQDN)
    self_key_path = os.path.join(directory, "self_key.pem")

    try:
        path, buf, cert, signed, expires = get_signed(common_name)
        self_public_key = asymmetric.load_public_key(path)
        private_key = asymmetric.load_private_key(self_key_path)
    except FileNotFoundError:  # certificate or private key not found
        with open(self_key_path, 'wb') as fh:
            if public_key.algorithm == "ec":
                self_public_key, private_key = asymmetric.generate_pair(
                    "ec", curve=public_key.curve)
            elif public_key.algorithm == "rsa":
                self_public_key, private_key = asymmetric.generate_pair(
                    "rsa", bit_size=public_key.bit_size)
            else:
                NotImplemented
            fh.write(asymmetric.dump_private_key(private_key, None))
    else:
        now = datetime.utcnow()
        if now + timedelta(days=1) < expires:
            click.echo(
                "Certificate %s still valid, delete to self-enroll again" %
                path)
            return

    builder = CSRBuilder({"common_name": common_name}, self_public_key)
    request = builder.build(private_key)
    pid = os.fork()
    if not pid:
        from certidude import authority
        from certidude.common import drop_privileges
        drop_privileges()
        assert os.getuid() != 0 and os.getgid() != 0
        path = os.path.join(directory, "requests", common_name + ".pem")
        click.echo("Writing request to %s" % path)
        with open(path, "wb") as fh:
            fh.write(
                pem_armor_csr(request))  # Write CSR with certidude permissions
        authority.sign(common_name,
                       skip_push=True,
                       overwrite=True,
                       profile=config.PROFILES["srv"])
        sys.exit(0)
    else:
        os.waitpid(pid, 0)
        if os.path.exists("/etc/systemd"):
            os.system("systemctl reload nginx")
        else:
            os.system("service nginx reload")
Esempio n. 3
0
def self_enroll():
    from certidude import const
    common_name = const.FQDN
    directory = os.path.join("/var/lib/certidude", const.FQDN)
    self_key_path = os.path.join(directory, "self_key.pem")

    try:
        path, buf, cert, signed, expires = get_signed(common_name)
        public_key = asymmetric.load_public_key(path)
        private_key = asymmetric.load_private_key(self_key_path)
    except FileNotFoundError:  # certificate or private key not found
        with open(self_key_path, 'wb') as fh:
            public_key, private_key = asymmetric.generate_pair('rsa',
                                                               bit_size=2048)
            fh.write(asymmetric.dump_private_key(private_key, None))
    else:
        now = datetime.utcnow()
        if now + timedelta(days=1) < expires:
            click.echo(
                "Certificate %s still valid, delete to self-enroll again" %
                path)
            return

    builder = CSRBuilder({"common_name": common_name}, public_key)
    request = builder.build(private_key)
    with open(os.path.join(directory, "requests", common_name + ".pem"),
              "wb") as fh:
        fh.write(pem_armor_csr(request))
    pid = os.fork()
    if not pid:
        from certidude import authority
        from certidude.common import drop_privileges
        drop_privileges()
        authority.sign(common_name, skip_push=True, overwrite=True)
        sys.exit(0)
    else:
        os.waitpid(pid, 0)
        if os.path.exists("/etc/systemd"):
            os.system("systemctl reload nginx")
        else:
            os.system("service nginx reload")
Esempio n. 4
0
 def on_patch(self, req, resp, cn):
     """
     Sign a certificate signing request
     """
     csr = authority.get_request(cn)
     cert = authority.sign(csr, overwrite=True, delete=True)
     os.unlink(csr.path)
     resp.body = "Certificate successfully signed"
     resp.status = falcon.HTTP_201
     resp.location = os.path.join(req.relative_uri, "..", "..", "signed", cn)
     logger.info(u"Signing request %s signed by %s from %s", csr.common_name,
         req.context.get("user"), req.context.get("remote_addr"))
Esempio n. 5
0
    def on_post(self, req, resp, cn):
        """
        Sign a certificate signing request
        """
        cert, buf = authority.sign(cn, overwrite=True)
        # Mailing and long poll publishing implemented in the function above

        resp.body = "Certificate successfully signed"
        resp.status = falcon.HTTP_201
        resp.location = os.path.join(req.relative_uri, "..", "..", "signed", cn)
        logger.info(u"Signing request %s signed by %s from %s", cn,
            req.context.get("user"), req.context.get("remote_addr"))
Esempio n. 6
0
    def on_post(self, req, resp):
        """
        Submit certificate signing request (CSR) in PEM format
        """

        body = req.stream.read(req.content_length)

        # Normalize body, TODO: newlines
        if not body.endswith("\n"):
            body += "\n"

        csr = Request(body)

        if not csr.common_name:
            logger.warning(u"Rejected signing request without common name from %s",
                req.context.get("remote_addr"))
            raise falcon.HTTPBadRequest(
                "Bad request",
                "No common name specified!")

        machine = req.context.get("machine")
        if machine:
            if csr.common_name != machine:
                raise falcon.HTTPBadRequest(
                    "Bad request",
                    "Common name %s differs from Kerberos credential %s!" % (csr.common_name, machine))
            if csr.signable:
                # Automatic enroll with Kerberos machine cerdentials
                resp.set_header("Content-Type", "application/x-x509-user-cert")
                resp.body = authority.sign(csr, overwrite=True).dump()
                return


        # Check if this request has been already signed and return corresponding certificte if it has been signed
        try:
            cert = authority.get_signed(csr.common_name)
        except EnvironmentError:
            pass
        else:
            if cert.pubkey == csr.pubkey:
                resp.status = falcon.HTTP_SEE_OTHER
                resp.location = os.path.join(os.path.dirname(req.relative_uri), "signed", csr.common_name)
                return

        # TODO: check for revoked certificates and return HTTP 410 Gone

        # Process automatic signing if the IP address is whitelisted, autosigning was requested and certificate can be automatically signed
        if req.get_param_as_bool("autosign") and csr.signable:
            for subnet in config.AUTOSIGN_SUBNETS:
                if req.context.get("remote_addr") in subnet:
                    try:
                        resp.set_header("Content-Type", "application/x-x509-user-cert")
                        resp.body = authority.sign(csr).dump()
                        return
                    except EnvironmentError: # Certificate already exists, try to save the request
                        pass
                    break

        # Attempt to save the request otherwise
        try:
            csr = authority.store_request(body)
        except errors.RequestExists:
            # We should stil redirect client to long poll URL below
            pass
        except errors.DuplicateCommonNameError:
            # TODO: Certificate renewal
            logger.warning(u"Rejected signing request with overlapping common name from %s",
                req.context.get("remote_addr"))
            raise falcon.HTTPConflict(
                "CSR with such CN already exists",
                "Will not overwrite existing certificate signing request, explicitly delete CSR and try again")
        else:
            push.publish("request-submitted", csr.common_name)

        # Wait the certificate to be signed if waiting is requested
        if req.get_param("wait"):
            # Redirect to nginx pub/sub
            url = config.PUSH_LONG_POLL % csr.fingerprint()
            click.echo("Redirecting to: %s"  % url)
            resp.status = falcon.HTTP_SEE_OTHER
            resp.set_header("Location", url.encode("ascii"))
            logger.debug(u"Redirecting signing request from %s to %s", req.context.get("remote_addr"), url)
        else:
            # Request was accepted, but not processed
            resp.status = falcon.HTTP_202
            logger.info(u"Signing request from %s stored", req.context.get("remote_addr"))