From df28a18166c330ef95cabd996297263c6f3a8713 Mon Sep 17 00:00:00 2001 From: Paul Donald Date: Fri, 25 Apr 2025 04:21:38 +0200 Subject: [PATCH] luci-app-chrony: Add chrony This prefers the NTS version, and does not discern the non-NTS version, since chronyd.init startup script doesn't either. Signed-off-by: Paul Donald --- applications/luci-app-chrony/Makefile | 17 + .../luci-static/resources/view/chrony.js | 232 ++++++++++++++ .../luci-app-chrony/po/templates/chrony.pot | 300 ++++++++++++++++++ .../share/luci/menu.d/luci-app-chrony.json | 13 + .../usr/share/rpcd/acl.d/luci-app-chrony.json | 11 + 5 files changed, 573 insertions(+) create mode 100644 applications/luci-app-chrony/Makefile create mode 100644 applications/luci-app-chrony/htdocs/luci-static/resources/view/chrony.js create mode 100644 applications/luci-app-chrony/po/templates/chrony.pot create mode 100644 applications/luci-app-chrony/root/usr/share/luci/menu.d/luci-app-chrony.json create mode 100644 applications/luci-app-chrony/root/usr/share/rpcd/acl.d/luci-app-chrony.json diff --git a/applications/luci-app-chrony/Makefile b/applications/luci-app-chrony/Makefile new file mode 100644 index 0000000000..115421529e --- /dev/null +++ b/applications/luci-app-chrony/Makefile @@ -0,0 +1,17 @@ +# +# Copyright (C) 2025 OpenWrt.org +# +# This is free software, licensed under Apache-2.0. +# See /LICENSE for more information. +# + +include $(TOPDIR)/rules.mk + +LUCI_TITLE:=LuCI support for chrony +LUCI_DEPENDS:=+luci-base +chrony-nts + +PKG_LICENSE:=Apache-2.0 + +include ../../luci.mk + +# call BuildPackage - OpenWrt buildroot signature diff --git a/applications/luci-app-chrony/htdocs/luci-static/resources/view/chrony.js b/applications/luci-app-chrony/htdocs/luci-static/resources/view/chrony.js new file mode 100644 index 0000000000..4dcb1525bf --- /dev/null +++ b/applications/luci-app-chrony/htdocs/luci-static/resources/view/chrony.js @@ -0,0 +1,232 @@ +'use strict'; +'require form'; +'require fs'; +'require uci'; +'require view'; +'require tools.widgets as widgets'; + +return view.extend({ + + render(data) { + const docUrl = 'https://chrony-project.org/documentation.html'; + let m, s, o; + + m = new form.Map('chrony', _('Chrony NTP/NTS daemon'), + '%s'.format('%s' + .format(docUrl, _('Documentation')))); + + // Interface + s = m.section(form.NamedSection, 'allow', 'allow', _('Allow'), _('An allow range permits access for chronyc from specific IPs to chronyd.') + '
' + + _('Delete this section to allow all local IPs.')); + s.anonymous = true; + s.addremove = true; + + o = s.option(widgets.NetworkSelect, 'interface', _('Interface'), + _('Choose IP ranges from this interface to set them as allowed ranges.') + '
' + + _('Choose a wan interface to allow from all IPs.') + '
' + + _('Additional firewall configuration is required if you intend wan access.')); + o.nocreate = true; + o.rmempty = false; + + // NTS + s = m.section(form.NamedSection, 'nts', 'nts', _('Network Time Security (NTS)')); + s.anonymous = true; + s.addremove = true; + + o = s.option(form.Flag, 'rtccheck', _('RTC Check'), + _('Check for the presence of %s.'.format('/dev/rtc0'), 'Check for RTC character device') + '
' + + _('Disables certificate time checks via %s if RTC is absent.'.format('nocerttimecheck') ) ); + o.default = o.disabled; + + o = s.option(form.Flag, 'systemcerts', _('Use system CA bundle')); + o.default = o.enabled; + + o = s.option(form.FileUpload, 'trustedcerts', _('Trusted certificates')); + o.optional = true; + o.root_directory = '/etc'; + + // Stepping + s = m.section(form.NamedSection, 'makestep', 'makestep', _('Stepping'), + _('Corrects the system clock by stepping immediately when it is so far adrift that the slewing process would take a very long time.')); + s.anonymous = true; + s.addremove = true; + s.singular = true; + + o = s.option(form.Value, 'threshold', _('Trigger Amount Threshold'), + _('Seconds float value.')); + o.datatype = 'float'; + o.optional = true; + + o = s.option(form.Value, 'limit', _('Limit'), + _('First x clock updates')); + o.datatype = 'integer'; + o.optional = true; + + // Logging + s = m.section(form.NamedSection, 'logging', 'logging', _('Logging')); + s.anonymous = true; + s.addremove = true; + + o = s.option(form.Value, 'logchange', _('Log any change more than'), + _('Seconds threshold for the adjustment of the system clock that will generate a syslog message.')); + o.datatype = 'float'; + o.placeholder = '1'; + o.optional = true; + + // System Clock + s = m.section(form.NamedSection, 'systemclock', 'systemclock', _('System Clock')); + s.anonymous = true; + s.addremove = true; + + o = s.option(form.Value, 'precision', _('Precision'), + _('Precision of the system clock (in seconds).')); + o.datatype = 'string'; + o.placeholder = _('8e-6 (8 microseconds)'); + o.optional = true; + + o = s.option(form.ListValue, 'leapsecmode', _('Leap second mode'), + _('Strategy to reconcile leap seconds in UTC with solar time.')); + o.value('', _('(default)')) + o.value('system') + o.value('step') + o.value('slew') + o.value('ignore') + o.optional = true; + + // Smoothing + s = m.section(form.NamedSection, 'smoothtime', 'smoothtime', _('Smoothing'), + _('Use only when the clients are not configured to poll another NTP server also, because they could reject this server as a falseticker or fail to select a source completely.')); + s.anonymous = true; + s.addremove = true; + + o = s.option(form.Value, 'maxppm', _('Max PPM'), + _('Maximum frequency offset of the smoothed time to the tracked NTP time (in ppm).')); + o.datatype = 'uinteger'; + o.placeholder = '400'; + o.optional = false; + + o = s.option(form.Value, 'maxwander', _('Max wander'), + _('Maximum rate at which the frequency offset is allowed to change (in ppm per second).')); + o.datatype = 'float'; + o.placeholder = '0.01'; + o.optional = false; + + o = s.option(form.Flag, 'leaponly', _('Leap seconds only'), + _('Only leap seconds are smoothed out; ignore normal offset and frequency changes.')); + o.default = o.disabled; + + // Server entries + s = m.section(form.TypedSection, 'server', _('Server'), + _('Remote NTP servers for your chronyd')); + s.anonymous = true; + s.addremove = true; + insertTypedSectionOptions(m, s, o, 'server'); + + // Pool entries + s = m.section(form.TypedSection, 'pool', _('Pool'), + _('Specifies a pool of NTP servers rather than a single NTP server.') + '
' + + _('The pool name is expected to resolve to multiple addresses which might change over time.')); + s.anonymous = true; + s.addremove = true; + insertTypedSectionOptions(m, s, o, 'pool'); + + // Peer entries + s = m.section(form.TypedSection, 'peer', _('Peer'), + _('Specifies a symmetric association with an NTP peer.') + '
' + + _('A single symmetric association allows the peers to be both servers and clients to each other.')); + s.anonymous = true; + s.addremove = true; + insertTypedSectionOptions(m, s, o, 'peer'); + + // Servers assigned (to us) via DHCP + s = m.section(form.NamedSection, 'dhcp_ntp_server', 'dhcp_ntp_server', _('DHCP(v6)'), + _('Options for servers provided to this host via DHCP(v6) (via the WAN for example).')); + s.anonymous = true; + insertTypedSectionOptions(m, s, o, 'dhcp_ntp_server'); + + + return m.render(); + } +}); + +function insertTypedSectionOptions(m, s, o, type) { + + o = s.option(form.Flag, 'disabled', _('Disabled')); + o.default = o.disabled; // disabled default is disabled i.e., enabled + + if (type != 'dhcp_ntp_server') { + o = s.option(form.Value, 'hostname', _('Hostname')); + o.optional = false; + o.depends('disabled', '0'); + } + + if (type != 'peer') { + o = s.option(form.Flag, 'iburst', _('iburst')); + o.rmempty = true; + o.default = o.disabled + o.depends('disabled', '0'); + + o = s.option(form.Flag, 'nts', _('NTS')); + o.rmempty = true; + o.default = o.disabled + o.depends('disabled', '0'); + } + + o = s.option(form.Flag, 'prefer', _('Prefer')); + o.default = o.disabled; + + o = s.option(form.Flag, 'xleave', _('Interleave')); + o.default = o.disabled; + + o = s.option(form.RangeSliderValue, 'minpoll', _('Minimum poll'), + _('(Log_2 i.e. y=2^x) interval between readings of the NIC clock.')); + o.min = -7; + o.max = 24; + o.step = 1; + o.default = 4; + o.calculate = (val) => { + return 2**Number(val); + }; + o.calcunits = _('seconds') + o.depends('disabled', '0'); + + o = s.option(form.RangeSliderValue, 'maxpoll', _('Maximum poll'), + _('(Log_2 i.e. y=2^x) interval between readings of the NIC clock.')); + o.min = -7; + o.max = 24; + o.step = 1; + o.default = 4; + o.calculate = (val) => { + return 2**Number(val); + }; + o.calcunits = _('seconds'); + o.depends('disabled', '0'); + + o = s.option(form.Value, 'mindelay', _('Minimum delay'), + _('A fixed round-trip delay in seconds to be used instead of that of the previous measurements.') + '
' + + _('Exponential and decimal notation are allowed.')); + o.placeholder = '1e-4' + o.depends('disabled', '0'); + + o = s.option(form.Value, 'maxdelay', _('Maximum delay'), + _('A fixed round-trip delay in seconds to be used instead of that of the previous measurements.') + '
' + + _('Exponential and decimal notation are allowed.')); + o.placeholder = '3'; + o.depends('disabled', '0'); + + o = s.option(form.RangeSliderValue, 'minsamples', _('Minimum samples')); + o.min = 4; + o.max = 64; + o.step = 1; + o.default = 6; + o.depends('disabled', '0'); + + o = s.option(form.RangeSliderValue, 'maxsamples', _('Maximum samples'), + _('Number of samples that chronyd should keep for each source.')); + o.min = 4; + o.max = 64; + o.step = 1; + o.default = 6; + o.depends('disabled', '0'); + +} diff --git a/applications/luci-app-chrony/po/templates/chrony.pot b/applications/luci-app-chrony/po/templates/chrony.pot new file mode 100644 index 0000000000..0178bfced2 --- /dev/null +++ b/applications/luci-app-chrony/po/templates/chrony.pot @@ -0,0 +1,300 @@ +msgid "" +msgstr "Content-Type: text/plain; charset=UTF-8" + +#: applications/luci-app-chrony/htdocs/luci-static/resources/view/chrony.js:183 +#: applications/luci-app-chrony/htdocs/luci-static/resources/view/chrony.js:195 +msgid "(Log_2 i.e. y=2^x) interval between readings of the NIC clock." +msgstr "" + +#: applications/luci-app-chrony/htdocs/luci-static/resources/view/chrony.js:90 +msgid "(default)" +msgstr "" + +#: applications/luci-app-chrony/htdocs/luci-static/resources/view/chrony.js:85 +msgid "8e-6 (8 microseconds)" +msgstr "" + +#: applications/luci-app-chrony/htdocs/luci-static/resources/view/chrony.js:207 +#: applications/luci-app-chrony/htdocs/luci-static/resources/view/chrony.js:213 +msgid "" +"A fixed round-trip delay in seconds to be used instead of that of the " +"previous measurements." +msgstr "" + +#: applications/luci-app-chrony/htdocs/luci-static/resources/view/chrony.js:137 +msgid "" +"A single symmetric association allows the peers to be both servers and " +"clients to each other." +msgstr "" + +#: applications/luci-app-chrony/htdocs/luci-static/resources/view/chrony.js:28 +msgid "Additional firewall configuration is required if you intend wan access." +msgstr "" + +#: applications/luci-app-chrony/htdocs/luci-static/resources/view/chrony.js:20 +msgid "Allow" +msgstr "" + +#: applications/luci-app-chrony/htdocs/luci-static/resources/view/chrony.js:20 +msgid "An allow range permits access for chronyc from specific IPs to chronyd." +msgstr "" + +#: applications/luci-app-chrony/htdocs/luci-static/resources/view/chrony.js:38 +msgctxt "Check for RTC character device" +msgid "Check for the presence of %s." +msgstr "" + +#: applications/luci-app-chrony/htdocs/luci-static/resources/view/chrony.js:26 +msgid "Choose IP ranges from this interface to set them as allowed ranges." +msgstr "" + +#: applications/luci-app-chrony/htdocs/luci-static/resources/view/chrony.js:27 +msgid "Choose a wan interface to allow from all IPs." +msgstr "" + +#: applications/luci-app-chrony/root/usr/share/luci/menu.d/luci-app-chrony.json:3 +msgid "Chrony" +msgstr "" + +#: applications/luci-app-chrony/htdocs/luci-static/resources/view/chrony.js:15 +msgid "Chrony NTP/NTS daemon" +msgstr "" + +#: applications/luci-app-chrony/htdocs/luci-static/resources/view/chrony.js:51 +msgid "" +"Corrects the system clock by stepping immediately when it is so far adrift " +"that the slewing process would take a very long time." +msgstr "" + +#: applications/luci-app-chrony/htdocs/luci-static/resources/view/chrony.js:143 +msgid "DHCP(v6)" +msgstr "" + +#: applications/luci-app-chrony/htdocs/luci-static/resources/view/chrony.js:21 +msgid "Delete this section to allow all local IPs." +msgstr "" + +#: applications/luci-app-chrony/htdocs/luci-static/resources/view/chrony.js:155 +msgid "Disabled" +msgstr "" + +#: applications/luci-app-chrony/htdocs/luci-static/resources/view/chrony.js:39 +msgid "Disables certificate time checks via %s if RTC is absent." +msgstr "" + +#: applications/luci-app-chrony/htdocs/luci-static/resources/view/chrony.js:17 +msgid "Documentation" +msgstr "" + +#: applications/luci-app-chrony/htdocs/luci-static/resources/view/chrony.js:208 +#: applications/luci-app-chrony/htdocs/luci-static/resources/view/chrony.js:214 +msgid "Exponential and decimal notation are allowed." +msgstr "" + +#: applications/luci-app-chrony/htdocs/luci-static/resources/view/chrony.js:62 +msgid "First x clock updates" +msgstr "" + +#: applications/luci-app-chrony/root/usr/share/rpcd/acl.d/luci-app-chrony.json:3 +msgid "Grant UCI access for luci-app-chrony" +msgstr "" + +#: applications/luci-app-chrony/htdocs/luci-static/resources/view/chrony.js:159 +msgid "Hostname" +msgstr "" + +#: applications/luci-app-chrony/htdocs/luci-static/resources/view/chrony.js:25 +msgid "Interface" +msgstr "" + +#: applications/luci-app-chrony/htdocs/luci-static/resources/view/chrony.js:179 +msgid "Interleave" +msgstr "" + +#: applications/luci-app-chrony/htdocs/luci-static/resources/view/chrony.js:88 +msgid "Leap second mode" +msgstr "" + +#: applications/luci-app-chrony/htdocs/luci-static/resources/view/chrony.js:115 +msgid "Leap seconds only" +msgstr "" + +#: applications/luci-app-chrony/htdocs/luci-static/resources/view/chrony.js:61 +msgid "Limit" +msgstr "" + +#: applications/luci-app-chrony/htdocs/luci-static/resources/view/chrony.js:71 +msgid "Log any change more than" +msgstr "" + +#: applications/luci-app-chrony/htdocs/luci-static/resources/view/chrony.js:67 +msgid "Logging" +msgstr "" + +#: applications/luci-app-chrony/htdocs/luci-static/resources/view/chrony.js:103 +msgid "Max PPM" +msgstr "" + +#: applications/luci-app-chrony/htdocs/luci-static/resources/view/chrony.js:109 +msgid "Max wander" +msgstr "" + +#: applications/luci-app-chrony/htdocs/luci-static/resources/view/chrony.js:212 +msgid "Maximum delay" +msgstr "" + +#: applications/luci-app-chrony/htdocs/luci-static/resources/view/chrony.js:104 +msgid "" +"Maximum frequency offset of the smoothed time to the tracked NTP time (in " +"ppm)." +msgstr "" + +#: applications/luci-app-chrony/htdocs/luci-static/resources/view/chrony.js:194 +msgid "Maximum poll" +msgstr "" + +#: applications/luci-app-chrony/htdocs/luci-static/resources/view/chrony.js:110 +msgid "" +"Maximum rate at which the frequency offset is allowed to change (in ppm per " +"second)." +msgstr "" + +#: applications/luci-app-chrony/htdocs/luci-static/resources/view/chrony.js:225 +msgid "Maximum samples" +msgstr "" + +#: applications/luci-app-chrony/htdocs/luci-static/resources/view/chrony.js:206 +msgid "Minimum delay" +msgstr "" + +#: applications/luci-app-chrony/htdocs/luci-static/resources/view/chrony.js:182 +msgid "Minimum poll" +msgstr "" + +#: applications/luci-app-chrony/htdocs/luci-static/resources/view/chrony.js:218 +msgid "Minimum samples" +msgstr "" + +#: applications/luci-app-chrony/htdocs/luci-static/resources/view/chrony.js:170 +msgid "NTS" +msgstr "" + +#: applications/luci-app-chrony/htdocs/luci-static/resources/view/chrony.js:33 +msgid "Network Time Security (NTS)" +msgstr "" + +#: applications/luci-app-chrony/htdocs/luci-static/resources/view/chrony.js:226 +msgid "Number of samples that chronyd should keep for each source." +msgstr "" + +#: applications/luci-app-chrony/htdocs/luci-static/resources/view/chrony.js:116 +msgid "" +"Only leap seconds are smoothed out; ignore normal offset and frequency " +"changes." +msgstr "" + +#: applications/luci-app-chrony/htdocs/luci-static/resources/view/chrony.js:144 +msgid "" +"Options for servers provided to this host via DHCP(v6) (via the WAN for " +"example)." +msgstr "" + +#: applications/luci-app-chrony/htdocs/luci-static/resources/view/chrony.js:135 +msgid "Peer" +msgstr "" + +#: applications/luci-app-chrony/htdocs/luci-static/resources/view/chrony.js:127 +msgid "Pool" +msgstr "" + +#: applications/luci-app-chrony/htdocs/luci-static/resources/view/chrony.js:82 +msgid "Precision" +msgstr "" + +#: applications/luci-app-chrony/htdocs/luci-static/resources/view/chrony.js:83 +msgid "Precision of the system clock (in seconds)." +msgstr "" + +#: applications/luci-app-chrony/htdocs/luci-static/resources/view/chrony.js:176 +msgid "Prefer" +msgstr "" + +#: applications/luci-app-chrony/htdocs/luci-static/resources/view/chrony.js:37 +msgid "RTC Check" +msgstr "" + +#: applications/luci-app-chrony/htdocs/luci-static/resources/view/chrony.js:121 +msgid "Remote NTP servers for your chronyd" +msgstr "" + +#: applications/luci-app-chrony/htdocs/luci-static/resources/view/chrony.js:57 +msgid "Seconds float value." +msgstr "" + +#: applications/luci-app-chrony/htdocs/luci-static/resources/view/chrony.js:72 +msgid "" +"Seconds threshold for the adjustment of the system clock that will generate " +"a syslog message." +msgstr "" + +#: applications/luci-app-chrony/htdocs/luci-static/resources/view/chrony.js:120 +msgid "Server" +msgstr "" + +#: applications/luci-app-chrony/htdocs/luci-static/resources/view/chrony.js:98 +msgid "Smoothing" +msgstr "" + +#: applications/luci-app-chrony/htdocs/luci-static/resources/view/chrony.js:128 +msgid "Specifies a pool of NTP servers rather than a single NTP server." +msgstr "" + +#: applications/luci-app-chrony/htdocs/luci-static/resources/view/chrony.js:136 +msgid "Specifies a symmetric association with an NTP peer." +msgstr "" + +#: applications/luci-app-chrony/htdocs/luci-static/resources/view/chrony.js:50 +msgid "Stepping" +msgstr "" + +#: applications/luci-app-chrony/htdocs/luci-static/resources/view/chrony.js:89 +msgid "Strategy to reconcile leap seconds in UTC with solar time." +msgstr "" + +#: applications/luci-app-chrony/htdocs/luci-static/resources/view/chrony.js:78 +msgid "System Clock" +msgstr "" + +#: applications/luci-app-chrony/htdocs/luci-static/resources/view/chrony.js:129 +msgid "" +"The pool name is expected to resolve to multiple addresses which might " +"change over time." +msgstr "" + +#: applications/luci-app-chrony/htdocs/luci-static/resources/view/chrony.js:56 +msgid "Trigger Amount Threshold" +msgstr "" + +#: applications/luci-app-chrony/htdocs/luci-static/resources/view/chrony.js:45 +msgid "Trusted certificates" +msgstr "" + +#: applications/luci-app-chrony/htdocs/luci-static/resources/view/chrony.js:99 +msgid "" +"Use only when the clients are not configured to poll another NTP server " +"also, because they could reject this server as a falseticker or fail to " +"select a source completely." +msgstr "" + +#: applications/luci-app-chrony/htdocs/luci-static/resources/view/chrony.js:42 +msgid "Use system CA bundle" +msgstr "" + +#: applications/luci-app-chrony/htdocs/luci-static/resources/view/chrony.js:165 +msgid "iburst" +msgstr "" + +#: applications/luci-app-chrony/htdocs/luci-static/resources/view/chrony.js:191 +#: applications/luci-app-chrony/htdocs/luci-static/resources/view/chrony.js:203 +msgid "seconds" +msgstr "" diff --git a/applications/luci-app-chrony/root/usr/share/luci/menu.d/luci-app-chrony.json b/applications/luci-app-chrony/root/usr/share/luci/menu.d/luci-app-chrony.json new file mode 100644 index 0000000000..87d2cfe1df --- /dev/null +++ b/applications/luci-app-chrony/root/usr/share/luci/menu.d/luci-app-chrony.json @@ -0,0 +1,13 @@ +{ + "admin/services/chrony": { + "title": "Chrony", + "order": 30, + "action": { + "type": "view", + "path": "chrony" + }, + "depends": { + "acl": [ "luci-app-chrony" ] + } + } +} diff --git a/applications/luci-app-chrony/root/usr/share/rpcd/acl.d/luci-app-chrony.json b/applications/luci-app-chrony/root/usr/share/rpcd/acl.d/luci-app-chrony.json new file mode 100644 index 0000000000..397f18f231 --- /dev/null +++ b/applications/luci-app-chrony/root/usr/share/rpcd/acl.d/luci-app-chrony.json @@ -0,0 +1,11 @@ +{ + "luci-app-chrony": { + "description": "Grant UCI access for luci-app-chrony", + "read": { + "uci": [ "chrony" ] + }, + "write": { + "uci": [ "chrony" ] + } + } +} -- 2.30.2