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 <newtwen+github@gmail.com>
This commit is contained in:
Paul Donald
2025-04-25 04:21:38 +02:00
parent 3c16c59007
commit df28a18166
5 changed files with 573 additions and 0 deletions
+17
View File
@@ -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
@@ -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('<a id="docUrl" href="%s" target="_blank" rel="noreferrer">%s</a>'
.format(docUrl, _('Documentation'))));
// Interface
s = m.section(form.NamedSection, 'allow', 'allow', _('Allow'), _('An allow range permits access for chronyc from specific IPs to chronyd.') + '<br/>' +
_('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.') + '<br/>' +
_('Choose a wan interface to allow from all IPs.') + '<br/>' +
_('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('<code>/dev/rtc0</code>'), 'Check for RTC character device') + '<br/>' +
_('Disables certificate time checks via %s if RTC is absent.'.format('<code>nocerttimecheck</code>') ) );
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.') + '<br/>' +
_('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.') + '<br/>' +
_('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.') + '<br/>' +
_('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.') + '<br/>' +
_('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');
}
@@ -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 ""
@@ -0,0 +1,13 @@
{
"admin/services/chrony": {
"title": "Chrony",
"order": 30,
"action": {
"type": "view",
"path": "chrony"
},
"depends": {
"acl": [ "luci-app-chrony" ]
}
}
}
@@ -0,0 +1,11 @@
{
"luci-app-chrony": {
"description": "Grant UCI access for luci-app-chrony",
"read": {
"uci": [ "chrony" ]
},
"write": {
"uci": [ "chrony" ]
}
}
}