Enable upgrades from a mod_nss-installed master to mod_ssl

The existing private/public keys are migrated to PEM files
via a PKCS#12 temporary file. This should work for both
IPA-generated and user-provided server certificates.

Related: https://pagure.io/freeipa/issue/3757
Reviewed-By: Christian Heimes <cheimes@redhat.com>
Reviewed-By: Stanislav Laznicka <slaznick@redhat.com>
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
This commit is contained in:
Rob Crittenden 2017-12-05 09:22:34 -05:00 committed by Stanislav Laznicka
parent 5c64e28512
commit 4596674481
No known key found for this signature in database
GPG Key ID: C98C414936B1A7F3
2 changed files with 88 additions and 59 deletions

View File

@ -116,6 +116,8 @@ class HTTPInstance(service.Service):
self.step("stopping httpd", self.__stop) self.step("stopping httpd", self.__stop)
self.step("backing up ssl.conf", self.backup_ssl_conf) self.step("backing up ssl.conf", self.backup_ssl_conf)
self.step("disabling nss.conf", self.disable_nss_conf) self.step("disabling nss.conf", self.disable_nss_conf)
self.step("configuring mod_ssl certificate paths",
self.configure_mod_ssl_certs)
self.step("setting mod_ssl protocol list to TLSv1.0 - TLSv1.2", self.step("setting mod_ssl protocol list to TLSv1.0 - TLSv1.2",
self.set_mod_ssl_protocol) self.set_mod_ssl_protocol)
self.step("configuring mod_ssl log directory", self.step("configuring mod_ssl log directory",
@ -198,6 +200,11 @@ class HTTPInstance(service.Service):
tasks.configure_http_gssproxy_conf(IPAAPI_USER) tasks.configure_http_gssproxy_conf(IPAAPI_USER)
services.knownservices.gssproxy.restart() services.knownservices.gssproxy.restart()
def get_mod_nss_nickname(self):
cert = installutils.get_directive(paths.HTTPD_NSS_CONF, 'NSSNickname')
nickname = installutils.unquote_directive_value(cert, quote_char="'")
return nickname
def backup_ssl_conf(self): def backup_ssl_conf(self):
self.fstore.backup_file(paths.HTTPD_SSL_CONF) self.fstore.backup_file(paths.HTTPD_SSL_CONF)
@ -225,7 +232,6 @@ class HTTPInstance(service.Service):
if sysupgrade.get_upgrade_state('http', OCSP_ENABLED) is None: if sysupgrade.get_upgrade_state('http', OCSP_ENABLED) is None:
self.__disable_mod_ssl_ocsp() self.__disable_mod_ssl_ocsp()
sysupgrade.set_upgrade_state('http', OCSP_ENABLED, False) sysupgrade.set_upgrade_state('http', OCSP_ENABLED, False)
>>>>>>> 754c26ef6... Revisions for ocsp
def __disable_mod_ssl_ocsp(self): def __disable_mod_ssl_ocsp(self):
aug = Augeas(flags=Augeas.NO_LOAD | Augeas.NO_MODL_AUTOLOAD) aug = Augeas(flags=Augeas.NO_LOAD | Augeas.NO_MODL_AUTOLOAD)
@ -250,6 +256,35 @@ class HTTPInstance(service.Service):
aug.set(ocsp_comment, '{} {}'.format(OCSP_DIRECTIVE, ocsp_state)) aug.set(ocsp_comment, '{} {}'.format(OCSP_DIRECTIVE, ocsp_state))
aug.save() aug.save()
# def disable_mod_nss_ocsp(self):
# if sysupgrade.get_upgrade_state('http', NSS_OCSP_ENABLED) is None:
# self.__disable_mod_nss_ocsp()
# sysupgrade.set_upgrade_state('http', NSS_OCSP_ENABLED, False)
# def __disable_mod_nss_ocsp(self):
# aug = Augeas(flags=Augeas.NO_LOAD | Augeas.NO_MODL_AUTOLOAD)
#
# aug.set('/augeas/load/Httpd/lens', 'Httpd.lns')
# aug.set('/augeas/load/Httpd/incl', paths.HTTPD_NSS_CONF)
# aug.load()
#
# path = '/files{}/VirtualHost'.format(paths.HTTPD_NSS_CONF)
# ocsp_path = '{}/directive[.="{}"]'.format(path, OCSP_DIRECTIVE)
# ocsp_arg = '{}/arg'.format(ocsp_path)
# ocsp_comment = '{}/#comment[.="{}"]'.format(path, OCSP_DIRECTIVE)
#
# ocsp_dir = aug.get(ocsp_path)
#
# # there is NSSOCSP directive in nss.conf file, comment it
# # otherwise just do nothing
# if ocsp_dir is not None:
# ocsp_state = aug.get(ocsp_arg)
# aug.remove(ocsp_arg)
# aug.rename(ocsp_path, '#comment')
# aug.set(ocsp_comment, '{} {}'.format(OCSP_DIRECTIVE, ocsp_state))
# aug.save()
def __add_include(self): def __add_include(self):
"""This should run after __set_mod_nss_port so is already backed up""" """This should run after __set_mod_nss_port so is already backed up"""
if installutils.update_file(paths.HTTPD_SSL_CONF, if installutils.update_file(paths.HTTPD_SSL_CONF,
@ -347,9 +382,9 @@ class HTTPInstance(service.Service):
finally: finally:
if prev_helper is not None: if prev_helper is not None:
certmonger.modify_ca_helper('IPA', prev_helper) certmonger.modify_ca_helper('IPA', prev_helper)
self.cert = x509.load_der_x509_certificate( self.cert = x509.load_certificate_from_file(
paths.HTTPD_CERT_FILE paths.HTTPD_CERT_FILE
) )
if prev_helper is not None: if prev_helper is not None:
self.add_cert_to_service() self.add_cert_to_service()
@ -363,7 +398,10 @@ class HTTPInstance(service.Service):
# store the CA cert nickname so that we can publish it later on # store the CA cert nickname so that we can publish it later on
# self.cacert_nickname = db.cacert_name # self.cacert_nickname = db.cacert_name
# FIXME: figure this out too # FIXME: figure this out too
sysupgrade.set_upgrade_state('ssl.conf', 'migrated_to_mod_ssl', True)
def configure_mod_ssl_certs(self):
"""Configure the mod_ssl certificate directives"""
installutils.set_directive(paths.HTTPD_SSL_CONF, installutils.set_directive(paths.HTTPD_SSL_CONF,
'SSLCertificateFile', 'SSLCertificateFile',
paths.HTTPD_CERT_FILE, False) paths.HTTPD_CERT_FILE, False)
@ -499,14 +537,14 @@ class HTTPInstance(service.Service):
str(e)) str(e))
def start_tracking_certificates(self): def start_tracking_certificates(self):
cert = x509.load_pem_x509_certificate(paths.HTTPD_CERT_FILE) cert = x509.load_certificate_from_file(paths.HTTPD_CERT_FILE)
if certs.is_ipa_issued_cert(api, cert): if certs.is_ipa_issued_cert(api, cert):
request_id = certmonger.start_tracking( request_id = certmonger.start_tracking(
certpath=(paths.HTTPD_CERT_FILE, paths.HTTPD_CERT_KEY), certpath=(paths.HTTPD_CERT_FILE, paths.HTTPD_KEY_FILE),
post_command='restart_httpd' post_command='restart_httpd', storage='FILE'
) )
subject = str(DN(cert.subject)) subject = str(DN(cert.subject))
certmonger.add_principal(request_id, principal) certmonger.add_principal(request_id, self.principal)
certmonger.add_subject(request_id, subject) certmonger.add_subject(request_id, subject)
else: else:
logger.debug("Will not track HTTP server cert %s as it is not " logger.debug("Will not track HTTP server cert %s as it is not "
@ -530,3 +568,32 @@ class HTTPInstance(service.Service):
remote_ldap.simple_bind(ipaldap.DIRMAN_DN, remote_ldap.simple_bind(ipaldap.DIRMAN_DN,
self.dm_password) self.dm_password)
replication.wait_for_entry(remote_ldap, service_dn, timeout=60) replication.wait_for_entry(remote_ldap, service_dn, timeout=60)
def migrate_to_mod_ssl(self):
"""For upgrades only, migrate from mod_nss to mod_ssl"""
db = certs.CertDB(api.env.realm, nssdir=paths.HTTPD_ALIAS_DIR)
nickname = self.get_mod_nss_nickname()
with tempfile.NamedTemporaryFile() as temp:
pk12_password = ipautil.ipa_generate_password()
pk12_pwdfile = ipautil.write_tmp_file(pk12_password)
db.export_pkcs12(temp.name, pk12_pwdfile.name, nickname)
certs.install_pem_from_p12(temp.name,
pk12_password,
paths.HTTPD_CERT_FILE)
certs.install_key_from_p12(temp.name,
pk12_password,
paths.HTTPD_KEY_FILE)
self.configure_mod_ssl_certs()
self.set_mod_ssl_protocol()
self.set_mod_ssl_logdir()
self.cert = x509.load_certificate_from_file(paths.HTTPD_CERT_FILE)
if self.ca_is_configured:
db.untrack_server_cert(nickname)
self.start_tracking_certificates()
# remove nickname and CA certs from NSS db
self.disable_nss_conf()

View File

@ -13,7 +13,7 @@ import fileinput
import sys import sys
from augeas import Augeas from augeas import Augeas
import dns.exception import dns.exception
from ipalib import api from ipalib import api, x509
from ipalib.install import certmonger, sysrestore from ipalib.install import certmonger, sysrestore
import SSSDConfig import SSSDConfig
import ipalib.util import ipalib.util
@ -58,6 +58,7 @@ else:
if six.PY3: if six.PY3:
unicode = str unicode = str
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -987,13 +988,12 @@ def certificate_renewal_update(ca, ds, http):
return False return False
# Check the http server cert if issued by IPA # Check the http server cert if issued by IPA
http_nickname = http.get_mod_nss_nickname() cert = x509.load_certificate_from_file(paths.HTTPD_CERT_FILE)
http_db = certs.CertDB(api.env.realm, nssdir=paths.HTTPD_ALIAS_DIR) if certs.is_ipa_issued_cert(api, cert):
if http_db.is_ipa_issued_cert(api, http_nickname):
requests.append( requests.append(
{ {
'cert-database': paths.HTTPD_ALIAS_DIR, 'cert-file': paths.HTTPD_CERT_FILE,
'cert-nickname': http_nickname, 'key-storage': paths.HTTPD_KEY_FILE,
'ca-name': 'IPA', 'ca-name': 'IPA',
'cert-postsave-command': template % 'restart_httpd', 'cert-postsave-command': template % 'restart_httpd',
} }
@ -1436,38 +1436,18 @@ def fix_trust_flags():
sysupgrade.set_upgrade_state('http', 'fix_trust_flags', True) sysupgrade.set_upgrade_state('http', 'fix_trust_flags', True)
def update_mod_nss_protocol(http): def migrate_to_mod_ssl(http):
logger.info('[Updating mod_nss protocol versions]') logger.info('[Migrating from mod_nss to mod_ssl]')
if sysupgrade.get_upgrade_state('nss.conf', 'protocol_updated_tls12'): if sysupgrade.get_upgrade_state('ssl.conf', 'migrated_to_mod_ssl'):
logger.info("Protocol versions already updated") logger.info("Already migrated to mod_ssl")
return return
http.set_mod_nss_protocol() http.migrate_to_mod_ssl()
sysupgrade.set_upgrade_state('nss.conf', 'protocol_updated_tls12', True) sysupgrade.set_upgrade_state('ssl.conf', 'migrated_to_mod_ssl', True)
def disable_mod_nss_ocsp(http):
logger.info('[Updating mod_nss enabling OCSP]')
http.disable_mod_nss_ocsp()
def update_mod_nss_cipher_suite(http):
logger.info('[Updating mod_nss cipher suite]')
revision = sysupgrade.get_upgrade_state('nss.conf', 'cipher_suite_updated')
if revision and revision >= httpinstance.NSS_CIPHER_REVISION:
logger.debug("Cipher suite already updated")
return
http.set_mod_nss_cipher_suite()
sysupgrade.set_upgrade_state(
'nss.conf',
'cipher_suite_updated',
httpinstance.NSS_CIPHER_REVISION)
def update_ipa_httpd_service_conf(http): def update_ipa_httpd_service_conf(http):
logger.info('[Updating HTTPD service IPA configuration]') logger.info('[Updating HTTPD service IPA configuration]')
@ -1628,21 +1608,6 @@ def enable_certauth(krb):
aug.close() aug.close()
def disable_httpd_system_trust(http):
ca_certs = []
db = certs.CertDB(api.env.realm, nssdir=paths.HTTPD_ALIAS_DIR)
for nickname, trust_flags in db.list_certs():
if not trust_flags.has_key:
cert = db.get_cert_from_db(nickname)
if cert:
ca_certs.append((cert, nickname, trust_flags))
if http.disable_system_trust():
for cert, nickname, trust_flags in ca_certs:
db.add_cert(cert, nickname, trust_flags)
def upgrade_configuration(): def upgrade_configuration():
""" """
Execute configuration upgrade of the IPA services Execute configuration upgrade of the IPA services
@ -1797,12 +1762,9 @@ def upgrade_configuration():
http.enable_kdcproxy() http.enable_kdcproxy()
http.stop() http.stop()
disable_httpd_system_trust(http)
update_ipa_httpd_service_conf(http) update_ipa_httpd_service_conf(http)
update_ipa_http_wsgi_conf(http) update_ipa_http_wsgi_conf(http)
update_mod_nss_protocol(http) migrate_to_mod_ssl(http)
update_mod_nss_cipher_suite(http)
disable_mod_nss_ocsp(http)
fix_trust_flags() fix_trust_flags()
update_http_keytab(http) update_http_keytab(http)
http.configure_gssproxy() http.configure_gssproxy()