From 8d7f67e08c8320712321501451e4a444b89a4423 Mon Sep 17 00:00:00 2001 From: Jan Cholasta Date: Wed, 9 Dec 2015 08:18:21 +0100 Subject: [PATCH] replica install: add remote connection check over API Add server_conncheck command which calls ipa-replica-conncheck --replica over oddjob. https://fedorahosted.org/freeipa/ticket/5497 Reviewed-By: Martin Babinsky Reviewed-By: Tomas Babej --- API.txt | 8 ++ VERSION | 4 +- freeipa.spec.in | 9 +- install/oddjob/Makefile.am | 3 + .../dbus-1/system.d/org.freeipa.server.conf | 21 +++ .../oddjob/etc/oddjobd.conf.d/ipa-server.conf | 20 +++ install/oddjob/org.freeipa.server.conncheck | 2 + install/tools/ipa-ca-install | 6 + install/tools/ipa-replica-conncheck | 129 +++++++++++++++--- .../updates/90-post_upgrade_plugins.update | 1 - ipalib/messages.py | 10 ++ ipalib/plugins/server.py | 70 +++++++++- ipaserver/install/adtrustinstance.py | 19 --- ipaserver/install/ca.py | 2 +- ipaserver/install/httpinstance.py | 26 ++++ ipaserver/install/installutils.py | 12 -- ipaserver/install/plugins/adtrust.py | 21 --- ipaserver/install/replication.py | 6 +- ipaserver/install/server/replicainstall.py | 6 +- ipaserver/install/server/upgrade.py | 1 + 20 files changed, 299 insertions(+), 77 deletions(-) create mode 100644 install/oddjob/etc/dbus-1/system.d/org.freeipa.server.conf create mode 100644 install/oddjob/etc/oddjobd.conf.d/ipa-server.conf create mode 100755 install/oddjob/org.freeipa.server.conncheck diff --git a/API.txt b/API.txt index 60c98c31a..15be32c19 100644 --- a/API.txt +++ b/API.txt @@ -3812,6 +3812,14 @@ option: Str('version?', exclude='webui') output: Entry('result', , Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None)) output: Output('summary', (, ), None) output: PrimaryKey('value', None, None) +command: server_conncheck +args: 2,1,3 +arg: Str('cn', attribute=True, cli_name='name', multivalue=False, primary_key=True, query=True, required=True) +arg: Str('remote_cn', cli_name='remote_name') +option: Str('version?', exclude='webui') +output: Output('result', , None) +output: Output('summary', (, ), None) +output: PrimaryKey('value', None, None) command: server_del args: 1,2,3 arg: Str('cn', attribute=True, cli_name='name', multivalue=True, primary_key=True, query=True, required=True) diff --git a/VERSION b/VERSION index 2446c92a0..ba89541d5 100644 --- a/VERSION +++ b/VERSION @@ -90,5 +90,5 @@ IPA_DATA_VERSION=20100614120000 # # ######################################################## IPA_API_VERSION_MAJOR=2 -IPA_API_VERSION_MINOR=161 -# Last change: pvoborni - topologysuffix: change iparepltopomanagedsuffix type +IPA_API_VERSION_MINOR=162 +# Last change: jcholast - replica install: add remote connection check over API diff --git a/freeipa.spec.in b/freeipa.spec.in index cc4c12214..72aaca991 100644 --- a/freeipa.spec.in +++ b/freeipa.spec.in @@ -162,6 +162,7 @@ Requires: p11-kit Requires: systemd-python Requires: %{etc_systemd_dir} Requires: gzip +Requires: oddjob Provides: %{alt_name}-server = %{version} Conflicts: %{alt_name}-server @@ -275,7 +276,6 @@ Requires: samba >= %{samba_version} Requires: samba-winbind Requires: libsss_idmap Requires: python-libsss_nss_idmap -Requires: oddjob Requires: python-sss # We use alternatives to divert winbind_krb5_locator.so plugin to libkrb5 # on the installes where server-trust-ad subpackage is installed because @@ -727,6 +727,8 @@ rm -rf %{buildroot} if [ $1 -gt 1 ] ; then /bin/systemctl condrestart certmonger.service 2>&1 || : fi +/bin/systemctl reload-or-try-restart dbus +/bin/systemctl reload-or-try-restart oddjobd %posttrans server @@ -752,6 +754,8 @@ if [ $1 = 0 ]; then # NOTE: systemd specific section /bin/systemctl --quiet stop ipa.service || : /bin/systemctl --quiet disable ipa.service || : + /bin/systemctl reload-or-try-restart dbus + /bin/systemctl reload-or-try-restart oddjobd # END fi @@ -909,6 +913,9 @@ fi %{_libexecdir}/ipa/ipa-ods-exporter %{_libexecdir}/ipa/ipa-httpd-kdcproxy %dir %{_libexecdir}/ipa/oddjob +%attr(0755,root,root) %{_libexecdir}/ipa/oddjob/org.freeipa.server.conncheck +%config(noreplace) %{_sysconfdir}/dbus-1/system.d/org.freeipa.server.conf +%config(noreplace) %{_sysconfdir}/oddjobd.conf.d/ipa-server.conf %dir %{_libdir}/ipa/certmonger %attr(755,root,root) %{_libdir}/ipa/certmonger/* # NOTE: systemd specific section diff --git a/install/oddjob/Makefile.am b/install/oddjob/Makefile.am index 5cdaf2b29..fb64f6cf4 100644 --- a/install/oddjob/Makefile.am +++ b/install/oddjob/Makefile.am @@ -6,14 +6,17 @@ dbusconfdir = $(sysconfdir)/dbus-1/system.d oddjob_SCRIPTS = \ com.redhat.idm.trust-fetch-domains \ + org.freeipa.server.conncheck \ $(NULL) dbusconf_DATA = \ etc/dbus-1/system.d/oddjob-ipa-trust.conf \ + etc/dbus-1/system.d/org.freeipa.server.conf \ $(NULL) oddjobconf_DATA = \ etc/oddjobd.conf.d/oddjobd-ipa-trust.conf \ + etc/oddjobd.conf.d/ipa-server.conf \ $(NULL) diff --git a/install/oddjob/etc/dbus-1/system.d/org.freeipa.server.conf b/install/oddjob/etc/dbus-1/system.d/org.freeipa.server.conf new file mode 100644 index 000000000..b2cbf746f --- /dev/null +++ b/install/oddjob/etc/dbus-1/system.d/org.freeipa.server.conf @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + diff --git a/install/oddjob/etc/oddjobd.conf.d/ipa-server.conf b/install/oddjob/etc/oddjobd.conf.d/ipa-server.conf new file mode 100644 index 000000000..3f806966b --- /dev/null +++ b/install/oddjob/etc/oddjobd.conf.d/ipa-server.conf @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + diff --git a/install/oddjob/org.freeipa.server.conncheck b/install/oddjob/org.freeipa.server.conncheck new file mode 100755 index 000000000..ab7a46a86 --- /dev/null +++ b/install/oddjob/org.freeipa.server.conncheck @@ -0,0 +1,2 @@ +#!/bin/sh +exec /usr/sbin/ipa-replica-conncheck --replica "$1" 2>&1 diff --git a/install/tools/ipa-ca-install b/install/tools/ipa-ca-install index b1d58f750..0b8f28cb7 100755 --- a/install/tools/ipa-ca-install +++ b/install/tools/ipa-ca-install @@ -149,8 +149,10 @@ def install_replica(safe_options, options, filename): config.ca_ds_port = 389 config.top_dir = tempfile.mkdtemp("ipa") config.dir = config.top_dir + cafile = paths.IPA_CA_CRT else: config = create_replica_config(dirman_password, filename, options) + cafile = config.dir + '/ca.crt' global REPLICA_INFO_TOP_DIR REPLICA_INFO_TOP_DIR = config.top_dir @@ -176,6 +178,10 @@ def install_replica(safe_options, options, filename): options.dm_password = config.dirman_password options.host_name = config.host_name options.subject = config.subject_base + if os.path.exists(cafile): + options.ca_cert_file = cafile + else: + options.ca_cert_file = None ca.install_check(True, config, options) if options.promote: diff --git a/install/tools/ipa-replica-conncheck b/install/tools/ipa-replica-conncheck index a67837c54..10e3437bd 100755 --- a/install/tools/ipa-replica-conncheck +++ b/install/tools/ipa-replica-conncheck @@ -22,11 +22,12 @@ from __future__ import print_function from ipapython.config import IPAOptionParser from ipapython import version -from ipapython import ipautil +from ipapython import ipautil, certdb from ipapython.ipautil import CalledProcessError +from ipalib import api, errors, x509 from ipaserver.install import installutils import ipaclient.ipachangeconf -from optparse import OptionGroup +from optparse import OptionGroup, OptionValueError from ipapython.ipa_log_manager import * import sys import os @@ -40,6 +41,7 @@ from socket import SOCK_STREAM, SOCK_DGRAM import distutils.spawn from ipaplatform.paths import paths import gssapi +from nss import nss CONNECT_TIMEOUT = 5 RESPONDERS = [ ] @@ -106,6 +108,30 @@ def print_info(msg): print(msg) def parse_options(): + def ca_cert_file_callback(option, opt, value, parser): + if not os.path.exists(value): + raise OptionValueError( + "%s option '%s' does not exist" % (opt, value)) + if not os.path.isfile(value): + raise OptionValueError( + "%s option '%s' is not a file" % (opt, value)) + if not os.path.isabs(value): + raise OptionValueError( + "%s option '%s' is not an absolute file path" % (opt, value)) + + initialized = nss.nss_is_initialized() + try: + x509.load_certificate_list_from_file(value) + except Exception: + raise OptionValueError( + "%s option '%s' is not a valid certificate file" % + (opt, value)) + finally: + if not initialized: + nss.nss_shutdown() + + parser.values.ca_cert_file = value + parser = IPAOptionParser(version=version.VERSION) replica_group = OptionGroup(parser, "on-replica options") @@ -123,6 +149,10 @@ def parse_options(): default=None, help="Principal to use to log in to remote master") replica_group.add_option("-w", "--password", dest="password", sensitive=True, help="Password for the principal"), + replica_group.add_option("--ca-cert-file", dest="ca_cert_file", + type="string", action="callback", + callback=ca_cert_file_callback, + help="load the CA certificate from this file") parser.add_option_group(replica_group) @@ -418,22 +448,89 @@ def main(): if returncode != 0: raise RuntimeError("Could not get ticket for master server: %s" % stderr) - user = principal.partition('@')[0] - ssh = SshExec(user, options.master) + try: + print_info("Check RPC connection to remote master") - print_info("Check SSH connection to remote master") - stdout, stderr, returncode = ssh('echo OK', verbose=True) - if returncode != 0: - print('Could not SSH into remote host. Error output:') - for line in stderr.splitlines(): - print(' %s' % line) - raise RuntimeError('Could not SSH to remote host.') + xmlrpc_uri = ('https://%s/ipa/xml' % + ipautil.format_netloc(options.master)) + api.bootstrap(context='client', xmlrpc_uri=xmlrpc_uri) + api.finalize() - print_info("Execute check on remote master") - stdout, stderr, returncode = ssh( - "/usr/sbin/ipa-replica-conncheck " + - " ".join(remote_check_opts)) - print_info(stdout) + if options.ca_cert_file: + nss_dir = None + else: + nss_dir = paths.IPA_NSSDB_DIR + + with certdb.NSSDatabase(nss_dir) as nss_db: + if options.ca_cert_file: + nss_dir = nss_db.secdir + + password = ipautil.ipa_generate_password() + password_file = ipautil.write_tmp_file(password) + nss_db.create_db(password_file.name) + + ca_certs = x509.load_certificate_list_from_file( + options.ca_cert_file, dbdir=nss_db.secdir) + for ca_cert in ca_certs: + nss_db.add_cert( + ca_cert.der_data, str(ca_cert.subject), 'C,,') + del ca_cert + del ca_certs + else: + nss_dir = None + + try: + api.Backend.rpcclient.connect(nss_dir=nss_dir) + api.Command.ping() + except Exception as e: + print_info( + "Could not connect to the remote host: %s" % e) + raise + + print_info("Execute check on remote master") + try: + result = api.Backend.rpcclient.forward( + 'server_conncheck', + ipautil.fsdecode(options.master), + ipautil.fsdecode(options.hostname), + version=u'2.162', + ) + except (errors.CommandError, errors.NetworkError) as e: + print_info( + "Remote master does not support check over RPC: " + "%s" % e) + raise + except errors.PublicError as e: + returncode = 1 + stderr = e + else: + for message in result['messages']: + print_info(message['message']) + returncode = int(not result['result']) + stderr = ("ipa-replica-conncheck returned non-zero " + "exit code") + finally: + if api.Backend.rpcclient.isconnected(): + api.Backend.rpcclient.disconnect() + except Exception: + print_info("Retrying using SSH...") + + user = principal.partition('@')[0] + ssh = SshExec(user, options.master) + + print_info("Check SSH connection to remote master") + stdout, stderr, returncode = ssh('echo OK', verbose=True) + if returncode != 0: + print('Could not SSH into remote host. Error output:') + for line in stderr.splitlines(): + print(' %s' % line) + raise RuntimeError('Could not SSH to remote host.') + + print_info("Execute check on remote master") + stdout, stderr, returncode = ssh( + "/usr/sbin/ipa-replica-conncheck " + + " ".join(remote_check_opts)) + print_info(stdout) if returncode != 0: raise RuntimeError("Remote master check failed with following error message(s):\n%s" % stderr) else: diff --git a/install/updates/90-post_upgrade_plugins.update b/install/updates/90-post_upgrade_plugins.update index 2089b3320..626255af7 100644 --- a/install/updates/90-post_upgrade_plugins.update +++ b/install/updates/90-post_upgrade_plugins.update @@ -19,4 +19,3 @@ plugin: update_managed_post plugin: update_managed_permissions plugin: update_idrange_baserid plugin: update_passync_privilege_update -plugin: update_oddjobd_for_adtrust diff --git a/ipalib/messages.py b/ipalib/messages.py index 44fee6d15..078e13f59 100644 --- a/ipalib/messages.py +++ b/ipalib/messages.py @@ -321,6 +321,16 @@ class CommandDeprecatedWarning(PublicMessage): format = _(u"'%(command)s' is deprecated. %(additional_info)s") +class ExternalCommandOutput(PublicMessage): + """ + **13016** Line of output from an external command. + """ + + errno = 13016 + type = "info" + format = _("%(line)s") + + def iter_messages(variables, base): """Return a tuple with all subclasses """ diff --git a/ipalib/plugins/server.py b/ipalib/plugins/server.py index 8284a5805..6286c5959 100644 --- a/ipalib/plugins/server.py +++ b/ipalib/plugins/server.py @@ -5,11 +5,15 @@ import string import os -from ipalib import api +import dbus +import dbus.mainloop.glib + +from ipalib import api, crud, errors, messages from ipalib import Int, Str from ipalib.plugable import Registry from ipalib.plugins.baseldap import * from ipalib.plugins import baseldap +from ipalib.request import context from ipalib import _, ngettext __doc__ = _(""" @@ -188,3 +192,67 @@ class server_del(LDAPDelete): __doc__ = _('Delete IPA server.') NO_CLI = True msg_summary = _('Deleted IPA server "%(value)s"') + + +@register() +class server_conncheck(crud.PKQuery): + __doc__ = _("Check connection to remote IPA server.") + + NO_CLI = True + + takes_args = ( + Str( + 'remote_cn', + cli_name='remote_name', + label=_('Remote server name'), + doc=_('Remote IPA server hostname'), + ), + ) + + has_output = output.standard_value + + def execute(self, *keys, **options): + # the server must be the local host + if keys[-2] != api.env.host: + raise errors.ValidationError( + name='cn', error=_("must be \"%s\"") % api.env.host) + + # the server entry must exist + try: + self.obj.get_dn_if_exists(*keys[:-1]) + except errors.NotFound: + self.obj.handle_not_found(keys[-2]) + + # the user must have the Replication Administrators privilege + privilege = u'Replication Administrators' + privilege_dn = self.api.Object.privilege.get_dn(privilege) + ldap = self.obj.backend + filter = ldap.make_filter( + {'krbprincipalname': context.principal, 'memberof': privilege_dn}, + rules=ldap.MATCH_ALL) + try: + ldap.find_entries(base_dn=self.api.env.basedn, filter=filter) + except errors.NotFound: + raise errors.ACIError( + info=_("not allowed to perform server connection check")) + + dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) + + bus = dbus.SystemBus() + obj = bus.get_object('org.freeipa.server', '/', + follow_name_owner_changes=True) + server = dbus.Interface(obj, 'org.freeipa.server') + + ret, stdout, stderr = server.conncheck(keys[-1]) + + result = dict( + result=(ret == 0), + value=keys[-2], + ) + + for line in stdout.splitlines(): + messages.add_message(options['version'], + result, + messages.ExternalCommandOutput(line=line)) + + return result diff --git a/ipaserver/install/adtrustinstance.py b/ipaserver/install/adtrustinstance.py index 813d48e50..118f848cf 100644 --- a/ipaserver/install/adtrustinstance.py +++ b/ipaserver/install/adtrustinstance.py @@ -726,9 +726,6 @@ class ADTRUSTInstance(service.Service): except Exception as e: root_logger.critical("Enabling nsswitch support in slapi-nis failed with error '%s'" % e) - def __enable_and_start_oddjobd(self): - installutils.enable_and_start_oddjobd(self.sstore) - def __start(self): try: self.start() @@ -881,7 +878,6 @@ class ADTRUSTInstance(service.Service): self.step("adding Default Trust View", self.__add_default_trust_view) self.step("setting SELinux booleans", \ self.__configure_selinux_for_smbd) - self.step("enabling oddjobd", self.__enable_and_start_oddjobd) self.step("starting CIFS services", self.__start) if self.add_sids: @@ -911,21 +907,6 @@ class ADTRUSTInstance(service.Service): except Exception: pass - # Restore oddjobd to its original state - oddjobd = services.service('oddjobd') - - if not self.sstore.restore_state('oddjobd', 'running'): - try: - oddjobd.stop() - except Exception: - pass - - if not self.sstore.restore_state('oddjobd', 'enabled'): - try: - oddjobd.disable() - except Exception: - pass - # Since we do not guarantee restoring back to working samba state, # we should not restore smb.conf diff --git a/ipaserver/install/ca.py b/ipaserver/install/ca.py index 1a51ebc8c..36a7d57b9 100644 --- a/ipaserver/install/ca.py +++ b/ipaserver/install/ca.py @@ -40,7 +40,7 @@ def install_check(standalone, replica_config, options): replica_conn_check( replica_config.master_host_name, host_name, realm_name, True, replica_config.ca_ds_port, options.admin_password, - principal=principal) + principal=principal, ca_cert_file=options.ca_cert_file) if options.skip_schema_check or options.promote: root_logger.info("Skipping CA DS schema check") diff --git a/ipaserver/install/httpinstance.py b/ipaserver/install/httpinstance.py index 1b68573d7..b51cc4a00 100644 --- a/ipaserver/install/httpinstance.py +++ b/ipaserver/install/httpinstance.py @@ -163,6 +163,7 @@ class HTTPInstance(service.Service): self.step("enable KDC proxy", self.enable_kdcproxy) self.step("restarting httpd", self.__start) self.step("configuring httpd to start on boot", self.__enable) + self.step("enabling oddjobd", self.enable_and_start_oddjobd) self.start_creation(runtime=60) @@ -441,6 +442,17 @@ class HTTPInstance(service.Service): f.write(http_txt) os.chmod(target_fname, 0o644) + def enable_and_start_oddjobd(self): + oddjobd = services.service('oddjobd') + self.sstore.backup_state('oddjobd', 'running', oddjobd.is_running()) + self.sstore.backup_state('oddjobd', 'enabled', oddjobd.is_enabled()) + + try: + oddjobd.enable() + oddjobd.start() + except Exception as e: + root_logger.critical("Unable to start oddjobd: {0}".format(str(e))) + def uninstall(self): if self.is_configured(): self.print_msg("Unconfiguring web server") @@ -448,6 +460,20 @@ class HTTPInstance(service.Service): running = self.restore_state("running") enabled = self.restore_state("enabled") + # Restore oddjobd to its original state + oddjobd = services.service('oddjobd') + + if not self.sstore.restore_state('oddjobd', 'running'): + try: + oddjobd.stop() + except Exception: + pass + + if not self.sstore.restore_state('oddjobd', 'enabled'): + try: + oddjobd.disable() + except Exception: + pass self.stop_tracking_certificates() diff --git a/ipaserver/install/installutils.py b/ipaserver/install/installutils.py index 156c8a5eb..bdbe2e38b 100644 --- a/ipaserver/install/installutils.py +++ b/ipaserver/install/installutils.py @@ -1103,18 +1103,6 @@ def realm_to_ldapi_uri(realm_name): return 'ldapi://' + ldapurl.ldapUrlEscape(socketname) -def enable_and_start_oddjobd(sstore): - oddjobd = services.service('oddjobd') - sstore.backup_state('oddjobd', 'running', oddjobd.is_running()) - sstore.backup_state('oddjobd', 'enabled', oddjobd.is_enabled()) - - try: - oddjobd.enable() - oddjobd.start() - except Exception as e: - root_logger.critical("Unable to start oddjobd: {0}".format(str(e))) - - def install_service_keytab(principal, server, path, force_service_add=False): try: diff --git a/ipaserver/install/plugins/adtrust.py b/ipaserver/install/plugins/adtrust.py index 45bcc5f2f..5510dfd36 100644 --- a/ipaserver/install/plugins/adtrust.py +++ b/ipaserver/install/plugins/adtrust.py @@ -164,26 +164,5 @@ class update_default_trust_view(Updater): return False, [update] - -class update_oddjobd_for_adtrust(Updater): - """ - Enables and starts oddjobd daemon if ipa-adtrust-install has been run - on this system. - """ - - def execute(self, **options): - adtrust_is_enabled = self.api.Command['adtrust_is_enabled']()['result'] - - if adtrust_is_enabled: - self.log.debug('Try to enable and start oddjobd') - sstore = sysrestore.StateFile(paths.SYSRESTORE) - installutils.enable_and_start_oddjobd(sstore) - else: - self.log.debug('ADTrust not configured on this server, do not ' - 'start and enable oddjobd') - - return False, [] - api.register(update_default_range) api.register(update_default_trust_view) -api.register(update_oddjobd_for_adtrust) diff --git a/ipaserver/install/replication.py b/ipaserver/install/replication.py index aaa841ca6..ee1b50724 100644 --- a/ipaserver/install/replication.py +++ b/ipaserver/install/replication.py @@ -67,7 +67,7 @@ STRIP_ATTRS = ('modifiersName', def replica_conn_check(master_host, host_name, realm, check_ca, dogtag_master_ds_port, admin_password=None, - principal="admin"): + principal="admin", ca_cert_file=None): """ Check the ports used by the replica both locally and remotely to be sure that replication will work. @@ -89,6 +89,10 @@ def replica_conn_check(master_host, host_name, realm, check_ca, if check_ca and dogtag_master_ds_port == 7389: args.append('--check-ca') + + if ca_cert_file: + args.extend(["--ca-cert-file", ca_cert_file]) + (stdin, stderr, returncode) = ipautil.run( args, raiseonerr=False, capture_output=False, nolog=nolog) diff --git a/ipaserver/install/server/replicainstall.py b/ipaserver/install/server/replicainstall.py index dc8f31db5..aca25bbe7 100644 --- a/ipaserver/install/server/replicainstall.py +++ b/ipaserver/install/server/replicainstall.py @@ -668,7 +668,8 @@ def install_check(installer): if not options.skip_conncheck: replica_conn_check( config.master_host_name, config.host_name, config.realm_name, - options.setup_ca, config.ca_ds_port, options.admin_password) + options.setup_ca, config.ca_ds_port, options.admin_password, + ca_cert_file=cafile) installer._remote_api = remote_api installer._fstore = fstore @@ -1206,7 +1207,8 @@ def promote_check(installer): replica_conn_check( config.master_host_name, config.host_name, config.realm_name, options.setup_ca, 389, - options.admin_password, principal=options.principal) + options.admin_password, principal=options.principal, + ca_cert_file=cafile) finally: os.environ['KRB5CCNAME'] = ccache diff --git a/ipaserver/install/server/upgrade.py b/ipaserver/install/server/upgrade.py index e193b85b9..8ac235593 100644 --- a/ipaserver/install/server/upgrade.py +++ b/ipaserver/install/server/upgrade.py @@ -1478,6 +1478,7 @@ def upgrade_configuration(): http.configure_selinux_for_httpd() http.change_mod_nss_port_from_http() http.configure_certmonger_renewal_guard() + http.enable_and_start_oddjobd() ds.configure_dirsrv_ccache()