diff --git a/CERTIFICATES.d/01-dialog-A.avif b/CERTIFICATES.d/01-dialog-A.avif index 2fc3c9bd..ec5eb968 100644 Binary files a/CERTIFICATES.d/01-dialog-A.avif and b/CERTIFICATES.d/01-dialog-A.avif differ diff --git a/CERTIFICATES.d/02-dialog-B.avif b/CERTIFICATES.d/02-dialog-B.avif index 5e408abe..b2549c85 100644 Binary files a/CERTIFICATES.d/02-dialog-B.avif and b/CERTIFICATES.d/02-dialog-B.avif differ diff --git a/CERTIFICATES.d/03-window.avif b/CERTIFICATES.d/03-window.avif index 96039a3b..a588614e 100644 Binary files a/CERTIFICATES.d/03-window.avif and b/CERTIFICATES.d/03-window.avif differ diff --git a/CERTIFICATES.d/04-certificate.avif b/CERTIFICATES.d/04-certificate.avif index e6663146..de5ea9f9 100644 Binary files a/CERTIFICATES.d/04-certificate.avif and b/CERTIFICATES.d/04-certificate.avif differ diff --git a/CERTIFICATES.md b/CERTIFICATES.md index 8ebd861e..37e138ce 100644 --- a/CERTIFICATES.md +++ b/CERTIFICATES.md @@ -32,13 +32,13 @@ is to use a desktop browser to get that information. This demonstration uses [Mozilla Firefox ↗️](https://www.mozilla.org/firefox/). Let's assume we want to make sure the certificate for -[git.eworm.de](https://git.eworm.de/) is available. Open that page in the -browser, then click the *lock* icon in addressbar, followed by "*Connection -secure*". +[rsc.eworm.de](https://rsc.eworm.de/) is available. Open that page in the +browser, then click the *shield* icon in addressbar, followed by +"*Connection secure*". ![screenshot: dialog A](CERTIFICATES.d/01-dialog-A.avif) -The dialog will change, click "*More information*". +The dialog will change, click "*More site information*". ![screenshot: dialog B](CERTIFICATES.d/02-dialog-B.avif) diff --git a/INITIAL-COMMANDS.md b/INITIAL-COMMANDS.md index e033b576..df64aa7e 100644 --- a/INITIAL-COMMANDS.md +++ b/INITIAL-COMMANDS.md @@ -18,9 +18,9 @@ Run the complete base installation: { :local BaseUrl "https://rsc.eworm.de/main/"; - :local CertCommonName "Root YE"; - :local CertFileName "Root-YE.pem"; - :local CertFingerprint "e14ffcad5b0025731006caa43a121a22d8e9700f4fb9cf852f02a708aa5d5666"; + :local CertCommonName "ISRG Root X2"; + :local CertFileName "ISRG-Root-X2.pem"; + :local CertFingerprint "69729b8e15a86efc177a57afb7171dfc64add28c2fca8cf1507e34453ccb1470"; :local CertSettings [ /certificate/settings/get ]; :if (!((($CertSettings->"builtin-trust-anchors") = "trusted" || \ diff --git a/README.d/01-download-certs.avif b/README.d/01-download-certs.avif index e4d8755c..f2afeb54 100644 Binary files a/README.d/01-download-certs.avif and b/README.d/01-download-certs.avif differ diff --git a/README.d/03-check-certs.avif b/README.d/03-check-certs.avif index 6610ac47..1f03ad2c 100644 Binary files a/README.d/03-check-certs.avif and b/README.d/03-check-certs.avif differ diff --git a/README.d/12-setup-lease-script.avif b/README.d/12-setup-lease-script.avif index 2a8bcb24..99f9c30c 100644 Binary files a/README.d/12-setup-lease-script.avif and b/README.d/12-setup-lease-script.avif differ diff --git a/README.md b/README.md index b8f5b736..f7143ddf 100644 --- a/README.md +++ b/README.md @@ -126,18 +126,18 @@ If you intend to download the scripts from a different location (for example from github.com) install the corresponding certificate chain. - /tool/fetch "https://rsc.eworm.de/main/certs/Root-YE.pem" dst-path="root-ye.pem"; + /tool/fetch "https://rsc.eworm.de/main/certs/ISRG-Root-X2.pem" dst-path="isrg-root-x2.pem"; ![screenshot: download certs](README.d/01-download-certs.avif) > ℹ️ **Info**: Note that the command above does *not* verify server > certificate, so if you want to be safe download with your workstations's > browser from CA's website and transfer the file to your MikroTik device: -> *Let's Encrypt* / *ISRG* [Root YE ↗️](https://letsencrypt.org/certs/gen-y/root-ye.pem) +> *Let's Encrypt* / *ISRG* [ISRG Root X2 ↗️](https://letsencrypt.org/certs/isrg-root-x2.pem) Then we import the certificate. - /certificate/import file-name="root-ye.pem" passphrase=""; + /certificate/import file-name="isrg-root-x2.pem" passphrase=""; Do not worry that the command is not shown - that happens because it contains a sensitive property, the passphrase. @@ -145,11 +145,11 @@ a sensitive property, the passphrase. ![screenshot: import certs](README.d/02-import-certs.avif) For basic verification we rename the certificate and print it by -fingerprint. Make sure exactly this one certificate ("*Root-YE*") +fingerprint. Make sure exactly this one certificate ("*ISRG-Root-X2*") is shown. - /certificate/set name="Root-YE" [ find where common-name="Root YE" ]; - /certificate/print proplist=name,fingerprint where fingerprint="e14ffcad5b0025731006caa43a121a22d8e9700f4fb9cf852f02a708aa5d5666"; + /certificate/set name="ISRG-Root-X2" [ find where common-name="ISRG Root X2" ]; + /certificate/print proplist=name,fingerprint where fingerprint="69729b8e15a86efc177a57afb7171dfc64add28c2fca8cf1507e34453ccb1470"; ![screenshot: check certs](README.d/03-check-certs.avif) @@ -258,8 +258,8 @@ Some events can run a script. If you want your DHCP hostnames to be available in DNS use `dhcp-to-dns` with the events from dhcp server. For a regular cleanup add a scheduler entry. - $ScriptInstallUpdate dhcp-to-dns,lease-script; - /ip/dhcp-server/set lease-script=lease-script [ find ]; + $ScriptInstallUpdate dhcp-to-dns,dhcpv4-server-lease; + /ip/dhcp-server/set lease-script="dhcpv4-server-lease" [ find ]; /system/scheduler/add name="dhcp-to-dns" interval=5m start-time=startup on-event="/system/script/run dhcp-to-dns;"; ![screenshot: setup lease script](README.d/12-setup-lease-script.avif) @@ -286,6 +286,8 @@ Available scripts * [Use wireless network with daily psk](doc/daily-psk.md) (`daily-psk`) * [Comment DHCP leases with info from access list](doc/dhcp-lease-comment.md) (`dhcp-lease-comment`) * [Create DNS records for DHCP leases](doc/dhcp-to-dns.md) (`dhcp-to-dns`) +* [Run other scripts on IPv4 DHCP server lease](doc/dhcpv4-server-lease.md) (`dhcpv4-server-lease`) +* [Run other scripts on IPv6 DHCP client lease](doc/dhcpv6-client-lease.md) (`dhcpv6-client-lease`) * [Automatically upgrade firmware and reboot](doc/firmware-upgrade-reboot.md) (`firmware-upgrade-reboot`) * [Download, import and update firewall address-lists](doc/fw-addr-lists.md) (`fw-addr-lists`) * [Wait for global functions und modules](doc/global-wait.md) (`global-wait`) @@ -294,7 +296,6 @@ Available scripts * [Create DNS records for IPSec peers](doc/ipsec-to-dns.md) (`ipsec-to-dns`) * [Update configuration on IPv6 prefix change](doc/ipv6-update.md) (`ipv6-update`) * [Manage IP addresses with bridge status](doc/ip-addr-bridge.md) (`ip-addr-bridge`) -* [Run other scripts on DHCP lease](doc/lease-script.md) (`lease-script`) * [Manage LEDs dark mode](doc/leds-mode.md) (`leds-day-mode`, `leds-night-mode` & `leds-toggle-mode`) * [Forward log messages via notification](doc/log-forward.md) (`log-forward`) * [Mode button with multiple presses](doc/mode-button.md) (`mode-button`) diff --git a/backup-email.rsc b/backup-email.rsc index fcafff45..70b18c06 100644 --- a/backup-email.rsc +++ b/backup-email.rsc @@ -16,6 +16,7 @@ do={ :error ("Global config and/or functions not ready."); }; } delay=500ms max=50; :local ScriptName [ :jobname ]; + :global BackupFileNameDate; :global BackupPassword; :global BackupRandomDelay; :global BackupSendBinary; @@ -73,7 +74,9 @@ # filename based on identity :local DirName ("tmpfs/" . $ScriptName); - :local FileName [ $CleanName ($Identity . "." . $Domain) ]; + :local Clock [ /system/clock/get ]; + :local FileName [ $CleanName ($Identity . "." . $Domain . [ $IfThenElse \ + ($BackupFileNameDate = true) ("-" . $Clock->"date" . "-" . $Clock->"time") "" ] ) ]; :local FilePath ($DirName . "/" . $FileName); :local BackupFile "none"; :local ExportFile "none"; diff --git a/backup-upload.rsc b/backup-upload.rsc index bded570c..b89d123d 100644 --- a/backup-upload.rsc +++ b/backup-upload.rsc @@ -17,6 +17,7 @@ do={ :error ("Global config and/or functions not ready."); }; } delay=500ms max=50; :local ScriptName [ :jobname ]; + :global BackupFileNameDate; :global BackupPassword; :global BackupRandomDelay; :global BackupSendBinary; @@ -72,7 +73,9 @@ # filename based on identity :local DirName ("tmpfs/" . $ScriptName); - :local FileName [ $CleanName ($Identity . "." . $Domain) ]; + :local Clock [ /system/clock/get ]; + :local FileName [ $CleanName ($Identity . "." . $Domain . [ $IfThenElse \ + ($BackupFileNameDate = true) ("-" . $Clock->"date" . "-" . $Clock->"time") "" ] ) ]; :local FilePath ($DirName . "/" . $FileName); :local BackupFile "none"; :local ExportFile "none"; diff --git a/certs/Makefile b/certs/Makefile index b0f029ab..8b516e4d 100644 --- a/certs/Makefile +++ b/certs/Makefile @@ -12,12 +12,12 @@ DOMAINS_DUAL = \ cloudflare-dns.com/SSL-com-Root-Certification-Authority-ECC \ dns.google/GTS-Root-RX \ dns.quad9.net/DigiCert-Global-Root-G3 \ - git.eworm.de/Root-YE \ + git.eworm.de/ISRG-Root-X2 \ gitlab.com/USERTrust-RSA-Certification-Authority \ lists.blocklist.de/GTS-Root-R4 \ matrix.org/GTS-Root-R4 \ - raw.githubusercontent.com/USERTrust-RSA-Certification-Authority \ - rsc.eworm.de/Root-YE \ + raw.githubusercontent.com/ISRG-Root-X1 \ + rsc.eworm.de/ISRG-Root-X2 \ upgrade.mikrotik.com/ISRG-Root-X1 DOMAINS_IPV4 = \ 1.1.1.1/SSL-com-Root-Certification-Authority-ECC \ diff --git a/check-certificates.rsc b/check-certificates.rsc index 0122122a..7e2fed04 100644 --- a/check-certificates.rsc +++ b/check-certificates.rsc @@ -60,8 +60,8 @@ http-header-field=({ [ $FetchUserAgentStr $ScriptName ] }) \ ($CertRenewUrl . $CertFileName) dst-path=$CertFileName as-value; } do={ - :if ($Err != "Fetch failed with status 404") do={ - $LogPrint warning $0 ("Failed fetching certificate: " . $Err); + :if (!($Err ~ "[Ss]tatus 404")) do={ + $LogPrint warning $0 ("Failed fetching certificate by '" . $FetchName . "': " . $Err); } :error false; } @@ -164,7 +164,8 @@ } $WaitFullyConnected; - :foreach Cert in=[ /certificate/find where !revoked !ca !scep-url expires-after<$CertRenewTime ] do={ + :foreach Cert in=[ /certificate/find where !revoked !scep-url expires-after<$CertRenewTime \ + !ca (common-name or subject-alt-name) ] do={ :local CertVal [ /certificate/get $Cert ]; :local LastName; :local FetchName; @@ -177,9 +178,11 @@ $LogPrint info $ScriptName ("Attempting to renew certificate '" . ($CertVal->"name") . "'."); :local ImportSuccess false; - :set LastName ($CertVal->"common-name"); - :set FetchName $LastName; - :set ImportSuccess [ $CheckCertificatesDownloadImport $ScriptName $LastName $FetchName ]; + :if ([ :len ($CertVal->"common-name") ] > 0) do={ + :set LastName ($CertVal->"common-name"); + :set FetchName $LastName; + :set ImportSuccess [ $CheckCertificatesDownloadImport $ScriptName $LastName $FetchName ]; + } :foreach SAN in=($CertVal->"subject-alt-name") do={ :if ($ImportSuccess = false) do={ :set LastName [ :pick $SAN ([ :find $SAN ":" ] + 1) [ :len $SAN ] ]; @@ -242,8 +245,8 @@ } } - :foreach Cert in=[ /certificate/find where !revoked !scep-url !(expires-after=[]) \ - expires-after<$CertWarnTime !(fingerprint=[]) ] do={ + :foreach Cert in=[ /certificate/find where !revoked !scep-url expires-after<$CertWarnTime \ + !(expires-after=[]) !(fingerprint=[]) ] do={ :local CertVal [ /certificate/get $Cert ]; :if ([ :len [ /certificate/scep-server/find where ca-cert=($CertVal->"ca") ] ] > 0) do={ diff --git a/collect-wireless-mac.capsman.rsc b/collect-wireless-mac.capsman.rsc index 8bb33d87..126153a0 100644 --- a/collect-wireless-mac.capsman.rsc +++ b/collect-wireless-mac.capsman.rsc @@ -3,7 +3,7 @@ # Copyright (c) 2013-2026 Christian Hesse # https://rsc.eworm.de/COPYING.md # -# provides: lease-script, order=40 +# provides: dhcpv4-server-lease, order=40 # requires RouterOS, version=7.19 # # collect wireless mac adresses in access list diff --git a/collect-wireless-mac.local.rsc b/collect-wireless-mac.local.rsc index 9d41d420..f3ede554 100644 --- a/collect-wireless-mac.local.rsc +++ b/collect-wireless-mac.local.rsc @@ -3,7 +3,7 @@ # Copyright (c) 2013-2026 Christian Hesse # https://rsc.eworm.de/COPYING.md # -# provides: lease-script, order=40 +# provides: dhcpv4-server-lease, order=40 # requires RouterOS, version=7.19 # # collect wireless mac adresses in access list diff --git a/collect-wireless-mac.template.rsc b/collect-wireless-mac.template.rsc index 837726e3..1e1ed139 100644 --- a/collect-wireless-mac.template.rsc +++ b/collect-wireless-mac.template.rsc @@ -3,7 +3,7 @@ # Copyright (c) 2013-2026 Christian Hesse # https://rsc.eworm.de/COPYING.md # -# provides: lease-script, order=40 +# provides: dhcpv4-server-lease, order=40 # requires RouterOS, version=7.19 # # collect wireless mac adresses in access list diff --git a/collect-wireless-mac.wifi.rsc b/collect-wireless-mac.wifi.rsc index d07ff357..35165a92 100644 --- a/collect-wireless-mac.wifi.rsc +++ b/collect-wireless-mac.wifi.rsc @@ -3,7 +3,7 @@ # Copyright (c) 2013-2026 Christian Hesse # https://rsc.eworm.de/COPYING.md # -# provides: lease-script, order=40 +# provides: dhcpv4-server-lease, order=40 # requires RouterOS, version=7.19 # # collect wireless mac adresses in access list diff --git a/contrib/html.sh b/contrib/html.sh index 03eba23d..adacbbb8 100755 --- a/contrib/html.sh +++ b/contrib/html.sh @@ -13,7 +13,9 @@ sed \ markdown -f toc,idanchor "${1}" | sed \ -e 's/href="\([-_\./[:alnum:]]*\)\.md\(#[-[:alnum:]]*\)\?"/href="\1.html\2"/g' \ -e '/| id="\L\1">|' \ - -e '//s|pre|pre class="code" onclick="CopyToClipboard(this)"|g' \ -e '/The above link may be broken on code hosting sites/s|blockquote|blockquote style="display: none;"|' diff --git a/contrib/logo-color.d/browser-01.avif b/contrib/logo-color.d/browser-01.avif index 3dc0a1f9..f758d1e7 100644 Binary files a/contrib/logo-color.d/browser-01.avif and b/contrib/logo-color.d/browser-01.avif differ diff --git a/contrib/logo-color.d/browser-02.avif b/contrib/logo-color.d/browser-02.avif index 1867fbe3..7679be16 100644 Binary files a/contrib/logo-color.d/browser-02.avif and b/contrib/logo-color.d/browser-02.avif differ diff --git a/contrib/logo-color.d/browser-03.avif b/contrib/logo-color.d/browser-03.avif index dc24bbbc..95732ad8 100644 Binary files a/contrib/logo-color.d/browser-03.avif and b/contrib/logo-color.d/browser-03.avif differ diff --git a/contrib/logo-color.html b/contrib/logo-color.html index 3e9c05fe..c39a20b8 100644 --- a/contrib/logo-color.html +++ b/contrib/logo-color.html @@ -45,7 +45,7 @@ logo and download it.

Screenshot Browser 03

(This example is with -Firefox. The workflow +Mozilla Firefox ↗️. The workflow for other browsers may differ.)

See how to diff --git a/contrib/telegram.md b/contrib/telegram.md index 36f7bc4e..192fd6cb 100644 --- a/contrib/telegram.md +++ b/contrib/telegram.md @@ -91,6 +91,14 @@ Notes /save dhcp-to-dns Create DNS records for DHCP leases with [dhcp-to-dns](https://rsc.eworm.de/doc/dhcp-to-dns.md). +#### dhcpv4-server-lease + + /save dhcpv4-server-lease Run other scripts on IPv4 DHCP server lease with [dhcpv4-server-lease](https://rsc.eworm.de/doc/dhcpv4-server-lease.md). + +#### dhcpv6-client-lease + + /save dhcpv6-client-lease Run other scripts on IPv6 DHCP client lease with [dhcpv6-client-lease](https://rsc.eworm.de/doc/dhcpv6-client-lease.md). + #### firmware-upgrade-reboot /save firmware-upgrade-reboot Automatically upgrade firmware and reboot with [firmware-upgrade-reboot](https://rsc.eworm.de/doc/firmware-upgrade-reboot.md). @@ -123,10 +131,6 @@ Notes /save ip-addr-bridge Manage IP addresses with [ip-addr-bridge](https://rsc.eworm.de/doc/ip-addr-bridge.md). -#### lease-script - - /save lease-script Run other scripts on DHCP lease with [lease-script](https://rsc.eworm.de/doc/lease-script.md). - #### leds-mode /save leds-mode Manage LEDs dark mode with [leds-mode](https://rsc.eworm.de/doc/leds-mode.md). diff --git a/dhcp-lease-comment.capsman.rsc b/dhcp-lease-comment.capsman.rsc index 7fd00a09..d039cb02 100644 --- a/dhcp-lease-comment.capsman.rsc +++ b/dhcp-lease-comment.capsman.rsc @@ -3,7 +3,7 @@ # Copyright (c) 2013-2026 Christian Hesse # https://rsc.eworm.de/COPYING.md # -# provides: lease-script, order=60 +# provides: dhcpv4-server-lease, order=60 # requires RouterOS, version=7.19 # # update dhcp-server lease comment with infos from access-list diff --git a/dhcp-lease-comment.local.rsc b/dhcp-lease-comment.local.rsc index 7ed6823a..2c5b4efa 100644 --- a/dhcp-lease-comment.local.rsc +++ b/dhcp-lease-comment.local.rsc @@ -3,7 +3,7 @@ # Copyright (c) 2013-2026 Christian Hesse # https://rsc.eworm.de/COPYING.md # -# provides: lease-script, order=60 +# provides: dhcpv4-server-lease, order=60 # requires RouterOS, version=7.19 # # update dhcp-server lease comment with infos from access-list diff --git a/dhcp-lease-comment.template.rsc b/dhcp-lease-comment.template.rsc index c901de40..a06983f0 100644 --- a/dhcp-lease-comment.template.rsc +++ b/dhcp-lease-comment.template.rsc @@ -3,7 +3,7 @@ # Copyright (c) 2013-2026 Christian Hesse # https://rsc.eworm.de/COPYING.md # -# provides: lease-script, order=60 +# provides: dhcpv4-server-lease, order=60 # requires RouterOS, version=7.19 # # update dhcp-server lease comment with infos from access-list diff --git a/dhcp-lease-comment.wifi.rsc b/dhcp-lease-comment.wifi.rsc index d481def8..3fea1bf6 100644 --- a/dhcp-lease-comment.wifi.rsc +++ b/dhcp-lease-comment.wifi.rsc @@ -3,7 +3,7 @@ # Copyright (c) 2013-2026 Christian Hesse # https://rsc.eworm.de/COPYING.md # -# provides: lease-script, order=60 +# provides: dhcpv4-server-lease, order=60 # requires RouterOS, version=7.19 # # update dhcp-server lease comment with infos from access-list diff --git a/dhcp-to-dns.rsc b/dhcp-to-dns.rsc index 94c7492e..37aaadac 100644 --- a/dhcp-to-dns.rsc +++ b/dhcp-to-dns.rsc @@ -3,7 +3,7 @@ # Copyright (c) 2013-2026 Christian Hesse # https://rsc.eworm.de/COPYING.md # -# provides: lease-script, order=20 +# provides: dhcpv4-server-lease, order=20 # requires RouterOS, version=7.19 # # check DHCP leases and add/remove/update DNS entries @@ -51,7 +51,6 @@ active-address=($DnsRecordVal->"address") server=($DnsRecordInfo->"server") status=bound ] ] > 0) do={ $LogPrint debug $ScriptName ("Lease for " . $MacInServer . " (" . $DnsRecordVal->"name" . ") still exists. Not deleting record."); } else={ - :local Found false; $LogPrint info $ScriptName ("Lease expired for " . $MacInServer . ", deleting record (" . $DnsRecordVal->"name" . ")."); /ip/dns/static/remove $DnsRecord; /ip/dns/static/remove [ find where type=CNAME comment=($DnsRecordVal->"comment") ]; diff --git a/dhcpv4-server-lease.rsc b/dhcpv4-server-lease.rsc new file mode 100644 index 00000000..899674d1 --- /dev/null +++ b/dhcpv4-server-lease.rsc @@ -0,0 +1,65 @@ +#!rsc by RouterOS +# RouterOS script: dhcpv4-server-lease +# Copyright (c) 2013-2026 Christian Hesse +# https://rsc.eworm.de/COPYING.md +# +# requires RouterOS, version=7.19 +# +# run scripts on IPv4 DHCP server lease +# https://rsc.eworm.de/doc/dhcpv4-server-lease.md + +:local ExitOK false; +:onerror Err { + :global GlobalConfigReady; :global GlobalFunctionsReady; + :retry { :if ($GlobalConfigReady != true || $GlobalFunctionsReady != true) \ + do={ :error ("Global config and/or functions not ready."); }; } delay=500ms max=50; + :local ScriptName [ :jobname ]; + + :global Grep; + :global IfThenElse; + :global LogPrint; + :global ParseKeyValueStore; + :global ScriptLock; + + :if ([ :typeof $leaseActIP ] = "nothing" || \ + [ :typeof $leaseActMAC ] = "nothing" || \ + [ :typeof $leaseServerName ] = "nothing" || \ + [ :typeof $leaseBound ] = "nothing") do={ + $LogPrint error $ScriptName ("This script is supposed to run from ip dhcp-server."); + :set ExitOK true; + :error false; + } + + $LogPrint debug $ScriptName ("DHCP Server " . $leaseServerName . " " . [ $IfThenElse ($leaseBound = 0) \ + "de" "" ] . "assigned lease " . $leaseActIP . " to " . $leaseActMAC); + + :if ([ $ScriptLock $ScriptName 10 ] = false) do={ + :set ExitOK true; + :error false; + } + + :if ([ :len [ /system/script/job/find where script=$ScriptName ] ] > 1) do={ + $LogPrint debug $ScriptName ("More invocations are waiting, exiting early."); + :set ExitOK true; + :error true; + } + + :local RunOrder ({}); + :foreach Script in=[ /system/script/find where source~("\n# provides: dhcpv4-server-lease\\b") ] do={ + :local ScriptVal [ /system/script/get $Script ]; + :local Store [ $ParseKeyValueStore [ $Grep ($ScriptVal->"source") ("\23 provides: dhcpv4-server-lease, ") ] ]; + + :set ($RunOrder->($Store->"order" . "-" . $ScriptVal->"name")) ($ScriptVal->"name"); + } + + :foreach Order,Script in=$RunOrder do={ + :onerror Err { + $LogPrint debug $ScriptName ("Running script with order " . $Order . ": " . $Script); + /system/script/run $Script; + } do={ + $LogPrint warning $ScriptName ("Running script '" . $Script . "' failed: " . $Err); + } + } +} do={ + :global ExitError; $ExitError $ExitOK [ :jobname ] $Err; +} diff --git a/dhcpv6-client-lease.rsc b/dhcpv6-client-lease.rsc new file mode 100644 index 00000000..d3c1b9aa --- /dev/null +++ b/dhcpv6-client-lease.rsc @@ -0,0 +1,63 @@ +#!rsc by RouterOS +# RouterOS script: dhcpv6-client-lease +# Copyright (c) 2026 Christian Hesse +# https://rsc.eworm.de/COPYING.md +# +# requires RouterOS, version=7.19 +# +# run scripts on IPv6 DHCP client lease +# https://rsc.eworm.de/doc/dhcpv6-client-lease.md + +:local ExitOK false; +:onerror Err { + :global GlobalConfigReady; :global GlobalFunctionsReady; + :retry { :if ($GlobalConfigReady != true || $GlobalFunctionsReady != true) \ + do={ :error ("Global config and/or functions not ready."); }; } delay=500ms max=50; + :local ScriptName [ :jobname ]; + + :global Grep; + :global LogPrint; + :global ParseKeyValueStore; + :global ScriptLock; + + :if ([ $ScriptLock $ScriptName 10 ] = false) do={ + :set ExitOK true; + :error false; + } + + :if (([ :typeof $"na-address" ] = "nothing" || [ :typeof $"na-valid" ] = "nothing") && \ + ([ :typeof $"pd-prefix" ] = "nothing" || [ :typeof $"pd-valid" ] = "nothing")) do={ + $LogPrint error $ScriptName ("This script is supposed to run from ipv6 dhcp-client."); + :set ExitOK true; + :error false; + } + + :global DHCPv6ClientLeaseVars { + "na-address"=$"na-address"; + "na-valid"=$"na-valid"; + "pd-prefix"=$"pd-prefix"; + "pd-valid"=$"pd-valid"; + "options"=$"options" }; + + :local RunOrder ({}); + :foreach Script in=[ /system/script/find where source~("\n# provides: dhcpv6-client-lease\\b") ] do={ + :local ScriptVal [ /system/script/get $Script ]; + :local Store [ $ParseKeyValueStore [ $Grep ($ScriptVal->"source") ("\23 provides: dhcpv6-client-lease, ") ] ]; + + :set ($RunOrder->($Store->"order" . "-" . $ScriptVal->"name")) ($ScriptVal->"name"); + } + + :foreach Order,Script in=$RunOrder do={ + :onerror Err { + $LogPrint debug $ScriptName ("Running script with order " . $Order . ": " . $Script); + /system/script/run $Script; + } do={ + $LogPrint warning $ScriptName ("Running script '" . $Script . "' failed: " . $Err); + } + } + + :set DHCPv6ClientLeaseVars; +} do={ + :global DHCPv6ClientLeaseVars; :set DHCPv6ClientLeaseVars; + :global ExitError; $ExitError $ExitOK [ :jobname ] $Err; +} diff --git a/doc/backup-email.md b/doc/backup-email.md index cf334697..e55a0d7b 100644 --- a/doc/backup-email.md +++ b/doc/backup-email.md @@ -34,6 +34,7 @@ Configuration The configuration goes to `global-config-overlay`, these are the parameters: +* `BackupFileNameDate`: whether to add date & time in filenames * `BackupSendBinary`: whether to send binary backup * `BackupSendExport`: whether to send configuration export * `BackupSendGlobalConfig`: whether to send `global-config-overlay` diff --git a/doc/backup-upload.md b/doc/backup-upload.md index 221cb721..bbf5227b 100644 --- a/doc/backup-upload.md +++ b/doc/backup-upload.md @@ -40,6 +40,7 @@ Configuration The configuration goes to `global-config-overlay`, these are the parameters: +* `BackupFileNameDate`: whether to add date & time in filenames * `BackupSendBinary`: whether to send binary backup * `BackupSendExport`: whether to send configuration export * `BackupSendGlobalConfig`: whether to send `global-config-overlay` diff --git a/doc/check-certificates.md b/doc/check-certificates.md index 1e69af46..c6db7c88 100644 --- a/doc/check-certificates.md +++ b/doc/check-certificates.md @@ -85,7 +85,7 @@ Given you have a certificate on you server, you can use `check-certificates` for the initial import. Just create a *dummy* certificate with short lifetime that matches criteria to be renewed: - /certificate/add name=example.com common-name=example.com days-valid=1; + /certificate/add name="example.com" common-name="example.com" subject-alt-name="DNS:example.com" days-valid=1; /certificate/sign example.com; /system/script/run check-certificates; diff --git a/doc/collect-wireless-mac.md b/doc/collect-wireless-mac.md index 9fef2adb..96b314e1 100644 --- a/doc/collect-wireless-mac.md +++ b/doc/collect-wireless-mac.md @@ -64,14 +64,14 @@ Usage and invocation Run this script from a dhcp server as lease-script to collect the MAC address when a new address is leased. You may want to use -[lease-script](lease-script.md). +[dhcpv4-server-lease](dhcpv4-server-lease.md). See also -------- * [Comment DHCP leases with info from access list](dhcp-lease-comment.md) * [Create DNS records for DHCP leases](dhcp-to-dns.md) -* [Run other scripts on DHCP lease](lease-script.md) +* [Run other scripts on IPv4 DHCP server lease](dhcpv4-server-lease.md) --- [⬅️ Go back to main README](../README.md) diff --git a/doc/dhcp-lease-comment.md b/doc/dhcp-lease-comment.md index 2bda80be..1a4e23aa 100644 --- a/doc/dhcp-lease-comment.md +++ b/doc/dhcp-lease-comment.md @@ -50,14 +50,14 @@ Usage and invocation Run this script from a dhcp server as lease-script to update the comment just after a new address is leased. You may want to use -[lease-script](lease-script.md). +[dhcpv4-server-lease](dhcpv4-server-lease.md). See also -------- * [Collect MAC addresses in wireless access list](collect-wireless-mac.md) * [Create DNS records for DHCP leases](dhcp-to-dns.md) -* [Run other scripts on DHCP lease](lease-script.md) +* [Run other scripts on IPv4 DHCP server lease](dhcpv4-server-lease.md) --- [⬅️ Go back to main README](../README.md) diff --git a/doc/dhcp-to-dns.md b/doc/dhcp-to-dns.md index 3636dfa3..8c282b00 100644 --- a/doc/dhcp-to-dns.md +++ b/doc/dhcp-to-dns.md @@ -28,7 +28,7 @@ Just install the script: $ScriptInstallUpdate dhcp-to-dns; Then run it from dhcp server as lease script. You may want to use -[lease-script](lease-script.md). +[dhcpv4-server-lease](dhcpv4-server-lease.md). A scheduler cares about cleanup: @@ -80,13 +80,30 @@ Note this information can be configured in wireless access list with then due to script execution order. Decrease the scheduler interval to reduce the effect. +Frequently asked questions +-------------------------- + +### Is it possible to have the hostname in reverse lookup? + +It used to be like that in the beginning. But there are way too many special +cases... Devices without hostname, devices with same hostname, devices +switching from one network to another, devices with same hostname in +different network, ... + +Fixing one broke another. It never really worked without problems. So +finally the code ended with what we have now. + +I know about that side effect and limitation with reverse lookup, but +there's really no (easy) way to get that right without mac address. The +reverse lookup will always give a name based on mac address. + See also -------- * [Collect MAC addresses in wireless access list](collect-wireless-mac.md) * [Comment DHCP leases with info from access list](dhcp-lease-comment.md) +* [Run other scripts on IPv4 DHCP server lease](dhcpv4-server-lease.md) * [Create DNS records for IPSec peers](ipsec-to-dns.md) -* [Run other scripts on DHCP lease](lease-script.md) --- [⬅️ Go back to main README](../README.md) diff --git a/doc/dhcpv4-server-lease.md b/doc/dhcpv4-server-lease.md new file mode 100644 index 00000000..e588101d --- /dev/null +++ b/doc/dhcpv4-server-lease.md @@ -0,0 +1,54 @@ +Run other scripts on IPv4 DHCP server lease +=========================================== + +[![GitHub stars](https://img.shields.io/github/stars/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=red)](https://github.com/eworm-de/routeros-scripts/stargazers) +[![GitHub forks](https://img.shields.io/github/forks/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=green)](https://github.com/eworm-de/routeros-scripts/network) +[![GitHub watchers](https://img.shields.io/github/watchers/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=blue)](https://github.com/eworm-de/routeros-scripts/watchers) +[![required RouterOS version](https://img.shields.io/badge/RouterOS-7.19-yellow?style=flat)](https://mikrotik.com/download/changelogs/) +[![Telegram group @routeros_scripts](https://img.shields.io/badge/Telegram-%40routeros__scripts-%2326A5E4?logo=telegram&style=flat)](https://t.me/routeros_scripts) +[![donate with PayPal](https://img.shields.io/badge/Like_it%3F-Donate!-orange?logo=githubsponsors&logoColor=orange&style=flat)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J) + +[⬅️ Go back to main README](../README.md) + +> ℹ️ **Info**: This script can not be used on its own but requires the base +> installation. See [main README](../README.md) for details. + +Description +----------- + +This script is supposed to run from IPv4 DHCP server as lease script. On a +DHCP lease it runs each script containing the following line, where `##` is +a decimal number for ordering: + + # provides: dhcpv4-server-lease, order=## + +Currently it runs if available, in order: + +* [dhcp-to-dns](dhcp-to-dns.md) +* [collect-wireless-mac](collect-wireless-mac.md) +* [dhcp-lease-comment](dhcp-lease-comment.md) +* `hotspot-to-wpa-cleanup`, which is an optional cleanup script + of [hotspot-to-wpa](hotspot-to-wpa.md) + +Requirements and installation +----------------------------- + +Just install the script: + + $ScriptInstallUpdate dhcpv4-server-lease; + +... and add it as `lease-script` to your dhcp server: + + /ip/dhcp-server/set lease-script="dhcpv4-server-lease" [ find ]; + +See also +-------- + +* [Collect MAC addresses in wireless access list](collect-wireless-mac.md) +* [Comment DHCP leases with info from access list](dhcp-lease-comment.md) +* [Create DNS records for DHCP leases](dhcp-to-dns.md) +* [Use WPA network with hotspot credentials](hotspot-to-wpa.md) + +--- +[⬅️ Go back to main README](../README.md) +[⬆️ Go back to top](#top) diff --git a/doc/dhcpv6-client-lease.md b/doc/dhcpv6-client-lease.md new file mode 100644 index 00000000..c126f2d4 --- /dev/null +++ b/doc/dhcpv6-client-lease.md @@ -0,0 +1,65 @@ +Run other scripts on IPv6 DHCP client lease +=========================================== + +[![GitHub stars](https://img.shields.io/github/stars/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=red)](https://github.com/eworm-de/routeros-scripts/stargazers) +[![GitHub forks](https://img.shields.io/github/forks/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=green)](https://github.com/eworm-de/routeros-scripts/network) +[![GitHub watchers](https://img.shields.io/github/watchers/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=blue)](https://github.com/eworm-de/routeros-scripts/watchers) +[![required RouterOS version](https://img.shields.io/badge/RouterOS-7.19-yellow?style=flat)](https://mikrotik.com/download/changelogs/) +[![Telegram group @routeros_scripts](https://img.shields.io/badge/Telegram-%40routeros__scripts-%2326A5E4?logo=telegram&style=flat)](https://t.me/routeros_scripts) +[![donate with PayPal](https://img.shields.io/badge/Like_it%3F-Donate!-orange?logo=githubsponsors&logoColor=orange&style=flat)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J) + +[⬅️ Go back to main README](../README.md) + +> ℹ️ **Info**: This script can not be used on its own but requires the base +> installation. See [main README](../README.md) for details. + +Description +----------- + +This script is supposed to run from IPv6 DHCP client as lease script. On a +DHCP leasse it runs each script containing the following line, where `##` is +a decimal number for ordering: + + # provides: dhcpv6-client-lease, order=## + +The lease script is started with some variables injected, but these are not +available in child scripts. However this script makes these variables +available with a global variable. This code is required in child script: + + :global EitherOr; + + :global DHCPv6ClientLeaseVars; + + :local NaAddress [ $EitherOr $"na-address" ($DHCPv6ClientLeaseVars->"na-address") ]; + :local NaValid [ $EitherOr $"na-valid" ($DHCPv6ClientLeaseVars->"na-valid") ]; + :local PdPrefix [ $EitherOr $"pd-prefix" ($DHCPv6ClientLeaseVars->"pd-prefix") ]; + :local PdValid [ $EitherOr $"pd-valid" ($DHCPv6ClientLeaseVars->"pd-valid") ]; + :local Options [ $EitherOr $"options" ($DHCPv6ClientLeaseVars->"options") ]; + +The values are available under different name then, use `$PdPrefix` instead +of `$"pd-prefix"`, and so on. The resulting script supports both, being a +lease script itself or being run as child. + +Currently it runs if available, in order: + +* [ipv6-update](ipv6-update.md) + +Requirements and installation +----------------------------- + +Just install the script: + + $ScriptInstallUpdate dhcpv6-client-lease; + +... and add it as `lease-script` to your dhcp client: + + /ipv6/dhcp-client/set lease-script="dhcpv6-client-lease" [ find ]; + +See also +-------- + +* [Update configuration on IPv6 prefix change](ipv6-update.md) + +--- +[⬅️ Go back to main README](../README.md) +[⬆️ Go back to top](#top) diff --git a/doc/hotspot-to-wpa.md b/doc/hotspot-to-wpa.md index 22d1dd13..e4e77098 100644 --- a/doc/hotspot-to-wpa.md +++ b/doc/hotspot-to-wpa.md @@ -49,24 +49,24 @@ and add a scheduler. For `wifi`: - $ScriptInstallUpdate hotspot-to-wpa-cleanup.wifi,lease-script; - /system/scheduler/add interval=1d name=hotspot-to-wpa-cleanup on-event="/system/script/run hotspot-to-wpa-cleanup.wifi;" start-time=startup; + $ScriptInstallUpdate hotspot-to-wpa-cleanup.wifi,dhcpv4-server-lease; + /system/scheduler/add interval=1d name="hotspot-to-wpa-cleanup" on-event="/system/script/run hotspot-to-wpa-cleanup.wifi;" start-time=startup; For legacy CAPsMAN: - $ScriptInstallUpdate hotspot-to-wpa-cleanup.capsman,lease-script; - /system/scheduler/add interval=1d name=hotspot-to-wpa-cleanup on-event="/system/script/run hotspot-to-wpa-cleanup.capsman;" start-time=startup; + $ScriptInstallUpdate hotspot-to-wpa-cleanup.capsman,dhcpv4-server-lease; + /system/scheduler/add interval=1d name="hotspot-to-wpa-cleanup" on-event="/system/script/run hotspot-to-wpa-cleanup.capsman;" start-time=startup; And add the lease script and matcher comment to your wpa interfaces' dhcp server. You can add more information to the comment, separated by comma. In this example the server is called `hotspot-to-wpa`. - /ip/dhcp-server/set lease-script=lease-script comment="hotspot-to-wpa=wpa" hotspot-to-wpa; + /ip/dhcp-server/set lease-script="dhcpv4-server-lease" comment="hotspot-to-wpa=wpa" hotspot-to-wpa; You can specify the timeout after which a device is removed from leases and access-list. The default is four weeks. - /ip/dhcp-server/set lease-script=lease-script comment="hotspot-to-wpa=wpa, timeout=2w" hotspot-to-wpa; + /ip/dhcp-server/set lease-script="dhcpv4-server-lease" comment="hotspot-to-wpa=wpa, timeout=2w" hotspot-to-wpa; Configuration ------------- @@ -117,7 +117,7 @@ passphrase from hotspot credentials. See also -------- -* [Run other scripts on DHCP lease](lease-script.md) +* [Run other scripts on IPv4 DHCP server lease](dhcpv4-server-lease.md) --- [⬅️ Go back to main README](../README.md) diff --git a/doc/ipv6-update.md b/doc/ipv6-update.md index 792f97e9..fb42e931 100644 --- a/doc/ipv6-update.md +++ b/doc/ipv6-update.md @@ -55,7 +55,7 @@ create it with: /ipv6/firewall/address-list/add address=2003:cf:2f0f:de00::/56 comment=ipv6-pool-isp list=extern; If the dynamic entry exists already you need to remove it before creating -the static one.. +the static one. Address list entries for specific interfaces can be updated as well. The interface needs to get its address from pool `isp` and the address list entry @@ -77,6 +77,7 @@ start with "`ipv6-pool-`" and actual pool name, followed by a comma, See also -------- +* [Run other scripts on IPv6 DHCP client lease](dhcpv6-client-lease.md) * [Run scripts on ppp connection](ppp-on-up.md) --- diff --git a/doc/lease-script.md b/doc/lease-script.md index 6bcf7e13..3cba7045 100644 --- a/doc/lease-script.md +++ b/doc/lease-script.md @@ -1,54 +1 @@ -Run other scripts on DHCP lease -=============================== - -[![GitHub stars](https://img.shields.io/github/stars/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=red)](https://github.com/eworm-de/routeros-scripts/stargazers) -[![GitHub forks](https://img.shields.io/github/forks/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=green)](https://github.com/eworm-de/routeros-scripts/network) -[![GitHub watchers](https://img.shields.io/github/watchers/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=blue)](https://github.com/eworm-de/routeros-scripts/watchers) -[![required RouterOS version](https://img.shields.io/badge/RouterOS-7.19-yellow?style=flat)](https://mikrotik.com/download/changelogs/) -[![Telegram group @routeros_scripts](https://img.shields.io/badge/Telegram-%40routeros__scripts-%2326A5E4?logo=telegram&style=flat)](https://t.me/routeros_scripts) -[![donate with PayPal](https://img.shields.io/badge/Like_it%3F-Donate!-orange?logo=githubsponsors&logoColor=orange&style=flat)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J) - -[⬅️ Go back to main README](../README.md) - -> ℹ️ **Info**: This script can not be used on its own but requires the base -> installation. See [main README](../README.md) for details. - -Description ------------ - -This script is supposed to run from dhcp server as lease script. On a dhcp -lease it runs each script containing the following line, where `##` is a -decimal number for ordering: - - # provides: lease-script, order=## - -Currently it runs if available, in order: - -* [dhcp-to-dns](dhcp-to-dns.md) -* [collect-wireless-mac](collect-wireless-mac.md) -* [dhcp-lease-comment](dhcp-lease-comment.md) -* `hotspot-to-wpa-cleanup`, which is an optional cleanup script - of [hotspot-to-wpa](hotspot-to-wpa.md) - -Requirements and installation ------------------------------ - -Just install the script: - - $ScriptInstallUpdate lease-script; - -... and add it as `lease-script` to your dhcp server: - - /ip/dhcp-server/set lease-script=lease-script [ find ]; - -See also --------- - -* [Collect MAC addresses in wireless access list](collect-wireless-mac.md) -* [Comment DHCP leases with info from access list](dhcp-lease-comment.md) -* [Create DNS records for DHCP leases](dhcp-to-dns.md) -* [Use WPA network with hotspot credentials](hotspot-to-wpa.md) - ---- -[⬅️ Go back to main README](../README.md) -[⬆️ Go back to top](#top) +This script has been renamed. Please see [dhcpv4-server-lease](dhcpv4-server-lease.md). diff --git a/doc/mod/notification-email.md b/doc/mod/notification-email.md index c45e917c..cae7c94b 100644 --- a/doc/mod/notification-email.md +++ b/doc/mod/notification-email.md @@ -37,7 +37,9 @@ Also make sure the device has correct time configured, best is to set up the ntp client. Then edit `global-config-overlay`, add `EmailGeneralTo` with a valid -recipient address. Finally reload the configuration. +recipient address. Optionally add `EmailServerCertificate` and add the CA +certificate name if you have certificate verification enabled. Finally +reload the configuration. > ℹ️ **Info**: Copy relevant configuration from > [`global-config`](../../global-config.rsc) (the one without `-overlay`) to diff --git a/doc/mod/ssh-keys-import.md b/doc/mod/ssh-keys-import.md index c2d3c951..c6530ec2 100644 --- a/doc/mod/ssh-keys-import.md +++ b/doc/mod/ssh-keys-import.md @@ -4,7 +4,7 @@ Import ssh keys for public key authentication [![GitHub stars](https://img.shields.io/github/stars/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=red)](https://github.com/eworm-de/routeros-scripts/stargazers) [![GitHub forks](https://img.shields.io/github/forks/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=green)](https://github.com/eworm-de/routeros-scripts/network) [![GitHub watchers](https://img.shields.io/github/watchers/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=blue)](https://github.com/eworm-de/routeros-scripts/watchers) -[![required RouterOS version](https://img.shields.io/badge/RouterOS-7.19-yellow?style=flat)](https://mikrotik.com/download/changelogs/) +[![required RouterOS version](https://img.shields.io/badge/RouterOS-7.21-yellow?style=flat)](https://mikrotik.com/download/changelogs/) [![Telegram group @routeros_scripts](https://img.shields.io/badge/Telegram-%40routeros__scripts-%2326A5E4?logo=telegram&style=flat)](https://t.me/routeros_scripts) [![donate with PayPal](https://img.shields.io/badge/Like_it%3F-Donate!-orange?logo=githubsponsors&logoColor=orange&style=flat)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J) @@ -38,9 +38,8 @@ import that key: $SSHKeysImport "ssh-rsa AAAAB3Nza...QYZk8= user" admin; The third part of the key (`user` in this example) is inherited as -`info` in RouterOS (or `key-owner` with RouterOS 7.20.x and before). Also -the `MD5` fingerprint is recorded, this helps to audit and verify the -available keys. +`info` in RouterOS. Also the `MD5` fingerprint is recorded, this helps +to audit and verify the available keys. > ℹ️️ **Info**: Use `ssh-keygen` to show a fingerprint of an existing public > key file: `ssh-keygen -l -E md5 -f ~/.ssh/id_ed25519.pub` diff --git a/doc/ppp-on-up.md b/doc/ppp-on-up.md index e92601a2..add5308f 100644 --- a/doc/ppp-on-up.md +++ b/doc/ppp-on-up.md @@ -36,6 +36,7 @@ Just install the script: See also -------- +* [Run other scripts on IPv6 DHCP client lease](dhcpv6-client-lease.md) * [Update configuration on IPv6 prefix change](ipv6-update.md) * [Update tunnelbroker configuration](update-tunnelbroker.md) diff --git a/global-config.rsc b/global-config.rsc index e8a86aac..0bb572b5 100644 --- a/global-config.rsc +++ b/global-config.rsc @@ -31,6 +31,8 @@ :global EmailGeneralCc ""; #:global EmailGeneralTo "mail@example.com"; #:global EmailGeneralCc "another@example.com,third@example.com"; +# Add the CA certificate name here for verification. +:global EmailServerCertificate ""; # You can send Telegram notifications. Register a bot # and add the token and chat ids here, then install the module: @@ -88,7 +90,9 @@ # Toggle this to disable color output in terminal/cli. :global TerminalColorOutput true; -# This defines what backups to generate and what password to use. +# This defines whether to add date & time in filenames, what backups to generate, +# the password to use, and what random delay (between 0 and given seconds) to apply. +:global BackupFileNameDate false; :global BackupSendBinary false; :global BackupSendExport true; :global BackupSendGlobalConfig true; @@ -115,7 +119,7 @@ # cert="Root YE" }; { url="https://raw.githubusercontent.com/stamparm/ipsum/refs/heads/master/levels/4.txt"; # # higher level (decrease the numerical value) for more addresses, and vice versa - cert="USERTrust RSA Certification Authority" }; + cert="ISRG Root X1" }; { url="https://www.dshield.org/block.txt"; cidr="/24"; cert="GTS Root R4" }; { url="https://lists.blocklist.de/lists/strongips.txt"; diff --git a/global-functions.rsc b/global-functions.rsc index e0a73045..30b0ccbc 100644 --- a/global-functions.rsc +++ b/global-functions.rsc @@ -15,7 +15,7 @@ # Git commit id & info, expected configuration version :global CommitId "unknown"; :global CommitInfo "unknown"; -:global ExpectedConfigVersion 139; +:global ExpectedConfigVersion 143; # global variables not to be changed by user :global GlobalFunctionsReady false; @@ -111,11 +111,13 @@ :local UseFor [ :tostr $2 ]; :global CertificateDownload; - :global EitherOr; :global LogPrint; :global ParseKeyValueStore; - :set UseFor [ $EitherOr $UseFor "undefined" ]; + :if ([ :len $UseFor ] = 0) do={ + $LogPrint warning $0 ("The intended use is undefined!"); + :set UseFor "undefined"; + } :if ([ /system/resource/get free-hdd-space ] < 8388608 && \ [ /certificate/settings/get crl-download ] = true && \ @@ -189,7 +191,12 @@ $LogPrint warning $0 ("Failed downloading certificate with CommonName '" . $CommonName . \ "' from repository! Trying fallback to mkcert.org..."); :do { - :if ([ :len [ /certificate/find where common-name="ISRG Root X1" ] ] = 0) do={ + :local CertSettings [ /certificate/settings/get ]; + :if ([ :len [ /certificate/find where common-name="ISRG Root X1" ] ] = 0 && \ + !((($CertSettings->"builtin-trust-anchors") = "trusted" || \ + ($CertSettings->"builtin-trust-store") ~ "fetch" || \ + ($CertSettings->"builtin-trust-store") = "all") && \ + [ :len [ /certificate/builtin/find where common-name="ISRG Root X1" ] ] > 0)) do={ $LogPrint error $0 ("Required certificate is not available."); :return false; } @@ -303,7 +310,7 @@ :for I from=0 to=([ :len $Input ] - 1) do={ :local Char [ :pick $Input $I ]; - :if ([ :typeof [ find "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" $Char ] ] = "nil") do={ + :if ([ :typeof [ :find "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" $Char ] ] = "nil") do={ :do { :if ([ :len $Return ] = 0) do={ :error true; @@ -805,10 +812,15 @@ # check if DNS is resolving :set IsDNSResolving do={ :do { - :resolve "low-ttl.eworm.de"; + :local I 1; + :retry { + :set I ($I ^ 1); + :resolve ("low-ttl.eworm." . ({ "de"; "net" }->$I)); + } delay=50ms max=6; } on-error={ :return false; } + :return true; } @@ -1193,10 +1205,12 @@ } :onerror Err { - /file/remove $DirName; + /file/remove [ find where name=$DirName ]; } do={ - $LogPrint error $0 ("Removing directory '" . $DirName . "' failed: " . $Err); - :return false; + :if (!($Err ~ "no such item")) do={ + $LogPrint error $0 ("Removing directory '" . $DirName . "' failed: " . $Err); + :return false; + } } :return true; } @@ -1222,10 +1236,12 @@ } :onerror Err { - /file/remove $FileName; + /file/remove [ find where name=$FileName ]; } do={ - $LogPrint error $0 ("Removing file '" . $FileName . "' failed: " . $Err); - :return false; + :if (!($Err ~ "no such item")) do={ + $LogPrint error $0 ("Removing file '" . $FileName . "' failed: " . $Err); + :return false; + } } :return true; } @@ -1285,12 +1301,15 @@ :global SymbolForNotification; :global ValidateSyntax; - :if ([ $CertificateAvailable "Root YE" "fetch" ] = false) do={ + :if ([ $CertificateAvailable "ISRG Root X2" "fetch" ] = false || \ + [ $CertificateAvailable "Root YE" "fetch" ] = false) do={ $LogPrint warning $0 ("Downloading certificate failed, trying without."); } :foreach Script in=$Scripts do={ - :if ([ :len [ /system/script/find where name=$Script ] ] = 0) do={ + :if ([ :len [ /system/script/find where name=$Script ] ] > 0) do={ + $LogPrint warning $0 ("Requested to add script '" . $Script . "', but that exists already!"); + } else={ $LogPrint info $0 ("Adding new script: " . $Script); /system/script/add name=$Script owner=$Script source="#!rsc by RouterOS\n" comment=$NewComment; } diff --git a/hotspot-to-wpa-cleanup.capsman.rsc b/hotspot-to-wpa-cleanup.capsman.rsc index 35275313..6f1ed372 100644 --- a/hotspot-to-wpa-cleanup.capsman.rsc +++ b/hotspot-to-wpa-cleanup.capsman.rsc @@ -3,7 +3,7 @@ # Copyright (c) 2021-2026 Christian Hesse # https://rsc.eworm.de/COPYING.md # -# provides: lease-script, order=80 +# provides: dhcpv4-server-lease, order=80 # requires RouterOS, version=7.19 # requires device-mode, hotspot # diff --git a/hotspot-to-wpa-cleanup.template.rsc b/hotspot-to-wpa-cleanup.template.rsc index 398f194f..8986b42d 100644 --- a/hotspot-to-wpa-cleanup.template.rsc +++ b/hotspot-to-wpa-cleanup.template.rsc @@ -3,7 +3,7 @@ # Copyright (c) 2021-2026 Christian Hesse # https://rsc.eworm.de/COPYING.md # -# provides: lease-script, order=80 +# provides: dhcpv4-server-lease, order=80 # requires RouterOS, version=7.19 # requires device-mode, hotspot # diff --git a/hotspot-to-wpa-cleanup.wifi.rsc b/hotspot-to-wpa-cleanup.wifi.rsc index 17b9e545..8a5a095c 100644 --- a/hotspot-to-wpa-cleanup.wifi.rsc +++ b/hotspot-to-wpa-cleanup.wifi.rsc @@ -3,7 +3,7 @@ # Copyright (c) 2021-2026 Christian Hesse # https://rsc.eworm.de/COPYING.md # -# provides: lease-script, order=80 +# provides: dhcpv4-server-lease, order=80 # requires RouterOS, version=7.19 # requires device-mode, hotspot # diff --git a/ipv6-update.rsc b/ipv6-update.rsc index c87410cc..f93e2786 100644 --- a/ipv6-update.rsc +++ b/ipv6-update.rsc @@ -4,6 +4,7 @@ # https://rsc.eworm.de/COPYING.md # # requires RouterOS, version=7.19 +# provides: dhcpv6-client-lease, order=40 # # update firewall and dns settings on IPv6 prefix change # https://rsc.eworm.de/doc/ipv6-update.md @@ -15,16 +16,19 @@ do={ :error ("Global config and/or functions not ready."); }; } delay=500ms max=50; :local ScriptName [ :jobname ]; + :global EitherOr; :global LogPrint; :global ParseKeyValueStore; :global ScriptLock; - :local NaAddress $"na-address"; - :local NaValid $"na-valid"; - :local PdPrefix $"pd-prefix"; - :local PdValid $"pd-valid"; + :global DHCPv6ClientLeaseVars; - :if ([ $ScriptLock $ScriptName ] = false) do={ + :local NaAddress [ $EitherOr $"na-address" ($DHCPv6ClientLeaseVars->"na-address") ]; + :local NaValid [ $EitherOr $"na-valid" ($DHCPv6ClientLeaseVars->"na-valid") ]; + :local PdPrefix [ $EitherOr $"pd-prefix" ($DHCPv6ClientLeaseVars->"pd-prefix") ]; + :local PdValid [ $EitherOr $"pd-valid" ($DHCPv6ClientLeaseVars->"pd-valid") ]; + + :if ([ $ScriptLock $ScriptName 10 ] = false) do={ :set ExitOK true; :error false; } @@ -50,7 +54,7 @@ :local Pool [ /ipv6/pool/get [ find where prefix=$PdPrefix ] name ]; :if ([ :len [ /ipv6/firewall/address-list/find where comment=("ipv6-pool-" . $Pool) ] ] = 0) do={ /ipv6/firewall/address-list/add list=("ipv6-pool-" . $Pool) address=:: comment=("ipv6-pool-" . $Pool) dynamic=yes; - $LogPrint warning $ScriptName ("Added dynamic ipv6 address list entry for ipv6-pool-" . $Pool); + $LogPrint info $ScriptName ("Added dynamic ipv6 address list entry for ipv6-pool-" . $Pool); } :local AddrList [ /ipv6/firewall/address-list/find where comment=("ipv6-pool-" . $Pool) ]; :local OldPrefix [ /ipv6/firewall/address-list/get ($AddrList->0) address ]; diff --git a/lease-script.rsc b/lease-script.rsc index 991e6fac..2da00ca8 100644 --- a/lease-script.rsc +++ b/lease-script.rsc @@ -1,65 +1,3 @@ #!rsc by RouterOS -# RouterOS script: lease-script -# Copyright (c) 2013-2026 Christian Hesse -# https://rsc.eworm.de/COPYING.md # -# requires RouterOS, version=7.19 -# -# run scripts on DHCP lease -# https://rsc.eworm.de/doc/lease-script.md - -:local ExitOK false; -:onerror Err { - :global GlobalConfigReady; :global GlobalFunctionsReady; - :retry { :if ($GlobalConfigReady != true || $GlobalFunctionsReady != true) \ - do={ :error ("Global config and/or functions not ready."); }; } delay=500ms max=50; - :local ScriptName [ :jobname ]; - - :global Grep; - :global IfThenElse; - :global LogPrint; - :global ParseKeyValueStore; - :global ScriptLock; - - :if ([ :typeof $leaseActIP ] = "nothing" || \ - [ :typeof $leaseActMAC ] = "nothing" || \ - [ :typeof $leaseServerName ] = "nothing" || \ - [ :typeof $leaseBound ] = "nothing") do={ - $LogPrint error $ScriptName ("This script is supposed to run from ip dhcp-server."); - :set ExitOK true; - :error false; - } - - $LogPrint debug $ScriptName ("DHCP Server " . $leaseServerName . " " . [ $IfThenElse ($leaseBound = 0) \ - "de" "" ] . "assigned lease " . $leaseActIP . " to " . $leaseActMAC); - - :if ([ $ScriptLock $ScriptName 10 ] = false) do={ - :set ExitOK true; - :error false; - } - - :if ([ :len [ /system/script/job/find where script=$ScriptName ] ] > 1) do={ - $LogPrint debug $ScriptName ("More invocations are waiting, exiting early."); - :set ExitOK true; - :error true; - } - - :local RunOrder ({}); - :foreach Script in=[ /system/script/find where source~("\n# provides: lease-script\\b") ] do={ - :local ScriptVal [ /system/script/get $Script ]; - :local Store [ $ParseKeyValueStore [ $Grep ($ScriptVal->"source") ("\23 provides: lease-script, ") ] ]; - - :set ($RunOrder->($Store->"order" . "-" . $ScriptVal->"name")) ($ScriptVal->"name"); - } - - :foreach Order,Script in=$RunOrder do={ - :onerror Err { - $LogPrint debug $ScriptName ("Running script with order " . $Order . ": " . $Script); - /system/script/run $Script; - } do={ - $LogPrint warning $ScriptName ("Running script '" . $Script . "' failed: " . $Err); - } - } -} do={ - :global ExitError; $ExitError $ExitOK [ :jobname ] $Err; -} +# dummy for migration diff --git a/mod/notification-email.rsc b/mod/notification-email.rsc index b14e1868..b6288821 100644 --- a/mod/notification-email.rsc +++ b/mod/notification-email.rsc @@ -37,7 +37,9 @@ # flush e-mail queue :set FlushEmailQueue do={ :onerror Err { :global EmailQueue; + :global EmailServerCertificate; + :global CertificateAvailable; :global EitherOr; :global EMailGenerateFrom; :global FileExists; @@ -48,11 +50,10 @@ :local AllDone true; :local QueueLen [ :len $EmailQueue ]; - :local Scheduler [ /system/scheduler/find where name="_FlushEmailQueue" ]; - :if ([ :len $Scheduler ] > 0 && $QueueLen = 0) do={ + :if ([ :len [ /system/scheduler/find where name="_FlushEmailQueue" ] ] > 0 && $QueueLen = 0) do={ $LogPrint warning $0 ("Flushing E-Mail messages from scheduler, but queue is empty."); - /system/scheduler/remove $Scheduler; + /system/scheduler/remove [ find where name="_FlushEmailQueue" ]; :return false; } @@ -60,15 +61,19 @@ :return true; } - :if ([ :len $Scheduler ] < 0) do={ + :if ([ :len [ /system/scheduler/find where name="_FlushEmailQueue" ] ] < 0) do={ /system/scheduler/add name="_FlushEmailQueue" interval=1m start-time=startup \ comment="Doing initial checks..." on-event=(":global FlushEmailQueue; \$FlushEmailQueue;"); - :set Scheduler [ /system/scheduler/find where name="_FlushEmailQueue" ]; } - :local SchedVal [ /system/scheduler/get $Scheduler ]; - :if (($SchedVal->"interval") < 1m) do={ - /system/scheduler/set interval=1m comment="Doing initial checks..." $Scheduler; + :do { + :if (([ /system/scheduler/get [ find where name="_FlushEmailQueue" ] ]->"interval") < 1m) do={ + /system/scheduler/set interval=1m comment="Doing initial checks..." \ + [ find where name="_FlushEmailQueue" ]; + } + } on-error={ + $LogPrint debug $0 ("The scheduler is gone, aborting."); + :return false; } :if ([ /tool/e-mail/get last-status ] = "in-progress") do={ @@ -87,7 +92,16 @@ :return false; } - /system/scheduler/set interval=($QueueLen . "m") comment="Sending..." $Scheduler; + :if (([ /tool/e-mail/get ]->"certificate-verification") ~ "^yes" && \ + [ :len $EmailServerCertificate ] > 0) do={ + :if ([ $CertificateAvailable $EmailServerCertificate "email" ] = false) do={ + $LogPrint warning $0 ("Downloading required certificate failed."); + :return false; + } + } + + /system/scheduler/set interval=($QueueLen . "m") comment="Sending..." \ + [ find where name="_FlushEmailQueue" ]; :foreach Id,Message in=$EmailQueue do={ :if ([ :typeof $Message ] = "array" ) do={ @@ -143,8 +157,8 @@ :return false; } - /system/scheduler/set interval=(($SchedVal->"run-count") . "m") \ - comment="Waiting for retry..." $Scheduler; + /system/scheduler/set interval=(([ get [ find where name="_FlushEmailQueue" ] ]->"run-count") . "m") \ + comment="Waiting for retry..." [ find where name="_FlushEmailQueue" ]; } do={ :global ExitOnError; $ExitOnError $0 $Err; } } diff --git a/mod/ssh-keys-import.rsc b/mod/ssh-keys-import.rsc index d1cc4fa3..ea8bf130 100644 --- a/mod/ssh-keys-import.rsc +++ b/mod/ssh-keys-import.rsc @@ -3,7 +3,7 @@ # Copyright (c) 2020-2026 Christian Hesse # https://rsc.eworm.de/COPYING.md # -# requires RouterOS, version=7.19 +# requires RouterOS, version=7.21 # # import ssh keys for public key authentication # https://rsc.eworm.de/doc/mod/ssh-keys-import.md @@ -40,9 +40,8 @@ :local FingerPrintMD5 [ :convert from=base64 transform=md5 to=hex ($KeyVal->1) ]; - :local RegEx ("\\bmd5=" . $FingerPrintMD5 . "\\b"); :if ([ :len [ /user/ssh-keys/find where user=$User \ - (key-owner~$RegEx or info~$RegEx) ] ] > 0) do={ + info~("\\bmd5=" . $FingerPrintMD5 . "\\b") ] ] > 0) do={ $LogPrint warning $0 ("The ssh public key (MD5:" . $FingerPrintMD5 . \ ") is already available for user '" . $User . "'."); :return false; diff --git a/netwatch-dns.rsc b/netwatch-dns.rsc index 9531d4ad..7c6a7b5b 100644 --- a/netwatch-dns.rsc +++ b/netwatch-dns.rsc @@ -115,13 +115,15 @@ :local Data false; :onerror Err { + :local I 1; :retry { + :set I ($I ^ 1); :set Data ([ /tool/fetch check-certificate=yes-without-crl output=user \ http-header-field=({ "accept: application/dns-message" }) \ url=(($DohServer->"doh-url") . "?dns=" . [ :convert to=base64 ([ :rndstr length=2 ] . \ - "\01\00" . "\00\01" . "\00\00" . "\00\00" . "\00\00" . "\09doh-check\05eworm\02de\00" . \ - "\00\10" . "\00\01") ]) as-value ]->"data"); - } delay=1s max=3; + "\01\00" . "\00\01" . "\00\00" . "\00\00" . "\00\00" . "\09doh-check\05eworm" . \ + ({ "\02de"; "\03net" }->$I) . "\00" . "\00\10" . "\00\01") ]) as-value ]->"data"); + } delay=500ms max=6; } do={ $LogPrint warning $ScriptName ("Request to DoH server " . ($DohServer->"doh-url") . \ " failed: " . $Err); diff --git a/news-and-changes.rsc b/news-and-changes.rsc index 709e7744..ed60a6c2 100644 --- a/news-and-changes.rsc +++ b/news-and-changes.rsc @@ -64,14 +64,19 @@ 137="Added support to send notifications via Gotify (gotify.net)."; 138="RouterOS 7.19 is suffering an issue with certificate store. Fixing trust state for all certificates..."; 139="Certificate Authorities will reduce the leaf certificate validity times soon. Thus the defaults for renewal and warning in 'check-certificates' were decreased."; + 140="The scripts 'lease-script' was renamed to 'dhcpv4-server-lease', configuration was updated automatically."; + 141="Introduced script 'dhcpv6-client-lease' to run several scripts on IPv6 DHCP client lease."; + 142="Added a setting for 'mod/notification-email' to check availability of certificate chain."; + 143="Made backup scripts 'backup-email' and 'backup-upload' support date & time in filenames."; }; # Migration steps to be applied on script updates :global GlobalConfigMigration { - 97=":local Rec [ /ip/dns/static/find where comment~\"^managed by dhcp-to-dns for \" ]; :if ([ :len \$Rec ] > 0) do={ /ip/dns/static/remove \$Rec; /system/script/run dhcp-to-dns; }"; - 100=":global ScriptInstallUpdate; :if ([ :len [ /system/script/find where name=\"ssh-keys-import\" source~\"^#!rsc by RouterOS\\r?\\n\" ] ] > 0) do={ /system/script/set name=\"mod/ssh-keys-import\" ssh-keys-import; \$ScriptInstallUpdate; }"; + 97=":local Rec [ /ip/dns/static/find where comment~\"^managed by dhcp-to-dns for \" ]; :if ([ :len \$Rec ] > 0) do={ /ip/dns/static/remove \$Rec; /system/script/run dhcp-to-dns; };"; + 100=":global ScriptInstallUpdate; :if ([ :len [ /system/script/find where name=\"ssh-keys-import\" source~\"^#!rsc by RouterOS\\r?\\n\" ] ] > 0) do={ /system/script/set name=\"mod/ssh-keys-import\" ssh-keys-import; \$ScriptInstallUpdate; };"; 104=":global CharacterReplace; :global ScriptInstallUpdate; :foreach Script in={ \"capsman-download-packages\"; \"capsman-rolling-upgrade\"; \"hotspot-to-wpa\"; \"hotspot-to-wpa-cleanup\" } do={ /system/script/set name=(\$Script . \".capsman\") [ find where name=\$Script ]; :foreach Scheduler in=[ /system/scheduler/find where on-event~(\$Script . \"([^-.]|\\\$)\") ] do={ /system/scheduler/set \$Scheduler on-event=[ \$CharacterReplace [ get \$Scheduler on-event ] \$Script (\$Script . \".capsman\") ]; }; }; /ip/hotspot/user/profile/set on-login=\"hotspot-to-wpa.capsman\" [ find where on-login=\"hotspot-to-wpa\" ]; \$ScriptInstallUpdate;"; - 111=":local Rec [ /ip/dns/static/find where comment~\"^managed by dhcp-to-dns for \" ]; :if ([ :len \$Rec ] > 0) do={ /ip/dns/static/remove \$Rec; /system/script/run dhcp-to-dns; }"; - 132=":if ([ :len [ /system/script/find where name=\"check-health\" ] ] > 0) do={ :local Code \":local Install \\\"check-health\\\"; :if ([ :len [ /system/health/find where type=\\\"\\\" name~\\\"-state\\\\\\\$\\\" ] ] > 0) do={ :set Install (\\\$Install . \\\",check-health.d/state\\\"); }; :if ([ :len [ /system/health/find where type=\\\"C\\\" ] ] > 0) do={ :set Install (\\\$Install . \\\",check-health.d/temperature\\\"); }; :if ([ :len [ /system/health/find where type=\\\"V\\\" ] ] > 0) do={ :set Install (\\\$Install . \\\",check-health.d/voltage\\\"); }; :global ScriptInstallUpdate; \\\$ScriptInstallUpdate \\\$Install;\"; :global ValidateSyntax; :if ([ \$ValidateSyntax \$Code ] = true) do={ :do { [ :parse \$Code ]; } on-error={ }; }; }"; + 111=":local Rec [ /ip/dns/static/find where comment~\"^managed by dhcp-to-dns for \" ]; :if ([ :len \$Rec ] > 0) do={ /ip/dns/static/remove \$Rec; /system/script/run dhcp-to-dns; };"; + 132=":if ([ :len [ /system/script/find where name=\"check-health\" ] ] > 0) do={ :local Code \":local Install \\\"check-health\\\"; :if ([ :len [ /system/health/find where type=\\\"\\\" name~\\\"-state\\\\\\\$\\\" ] ] > 0) do={ :set Install (\\\$Install . \\\",check-health.d/state\\\"); }; :if ([ :len [ /system/health/find where type=\\\"C\\\" ] ] > 0) do={ :set Install (\\\$Install . \\\",check-health.d/temperature\\\"); }; :if ([ :len [ /system/health/find where type=\\\"V\\\" ] ] > 0) do={ :set Install (\\\$Install . \\\",check-health.d/voltage\\\"); }; :global ScriptInstallUpdate; \\\$ScriptInstallUpdate \\\$Install;\"; :global ValidateSyntax; :if ([ \$ValidateSyntax \$Code ] = true) do={ :do { [ :parse \$Code ]; } on-error={ }; }; };"; 138="/certificate/set trusted=yes [ find where trusted=yes ];"; + 140=":if ([ :len [ /system/script/find where name=\"lease-script\" ] ] > 0) do={ /system/script/set name=\"dhcpv4-server-lease\" \"lease-script\"; :global ScriptInstallUpdate; \$ScriptInstallUpdate; /ip/dhcp-server/set lease-script=\"dhcpv4-server-lease\" [ find where lease-script=\"lease-script\" ]; };"; };