BATS fell down pushing a process into the background, so I switched to shunit2 /...
authorJude N <jude@pwan.org>
Sun, 22 Mar 2015 15:47:07 +0000 (11:47 -0400)
committerJude N <jude@pwan.org>
Sun, 22 Mar 2015 15:47:07 +0000 (11:47 -0400)
certmaster/certmaster.py
certmaster/config.py
certmaster/requester.py
certmaster/utils.py
scripts/certmaster-ca
scripts/certmaster-request
tests/certmaster.bats [deleted file]
tests/test-certmaster.sh [new file with mode: 0755]

index 71db996..36f2bc4 100644 (file)
@@ -64,7 +64,7 @@ class CertMaster(object):
         self.cacert = {}
 
         for (s_caname,a_ca) in self.cfg.ca.iteritems():
-            s_cadir = a_ca['cadir']
+            s_cadir = a_ca.cadir
 
             if s_caname == "":
                 mycn = '%s-CA-KEY' % usename
@@ -88,10 +88,10 @@ class CertMaster(object):
                 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)
+            a_ca.cakey = certs.retrieve_key_from_file(s_ca_key_file)
+            a_ca.cacert = certs.retrieve_cert_from_file(s_ca_cert_file)
 
-            for dirpath in [a_ca['cadir'], a_ca['certroot'], a_ca['csrroot'], a_ca['csrroot']]:
+            for dirpath in [a_ca.cadir, a_ca.certroot, a_ca.csrroot, a_ca.csrroot]:
                 if not os.path.exists(dirpath):
                     os.makedirs(dirpath)
 
@@ -117,13 +117,18 @@ class CertMaster(object):
         commonname = commonname.replace('\\', '')
         return commonname
 
-    def wait_for_cert(self, csrbuf, ca='', with_triggers=True):
+    def wait_for_cert(self, csrbuf, ca_name, with_triggers=True):
         """
            takes csr as a string
            returns True, caller_cert, ca_cert
            returns False, '', ''
         """
 
+        try:
+            certauth = self.cfg.ca[ca_name]
+        except:
+            raise codes.CMException("Unknown cert authority: %s" % ca_name)
+
         try:
             csrreq = crypto.load_certificate_request(crypto.FILETYPE_PEM, csrbuf)
         except crypto.Error, e:
@@ -138,8 +143,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.ca[ca]['certroot'], requesting_host)
-        csrfile = '%s/%s.csr' % (self.cfg.ca[ca]['csrroot'], requesting_host)
+        certfile = '%s/%s.cert' % (certauth.certroot, requesting_host)
+        csrfile = '%s/%s.csr' % (certauth.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,7 +170,7 @@ class CertMaster(object):
         if os.path.exists(certfile):
             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[ca])
+            cacert_buf = crypto.dump_certificate(crypto.FILETYPE_PEM, certauth.cacert)
             if with_triggers:
                 self._run_triggers(requesting_host,'/var/lib/certmaster/triggers/request/post/*')
             return True, cert_buf, cacert_buf
@@ -174,12 +179,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.ca[ca]['autosign']:
-            cert_fn = self.sign_this_csr(csrreq,ca=ca)
+        if certauth.autosign:
+            cert_fn = self.sign_this_csr(csrreq,certauth)
             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[ca])
-            self.logger.info("cert for %s for ca %s was autosigned" % (requesting_host,ca))
+            cacert_buf = crypto.dump_certificate(crypto.FILETYPE_PEM, certauth.cacert)
+            self.logger.info("cert for %s for ca %s was autosigned" % (requesting_host,ca_name))
             if with_triggers:
                 self._run_triggers(None,'/var/lib/certmaster/triggers/request/post/*')
             return True, cert_buf, cacert_buf
@@ -190,16 +195,16 @@ class CertMaster(object):
             destfo.write(crypto.dump_certificate_request(crypto.FILETYPE_PEM, csrreq))
             destfo.close()
             del destfo
