From b62545e74cb028ffa91dce88205351b69b5601a0 Mon Sep 17 00:00:00 2001 From: "Endi S. Dewata" Date: Tue, 14 Nov 2023 12:27:56 -0600 Subject: [PATCH] Add pki-server cert-request The pki-server cert-request has been added to simplify creating CSRs for system certs so it's no longer necessary to specify the NSS database path, password file, CSR path, and also to fix the file ownership. The cert_folder(), cert_file(), and csr_file() in PKIInstance have been moved into PKIServer so they can be reused. The cert_folder() has been renamed to certs_dir() for consistency. The PKIServer.create() and instance_layout.py have been updated to create the certs folder so it's guaranteed to exist. The RemoveCertCSRfromConfig upgrade script has been updated to use the new methods in PKIServer. The tests for installing CA with existing NSS database and existing HSM have been updated to use the new command. --- .github/workflows/ca-existing-hsm-test.yml | 55 +++-------- .github/workflows/ca-existing-nssdb-test.yml | 32 ++----- base/server/python/pki/server/__init__.py | 18 ++++ base/server/python/pki/server/cli/cert.py | 91 +++++++++++++++++++ .../deployment/scriptlets/instance_layout.py | 2 + base/server/python/pki/server/instance.py | 44 ++++++--- .../11.5.0/04-RemoveCertCSRfromConfig.py | 13 ++- docs/changes/v11.5.0/Tools-Changes.adoc | 4 + 8 files changed, 177 insertions(+), 82 deletions(-) diff --git a/.github/workflows/ca-existing-hsm-test.yml b/.github/workflows/ca-existing-hsm-test.yml index 37252a9edff..473fb2ef709 100644 --- a/.github/workflows/ca-existing-hsm-test.yml +++ b/.github/workflows/ca-existing-hsm-test.yml @@ -75,22 +75,18 @@ jobs: - name: Create CA signing cert in HSM run: | - docker exec pki runuser -u pkiuser -- \ - pki \ - -d /etc/pki/pki-tomcat/alias \ - -f /etc/pki/pki-tomcat/password.conf \ + docker exec pki pki-server cert-request \ --token HSM \ - nss-cert-request \ --subject "CN=CA Signing Certificate" \ --ext /usr/share/pki/server/certs/ca_signing.conf \ - --csr /tmp/ca_signing.csr + ca_signing docker exec pki runuser -u pkiuser -- \ pki \ -d /etc/pki/pki-tomcat/alias \ -f /etc/pki/pki-tomcat/password.conf \ --token HSM \ nss-cert-issue \ - --csr /tmp/ca_signing.csr \ + --csr /etc/pki/pki-tomcat/certs/ca_signing.csr \ --ext /usr/share/pki/server/certs/ca_signing.conf \ --cert /tmp/ca_signing.crt docker exec pki runuser -u pkiuser -- \ @@ -123,15 +119,11 @@ jobs: - name: Create CA OCSP signing cert in HSM run: | - docker exec pki runuser -u pkiuser -- \ - pki \ - -d /etc/pki/pki-tomcat/alias \ - -f /etc/pki/pki-tomcat/password.conf \ + docker exec pki pki-server cert-request \ --token HSM \ - nss-cert-request \ --subject "CN=OCSP Signing Certificate" \ --ext /usr/share/pki/server/certs/ocsp_signing.conf \ - --csr /tmp/ca_ocsp_signing.csr + ca_ocsp_signing docker exec pki runuser -u pkiuser -- \ pki \ -d /etc/pki/pki-tomcat/alias \ @@ -139,7 +131,7 @@ jobs: --token HSM \ nss-cert-issue \ --issuer HSM:ca_signing \ - --csr /tmp/ca_ocsp_signing.csr \ + --csr /etc/pki/pki-tomcat/certs/ca_ocsp_signing.csr \ --ext /usr/share/pki/server/certs/ocsp_signing.conf \ --cert /tmp/ca_ocsp_signing.crt docker exec pki runuser -u pkiuser -- \ @@ -171,15 +163,11 @@ jobs: - name: Create CA audit signing cert in HSM run: | - docker exec pki runuser -u pkiuser -- \ - pki \ - -d /etc/pki/pki-tomcat/alias \ - -f /etc/pki/pki-tomcat/password.conf \ + docker exec pki pki-server cert-request \ --token HSM \ - nss-cert-request \ --subject "CN=Audit Signing Certificate" \ --ext /usr/share/pki/server/certs/audit_signing.conf \ - --csr /tmp/ca_audit_signing.csr + ca_audit_signing docker exec pki runuser -u pkiuser -- \ pki \ -d /etc/pki/pki-tomcat/alias \ @@ -187,7 +175,7 @@ jobs: --token HSM \ nss-cert-issue \ --issuer HSM:ca_signing \ - --csr /tmp/ca_audit_signing.csr \ + --csr /etc/pki/pki-tomcat/certs/ca_audit_signing.csr \ --ext /usr/share/pki/server/certs/audit_signing.conf \ --cert /tmp/ca_audit_signing.crt docker exec pki runuser -u pkiuser -- \ @@ -220,15 +208,11 @@ jobs: - name: Create subsystem cert in HSM run: | - docker exec pki runuser -u pkiuser -- \ - pki \ - -d /etc/pki/pki-tomcat/alias \ - -f /etc/pki/pki-tomcat/password.conf \ + docker exec pki pki-server cert-request \ --token HSM \ - nss-cert-request \ --subject "CN=Subsystem Certificate" \ --ext /usr/share/pki/server/certs/subsystem.conf \ - --csr /tmp/subsystem.csr + subsystem docker exec pki runuser -u pkiuser -- \ pki \ -d /etc/pki/pki-tomcat/alias \ @@ -236,7 +220,7 @@ jobs: --token HSM \ nss-cert-issue \ --issuer HSM:ca_signing \ - --csr /tmp/subsystem.csr \ + --csr /etc/pki/pki-tomcat/certs/subsystem.csr \ --ext /usr/share/pki/server/certs/subsystem.conf \ --cert /tmp/subsystem.crt docker exec pki runuser -u pkiuser -- \ @@ -268,14 +252,10 @@ jobs: - name: Create SSL server cert in server's NSS database run: | - docker exec pki runuser -u pkiuser -- \ - pki \ - -d /etc/pki/pki-tomcat/alias \ - -f /etc/pki/pki-tomcat/password.conf \ - nss-cert-request \ + docker exec pki pki-server cert-request \ --subject "CN=pki.example.com" \ --ext /usr/share/pki/server/certs/sslserver.conf \ - --csr /tmp/sslserver.csr + sslserver docker exec pki runuser -u pkiuser -- \ pki \ -d /etc/pki/pki-tomcat/alias \ @@ -283,7 +263,7 @@ jobs: --token HSM \ nss-cert-issue \ --issuer HSM:ca_signing \ - --csr /tmp/sslserver.csr \ + --csr /etc/pki/pki-tomcat/certs/sslserver.csr \ --ext /usr/share/pki/server/certs/sslserver.conf \ --cert /tmp/sslserver.crt docker exec pki runuser -u pkiuser -- \ @@ -354,11 +334,6 @@ jobs: -D pki_audit_signing_token=HSM \ -D pki_subsystem_token=HSM \ -D pki_sslserver_token=internal \ - -D pki_ca_signing_csr_path=/tmp/ca_signing.csr \ - -D pki_ocsp_signing_csr_path=/tmp/ca_ocsp_signing.csr \ - -D pki_audit_signing_csr_path=/tmp/ca_audit_signing.csr \ - -D pki_subsystem_csr_path=/tmp/subsystem.csr \ - -D pki_sslserver_csr_path=/tmp/sslserver.csr \ -D pki_admin_cert_path=/tmp/admin.crt \ -D pki_admin_csr_path=/tmp/admin.csr \ -v diff --git a/.github/workflows/ca-existing-nssdb-test.yml b/.github/workflows/ca-existing-nssdb-test.yml index 9f1339c3dcf..0cbd547c2b5 100644 --- a/.github/workflows/ca-existing-nssdb-test.yml +++ b/.github/workflows/ca-existing-nssdb-test.yml @@ -54,13 +54,10 @@ jobs: - name: Create CA signing cert in server's NSS database run: | - docker exec pki mkdir -p /etc/pki/pki-tomcat/certs - docker exec pki pki \ - -d /etc/pki/pki-tomcat/alias \ - nss-cert-request \ + docker exec pki pki-server cert-request \ --subject "CN=CA Signing Certificate" \ --ext /usr/share/pki/server/certs/ca_signing.conf \ - --csr /etc/pki/pki-tomcat/certs/ca_signing.csr + ca_signing docker exec pki pki \ -d /etc/pki/pki-tomcat/alias \ nss-cert-issue \ @@ -88,12 +85,10 @@ jobs: - name: Create CA OCSP signing cert in server's NSS database run: | - docker exec pki pki \ - -d /etc/pki/pki-tomcat/alias \ - nss-cert-request \ + docker exec pki pki-server cert-request \ --subject "CN=OCSP Signing Certificate" \ --ext /usr/share/pki/server/certs/ocsp_signing.conf \ - --csr /etc/pki/pki-tomcat/certs/ca_ocsp_signing.csr + ca_ocsp_signing docker exec pki pki \ -d /etc/pki/pki-tomcat/alias \ nss-cert-issue \ @@ -121,12 +116,10 @@ jobs: - name: Create CA audit signing cert in server's NSS database run: | - docker exec pki pki \ - -d /etc/pki/pki-tomcat/alias \ - nss-cert-request \ + docker exec pki pki-server cert-request \ --subject "CN=Audit Signing Certificate" \ --ext /usr/share/pki/server/certs/audit_signing.conf \ - --csr /etc/pki/pki-tomcat/certs/ca_audit_signing.csr + ca_audit_signing docker exec pki pki \ -d /etc/pki/pki-tomcat/alias \ nss-cert-issue \ @@ -155,12 +148,10 @@ jobs: - name: Create subsystem cert in server's NSS database run: | - docker exec pki pki \ - -d /etc/pki/pki-tomcat/alias \ - nss-cert-request \ + docker exec pki pki-server cert-request \ --subject "CN=Subsystem Certificate" \ --ext /usr/share/pki/server/certs/subsystem.conf \ - --csr /etc/pki/pki-tomcat/certs/subsystem.csr + subsystem docker exec pki pki \ -d /etc/pki/pki-tomcat/alias \ nss-cert-issue \ @@ -188,12 +179,10 @@ jobs: - name: Create SSL server cert in server's NSS database run: | - docker exec pki pki \ - -d /etc/pki/pki-tomcat/alias \ - nss-cert-request \ + docker exec pki pki-server cert-request \ --subject "CN=pki.example.com" \ --ext /usr/share/pki/server/certs/sslserver.conf \ - --csr /etc/pki/pki-tomcat/certs/sslserver.csr + sslserver docker exec pki pki \ -d /etc/pki/pki-tomcat/alias \ nss-cert-issue \ @@ -243,7 +232,6 @@ jobs: - name: Install CA with existing NSS database run: | - docker exec pki chown -R pkiuser:pkiuser /etc/pki/pki-tomcat/certs docker exec pki pkispawn \ -f /usr/share/pki/server/examples/installation/ca.cfg \ -s CA \ diff --git a/base/server/python/pki/server/__init__.py b/base/server/python/pki/server/__init__.py index ceca4b1adf2..4ebfd3252cf 100644 --- a/base/server/python/pki/server/__init__.py +++ b/base/server/python/pki/server/__init__.py @@ -154,6 +154,10 @@ def bin_dir(self): def conf_dir(self): return os.path.join(self.base_dir, 'conf') + @property + def certs_dir(self): + return os.path.join(self.conf_dir, 'certs') + @property def lib_dir(self): return os.path.join(self.base_dir, 'lib') @@ -307,6 +311,18 @@ def export_ca_cert(self): # TODO: handle other types of HTTP connector + def cert_file(self, cert_id): + ''' + Compute name of certificate under instance certs folder. + ''' + return os.path.join(self.certs_dir, cert_id + '.crt') + + def csr_file(self, cert_id): + ''' + Compute name of CSR under instance certs folder. + ''' + return os.path.join(self.certs_dir, cert_id + '.csr') + def create_catalina_policy(self): logger.info('Creating catalina.policy') @@ -685,6 +701,8 @@ def create(self, force=False): self.create_conf_dir(exist_ok=True) + self.makedirs(self.certs_dir, exist_ok=True) + catalina_policy = os.path.join(Tomcat.CONF_DIR, 'catalina.policy') self.copy(catalina_policy, self.catalina_policy, force=force) diff --git a/base/server/python/pki/server/cli/cert.py b/base/server/python/pki/server/cli/cert.py index 190eb742b4e..2a452f9ec9b 100644 --- a/base/server/python/pki/server/cli/cert.py +++ b/base/server/python/pki/server/cli/cert.py @@ -54,6 +54,7 @@ def __init__(self): self.add_module(CertShowCLI()) self.add_module(CertValidateCLI()) self.add_module(CertUpdateCLI()) + self.add_module(CertRequestCLI()) self.add_module(CertCreateCLI()) self.add_module(CertImportCLI()) self.add_module(CertExportCLI()) @@ -502,6 +503,96 @@ def execute(self, argv): self.print_message('Updated "%s" system certificate' % cert_id) +class CertRequestCLI(pki.cli.CLI): + ''' + Generate a key pair and an enrollment request for a system certificate. + ''' + + help = '''\ + Usage: pki-server cert-request [OPTIONS] + + -i, --instance Instance ID (default: pki-tomcat) + --token Token for storing the key pair + --subject Subject DN + --ext Configuration file for CSR extension + -v, --verbose Run in verbose mode. + --debug Run in debug mode. + --help Show help message. + ''' # noqa: E501 + + def __init__(self): + super().__init__('request', inspect.cleandoc(self.__class__.__doc__)) + + def print_help(self): + print(textwrap.dedent(self.__class__.help)) + + def execute(self, argv): + + try: + opts, args = getopt.gnu_getopt(argv, 'i:v', [ + 'instance=', 'token=', 'subject=', 'ext=', + 'verbose', 'debug', 'help']) + + except getopt.GetoptError as e: + logger.error(e) + self.print_help() + sys.exit(1) + + instance_name = 'pki-tomcat' + token = None + subject_dn = None + ext_conf = None + + for o, a in opts: + if o in ('-i', '--instance'): + instance_name = a + + elif o == '--token': + token = a + + elif o == '--subject': + subject_dn = a + + elif o == '--ext': + ext_conf = a + + elif o == '--debug': + logging.getLogger().setLevel(logging.DEBUG) + + elif o in ('-v', '--verbose'): + logging.getLogger().setLevel(logging.INFO) + + elif o == '--help': + self.print_help() + sys.exit() + + else: + logger.error('Invalid option: %s', o) + self.print_help() + sys.exit(1) + + if len(args) < 1: + raise Exception('Missing certificate ID') + + if subject_dn is None: + raise Exception('Missing subject DN') + + cert_id = args[0] + + instance = pki.server.instance.PKIServerFactory.create(instance_name) + + if not instance.exists(): + raise Exception('Invalid instance: %s' % instance_name) + + instance.load() + + instance.cert_request( + cert_id, + subject_dn, + token=token, + ext_conf=ext_conf) + + class CertCreateCLI(pki.cli.CLI): def __init__(self): super().__init__('create', 'Create system certificate.') diff --git a/base/server/python/pki/server/deployment/scriptlets/instance_layout.py b/base/server/python/pki/server/deployment/scriptlets/instance_layout.py index 2aee4843d4e..9dc3da251a8 100644 --- a/base/server/python/pki/server/deployment/scriptlets/instance_layout.py +++ b/base/server/python/pki/server/deployment/scriptlets/instance_layout.py @@ -54,6 +54,8 @@ def spawn(self, deployer): logger.info('Creating %s', instance.conf_dir) instance.makedirs(instance.conf_dir, exist_ok=True) + instance.makedirs(instance.certs_dir, exist_ok=True) + # Configuring internal token password internal_token = deployer.mdict['pki_self_signed_token'] diff --git a/base/server/python/pki/server/instance.py b/base/server/python/pki/server/instance.py index 7ca804aa480..5e98d2b87b4 100644 --- a/base/server/python/pki/server/instance.py +++ b/base/server/python/pki/server/instance.py @@ -699,18 +699,6 @@ def cert_update_config(self, cert_id, cert): raise pki.server.PKIServerException( 'No subsystem can be loaded for %s in instance %s.' % (cert_id, self.name)) - @property - def cert_folder(self): - return os.path.join(pki.CONF_DIR, self.name, 'certs') - - def cert_file(self, cert_id): - """Compute name of certificate under instance cert folder.""" - return os.path.join(self.cert_folder, cert_id + '.crt') - - def csr_file(self, cert_id): - """Compute name of CSR under instance cert folder.""" - return os.path.join(self.cert_folder, cert_id + '.csr') - def cert_import(self, cert_id, cert_file=None): """ Import cert from cert_file into NSS db with appropriate trust @@ -775,6 +763,35 @@ def cert_import(self, cert_id, cert_file=None): finally: nssdb.close() + def cert_request(self, cert_id, subject_dn, token=None, ext_conf=None): + + token = pki.nssdb.normalize_token(token) + csr_file = self.csr_file(cert_id) + + cmd = [ + '/usr/sbin/runuser', + '-u', self.user, '--', + 'pki', + '-d', self.nssdb_dir, + '-f', self.password_conf + ] + + if token: + cmd.extend(['--token', token]) + + cmd.extend([ + 'nss-cert-request', + '--subject', subject_dn, + '--csr', csr_file + ]) + + if ext_conf: + cmd.extend(['--ext', ext_conf]) + + logger.debug('Command: %s', ' '.join(cmd)) + + subprocess.check_call(cmd) + def cert_create( self, cert_id=None, username=None, password=None, @@ -852,9 +869,6 @@ def cert_create( "'temp_cert' must be used with 'cert_id'") new_cert_file = output - if not os.path.exists(self.cert_folder): - os.makedirs(self.cert_folder) - if temp_cert: assert subsystem is not None # temp_cert only supported with cert_id diff --git a/base/server/upgrade/11.5.0/04-RemoveCertCSRfromConfig.py b/base/server/upgrade/11.5.0/04-RemoveCertCSRfromConfig.py index ca94c461d25..91c74b7cf4e 100644 --- a/base/server/upgrade/11.5.0/04-RemoveCertCSRfromConfig.py +++ b/base/server/upgrade/11.5.0/04-RemoveCertCSRfromConfig.py @@ -19,26 +19,29 @@ def __init__(self): super().__init__() self.message = 'Removes certs data and CSR from CS.cfg' + def upgrade_instance(self, instance): + + instance.makedirs(instance.certs_dir, exist_ok=True) + def upgrade_subsystem(self, instance, subsystem): self.backup(subsystem.cs_conf) - certs_path = os.path.join(instance.conf_dir, 'certs') - instance.makedirs(certs_path, exist_ok=True) + logger.info('Removing certs data') certs = subsystem.find_system_certs() for cert in certs: - self.clean_cert_csr(cert['id'], subsystem, certs_path) + self.clean_cert_csr(cert['id'], subsystem) subsystem.save() - def clean_cert_csr(self, tag, subsystem, dest_path): + def clean_cert_csr(self, tag, subsystem): subsystem.config.pop('%s.%s.cert' % (subsystem.name, tag), None) cert_req = subsystem.config.pop('%s.%s.certreq' % (subsystem.name, tag), None) if tag != 'sslserver' and tag != 'subsystem': tag = subsystem.name + '_' + tag if cert_req: csr_data = pki.nssdb.convert_csr(cert_req, 'base64', 'pem') - csr_file = os.path.join(dest_path, tag + '.csr') + csr_file = subsystem.instance.csr_file(tag) with open(csr_file, 'w', encoding='utf-8') as f: f.write(csr_data) os.chown(csr_file, subsystem.instance.uid, subsystem.instance.gid) diff --git a/docs/changes/v11.5.0/Tools-Changes.adoc b/docs/changes/v11.5.0/Tools-Changes.adoc index bf89957adbc..5a362beddee 100644 --- a/docs/changes/v11.5.0/Tools-Changes.adoc +++ b/docs/changes/v11.5.0/Tools-Changes.adoc @@ -37,3 +37,7 @@ The `pki nss-cert-del` command has been added to delete a certificate from NSS d The `pki client-cert-del` command has been deprecated. Use `pki nss-cert-del` command instead. + +== New pki-server cert-request CLI == + +The `pki-server cert-request` command has been added to generate a key pair and an enrollment request for a system certificate.