From fb28dfff93b382207635eaa776eb31f99b0f5b2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tibor=20Dudl=C3=A1k?= Date: Mon, 5 Mar 2018 16:42:39 +0100 Subject: [PATCH] FreeIPA server is time synchronization client only This will change behaviour that FreeIPA server will be no more ntpd server and time service is no longer part of FreeIPA topology. As dependency for ntpd was completely removed, and there is only dependency for chrony, FreeIPA will configure every host to became chronyd service's clients. FreeIPA have not supported --ntp-server option now it must to support client configuration of chrony. Configuration of chrony is moved to client-install therefore NTP related options are now passed to the ipa-client-install script method sync_time which now handles configuration of chrony. Server installation has to configure chrony before handling certificates so there is call to configure chrony outside of using server's statestore and filestore. Removed behavior that there is always --no-ntp option set. Resolves: https://pagure.io/freeipa/issue/7024 Reviewed-By: Rob Crittenden --- ipaclient/install/client.py | 143 +++++++++++++++------------ ipaclient/install/ntpconf.py | 4 +- ipaserver/install/server/__init__.py | 1 - ipaserver/install/server/install.py | 11 ++- 4 files changed, 93 insertions(+), 66 deletions(-) diff --git a/ipaclient/install/client.py b/ipaclient/install/client.py index be2788298..4bdb99415 100644 --- a/ipaclient/install/client.py +++ b/ipaclient/install/client.py @@ -1987,7 +1987,7 @@ def install_check(options): "using 'ipa-client-install --uninstall'.") raise ScriptError(rval=CLIENT_ALREADY_CONFIGURED) - if options.conf_ntp and not options.on_master and not options.force_chrony: + if options.conf_ntp and not options.force_chrony: try: ntpconf.check_timedate_services() except ntpconf.NTPConflictingService as e: @@ -2345,6 +2345,71 @@ def update_ipa_nssdb(): (nickname, sys_db.secdir, e)) +def sync_time(options, fstore, statestore): + # We assume that NTP servers are discoverable through SRV records + # in the DNS. + # If that fails, we try to sync directly with IPA server, + # assuming it runs NTP + + # disable other time&date services first + if options.force_chrony: + ntpconf.force_chrony(statestore) + + print("Synchronizing time") + print(" [1/1]: Configuring chrony client") + logger.info('Synchronizing time with KDC...') + + if not options.ntp_servers: + ds = ipadiscovery.IPADiscovery() + ntp_servers = ds.ipadns_search_srv(cli_domain, '_ntp._udp', + None, break_on_first=False) + else: + ntp_servers = options.ntp_servers + + if ntp_servers: + synced_time = ntpconf.configure_chrony(ntp_servers, fstore, statestore) + else: + synced_time = False + logger.warning("No SRV records of NTP servers found nor NTP server " + "address was privided. Skipping chrony configuration") + + if not synced_time: + print("Warning: IPA Server was unable to sync time with chrony!") + print(" Time synchronization is required " + "for IPA Server to work correctly") + logger.warning( + "Unable to sync time with chrony server, assuming the time " + "is in sync. Please check that 123 UDP port is opened, " + "and any time server is on network.") + + +def restore_time_sync(statestore, fstore): + chrony_configured = statestore.has_state('ntp') + if chrony_configured: + chrony_enabled = statestore.restore_state('ntp', 'enabled') + restored = False + + try: + # Restore might fail due to file missing in backup + # the reason for it might be that freeipa-client was updated + # to this version but not unenrolled/enrolled again + # In such case it is OK to fail + restored = fstore.restore_file(paths.CHRONY_CONF) + except Exception: + pass + + if not chrony_enabled: + services.knownservices.chronyd.stop() + services.knownservices.chronyd.disable() + elif restored: + services.knownservices.chronyd.restart() + + try: + ntpconf.restore_forced_chronyd(statestore) + except CalledProcessError as e: + logger.error('Failed to restore time synchronization service: %s', e) + + def install(options): try: _install(options) @@ -2390,42 +2455,14 @@ def _install(options): tasks.backup_hostname(fstore, statestore) tasks.set_hostname(options.hostname) - if not options.on_master and options.conf_ntp: - # Attempt to sync time with IPA server. - # If we're skipping NTP configuration, we also skip the time sync here. - # We assume that NTP servers are discoverable through SRV records - # in the DNS. - # If that fails, we try to sync directly with IPA server, - # assuming it runs NTP - - # disable other time&date services first - if options.force_chrony: - ntpconf.force_chrony(statestore) - - logger.info('Synchronizing time with KDC...') - - if not options.ntp_servers: - ds = ipadiscovery.IPADiscovery() - ntp_servers = ds.ipadns_search_srv(cli_domain, '_ntp._udp', - None, break_on_first=False) - - if not ntp_servers: - logger.warning("No SRV records of NTP servers found. IPA " - "server address will be used") - ntp_servers = cli_server - else: - ntp_servers = options.ntp_servers - - synced_time = ntpconf.configure_chrony(ntp_servers, fstore, - statestore, options.debug) - - if not synced_time: - logger.warning( - "Unable to sync time with chrony server, assuming the time " - "is in sync. Please check that 123 UDP port is opened, " - "and any time server is on network.") - else: - logger.info('Skipping synchronizing time with chrony server.') + if options.conf_ntp: + # Attempt to sync time with NTP server (chrony). + sync_time(options, fstore, statestore) + elif options.on_master: + # If we're on master skipping the time sync here because it was done + # in ipa-server-install + logger.info("Skipping attempt to configure and synchronize time with" + " chrony server as it has been already done on master.") if not options.unattended: if (options.principal is None and options.password is None and @@ -3241,31 +3278,7 @@ def uninstall(options): service.service_name ) - chrony_configured = statestore.has_state('ntp') - if chrony_configured: - chrony_enabled = statestore.restore_state('ntp', 'enabled') - restored = False - - try: - # Restore might fail due to file missing in backup - # the reason for it might be that freeipa-client was updated - # to this version but not unenrolled/enrolled again - # In such case it is OK to fail - restored = fstore.restore_file(paths.CHRONY_CONF) - except Exception: - pass - - if not chrony_enabled: - services.knownservices.chronyd.stop() - services.knownservices.chronyd.disable() - else: - if restored: - services.knownservices.chronyd.restart() - - try: - ntpconf.restore_forced_chronyd(statestore) - except CalledProcessError as e: - logger.error('Failed to restore time synchronization service: %s', e) + restore_time_sync(statestore, fstore) if was_sshd_configured and services.knownservices.sshd.is_running(): services.knownservices.sshd.restart() @@ -3444,7 +3457,7 @@ class ClientInstallInterface(hostname_.HostNameInstallInterface, ntp_servers = enroll_only(ntp_servers) no_ntp = knob( - None, True, + None, description="do not configure ntp", cli_names=[None, '-N'], ) @@ -3539,6 +3552,10 @@ class ClientInstallInterface(hostname_.HostNameInstallInterface, raise RuntimeError( "--force-chrony cannot be used together with --no-ntp") + if self.ntp_servers and self.no_ntp: + raise RuntimeError( + "--ntp-server cannot be used together with --no-ntp") + if self.no_nisdomain and self.nisdomain: raise RuntimeError( "--no-nisdomain cannot be used together with --nisdomain") diff --git a/ipaclient/install/ntpconf.py b/ipaclient/install/ntpconf.py index a9f05687d..5ed2a13f3 100644 --- a/ipaclient/install/ntpconf.py +++ b/ipaclient/install/ntpconf.py @@ -103,10 +103,12 @@ def configure_chrony(ntp_servers, fstore=None, sysstore=None, debug=False): try: logger.info('Attempting to sync time using chronyd.') ipautil.run(cmd) + logger.info('Time is in sync.') return True except ipautil.CalledProcessError as e: if e.returncode is 1: - logger.debug('Process chronyc waitsync failed to sync time') + logger.warning('Process chronyc waitsync failed to sync time!') + logger.warning('Configuration of chrony was changed by installer.') return False diff --git a/ipaserver/install/server/__init__.py b/ipaserver/install/server/__init__.py index 9b796899a..7dc7d6dbd 100644 --- a/ipaserver/install/server/__init__.py +++ b/ipaserver/install/server/__init__.py @@ -168,7 +168,6 @@ class ServerInstallInterface(ServerCertificateInstallInterface, kinit_attempts = 1 fixed_primary = True - ntp_servers = None force_chrony = False permit = False enable_dns_updates = False diff --git a/ipaserver/install/server/install.py b/ipaserver/install/server/install.py index 58aaedf5a..675c788a2 100644 --- a/ipaserver/install/server/install.py +++ b/ipaserver/install/server/install.py @@ -761,6 +761,13 @@ def install(installer): # Create a directory server instance if not options.external_cert_files: + # We have to sync time before certificate handling on master. + # As chrony configuration is moved from client here, unconfiguration of + # chrony will be handled here in uninstall() method as well by invoking + # the ipa-server-install --uninstall + if not options.no_ntp: + ipaclient.install.client.sync_time(options, fstore, sstore) + if options.dirsrv_cert_files: ds = dsinstance.DsInstance(fstore=fstore, domainlevel=options.domainlevel, @@ -906,7 +913,7 @@ def install(installer): try: args = [paths.IPA_CLIENT_INSTALL, "--on-master", "--unattended", "--domain", domain_name, "--server", host_name, - "--realm", realm_name, "--hostname", host_name] + "--realm", realm_name, "--hostname", host_name, "--no-ntp"] if options.no_dns_sshfp: args.append("--no-dns-sshfp") if options.ssh_trust_dns: @@ -1082,6 +1089,8 @@ def uninstall(installer): except Exception: pass + ipaclient.install.client.restore_time_sync(sstore, fstore) + kra.uninstall() ca.uninstall()