-            self.logger.info("cert for %s for CA %s created and ready to be signed" % (requesting_host, ca))
+            self.logger.info("cert for %s for CA %s created and ready to be signed" % (requesting_host, ca_name))
             if with_triggers:
                 self._run_triggers(None,'/var/lib/certmaster/triggers/request/post/*')
             return False, '', ''
 
         return False, '', ''
 
-    def get_csrs_waiting(self, ca=''):
+    def get_csrs_waiting(self, certauth):
         hosts = []
-        csrglob = '%s/*.csr' % self.cfg.ca[ca]['csrroot']
+        csrglob = '%s/*.csr' % certauth.csrroot
         csr_list = glob.glob(csrglob)
         for f in csr_list:
             hn = os.path.basename(f)
@@ -207,12 +212,12 @@ class CertMaster(object):
             hosts.append(hn)
         return hosts
 
-    def remove_this_cert(self, hn, with_triggers=True, ca=''):
+    def remove_this_cert(self, hn, certauth, with_triggers=True):
         """ removes cert for hostname using unlink """
         cm = self
-        csrglob = '%s/%s.csr' % (cm.cfg.ca[ca]['csrroot'], hn)
+        csrglob = '%s/%s.csr' % (certauth.csrroot, hn)
         csrs = glob.glob(csrglob)
-        certglob = '%s/%s.cert' % (cm.cfg.ca[ca]['certroot'], hn)
+        certglob = '%s/%s.cert' % (certauth.certroot, hn)
         certs = glob.glob(certglob)
         if not csrs and not certs:
             # FIXME: should be an exception?
@@ -227,7 +232,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, ca=''):
+    def sign_this_csr(self, csr, certauth,with_triggers=True):
         """returns the path to the signed cert file"""
         csr_unlink_file = None
 
@@ -237,10 +242,10 @@ class CertMaster(object):
                 csr_buf = csrfo.read()
                 csr_unlink_file = 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))
+            elif os.path.exists('%s/%s' % (certauth.csrroot, csr)): # we have a partial path?
+                csrfo = open('%s/%s' % (certauth.csrroot, csr))
                 csr_buf = csrfo.read()
-                csr_unlink_file = '%s/%s' % (self.cfg.ca[ca]['csrroot'], csr)
+                csr_unlink_file = '%s/%s' % (certauth.csrroot, csr)
 
             # we have a string of some kind
             else:
@@ -261,9 +266,9 @@ class CertMaster(object):
             self._run_triggers(requesting_host,'/var/lib/certmaster/triggers/sign/pre/*')
 
 
-        certfile = '%s/%s.cert' % (self.cfg.ca[ca]['certroot'], requesting_host)
+        certfile = '%s/%s.cert' % (certauth.certroot, requesting_host)
         self.logger.info("Signing for csr %s requested" % certfile)
-        thiscert = certs.create_slave_certificate(csrreq, self.cakey[ca], self.cacert[ca], self.cfg.ca[ca]['cadir'])
+        thiscert = certs.create_slave_certificate(csrreq, certauth.cakey, certauth.cacert, certauth.cadir)
 
         destfo = open(certfile, 'w')
         destfo.write(crypto.dump_certificate(crypto.FILETYPE_PEM, thiscert))
@@ -282,8 +287,8 @@ class CertMaster(object):
         return certfile
 
     # return a list of already signed certs
-    def get_signed_certs(self, hostglobs=None, ca=''):
-        certglob = "%s/*.cert" % (self.cfg.ca[ca]['certroot'])
+    def get_signed_certs(self, certauth,hostglobs=None):
+        certglob = "%s/*.cert" % (certauth.certroot)
 
         certs = []
         globs = "*"
@@ -291,7 +296,7 @@ class CertMaster(object):
             globs = hostglobs
 
         for hostglob in globs:
-            certglob = "%s/%s.cert" % (self.cfg.ca[ca]['certroot'], hostglob)
+            certglob = "%s/%s.cert" % (certauth.certroot, hostglob)
             certs = certs + glob.glob(certglob)
 
         signed_certs = []
