From f568fe92224dfa37a644e4cfd78dc3e73ed5eead Mon Sep 17 00:00:00 2001 From: Miroslav Lichvar Date: Wed, 15 Jul 2020 17:03:17 +0200 Subject: [PATCH 1/3] Parse NTP server options from config file When parsing existing NTP servers from chrony.conf, instead of hardcoding the iburst option, check the line for some of the important and frequently used options. --- pyanaconda/ntp.py | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/pyanaconda/ntp.py b/pyanaconda/ntp.py index eed4b34307c..c50878cd5fc 100644 --- a/pyanaconda/ntp.py +++ b/pyanaconda/ntp.py @@ -40,7 +40,9 @@ #example line: #server 0.fedora.pool.ntp.org iburst -SRV_LINE_REGEXP = re.compile(r"^\s*(server|pool)\s*([-a-zA-Z.0-9]+)\s*[a-zA-Z]+\s*$") +SRV_LINE_REGEXP = re.compile(r"^\s*(server|pool)\s*([-a-zA-Z.0-9]+)\s?([a-zA-Z0-9\s]*)$") +SRV_NOARG_OPTIONS = ["burst", "iburst", "nts", "prefer", "require", "trust", "noselect", "xleave"] +SRV_ARG_OPTIONS = ["key", "minpoll", "maxpoll"] #treat pools as four servers with the same name SERVERS_PER_POOL = 4 @@ -143,7 +145,23 @@ def get_servers_from_config(conf_file_path=NTP_CONFIG_FILE): server = TimeSourceData() server.type = match.group(1).upper() server.hostname = match.group(2) - server.options = ["iburst"] + server.options = [] + + words = match.group(3).lower().split() + skip_argument = False + + for i in range(len(words)): + if skip_argument: + skip_argument = False + continue + if words[i] in SRV_NOARG_OPTIONS: + server.options.append(words[i]) + elif words[i] in SRV_ARG_OPTIONS and i + 1 < len(words): + server.options.append(' '.join(words[i:i+2])) + skip_argument = True + else: + log.debug("Unknown NTP server option %s", words[i]) + servers.append(server) except IOError as ioerr: From 20ea907ce95498ade5f77a9eed26dab78978cae2 Mon Sep 17 00:00:00 2001 From: Miroslav Lichvar Date: Wed, 15 Jul 2020 17:45:55 +0200 Subject: [PATCH 2/3] Add connection test for NTS When "nts" is included in the server options, don't send NTP requests to the server as it may be a dedicated NTS-KE server with no NTP server running, or it may be running on a different port than 123. Try to make a TCP connection to the NTS-KE port (4460) instead. --- pyanaconda/ntp.py | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/pyanaconda/ntp.py b/pyanaconda/ntp.py index c50878cd5fc..cde27258aaa 100644 --- a/pyanaconda/ntp.py +++ b/pyanaconda/ntp.py @@ -97,18 +97,24 @@ def get_ntp_servers_summary(servers, states): return summary -def ntp_server_working(server_hostname): +def ntp_server_working(server_hostname, nts_enabled): """Tries to do an NTP request to the server (timeout may take some time). + If NTS is enabled, try making a TCP connection to the NTS-KE port instead. + :param server_hostname: a host name or an IP address of an NTP server :type server_hostname: string :return: True if the given server is reachable and working, False otherwise :rtype: bool """ - client = ntplib.NTPClient() - try: - client.request(server_hostname) + # ntplib doesn't support NTS + if nts_enabled: + s = socket.create_connection((server_hostname, 4460), 2) + s.close() + else: + client = ntplib.NTPClient() + client.request(server_hostname) except ntplib.NTPException: return False # address related error @@ -324,8 +330,9 @@ def check_status(self, server): :param TimeSourceData server: an NTP server """ - # Get a hostname. + # Get a hostname and NTS option. hostname = server.hostname + nts_enabled = "nts" in server.options # Reset the current status. self._set_status(hostname, NTP_SERVER_QUERY) @@ -334,7 +341,7 @@ def check_status(self, server): threadMgr.add(AnacondaThread( prefix=THREAD_NTP_SERVER_CHECK, target=self._check_status, - args=(hostname, )) + args=(hostname, nts_enabled)) ) def _set_status(self, hostname, status): @@ -345,13 +352,13 @@ def _set_status(self, hostname, status): """ self._cache[hostname] = status - def _check_status(self, hostname): + def _check_status(self, hostname, nts_enabled): """Check if an NTP server appears to be working. :param str hostname: a hostname of an NTP server """ log.debug("Checking NTP server %s", hostname) - result = ntp_server_working(hostname) + result = ntp_server_working(hostname, nts_enabled) if result: log.debug("NTP server %s appears to be working.", hostname) From c236e3f65d7a4f5f5f34ed64a5618164f9bb6f21 Mon Sep 17 00:00:00 2001 From: Miroslav Lichvar Date: Wed, 15 Jul 2020 14:45:23 +0200 Subject: [PATCH 3/3] Add NTS support to time sources in GUI Add an NTS checkbox and column to enable and display the "nts" option of NTP time sources. --- pyanaconda/ui/gui/spokes/datetime_spoke.glade | 34 +++++++++++++++++-- pyanaconda/ui/gui/spokes/datetime_spoke.py | 26 ++++++++++++-- 2 files changed, 55 insertions(+), 5 deletions(-) diff --git a/pyanaconda/ui/gui/spokes/datetime_spoke.glade b/pyanaconda/ui/gui/spokes/datetime_spoke.glade index 49e33776f51..3ebc12a4467 100644 --- a/pyanaconda/ui/gui/spokes/datetime_spoke.glade +++ b/pyanaconda/ui/gui/spokes/datetime_spoke.glade @@ -83,6 +83,8 @@ + + @@ -220,6 +222,21 @@ 3 + + + Server supports Network Time Security (NTS) authentication + True + True + False + 0 + True + + + False + True + 4 + + True @@ -266,6 +283,19 @@ + + + NTS + + + + + + 2 + + + + Working @@ -282,7 +312,7 @@ - 3 + 4 @@ -293,7 +323,7 @@ True True - 4 + 5 diff --git a/pyanaconda/ui/gui/spokes/datetime_spoke.py b/pyanaconda/ui/gui/spokes/datetime_spoke.py index ea121e7e4de..a8036cdb888 100644 --- a/pyanaconda/ui/gui/spokes/datetime_spoke.py +++ b/pyanaconda/ui/gui/spokes/datetime_spoke.py @@ -63,9 +63,10 @@ SERVER_HOSTNAME = 0 SERVER_POOL = 1 -SERVER_WORKING = 2 -SERVER_USE = 3 -SERVER_OBJECT = 4 +SERVER_NTS = 2 +SERVER_WORKING = 3 +SERVER_USE = 4 +SERVER_OBJECT = 5 DEFAULT_TZ = "America/New_York" @@ -184,6 +185,7 @@ def __init__(self, data, servers, states): self._serversStore = self.builder.get_object("serversStore") self._addButton = self.builder.get_object("addButton") self._poolCheckButton = self.builder.get_object("poolCheckButton") + self._ntsCheckButton = self.builder.get_object("ntsCheckButton") self._serverCheck = self.add_check(self._serverEntry, self._validate_server) self._serverCheck.update_check_status() @@ -256,6 +258,7 @@ def _add_row(self, server): itr = self._serversStore.append([ "", False, + False, constants.NTP_SERVER_QUERY, True, server @@ -268,6 +271,7 @@ def _refresh_row(self, itr): server = self._serversStore[itr][SERVER_OBJECT] self._serversStore.set_value(itr, SERVER_HOSTNAME, server.hostname) self._serversStore.set_value(itr, SERVER_POOL, server.type == TIME_SOURCE_POOL) + self._serversStore.set_value(itr, SERVER_NTS, "nts" in server.options) def _update_rows(self): """Periodically update the status of all rows. @@ -300,12 +304,16 @@ def on_entry_activated(self, entry, *args): server.hostname = entry.get_text() server.options = ["iburst"] + if self._ntsCheckButton.get_active(): + server.options.append("nts") + self._servers.append(server) self._states.check_status(server) self._add_row(server) entry.set_text("") self._poolCheckButton.set_active(False) + self._ntsCheckButton.set_active(False) def on_add_clicked(self, *args): self._serverEntry.emit("activate") @@ -326,6 +334,18 @@ def on_pool_toggled(self, renderer, path, *args): self._refresh_row(itr) + def on_nts_toggled(self, renderer, path, *args): + itr = self._serversStore.get_iter(path) + server = self._serversStore[itr][SERVER_OBJECT] + + if "nts" in server.options: + server.options.remove("nts") + else: + server.options.append("nts") + + self._states.check_status(server) + self._refresh_row(itr) + def on_server_editing_started(self, renderer, editable, path): itr = self._serversStore.get_iter(path) self._active_server = self._serversStore[itr][SERVER_OBJECT]