(Not working yet, but the changeset was getting too big: The service starts, but...
authorJude Nagurney <jude@pwan.org>
Mon, 2 Mar 2015 02:41:08 +0000 (21:41 -0500)
committerJude Nagurney <jude@pwan.org>
Mon, 2 Mar 2015 02:41:08 +0000 (21:41 -0500)
13 files changed:
.gitignore
AUTHORS
MANIFEST.in
Makefile
certmaster/certmaster.py
certmaster/config.py
certmaster/requester.py
certmaster/utils.py
etc/certmaster.conf
etc/minion.conf
scripts/certmaster-ca
scripts/certmaster-request
scripts/certmaster-sync

index 35a6b22..442342c 100644 (file)
@@ -3,3 +3,6 @@
 *~
 \#*\#
 build/
+MANIFEST
+dist
+rpm-build
diff --git a/AUTHORS b/AUTHORS
index 5ab8e48..fe269a0 100644 (file)
--- a/AUTHORS
+++ b/AUTHORS
@@ -11,6 +11,7 @@ Additional patches and contributions by ...
    Tanabe Ken-ichi <nabeken@tknetworks.org>
    Steve Salesvan <ssalevan@redhat.com>
    Jonathan Barber <j.barber@dundee.ac.uk>
+   Jude Nagurney <jude@pwan.org>
    ... 
    
    [ send in patches to get your name here ]
index 782337c..9633326 100644 (file)
@@ -4,7 +4,7 @@ recursive-include docs *
 recursive-include init-scripts *
 recursive-include po *.po
 recursive-include po *.pot
-recursice-include triggers *
+recursive-include triggers *
 include AUTHORS
 include LICENSE
 include README
index dc2ed1b..af1432e 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,6 +1,7 @@
 VERSION                = 0.28
 RELEASE                = 1
 PYTHON         = /usr/bin/python
+DIST            = pwan
 
 MESSAGESPOT=po/messages.pot
 
@@ -92,7 +93,9 @@ async: install
 rpms: build manpage sdist
        mkdir -p rpm-build
        cp dist/*.gz rpm-build/
+       echo ${RELEASE}
        rpmbuild --define "_topdir %(pwd)/rpm-build" \
+        --define "dist ${DIST}" \
        --define "_builddir %{_topdir}" \
        --define "_rpmdir %{_topdir}" \
        --define "_srcrpmdir %{_topdir}" \
index 7b133df..3fcb78f 100644 (file)
@@ -57,34 +57,43 @@ class CertMaster(object):
 
         usename = utils.get_hostname(talk_to_certmaster=False)
 
-        mycn = '%s-CA-KEY' % usename
-        self.ca_key_file = '%s/certmaster.key' % self.cfg.cadir
-        self.ca_cert_file = '%s/certmaster.crt' % self.cfg.cadir
-
         self.logger = logger.Logger().logger
         self.audit_logger = logger.AuditLogger()
 
-        # if ca_key_file exists and ca_cert_file is missing == minion only setup
-        if os.path.exists(self.ca_key_file) and not os.path.exists(self.ca_cert_file):
-            return
+        self.cakey = {}
+        self.cacert = {}
 
-        try:
-            if not os.path.exists(self.cfg.cadir):
-                os.makedirs(self.cfg.cadir)
-            if not os.path.exists(self.ca_key_file) and not os.path.exists(self.ca_cert_file):
-                certs.create_ca(CN=mycn, ca_key_file=self.ca_key_file, ca_cert_file=self.ca_cert_file)
-        except (IOError, OSError), e:
-            print 'Cannot make certmaster certificate authority keys/certs, aborting: %s' % e
-            sys.exit(1)
+        for (s_caname,a_ca) in self.cfg.ca.iteritems():
+            s_cadir = a_ca['cadir']
 
+            if s_caname == "":
+                mycn = '%s-CA-KEY' % usename
+            else:
+                mycn = '%s-%s-CA-KEY' % (s_caname.upper(),usename)
+
+            s_ca_key_file = '%s/certmaster.key' % s_cadir
+            s_ca_cert_file = '%s/certmaster.crt' % s_cadir
 
-        # open up the cakey and cacert so we have them available
-        self.cakey = certs.retrieve_key_from_file(self.ca_key_file)
-        self.cacert = certs.retrieve_cert_from_file(self.ca_cert_file)
+            # if ca_key_file exists and ca_cert_file is missing == minion only setup
+            if os.path.exists(s_ca_key_file) and not os.path.exists(s_ca_cert_file):
+                continue
 
-        for dirpath in [self.cfg.cadir, self.cfg.certroot, self.cfg.csrroot]:
-            if not os.path.exists(dirpath):
-                os.makedirs(dirpath)
+            try:
+                if not os.path.exists(s_cadir):
+                    os.makedirs(s_cadir)
+                if not os.path.exists(s_ca_key_file) and not os.path.exists(s_ca_cert_file):
+                    certs.create_ca(CN=mycn, ca_key_file=s_ca_key_file, ca_cert_file=s_ca_cert_file)
+            except (IOError, OSError), e:
+                print 'Cannot make certmaster certificate authority keys/certs for CA %s, aborting: %s' % (s_caname, e)
+                sys.exit(1)
+
+            # open up the cakey and cacert so we have them available
+            self.cakey[s_caname] = certs.retrieve_key_from_file(s_ca_key_file)
+            self.cacert[s_caname] = certs.retrieve_cert_from_file(s_ca_cert_file)
+
+            for dirpath in [a_ca['cadir'], a_ca['certroot'], a_ca['csrroot'], a_ca['csrroot']]:
+                if not os.path.exists(dirpath):
+                    os.makedirs(dirpath)
 
         # setup handlers
         self.handlers = {
@@ -108,7 +117,7 @@ class CertMaster(object):
         commonname = commonname.replace('\\', '')
         return commonname
 
-    def wait_for_cert(self, csrbuf, with_triggers=True):
+    def wait_for_cert(self, csrbuf, ca='', with_triggers=True):
         """
            takes csr as a string
            returns True, caller_cert, ca_cert
@@ -129,8 +138,8 @@ class CertMaster(object):
         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
 
-        certfile = '%s/%s.cert' % (self.cfg.certroot, requesting_host)
-        csrfile = '%s/%s.csr' % (self.cfg.csrroot, requesting_host)
+        certfile = '%s/%s.cert' % (self.cfg.ca[ca]['certroot'], requesting_host)
+        csrfile = '%s/%s.csr' % (self.cfg.ca[ca]['csrroot'], requesting_host)
 
         # check for old csr on disk
         # if we have it - compare the two - if they are not the same - raise a fault
@@ -165,12 +174,12 @@ class CertMaster(object):
         # if we're autosign then sign it, write out the cert and return True, etc, etc
         # else write out the csr
 
-        if self.cfg.autosign:
+        if self.cfg.ca[ca]['autosign']:
             cert_fn = self.sign_this_csr(csrreq)
             cert = certs.retrieve_cert_from_file(cert_fn)
             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))
+            self.logger.info("cert for %s for ca %s was autosigned" % (requesting_host,ca))
             if with_triggers:
                 self._run_triggers(None,'/var/lib/certmaster/triggers/request/post/*')
             return True, cert_buf, cacert_buf
@@ -181,16 +190,16 @@ class CertMaster(object):
             destfo.write(crypto.dump_certificate_request(crypto.FILETYPE_PEM, csrreq))
             destfo.close()
             del destfo
-            self.logger.info("cert for %s created and ready to be signed" % (requesting_host))
+            self.logger.info("cert for %s for CA %s created and ready to be signed" % (requesting_host, ca))
             if with_triggers:
                 self._run_triggers(None,'/var/lib/certmaster/triggers/request/post/*')
             return False, '', ''
 
         return False, '', ''
 
-    def get_csrs_waiting(self):
+    def get_csrs_waiting(self, ca=''):
         hosts = []
-        csrglob = '%s/*.csr' % self.cfg.csrroot
+        csrglob = '%s/*.csr' % self.cfg.ca[ca]['csrroot']
         csr_list = glob.glob(csrglob)
         for f in csr_list:
             hn = os.path.basename(f)
@@ -198,12 +207,12 @@ class CertMaster(object):
             hosts.append(hn)
         return hosts
 
-    def remove_this_cert(self, hn, with_triggers=True):
+    def remove_this_cert(self, hn, with_triggers=True, ca=''):
         """ removes cert for hostname using unlink """
         cm = self
-        csrglob = '%s/%s.csr' % (cm.cfg.csrroot, hn)
+        csrglob = '%s/%s.csr' % (cm.cfg.ca[ca]['csrroot'], hn)
         csrs = glob.glob(csrglob)
-        certglob = '%s/%s.cert' % (cm.cfg.certroot, hn)
+        certglob = '%s/%s.cert' % (cm.cfg.ca[ca]['certroot'], hn)
         certs = glob.glob(certglob)
         if not csrs and not certs:
             # FIXME: should be an exception?
@@ -218,7 +227,7 @@ class CertMaster(object):
         if with_triggers:
             self._run_triggers(hn,'/var/lib/certmaster/triggers/remove/post/*')
 
-    def sign_this_csr(self, csr, with_triggers=True):
+    def sign_this_csr(self, csr, with_triggers=True,ca=''):
         """returns the path to the signed cert file"""
         csr_unlink_file = None
 
@@ -228,10 +237,10 @@ class CertMaster(object):
                 csr_buf = csrfo.read()
                 csr_unlink_file = csr
 
-            elif os.path.exists('%s/%s' % (self.cfg.csrroot, csr)): # we have a partial path?
-                csrfo = open('%s/%s' % (self.cfg.csrroot, csr))
+            elif os.path.exists('%s/%s' % (self.cfg.ca[ca]['csrroot'], csr)): # we have a partial path?
+                csrfo = open('%s/%s' % (self.cfg.ca[ca]['csrroot'], csr))
                 csr_buf = csrfo.read()
-                csr_unlink_file = '%s/%s' % (self.cfg.csrroot, csr)
+                csr_unlink_file = '%s/%s' % (self.cfg.ca[ca]['csrroot'], csr)
 
             # we have a string of some kind
             else:
@@ -252,9 +261,9 @@ class CertMaster(object):
             self._run_triggers(requesting_host,'/var/lib/certmaster/triggers/sign/pre/*')
 
 
-        certfile = '%s/%s.cert' % (self.cfg.certroot, requesting_host)
+        certfile = '%s/%s.cert' % (self.cfg.ca[ca]['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)
+        thiscert = certs.create_slave_certificate(csrreq, self.cakey, self.cacert, self.cfg.ca[ca]['cadir'])
 
         destfo = open(certfile, 'w')
         destfo.write(crypto.dump_certificate(crypto.FILETYPE_PEM, thiscert))
@@ -273,8 +282,8 @@ class CertMaster(object):
         return certfile
 
     # return a list of already signed certs
-    def get_signed_certs(self, hostglobs=None):
-        certglob = "%s/*.cert" % (self.cfg.certroot)
+    def get_signed_certs(self, hostglobs=None, ca=''):
+        certglob = "%s/*.cert" % (self.cfg.ca[ca]['certroot'])
 
         certs = []
         globs = "*"
@@ -282,7 +291,7 @@ class CertMaster(object):
             globs = hostglobs
 
         for hostglob in globs:
-            certglob = "%s/%s.cert" % (self.cfg.certroot, hostglob)
+            certglob = "%s/%s.cert" % (self.cfg.ca[ca]['certroot'], hostglob)
             certs = certs + glob.glob(certglob)
 
         signed_certs = []
@@ -300,8 +309,8 @@ class CertMaster(object):
         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)
+    def get_cert_hashes(self, hostglobs=None,ca=''):
+        certglob = "%s/*.cert" % (self.cfg.ca[ca]['certroot'])
 
         certfiles = []
         globs = "*"
@@ -309,7 +318,7 @@ class CertMaster(object):
             globs = hostglobs
 
         for hostglob in globs:
-            certglob = "%s/%s.cert" % (self.cfg.certroot, hostglob)
+            certglob = "%s/%s.cert" % (self.cfg.ca[ca]['certroot'], hostglob)
             certfiles = certfiles + glob.glob(certglob)
 
         cert_hashes = []
index 4cf2a7f..cac9394 100644 (file)
@@ -60,7 +60,7 @@ class Option(object):
         @param obj: The configuration instance to modify.
         @param objtype: The type of the config instance (not used).
         @return: The parsed option value or the default value if the value
-            wasn't set in the configuration file.
+            was not set in the configuration file.
         '''
         if obj is None:
             return self
@@ -475,4 +475,26 @@ def read_config(config_file, BaseConfigDerived):
             print >> sys.stderr, "Error reading config file: %s" % e
             sys.exit(1)
     opts.populate(confparser, 'main')
+
+    ## build up the cas structure
+    opts.ca = {}
+    opts.ca[''] = {}
+
+    ## Add the default items when just using a single ca
+    main_items = confparser.items('main')
+    for (key,value) in main_items:
+        if key in ['autosign','cadir','cert_dir','certroot','csrroot']:
+            opts.ca[''][key] = value
+
+    ## Add additonal ca sections
+    sections = confparser.sections()
+    for a_section in sections:
+        if a_section.startswith('ca:'):
+            ca_name = a_section[3:]
+            items = confparser.items(a_section)
+            opts.ca[ca_name] = {}
+            for (key,value) in items:
+                opts.ca[ca_name][key] = value
+                print 'opts.ca: %s %s %s' % (ca_name, key, value)
+    
     return opts
index 1fd6826..9a0e94f 100644 (file)
@@ -15,8 +15,8 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 
 import utils
 
-def request_cert(hostname=None):
+def request_cert(hostname=None, ca=''):
     # this should be enough, but do we want to allow parameters
     # for overriding the server and port from the config file?
     # maybe not. -- mpd
-    utils.create_minion_keys(hostname)
+    utils.create_minion_keys(hostname,ca)
index b135e7d..d160982 100644 (file)
@@ -122,13 +122,15 @@ def get_hostname(talk_to_certmaster=True):
 # FIXME: move to requestor module and also create a verbose mode
 # prints to the screen for usage by /usr/bin/certmaster-request
 
-def create_minion_keys(hostname=None):
+def create_minion_keys(hostname=None, ca=''):
     log = logger.Logger().logger
 
     # FIXME: paths should not be hard coded here, move to settings universally
     config_file = '/etc/certmaster/minion.conf'
     config = read_config(config_file, MinionConfig)
-    cert_dir = config.cert_dir
+
+    cert_dir = config.ca[ca]['cert_dir']
+        
     master_uri = 'http://%s:%s/' % (config.certmaster, config.certmaster_port)
 
     hn = hostname
@@ -146,7 +148,6 @@ def create_minion_keys(hostname=None):
     cert_file = '%s/%s.cert' % (cert_dir, hn)
     ca_cert_file = '%s/ca.cert' % cert_dir
 
-
     if os.path.exists(cert_file) and os.path.exists(ca_cert_file):
         # print "DEBUG: err, no cert_file"
         return
@@ -171,7 +172,7 @@ def create_minion_keys(hostname=None):
         try:
             # print "DEBUG: submitting CSR to certmaster: %s" % master_uri
             log.debug("submitting CSR: %s  to certmaster %s" % (csr_file, master_uri))
-            result, cert_string, ca_cert_string = submit_csr_to_master(csr_file, master_uri)
+            result, cert_string, ca_cert_string = submit_csr_to_master(csr_file, master_uri, ca)
         except socket.error, e:
             log.warning("Could not locate certmaster at %s" % master_uri)
 
@@ -231,7 +232,7 @@ def run_triggers(ref, globber):
             raise codes.CMException, "certmaster trigger failed: %(file)s returns %(code)d" % { "file" : file, "code" : rc }
 
 
-def submit_csr_to_master(csr_file, master_uri):
+def submit_csr_to_master(csr_file, master_uri, ca=''):
     """"
     gets us our cert back from the certmaster.wait_for_cert() method
     takes csr_file as path location and master_uri
@@ -243,4 +244,4 @@ def submit_csr_to_master(csr_file, master_uri):
     s = xmlrpclib.ServerProxy(master_uri)
 
     # print "DEBUG: waiting for cert"
-    return s.wait_for_cert(csr)
+    return s.wait_for_cert(csr,ca)
index cfdca9d..e8d6c67 100644 (file)
@@ -1,12 +1,30 @@
 # configuration for certmasterd and certmaster-ca
 
 [main]
-autosign = no
 listen_addr = 
 listen_port = 51235
+cert_extension = cert
+sync_certs = False
+
+# Use thse settings if no --ca flag provided
+autosign = no
 cadir = /etc/pki/certmaster/ca
 cert_dir = /etc/pki/certmaster
 certroot = /var/lib/certmaster/certmaster/certs
 csrroot = /var/lib/certmaster/certmaster/csrs
-cert_extension = cert
-sync_certs = False
+
+# use these directories if '--ca=ldap' provided in the certmaster-ca commands
+# [ca:ldap]
+# autosign = yes
+# cadir = /etc/pki/certmaster/ldap-ca
+# cert_dir = /etc/pki/certmaster/ldap
+# certroot = /var/lib/certmaster/ldap/certs
+# csrroot = /var/lib/certmaster/ldap/csrs
+
+# use these directories if '--ca=yourapp' provided in the certmaster-ca commands
+# [ca:yourapp]
+# autosign = yes
+# cadir = /etc/pki/certmaster/yourapp-ca
+# cert_dir = /etc/pki/certmaster/yourapp
+# certroot = /var/lib/certmaster/yourapp/certs
+# csrroot = /var/lib/certmaster/yourapp/csrs
index 47c6540..7a25ce2 100644 (file)
@@ -6,3 +6,6 @@ certmaster_port = 51235
 log_level = DEBUG
 cert_dir = /etc/pki/certmaster
 
+# [ca:ldap]
+# cert_dir = /etc/pki/certmaster-ldap
+
index d10c1a2..0e43253 100755 (executable)
@@ -1,8 +1,9 @@
 #!/usr/bin/python -tt
 # sign/list keys
+# --ca ca sign/list certs for the 'ca' 
 # --sign hostname hostname hostname
 # --list # lists all csrs needing to be signed
-# --list-all ?
+# --list-all ca list all certs for a given ca
 # --clean? not sure what it will do
 
 import sys
@@ -27,7 +28,9 @@ class CertmasterCAOptionParser(optparse.OptionParser):
 def parseargs(args):
     usage = 'certmaster-ca <option> [args]'
     parser = CertmasterCAOptionParser(usage=usage,version=True)
-    
+
+    parser.add_option("", '--ca', default='', action="store", dest="ca", metavar="CA",
+          help="certificate authority used to sign/list certs")
     parser.add_option('-l', '--list', default=False, action="store_true",
           help='list signing requests remaining')
     parser.add_option('-s', '--sign', default=False, action="store_true",
@@ -61,7 +64,7 @@ def main(args):
 
         
     if opts.list:
-        hns = cm.get_csrs_waiting()
+        hns = cm.get_csrs_waiting(opts.ca)
         if hns:
             for hn in sorted(hns):
                 print hn
@@ -76,14 +79,14 @@ def main(args):
             return 1
             
         for hn in args:
-            csrglob = '%s/%s.csr' % (cm.cfg.csrroot, hn)
+            csrglob = '%s/%s.csr' % (cm.cfg.cas[opts.ca]['csrroot'], hn)
             csrs = glob.glob(csrglob)
             if not csrs:
                 errorprint('No match for %s to sign' % hn)
                 return 1
             
             for fn in csrs:
-                certfile = cm.sign_this_csr(fn)
+                certfile = cm.sign_this_csr(opts.ca,fn)
                 print '%s signed - cert located at %s' % (fn, certfile)
         return 0
     
@@ -93,7 +96,7 @@ def main(args):
             return 1
         
         for hn in args:
-            cm.remove_this_cert(hn)
+            cm.remove_this_cert(opts.ca,hn)
         
         return 0
 
@@ -102,7 +105,7 @@ def main(args):
         if args:
             hostglobs = args
 
-        signed_certs = cm.get_signed_certs(args)
+        signed_certs = cm.get_signed_certs(opts.ca, args)
 
         for i in sorted(signed_certs):
             print i
@@ -114,7 +117,7 @@ def main(args):
         if args:
             hostglobs = args
             
-        cert_hashes = cm.get_cert_hashes(hostglobs)
+        cert_hashes = cm.get_cert_hashes(opts.ca, hostglobs)
 
         for i in sorted(cert_hashes):
             print i
index 1d7df8c..4c14443 100755 (executable)
@@ -27,6 +27,10 @@ if __name__ == "__main__":
         metavar="NAME", 
         help='hostname to use as the CN for the certificate')
     
+    parser.add_option('--ca', action="store", dest="ca", default='',
+        metavar="CA", 
+        help='certificate authority used to sign the certificate')
+    
     (opts, args) = parser.parse_args()
 
-    requester.request_cert(hostname=opts.hostname)
+    requester.request_cert(hostname=opts.hostname, ca=opts.ca)
index fd1db93..4d9559f 100644 (file)
@@ -79,8 +79,9 @@ def local_certs():
     results = []
     for f in files:
         hostname = os.path.basename(f).replace('.' + cm.cfg.cert_extension, '')
+        dirname = os.path.dirname(f)
         digest = checksum(f)
-        results.append([hostname, digest])
+        results.append([hostname, digest, dirname])
     return results
 
 def checksum(f):
@@ -119,7 +120,7 @@ def copy_updated_certs(local, remote):
         for cert in local:
             if cert not in peers:
                 cert_name = '%s.%s' % (cert[0], cm.cfg.cert_extension)
-                full_path = os.path.join(cm.cfg.certroot, cert_name)
+                full_path = os.path.join(cert[2], cert_name)
                 fd = open(full_path)
                 certblob = fd.read()
                 fd.close()