@@ -309,8 +314,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,ca=''):
-        certglob = "%s/*.cert" % (self.cfg.ca[ca]['certroot'])
+    def get_cert_hashes(self, certauth, hostglobs=None):
+        certglob = "%s/*.cert" % (certauth.certroot)
 
         certfiles = []
         globs = "*"
@@ -318,7 +323,7 @@ class CertMaster(object):
             globs = hostglobs
 
         for hostglob in globs:
-            certglob = "%s/%s.cert" % (self.cfg.ca[ca]['certroot'], hostglob)
+            certglob = "%s/%s.cert" % (certauth.certroot, hostglob)
             certfiles = certfiles + glob.glob(certglob)
 
         cert_hashes = []
index 205af35..7f7e623 100644 (file)
@@ -478,22 +478,29 @@ def read_config(config_file, BaseConfigDerived):
 
     ## build up the cas structure
     opts.ca = {}
-    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
+#    main_items = confparser.items('main')
+#    for (key,value) in main_items:
+#        if key in ['autosign','cadir','cert_dir','certroot','csrroot']:
+#            print "main ca: key: %s, value: %s" % (key,value)
+#            opts.ca[''][key] = value
+    opts.ca[''] = BaseConfigDerived()
+    opts.ca[''].populate(confparser,'main')
 
     ## 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
+#            items = confparser.items(a_section)
+#            opts.ca[ca_name] = {}
+#            for (key,value) in items:
+#                opts.ca[ca_name][key] = value
+            opts.ca[ca_name] = BaseConfigDerived()
+            opts.ca[ca_name].populate(confparser,a_section)
+            opts.ca[ca_name].cakey = None
+            opts.ca[ca_name].cacert = None
     
     return opts
index 9a0e94f..8a857ad 100644 (file)
@@ -15,8 +15,8 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 
 import utils
 
-def request_cert(hostname=None, ca=''):
+def request_cert(hostname=None, ca_name=''):
     # 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,ca)
+    utils.create_minion_keys(hostname,ca_name)
index d160982..e348ec4 100644 (file)
@@ -28,7 +28,6 @@ import sub_process
 
 # FIXME: module needs better pydoc
 
-
 # FIXME: can remove this constant?
 REMOTE_ERROR = "REMOTE_ERROR"
 
@@ -38,9 +37,6 @@ if (hasattr(os, "devnull")):
 else:
     REDIRECT_TO = "/dev/null"
 
-
-
-
 def trace_me():
     x = traceback.extract_stack()
     bar = string.join(traceback.format_list(x))
@@ -122,14 +118,19 @@ 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, ca=''):
+def create_minion_keys(hostname=None, ca_name=''):
     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.ca[ca]['cert_dir']
+    try:
+        certauth=config.ca[ca_name]
+    except:
+        raise codes.CMException("Unknown cert authority: %s" % ca_name)
+
+    cert_dir = certauth.cert_dir
         
     master_uri = 'http://%s:%s/' % (config.certmaster, config.certmaster_port)
 
@@ -172,7 +173,7 @@ def create_minion_keys(hostname=None, ca=''):
         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, ca)
+            result, cert_string, ca_cert_string = submit_csr_to_master(csr_file, master_uri, ca_name)
         except socket.error, e:
             log.warning("Could not locate certmaster at %s" % master_uri)
 
