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")
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")
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")
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"))
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"))
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"))