According to this request I am trying to automate setup.
Why NTS important:
- Prevent replay and MITM
- Prevent HTTPS downgrade via making cert "expired"
- Better privacy against corporate networks
Read on the bottom with it need your help
So I wrote this:
#!/bin/sh
# nts.sh
# Run directly on OpenWrt (tested on OP24)
set -e
echo "==> Disabling sysntpd (if present)"
if /etc/init.d/sysntpd >/dev/null 2>&1; then
/etc/init.d/sysntpd stop || true
/etc/init.d/sysntpd disable || true
fi
echo "==> Updating package lists"
opkg update
echo "==> Installing chrony-nts and luci-app-chrony"
opkg install chrony-nts
echo "==> Disabling DHCP-provided NTP sources in UCI"
uci set chrony.@pool[0].disabled='1' 2>/dev/null || true
uci set chrony.@dhcp_ntp_server[0].disabled='1' 2>/dev/null || true
echo "==> Adding NTS servers to UCI (/etc/config/chrony)"
uci set chrony.cloudflare='server'
uci set chrony.cloudflare.hostname='time.cloudflare.com'
uci set chrony.cloudflare.iburst='yes'
uci set chrony.cloudflare.nts='yes'
uci set chrony.time_nl='server'
uci set chrony.time_nl.hostname='ntppool1.time.nl'
uci set chrony.time_nl.iburst='yes'
uci set chrony.time_nl.nts='yes'
uci set chrony.netnod='server'
uci set chrony.netnod.hostname='nts.netnod.se'
uci set chrony.netnod.iburst='yes'
uci set chrony.netnod.nts='yes'
uci set chrony.ptb='server'
uci set chrony.ptb.hostname='ptbtime1.ptb.de'
uci set chrony.ptb.iburst='yes'
uci set chrony.ptb.nts='yes'
uci set chrony.dfm='server'
uci set chrony.dfm.hostname='time.dfm.dk'
uci set chrony.dfm.iburst='yes'
uci set chrony.dfm.nts='yes'
uci set chrony.cifelli='server'
uci set chrony.cifelli.hostname='time.cifelli.xyz'
uci set chrony.cifelli.iburst='yes'
uci set chrony.cifelli.nts='yes'
# Ensure nts section exists and useful options set
uci set chrony.@nts[0].rtccheck='yes' 2>/dev/null || uci add chrony nts; uci set chrony.@nts[-1].rtccheck='yes'
uci set chrony.@nts[0].systemcerts='yes' 2>/dev/null || uci add chrony nts; uci set chrony.@nts[-1].systemcerts='yes'
echo "==> Committing UCI changes"
uci commit chrony
echo "==> Writing persistent chrony drop-in /etc/chrony.d/20-nts.conf"
cat > /etc/chrony.d/20-nts.conf <<'EOF'
# Require at least 2 reachable sources
minsources 2
# Use NTS sources only (require authenticated sources)
authselectmode require
# Disable chronyc remote access from network
cmdport 0
# NTS servers (mirrors those in UCI)
server time.cloudflare.com iburst nts
server ntppool1.time.nl iburst nts
server nts.netnod.se iburst nts
server ptbtime1.ptb.de iburst nts
server time.dfm.dk iburst nts
server time.cifelli.xyz iburst nts
EOF
echo "==> Ensuring /etc/chrony.d is preserved across sysupgrade"
grep -Fxq "/etc/chrony.d/" /etc/sysupgrade.conf 2>/dev/null || echo "/etc/chrony.d/" >> /etc/sysupgrade.conf
echo "==> Adding boot-time copy of /etc/chrony.d -> /var/etc/chrony.d in /etc/rc.local"
# Insert copy lines before the final 'exit 0' (or add file if missing)
RCLOCAL=/etc/rc.local
TMPFILE=$(mktemp)
cp "$RCLOCAL" "${TMPFILE}" 2>/dev/null || echo -e "#!/bin/sh\n\nexit 0" > "${TMPFILE}"
# Remove any previous lines we added
sed -i '/# Begin chrony-d copy/,/# End chrony-d copy/d' "${TMPFILE}" || true
# Insert our lines before exit 0
awk '
BEGIN{insert=0}
/^exit 0/ && !insert{
print "# Begin chrony-d copy (added by setup-nts-openwrt.sh)"
print "sleep 5"
print "mkdir -p \"/var/etc/chrony.d/\" || true"
print "cp -a \"/etc/chrony.d/\"* \"/var/etc/chrony.d/\" 2>/dev/null || true"
print "service chronyd restart 2>/dev/null || true"
print "# End chrony-d copy"
insert=1
}
{print}
' "${TMPFILE}" > "${TMPFILE}.2" && mv "${TMPFILE}.2" "${TMPFILE}"
# Ensure executable and install
chmod +x "${TMPFILE}"
mv "${TMPFILE}" "${RCLOCAL}"
echo "==> Restarting/enabling chronyd and luci"
/etc/init.d/chronyd enable || true
/etc/init.d/chronyd restart || true
echo "==> Waiting 6 seconds for chronyd to initialize"
sleep 6
echo "==> Checking chrony status (chronyc sources)"
if command -v chronyc >/dev/null 2>&1; then
chronyc sources || true
fi
echo "==> NOTE: Consider hardcoding selected NTS server IPs in /etc/hosts if your DNS validates using time (manual step)."
echo "==> NOTE: If menu didn't appeared in LuCi or stats shows nothing, try rebooting your router."
sleep 3
echo "==> NOTE: Due to capability issues, on older OpenWRT, automated LuCi menu installation removed from script. If you are on OP24 run:"
echo "==> opkg install luci-app-chrony"
sleep 3
echo "==> If you will get any certs errors, try running:"
echo "==> uci set chrony.@nts[0].systemcerts='yes'"
sleep 3
echo "==> Finalizing: enabling chrony daemon and done."
/etc/init.d/chronyd restart || true
# fin
SCRIPT_PATH="$0"
if [ -f "$SCRIPT_PATH" ]; then
echo "==> Self-removing script: $SCRIPT_PATH"
rm -f "$SCRIPT_PATH" || true
fi
echo "!!! Done !!!"
exit 0
To run it:
nano nts.sh
And paste it there. Then ctrl+o, enter, ctrl+x, enter. Then:
scp -O nts.sh root@192.168.8.1:/tmp
Then:
ssh root@192.168.1
And:
cd /tmp && chmod +x nts.sh
Then:
./install.sh
Script aims setup NTS automatically and add luci-app-chrony GUI to LUCI
So, I tested it on OP24 Beryl AX. And I don't know will it work on other devices.
So I will be really glad if someone will test it.
Also I ask @bruce and @admon to take a look on it and maybe test it ![]()