@@ -232,7 +233,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, ca=''):
+def submit_csr_to_master(csr_file, master_uri, ca_name=''):
     """"
     gets us our cert back from the certmaster.wait_for_cert() method
     takes csr_file as path location and master_uri
@@ -244,4 +245,4 @@ def submit_csr_to_master(csr_file, master_uri, ca=''):
     s = xmlrpclib.ServerProxy(master_uri)
 
     # print "DEBUG: waiting for cert"
-    return s.wait_for_cert(csr,ca)
+    return s.wait_for_cert(csr,ca_name)
index 7f8f967..75bfad3 100755 (executable)
@@ -15,15 +15,12 @@ import certmaster
 import certmaster.certs
 import certmaster.certmaster
 
-
-
-
 def errorprint(stuff):
     print >> sys.stderr, stuff
 
 class CertmasterCAOptionParser(optparse.OptionParser):
     def get_version(self):
-        return file("/etc/func/version").read().strip()
+        return file("/etc/certmaster/version").read().strip()
 
 def parseargs(args):
     usage = 'certmaster-ca <option> [args]'
@@ -62,9 +59,15 @@ def main(args):
     
     (opts, args) = parseargs(args)
 
+    ## Check that the ca option matches a configured ca
+    try:
+        certauth = cm.cfg.ca[opts.ca]
+    except:
+        errorprint("Unknown ca %s: check /etc/certmaster.cfg" % opts.ca)
+        return 1
         
     if opts.list:
-        hns = cm.get_csrs_waiting(ca=opts.ca)
+        hns = cm.get_csrs_waiting(certauth)
         if hns:
             for hn in sorted(hns):
                 print hn
@@ -79,14 +82,14 @@ def main(args):
             return 1
             
         for hn in args:
-            csrglob = '%s/%s.csr' % (cm.cfg.cas[opts.ca]['csrroot'], hn)
+            csrglob = '%s/%s.csr' % (certauth.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, ca=opts.ca)
+                certfile = cm.sign_this_csr(fn, certauth)
                 print '%s signed - cert located at %s' % (fn, certfile)
         return 0
     
@@ -96,7 +99,7 @@ def main(args):
             return 1
         
         for hn in args:
-            cm.remove_this_cert(hn, ca=opts.ca)
+            cm.remove_this_cert(hn, certauth)
         
         return 0
 
@@ -105,7 +108,7 @@ def main(args):
         if args:
             hostglobs = args
 
-        signed_certs = cm.get_signed_certs(args, ca=opts.ca)
+        signed_certs = cm.get_signed_certs(certauth, args)
 
         for i in sorted(signed_certs):
             print i
@@ -117,7 +120,7 @@ def main(args):
         if args:
             hostglobs = args
             
-        cert_hashes = cm.get_cert_hashes(hostglobs, ca=opts.ca)
+        cert_hashes = cm.get_cert_hashes(certauth, hostglobs)
 
         for i in sorted(cert_hashes):
             print i
index 4c14443..6b3414f 100755 (executable)
@@ -33,4 +33,4 @@ if __name__ == "__main__":
     
     (opts, args) = parser.parse_args()
 
-    requester.request_cert(hostname=opts.hostname, ca=opts.ca)
+    requester.request_cert(hostname=opts.hostname, ca_name=opts.ca)
diff --git a/tests/certmaster.bats b/tests/certmaster.bats
deleted file mode 100644 (file)
index f10672c..0000000
+++ /dev/null
@@ -1,113 +0,0 @@
-#!/usr/bin/env bats
-
-setup() {
-    /etc/init.d/certmaster stop || true
-    mkdir -p /etc/certmaster
-    cp certmaster.conf.tst /etc/certmaster/certmaster.conf
-    cp minion.conf.tst /etc/certmaster/minion.conf
-    rm -rf /var/lib/certmaster/certmaster
-    rm -rf /var/lib/certmaster/test
-    rm -rf /etc/pki/certmaster
-    rm -rf /etc/pki/certmaster-test
-    /etc/init.d/certmaster start
-}
-
-teardown() {
-    /etc/init.d/certmaster stop
-}
-
-@test "check that certmaster-ca is availabe" {
-    command -v certmaster-ca
-}
-
-@test "check that certmaster-request is available" {
-    command -v certmaster-request
-}
-
-@test "check that the certmaster daemon is running" {
-    /etc/init.d/certmaster status
-}
-
-@test "check certmaster-request --help" {
-    run certmaster-request --help
-
-    expected=$(cat <<EOF
-Usage: certmaster-request [options]
-
-Options:
-  -h, --help       show this help message and exit
-  --hostname=NAME  hostname to use as the CN for the certificate
-  --ca=CA          certificate authority used to sign the certificate
-EOF
-)
-    [ "$output" = "$expected" ]
-
-}
-
-@test "check certmaster-request -h" {
-    run certmaster-request -h
-
-    expected=$(cat <<EOF
-Usage: certmaster-request [options]
-
-Options:
-  -h, --help       show this help message and exit
-  --hostname=NAME  hostname to use as the CN for the certificate
-  --ca=CA          certificate authority used to sign the certificate
-EOF
-)
-    [ "$output" = "$expected" ]
-
-}
-
-@test "check certmaster-request --blah" {
-
-    run certmaster-request --blah
-
-    expected=$(cat << EOF
-Usage: certmaster-request [options]
-
-certmaster-request: error: no such option: --blah
-EOF
-)
-
-}
-
-@test "signing a cert with the autosigning test ca" {
-    run certmaster-request --hostname testcert.pwan.co --ca test
-
-    stat /etc/pki/certmaster-test
-    stat /etc/pki/certmaster-test/testcert.pwan.co.cert
-    stat /etc/pki/certmaster-test/testcert.pwan.co.pem
-    stat /etc/pki/certmaster-test/testcert.pwan.co.csr
-
-    subject=`openssl x509 -in /etc/pki/certmaster-test/testcert.pwan.co.cert -subject -noout`
-    [[ $subject == *"CN=testcert.pwan.co"*  ]]
-
-    openssl rsa -in /etc/pki/certmaster-test/testcert.pwan.co.pem -check
-    openssl req -text -noout -verify -in /etc/pki/certmaster-test/testcert.pwan.co.csr
-}
-
-@test "signing a cert with the non-autosigning default ca" {
-
-    setsid certmaster-request --hostname defaultcert.pwan.co
-
-    echo "hello" > blah.txt
-    output=`certmaster-ca --list`
-    echo "$output" >> blah.txt
-    [[ $output == *"defaultcert.pwan.co"* ]]
-
-    run certmaster-ca --sign defaultcert.pwan.co
-
-    stat /etc/pki/certmaster
-    stat /etc/pki/certmaster/defaultcert,pwan.co.cert
-    stat /etc/pki/certmaster/defaultcert,pwan.co.pem
-    stat /etc/pki/certmaster/defaultcert,pwan.co.csr
-
-    subject=`openssl x509 -in /etc/pki/certmaster/defaultcert.pwan.co.cert -subject -noout`
-    [[ $subject == *"CN=defaultcert.pwan.co"*  ]]
-
-    openssl rsa -in /etc/pki/certmaster/defaultcert.pwan.co.pem -check
-    openssl req -text -noout -verify -in /etc/pki/certmaster/defaultcert.pwan.co.csr
-
-}
diff --git a/tests/test-certmaster.sh b/tests/test-certmaster.sh
new file mode 100755 (executable)
index 0000000..eab2d9a
--- /dev/null
@@ -0,0 +1,240 @@
+#!/bin/bash
+# shunit2 tests for certmaster
+# (sorry bats, but I couldn't figure out how to push a command into the background with ya)
+
+setUp() 
+{
+    /etc/init.d/certmaster stop >& /dev/null || true
+    mkdir -p /etc/certmaster
+    cp certmaster.conf.tst /etc/certmaster/certmaster.conf
+    cp minion.conf.tst /etc/certmaster/minion.conf
+    rm -rf /var/lib/certmaster
+    rm -rf /var/lib/certmaster/test
+    rm -rf /etc/pki/certmaster
+    rm -rf /etc/pki/certmaster-test
+    /etc/init.d/certmaster start  >& /dev/null
+}
+
+tearDown() {
+    /etc/init.d/certmaster stop >& /dev/null
+}
+
+test_CertmasterCaAvailable()
+{
+    [[ -x "/usr/bin/certmaster-ca" ]]
+    assertTrue "certmaster-ca exists" $?
+}
+
+test_CertmasterRequestAvailable()
+{
+    [[ -x "/usr/bin/certmaster-request" ]]
+    assertTrue "certmaster-request exists" $?
+}
+
+test_CertmasterDaemonRunning()
+{
+    /etc/init.d/certmaster status
+    assertTrue "certmaster daemon running" $?
+}
+
+test_CertmasterRequestHelp()
+{
+    actual=`certmaster-request --help`
+
+    expected=$(cat <<EOF
+Usage: certmaster-request [options]
+
+Options:
+  -h, --help       show this help message and exit
+  --hostname=NAME  hostname to use as the CN for the certificate
+  --ca=CA          certificate authority used to sign the certificate
+EOF
+)
+
+   assertEquals "certmaster-request --help" "$actual" "$expected"
+
+}
+
+test_CertmasterRequestHFlag() 
+{
+    actual=`certmaster-request -h`
+
+    expected=$(cat <<EOF
+Usage: certmaster-request [options]
+
+Options:
+  -h, --help       show this help message and exit
+  --hostname=NAME  hostname to use as the CN for the certificate
+  --ca=CA          certificate authority used to sign the certificate
+EOF
+)
+   assertEquals "certmaster-request -h" "$actual" "$expected"
+
+}
+
+test_CertmasterRequestBadFlag()
+{
+
+    # backticks don't capture stderr...
+    actual=$(certmaster-request --blah 2>&1)
+
+    expected=$(cat <<EOF
+Usage: certmaster-request [options]
+
+certmaster-request: error: no such option: --blah
+EOF
+)
+   assertEquals "certmaster-request --blah" "$actual" "$expected"
+
+}
+
+test_CertmasterCAHelp()
+{
+    actual=`certmaster-ca --help`
+    expected=$(cat <<EOF
+Usage: certmaster-ca <option> [args]
+
+Options:
+  --version         show program's version number and exit
+  -h, --help        show this help message and exit
+  --ca=CA           certificate authority used to sign/list certs
+  -l, --list        list signing requests remaining
+  -s, --sign        sign requests of hosts specified
+  -c, --clean       clean out all certs or csrs for the hosts specified
+  --list-signed     list all signed certs
+  --list-cert-hash  list the cert hash for signed certs
+EOF
+)
+   assertEquals "certmaster-ca --help" "$actual" "$expected"
+}
+
+test_CertmasterCAHFlag()
+{
+    actual=`certmaster-ca -h`
+    expected=$(cat <<EOF
+Usage: certmaster-ca <option> [args]
+
+Options:
+  --version         show program's version number and exit
+  -h, --help        show this help message and exit
+  --ca=CA           certificate authority used to sign/list certs
+  -l, --list        list signing requests remaining
+  -s, --sign        sign requests of hosts specified
+  -c, --clean       clean out all certs or csrs for the hosts specified
+  --list-signed     list all signed certs
+  --list-cert-hash  list the cert hash for signed certs
+EOF
+)
+   assertEquals "certmaster-ca -h" "$actual" "$expected"
+}
+
+test_CertmasterCAVersion()
+{
+    actual=`certmaster-ca --version`
+
+    [[ "$actual" == *"version:"* ]]    
+    assertTrue "version includes a version" $?
+
+    [[ "$actual" == *"release:"* ]]    
+    assertTrue "version includes a release" $?
+}
+
+test_TestCA_Autosigning()
+{
+    certmaster-request --hostname testcert.pwan.co --ca test
+
+    [[ -e /etc/pki/certmaster-test ]]
+    assertTrue "/etc/pki/certmaster-test exists" $?
+    [[ -e /etc/pki/certmaster-test/testcert.pwan.co.cert ]]
+    assertTrue "testcert.pwan.co.cert exists" $?
+    [[ -e  /etc/pki/certmaster-test/testcert.pwan.co.pem ]]
+    assertTrue "testcert.pwan.co.pem exists" $?
+    [[ -e /etc/pki/certmaster-test/testcert.pwan.co.csr ]]
+    assertTrue "testcert.pwan.co.csr exists" $?
+
+    subject=`openssl x509 -in /etc/pki/certmaster-test/testcert.pwan.co.cert -subject -noout`
+    [[ $subject == *"CN=testcert.pwan.co"* ]]
+
+    openssl rsa -in /etc/pki/certmaster-test/testcert.pwan.co.pem -check > /dev/null 2>&1
+    assertTrue "test.pwan.co.pem OK" $?
+    openssl req -text -noout -verify -in /etc/pki/certmaster-test/testcert.pwan.co.csr > /dev/null 2>&1
+    assertTrue "test.pwan.co.csr OK" $?
+
+    # Verify there are no certs left to sign
+    output=`certmaster-ca --list --ca test`
+    assertEquals "nothing to sign" "$output" "No certificates to sign"
+
+    # Verify the cert shows up in the signed list
+    output=`certmaster-ca --list-signed --ca test`
+    [[ $output == *"testcert.pwan.co"* ]]
+    assertTrue "--list-signed includes testcert" $?
+
+    # Verify the cert shows up in the list-cert-hash command
+    output=`certmaster-ca --list-cert-hash --ca test`
+    [[ $output == *"testcert.pwan.co"* ]]
+    assertTrue "--list-cert-hash includes testcert" $?
+
+}
+
+test_DefaultCA_NonAutosigning() {
+
+    # Turn on job control, so 'fg' is available
+    set -m 
+
+    # Request a cert
+    certmaster-request --hostname defaultcert.pwan.co &
+    sleep 1
+    echo "...patience grasshopper..."
+
+    # Verify the cert is waiting to be signed
+    output=`certmaster-ca --list`
+    [[ $output == *"defaultcert.pwan.co"* ]]
+    assertTrue "$output includes defaultcert" $?
+
+    # Sign the cert
+    output=`certmaster-ca --sign defaultcert.pwan.co`
+    sleep 1
+
+    # Bring the request back to the foreground so it can finish
+    fg 
+
+    # Verify there are no certs left to sign
+    output=`certmaster-ca --list`
+    assertEquals "nothing to sign" "$output" "No certificates to sign"
+
+    # Verify the cert shows up in the signed list
+    output=`certmaster-ca --list-signed`
+    [[ $output == *"defaultcert.pwan.co"* ]]
+    assertTrue "--list-signed includes defaultcert" $?
+
+    # Verify the cert shows up in the list-cert-hash command
+    output=`certmaster-ca --list-cert-hash`
+    [[ $output == *"defaultcert.pwan.co"* ]]
+    assertTrue "--list-cert-hash includes defaultcert" $?
+
+    # Verify all the expected files exist
+    [[ -e /etc/pki/certmaster ]]
+    assertTrue "/etc/pki/certmaster exists" $?
+    [[ -e  /etc/pki/certmaster/defaultcert.pwan.co.cert ]]
+    assertTrue "defaultcert.pwan.co.cert.exists" $?
+    [[ -e /etc/pki/certmaster/defaultcert.pwan.co.pem ]]
+    assertTrue "defaultcert.pwan.co.pem exists" $?
+    [[ -e /etc/pki/certmaster/defaultcert.pwan.co.csr ]]
+    assertTrue "default.pwan.co.csr exists" $?
+
+    # Verify the cert's CN
+    subject=`openssl x509 -in /etc/pki/certmaster/defaultcert.pwan.co.cert -subject -noout`
+    [[ $subject == *"CN=defaultcert.pwan.co"* ]]
+
+    # Verify the key and signing request are valid
+    openssl rsa -in /etc/pki/certmaster/defaultcert.pwan.co.pem -check > /dev/null 2>&1
+    assertTrue "default.pwan.co.pem OK" $?
+    openssl req -text -noout -verify -in /etc/pki/certmaster/defaultcert.pwan.co.csr > /dev/nulla 2>&1
+    assertTrue "defaultcert.pwan.co.csr OK" $?
+
+    set +m
+}
+
+
+# load shunit2
+. /usr/share/shunit2/shunit2