Yaniv Dary has uploaded a new change for review. Change subject: packaging: setup: Make pki interaction easier ......................................................................
packaging: setup: Make pki interaction easier Make pki interaction use files and ask again if needed. Requires: http://gerrit.ovirt.org/32742 Bug-Url: https://bugzilla.redhat.com/1118328 Bug-Url: https://bugzilla.redhat.com/1118330 Change-Id: Ia5b3713330d6e11bd7c374e04f17660be79f2251 Signed-off-by: Yedidyah Bar David <[email protected]> (cherry picked from commit c390da6ed5a42c1c49eadc2c039c8f8aa316ff9e) --- M packaging/setup/ovirt_engine_setup/reports/constants.py M packaging/setup/plugins/ovirt-engine-setup/ovirt-engine-reports/config/sso.py M packaging/setup/plugins/ovirt-engine-setup/ovirt-engine-reports/pki/apache.py M packaging/setup/plugins/ovirt-engine-setup/ovirt-engine-reports/pki/jboss.py 4 files changed, 297 insertions(+), 145 deletions(-) git pull ssh://gerrit.ovirt.org:29418/ovirt-reports refs/changes/39/33139/1 diff --git a/packaging/setup/ovirt_engine_setup/reports/constants.py b/packaging/setup/ovirt_engine_setup/reports/constants.py index b691e66..e113838 100644 --- a/packaging/setup/ovirt_engine_setup/reports/constants.py +++ b/packaging/setup/ovirt_engine_setup/reports/constants.py @@ -299,6 +299,10 @@ OVIRT_ENGINE_PKIDIR, 'certs', ) + OVIRT_ENGINE_PKIREQUESTSDIR = os.path.join( + OVIRT_ENGINE_PKIDIR, + 'requests', + ) # These are generated by engine ca.py. If not found we do that # ourselves. @@ -420,14 +424,8 @@ JBOSS_DEBUG_ADDRESS = 'OVESETUP_REPORTS_CONFIG/jbossDebugAddress' JBOSS_NEEDED = 'OVESETUP_REPORTS_CONFIG/jbossNeeded' - @osetupattrs( - answerfile=True, - summary=True, - description=_('Engine Host FQDN'), - postinstallfile=True, - ) - def ENGINE_FQDN(self): - return 'OVESETUP_REPORTS_CONFIG/engineFqdn' + PKI_JBOSS_CSR_FILENAME = 'OVESETUP_REPORTS_CONFIG/pkiJbossCSRFilename' + PKI_APACHE_CSR_FILENAME = 'OVESETUP_REPORTS_CONFIG/pkiApacheCSRFilename' @util.export @@ -690,4 +688,20 @@ ENABLE = 'OVESETUP_ENGINE_CORE/enable' [email protected] [email protected] +@osetupattrsclass +class EngineConfigEnv(object): + """Sync with ovirt-engine""" + + @osetupattrs( + answerfile=True, + summary=True, + description=_('Engine Host FQDN'), + postinstallfile=True, + ) + def ENGINE_FQDN(self): + return 'OVESETUP_ENGINE_CONFIG/fqdn' + + # vim: expandtab tabstop=4 shiftwidth=4 diff --git a/packaging/setup/plugins/ovirt-engine-setup/ovirt-engine-reports/config/sso.py b/packaging/setup/plugins/ovirt-engine-setup/ovirt-engine-reports/config/sso.py index 08f158b..495afb3 100644 --- a/packaging/setup/plugins/ovirt-engine-setup/ovirt-engine-reports/config/sso.py +++ b/packaging/setup/plugins/ovirt-engine-setup/ovirt-engine-reports/config/sso.py @@ -27,6 +27,7 @@ from ovirt_engine_setup import constants as osetupcons +from ovirt_engine_setup import hostname as osetuphostname from ovirt_engine_setup.reports import constants as oreportscons from ovirt_engine_setup.engine_common \ import constants as oengcommcons @@ -43,51 +44,29 @@ ) def _init(self): self.environment.setdefault( - oreportscons.ConfigEnv.ENGINE_FQDN, + oreportscons.EngineConfigEnv.ENGINE_FQDN, None ) @plugin.event( stage=plugin.Stages.STAGE_CUSTOMIZATION, after=( - oreportscons.Stages.CORE_ENABLE, + osetupcons.Stages.DIALOG_TITLES_S_NETWORK, + oengcommcons.Stages.NETWORK_OWNERS_CONFIG_CUSTOMIZED, + ), + before=( + osetupcons.Stages.DIALOG_TITLES_E_NETWORK, ), condition=lambda self: self.environment[oreportscons.CoreEnv.ENABLE], ) def _customization(self): - if self.environment[oreportscons.EngineCoreEnv.ENABLE]: - self.environment[ - oreportscons.ConfigEnv.ENGINE_FQDN - ] = self.environment[osetupcons.ConfigEnv.FQDN] - else: - interactive = self.environment[ - oreportscons.ConfigEnv.ENGINE_FQDN - ] is None - validFQDN = False - while not validFQDN: - if interactive: - self.environment[ - oreportscons.ConfigEnv.ENGINE_FQDN - ] = self.dialog.queryString( - name='OVESETUP_REPORTS_ENGINE_FQDN', - note=_( - 'Fully qualified DNS name of the engine host: ' - ), - prompt=True, - ) - # TODO do some real validation - - # either syntactic/dns lookup/etc or just try to connect to it - validFQDN = self.environment[ - oreportscons.ConfigEnv.ENGINE_FQDN - ] != '' - if not validFQDN: - self.logger.error( - _('Host name is not valid: {error}').format( - error='Cannot be empty', - ), - ) - if not interactive: - break + osetuphostname.Hostname( + plugin=self, + ).getHostname( + envkey=oreportscons.EngineConfigEnv.ENGINE_FQDN, + whichhost=_('the engine'), + supply_default=False, + ) @plugin.event( stage=plugin.Stages.STAGE_MISC, @@ -110,7 +89,9 @@ 'https://{host}:{port}/ovirt-engine/services' '/get-session-user\n' ).format( - host=self.environment[oreportscons.ConfigEnv.ENGINE_FQDN], + host=self.environment[ + oreportscons.EngineConfigEnv.ENGINE_FQDN + ], # TODO - this should be customizable as well, but default # works (443). port=self.environment[ diff --git a/packaging/setup/plugins/ovirt-engine-setup/ovirt-engine-reports/pki/apache.py b/packaging/setup/plugins/ovirt-engine-setup/ovirt-engine-reports/pki/apache.py index c3dc6e3..58508ba 100644 --- a/packaging/setup/plugins/ovirt-engine-setup/ovirt-engine-reports/pki/apache.py +++ b/packaging/setup/plugins/ovirt-engine-setup/ovirt-engine-reports/pki/apache.py @@ -19,7 +19,10 @@ """apache PKI plugin.""" +import contextlib import os +import tempfile +import urllib2 import gettext @@ -38,6 +41,7 @@ from ovirt_engine_setup import constants as osetupcons +from ovirt_engine_setup.engine_common import constants as oengcommcons from ovirt_engine_setup.reports import constants as oreportscons @@ -58,7 +62,7 @@ req = X509.Request() req.set_pubkey(evp) req.sign(evp, 'sha1') - return rsapem, req.as_pem() + return rsapem, req.as_pem(), req.get_pubkey().as_pem(cipher=None) def __init__(self, context): super(Plugin, self).__init__(context=context) @@ -66,6 +70,7 @@ self._need_key = False self._need_cert = False self._need_ca_cert = False + self._csr_file = None @plugin.event( stage=plugin.Stages.STAGE_INIT, @@ -79,12 +84,20 @@ oreportscons.ConfigEnv.APACHE_CA_CERTIFICATE, None ) + self.environment.setdefault( + oreportscons.ConfigEnv.PKI_APACHE_CSR_FILENAME, + None + ) @plugin.event( stage=plugin.Stages.STAGE_CUSTOMIZATION, + before=( + oengcommcons.Stages.DIALOG_TITLES_E_PKI, + ), after=( oreportscons.Stages.CORE_ENABLE, oreportscons.Stages.ENGINE_CORE_ENABLE, + oengcommcons.Stages.DIALOG_TITLES_S_PKI, ), condition=lambda self: ( self.environment[ @@ -124,7 +137,8 @@ ) if self._need_key: - self._key, req = self._genReq() + self._key, req, my_pubk = self._genReq() + self._need_cert = True if ( self._need_cert and @@ -132,76 +146,133 @@ oreportscons.ConfigEnv.APACHE_CERTIFICATE ] is None ): - self.dialog.displayMultiString( - name='REPORTS_APACHE_CERTIFICATE_REQUEST', - value=req.splitlines(), - note=_( - '\n\nPlease issue Reports certificate based ' - 'on this certificate request\n\n' - ), + csr_fname = self.environment[ + oreportscons.ConfigEnv.PKI_APACHE_CSR_FILENAME + ] + with ( + open(csr_fname, 'w') if csr_fname + else tempfile.NamedTemporaryFile(mode='w', delete=False) + ) as self._csr_file: + self._csr_file.write(req) + + remote_name = '{name}-{fqdn}'.format( + name=oreportscons.Const.PKI_REPORTS_APACHE_CERT_NAME, + fqdn=self.environment[osetupcons.ConfigEnv.FQDN], + ) + enroll_command = ( + " /usr/share/ovirt-engine/bin/pki-enroll-request.sh \\\n" + " --name={remote_name} \\\n" + " --subject=\"" + "$(openssl x509 -in {pkidir}/ca.pem -noout " + "-subject | sed 's;subject= \(/C=[^/]*/O=[^/]*\)/.*;\\1;')" + "/CN={fqdn}\"" + ).format( + remote_name=remote_name, + pkidir=oreportscons.FileLocations.OVIRT_ENGINE_PKIDIR, + fqdn=self.environment[osetupcons.ConfigEnv.FQDN], ) self.dialog.note( text=_( - "Please enroll SSL certificate for apache.\n" - "It can be done using engine internal CA, if no 3rd " - "party CA is available, with this sequence:\n" - - "1. Copy and save certificate request at\n" - " /etc/pki/ovirt-engine/requests/{name}.req\n" - "on the engine host\n\n" - "2. execute, on the engine host, this command " - "to enroll the cert:\n" - " /usr/share/ovirt-engine/bin/pki-enroll-request.sh \\\n" - " --name={name} \\\n" - " --subject=\"/C=<country>/O=<organization>/" - "CN={fqdn}\"\n" - "Substitute <country>, <organization> to suite your " - "environment\n" - "(i.e. the values must match values in the " - "certificate authority of your engine)\n\n" - - "3. Certificate will be available at\n" - " /etc/pki/ovirt-engine/certs/{name}.cer\n" - "on the engine host, please copy that content here " - "when required\n" + "\nTo sign the Apache certificate on the engine server, " + "please:\n\n" + "1. Copy {tmpcsr} from here to {enginecsr} on the engine " + "server.\n\n" + "2. Run on the engine server:\n\n" + "{enroll_command}\n\n" + "3. Copy {enginecert} from the engine server to some file " + "here. Provide the file name below.\n\n" + "See {url} for more details, including using an external " + "certificate authority." ).format( - fqdn=self.environment[osetupcons.ConfigEnv.FQDN], - name=oreportscons.Const.PKI_REPORTS_APACHE_CERT_NAME, + tmpcsr=self._csr_file.name, + enginecsr='{pkireqdir}/{remote_name}.req'.format( + pkireqdir=oreportscons.FileLocations. + OVIRT_ENGINE_PKIREQUESTSDIR, + remote_name=remote_name, + ), + enroll_command=enroll_command, + enginecert='{pkicertdir}/{remote_name}.cer'.format( + pkicertdir=oreportscons.FileLocations. + OVIRT_ENGINE_PKICERTSDIR, + remote_name=remote_name, + ), + url="http://www.ovirt.org/Features/Separate-Reports-Host", ), ) - self.environment[ - oreportscons.ConfigEnv.APACHE_CERTIFICATE - ] = self.dialog.queryMultiString( - name='REPORTS_APACHE_CERTIFICATE', - note=_( - '\n\nPlease input Reports certificate chain that ' - 'matches certificate request, (issuer is not ' - 'mandatory, from intermediate and upper)\n\n' - ), - ) + goodcert = False + while not goodcert: + filename = self.dialog.queryString( + name='REPORTS_APACHE_CERT_FILENAME', + note=_( + '\nPlease input the location of the file where you ' + 'copied the signed certificate in step 3 above: ' + ), + prompt=True, + ) + try: + with open(filename) as f: + cert = f.read() + goodcert = my_pubk == X509.load_cert_string( + cert + ).get_pubkey().as_pem(cipher=None) + self.environment[ + oreportscons.ConfigEnv.APACHE_CERTIFICATE + ] = cert + if not goodcert: + self.logger.error( + _( + 'The certificate in {cert} does not match ' + 'the request in {req}. Please try again.' + ).format( + cert=filename, + req=self._csr_file.name, + ) + ) + except: + self.logger.error( + _( + 'Error while reading or parsing {cert}. ' + 'Please try again.' + ).format( + cert=filename, + ) + ) + self.logger.debug('Error reading cert', exc_info=True) + self.logger.info(_('Apache certificate read successfully')) - if ( + while ( self._need_ca_cert and self.environment[ oreportscons.ConfigEnv.APACHE_CA_CERTIFICATE ] is None ): - self.environment[ - oreportscons.ConfigEnv.APACHE_CA_CERTIFICATE - ] = self.dialog.queryMultiString( - name='REPORTS_APACHE_CA_CERTIFICATE', - note=_( - "\n\nPlease provide PEM-encoded CA Cert bundle for " - "apache.\n" - "If using the engine CA, please copy and paste the " - "contents of the file {cacert} on the engine host." - ).format( - cacert=oreportscons.FileLocations. - OVIRT_ENGINE_PKI_APACHE_CA_CERT, - ), - ) + remote_engine_host = self.environment[ + oreportscons.EngineConfigEnv.ENGINE_FQDN + ] + + with contextlib.closing( + urllib2.urlopen( + 'http://{engine_fqdn}/ovirt-engine/services/' + 'pki-resource?resource=ca-certificate&' + 'format=X509-PEM'.format( + engine_fqdn=remote_engine_host + ) + ) + ) as urlObj: + engine_ca_cert = urlObj.read() + if engine_ca_cert: + self.environment[ + oreportscons.ConfigEnv.APACHE_CA_CERTIFICATE + ] = engine_ca_cert + else: + self.logger.error( + _( + 'Failed to get CA Certificate from engine. ' + 'Please try again.' + ) + ) @plugin.event( stage=plugin.Stages.STAGE_MISC, @@ -292,5 +363,19 @@ oreportscons.FileLocations.OVIRT_ENGINE_PKI_APACHE_CA_CERT ) + @plugin.event( + stage=plugin.Stages.STAGE_CLEANUP, + ) + def _cleanup(self): + if self._csr_file is not None: + try: + os.unlink(self._csr_file.name) + except OSError as e: + self.logger.debug( + "Failed to delete '%s'", + self._csr_file.name, + exc_info=True, + ) + # vim: expandtab tabstop=4 shiftwidth=4 diff --git a/packaging/setup/plugins/ovirt-engine-setup/ovirt-engine-reports/pki/jboss.py b/packaging/setup/plugins/ovirt-engine-setup/ovirt-engine-reports/pki/jboss.py index e3bd8fd..74e9dfa 100644 --- a/packaging/setup/plugins/ovirt-engine-setup/ovirt-engine-reports/pki/jboss.py +++ b/packaging/setup/plugins/ovirt-engine-setup/ovirt-engine-reports/pki/jboss.py @@ -20,6 +20,7 @@ import os +import tempfile import gettext @@ -38,6 +39,7 @@ from ovirt_engine_setup import constants as osetupcons +from ovirt_engine_setup.engine_common import constants as oengcommcons from ovirt_engine_setup.reports import constants as oreportscons @@ -58,7 +60,7 @@ req = X509.Request() req.set_pubkey(evp) req.sign(evp, 'sha1') - return rsapem, req.as_pem() + return rsapem, req.as_pem(), req.get_pubkey().as_pem(cipher=None) def __init__(self, context): super(Plugin, self).__init__(context=context) @@ -66,6 +68,7 @@ self._need_key = False self._need_cert = False self._on_separate_h = False + self._csr_file = None @plugin.event( stage=plugin.Stages.STAGE_INIT, @@ -75,12 +78,20 @@ oreportscons.ConfigEnv.JBOSS_CERTIFICATE_CHAIN, None ) + self.environment.setdefault( + oreportscons.ConfigEnv.PKI_JBOSS_CSR_FILENAME, + None + ) @plugin.event( stage=plugin.Stages.STAGE_CUSTOMIZATION, + before=( + oengcommcons.Stages.DIALOG_TITLES_E_PKI, + ), after=( oreportscons.Stages.CORE_ENABLE, oreportscons.Stages.ENGINE_CORE_ENABLE, + oengcommcons.Stages.DIALOG_TITLES_S_PKI, ), condition=lambda self: ( self.environment[ @@ -106,7 +117,8 @@ ) if self._need_key: - self._key, req = self._genReq() + self._key, req, my_pubk = self._genReq() + self._need_cert = True if ( self._need_cert and @@ -114,55 +126,101 @@ oreportscons.ConfigEnv.JBOSS_CERTIFICATE_CHAIN ] is None ): - self.dialog.displayMultiString( - name='REPORTS_JBOSS_CERTIFICATE_REQUEST', - value=req.splitlines(), - note=_( - '\n\nPlease issue Reports certificate based ' - 'on this certificate request\n\n' - ), + csr_fname = self.environment[ + oreportscons.ConfigEnv.PKI_JBOSS_CSR_FILENAME + ] + with ( + open(csr_fname, 'w') if csr_fname + else tempfile.NamedTemporaryFile(mode='w', delete=False) + ) as self._csr_file: + self._csr_file.write(req) + + remote_name = '{name}-{fqdn}'.format( + name=oreportscons.Const.PKI_REPORTS_JBOSS_CERT_NAME, + fqdn=self.environment[osetupcons.ConfigEnv.FQDN], + ) + enroll_command = ( + " /usr/share/ovirt-engine/bin/pki-enroll-request.sh \\\n" + " --name={remote_name} \\\n" + " --subject=\"" + "$(openssl x509 -in {pkidir}/ca.pem -noout " + "-subject | sed 's;subject= \(/C=[^/]*/O=[^/]*\)/.*;\\1;')" + "/CN={fqdn}\"" + ).format( + remote_name=remote_name, + pkidir=oreportscons.FileLocations.OVIRT_ENGINE_PKIDIR, + fqdn=self.environment[osetupcons.ConfigEnv.FQDN], ) self.dialog.note( text=_( - "Enroll SSL certificate for the Reports service.\n" - "It can be done using engine internal CA, if no 3rd " - "party CA is available, with this sequence:\n" - - "1. Copy and save certificate request at\n" - " /etc/pki/ovirt-engine/requests/{name}.req\n" - "on the engine server\n\n" - "2. execute, on the engine host, this command " - "to enroll the cert:\n" - " /usr/share/ovirt-engine/bin/pki-enroll-request.sh \\\n" - " --name={name} \\\n" - " --subject=\"/C=<country>/O=<organization>/" - "CN={fqdn}\"\n" - "Substitute <country>, <organization> to suite your " - "environment\n" - "(i.e. the values must match values in the " - "certificate authority of your engine)\n\n" - - "3. Certificate will be available at\n" - " /etc/pki/ovirt-engine/certs/{name}.cer\n" - "on the engine host, please copy that content here " - "when required\n" + "\nTo sign the Reports certificate on the engine server, " + "please:\n\n" + "1. Copy {tmpcsr} from here to {enginecsr} on the engine " + "server.\n\n" + "2. Run on the engine server:\n\n" + "{enroll_command}\n\n" + "3. Copy {enginecert} from the engine server to some file " + "here. Provide the file name below.\n\n" + "See {url} for more details, including using an external " + "certificate authority." ).format( - fqdn=self.environment[osetupcons.ConfigEnv.FQDN], - name=oreportscons.Const.PKI_REPORTS_JBOSS_CERT_NAME, + tmpcsr=self._csr_file.name, + enginecsr='{pkireqdir}/{remote_name}.req'.format( + pkireqdir=oreportscons.FileLocations. + OVIRT_ENGINE_PKIREQUESTSDIR, + remote_name=remote_name, + ), + enroll_command=enroll_command, + enginecert='{pkicertdir}/{remote_name}.cer'.format( + pkicertdir=oreportscons.FileLocations. + OVIRT_ENGINE_PKICERTSDIR, + remote_name=remote_name, + ), + url="http://www.ovirt.org/Features/Separate-Reports-Host", ), ) - self.environment[ - oreportscons.ConfigEnv.JBOSS_CERTIFICATE_CHAIN - ] = self.dialog.queryMultiString( - name='REPORTS_JBOSS_CERTIFICATE_CHAIN', - note=_( - '\n\nPlease input Reports certificate chain that ' - 'matches certificate request, (issuer is not ' - 'mandatory, from intermediate and upper)\n\n' - ), - ) + goodcert = False + while not goodcert: + filename = self.dialog.queryString( + name='REPORTS_JBOSS_CERT_FILENAME', + note=_( + '\nPlease input the location of the file where you ' + 'copied the signed certificate in step 3 above: ' + ), + prompt=True, + ) + try: + with open(filename) as f: + cert = f.read() + goodcert = my_pubk == X509.load_cert_string( + cert + ).get_pubkey().as_pem(cipher=None) + self.environment[ + oreportscons.ConfigEnv.JBOSS_CERTIFICATE_CHAIN + ] = cert + if not goodcert: + self.logger.error( + _( + 'The certificate in {cert} does not match ' + 'the request in {req}. Please try again.' + ).format( + cert=filename, + req=self._csr_file.name, + ) + ) + except: + self.logger.error( + _( + 'Error while reading or parsing {cert}. ' + 'Please try again.' + ).format( + cert=filename, + ) + ) + self.logger.debug('Error reading cert', exc_info=True) + self.logger.info(_('Reports certificate read successfully')) @plugin.event( stage=plugin.Stages.STAGE_MISC, @@ -215,5 +273,19 @@ ) ) + @plugin.event( + stage=plugin.Stages.STAGE_CLEANUP, + ) + def _cleanup(self): + if self._csr_file is not None: + try: + os.unlink(self._csr_file.name) + except OSError as e: + self.logger.debug( + "Failed to delete '%s'", + self._csr_file.name, + exc_info=True, + ) + # vim: expandtab tabstop=4 shiftwidth=4 -- To view, visit http://gerrit.ovirt.org/33139 To unsubscribe, visit http://gerrit.ovirt.org/settings Gerrit-MessageType: newchange Gerrit-Change-Id: Ia5b3713330d6e11bd7c374e04f17660be79f2251 Gerrit-PatchSet: 1 Gerrit-Project: ovirt-reports Gerrit-Branch: ovirt-engine-reports-3.5 Gerrit-Owner: Yaniv Dary <[email protected]> Gerrit-Reviewer: Yedidyah Bar David <[email protected]> _______________________________________________ Engine-patches mailing list [email protected] http://lists.ovirt.org/mailman/listinfo/engine-patches
