From 436f01bbbc6a93fc149cd58e99833666dd0301e4 Mon Sep 17 00:00:00 2001 From: Marco Fargetta Date: Wed, 11 Oct 2023 17:23:13 +0200 Subject: [PATCH] Remove CSR from CS.cfg and store them in certs folder CSR are created and stored in `/certs` folder as `.csr` files. Fix #2111 --- .../python/pki/server/deployment/__init__.py | 64 ++++++++++++------- .../deployment/scriptlets/configuration.py | 23 +------ .../deployment/scriptlets/instance_layout.py | 4 ++ base/server/python/pki/server/subsystem.py | 9 ++- .../cms/servlet/csadmin/GetConfigEntries.java | 29 +++++++++ .../11.5.0/04-RemoveCertCSRfromConfig.py | 52 +++++++++++++++ docs/changes/v11.5.0/Server-Changes.adoc | 6 ++ 7 files changed, 141 insertions(+), 46 deletions(-) create mode 100644 base/server/upgrade/11.5.0/04-RemoveCertCSRfromConfig.py diff --git a/base/server/python/pki/server/deployment/__init__.py b/base/server/python/pki/server/deployment/__init__.py index 1697d04660c..4ab628cb4ed 100644 --- a/base/server/python/pki/server/deployment/__init__.py +++ b/base/server/python/pki/server/deployment/__init__.py @@ -1391,15 +1391,15 @@ def import_master_config(self, subsystem): continue # check CSR in CS.cfg - param = '%s.%s.certreq' % (subsystem.name, tag) - csr = subsystem.config.get(param) + cert_id = self.get_cert_id(subsystem, tag) + param = 'pki_%s_csr_path' % cert_id - if csr: + if self.mdict.get(param): # CSR already exists continue - # CSR doesn't exist, import from master - names.append(param) + # CSR doesn't provided, import from master + names.append('%s.%s.certreq' % (subsystem.name, tag)) if subsystem.name == 'ca': substores.append('ca.connector.KRA') @@ -1429,10 +1429,26 @@ def import_master_config(self, subsystem): logger.info('Importing %s master config params', subsystem.type) - subsystem.import_master_config(master_properties) + requests = {key: val for key, val in master_properties.items() if key.endswith('.certreq')} + for key, value in requests.items(): + self.store_master_cert_request(subsystem, key, value) + master_properties.pop(key) + subsystem.import_master_config(master_properties) return master_config + def store_master_cert_request(self, subsystem, key, csr): + + nickname = subsystem.config.get(key.replace('.certreq', '.nickname')) + + csr_path = os.path.join(self.instance.conf_dir, 'certs', nickname + '.csr') + try: + self.file.create(csr_path) + with open(csr_path, 'w', encoding='utf-8') as f: + f.write(pki.nssdb.convert_csr(csr, 'base64', 'pem')) + except OSError: + logger.debug('Certificate request %s not stored', key) + def setup_database(self, subsystem, master_config): if config.str2bool(self.mdict['pki_ds_remove_data']): @@ -1904,6 +1920,7 @@ def import_system_cert_request(self, subsystem, tag): cert_id = self.get_cert_id(subsystem, tag) param = 'pki_%s_csr_path' % cert_id csr_path = self.mdict.get(param) + certs_folder = os.path.join(self.instance.conf_dir, 'certs') if not csr_path: # no CSR file to import @@ -1914,11 +1931,11 @@ def import_system_cert_request(self, subsystem, tag): if not os.path.exists(csr_path): raise Exception('Invalid path in %s: %s' % (param, csr_path)) - with open(csr_path, encoding='utf-8') as f: - csr_data = f.read() - - b64_csr = pki.nssdb.convert_csr(csr_data, 'pem', 'base64') - subsystem.config['%s.%s.certreq' % (subsystem.name, tag)] = b64_csr + cert_nickname = subsystem.config.get('%s.%s.nickname' % (subsystem.name, tag)) + self.file.copy( + old_name=csr_path, + new_name=os.path.join(certs_folder, cert_nickname + '.csr'), + overwrite_flag=True) def import_system_cert_requests(self, subsystem): @@ -1938,7 +1955,6 @@ def import_system_cert_requests(self, subsystem): self.import_system_cert_request(subsystem, 'sslserver') def import_ca_signing_cert(self, nssdb): - param = 'pki_ca_signing_cert_path' cert_file = self.mdict.get(param) @@ -2710,12 +2726,17 @@ def create_cert_setup_request(self, subsystem, tag, cert): request.systemCert.keyAlgorithm = subsystem.config['preop.cert.%s.keyalgorithm' % tag] request.systemCert.requestType = 'pkcs10' - request.systemCert.request = subsystem.config.get('%s.%s.certreq' % (subsystem.name, tag)) + try: + csr_path = os.path.join(self.instance.conf_dir, 'certs', cert.get('nickname') + '.csr') + with open(csr_path, 'r', encoding='utf-8') as f: + csr_data = f.read() + request.systemCert.request = pki.nssdb.convert_csr(csr_data, 'pem', 'base64') - request.systemCert.cert = cert.get('data') + except FileNotFoundError: + logger.debug('Certificate request for %s not provided', tag) + request.systemCert.cert = cert.get('data') request.systemCert.profile = subsystem.config['preop.cert.%s.profile' % tag] - request.systemCert.req_ext_oid = subsystem.config.get('preop.cert.%s.ext.oid' % tag) request.systemCert.req_ext_data = subsystem.config.get('preop.cert.%s.ext.data' % tag) request.systemCert.req_ext_critical = subsystem.config.get( @@ -2835,7 +2856,7 @@ def generate_csr(self, logger.debug('generate_csr: calling PKCS10Client for %s', cert_id) - b64_csr = nssdb.create_request_with_wrapping_key( + nssdb.create_request_with_wrapping_key( subject_dn=subject_dn, request_file=csr_path, key_size=key_size) @@ -2858,14 +2879,13 @@ def generate_csr(self, generic_exts=generic_exts, use_jss=True) - with open(csr_pathname, encoding='utf-8') as f: - csr = f.read() - - b64_csr = pki.nssdb.convert_csr(csr, 'pem', 'base64') - shutil.move(csr_pathname, csr_path) - subsystem.config['%s.%s.certreq' % (subsystem.name, tag)] = b64_csr + certs_folder = os.path.join(self.instance.conf_dir, 'certs') + self.file.copy( + old_name=csr_path, + new_name=os.path.join(certs_folder, cert_id + '.csr'), + overwrite_flag=True) def create_cert_request(self, nssdb, tag, request): diff --git a/base/server/python/pki/server/deployment/scriptlets/configuration.py b/base/server/python/pki/server/deployment/scriptlets/configuration.py index d69f1ed088f..c075bef3c23 100644 --- a/base/server/python/pki/server/deployment/scriptlets/configuration.py +++ b/base/server/python/pki/server/deployment/scriptlets/configuration.py @@ -71,28 +71,9 @@ def spawn(self, deployer): deployer.update_sslserver_cert_nickname(subsystem) - if len(subsystems) > 1: + if len(subsystems) == 1: - for s in subsystems: - - # find a subsystem that is already installed - if s.name == subsystem.name: - continue - - # import request data from the existing subsystem - # into the new subsystem being installed - - logger.info('Importing sslserver request data from %s', s.type) - subsystem.config['%s.sslserver.certreq' % subsystem.name] = \ - s.config['%s.sslserver.certreq' % s.name] - - logger.info('Importing subsystem request data from %s', s.type) - subsystem.config['%s.subsystem.certreq' % subsystem.name] = \ - s.config['%s.subsystem.certreq' % s.name] - - break - - else: # self-signed CA + # self-signed CA # To be implemented in ticket #1692. 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 3bc790c4492..7feafd378c6 100644 --- a/base/server/python/pki/server/deployment/scriptlets/instance_layout.py +++ b/base/server/python/pki/server/deployment/scriptlets/instance_layout.py @@ -240,6 +240,10 @@ def spawn(self, deployer): instance.conf_dir, conf_link) + # Create /etc/pki//certs + certs_path = os.path.join(instance.conf_dir, 'certs') + deployer.directory.create(certs_path) + # Link /var/lib/pki//logs to /var/log/pki/ logs_link = os.path.join(instance.base_dir, 'logs') deployer.symlink.create( diff --git a/base/server/python/pki/server/subsystem.py b/base/server/python/pki/server/subsystem.py index c29ce9f6a54..5fe60c6b2fd 100644 --- a/base/server/python/pki/server/subsystem.py +++ b/base/server/python/pki/server/subsystem.py @@ -306,8 +306,6 @@ def get_cert_info(self, tag): cert['id'] = tag cert['nickname'] = self.config.get('%s.%s.nickname' % (self.name, tag)) cert['token'] = self.config.get('%s.%s.tokenname' % (self.name, tag)) - cert['data'] = self.config.get('%s.%s.cert' % (self.name, tag)) - cert['request'] = self.config.get('%s.%s.certreq' % (self.name, tag)) cert['certusage'] = self.config.get('%s.cert.%s.certusage' % (self.name, tag)) return cert @@ -330,7 +328,12 @@ def update_system_cert(self, cert): cert_id = cert['id'] self.config['%s.%s.nickname' % (self.name, cert_id)] = cert.get('nickname') self.config['%s.%s.tokenname' % (self.name, cert_id)] = cert.get('token') - self.config['%s.%s.certreq' % (self.name, cert_id)] = cert.get('request') + certs_path = os.path.join(self.instance.conf_dir, 'certs') + if not os.path.isdir(certs_path): + os.mkdir(certs_path) + csr_file = os.path.join(certs_path, cert.get('nickname') + '.csr') + with open(csr_file, "w", encoding='utf-8') as f: + f.write(pki.nssdb.convert_csr(cert.get('request'), 'base64', 'pem')) def validate_system_cert(self, tag): diff --git a/base/server/src/main/java/com/netscape/cms/servlet/csadmin/GetConfigEntries.java b/base/server/src/main/java/com/netscape/cms/servlet/csadmin/GetConfigEntries.java index 963e70fb0db..5ad7f1014ef 100644 --- a/base/server/src/main/java/com/netscape/cms/servlet/csadmin/GetConfigEntries.java +++ b/base/server/src/main/java/com/netscape/cms/servlet/csadmin/GetConfigEntries.java @@ -18,6 +18,9 @@ package com.netscape.cms.servlet.csadmin; import java.io.IOException; +import java.nio.file.FileSystems; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.Enumeration; import java.util.Locale; import java.util.StringTokenizer; @@ -27,6 +30,7 @@ import org.dogtagpki.server.authentication.AuthToken; import org.dogtagpki.server.authorization.AuthzToken; +import org.dogtagpki.util.cert.CertUtil; import org.w3c.dom.Node; import com.netscape.certsrv.authorization.EAuthzAccessDenied; @@ -159,6 +163,8 @@ protected void process(CMSRequest cmsReq) throws EBaseException { } else if (name.equals("internaldb.replication.password")) { value = getReplicationPassword(); + } else if (name.endsWith(".certreq")) { + value = getCSR(name); } else { value = config.getString(name, null); if ("localhost".equals(value)) @@ -218,4 +224,27 @@ private String getReplicationPassword() throws EBaseException { return pwdStore.getPassword("replicationdb", 0); } + /** + * @author Marco Fargetta + * @deprecated .,certreq configuration properties will be removed in future versions.. + */ + @Deprecated (since = "11.5.0") + private String getCSR(String param) throws EBaseException { + CMSEngine engine = getCMSEngine(); + EngineConfig config = engine.getConfig(); + String csr = null; + + String nickname = config.getString(param.replace(".certreq", ".nickname"), null); + if (nickname == null || nickname.isEmpty()) { + return null; + } + Path csrConfCertsPath = FileSystems.getDefault().getPath(CMS.getInstanceDir(), "conf", "certs", nickname + ".csr"); + try { + csr = Files.readString(csrConfCertsPath); + } catch (IOException e) { + logger.warn("GetConfigEntries: impossible to access the csr file" + csrConfCertsPath, e); + return null; + } + return CertUtil.unwrapPKCS10(csr, true); + } } diff --git a/base/server/upgrade/11.5.0/04-RemoveCertCSRfromConfig.py b/base/server/upgrade/11.5.0/04-RemoveCertCSRfromConfig.py new file mode 100644 index 00000000000..c0b25844cbd --- /dev/null +++ b/base/server/upgrade/11.5.0/04-RemoveCertCSRfromConfig.py @@ -0,0 +1,52 @@ +# Authors: +# Marco Fargetta +# +# Copyright Red Hat, Inc. +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import logging +import os + +import pki + +logger = logging.getLogger(__name__) + + +class RemoveCertCSRfromConfig(pki.server.upgrade.PKIServerUpgradeScriptlet): + + def __init__(self): + super().__init__() + self.message = 'Removes certs data and CSR from CS.cfg' + + def upgrade_subsystem(self, instance, subsystem): + + self.backup(subsystem.cs_conf) + certs_path = os.path.join(instance.conf_dir, 'certs') + if not os.path.isdir(certs_path): + os.mkdir(certs_path) + + logger.info('Removing certs data') + if subsystem.name == 'ca': + self.clean_cert_csr('signing', subsystem, certs_path) + self.clean_cert_csr('ocsp_signing', subsystem, certs_path) + if subsystem.name == 'kra': + self.clean_cert_csr('storage', subsystem, certs_path) + self.clean_cert_csr('transport', subsystem, certs_path) + if subsystem.name == 'ocsp': + self.clean_cert_csr('signing', subsystem, certs_path) + + self.clean_cert_csr('sslserver', subsystem, certs_path) + self.clean_cert_csr('subsystem', subsystem, certs_path) + self.clean_cert_csr('audit_signing', subsystem, certs_path) + + subsystem.save() + + def clean_cert_csr(self, tag, subsystem, dest_path): + subsystem.config.pop('%s.%s.cert' % (subsystem.name, tag), None) + cert_req = subsystem.config.pop('%s.%s.certreq' % (subsystem.name, tag), None) + nickname = subsystem.config.get('%s.%s.nickname' % (subsystem.name, tag)) + if cert_req: + csr_data = pki.nssdb.convert_csr(cert_req, 'base64', 'pem') + with open(os.path.join(dest_path, nickname + '.csr'), 'w', encoding='utf-8') as f: + f.write(csr_data) diff --git a/docs/changes/v11.5.0/Server-Changes.adoc b/docs/changes/v11.5.0/Server-Changes.adoc index fade397b3d7..71fc0cc6bbf 100644 --- a/docs/changes/v11.5.0/Server-Changes.adoc +++ b/docs/changes/v11.5.0/Server-Changes.adoc @@ -14,3 +14,9 @@ A new `pki_ds_url` parameter has been added for `pkispawn` to replace the follow A new `pki_http_enable` parameter has been added for `pkispawn` to enable/disable the plain HTTP connector in `server.xml`. The default value is `True`. + +== Remove cert and CSR from _CS.cfg_ == + +The parameters `..cert` and `..certreq` are removed from `CS.cfg` files. +Certificates are retrieved from the nssdb configured and they are not stored in other places. +CSR are stored in the folder `/certs` as `.csr` and they are retrieved from this location. \ No newline at end of file