From dddb4dd85a1f27fb23625233671b0d7dcae3697b Mon Sep 17 00:00:00 2001 From: Matthew Otto Date: Sun, 4 Jan 2026 18:40:26 -0600 Subject: [PATCH 01/12] add NTP data view --- net/chrony/Makefile | 2 +- net/chrony/pkg-descr | 4 ++++ .../OPNsense/Chrony/Api/ServiceController.php | 11 +++++++++++ .../mvc/app/views/OPNsense/Chrony/general.volt | 11 +++++++++++ .../service/conf/actions.d/actions_chrony.conf | 6 ++++++ 5 files changed, 33 insertions(+), 1 deletion(-) diff --git a/net/chrony/Makefile b/net/chrony/Makefile index d4d807ea8b..00a06140da 100644 --- a/net/chrony/Makefile +++ b/net/chrony/Makefile @@ -1,5 +1,5 @@ PLUGIN_NAME= chrony -PLUGIN_VERSION= 1.5 +PLUGIN_VERSION= 1.6 PLUGIN_REVISION= 3 PLUGIN_COMMENT= Chrony time synchronisation PLUGIN_DEPENDS= chrony diff --git a/net/chrony/pkg-descr b/net/chrony/pkg-descr index f38222784a..fae71365be 100644 --- a/net/chrony/pkg-descr +++ b/net/chrony/pkg-descr @@ -4,6 +4,10 @@ better in virtual environments. Plugin Changelog ---------------- +1.6 + +* Add NTP data tab + 1.5 * Allow adding a fallback NTP when using NTS diff --git a/net/chrony/src/opnsense/mvc/app/controllers/OPNsense/Chrony/Api/ServiceController.php b/net/chrony/src/opnsense/mvc/app/controllers/OPNsense/Chrony/Api/ServiceController.php index e3a6813908..2c88330d0b 100644 --- a/net/chrony/src/opnsense/mvc/app/controllers/OPNsense/Chrony/Api/ServiceController.php +++ b/net/chrony/src/opnsense/mvc/app/controllers/OPNsense/Chrony/Api/ServiceController.php @@ -82,4 +82,15 @@ public function chronyauthdataAction() $response = $backend->configdRun("chrony chronyauthdata"); return array("response" => $response); } + + /** + * show chrony ntpdata + * @return array + */ + public function chronyntpdataAction() + { + $backend = new Backend(); + $response = $backend->configdRun("chrony chronyntpdata"); + return array("response" => $response); + } } diff --git a/net/chrony/src/opnsense/mvc/app/views/OPNsense/Chrony/general.volt b/net/chrony/src/opnsense/mvc/app/views/OPNsense/Chrony/general.volt index 40d7552e95..0cf9be2381 100644 --- a/net/chrony/src/opnsense/mvc/app/views/OPNsense/Chrony/general.volt +++ b/net/chrony/src/opnsense/mvc/app/views/OPNsense/Chrony/general.volt @@ -31,6 +31,7 @@
  • {{ lang._('Source Stats') }}
  • {{ lang._('Tracking') }}
  • {{ lang._('Auth Data') }}
  • +
  • {{ lang._('NTP Data') }}
  • @@ -55,6 +56,9 @@
    
         
    +
    +
    
    +    
    From 198400ec57b8b59359cbd443cade17d06ae1c64c Mon Sep 17 00:00:00 2001 From: Matthew Otto Date: Mon, 5 Jan 2026 11:00:11 -0600 Subject: [PATCH 04/12] add local / orphan mode support (#5115) --- .../OPNsense/Chrony/forms/general.xml | 44 +++++++++++------- .../app/models/OPNsense/Chrony/General.xml | 32 ++++++++----- .../templates/OPNsense/Chrony/chrony.conf | 45 ++++++++++--------- 3 files changed, 73 insertions(+), 48 deletions(-) diff --git a/net/chrony/src/opnsense/mvc/app/controllers/OPNsense/Chrony/forms/general.xml b/net/chrony/src/opnsense/mvc/app/controllers/OPNsense/Chrony/forms/general.xml index 3b6b358ffb..be7d297948 100644 --- a/net/chrony/src/opnsense/mvc/app/controllers/OPNsense/Chrony/forms/general.xml +++ b/net/chrony/src/opnsense/mvc/app/controllers/OPNsense/Chrony/forms/general.xml @@ -5,12 +5,40 @@ checkbox Enable Chrony time daemon. + + general.peers + + + select_multiple + true + Set as many NTP peers you need. + + + general.localstratum + + text + (1-15) Local mode allows the system clock to be used when no other clocks are available. The number here specifies the stratum reported by the local clock and should normally be set to a number high enough to ensure that any other servers available to clients are preferred over this server. + + + general.orphanmode + + checkbox + + general.port text Set the port chrony listen to. + + general.allowednetworks + + + select_multiple + true + Set the networks allowed to synchronize time with this server. If this value is not set it will also not listen to the port and just synchronize the time for itself. + general.ntsclient @@ -23,26 +51,10 @@ checkbox If you run NTS mode you can enable this option in order to ignore wrong time in certificates for the first check. This helps if your system starts with wrong time. - - general.peers - - - select_multiple - true - Set as many NTP peers you need. - general.fallbackpeers text Set fallback peer if you use NTS and your system starts with wrong time. Best to only use this for internal trusted peers. - - general.allowednetworks - - - select_multiple - true - Set the networks allowed to synchronize time with this server. If this value is not set it will also not listen to the port and just synchronize the time for itself. - diff --git a/net/chrony/src/opnsense/mvc/app/models/OPNsense/Chrony/General.xml b/net/chrony/src/opnsense/mvc/app/models/OPNsense/Chrony/General.xml index 08d29de0cf..ee22171f0a 100644 --- a/net/chrony/src/opnsense/mvc/app/models/OPNsense/Chrony/General.xml +++ b/net/chrony/src/opnsense/mvc/app/models/OPNsense/Chrony/General.xml @@ -7,10 +7,31 @@ 0 Y + + 0.opnsense.pool.ntp.org + Y + , + Y + + + 1 + 15 + N + Local stratum must be within 1-15. + + + 0 + Y + 323 Y + + N + , + Y + 0 Y @@ -19,19 +40,8 @@ 0 Y - - 0.opnsense.pool.ntp.org - Y - , - Y - N - - N - , - Y - diff --git a/net/chrony/src/opnsense/service/templates/OPNsense/Chrony/chrony.conf b/net/chrony/src/opnsense/service/templates/OPNsense/Chrony/chrony.conf index 7cd4f7a16f..04bd7dbf6e 100644 --- a/net/chrony/src/opnsense/service/templates/OPNsense/Chrony/chrony.conf +++ b/net/chrony/src/opnsense/service/templates/OPNsense/Chrony/chrony.conf @@ -5,32 +5,35 @@ driftfile /var/db/chrony/drift pidfile /var/run/chrony/chronyd.pid makestep 1 3 -{% if helpers.exists('OPNsense.chrony.general.ntsclient') and OPNsense.chrony.general.ntsclient == '1' %} -ntsdumpdir /var/lib/chrony -ntstrustedcerts /usr/local/etc/ssl/cert.pem -nosystemcert -{% endif %} - -{% if helpers.exists('OPNsense.chrony.general.ntsnocert') and OPNsense.chrony.general.ntsnocert == '1' %} -nocerttimecheck 1 -{% endif %} - -{% if not helpers.empty('OPNsense.chrony.general.peers') %} -{% for peer in OPNsense.chrony.general.peers.split(',') %} +{% if not helpers.empty('OPNsense.chrony.general.peers') %} +{% for peer in OPNsense.chrony.general.peers.split(',') %} server {{ peer }} iburst {% if helpers.exists('OPNsense.chrony.general.ntsclient') and OPNsense.chrony.general.ntsclient == '1' %}nts{% endif %} +{% endfor %} +{% endif %} -{% endfor %} -{% endif %} - -{% if helpers.exists('OPNsense.chrony.general.fallbackpeers') and OPNsense.chrony.general.fallbackpeers != '' %} +{% if helpers.exists('OPNsense.chrony.general.fallbackpeers') and OPNsense.chrony.general.fallbackpeers != '' %} authselectmode mix server {{ OPNsense.chrony.general.fallbackpeers }} -{% endif %} +{% endif %} -{% if not helpers.empty('OPNsense.chrony.general.allowednetworks') %} -{% for network in OPNsense.chrony.general.allowednetworks.split(',') %} +{% if not helpers.empty('OPNsense.chrony.general.localstratum') %} +local stratum {{ OPNsense.chrony.general.localstratum }} {% if helpers.exists('OPNsense.chrony.general.orphanmode') and OPNsense.chrony.general.orphanmode == '1' %}orphan{% endif %} +{% endif %} + +{% if not helpers.empty('OPNsense.chrony.general.allowednetworks') %} +{% for network in OPNsense.chrony.general.allowednetworks.split(',') %} allow {{ network }} -{% endfor %} -{% endif %} +{% endfor %} +{% endif %} + +{% if helpers.exists('OPNsense.chrony.general.ntsclient') and OPNsense.chrony.general.ntsclient == '1' %} +ntsdumpdir /var/lib/chrony +ntstrustedcerts /usr/local/etc/ssl/cert.pem +nosystemcert +{% endif %} + +{% if helpers.exists('OPNsense.chrony.general.ntsnocert') and OPNsense.chrony.general.ntsnocert == '1' %} +nocerttimecheck 1 +{% endif %} {% endif %} From 6dee8c0a3a2b4b847b470ed5fabf5e303909cce9 Mon Sep 17 00:00:00 2001 From: Matthew Otto Date: Mon, 5 Jan 2026 19:23:18 -0600 Subject: [PATCH 05/12] achieve basic functionality with grid --- net/chrony/pkg-descr | 9 ++- .../OPNsense/Chrony/Api/GeneralController.php | 32 +++++++++- ...eralController.php => IndexController.php} | 6 +- .../OPNsense/Chrony/forms/dialogPeer.xml | 64 +++++++++++++++++++ .../OPNsense/Chrony/forms/general.xml | 8 --- .../app/models/OPNsense/Chrony/General.xml | 40 ++++++++++-- .../app/models/OPNsense/Chrony/Menu/Menu.xml | 2 +- .../Chrony/{general.volt => index.volt} | 39 ++++++----- .../templates/OPNsense/Chrony/chrony.conf | 4 +- 9 files changed, 168 insertions(+), 36 deletions(-) rename net/chrony/src/opnsense/mvc/app/controllers/OPNsense/Chrony/{GeneralController.php => IndexController.php} (85%) create mode 100644 net/chrony/src/opnsense/mvc/app/controllers/OPNsense/Chrony/forms/dialogPeer.xml rename net/chrony/src/opnsense/mvc/app/views/OPNsense/Chrony/{general.volt => index.volt} (85%) diff --git a/net/chrony/pkg-descr b/net/chrony/pkg-descr index fae71365be..53dafbbc8c 100644 --- a/net/chrony/pkg-descr +++ b/net/chrony/pkg-descr @@ -6,7 +6,14 @@ Plugin Changelog 1.6 -* Add NTP data tab +* Update config UI to expose the following features: + - local/orphan mode + - pools + - prefer + - iburst + - min/max poll + - interleaving +* Add NTP data diagnostics 1.5 diff --git a/net/chrony/src/opnsense/mvc/app/controllers/OPNsense/Chrony/Api/GeneralController.php b/net/chrony/src/opnsense/mvc/app/controllers/OPNsense/Chrony/Api/GeneralController.php index d96f5a1213..51fb579c01 100644 --- a/net/chrony/src/opnsense/mvc/app/controllers/OPNsense/Chrony/Api/GeneralController.php +++ b/net/chrony/src/opnsense/mvc/app/controllers/OPNsense/Chrony/Api/GeneralController.php @@ -32,6 +32,36 @@ class GeneralController extends ApiMutableModelControllerBase { - protected static $internalModelClass = '\OPNsense\Chrony\General'; protected static $internalModelName = 'general'; + protected static $internalModelClass = '\OPNsense\Chrony\General'; + + public function searchItemAction() + { + return $this->searchBase("peers.peer", null, "address"); + } + + public function setItemAction($uuid) + { + return $this->setBase("peer", "peers.peer", $uuid); + } + + public function addItemAction() + { + return $this->addBase("peer", "peers.peer"); + } + + public function getItemAction($uuid = null) + { + return $this->getBase("peer", "peers.peer", $uuid); + } + + public function delItemAction($uuid) + { + return $this->delBase("peers.peer", $uuid); + } + + public function toggleItemAction($uuid, $enabled = null) + { + return $this->toggleBase("peers.peer", $uuid, $enabled); + } } diff --git a/net/chrony/src/opnsense/mvc/app/controllers/OPNsense/Chrony/GeneralController.php b/net/chrony/src/opnsense/mvc/app/controllers/OPNsense/Chrony/IndexController.php similarity index 85% rename from net/chrony/src/opnsense/mvc/app/controllers/OPNsense/Chrony/GeneralController.php rename to net/chrony/src/opnsense/mvc/app/controllers/OPNsense/Chrony/IndexController.php index faa214b6a0..088f439543 100644 --- a/net/chrony/src/opnsense/mvc/app/controllers/OPNsense/Chrony/GeneralController.php +++ b/net/chrony/src/opnsense/mvc/app/controllers/OPNsense/Chrony/IndexController.php @@ -28,11 +28,13 @@ namespace OPNsense\Chrony; -class GeneralController extends \OPNsense\Base\IndexController +class IndexController extends \OPNsense\Base\IndexController { public function indexAction() { + $this->view->pick('OPNsense/Chrony/index'); $this->view->generalForm = $this->getForm('general'); - $this->view->pick('OPNsense/Chrony/general'); + $this->view->formDialogPeer = $this->getForm("dialogPeer"); + $this->view->formGridPeer = $this->getFormGrid("dialogPeer"); } } diff --git a/net/chrony/src/opnsense/mvc/app/controllers/OPNsense/Chrony/forms/dialogPeer.xml b/net/chrony/src/opnsense/mvc/app/controllers/OPNsense/Chrony/forms/dialogPeer.xml new file mode 100644 index 0000000000..66a2bed252 --- /dev/null +++ b/net/chrony/src/opnsense/mvc/app/controllers/OPNsense/Chrony/forms/dialogPeer.xml @@ -0,0 +1,64 @@ +
    + + peer.pool + + checkbox + Address refers to a pool of NTP servers + + 6em + boolean + boolean + + + + peer.address + + text + The address/hostname of the NTP server or pool. + + + peer.iburst + + checkbox + Enable iburst for this source. + + 6em + boolean + boolean + + + + peer.prefer + + checkbox + Prefer this source over sources without the prefer option. + + 6em + boolean + boolean + + + + peer.xleave + + checkbox + Enable interleaved mode for this source. + + 6em + boolean + boolean + + + + general.minpoll + + text + The minimum interval between requests sent to the server as a power of 2 in seconds. + + + general.maxpoll + + text + The maximum interval between requests sent to the server as a power of 2 in seconds. + +
    \ No newline at end of file diff --git a/net/chrony/src/opnsense/mvc/app/controllers/OPNsense/Chrony/forms/general.xml b/net/chrony/src/opnsense/mvc/app/controllers/OPNsense/Chrony/forms/general.xml index be7d297948..a14a75025b 100644 --- a/net/chrony/src/opnsense/mvc/app/controllers/OPNsense/Chrony/forms/general.xml +++ b/net/chrony/src/opnsense/mvc/app/controllers/OPNsense/Chrony/forms/general.xml @@ -5,14 +5,6 @@ checkbox Enable Chrony time daemon. - - general.peers - - - select_multiple - true - Set as many NTP peers you need. - general.localstratum diff --git a/net/chrony/src/opnsense/mvc/app/models/OPNsense/Chrony/General.xml b/net/chrony/src/opnsense/mvc/app/models/OPNsense/Chrony/General.xml index ee22171f0a..3367735f95 100644 --- a/net/chrony/src/opnsense/mvc/app/models/OPNsense/Chrony/General.xml +++ b/net/chrony/src/opnsense/mvc/app/models/OPNsense/Chrony/General.xml @@ -7,11 +7,41 @@ 0 Y - - 0.opnsense.pool.ntp.org - Y - , - Y + + + + 1 + Y + +
    + opnsense.pool.ntp.org + Y +
    + + 0 + Y + + + 0 + Y + + + 0 + Y + + + -6 + 24 + N + minpoll value must be between -6 and 24. + + + -6 + 24 + N + maxpoll value must be between -6 and 24. + +
    1 diff --git a/net/chrony/src/opnsense/mvc/app/models/OPNsense/Chrony/Menu/Menu.xml b/net/chrony/src/opnsense/mvc/app/models/OPNsense/Chrony/Menu/Menu.xml index e11f5fe9cb..fef1b45bd5 100644 --- a/net/chrony/src/opnsense/mvc/app/models/OPNsense/Chrony/Menu/Menu.xml +++ b/net/chrony/src/opnsense/mvc/app/models/OPNsense/Chrony/Menu/Menu.xml @@ -1,7 +1,7 @@ - + diff --git a/net/chrony/src/opnsense/mvc/app/views/OPNsense/Chrony/general.volt b/net/chrony/src/opnsense/mvc/app/views/OPNsense/Chrony/index.volt similarity index 85% rename from net/chrony/src/opnsense/mvc/app/views/OPNsense/Chrony/general.volt rename to net/chrony/src/opnsense/mvc/app/views/OPNsense/Chrony/index.volt index 255c464b33..bd27a5a5f9 100644 --- a/net/chrony/src/opnsense/mvc/app/views/OPNsense/Chrony/general.volt +++ b/net/chrony/src/opnsense/mvc/app/views/OPNsense/Chrony/index.volt @@ -38,10 +38,14 @@
    {{ partial("layout_partials/base_form",['fields':generalForm,'id':'frm_general_settings'])}} -
    -
    - -
    + +

    {{ lang._('Sources') }}

    + + {{ partial('layout_partials/base_bootgrid_table', formGridPeer) }} + + {{ partial('layout_partials/base_apply_button', {'data_endpoint': '/api/chrony/service/reconfigure'}) }} + + {{ partial("layout_partials/base_dialog",['fields':formDialogPeer,'id':formGridPeer['edit_dialog_id'],'label':lang._('Edit source')])}}
    @@ -62,7 +66,21 @@
    diff --git a/net/chrony/src/opnsense/service/templates/OPNsense/Chrony/chrony.conf b/net/chrony/src/opnsense/service/templates/OPNsense/Chrony/chrony.conf index 04bd7dbf6e..670c2cc954 100644 --- a/net/chrony/src/opnsense/service/templates/OPNsense/Chrony/chrony.conf +++ b/net/chrony/src/opnsense/service/templates/OPNsense/Chrony/chrony.conf @@ -6,8 +6,8 @@ pidfile /var/run/chrony/chronyd.pid makestep 1 3 {% if not helpers.empty('OPNsense.chrony.general.peers') %} -{% for peer in OPNsense.chrony.general.peers.split(',') %} -server {{ peer }} iburst {% if helpers.exists('OPNsense.chrony.general.ntsclient') and OPNsense.chrony.general.ntsclient == '1' %}nts{% endif %} +{% for peer in OPNsense.chrony.general.peers.peer %} +{% if peer.pool == '1' %}pool {% else %}server {% endif %}{{ peer.address }}{% if peer.prefer == '1' %} prefer{% endif %}{% if peer.iburst == '1' %} iburst{% endif %}{% if peer.xleave == '1' %} xleave{% endif %} {% endfor %} {% endif %} From b3c3ff8c9403a4c226923bd5ea9a51a8ecc7004d Mon Sep 17 00:00:00 2001 From: Matthew Otto Date: Mon, 5 Jan 2026 22:46:50 -0600 Subject: [PATCH 06/12] add minpoll/maxpoll support --- .../OPNsense/Chrony/forms/dialogPeer.xml | 16 ++++++++-------- .../mvc/app/models/OPNsense/Chrony/General.xml | 10 +++++----- .../templates/OPNsense/Chrony/chrony.conf | 3 ++- 3 files changed, 15 insertions(+), 14 deletions(-) diff --git a/net/chrony/src/opnsense/mvc/app/controllers/OPNsense/Chrony/forms/dialogPeer.xml b/net/chrony/src/opnsense/mvc/app/controllers/OPNsense/Chrony/forms/dialogPeer.xml index 66a2bed252..0f2c261516 100644 --- a/net/chrony/src/opnsense/mvc/app/controllers/OPNsense/Chrony/forms/dialogPeer.xml +++ b/net/chrony/src/opnsense/mvc/app/controllers/OPNsense/Chrony/forms/dialogPeer.xml @@ -17,10 +17,10 @@ The address/hostname of the NTP server or pool.
    - peer.iburst - + peer.prefer + checkbox - Enable iburst for this source. + Prefer this source over sources without the prefer option. 6em boolean @@ -28,10 +28,10 @@ - peer.prefer - + peer.iburst + checkbox - Prefer this source over sources without the prefer option. + Enable iburst for this source. 6em boolean @@ -50,13 +50,13 @@ - general.minpoll + peer.minpoll text The minimum interval between requests sent to the server as a power of 2 in seconds. - general.maxpoll + peer.maxpoll text The maximum interval between requests sent to the server as a power of 2 in seconds. diff --git a/net/chrony/src/opnsense/mvc/app/models/OPNsense/Chrony/General.xml b/net/chrony/src/opnsense/mvc/app/models/OPNsense/Chrony/General.xml index 3367735f95..eed78dc6fb 100644 --- a/net/chrony/src/opnsense/mvc/app/models/OPNsense/Chrony/General.xml +++ b/net/chrony/src/opnsense/mvc/app/models/OPNsense/Chrony/General.xml @@ -10,21 +10,21 @@ - 1 + 0 Y
    opnsense.pool.ntp.org Y
    - - 0 - Y - 0 Y + + 0 + Y + 0 Y diff --git a/net/chrony/src/opnsense/service/templates/OPNsense/Chrony/chrony.conf b/net/chrony/src/opnsense/service/templates/OPNsense/Chrony/chrony.conf index 670c2cc954..0d31e8eb17 100644 --- a/net/chrony/src/opnsense/service/templates/OPNsense/Chrony/chrony.conf +++ b/net/chrony/src/opnsense/service/templates/OPNsense/Chrony/chrony.conf @@ -7,7 +7,8 @@ makestep 1 3 {% if not helpers.empty('OPNsense.chrony.general.peers') %} {% for peer in OPNsense.chrony.general.peers.peer %} -{% if peer.pool == '1' %}pool {% else %}server {% endif %}{{ peer.address }}{% if peer.prefer == '1' %} prefer{% endif %}{% if peer.iburst == '1' %} iburst{% endif %}{% if peer.xleave == '1' %} xleave{% endif %} +{% if peer.pool == '1' %}pool {% else %}server {% endif %}{{peer.address}}{% if peer.prefer == '1' %} prefer{% endif %}{% if peer.iburst == '1' %} iburst{% endif %}{% if peer.xleave == '1' %} xleave{% endif %}{% if peer.minpoll is defined and peer.minpoll != '' %} minpoll{{ peer.minpoll }}{% endif %}{% if peer.maxpoll is defined and peer.maxpoll != '' %} maxpoll {{ peer.maxpoll }}{% endif %} + {% endfor %} {% endif %} From 4940481d61edf267e6227b1feed31c6e83915905 Mon Sep 17 00:00:00 2001 From: Matthew Otto Date: Mon, 5 Jan 2026 22:50:32 -0600 Subject: [PATCH 07/12] fix minpoll bug --- .../src/opnsense/service/templates/OPNsense/Chrony/chrony.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/chrony/src/opnsense/service/templates/OPNsense/Chrony/chrony.conf b/net/chrony/src/opnsense/service/templates/OPNsense/Chrony/chrony.conf index 0d31e8eb17..591d993d1a 100644 --- a/net/chrony/src/opnsense/service/templates/OPNsense/Chrony/chrony.conf +++ b/net/chrony/src/opnsense/service/templates/OPNsense/Chrony/chrony.conf @@ -7,7 +7,7 @@ makestep 1 3 {% if not helpers.empty('OPNsense.chrony.general.peers') %} {% for peer in OPNsense.chrony.general.peers.peer %} -{% if peer.pool == '1' %}pool {% else %}server {% endif %}{{peer.address}}{% if peer.prefer == '1' %} prefer{% endif %}{% if peer.iburst == '1' %} iburst{% endif %}{% if peer.xleave == '1' %} xleave{% endif %}{% if peer.minpoll is defined and peer.minpoll != '' %} minpoll{{ peer.minpoll }}{% endif %}{% if peer.maxpoll is defined and peer.maxpoll != '' %} maxpoll {{ peer.maxpoll }}{% endif %} +{% if peer.pool == '1' %}pool {% else %}server {% endif %}{{peer.address}}{% if peer.prefer == '1' %} prefer{% endif %}{% if peer.iburst == '1' %} iburst{% endif %}{% if peer.xleave == '1' %} xleave{% endif %}{% if peer.minpoll is defined and peer.minpoll != '' %} minpoll {{ peer.minpoll }}{% endif %}{% if peer.maxpoll is defined and peer.maxpoll != '' %} maxpoll {{ peer.maxpoll }}{% endif %} {% endfor %} {% endif %} From 5bb39b1cef542f385602e259bf50385bf341c648 Mon Sep 17 00:00:00 2001 From: Matthew Otto Date: Mon, 5 Jan 2026 23:13:52 -0600 Subject: [PATCH 08/12] replace apply with save (entire config updates now) --- .../mvc/app/views/OPNsense/Chrony/index.volt | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/net/chrony/src/opnsense/mvc/app/views/OPNsense/Chrony/index.volt b/net/chrony/src/opnsense/mvc/app/views/OPNsense/Chrony/index.volt index bd27a5a5f9..998b54351c 100644 --- a/net/chrony/src/opnsense/mvc/app/views/OPNsense/Chrony/index.volt +++ b/net/chrony/src/opnsense/mvc/app/views/OPNsense/Chrony/index.volt @@ -43,9 +43,12 @@ {{ partial('layout_partials/base_bootgrid_table', formGridPeer) }} - {{ partial('layout_partials/base_apply_button', {'data_endpoint': '/api/chrony/service/reconfigure'}) }} - {{ partial("layout_partials/base_dialog",['fields':formDialogPeer,'id':formGridPeer['edit_dialog_id'],'label':lang._('Edit source')])}} + +
    +
    + +
    @@ -151,5 +154,16 @@ $(function() { var targetTab = $(e.target).attr("href"); autoRefresh(targetTab); }); + + // link save button to API set action + $("#saveAct").click(function(){ + saveFormToEndpoint(url="/api/chrony/general/set", formid='frm_general_settings',callback_ok=function(){ + $("#saveAct_progress").addClass("fa fa-spinner fa-pulse"); + ajaxCall(url="/api/chrony/service/reconfigure", sendData={}, callback=function(data,status) { + updateServiceControlUI('chrony'); + $("#saveAct_progress").removeClass("fa fa-spinner fa-pulse"); + }); + }); + }); }); From 8fca10855e2a1bf1a7d5a2bf67ff57cf19b2fcb7 Mon Sep 17 00:00:00 2001 From: Matthew Otto Date: Tue, 6 Jan 2026 00:46:52 -0600 Subject: [PATCH 09/12] fix bug when defining only a single source --- .../app/models/OPNsense/Chrony/General.xml | 2 +- .../templates/OPNsense/Chrony/chrony.conf | 22 +++++++++++-------- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/net/chrony/src/opnsense/mvc/app/models/OPNsense/Chrony/General.xml b/net/chrony/src/opnsense/mvc/app/models/OPNsense/Chrony/General.xml index eed78dc6fb..897957c012 100644 --- a/net/chrony/src/opnsense/mvc/app/models/OPNsense/Chrony/General.xml +++ b/net/chrony/src/opnsense/mvc/app/models/OPNsense/Chrony/General.xml @@ -54,7 +54,7 @@ Y - 323 + 123 Y diff --git a/net/chrony/src/opnsense/service/templates/OPNsense/Chrony/chrony.conf b/net/chrony/src/opnsense/service/templates/OPNsense/Chrony/chrony.conf index 591d993d1a..74ba2732e9 100644 --- a/net/chrony/src/opnsense/service/templates/OPNsense/Chrony/chrony.conf +++ b/net/chrony/src/opnsense/service/templates/OPNsense/Chrony/chrony.conf @@ -1,12 +1,18 @@ {% if helpers.exists('OPNsense.chrony.general.enabled') and OPNsense.chrony.general.enabled == '1' %} port {{ OPNsense.chrony.general.port }} -driftfile /var/db/chrony/drift -pidfile /var/run/chrony/chronyd.pid -makestep 1 3 +{% if not helpers.empty('OPNsense.chrony.general.allowednetworks') %} +{% for network in OPNsense.chrony.general.allowednetworks.split(',') %} +allow {{ network }} +{% endfor %} +{% endif %} {% if not helpers.empty('OPNsense.chrony.general.peers') %} -{% for peer in OPNsense.chrony.general.peers.peer %} +{% set peers = OPNsense.chrony.general.peers.peer %} +{% if peers is mapping %} +{% set peers = [peers] %} +{% endif %} +{% for peer in peers %} {% if peer.pool == '1' %}pool {% else %}server {% endif %}{{peer.address}}{% if peer.prefer == '1' %} prefer{% endif %}{% if peer.iburst == '1' %} iburst{% endif %}{% if peer.xleave == '1' %} xleave{% endif %}{% if peer.minpoll is defined and peer.minpoll != '' %} minpoll {{ peer.minpoll }}{% endif %}{% if peer.maxpoll is defined and peer.maxpoll != '' %} maxpoll {{ peer.maxpoll }}{% endif %} {% endfor %} @@ -19,13 +25,11 @@ server {{ OPNsense.chrony.general.fallbackpeers }} {% if not helpers.empty('OPNsense.chrony.general.localstratum') %} local stratum {{ OPNsense.chrony.general.localstratum }} {% if helpers.exists('OPNsense.chrony.general.orphanmode') and OPNsense.chrony.general.orphanmode == '1' %}orphan{% endif %} -{% endif %} -{% if not helpers.empty('OPNsense.chrony.general.allowednetworks') %} -{% for network in OPNsense.chrony.general.allowednetworks.split(',') %} -allow {{ network }} -{% endfor %} {% endif %} +driftfile /var/db/chrony/drift +pidfile /var/run/chrony/chronyd.pid +makestep 1 3 {% if helpers.exists('OPNsense.chrony.general.ntsclient') and OPNsense.chrony.general.ntsclient == '1' %} ntsdumpdir /var/lib/chrony From 32e8b9e9a5b634ebcc11a3b4a9a5685eb1c2a8f3 Mon Sep 17 00:00:00 2001 From: Matthew Otto Date: Tue, 6 Jan 2026 01:01:48 -0600 Subject: [PATCH 10/12] make diagnostic tabs show column descriptions --- .../src/opnsense/service/conf/actions.d/actions_chrony.conf | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/net/chrony/src/opnsense/service/conf/actions.d/actions_chrony.conf b/net/chrony/src/opnsense/service/conf/actions.d/actions_chrony.conf index 8d3e6d21ad..74c4253f9d 100644 --- a/net/chrony/src/opnsense/service/conf/actions.d/actions_chrony.conf +++ b/net/chrony/src/opnsense/service/conf/actions.d/actions_chrony.conf @@ -23,13 +23,13 @@ type:script_output message:request chrony status [chronysources] -command:/usr/local/bin/chronyc -m 'timeout 100' 'retries 0' sources +command:/usr/local/bin/chronyc -m 'timeout 100' 'retries 0' 'sources -v' parameters: type:script_output message:show chrony sources [chronysourcestats] -command:/usr/local/bin/chronyc -m 'timeout 100' 'retries 0' sourcestats +command:/usr/local/bin/chronyc -m 'timeout 100' 'retries 0' 'sourcestats -v' parameters: type:script_output message:show chrony sourcestats @@ -41,7 +41,7 @@ type:script_output message:show chrony tracking [chronyauthdata] -command:/usr/local/bin/chronyc -N -m 'timeout 100' 'retries 0' authdata +command:/usr/local/bin/chronyc -N -m 'timeout 100' 'retries 0' 'authdata -v' parameters: type:script_output message:show chrony authdata From 7043f2d6445558cd0fb904b3941e0e231203d7c0 Mon Sep 17 00:00:00 2001 From: Matthew Otto Date: Tue, 6 Jan 2026 11:08:36 -0600 Subject: [PATCH 11/12] add proper NTS support --- .../OPNsense/Chrony/forms/dialogPeer.xml | 11 ++++ .../OPNsense/Chrony/forms/general.xml | 12 ---- .../app/models/OPNsense/Chrony/General.xml | 57 +++++++++---------- .../templates/OPNsense/Chrony/chrony.conf | 11 +--- 4 files changed, 39 insertions(+), 52 deletions(-) diff --git a/net/chrony/src/opnsense/mvc/app/controllers/OPNsense/Chrony/forms/dialogPeer.xml b/net/chrony/src/opnsense/mvc/app/controllers/OPNsense/Chrony/forms/dialogPeer.xml index 0f2c261516..6e498422a5 100644 --- a/net/chrony/src/opnsense/mvc/app/controllers/OPNsense/Chrony/forms/dialogPeer.xml +++ b/net/chrony/src/opnsense/mvc/app/controllers/OPNsense/Chrony/forms/dialogPeer.xml @@ -61,4 +61,15 @@ text The maximum interval between requests sent to the server as a power of 2 in seconds. + + peer.nts + + checkbox + Enable NTS authentication. + + 6em + boolean + boolean + + \ No newline at end of file diff --git a/net/chrony/src/opnsense/mvc/app/controllers/OPNsense/Chrony/forms/general.xml b/net/chrony/src/opnsense/mvc/app/controllers/OPNsense/Chrony/forms/general.xml index a14a75025b..ba5a0ccbee 100644 --- a/net/chrony/src/opnsense/mvc/app/controllers/OPNsense/Chrony/forms/general.xml +++ b/net/chrony/src/opnsense/mvc/app/controllers/OPNsense/Chrony/forms/general.xml @@ -31,22 +31,10 @@ true Set the networks allowed to synchronize time with this server. If this value is not set it will also not listen to the port and just synchronize the time for itself. - - general.ntsclient - - checkbox - Enable NTS in client mode. This will add another layer of security for peers when OPNsense is the client. Every server in Peers has to support NTS. - general.ntsnocert checkbox If you run NTS mode you can enable this option in order to ignore wrong time in certificates for the first check. This helps if your system starts with wrong time. - - general.fallbackpeers - - text - Set fallback peer if you use NTS and your system starts with wrong time. Best to only use this for internal trusted peers. - diff --git a/net/chrony/src/opnsense/mvc/app/models/OPNsense/Chrony/General.xml b/net/chrony/src/opnsense/mvc/app/models/OPNsense/Chrony/General.xml index 897957c012..78f763a850 100644 --- a/net/chrony/src/opnsense/mvc/app/models/OPNsense/Chrony/General.xml +++ b/net/chrony/src/opnsense/mvc/app/models/OPNsense/Chrony/General.xml @@ -7,6 +7,29 @@ 0 Y + + 1 + 15 + N + Local stratum must be within 1-15. + + + 0 + Y + + + 123 + Y + + + N + , + Y + + + 0 + Y + @@ -41,37 +64,11 @@ N maxpoll value must be between -6 and 24. + + 0 + Y + - - 1 - 15 - N - Local stratum must be within 1-15. - - - 0 - Y - - - 123 - Y - - - N - , - Y - - - 0 - Y - - - 0 - Y - - - N - diff --git a/net/chrony/src/opnsense/service/templates/OPNsense/Chrony/chrony.conf b/net/chrony/src/opnsense/service/templates/OPNsense/Chrony/chrony.conf index 74ba2732e9..364f6ea59f 100644 --- a/net/chrony/src/opnsense/service/templates/OPNsense/Chrony/chrony.conf +++ b/net/chrony/src/opnsense/service/templates/OPNsense/Chrony/chrony.conf @@ -13,16 +13,11 @@ allow {{ network }} {% set peers = [peers] %} {% endif %} {% for peer in peers %} -{% if peer.pool == '1' %}pool {% else %}server {% endif %}{{peer.address}}{% if peer.prefer == '1' %} prefer{% endif %}{% if peer.iburst == '1' %} iburst{% endif %}{% if peer.xleave == '1' %} xleave{% endif %}{% if peer.minpoll is defined and peer.minpoll != '' %} minpoll {{ peer.minpoll }}{% endif %}{% if peer.maxpoll is defined and peer.maxpoll != '' %} maxpoll {{ peer.maxpoll }}{% endif %} +{% if peer.pool == '1' %}pool {% else %}server {% endif %}{{peer.address}}{% if peer.prefer == '1' %} prefer{% endif %}{% if peer.iburst == '1' %} iburst{% endif %}{% if peer.xleave == '1' %} xleave{% endif %}{% if peer.minpoll is defined and peer.minpoll != '' %} minpoll {{ peer.minpoll }}{% endif %}{% if peer.maxpoll is defined and peer.maxpoll != '' %} maxpoll {{ peer.maxpoll }}{% endif %}{% if peer.nts == '1' %} nts{% endif %} {% endfor %} {% endif %} -{% if helpers.exists('OPNsense.chrony.general.fallbackpeers') and OPNsense.chrony.general.fallbackpeers != '' %} -authselectmode mix -server {{ OPNsense.chrony.general.fallbackpeers }} -{% endif %} - {% if not helpers.empty('OPNsense.chrony.general.localstratum') %} local stratum {{ OPNsense.chrony.general.localstratum }} {% if helpers.exists('OPNsense.chrony.general.orphanmode') and OPNsense.chrony.general.orphanmode == '1' %}orphan{% endif %} @@ -31,11 +26,7 @@ driftfile /var/db/chrony/drift pidfile /var/run/chrony/chronyd.pid makestep 1 3 -{% if helpers.exists('OPNsense.chrony.general.ntsclient') and OPNsense.chrony.general.ntsclient == '1' %} ntsdumpdir /var/lib/chrony -ntstrustedcerts /usr/local/etc/ssl/cert.pem -nosystemcert -{% endif %} {% if helpers.exists('OPNsense.chrony.general.ntsnocert') and OPNsense.chrony.general.ntsnocert == '1' %} nocerttimecheck 1 From 3f7981faf5bc65804f083edca4e52c09fe36d3a6 Mon Sep 17 00:00:00 2001 From: Matthew Otto Date: Tue, 6 Jan 2026 11:13:52 -0600 Subject: [PATCH 12/12] update pkg-descr --- net/chrony/pkg-descr | 1 + 1 file changed, 1 insertion(+) diff --git a/net/chrony/pkg-descr b/net/chrony/pkg-descr index 53dafbbc8c..7c7d2425b4 100644 --- a/net/chrony/pkg-descr +++ b/net/chrony/pkg-descr @@ -13,6 +13,7 @@ Plugin Changelog - iburst - min/max poll - interleaving +* Add per-source NTS option * Add NTP data diagnostics 1.5