X-Git-Url: https://pwan.org/git/?a=blobdiff_plain;f=certmaster%2Fcertmaster.py;h=9548b8b77d0071d6e3dccbf2a5e382fc026d74c6;hb=67070b83b2873cd26228d5002989cae73ead0167;hp=88ea265f02c1c7c3c60e1428ca8b3bfc96ed1fb3;hpb=6146feb4b676ba7e10f5f175bb50149c20c53b18;p=certmaster.git diff --git a/certmaster/certmaster.py b/certmaster/certmaster.py old mode 100755 new mode 100644 index 88ea265..9548b8b --- a/certmaster/certmaster.py +++ b/certmaster/certmaster.py @@ -16,7 +16,9 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. # standard modules import SimpleXMLRPCServer +import string import sys +import traceback import os import os.path from OpenSSL import crypto @@ -48,7 +50,7 @@ class CertMaster(object): self.ca_cert_file = '%s/certmaster.crt' % self.cfg.cadir self.logger = logger.Logger().logger - self.audit_logger = logger.AuditLogger().logger + self.audit_logger = logger.AuditLogger() try: if not os.path.exists(self.cfg.cadir): @@ -72,15 +74,12 @@ class CertMaster(object): self.handlers = { 'wait_for_cert': self.wait_for_cert, } + def _dispatch(self, method, params): if method == 'trait_names' or method == '_getAttributeNames': return self.handlers.keys() - -# ip = self.client_address -# print ip -# self.audit_logger.log_call(ip, method, params) if method in self.handlers.keys(): return self.handlers[method](*params) @@ -93,7 +92,7 @@ class CertMaster(object): commonname = commonname.replace('\\', '') return commonname - def wait_for_cert(self, csrbuf): + def wait_for_cert(self, csrbuf, with_triggers=True): """ takes csr as a string returns True, caller_cert, ca_cert @@ -107,7 +106,9 @@ class CertMaster(object): return False, '', '' requesting_host = self._sanitize_cn(csrreq.get_subject().CN) - + + if with_triggers: + self._run_triggers(requesting_host, '/var/lib/certmaster/triggers/request/pre/*') self.logger.info("%s requested signing of cert %s" % (requesting_host,csrreq.get_subject().CN)) # get rid of dodgy characters in the filename we're about to make @@ -117,6 +118,8 @@ class CertMaster(object): # check for old csr on disk # if we have it - compare the two - if they are not the same - raise a fault + self.logger.debug("csrfile: %s certfile: %s" % (csrfile, certfile)) + if os.path.exists(csrfile): oldfo = open(csrfile) oldcsrbuf = oldfo.read() @@ -129,7 +132,8 @@ class CertMaster(object): if not newdig == olddig: self.logger.info("A cert for %s already exists and does not match the requesting cert" % (requesting_host)) # XXX raise a proper fault - return False, '', '' + return False, '', '' + # look for a cert: # if we have it, then return True, etc, etc @@ -137,6 +141,8 @@ class CertMaster(object): slavecert = certs.retrieve_cert_from_file(certfile) cert_buf = crypto.dump_certificate(crypto.FILETYPE_PEM, slavecert) cacert_buf = crypto.dump_certificate(crypto.FILETYPE_PEM, self.cacert) + if with_triggers: + self._run_triggers(requesting_host,'/var/lib/certmaster/triggers/request/post/*') return True, cert_buf, cacert_buf # if we don't have a cert then: @@ -149,6 +155,8 @@ class CertMaster(object): cert_buf = crypto.dump_certificate(crypto.FILETYPE_PEM, cert) cacert_buf = crypto.dump_certificate(crypto.FILETYPE_PEM, self.cacert) self.logger.info("cert for %s was autosigned" % (requesting_host)) + if with_triggers: + self._run_triggers(None,'/var/lib/certmaster/triggers/request/post/*') return True, cert_buf, cacert_buf else: @@ -158,6 +166,8 @@ class CertMaster(object): destfo.close() del destfo self.logger.info("cert for %s created and ready to be signed" % (requesting_host)) + if with_triggers: + self._run_triggers(None,'/var/lib/certmaster/triggers/request/post/*') return False, '', '' return False, '', '' @@ -172,7 +182,7 @@ class CertMaster(object): hosts.append(hn) return hosts - def remove_this_cert(self, hn): + def remove_this_cert(self, hn, with_triggers=True): """ removes cert for hostname using unlink """ cm = self csrglob = '%s/%s.csr' % (cm.cfg.csrroot, hn) @@ -183,12 +193,16 @@ class CertMaster(object): # FIXME: should be an exception? print 'No match for %s to clean up' % hn return + if with_triggers: + self._run_triggers(hn,'/var/lib/certmaster/triggers/remove/pre/*') for fn in csrs + certs: print 'Cleaning out %s for host matching %s' % (fn, hn) self.logger.info('Cleaning out %s for host matching %s' % (fn, hn)) - os.unlink(fn) + os.unlink(fn) + if with_triggers: + self._run_triggers(hn,'/var/lib/certmaster/triggers/remove/post/*') - def sign_this_csr(self, csr): + def sign_this_csr(self, csr, with_triggers=True): """returns the path to the signed cert file""" csr_unlink_file = None @@ -215,24 +229,88 @@ class CertMaster(object): else: # assume we got a bare csr req csrreq = csr + + requesting_host = self._sanitize_cn(csrreq.get_subject().CN) - + if with_triggers: + self._run_triggers(requesting_host,'/var/lib/certmaster/triggers/sign/pre/*') + + certfile = '%s/%s.cert' % (self.cfg.certroot, requesting_host) + self.logger.info("Signing for csr %s requested" % certfile) thiscert = certs.create_slave_certificate(csrreq, self.cakey, self.cacert, self.cfg.cadir) + destfo = open(certfile, 'w') destfo.write(crypto.dump_certificate(crypto.FILETYPE_PEM, thiscert)) destfo.close() del destfo + + + self.logger.info("csr %s signed" % (certfile)) + if with_triggers: + self._run_triggers(requesting_host,'/var/lib/certmaster/triggers/sign/post/*') + + if csr_unlink_file and os.path.exists(csr_unlink_file): os.unlink(csr_unlink_file) return certfile + + # return a list of already signed certs + def get_signed_certs(self, hostglobs=None): + certglob = "%s/*.cert" % (self.cfg.certroot) + + certs = [] + globs = "*" + if hostglobs: + globs = hostglobs + + for hostglob in globs: + certglob = "%s/%s.cert" % (self.cfg.certroot, hostglob) + certs = certs + glob.glob(certglob) + + signed_certs = [] + for cert in certs: + # just want the hostname, so strip off path and ext + signed_certs.append(os.path.basename(cert).split(".cert", 1)[0]) + + return signed_certs + + def get_peer_certs(self): + """ + Returns a list of all certs under peerroot + """ + myglob = os.path.join(self.cfg.peerroot, '*.%s' % self.cfg.cert_extension) + return glob.glob(myglob) + + # return a list of the cert hash string we use to identify systems + def get_cert_hashes(self, hostglobs=None): + certglob = "%s/*.cert" % (self.cfg.certroot) + + certfiles = [] + globs = "*" + if hostglobs: + globs = hostglobs + + for hostglob in globs: + certglob = "%s/%s.cert" % (self.cfg.certroot, hostglob) + certfiles = certfiles + glob.glob(certglob) + cert_hashes = [] + for certfile in certfiles: + cert = certs.retrieve_cert_from_file(certfile) + cert_hashes.append("%s-%s" % (cert.get_subject().CN, cert.subject_name_hash())) + + return cert_hashes + + def _run_triggers(self, ref, globber): + return utils.run_triggers(ref, globber) + class CertmasterXMLRPCServer(SimpleXMLRPCServer.SimpleXMLRPCServer): - def __init__(self, args): + def __init__(self, addr): self.allow_reuse_address = True - SimpleXMLRPCServer.SimpleXMLRPCServer.__init__(self, args) + SimpleXMLRPCServer.SimpleXMLRPCServer.__init__(self, addr) def serve(xmlrpcinstance): @@ -241,24 +319,48 @@ def serve(xmlrpcinstance): Code for starting the XMLRPC service. """ - server = CertmasterXMLRPCServer((xmlrpcinstance.cfg.listen_addr, CERTMASTER_LISTEN_PORT)) + + config = read_config(CERTMASTER_CONFIG, CMConfig) + listen_addr = config.listen_addr + listen_port = config.listen_port + if listen_port == '': + listen_port = CERTMASTER_LISTEN_PORT + server = CertmasterXMLRPCServer((listen_addr,listen_port)) server.logRequests = 0 # don't print stuff to console server.register_instance(xmlrpcinstance) xmlrpcinstance.logger.info("certmaster started") - xmlrpcinstance.audit_logger.info("certmaster started") + xmlrpcinstance.audit_logger.logger.info("certmaster started") server.serve_forever() +def excepthook(exctype, value, tracebackobj): + exctype_blurb = "Exception occured: %s" % exctype + excvalue_blurb = "Exception value: %s" % value + exctb_blurb = "Exception Info:\n%s" % string.join(traceback.format_list(traceback.extract_tb(tracebackobj))) + + print exctype_blurb + print excvalue_blurb + print exctb_blurb + + log = logger.Logger().logger + log.info(exctype_blurb) + log.info(excvalue_blurb) + log.info(exctb_blurb) + def main(argv): - + + sys.excepthook = excepthook cm = CertMaster('/etc/certmaster/certmaster.conf') + if "--version" in sys.argv or "-v" in sys.argv: + print >> sys.stderr, file("/etc/certmaster/version").read().strip() + sys.exit(0) + if "daemon" in argv or "--daemon" in argv: utils.daemonize("/var/run/certmaster.pid") else: print "serving...\n" - # just let exceptions bubble up for now serve(cm)