diff --git a/BRANCHES.md b/BRANCHES.md new file mode 100644 index 0000000..8a0bdad --- /dev/null +++ b/BRANCHES.md @@ -0,0 +1,50 @@ +Installing from branches +======================== + +[![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.15-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) + +> ⚠️ **Warning**: Living on the edge? Great, read on! +> If not: Please use the `main` branch and leave this page! + +These scripts are developed in a [git](https://git-scm.com/) repository. +Development and experimental branches are used to provide early access +for specific changes. You can install scripts from these branches +for testing. + +## Install single script + +To install a single script from `next` branch: + + $ScriptInstallUpdate script-name "base-url=https://rsc.eworm.de/next/"; + +## Switch existing script + +Alternatively switch an existing script to update from `next` branch: + + /system/script/set comment="base-url=https://rsc.eworm.de/next/" script-name; + $ScriptInstallUpdate; + +## Switch installation + +Last but not least - to switch the complete installation to the `next` +branch edit `global-config-overlay` and add: + + :global ScriptUpdatesBaseUrl "https://rsc.eworm.de/next/"; + +... then reload the configuration and update: + + /system/script/run global-config; + $ScriptInstallUpdate; + +> ℹ️ **Info**: Replace `next` with *whatever* to use another specific branch. + +--- +[⬅️ Go back to main README](README.md) +[⬆️ Go back to top](#top) diff --git a/CERTIFICATES.d/01-dialog-A.avif b/CERTIFICATES.d/01-dialog-A.avif new file mode 100644 index 0000000..2fc3c9b Binary files /dev/null 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 new file mode 100644 index 0000000..5e408ab Binary files /dev/null and b/CERTIFICATES.d/02-dialog-B.avif differ diff --git a/CERTIFICATES.d/03-window.avif b/CERTIFICATES.d/03-window.avif new file mode 100644 index 0000000..96039a3 Binary files /dev/null and b/CERTIFICATES.d/03-window.avif differ diff --git a/CERTIFICATES.d/04-certificate.avif b/CERTIFICATES.d/04-certificate.avif new file mode 100644 index 0000000..e666314 Binary files /dev/null and b/CERTIFICATES.d/04-certificate.avif differ diff --git a/CERTIFICATES.md b/CERTIFICATES.md new file mode 100644 index 0000000..5432d78 --- /dev/null +++ b/CERTIFICATES.md @@ -0,0 +1,82 @@ +Certificate name from browser +============================= + +[![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.15-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) + +All well known desktop, mobile and server operating systems come with a +certificate store that is populated with a set of well known and trusted +certificates, acting as *trust anchors*. + +However RouterOS does not, still sometimes a specific certificate is +required to properly verify a chain of trust. One example is downloading +the scripts from this repository with `fetch` command, thus the very +first step of [installation](README.md#the-long-way-in-detail) is importing +the certificate. + +The scripts can install additional certificates when required. This happens +from this repository if available, or from [mkcert.org](https://mkcert.org) +as a fallback. + +Get the certificate's CommonName +-------------------------------- + +But how to determine what certificate may be required? Often easiest way +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*". + +![screenshot: dialog A](CERTIFICATES.d/01-dialog-A.avif) + +The dialog will change, click "*More information*". + +![screenshot: dialog B](CERTIFICATES.d/02-dialog-B.avif) + +A new window opens, click the button "*View Certificate*". (That window +can be closed now.) + +![screenshot: window](CERTIFICATES.d/03-window.avif) + +A new tab opens, showing information on the server certificate and its +chain of trust. The leftmost certificate is what we are interested in. + +![screenshot: certificate](CERTIFICATES.d/04-certificate.avif) + +Now we know that "`ISRG Root X2`" is required, some scripts need just +that information. + +Import a certificate by CommonName +---------------------------------- + +Running the function `$CertificateAvailable` with that name as parameter +makes sure the certificate is available in the device's store: + + $CertificateAvailable "ISRG Root X2"; + +If the certificate is actually available already nothing happens, and there +is no output. Otherwise the certificate is downloaded and imported. + +If importing a certificate with that exact name fails a warning is given +and nothing is actually imported. + +See also +-------- + +* [Download, import and update firewall address-lists](doc/fw-addr-lists.md) +* [Manage DNS and DoH servers from netwatch](doc/netwatch-dns.md) +* [Send notifications via Matrix](doc/mod/notification-matrix.md) +* [Send notifications via Ntfy](doc/mod/notification-ntfy.md) + +--- +[⬅️ Go back to main README](README.md) +[⬆️ Go back to top](#top) diff --git a/CONTRIBUTIONS.md b/CONTRIBUTIONS.md index 39fc8a9..0b35c40 100644 --- a/CONTRIBUTIONS.md +++ b/CONTRIBUTIONS.md @@ -1,6 +1,13 @@ Past Contributions ================== +[![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.15-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) Thanks a lot for your contributions! ❤️ @@ -13,7 +20,10 @@ for details! * [Anatoly Bubenkov](mailto:bubenkoff@gmail.com) (@bubenkoff) * [Ben Harris](mailto:mail@bharr.is) (@bharrisau) * [Daniel Ziegenberg](mailto:daniel@ziegenberg.at) (@ziegenberg) +* [Ignacio Serrano](mailto:ignic@ignic.com) (@ignic) * [Michael Gisbers](mailto:michael@gisbers.de) (@mgisbers) +* [Miquel Bonastre](mailto:mbonastre@yahoo.com) (@mbonastre) +* @netravnen * [netztrip](mailto:dave-tvg@netztrip.de) (@netztrip) * [Stefan Müller](mailto:stefan.mueller.83@gmail.com) (@PackElend) @@ -26,17 +36,21 @@ Add yourself to the list, * Andrea Ruffini Perico * Andrew Cox * Christoph Boss (@Kampfwurst) +* Daniel Ziegenberg (@ziegenberg) * Devin Dean (@dd2594gh) * Evaldo Gardenal +* Florian Estraviz * Giorgio Bikos * Harold Schoemaker * Hugo BV * Klaus Michael Rübsam +* Leonardo Valeri Manera * Linux-Schmie.de Michael Gisbers * Manuel Kuhn * Marek Čábák * Oleksandr Yukhymchuk * Peter Holtkamp +* Peter Ponzel * Reiner Vehrenkamp * Richard Österreicher * Simon Hitzemann diff --git a/DEBUG.md b/DEBUG.md new file mode 100644 index 0000000..d5e9beb --- /dev/null +++ b/DEBUG.md @@ -0,0 +1,63 @@ +Debug output and logs +===================== + +[![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.15-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) + +Sometimes scripts do not behave as expected. In these cases debug output +or logs can help. + +## Debug output + +Run this command in a terminal: + + :set PrintDebug true; + +You will then see debug output when running the script from terminal. + +To revert to default output run: + + :set PrintDebug false; + +### Debug output for specific script + +Even having debug output for a specific script or function only (or a +set of) is possible. To enable debug output for `telegram-chat` run: + + :set ($PrintDebugOverride->"telegram-chat") true; + +## Debug logs + +The debug info can go to system log. To make it show up in `memory` run: + + /system/logging/add topics=script,debug action=memory; + +Other actions (`disk`, `email`, `remote` or `support`) can be used as +well. I do not recommend using `echo` - use [debug output](#debug-output) +instead. + +Disable or remote that setting to restore regular logging. + +## Verbose output + +Specific scripts can generate huge amount of output. These do use a function +`$LogPrintVerbose`, which is declared, but has no code, intentionally. + +If you *really* want that output set the function to be the same as +`$LogPrint`: + + :set LogPrintVerbose $LogPrint; + +To revert that change just run: + + :set LogPrintVerbose; + +--- +[⬅️ Go back to main README](README.md) +[⬆️ Go back to top](#top) diff --git a/INITIAL-COMMANDS.md b/INITIAL-COMMANDS.md index da951aa..8b64d28 100644 --- a/INITIAL-COMMANDS.md +++ b/INITIAL-COMMANDS.md @@ -1,6 +1,13 @@ Initial commands ================ +[![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.15-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) > ⚠️ **Warning**: These command are inteneded for initial setup. If you are @@ -10,21 +17,22 @@ Initial commands Run the complete base installation: { - /tool/fetch "https://git.eworm.de/cgit/routeros-scripts/plain/certs/E1.pem" dst-path="letsencrypt-E1.pem" as-value; + /tool/fetch "https://git.eworm.de/cgit/routeros-scripts/plain/certs/ISRG-Root-X2.pem" dst-path="isrg-root-x2.pem" as-value; :delay 1s; - /certificate/import file-name=letsencrypt-E1.pem passphrase=""; - :if ([ :len [ /certificate/find where fingerprint="46494e30379059df18be52124305e606fc59070e5b21076ce113954b60517cda" or fingerprint="69729b8e15a86efc177a57afb7171dfc64add28c2fca8cf1507e34453ccb1470" ] ] != 2) do={ + /certificate/import file-name="isrg-root-x2.pem" passphrase=""; + :if ([ :len [ /certificate/find where fingerprint="69729b8e15a86efc177a57afb7171dfc64add28c2fca8cf1507e34453ccb1470" ] ] != 1) do={ :error "Something is wrong with your certificates!"; }; - /file/remove "letsencrypt-E1.pem"; :delay 1s; + /system/script/set name=("global-config-overlay-" . [ /system/clock/get date ] . "-" . [ /system/clock/get time ]) [ find where name="global-config-overlay" ]; :foreach Script in={ "global-config"; "global-config-overlay"; "global-functions" } do={ - /system/script/add name=$Script source=([ /tool/fetch check-certificate=yes-without-crl ("https://git.eworm.de/cgit/routeros-scripts/plain/" . $Script . ".rsc") output=user as-value]->"data"); + /system/script/remove [ find where name=$Script ]; + /system/script/add name=$Script owner=$Script source=([ /tool/fetch check-certificate=yes-without-crl ("https://git.eworm.de/cgit/routeros-scripts/plain/" . $Script . ".rsc") output=user as-value]->"data"); }; /system/script { run global-config; run global-functions; }; + /system/scheduler/remove [ find where name="global-scripts" ]; /system/scheduler/add name="global-scripts" start-time=startup on-event="/system/script { run global-config; run global-functions; }"; :global CertificateNameByCN; - $CertificateNameByCN "E1"; $CertificateNameByCN "ISRG Root X2"; }; @@ -32,6 +40,14 @@ Then continue setup with [scheduled automatic updates](README.md#scheduled-automatic-updates) or [editing configuration](README.md#editing-configuration). +## Fix existing installation + +The [initial commands](#initial-commands) above allow to fix an existing +installation in case it ever breaks. If `global-config-overlay` did exist +before it is renamed with a date and time suffix (like +`global-config-overlay-2024-01-25-09:33:12`). Make sure to restore the +configuration overlay if required. + --- [⬅️ Go back to main README](README.md) [⬆️ Go back to top](#top) diff --git a/Makefile b/Makefile index 100aa78..d21713c 100644 --- a/Makefile +++ b/Makefile @@ -4,31 +4,31 @@ CAPSMAN = $(wildcard *.capsman.rsc) LOCAL = $(wildcard *.local.rsc) -WIFIWAVE2 = $(wildcard *.wifiwave2.rsc) +WIFI = $(wildcard *.wifi.rsc) MARKDOWN = $(wildcard *.md doc/*.md doc/mod/*.md) HTML = $(MARKDOWN:.md=.html) -all: $(CAPSMAN) $(LOCAL) $(WIFIWAVE2) $(HTML) +all: $(CAPSMAN) $(LOCAL) $(WIFI) $(HTML) %.html: %.md Makefile markdown $< | sed 's/href="\([-_\./[:alnum:]]*\)\.md"/href="\1.html"/g' > $@ -%.local.rsc: %.template.rsc Makefile - sed -e '/\/caps-man/d' -e '/\/interface\/wifiwave2/d' -e 's|%TEMPL%|.local|' \ - -e '/^# NOT \/interface\/wireless #$$/,/^# NOT \/interface\/wireless #$$/d' \ - -e '/^# !!/,/^# !!/c # !! Do not edit this file, it is generated from template!' \ - < $< > $@ - %.capsman.rsc: %.template.rsc Makefile - sed -e '/\/interface\/wifiwave2/d' -e '/\/interface\/wireless/d' -e 's|%TEMPL%|.capsman|' \ - -e '/^# NOT \/caps-man #$$/,/^# NOT \/caps-man #$$/d' \ + sed -e '/\/interface\/wifi\//d' -e '/\/interface\/wireless\//d' -e 's|%TEMPL%|.capsman|' \ + -e '/^# NOT \/caps-man\/ #$$/,/^# NOT \/caps-man\/ #$$/d' \ -e '/^# !!/,/^# !!/c # !! Do not edit this file, it is generated from template!' \ < $< > $@ -%.wifiwave2.rsc: %.template.rsc Makefile - sed -e '/\/caps-man/d' -e '/\/interface\/wireless/d' -e 's|%TEMPL%|.wifiwave2|' \ - -e '/^# NOT \/interface\/wifiwave2 #$$/,/^# NOT \/interface\/wifiwave2 #$$/d' \ +%.local.rsc: %.template.rsc Makefile + sed -e '/\/caps-man\//d' -e '/\/interface\/wifi\//d' -e 's|%TEMPL%|.local|' \ + -e '/^# NOT \/interface\/wireless\/ #$$/,/^# NOT \/interface\/wireless\/ #$$/d' \ + -e '/^# !!/,/^# !!/c # !! Do not edit this file, it is generated from template!' \ + < $< > $@ + +%.wifi.rsc: %.template.rsc Makefile + sed -e '/\/caps-man\//d' -e '/\/interface\/wireless\//d' -e 's|%TEMPL%|.wifi|' \ + -e '/^# NOT \/interface\/wifi\/ #$$/,/^# NOT \/interface\/wifi\/ #$$/d' \ -e '/^# !!/,/^# !!/c # !! Do not edit this file, it is generated from template!' \ < $< > $@ diff --git a/README.d/01-download-certs.avif b/README.d/01-download-certs.avif index b27b23b..d41ca05 100644 Binary files a/README.d/01-download-certs.avif and b/README.d/01-download-certs.avif differ diff --git a/README.d/02-import-certs.avif b/README.d/02-import-certs.avif index d42994b..bf7d577 100644 Binary files a/README.d/02-import-certs.avif and b/README.d/02-import-certs.avif differ diff --git a/README.d/03-check-certs.avif b/README.d/03-check-certs.avif index 33bdc40..4717b3e 100644 Binary files a/README.d/03-check-certs.avif and b/README.d/03-check-certs.avif differ diff --git a/README.d/11-schedule-script.avif b/README.d/11-schedule-script.avif index 27541b7..d6eb0f8 100644 Binary files a/README.d/11-schedule-script.avif and b/README.d/11-schedule-script.avif differ diff --git a/README.d/12-setup-lease-script.avif b/README.d/12-setup-lease-script.avif index 365e0e8..fb4024e 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.d/upstream.png b/README.d/upstream.png new file mode 100644 index 0000000..fd5e877 Binary files /dev/null and b/README.d/upstream.png differ diff --git a/README.md b/README.md index 4ad3d2f..fae6986 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ RouterOS Scripts [![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.10-yellow?style=flat)](https://mikrotik.com/download/changelogs/) +[![required RouterOS version](https://img.shields.io/badge/RouterOS-7.15-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) @@ -21,6 +21,8 @@ to manage RouterOS devices or extend their functionality. Requirements ------------ +### Software (RouterOS) + Latest version of the scripts require recent RouterOS to function properly. Make sure to install latest updates before you begin. If new functionality or a breaking change in RouterOS `7.n` is used in my scripts I push my @@ -32,6 +34,20 @@ Specific scripts may require even newer RouterOS version. > ℹ️ **Info**: The `main` branch is now RouterOS v7 only. If you are still > running RouterOS v6 switch to `routeros-v6` branch! +Starting with RouterOS 7.17 the +[device-mode](https://help.mikrotik.com/docs/spaces/ROS/pages/93749258/Device-mode) +has been extended to give more fine-grained control over what features are +available. You need to enable `scheduler` and `fetch` at least, specific +scripts may require additional features. + +### Hardware + +RouterOS packages increase in size with each release. This becomes a +problem for devices with 16MB storage and below, those with an ARM CPU +are specifically affected. + +Huge configuration and lots of scripts give an extra risk. **Take care!** + Initial setup ------------- @@ -59,33 +75,31 @@ download the certificates. If you intend to download the scripts from a different location (for example from github.com) install the corresponding certificate chain. - /tool/fetch "https://git.eworm.de/cgit/routeros-scripts/plain/certs/E1.pem" dst-path="letsencrypt-E1.pem"; + /tool/fetch "https://git.eworm.de/cgit/routeros-scripts/plain/certs/ISRG-Root-X2.pem" dst-path="isrg-root-x2.pem"; ![screenshot: download certs](README.d/01-download-certs.avif) Note that the commands above do *not* verify server certificate, so if you want to be safe download with your workstations's browser and transfer the -files to your MikroTik device. +file to your MikroTik device. * [ISRG Root X2](https://letsencrypt.org/certs/isrg-root-x2.pem) -* Let's Encrypt [E1](https://letsencrypt.org/certs/lets-encrypt-e1.pem) -Then we import the certificates. +Then we import the certificate. - /certificate/import file-name=letsencrypt-E1.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. ![screenshot: import certs](README.d/02-import-certs.avif) -For basic verification we rename the certificates and print them by -fingerprint. Make sure exactly these two certificates ("*E1*" and -"*ISRG-Root-X2*") are shown. +For basic verification we rename the certificate and print it by +fingerprint. Make sure exactly this one certificate ("*ISRG-Root-X2*") +is shown. - /certificate/set name="E1" [ find where common-name="E1" ]; /certificate/set name="ISRG-Root-X2" [ find where common-name="ISRG Root X2" ]; - /certificate/print proplist=name where fingerprint="46494e30379059df18be52124305e606fc59070e5b21076ce113954b60517cda" or fingerprint="69729b8e15a86efc177a57afb7171dfc64add28c2fca8cf1507e34453ccb1470"; + /certificate/print proplist=name,fingerprint where fingerprint="69729b8e15a86efc177a57afb7171dfc64add28c2fca8cf1507e34453ccb1470"; ![screenshot: check certs](README.d/03-check-certs.avif) @@ -97,7 +111,7 @@ date and time is set correctly! Now let's download the main scripts and add them in configuration on the fly. - :foreach Script in={ "global-config"; "global-config-overlay"; "global-functions" } do={ /system/script/add name=$Script source=([ /tool/fetch check-certificate=yes-without-crl ("https://git.eworm.de/cgit/routeros-scripts/plain/" . $Script . ".rsc") output=user as-value]->"data"); }; + :foreach Script in={ "global-config"; "global-config-overlay"; "global-functions" } do={ /system/script/add name=$Script owner=$Script source=([ /tool/fetch check-certificate=yes-without-crl ("https://git.eworm.de/cgit/routeros-scripts/plain/" . $Script . ".rsc") output=user as-value]->"data"); }; ![screenshot: import scripts](README.d/04-import-scripts.avif) @@ -129,6 +143,11 @@ Save changes and exit with `Ctrl-o`. ![screenshot: edit global-config-overlay](README.d/07-edit-global-config-overlay.avif) +Additionally creating configuration snippets is supported. The script name +of these snippets has to start with `global-config-overlay.d/` to make them +being loaded automatically. This allows to split off parts of the +configuration. + To apply your changes run `global-config`, which will automatically load the overlay as well: @@ -141,7 +160,7 @@ This last step is required when ever you make changes to your configuration. > ℹ️ **Info**: It is recommended to edit the configuration using the command > line interface. If using Winbox on Windows OS, the line endings may be > missing. To fix this run: -> `/system/script/set source=[ $Unix2Dos [ get global-config-overlay source ] ] global-config-overlay;` +> `/system/script/set source=[ :tocrlf [ get global-config-overlay source ] ] global-config-overlay;` Updating scripts ---------------- @@ -173,10 +192,10 @@ Scheduler and events Most scripts are designed to run regularly from [scheduler](https://wiki.mikrotik.com/wiki/Manual:System/Scheduler). We just -added `check-routeros-update`, so let's run it every hour to make sure not to +added `check-routeros-update`, so let's run it daily to make sure not to miss an update. - /system/scheduler/add name="check-routeros-update" interval=1h on-event="/system/script/run check-routeros-update;"; + /system/scheduler/add name="check-routeros-update" interval=1d start-time=startup on-event="/system/script/run check-routeros-update;"; ![screenshot: schedule script](README.d/11-schedule-script.avif) @@ -215,7 +234,7 @@ Available scripts * [Download, import and update firewall address-lists](doc/fw-addr-lists.md) * [Wait for global functions und modules](doc/global-wait.md) * [Send GPS position to server](doc/gps-track.md) -* [Use WPA2 network with hotspot credentials](doc/hotspot-to-wpa.md) +* [Use WPA network with hotspot credentials](doc/hotspot-to-wpa.md) * [Create DNS records for IPSec peers](doc/ipsec-to-dns.md) * [Update configuration on IPv6 prefix change](doc/ipv6-update.md) * [Manage IP addresses with bridge status](doc/ip-addr-bridge.md) @@ -351,6 +370,8 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the Upstream -------- +[![upstream](README.d/upstream.png)](https://rsc.eworm.de/) + URL: [GitHub.com](https://github.com/eworm-de/routeros-scripts#routeros-scripts) diff --git a/accesslist-duplicates.capsman.rsc b/accesslist-duplicates.capsman.rsc index b986862..27546c8 100644 --- a/accesslist-duplicates.capsman.rsc +++ b/accesslist-duplicates.capsman.rsc @@ -1,31 +1,37 @@ #!rsc by RouterOS # RouterOS script: accesslist-duplicates.capsman -# Copyright (c) 2018-2023 Christian Hesse -# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md +# Copyright (c) 2018-2025 Christian Hesse +# https://rsc.eworm.de/COPYING.md +# +# requires RouterOS, version=7.15 # # print duplicate antries in wireless access list -# https://git.eworm.de/cgit/routeros-scripts/about/doc/accesslist-duplicates.md +# https://rsc.eworm.de/doc/accesslist-duplicates.md # # !! Do not edit this file, it is generated from template! -:local 0 "accesslist-duplicates.capsman"; :global GlobalFunctionsReady; :while ($GlobalFunctionsReady != true) do={ :delay 500ms; } -:global Read; +:local ExitOK false; +:do { + :local ScriptName [ :jobname ]; -:local Seen ({}); + :local Seen ({}); -:foreach AccList in=[ /caps-man/access-list/find where mac-address!="00:00:00:00:00:00" ] do={ - :local Mac [ /caps-man/access-list/get $AccList mac-address ]; - :if ($Seen->$Mac = 1) do={ - /caps-man/access-list/print where mac-address=$Mac; - :put "\nNumeric id to remove, any key to skip!"; - :local Remove [ :tonum [ $Read ] ]; - :if ([ :typeof $Remove ] = "num") do={ - :put ("Removing numeric id " . $Remove . "...\n"); - /caps-man/access-list/remove $Remove; + :foreach AccList in=[ /caps-man/access-list/find where mac-address!="00:00:00:00:00:00" ] do={ + :local Mac [ /caps-man/access-list/get $AccList mac-address ]; + :if ($Seen->$Mac = 1) do={ + /caps-man/access-list/print where mac-address=$Mac; + :local Remove [ :tonum [ /terminal/ask prompt="\nNumeric id to remove, any key to skip!" ] ]; + + :if ([ :typeof $Remove ] = "num") do={ + :put ("Removing numeric id " . $Remove . "...\n"); + /caps-man/access-list/remove $Remove; + } } + :set ($Seen->$Mac) 1; } - :set ($Seen->$Mac) 1; +} on-error={ + :global ExitError; $ExitError $ExitOK [ :jobname ]; } diff --git a/accesslist-duplicates.local.rsc b/accesslist-duplicates.local.rsc index b136de3..589815d 100644 --- a/accesslist-duplicates.local.rsc +++ b/accesslist-duplicates.local.rsc @@ -1,31 +1,37 @@ #!rsc by RouterOS # RouterOS script: accesslist-duplicates.local -# Copyright (c) 2018-2023 Christian Hesse -# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md +# Copyright (c) 2018-2025 Christian Hesse +# https://rsc.eworm.de/COPYING.md +# +# requires RouterOS, version=7.15 # # print duplicate antries in wireless access list -# https://git.eworm.de/cgit/routeros-scripts/about/doc/accesslist-duplicates.md +# https://rsc.eworm.de/doc/accesslist-duplicates.md # # !! Do not edit this file, it is generated from template! -:local 0 "accesslist-duplicates.local"; :global GlobalFunctionsReady; :while ($GlobalFunctionsReady != true) do={ :delay 500ms; } -:global Read; +:local ExitOK false; +:do { + :local ScriptName [ :jobname ]; -:local Seen ({}); + :local Seen ({}); -:foreach AccList in=[ /interface/wireless/access-list/find where mac-address!="00:00:00:00:00:00" ] do={ - :local Mac [ /interface/wireless/access-list/get $AccList mac-address ]; - :if ($Seen->$Mac = 1) do={ - /interface/wireless/access-list/print where mac-address=$Mac; - :put "\nNumeric id to remove, any key to skip!"; - :local Remove [ :tonum [ $Read ] ]; - :if ([ :typeof $Remove ] = "num") do={ - :put ("Removing numeric id " . $Remove . "...\n"); - /interface/wireless/access-list/remove $Remove; + :foreach AccList in=[ /interface/wireless/access-list/find where mac-address!="00:00:00:00:00:00" ] do={ + :local Mac [ /interface/wireless/access-list/get $AccList mac-address ]; + :if ($Seen->$Mac = 1) do={ + /interface/wireless/access-list/print where mac-address=$Mac; + :local Remove [ :tonum [ /terminal/ask prompt="\nNumeric id to remove, any key to skip!" ] ]; + + :if ([ :typeof $Remove ] = "num") do={ + :put ("Removing numeric id " . $Remove . "...\n"); + /interface/wireless/access-list/remove $Remove; + } } + :set ($Seen->$Mac) 1; } - :set ($Seen->$Mac) 1; +} on-error={ + :global ExitError; $ExitError $ExitOK [ :jobname ]; } diff --git a/accesslist-duplicates.template.rsc b/accesslist-duplicates.template.rsc index 1e5fc80..ccbca3d 100644 --- a/accesslist-duplicates.template.rsc +++ b/accesslist-duplicates.template.rsc @@ -1,40 +1,46 @@ #!rsc by RouterOS # RouterOS script: accesslist-duplicates%TEMPL% -# Copyright (c) 2018-2023 Christian Hesse -# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md +# Copyright (c) 2018-2025 Christian Hesse +# https://rsc.eworm.de/COPYING.md +# +# requires RouterOS, version=7.15 # # print duplicate antries in wireless access list -# https://git.eworm.de/cgit/routeros-scripts/about/doc/accesslist-duplicates.md +# https://rsc.eworm.de/doc/accesslist-duplicates.md # # !! This is just a template to generate the real script! # !! Pattern '%TEMPL%' is replaced, paths are filtered. -:local 0 "accesslist-duplicates%TEMPL%"; :global GlobalFunctionsReady; :while ($GlobalFunctionsReady != true) do={ :delay 500ms; } -:global Read; +:local ExitOK false; +:do { + :local ScriptName [ :jobname ]; -:local Seen ({}); + :local Seen ({}); -:foreach AccList in=[ /caps-man/access-list/find where mac-address!="00:00:00:00:00:00" ] do={ -:foreach AccList in=[ /interface/wifiwave2/access-list/find where mac-address!="00:00:00:00:00:00" ] do={ -:foreach AccList in=[ /interface/wireless/access-list/find where mac-address!="00:00:00:00:00:00" ] do={ - :local Mac [ /caps-man/access-list/get $AccList mac-address ]; - :local Mac [ /interface/wifiwave2/access-list/get $AccList mac-address ]; - :local Mac [ /interface/wireless/access-list/get $AccList mac-address ]; - :if ($Seen->$Mac = 1) do={ - /caps-man/access-list/print where mac-address=$Mac; - /interface/wifiwave2/access-list/print where mac-address=$Mac; - /interface/wireless/access-list/print where mac-address=$Mac; - :put "\nNumeric id to remove, any key to skip!"; - :local Remove [ :tonum [ $Read ] ]; - :if ([ :typeof $Remove ] = "num") do={ - :put ("Removing numeric id " . $Remove . "...\n"); - /caps-man/access-list/remove $Remove; - /interface/wifiwave2/access-list/remove $Remove; - /interface/wireless/access-list/remove $Remove; + :foreach AccList in=[ /caps-man/access-list/find where mac-address!="00:00:00:00:00:00" ] do={ + :foreach AccList in=[ /interface/wifi/access-list/find where mac-address!="00:00:00:00:00:00" ] do={ + :foreach AccList in=[ /interface/wireless/access-list/find where mac-address!="00:00:00:00:00:00" ] do={ + :local Mac [ /caps-man/access-list/get $AccList mac-address ]; + :local Mac [ /interface/wifi/access-list/get $AccList mac-address ]; + :local Mac [ /interface/wireless/access-list/get $AccList mac-address ]; + :if ($Seen->$Mac = 1) do={ + /caps-man/access-list/print where mac-address=$Mac; + /interface/wifi/access-list/print where mac-address=$Mac; + /interface/wireless/access-list/print where mac-address=$Mac; + :local Remove [ :tonum [ /terminal/ask prompt="\nNumeric id to remove, any key to skip!" ] ]; + + :if ([ :typeof $Remove ] = "num") do={ + :put ("Removing numeric id " . $Remove . "...\n"); + /caps-man/access-list/remove $Remove; + /interface/wifi/access-list/remove $Remove; + /interface/wireless/access-list/remove $Remove; + } } + :set ($Seen->$Mac) 1; } - :set ($Seen->$Mac) 1; +} on-error={ + :global ExitError; $ExitError $ExitOK [ :jobname ]; } diff --git a/accesslist-duplicates.wifi.rsc b/accesslist-duplicates.wifi.rsc new file mode 100644 index 0000000..527ebb4 --- /dev/null +++ b/accesslist-duplicates.wifi.rsc @@ -0,0 +1,37 @@ +#!rsc by RouterOS +# RouterOS script: accesslist-duplicates.wifi +# Copyright (c) 2018-2025 Christian Hesse +# https://rsc.eworm.de/COPYING.md +# +# requires RouterOS, version=7.15 +# +# print duplicate antries in wireless access list +# https://rsc.eworm.de/doc/accesslist-duplicates.md +# +# !! Do not edit this file, it is generated from template! + +:global GlobalFunctionsReady; +:while ($GlobalFunctionsReady != true) do={ :delay 500ms; } + +:local ExitOK false; +:do { + :local ScriptName [ :jobname ]; + + :local Seen ({}); + + :foreach AccList in=[ /interface/wifi/access-list/find where mac-address!="00:00:00:00:00:00" ] do={ + :local Mac [ /interface/wifi/access-list/get $AccList mac-address ]; + :if ($Seen->$Mac = 1) do={ + /interface/wifi/access-list/print where mac-address=$Mac; + :local Remove [ :tonum [ /terminal/ask prompt="\nNumeric id to remove, any key to skip!" ] ]; + + :if ([ :typeof $Remove ] = "num") do={ + :put ("Removing numeric id " . $Remove . "...\n"); + /interface/wifi/access-list/remove $Remove; + } + } + :set ($Seen->$Mac) 1; + } +} on-error={ + :global ExitError; $ExitError $ExitOK [ :jobname ]; +} diff --git a/accesslist-duplicates.wifiwave2.rsc b/accesslist-duplicates.wifiwave2.rsc deleted file mode 100644 index 4297898..0000000 --- a/accesslist-duplicates.wifiwave2.rsc +++ /dev/null @@ -1,31 +0,0 @@ -#!rsc by RouterOS -# RouterOS script: accesslist-duplicates.wifiwave2 -# Copyright (c) 2018-2023 Christian Hesse -# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md -# -# print duplicate antries in wireless access list -# https://git.eworm.de/cgit/routeros-scripts/about/doc/accesslist-duplicates.md -# -# !! Do not edit this file, it is generated from template! - -:local 0 "accesslist-duplicates.wifiwave2"; -:global GlobalFunctionsReady; -:while ($GlobalFunctionsReady != true) do={ :delay 500ms; } - -:global Read; - -:local Seen ({}); - -:foreach AccList in=[ /interface/wifiwave2/access-list/find where mac-address!="00:00:00:00:00:00" ] do={ - :local Mac [ /interface/wifiwave2/access-list/get $AccList mac-address ]; - :if ($Seen->$Mac = 1) do={ - /interface/wifiwave2/access-list/print where mac-address=$Mac; - :put "\nNumeric id to remove, any key to skip!"; - :local Remove [ :tonum [ $Read ] ]; - :if ([ :typeof $Remove ] = "num") do={ - :put ("Removing numeric id " . $Remove . "...\n"); - /interface/wifiwave2/access-list/remove $Remove; - } - } - :set ($Seen->$Mac) 1; -} diff --git a/backup-cloud.rsc b/backup-cloud.rsc index 934edb9..c4e23b2 100644 --- a/backup-cloud.rsc +++ b/backup-cloud.rsc @@ -1,61 +1,104 @@ #!rsc by RouterOS # RouterOS script: backup-cloud -# Copyright (c) 2013-2023 Christian Hesse -# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md +# Copyright (c) 2013-2025 Christian Hesse +# https://rsc.eworm.de/COPYING.md # # provides: backup-script, order=40 +# requires RouterOS, version=7.15 # # upload backup to MikroTik cloud -# https://git.eworm.de/cgit/routeros-scripts/about/doc/backup-cloud.md +# https://rsc.eworm.de/doc/backup-cloud.md -:local 0 "backup-cloud"; :global GlobalFunctionsReady; :while ($GlobalFunctionsReady != true) do={ :delay 500ms; } -:global BackupPassword; -:global BackupRandomDelay; -:global Identity; - -:global DeviceInfo; -:global FormatLine; -:global LogPrintExit2; -:global RandomDelay; -:global ScriptFromTerminal; -:global ScriptLock; -:global SendNotification2; -:global SymbolForNotification; -:global WaitFullyConnected; - -$ScriptLock $0; -$WaitFullyConnected; - -:if ([ $ScriptFromTerminal $0 ] = false && $BackupRandomDelay > 0) do={ - $RandomDelay $BackupRandomDelay; -} - +:local ExitOK false; :do { - # we are not interested in output, but print is - # required to fetch information from cloud - /system/backup/cloud/print as-value; - :if ([ :len [ /system/backup/cloud/find ] ] > 0) do={ - /system/backup/cloud/upload-file action=create-and-upload \ - password=$BackupPassword replace=[ get ([ find ]->0) name ]; - } else={ - /system/backup/cloud/upload-file action=create-and-upload \ - password=$BackupPassword; - } - :local Cloud [ /system/backup/cloud/get ([ find ]->0) ]; + :local ScriptName [ :jobname ]; - $SendNotification2 ({ origin=$0; \ - subject=([ $SymbolForNotification "floppy-disk,cloud" ] . "Cloud backup"); \ - message=("Uploaded backup for " . $Identity . " to cloud.\n\n" . \ - [ $DeviceInfo ] . "\n\n" . \ - [ $FormatLine "Name" ($Cloud->"name") ] . "\n" . \ - [ $FormatLine "Size" ($Cloud->"size" . " B (" . ($Cloud->"size" / 1024) . " KiB)") ] . "\n" . \ - [ $FormatLine "Download key" ($Cloud->"secret-download-key") ]); silent=true }); + :global BackupRandomDelay; + :global Identity; + :global PackagesUpdateBackupFailure; + + :global DeviceInfo; + :global FormatLine; + :global HumanReadableNum; + :global LogPrint; + :global MkDir; + :global RandomDelay; + :global RmDir; + :global ScriptFromTerminal; + :global ScriptLock; + :global SendNotification2; + :global SymbolForNotification; + :global WaitForFile; + :global WaitFullyConnected; + + :if ([ $ScriptLock $ScriptName ] = false) do={ + :set PackagesUpdateBackupFailure true; + :set ExitOK true; + :error false; + } + + :if ([ :len [ /system/scheduler/find where name="running-from-backup-partition" ] ] > 0) do={ + $LogPrint warning $ScriptName ("Running from backup partition, refusing to act."); + :set PackagesUpdateBackupFailure true; + :set ExitOK true; + :error false; + } + + $WaitFullyConnected; + + :if ([ $ScriptFromTerminal $ScriptName ] = false && $BackupRandomDelay > 0) do={ + $RandomDelay $BackupRandomDelay; + } + + :if ([ $MkDir ("tmpfs/backup-cloud") ] = false) do={ + $LogPrint error $ScriptName ("Failed creating directory!"); + :set ExitOK true; + :error false; + } + + :local I 5; + :do { + :execute { + :global BackupPassword; + + :local Backup ([ /system/backup/cloud/find ]->0); + :if ([ :typeof $Backup ] = "id") do={ + /system/backup/cloud/upload-file action=create-and-upload \ + password=$BackupPassword replace=$Backup; + } else={ + /system/backup/cloud/upload-file action=create-and-upload \ + password=$BackupPassword; + } + /file/add name="tmpfs/backup-cloud/done"; + } as-string; + :set I ($I - 1); + } while=([ $WaitForFile "tmpfs/backup-cloud/done" 200ms ] = false && $I > 0); + + :if ([ $WaitForFile "tmpfs/backup-cloud/done" ] = true) do={ + :if ($I < 4) do={ + :log warning ($ScriptName . ": Retry successful, please discard previous connection errors."); + } + + :local Cloud [ /system/backup/cloud/get ([ find ]->0) ]; + + $SendNotification2 ({ origin=$ScriptName; \ + subject=([ $SymbolForNotification "floppy-disk,cloud" ] . "Cloud backup"); \ + message=("Uploaded backup for " . $Identity . " to cloud.\n\n" . \ + [ $DeviceInfo ] . "\n\n" . \ + [ $FormatLine "Name" ($Cloud->"name") ] . "\n" . \ + [ $FormatLine "Size" ([ $HumanReadableNum ($Cloud->"size") 1024 ] . "B") ] . "\n" . \ + [ $FormatLine "Download key" ($Cloud->"secret-download-key") ]); silent=true }); + } else={ + $SendNotification2 ({ origin=$ScriptName; \ + subject=([ $SymbolForNotification "floppy-disk,warning-sign" ] . "Cloud backup failed"); \ + message=("Failed uploading backup for " . $Identity . " to cloud!\n\n" . [ $DeviceInfo ]) }); + $LogPrint error $ScriptName ("Failed uploading backup for " . $Identity . " to cloud!"); + :set PackagesUpdateBackupFailure true; + } + $RmDir "tmpfs/backup-cloud"; } on-error={ - $SendNotification2 ({ origin=$0; \ - subject=([ $SymbolForNotification "floppy-disk,warning-sign" ] . "Cloud backup failed"); \ - message=("Failed uploading backup for " . $Identity . " to cloud!\n\n" . [ $DeviceInfo ]) }); - $LogPrintExit2 error $0 ("Failed uploading backup for " . $Identity . " to cloud!") true; + :global ExitError; $ExitError $ExitOK [ :jobname ]; } diff --git a/backup-email.rsc b/backup-email.rsc index 95e9015..d097301 100644 --- a/backup-email.rsc +++ b/backup-email.rsc @@ -1,111 +1,140 @@ #!rsc by RouterOS # RouterOS script: backup-email -# Copyright (c) 2013-2023 Christian Hesse -# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md +# Copyright (c) 2013-2025 Christian Hesse +# https://rsc.eworm.de/COPYING.md # # provides: backup-script, order=20 +# requires RouterOS, version=7.15 # # create and email backup and config file -# https://git.eworm.de/cgit/routeros-scripts/about/doc/backup-email.md +# https://rsc.eworm.de/doc/backup-email.md -:local 0 "backup-email"; :global GlobalFunctionsReady; :while ($GlobalFunctionsReady != true) do={ :delay 500ms; } -:global BackupPassword; -:global BackupRandomDelay; -:global BackupSendBinary; -:global BackupSendExport; -:global BackupSendGlobalConfig; -:global Domain; -:global Identity; +:local ExitOK false; +:do { + :local ScriptName [ :jobname ]; -:global CharacterReplace; -:global DeviceInfo; -:global FormatLine; -:global LogPrintExit2; -:global MkDir; -:global RandomDelay; -:global ScriptFromTerminal; -:global ScriptLock; -:global SendEMail2; -:global SymbolForNotification; -:global WaitForFile; -:global WaitFullyConnected; + :global BackupPassword; + :global BackupRandomDelay; + :global BackupSendBinary; + :global BackupSendExport; + :global BackupSendGlobalConfig; + :global Domain; + :global Identity; + :global PackagesUpdateBackupFailure; -:if ([ :typeof $SendEMail2 ] = "nothing") do={ - $LogPrintExit2 error $0 ("The module for sending notifications via e-mail is not installed.") true; -} + :global CleanName; + :global DeviceInfo; + :global FormatLine; + :global LogPrint; + :global MkDir; + :global RandomDelay; + :global ScriptFromTerminal; + :global ScriptLock; + :global SendEMail2; + :global SymbolForNotification; + :global WaitForFile; + :global WaitFullyConnected; -:if ($BackupSendBinary != true && \ - $BackupSendExport != true) do={ - $LogPrintExit2 error $0 ("Configured to send neither backup nor config export.") true; -} - -$ScriptLock $0; -$WaitFullyConnected; - -:if ([ $ScriptFromTerminal $0 ] = false && $BackupRandomDelay > 0) do={ - $RandomDelay $BackupRandomDelay; -} - -# filename based on identity -:local DirName ("tmpfs/" . $0); -:local FileName [ $CharacterReplace ($Identity . "." . $Domain) "." "_" ]; -:local FilePath ($DirName . "/" . $FileName); -:local BackupFile "none"; -:local ExportFile "none"; -:local ConfigFile "none"; -:local Attach ({}); - -:if ([ $MkDir $DirName ] = false) do={ - $LogPrintExit2 error $0 ("Failed creating directory!") true; -} - -# binary backup -:if ($BackupSendBinary = true) do={ - /system/backup/save encryption=aes-sha256 name=$FilePath password=$BackupPassword; - $WaitForFile ($FilePath . ".backup"); - :set BackupFile ($FileName . ".backup"); - :set Attach ($Attach, ($FilePath . ".backup")); -} - -# create configuration export -:if ($BackupSendExport = true) do={ - /export terse show-sensitive file=$FilePath; - $WaitForFile ($FilePath . ".rsc"); - :set ExportFile ($FileName . ".rsc"); - :set Attach ($Attach, ($FilePath . ".rsc")); -} - -# global-config-overlay -:if ($BackupSendGlobalConfig = true) do={ - # Do *NOT* use '/file/add ...' here, as it is limited to 4095 bytes! - :execute script={ :put [ /system/script/get global-config-overlay source ]; } \ - file=($FilePath . ".conf"); - $WaitForFile ($FilePath . ".conf.txt"); - :set ConfigFile ($FileName . ".conf.txt"); - :set Attach ($Attach, ($FilePath . ".conf.txt")); -} - -# send email with status and files -$SendEMail2 ({ origin=$0; \ - subject=([ $SymbolForNotification "floppy-disk,incoming-envelope" ] . \ - "Backup & Config"); \ - message=("See attached files for backup and config export for " . \ - $Identity . ".\n\n" . \ - [ $DeviceInfo ] . "\n\n" . \ - [ $FormatLine "Backup file" $BackupFile ] . "\n" . \ - [ $FormatLine "Export file" $ExportFile ] . "\n" . \ - [ $FormatLine "Config file" $ConfigFile ]); \ - attach=$Attach; remove-attach=true }); - -# wait for the mail to be sent -:local I 0; -:while ([ :len [ /file/find where name ~ ($FilePath . "\\.(backup|rsc)\$") ] ] > 0) do={ - :if ($I >= 120) do={ - $LogPrintExit2 warning $0 ("Files are still available, sending e-mail failed.") true; + :if ([ :typeof $SendEMail2 ] = "nothing") do={ + $LogPrint error $ScriptName ("The module for sending notifications via e-mail is not installed."); + :set ExitOK true; + :error false; } - :delay 1s; - :set I ($I + 1); + + :if ($BackupSendBinary != true && \ + $BackupSendExport != true) do={ + $LogPrint error $ScriptName ("Configured to send neither backup nor config export."); + :set ExitOK true; + :error false; + } + + :if ([ $ScriptLock $ScriptName ] = false) do={ + :set PackagesUpdateBackupFailure true; + :set ExitOK true; + :error false; + } + + :if ([ :len [ /system/scheduler/find where name="running-from-backup-partition" ] ] > 0) do={ + $LogPrint warning $ScriptName ("Running from backup partition, refusing to act."); + :set PackagesUpdateBackupFailure true; + :set ExitOK true; + :error false; + } + + $WaitFullyConnected; + + :if ([ $ScriptFromTerminal $ScriptName ] = false && $BackupRandomDelay > 0) do={ + $RandomDelay $BackupRandomDelay; + } + + # filename based on identity + :local DirName ("tmpfs/" . $ScriptName); + :local FileName [ $CleanName ($Identity . "." . $Domain) ]; + :local FilePath ($DirName . "/" . $FileName); + :local BackupFile "none"; + :local ExportFile "none"; + :local ConfigFile "none"; + :local Attach ({}); + + :if ([ $MkDir $DirName ] = false) do={ + $LogPrint error $ScriptName ("Failed creating directory!"); + :set ExitOK true; + :error false; + } + + # binary backup + :if ($BackupSendBinary = true) do={ + /system/backup/save encryption=aes-sha256 name=$FilePath password=$BackupPassword; + $WaitForFile ($FilePath . ".backup"); + :set BackupFile ($FileName . ".backup"); + :set Attach ($Attach, ($FilePath . ".backup")); + } + + # create configuration export + :if ($BackupSendExport = true) do={ + /export terse show-sensitive file=$FilePath; + $WaitForFile ($FilePath . ".rsc"); + :set ExportFile ($FileName . ".rsc"); + :set Attach ($Attach, ($FilePath . ".rsc")); + } + + # global-config-overlay + :if ($BackupSendGlobalConfig = true) do={ + # Do *NOT* use '/file/add ...' here, as it is limited to 4095 bytes! + :execute script={ :put [ /system/script/get global-config-overlay source ]; } \ + file=($FilePath . ".conf\00"); + $WaitForFile ($FilePath . ".conf"); + :set ConfigFile ($FileName . ".conf"); + :set Attach ($Attach, ($FilePath . ".conf")); + } + + # send email with status and files + $SendEMail2 ({ origin=$ScriptName; \ + subject=([ $SymbolForNotification "floppy-disk,incoming-envelope" ] . \ + "Backup & Config"); \ + message=("See attached files for backup and config export for " . \ + $Identity . ".\n\n" . \ + [ $DeviceInfo ] . "\n\n" . \ + [ $FormatLine "Backup file" $BackupFile ] . "\n" . \ + [ $FormatLine "Export file" $ExportFile ] . "\n" . \ + [ $FormatLine "Config file" $ConfigFile ]); \ + attach=$Attach; remove-attach=true }); + + # wait for the mail to be sent + :local I 0; + :while ([ :len [ /file/find where name ~ ($FilePath . "\\.(backup|rsc)\$") ] ] > 0) do={ + :if ($I >= 120) do={ + $LogPrint warning $ScriptName ("Files are still available, sending e-mail failed."); + :set PackagesUpdateBackupFailure true; + :set ExitOK true; + :error false; + } + :delay 1s; + :set I ($I + 1); + } +} on-error={ + :global ExitError; $ExitError $ExitOK [ :jobname ]; } diff --git a/backup-partition.rsc b/backup-partition.rsc index b03ad7e..1f0cf2e 100644 --- a/backup-partition.rsc +++ b/backup-partition.rsc @@ -1,39 +1,126 @@ #!rsc by RouterOS # RouterOS script: backup-partition -# Copyright (c) 2022-2023 Christian Hesse -# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md +# Copyright (c) 2022-2025 Christian Hesse +# https://rsc.eworm.de/COPYING.md # # provides: backup-script, order=70 +# requires RouterOS, version=7.15 +# requires device-mode, scheduler # # save configuration to fallback partition -# https://git.eworm.de/cgit/routeros-scripts/about/doc/backup-partition.md +# https://rsc.eworm.de/doc/backup-partition.md -:local 0 "backup-partition"; :global GlobalFunctionsReady; :while ($GlobalFunctionsReady != true) do={ :delay 500ms; } -:global LogPrintExit2; -:global ScriptLock; - -$ScriptLock $0; - -:if ([ :len [ /partitions/find ] ] < 2) do={ - $LogPrintExit2 error $0 ("Device does not have a fallback partition.") true; -} - -:local ActiveRunning [ /partitions/find where active running ]; - -:if ([ :len $ActiveRunning ] < 1) do={ - $LogPrintExit2 error $0 ("Device is not running from active partition.") true; -} - -:local ActiveRunningVar [ /partitions/get $ActiveRunning ]; - +:local ExitOK false; :do { - /partitions/save-config-to ($ActiveRunningVar->"fallback-to"); - $LogPrintExit2 info $0 ("Saved configuration to partition '" . \ - ($ActiveRunningVar->"fallback-to") . "'.") false; + :local ScriptName [ :jobname ]; + + :global BackupPartitionCopyBeforeFeatureUpdate; + :global PackagesUpdateBackupFailure; + + :global LogPrint; + :global ScriptFromTerminal; + :global ScriptLock; + :global VersionToNum; + + :local CopyTo do={ + :local ScriptName [ :tostr $1 ]; + :local FallbackTo [ :toid $2 ]; + :local FallbackToName [ :tostr $3 ]; + + :global LogPrint; + + :do { + /partitions/copy-to $FallbackTo; + $LogPrint info $ScriptName ("Copied RouterOS to partition '" . $FallbackToName . "'."); + :return true; + } on-error={ + $LogPrint error $ScriptName ("Failed copying RouterOS to partition '" . $FallbackToName . "'!"); + :return false; + } + } + + :if ([ $ScriptLock $ScriptName ] = false) do={ + :set PackagesUpdateBackupFailure true; + :set ExitOK true; + :error false; + } + + :if ([ :len [ /system/scheduler/find where name="running-from-backup-partition" ] ] > 0) do={ + $LogPrint warning $ScriptName ("Running from backup partition, refusing to act."); + :set PackagesUpdateBackupFailure true; + :set ExitOK true; + :error false; + } + + :if ([ :len [ /partitions/find ] ] < 2) do={ + $LogPrint error $ScriptName ("Device does not have a fallback partition."); + :set PackagesUpdateBackupFailure true; + :set ExitOK true; + :error false; + } + + :local ActiveRunning [ /partitions/find where active running ]; + + :if ([ :len $ActiveRunning ] < 1) do={ + $LogPrint error $ScriptName ("Device is not running from active partition."); + :set PackagesUpdateBackupFailure true; + :set ExitOK true; + :error false; + } + + :local FallbackToName [ /partitions/get $ActiveRunning fallback-to ]; + :local FallbackTo [ /partition/find where name=$FallbackToName !active ]; + + :if ([ :len $FallbackTo ] < 1) do={ + $LogPrint error $ScriptName ("There is no inactive partition named '" . $FallbackToName . "'."); + :set PackagesUpdateBackupFailure true; + :set ExitOK true; + :error false; + } + + :if ([ /partitions/get $ActiveRunning version ] != [ /partitions/get $FallbackTo version]) do={ + :if ([ $ScriptFromTerminal $ScriptName ] = true) do={ + :put ("The partitions have different RouterOS versions. Copy over to '" . $FallbackToName . "'? [y/N]"); + :if (([ /terminal/inkey timeout=60 ] % 32) = 25) do={ + :if ([ $CopyTo $ScriptName $FallbackTo $FallbackToName ] = false) do={ + :set PackagesUpdateBackupFailure true; + :set ExitOK true; + :error false; + } + } + } else={ + :local Update [ /system/package/update/get ]; + :local NumInstalled [ $VersionToNum ($Update->"installed-version") ]; + :local NumLatest [ $VersionToNum ($Update->"latest-version") ]; + :local BitMask [ $VersionToNum "255.255zero0" ]; + :if ($BackupPartitionCopyBeforeFeatureUpdate = true && $NumLatest > 0 && \ + ($NumInstalled & $BitMask) != ($NumLatest & $BitMask)) do={ + :if ([ $CopyTo $ScriptName $FallbackTo $FallbackToName ] = false) do={ + :set PackagesUpdateBackupFailure true; + :set ExitOK true; + :error false; + } + } + } + } + + :do { + /system/scheduler/add start-time=startup name="running-from-backup-partition" \ + on-event=(":log warning (\"Running from partition '\" . " . \ + "[ /partitions/get [ find where running ] name ] . \"'!\")"); + /partitions/save-config-to $FallbackTo; + /system/scheduler/remove "running-from-backup-partition"; + $LogPrint info $ScriptName ("Saved configuration to partition '" . $FallbackToName . "'."); + } on-error={ + /system/scheduler/remove [ find where name="running-from-backup-partition" ]; + $LogPrint error $ScriptName ("Failed saving configuration to partition '" . $FallbackToName . "'!"); + :set PackagesUpdateBackupFailure true; + :set ExitOK true; + :error false; + } } on-error={ - $LogPrintExit2 error $0 ("Failed saving configuration to partition '" . \ - ($ActiveRunningVar->"fallback-to") . "'!") true; + :global ExitError; $ExitError $ExitOK [ :jobname ]; } diff --git a/backup-upload.rsc b/backup-upload.rsc index 4c8af4a..14c3914 100644 --- a/backup-upload.rsc +++ b/backup-upload.rsc @@ -1,133 +1,178 @@ #!rsc by RouterOS # RouterOS script: backup-upload -# Copyright (c) 2013-2023 Christian Hesse -# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md +# Copyright (c) 2013-2025 Christian Hesse +# https://rsc.eworm.de/COPYING.md # # provides: backup-script, order=50 +# requires RouterOS, version=7.15 +# requires device-mode, fetch # # create and upload backup and config file -# https://git.eworm.de/cgit/routeros-scripts/about/doc/backup-upload.md +# https://rsc.eworm.de/doc/backup-upload.md -:local 0 "backup-upload"; :global GlobalFunctionsReady; :while ($GlobalFunctionsReady != true) do={ :delay 500ms; } -:global BackupPassword; -:global BackupRandomDelay; -:global BackupSendBinary; -:global BackupSendExport; -:global BackupSendGlobalConfig; -:global BackupUploadPass; -:global BackupUploadUrl; -:global BackupUploadUser; -:global Domain; -:global Identity; +:local ExitOK false; +:do { + :local ScriptName [ :jobname ]; -:global CharacterReplace; -:global DeviceInfo; -:global FormatLine; -:global IfThenElse; -:global LogPrintExit2; -:global MkDir; -:global RandomDelay; -:global ScriptFromTerminal; -:global ScriptLock; -:global SendNotification2; -:global SymbolForNotification; -:global WaitForFile; -:global WaitFullyConnected; + :global BackupPassword; + :global BackupRandomDelay; + :global BackupSendBinary; + :global BackupSendExport; + :global BackupSendGlobalConfig; + :global BackupUploadPass; + :global BackupUploadUrl; + :global BackupUploadUser; + :global Domain; + :global Identity; + :global PackagesUpdateBackupFailure; -:if ($BackupSendBinary != true && \ - $BackupSendExport != true) do={ - $LogPrintExit2 error $0 ("Configured to send neither backup nor config export.") true; -} + :global CleanName; + :global DeviceInfo; + :global IfThenElse; + :global LogPrint; + :global MkDir; + :global RandomDelay; + :global RmDir; + :global RmFile; + :global ScriptFromTerminal; + :global ScriptLock; + :global SendNotification2; + :global SymbolForNotification; + :global WaitForFile; + :global WaitFullyConnected; -$ScriptLock $0; -$WaitFullyConnected; - -:if ([ $ScriptFromTerminal $0 ] = false && $BackupRandomDelay > 0) do={ - $RandomDelay $BackupRandomDelay; -} - -# filename based on identity -:local DirName ("tmpfs/" . $0); -:local FileName [ $CharacterReplace ($Identity . "." . $Domain) "." "_" ]; -:local FilePath ($DirName . "/" . $FileName); -:local BackupFile "none"; -:local ExportFile "none"; -:local ConfigFile "none"; -:local Failed 0; - -:if ([ $MkDir $DirName ] = false) do={ - $LogPrintExit2 error $0 ("Failed creating directory!") true; -} - -# binary backup -:if ($BackupSendBinary = true) do={ - /system/backup/save encryption=aes-sha256 name=$FilePath password=$BackupPassword; - $WaitForFile ($FilePath . ".backup"); - - :do { - /tool/fetch upload=yes url=($BackupUploadUrl . "/" . $FileName . ".backup") \ - user=$BackupUploadUser password=$BackupUploadPass src-path=($FilePath . ".backup"); - :set BackupFile ($FileName . ".backup"); - } on-error={ - $LogPrintExit2 error $0 ("Uploading backup file failed!") false; - :set BackupFile "failed"; - :set Failed 1; + :if ($BackupSendBinary != true && \ + $BackupSendExport != true) do={ + $LogPrint error $ScriptName ("Configured to send neither backup nor config export."); + :set ExitOK true; + :error false; } - /file/remove ($FilePath . ".backup"); -} - -# create configuration export -:if ($BackupSendExport = true) do={ - /export terse show-sensitive file=$FilePath; - $WaitForFile ($FilePath . ".rsc"); - - :do { - /tool/fetch upload=yes url=($BackupUploadUrl . "/" . $FileName . ".rsc") \ - user=$BackupUploadUser password=$BackupUploadPass src-path=($FilePath . ".rsc"); - :set ExportFile ($FileName . ".rsc"); - } on-error={ - $LogPrintExit2 error $0 ("Uploading configuration export failed!") false; - :set ExportFile "failed"; - :set Failed 1; + :if ([ $ScriptLock $ScriptName ] = false) do={ + :set PackagesUpdateBackupFailure true; + :set ExitOK true; + :error false; } - /file/remove ($FilePath . ".rsc"); -} - -# global-config-overlay -:if ($BackupSendGlobalConfig = true) do={ - # Do *NOT* use '/file/add ...' here, as it is limited to 4095 bytes! - :execute script={ :put [ /system/script/get global-config-overlay source ]; } \ - file=($FilePath . ".conf"); - $WaitForFile ($FilePath . ".conf.txt"); - - :do { - /tool/fetch upload=yes url=($BackupUploadUrl . "/" . $FileName . ".conf") \ - user=$BackupUploadUser password=$BackupUploadPass src-path=($FilePath . ".conf.txt"); - :set ConfigFile ($FileName . ".conf"); - } on-error={ - $LogPrintExit2 error $0 ("Uploading global-config-overlay failed!") false; - :set ConfigFile "failed"; - :set Failed 1; + :if ([ :len [ /system/scheduler/find where name="running-from-backup-partition" ] ] > 0) do={ + $LogPrint warning $ScriptName ("Running from backup partition, refusing to act."); + :set PackagesUpdateBackupFailure true; + :set ExitOK true; + :error false; } - /file/remove ($FilePath . ".conf.txt"); -} + $WaitFullyConnected; -$SendNotification2 ({ origin=$0; \ - subject=[ $IfThenElse ($Failed > 0) \ - ([ $SymbolForNotification "floppy-disk,warning-sign" ] . "Backup & Config upload with failure") \ - ([ $SymbolForNotification "floppy-disk,up-arrow" ] . "Backup & Config upload") ]; \ - message=("Backup and config export upload for " . $Identity . ".\n\n" . \ - [ $DeviceInfo ] . "\n\n" . \ - [ $FormatLine "Backup file" $BackupFile ] . "\n" . \ - [ $FormatLine "Export file" $ExportFile ] . "\n" . \ - [ $FormatLine "Config file" $ConfigFile ]); silent=true }); + :if ([ $ScriptFromTerminal $ScriptName ] = false && $BackupRandomDelay > 0) do={ + $RandomDelay $BackupRandomDelay; + } -:if ($Failed = 1) do={ - :error "An error occured!"; + # filename based on identity + :local DirName ("tmpfs/" . $ScriptName); + :local FileName [ $CleanName ($Identity . "." . $Domain) ]; + :local FilePath ($DirName . "/" . $FileName); + :local BackupFile "none"; + :local ExportFile "none"; + :local ConfigFile "none"; + :local Failed 0; + + :if ([ $MkDir $DirName ] = false) do={ + $LogPrint error $ScriptName ("Failed creating directory!"); + :set ExitOK true; + :error false; + } + + # binary backup + :if ($BackupSendBinary = true) do={ + /system/backup/save encryption=aes-sha256 name=$FilePath password=$BackupPassword; + $WaitForFile ($FilePath . ".backup"); + + :do { + /tool/fetch upload=yes url=($BackupUploadUrl . "/" . $FileName . ".backup") \ + user=$BackupUploadUser password=$BackupUploadPass src-path=($FilePath . ".backup"); + :set BackupFile [ /file/get ($FilePath . ".backup") ]; + :set ($BackupFile->"name") ($FileName . ".backup"); + } on-error={ + $LogPrint error $ScriptName ("Uploading backup file failed!"); + :set BackupFile "failed"; + :set Failed 1; + } + + $RmFile ($FilePath . ".backup"); + } + + # create configuration export + :if ($BackupSendExport = true) do={ + /export terse show-sensitive file=$FilePath; + $WaitForFile ($FilePath . ".rsc"); + + :do { + /tool/fetch upload=yes url=($BackupUploadUrl . "/" . $FileName . ".rsc") \ + user=$BackupUploadUser password=$BackupUploadPass src-path=($FilePath . ".rsc"); + :set ExportFile [ /file/get ($FilePath . ".rsc") ]; + :set ($ExportFile->"name") ($FileName . ".rsc"); + } on-error={ + $LogPrint error $ScriptName ("Uploading configuration export failed!"); + :set ExportFile "failed"; + :set Failed 1; + } + + $RmFile ($FilePath . ".rsc"); + } + + # global-config-overlay + :if ($BackupSendGlobalConfig = true) do={ + # Do *NOT* use '/file/add ...' here, as it is limited to 4095 bytes! + :execute script={ :put [ /system/script/get global-config-overlay source ]; } \ + file=($FilePath . ".conf\00"); + $WaitForFile ($FilePath . ".conf"); + + :do { + /tool/fetch upload=yes url=($BackupUploadUrl . "/" . $FileName . ".conf") \ + user=$BackupUploadUser password=$BackupUploadPass src-path=($FilePath . ".conf"); + :set ConfigFile [ /file/get ($FilePath . ".conf") ]; + :set ($ConfigFile->"name") ($FileName . ".conf"); + } on-error={ + $LogPrint error $ScriptName ("Uploading global-config-overlay failed!"); + :set ConfigFile "failed"; + :set Failed 1; + } + + $RmFile ($FilePath . ".conf"); + } + + :local FileInfo do={ + :local Name $1; + :local File $2; + + :global FormatLine; + :global HumanReadableNum; + :global IfThenElse; + + :return \ + [ $IfThenElse ([ :typeof $File ] = "array") \ + ($Name . ":\n" . [ $FormatLine " name" ($File->"name") ] . "\n" . \ + [ $FormatLine " size" ([ $HumanReadableNum ($File->"size") 1024 ] . "B") ]) \ + [ $FormatLine $Name $File ] ]; + } + + $SendNotification2 ({ origin=$ScriptName; \ + subject=[ $IfThenElse ($Failed > 0) \ + ([ $SymbolForNotification "floppy-disk,warning-sign" ] . "Backup & Config upload with failure") \ + ([ $SymbolForNotification "floppy-disk,arrow-up" ] . "Backup & Config upload") ]; \ + message=("Backup and config export upload for " . $Identity . ".\n\n" . \ + [ $DeviceInfo ] . "\n\n" . \ + [ $FileInfo "Backup file" $BackupFile ] . "\n" . \ + [ $FileInfo "Export file" $ExportFile ] . "\n" . \ + [ $FileInfo "Config file" $ConfigFile ]); silent=true }); + + :if ($Failed = 1) do={ + :set PackagesUpdateBackupFailure true; + } + $RmDir $DirName; +} on-error={ + :global ExitError; $ExitError $ExitOK [ :jobname ]; } diff --git a/capsman-download-packages.capsman.rsc b/capsman-download-packages.capsman.rsc index 99ff052..25c43f5 100644 --- a/capsman-download-packages.capsman.rsc +++ b/capsman-download-packages.capsman.rsc @@ -1,89 +1,92 @@ #!rsc by RouterOS # RouterOS script: capsman-download-packages.capsman -# Copyright (c) 2018-2023 Christian Hesse +# Copyright (c) 2018-2025 Christian Hesse # Michael Gisbers -# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md +# https://rsc.eworm.de/COPYING.md +# +# requires RouterOS, version=7.15 # # download and cleanup packages for CAP installation from CAPsMAN -# https://git.eworm.de/cgit/routeros-scripts/about/doc/capsman-download-packages.md +# https://rsc.eworm.de/doc/capsman-download-packages.md # # !! Do not edit this file, it is generated from template! -:local 0 "capsman-download-packages.capsman"; :global GlobalFunctionsReady; :while ($GlobalFunctionsReady != true) do={ :delay 500ms; } -:global CleanFilePath; -:global DownloadPackage; -:global LogPrintExit2; -:global MkDir; -:global ScriptLock; -:global WaitFullyConnected; +:local ExitOK false; +:do { + :local ScriptName [ :jobname ]; -$ScriptLock $0; -$WaitFullyConnected; + :global CleanFilePath; + :global DownloadPackage; + :global LogPrint; + :global MkDir; + :global RmFile; + :global ScriptLock; + :global WaitFullyConnected; -:local PackagePath [ $CleanFilePath [ /caps-man/manager/get package-path ] ]; -:local InstalledVersion [ /system/package/update/get installed-version ]; -:local Updated false; - -:if ([ :len $PackagePath ] = 0) do={ - $LogPrintExit2 warning $0 ("The CAPsMAN package path is not defined, can not download packages.") true; -} - -:if ([ :len [ /file/find where name=$PackagePath type="directory" ] ] = 0) do={ - :if ([ $MkDir $PackagePath ] = false) do={ - $LogPrintExit2 warning $0 ("Creating directory at CAPsMAN package path (" . \ - $PackagePath . ") failed!") true; + :if ([ $ScriptLock $ScriptName ] = false) do={ + :set ExitOK true; + :error false; } - $LogPrintExit2 info $0 ("Created directory at CAPsMAN package path (" . $PackagePath . \ - "). Please place your packages!") false; -} + $WaitFullyConnected; -:foreach Package in=[ /file/find where type=package \ - package-version!=$InstalledVersion name~("^" . $PackagePath) ] do={ - :local File [ /file/get $Package ]; - :if ($File->"package-architecture" = "mips") do={ - :set ($File->"package-architecture") "mipsbe"; - } - :if ([ $DownloadPackage ($File->"package-name") $InstalledVersion \ - ($File->"package-architecture") $PackagePath ] = true) do={ - :set Updated true; - /file/remove $Package; - } -} + :local PackagePath [ $CleanFilePath [ /caps-man/manager/get package-path ] ]; + :local InstalledVersion [ /system/package/update/get installed-version ]; + :local Updated false; -:if ([ :len [ /system/logging/find where topics~"error" !(topics~"!error") \ - !(topics~"!caps") action=memory !disabled !invalid ] ] < 1) do={ - $LogPrintExit2 warning $0 ("Looks like error messages for 'caps' are not sent to memory. " . \ - "Probably can not download packages automatically.") false; -} else={ - :if ($Updated = false && [ /system/resource/get uptime ] < 2m) do={ - $LogPrintExit2 info $0 ("No packages downloaded, yet. Delaying for logs.") false; - :delay 2m; + :if ([ :len $PackagePath ] = 0) do={ + $LogPrint warning $ScriptName ("The CAPsMAN package path is not defined, can not download packages."); + :set ExitOK true; + :error false; } -} -:foreach Log in=[ /log/find where topics=({"caps"; "error"}) \ - message~("upgrade status: failed, failed to download file '.*-" . $InstalledVersion . \ - "-.*\\.npk', no such file") ] do={ - :local Message [ /log/get $Log message ]; - :local Package [ :pick $Message \ - ([ :find $Message "'" ] + 1) \ - [ :find $Message ("-" . $InstalledVersion . "-") ] ]; - :local Arch [ :pick $Message \ - ([ :find $Message ("-" . $InstalledVersion . "-") ] + 2 + [ :len $InstalledVersion ]) \ - [ :find $Message ".npk" ] ]; - :if ([ $DownloadPackage $Package $InstalledVersion $Arch $PackagePath ] = true) do={ - :set Updated true; + :if ([ :len [ /file/find where name=$PackagePath type="directory" ] ] = 0) do={ + :if ([ $MkDir $PackagePath ] = false) do={ + $LogPrint warning $ScriptName ("Creating directory at CAPsMAN package path (" . \ + $PackagePath . ") failed!"); + :set ExitOK true; + :error false; + } + $LogPrint info $ScriptName ("Created directory at CAPsMAN package path (" . $PackagePath . \ + "). Please place your packages!"); } -} -:if ($Updated = true) do={ - :local Script ([ /system/script/find where source~"\n# provides: capsman-rolling-upgrade\n" ]->0); - :if ([ :len $Script ] > 0) do={ - /system/script/run $Script; - } else={ - /caps-man/remote-cap/upgrade [ find where version!=$InstalledVersion ]; + :foreach Package in=[ /file/find where type=package \ + package-version!=$InstalledVersion name~("^" . $PackagePath) ] do={ + :local File [ /file/get $Package ]; + :if ($File->"package-architecture" = "mips") do={ + :set ($File->"package-architecture") "mipsbe"; + } + :if ([ $DownloadPackage ($File->"package-name") $InstalledVersion \ + ($File->"package-architecture") $PackagePath ] = true) do={ + :set Updated true; + $RmFile ($File->"name"); + } } + + :if ([ :len [ /file/find where type=package name~("^" . $PackagePath) ] ] = 0) do={ + $LogPrint info $ScriptName ("No packages available, downloading default set."); + :foreach Arch in={ "arm"; "mipsbe" } do={ + :foreach Package in={ "routeros"; "wireless" } do={ + :if ([ $DownloadPackage $Package $InstalledVersion $Arch $PackagePath ] = true) do={ + :set Updated true; + } + } + } + } + + :if ($Updated = true) do={ + :local Scripts [ /system/script/find where source~"\n# provides: capsman-rolling-upgrade.capsman\r?\n" ]; + :if ([ :len $Scripts ] > 0) do={ + :foreach Script in=$Scripts do={ + /system/script/run $Script; + } + } else={ + /caps-man/remote-cap/upgrade [ find where version!=$InstalledVersion ]; + } + } +} on-error={ + :global ExitError; $ExitError $ExitOK [ :jobname ]; } diff --git a/capsman-download-packages.template.rsc b/capsman-download-packages.template.rsc index 71bced0..b269838 100644 --- a/capsman-download-packages.template.rsc +++ b/capsman-download-packages.template.rsc @@ -1,106 +1,103 @@ #!rsc by RouterOS # RouterOS script: capsman-download-packages%TEMPL% -# Copyright (c) 2018-2023 Christian Hesse +# Copyright (c) 2018-2025 Christian Hesse # Michael Gisbers -# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md +# https://rsc.eworm.de/COPYING.md +# +# requires RouterOS, version=7.15 # # download and cleanup packages for CAP installation from CAPsMAN -# https://git.eworm.de/cgit/routeros-scripts/about/doc/capsman-download-packages.md +# https://rsc.eworm.de/doc/capsman-download-packages.md # -# !! This is just a template! Replace '%PATH%' with 'caps-man', -# !! 'interface/wireless' or 'interface/wifiwave2'! +# !! This is just a template to generate the real script! +# !! Pattern '%TEMPL%' is replaced, paths are filtered. -:local 0 "capsman-download-packages%TEMPL%"; :global GlobalFunctionsReady; :while ($GlobalFunctionsReady != true) do={ :delay 500ms; } -:global CleanFilePath; -:global DownloadPackage; -:global LogPrintExit2; -:global MkDir; -:global ScriptLock; -:global WaitFullyConnected; +:local ExitOK false; +:do { + :local ScriptName [ :jobname ]; -$ScriptLock $0; -$WaitFullyConnected; + :global CleanFilePath; + :global DownloadPackage; + :global LogPrint; + :global MkDir; + :global RmFile; + :global ScriptLock; + :global WaitFullyConnected; -:local PackagePath [ $CleanFilePath [ /caps-man/manager/get package-path ] ]; -:local PackagePath [ $CleanFilePath [ /interface/wifiwave2/capsman/get package-path ] ]; -:local InstalledVersion [ /system/package/update/get installed-version ]; -:local Updated false; - -:if ([ :len $PackagePath ] = 0) do={ - $LogPrintExit2 warning $0 ("The CAPsMAN package path is not defined, can not download packages.") true; -} - -:if ([ :len [ /file/find where name=$PackagePath type="directory" ] ] = 0) do={ - :if ([ $MkDir $PackagePath ] = false) do={ - $LogPrintExit2 warning $0 ("Creating directory at CAPsMAN package path (" . \ - $PackagePath . ") failed!") true; + :if ([ $ScriptLock $ScriptName ] = false) do={ + :set ExitOK true; + :error false; } - $LogPrintExit2 info $0 ("Created directory at CAPsMAN package path (" . $PackagePath . \ - "). Please place your packages!") false; -} + $WaitFullyConnected; -:foreach Package in=[ /file/find where type=package \ - package-version!=$InstalledVersion name~("^" . $PackagePath) ] do={ - :local File [ /file/get $Package ]; - :if ($File->"package-architecture" = "mips") do={ - :set ($File->"package-architecture") "mipsbe"; - } - :if ([ $DownloadPackage ($File->"package-name") $InstalledVersion \ - ($File->"package-architecture") $PackagePath ] = true) do={ - :set Updated true; - /file/remove $Package; - } -} + :local PackagePath [ $CleanFilePath [ /caps-man/manager/get package-path ] ]; + :local PackagePath [ $CleanFilePath [ /interface/wifi/capsman/get package-path ] ]; + :local InstalledVersion [ /system/package/update/get installed-version ]; + :local Updated false; -# NOT /interface/wifiwave2 # -:if ([ :len [ /system/logging/find where topics~"error" !(topics~"!error") \ - !(topics~"!caps") action=memory !disabled !invalid ] ] < 1) do={ - $LogPrintExit2 warning $0 ("Looks like error messages for 'caps' are not sent to memory. " . \ - "Probably can not download packages automatically.") false; -} else={ - :if ($Updated = false && [ /system/resource/get uptime ] < 2m) do={ - $LogPrintExit2 info $0 ("No packages downloaded, yet. Delaying for logs.") false; - :delay 2m; + :if ([ :len $PackagePath ] = 0) do={ + $LogPrint warning $ScriptName ("The CAPsMAN package path is not defined, can not download packages."); + :set ExitOK true; + :error false; } -} -:foreach Log in=[ /log/find where topics=({"caps"; "error"}) \ - message~("upgrade status: failed, failed to download file '.*-" . $InstalledVersion . \ - "-.*\\.npk', no such file") ] do={ - :local Message [ /log/get $Log message ]; - :local Package [ :pick $Message \ - ([ :find $Message "'" ] + 1) \ - [ :find $Message ("-" . $InstalledVersion . "-") ] ]; - :local Arch [ :pick $Message \ - ([ :find $Message ("-" . $InstalledVersion . "-") ] + 2 + [ :len $InstalledVersion ]) \ - [ :find $Message ".npk" ] ]; - :if ([ $DownloadPackage $Package $InstalledVersion $Arch $PackagePath ] = true) do={ - :set Updated true; + :if ([ :len [ /file/find where name=$PackagePath type="directory" ] ] = 0) do={ + :if ([ $MkDir $PackagePath ] = false) do={ + $LogPrint warning $ScriptName ("Creating directory at CAPsMAN package path (" . \ + $PackagePath . ") failed!"); + :set ExitOK true; + :error false; + } + $LogPrint info $ScriptName ("Created directory at CAPsMAN package path (" . $PackagePath . \ + "). Please place your packages!"); } -} -# NOT /interface/wifiwave2 # -# NOT /caps-man # -:if ([ :len [ /file/find where type=package name~("^" . $PackagePath) ] ] = 0) do={ - $LogPrintExit2 info $0 ("No packages available, downloading default set.") false; - :foreach Arch in={ "arm"; "arm64" } do={ - :foreach Package in={ "routeros"; "wifiwave2" } do={ - :if ([ $DownloadPackage $Package $InstalledVersion $Arch $PackagePath ] = true) do={ - :set Updated true; + + :foreach Package in=[ /file/find where type=package \ + package-version!=$InstalledVersion name~("^" . $PackagePath) ] do={ + :local File [ /file/get $Package ]; + :if ($File->"package-architecture" = "mips") do={ + :set ($File->"package-architecture") "mipsbe"; + } + :if ([ $DownloadPackage ($File->"package-name") $InstalledVersion \ + ($File->"package-architecture") $PackagePath ] = true) do={ + :set Updated true; + $RmFile ($File->"name"); + } + } + + :if ([ :len [ /file/find where type=package name~("^" . $PackagePath) ] ] = 0) do={ + $LogPrint info $ScriptName ("No packages available, downloading default set."); +# NOT /interface/wifi/ # + :foreach Arch in={ "arm"; "mipsbe" } do={ + :foreach Package in={ "routeros"; "wireless" } do={ +# NOT /interface/wifi/ # +# NOT /caps-man/ # + :foreach Arch in={ "arm"; "arm64" } do={ + :local Packages { "arm"={ "routeros"; "wifi-qcom"; "wifi-qcom-ac" }; + "arm64"={ "routeros"; "wifi-qcom" } }; + :foreach Package in=($Packages->$Arch) do={ +# NOT /caps-man/ # + :if ([ $DownloadPackage $Package $InstalledVersion $Arch $PackagePath ] = true) do={ + :set Updated true; + } } } } -} -# NOT /caps-man # -:if ($Updated = true) do={ - :local Script ([ /system/script/find where source~"\n# provides: capsman-rolling-upgrade\n" ]->0); - :if ([ :len $Script ] > 0) do={ - /system/script/run $Script; - } else={ - /caps-man/remote-cap/upgrade [ find where version!=$InstalledVersion ]; - /interface/wifiwave2/capsman/remote-cap/upgrade [ find where version!=$InstalledVersion ]; + :if ($Updated = true) do={ + :local Scripts [ /system/script/find where source~"\n# provides: capsman-rolling-upgrade%TEMPL%\r?\n" ]; + :if ([ :len $Scripts ] > 0) do={ + :foreach Script in=$Scripts do={ + /system/script/run $Script; + } + } else={ + /caps-man/remote-cap/upgrade [ find where version!=$InstalledVersion ]; + /interface/wifi/capsman/remote-cap/upgrade [ find where version!=$InstalledVersion ]; + } } +} on-error={ + :global ExitError; $ExitError $ExitOK [ :jobname ]; } diff --git a/capsman-download-packages.wifi.rsc b/capsman-download-packages.wifi.rsc new file mode 100644 index 0000000..901bb0a --- /dev/null +++ b/capsman-download-packages.wifi.rsc @@ -0,0 +1,94 @@ +#!rsc by RouterOS +# RouterOS script: capsman-download-packages.wifi +# Copyright (c) 2018-2025 Christian Hesse +# Michael Gisbers +# https://rsc.eworm.de/COPYING.md +# +# requires RouterOS, version=7.15 +# +# download and cleanup packages for CAP installation from CAPsMAN +# https://rsc.eworm.de/doc/capsman-download-packages.md +# +# !! Do not edit this file, it is generated from template! + +:global GlobalFunctionsReady; +:while ($GlobalFunctionsReady != true) do={ :delay 500ms; } + +:local ExitOK false; +:do { + :local ScriptName [ :jobname ]; + + :global CleanFilePath; + :global DownloadPackage; + :global LogPrint; + :global MkDir; + :global RmFile; + :global ScriptLock; + :global WaitFullyConnected; + + :if ([ $ScriptLock $ScriptName ] = false) do={ + :set ExitOK true; + :error false; + } + $WaitFullyConnected; + + :local PackagePath [ $CleanFilePath [ /interface/wifi/capsman/get package-path ] ]; + :local InstalledVersion [ /system/package/update/get installed-version ]; + :local Updated false; + + :if ([ :len $PackagePath ] = 0) do={ + $LogPrint warning $ScriptName ("The CAPsMAN package path is not defined, can not download packages."); + :set ExitOK true; + :error false; + } + + :if ([ :len [ /file/find where name=$PackagePath type="directory" ] ] = 0) do={ + :if ([ $MkDir $PackagePath ] = false) do={ + $LogPrint warning $ScriptName ("Creating directory at CAPsMAN package path (" . \ + $PackagePath . ") failed!"); + :set ExitOK true; + :error false; + } + $LogPrint info $ScriptName ("Created directory at CAPsMAN package path (" . $PackagePath . \ + "). Please place your packages!"); + } + + :foreach Package in=[ /file/find where type=package \ + package-version!=$InstalledVersion name~("^" . $PackagePath) ] do={ + :local File [ /file/get $Package ]; + :if ($File->"package-architecture" = "mips") do={ + :set ($File->"package-architecture") "mipsbe"; + } + :if ([ $DownloadPackage ($File->"package-name") $InstalledVersion \ + ($File->"package-architecture") $PackagePath ] = true) do={ + :set Updated true; + $RmFile ($File->"name"); + } + } + + :if ([ :len [ /file/find where type=package name~("^" . $PackagePath) ] ] = 0) do={ + $LogPrint info $ScriptName ("No packages available, downloading default set."); + :foreach Arch in={ "arm"; "arm64" } do={ + :local Packages { "arm"={ "routeros"; "wifi-qcom"; "wifi-qcom-ac" }; + "arm64"={ "routeros"; "wifi-qcom" } }; + :foreach Package in=($Packages->$Arch) do={ + :if ([ $DownloadPackage $Package $InstalledVersion $Arch $PackagePath ] = true) do={ + :set Updated true; + } + } + } + } + + :if ($Updated = true) do={ + :local Scripts [ /system/script/find where source~"\n# provides: capsman-rolling-upgrade.wifi\r?\n" ]; + :if ([ :len $Scripts ] > 0) do={ + :foreach Script in=$Scripts do={ + /system/script/run $Script; + } + } else={ + /interface/wifi/capsman/remote-cap/upgrade [ find where version!=$InstalledVersion ]; + } + } +} on-error={ + :global ExitError; $ExitError $ExitOK [ :jobname ]; +} diff --git a/capsman-download-packages.wifiwave2.rsc b/capsman-download-packages.wifiwave2.rsc deleted file mode 100644 index 6a12b2d..0000000 --- a/capsman-download-packages.wifiwave2.rsc +++ /dev/null @@ -1,74 +0,0 @@ -#!rsc by RouterOS -# RouterOS script: capsman-download-packages.wifiwave2 -# Copyright (c) 2018-2023 Christian Hesse -# Michael Gisbers -# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md -# -# download and cleanup packages for CAP installation from CAPsMAN -# https://git.eworm.de/cgit/routeros-scripts/about/doc/capsman-download-packages.md -# -# !! Do not edit this file, it is generated from template! - -:local 0 "capsman-download-packages.wifiwave2"; -:global GlobalFunctionsReady; -:while ($GlobalFunctionsReady != true) do={ :delay 500ms; } - -:global CleanFilePath; -:global DownloadPackage; -:global LogPrintExit2; -:global MkDir; -:global ScriptLock; -:global WaitFullyConnected; - -$ScriptLock $0; -$WaitFullyConnected; - -:local PackagePath [ $CleanFilePath [ /interface/wifiwave2/capsman/get package-path ] ]; -:local InstalledVersion [ /system/package/update/get installed-version ]; -:local Updated false; - -:if ([ :len $PackagePath ] = 0) do={ - $LogPrintExit2 warning $0 ("The CAPsMAN package path is not defined, can not download packages.") true; -} - -:if ([ :len [ /file/find where name=$PackagePath type="directory" ] ] = 0) do={ - :if ([ $MkDir $PackagePath ] = false) do={ - $LogPrintExit2 warning $0 ("Creating directory at CAPsMAN package path (" . \ - $PackagePath . ") failed!") true; - } - $LogPrintExit2 info $0 ("Created directory at CAPsMAN package path (" . $PackagePath . \ - "). Please place your packages!") false; -} - -:foreach Package in=[ /file/find where type=package \ - package-version!=$InstalledVersion name~("^" . $PackagePath) ] do={ - :local File [ /file/get $Package ]; - :if ($File->"package-architecture" = "mips") do={ - :set ($File->"package-architecture") "mipsbe"; - } - :if ([ $DownloadPackage ($File->"package-name") $InstalledVersion \ - ($File->"package-architecture") $PackagePath ] = true) do={ - :set Updated true; - /file/remove $Package; - } -} - -:if ([ :len [ /file/find where type=package name~("^" . $PackagePath) ] ] = 0) do={ - $LogPrintExit2 info $0 ("No packages available, downloading default set.") false; - :foreach Arch in={ "arm"; "arm64" } do={ - :foreach Package in={ "routeros"; "wifiwave2" } do={ - :if ([ $DownloadPackage $Package $InstalledVersion $Arch $PackagePath ] = true) do={ - :set Updated true; - } - } - } -} - -:if ($Updated = true) do={ - :local Script ([ /system/script/find where source~"\n# provides: capsman-rolling-upgrade\n" ]->0); - :if ([ :len $Script ] > 0) do={ - /system/script/run $Script; - } else={ - /interface/wifiwave2/capsman/remote-cap/upgrade [ find where version!=$InstalledVersion ]; - } -} diff --git a/capsman-rolling-upgrade.capsman.rsc b/capsman-rolling-upgrade.capsman.rsc index 7f83b6c..791b3db 100644 --- a/capsman-rolling-upgrade.capsman.rsc +++ b/capsman-rolling-upgrade.capsman.rsc @@ -1,40 +1,50 @@ #!rsc by RouterOS # RouterOS script: capsman-rolling-upgrade.capsman -# Copyright (c) 2018-2023 Christian Hesse +# Copyright (c) 2018-2025 Christian Hesse # Michael Gisbers -# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md +# https://rsc.eworm.de/COPYING.md # -# provides: capsman-rolling-upgrade +# provides: capsman-rolling-upgrade.capsman +# requires RouterOS, version=7.15 # # upgrade CAPs one after another -# https://git.eworm.de/cgit/routeros-scripts/about/doc/capsman-rolling-upgrade.md +# https://rsc.eworm.de/doc/capsman-rolling-upgrade.md # # !! Do not edit this file, it is generated from template! -:local 0 "capsman-rolling-upgrade.capsman"; :global GlobalFunctionsReady; :while ($GlobalFunctionsReady != true) do={ :delay 500ms; } -:global LogPrintExit2; -:global ScriptLock; +:local ExitOK false; +:do { + :local ScriptName [ :jobname ]; -$ScriptLock $0; + :global LogPrint; + :global ScriptLock; -:local InstalledVersion [ /system/package/update/get installed-version ]; - -:local RemoteCapCount [ :len [ /caps-man/remote-cap/find ] ]; -:if ($RemoteCapCount > 0) do={ - :local Delay (600 / $RemoteCapCount); - :if ($Delay > 120) do={ :set Delay 120; } - :foreach RemoteCap in=[ /caps-man/remote-cap/find where version!=$InstalledVersion ] do={ - :local RemoteCapVal [ /caps-man/remote-cap/get $RemoteCap ]; - :if ([ :len $RemoteCapVal ] > 1) do={ - $LogPrintExit2 info $0 ("Starting upgrade for " . $RemoteCapVal->"name" . \ - " (" . $RemoteCapVal->"identity" . ")...") false; - /caps-man/remote-cap/upgrade $RemoteCap; - } else={ - $LogPrintExit2 warning $0 ("Remote CAP vanished, skipping upgrade.") false; - } - :delay ($Delay . "s"); + :if ([ $ScriptLock $ScriptName ] = false) do={ + :set ExitOK true; + :error false; } + + :local InstalledVersion [ /system/package/update/get installed-version ]; + + :local RemoteCapCount [ :len [ /caps-man/remote-cap/find ] ]; + :if ($RemoteCapCount > 0) do={ + :local Delay (600 / $RemoteCapCount); + :if ($Delay > 120) do={ :set Delay 120; } + :foreach RemoteCap in=[ /caps-man/remote-cap/find where version!=$InstalledVersion ] do={ + :local RemoteCapVal [ /caps-man/remote-cap/get $RemoteCap ]; + :if ([ :len $RemoteCapVal ] > 1) do={ + $LogPrint info $ScriptName ("Starting upgrade for " . $RemoteCapVal->"name" . \ + " (" . $RemoteCapVal->"identity" . ")..."); + /caps-man/remote-cap/upgrade $RemoteCap; + } else={ + $LogPrint warning $ScriptName ("Remote CAP vanished, skipping upgrade."); + } + :delay ($Delay . "s"); + } + } +} on-error={ + :global ExitError; $ExitError $ExitOK [ :jobname ]; } diff --git a/capsman-rolling-upgrade.template.rsc b/capsman-rolling-upgrade.template.rsc index 0890ad2..0b1cc2b 100644 --- a/capsman-rolling-upgrade.template.rsc +++ b/capsman-rolling-upgrade.template.rsc @@ -1,48 +1,58 @@ #!rsc by RouterOS # RouterOS script: capsman-rolling-upgrade%TEMPL% -# Copyright (c) 2018-2023 Christian Hesse +# Copyright (c) 2018-2025 Christian Hesse # Michael Gisbers -# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md +# https://rsc.eworm.de/COPYING.md # -# provides: capsman-rolling-upgrade +# provides: capsman-rolling-upgrade%TEMPL% +# requires RouterOS, version=7.15 # # upgrade CAPs one after another -# https://git.eworm.de/cgit/routeros-scripts/about/doc/capsman-rolling-upgrade.md +# https://rsc.eworm.de/doc/capsman-rolling-upgrade.md # -# !! This is just a template! Replace '%PATH%' with 'caps-man', -# !! 'interface/wireless' or 'interface/wifiwave2'! +# !! This is just a template to generate the real script! +# !! Pattern '%TEMPL%' is replaced, paths are filtered. -:local 0 "capsman-rolling-upgrade%TEMPL%"; :global GlobalFunctionsReady; :while ($GlobalFunctionsReady != true) do={ :delay 500ms; } -:global LogPrintExit2; -:global ScriptLock; +:local ExitOK false; +:do { + :local ScriptName [ :jobname ]; -$ScriptLock $0; + :global LogPrint; + :global ScriptLock; -:local InstalledVersion [ /system/package/update/get installed-version ]; - -:local RemoteCapCount [ :len [ /caps-man/remote-cap/find ] ]; -:local RemoteCapCount [ :len [ /interface/wifiwave2/capsman/remote-cap/find ] ]; -:if ($RemoteCapCount > 0) do={ - :local Delay (600 / $RemoteCapCount); - :if ($Delay > 120) do={ :set Delay 120; } - :foreach RemoteCap in=[ /caps-man/remote-cap/find where version!=$InstalledVersion ] do={ - :foreach RemoteCap in=[ /interface/wifiwave2/capsman/remote-cap/find where version!=$InstalledVersion ] do={ - :local RemoteCapVal [ /caps-man/remote-cap/get $RemoteCap ]; - :local RemoteCapVal [ /interface/wifiwave2/capsman/remote-cap/get $RemoteCap ]; - :if ([ :len $RemoteCapVal ] > 1) do={ -# NOT /caps-man # - :set ($RemoteCapVal->"name") ($RemoteCapVal->"common-name"); -# NOT /caps-man # - $LogPrintExit2 info $0 ("Starting upgrade for " . $RemoteCapVal->"name" . \ - " (" . $RemoteCapVal->"identity" . ")...") false; - /caps-man/remote-cap/upgrade $RemoteCap; - /interface/wifiwave2/capsman/remote-cap/upgrade $RemoteCap; - } else={ - $LogPrintExit2 warning $0 ("Remote CAP vanished, skipping upgrade.") false; - } - :delay ($Delay . "s"); + :if ([ $ScriptLock $ScriptName ] = false) do={ + :set ExitOK true; + :error false; } + + :local InstalledVersion [ /system/package/update/get installed-version ]; + + :local RemoteCapCount [ :len [ /caps-man/remote-cap/find ] ]; + :local RemoteCapCount [ :len [ /interface/wifi/capsman/remote-cap/find ] ]; + :if ($RemoteCapCount > 0) do={ + :local Delay (600 / $RemoteCapCount); + :if ($Delay > 120) do={ :set Delay 120; } + :foreach RemoteCap in=[ /caps-man/remote-cap/find where version!=$InstalledVersion ] do={ + :foreach RemoteCap in=[ /interface/wifi/capsman/remote-cap/find where version!=$InstalledVersion ] do={ + :local RemoteCapVal [ /caps-man/remote-cap/get $RemoteCap ]; + :local RemoteCapVal [ /interface/wifi/capsman/remote-cap/get $RemoteCap ]; + :if ([ :len $RemoteCapVal ] > 1) do={ +# NOT /caps-man/ # + :set ($RemoteCapVal->"name") ($RemoteCapVal->"common-name"); +# NOT /caps-man/ # + $LogPrint info $ScriptName ("Starting upgrade for " . $RemoteCapVal->"name" . \ + " (" . $RemoteCapVal->"identity" . ")..."); + /caps-man/remote-cap/upgrade $RemoteCap; + /interface/wifi/capsman/remote-cap/upgrade $RemoteCap; + } else={ + $LogPrint warning $ScriptName ("Remote CAP vanished, skipping upgrade."); + } + :delay ($Delay . "s"); + } + } +} on-error={ + :global ExitError; $ExitError $ExitOK [ :jobname ]; } diff --git a/capsman-rolling-upgrade.wifi.rsc b/capsman-rolling-upgrade.wifi.rsc new file mode 100644 index 0000000..4afdee2 --- /dev/null +++ b/capsman-rolling-upgrade.wifi.rsc @@ -0,0 +1,51 @@ +#!rsc by RouterOS +# RouterOS script: capsman-rolling-upgrade.wifi +# Copyright (c) 2018-2025 Christian Hesse +# Michael Gisbers +# https://rsc.eworm.de/COPYING.md +# +# provides: capsman-rolling-upgrade.wifi +# requires RouterOS, version=7.15 +# +# upgrade CAPs one after another +# https://rsc.eworm.de/doc/capsman-rolling-upgrade.md +# +# !! Do not edit this file, it is generated from template! + +:global GlobalFunctionsReady; +:while ($GlobalFunctionsReady != true) do={ :delay 500ms; } + +:local ExitOK false; +:do { + :local ScriptName [ :jobname ]; + + :global LogPrint; + :global ScriptLock; + + :if ([ $ScriptLock $ScriptName ] = false) do={ + :set ExitOK true; + :error false; + } + + :local InstalledVersion [ /system/package/update/get installed-version ]; + + :local RemoteCapCount [ :len [ /interface/wifi/capsman/remote-cap/find ] ]; + :if ($RemoteCapCount > 0) do={ + :local Delay (600 / $RemoteCapCount); + :if ($Delay > 120) do={ :set Delay 120; } + :foreach RemoteCap in=[ /interface/wifi/capsman/remote-cap/find where version!=$InstalledVersion ] do={ + :local RemoteCapVal [ /interface/wifi/capsman/remote-cap/get $RemoteCap ]; + :if ([ :len $RemoteCapVal ] > 1) do={ + :set ($RemoteCapVal->"name") ($RemoteCapVal->"common-name"); + $LogPrint info $ScriptName ("Starting upgrade for " . $RemoteCapVal->"name" . \ + " (" . $RemoteCapVal->"identity" . ")..."); + /interface/wifi/capsman/remote-cap/upgrade $RemoteCap; + } else={ + $LogPrint warning $ScriptName ("Remote CAP vanished, skipping upgrade."); + } + :delay ($Delay . "s"); + } + } +} on-error={ + :global ExitError; $ExitError $ExitOK [ :jobname ]; +} diff --git a/capsman-rolling-upgrade.wifiwave2.rsc b/capsman-rolling-upgrade.wifiwave2.rsc deleted file mode 100644 index c0c08e7..0000000 --- a/capsman-rolling-upgrade.wifiwave2.rsc +++ /dev/null @@ -1,41 +0,0 @@ -#!rsc by RouterOS -# RouterOS script: capsman-rolling-upgrade.wifiwave2 -# Copyright (c) 2018-2023 Christian Hesse -# Michael Gisbers -# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md -# -# provides: capsman-rolling-upgrade -# -# upgrade CAPs one after another -# https://git.eworm.de/cgit/routeros-scripts/about/doc/capsman-rolling-upgrade.md -# -# !! Do not edit this file, it is generated from template! - -:local 0 "capsman-rolling-upgrade.wifiwave2"; -:global GlobalFunctionsReady; -:while ($GlobalFunctionsReady != true) do={ :delay 500ms; } - -:global LogPrintExit2; -:global ScriptLock; - -$ScriptLock $0; - -:local InstalledVersion [ /system/package/update/get installed-version ]; - -:local RemoteCapCount [ :len [ /interface/wifiwave2/capsman/remote-cap/find ] ]; -:if ($RemoteCapCount > 0) do={ - :local Delay (600 / $RemoteCapCount); - :if ($Delay > 120) do={ :set Delay 120; } - :foreach RemoteCap in=[ /interface/wifiwave2/capsman/remote-cap/find where version!=$InstalledVersion ] do={ - :local RemoteCapVal [ /interface/wifiwave2/capsman/remote-cap/get $RemoteCap ]; - :if ([ :len $RemoteCapVal ] > 1) do={ - :set ($RemoteCapVal->"name") ($RemoteCapVal->"common-name"); - $LogPrintExit2 info $0 ("Starting upgrade for " . $RemoteCapVal->"name" . \ - " (" . $RemoteCapVal->"identity" . ")...") false; - /interface/wifiwave2/capsman/remote-cap/upgrade $RemoteCap; - } else={ - $LogPrintExit2 warning $0 ("Remote CAP vanished, skipping upgrade.") false; - } - :delay ($Delay . "s"); - } -} diff --git a/certificate-renew-issued.rsc b/certificate-renew-issued.rsc index 2b18a76..91a48de 100644 --- a/certificate-renew-issued.rsc +++ b/certificate-renew-issued.rsc @@ -1,41 +1,52 @@ #!rsc by RouterOS # RouterOS script: certificate-renew-issued -# Copyright (c) 2019-2023 Christian Hesse -# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md +# Copyright (c) 2019-2025 Christian Hesse +# https://rsc.eworm.de/COPYING.md +# +# requires RouterOS, version=7.15 # # renew locally issued certificates -# https://git.eworm.de/cgit/routeros-scripts/about/doc/certificate-renew-issued.md +# https://rsc.eworm.de/doc/certificate-renew-issued.md -:local 0 "certificate-renew-issued"; :global GlobalFunctionsReady; :while ($GlobalFunctionsReady != true) do={ :delay 500ms; } -:global CertIssuedExportPass; +:local ExitOK false; +:do { + :local ScriptName [ :jobname ]; -:global LogPrintExit2; -:global MkDir; -:global ScriptLock; + :global CertIssuedExportPass; -$ScriptLock $0; + :global LogPrint; + :global MkDir; + :global ScriptLock; -:foreach Cert in=[ /certificate/find where issued expires-after<3w ] do={ - :local CertVal [ /certificate/get $Cert ]; - /certificate/issued-revoke $Cert; - /certificate/set name=($CertVal->"name" . "-revoked-" . [ /system/clock/get date ]) $Cert; - /certificate/add name=($CertVal->"name") common-name=($CertVal->"common-name") \ - key-usage=($CertVal->"key-usage") subject-alt-name=($CertVal->"subject-alt-name"); - /certificate/sign ($CertVal->"name") ca=($CertVal->"ca"); - :if ([ :typeof ($CertIssuedExportPass->($CertVal->"common-name")) ] = "str") do={ - :if ([ $MkDir "cert-issued" ] = true) do={ - /certificate/export-certificate ($CertVal->"name") type=pkcs12 \ - file-name=("cert-issued/" . $CertVal->"common-name") \ - export-passphrase=($CertIssuedExportPass->($CertVal->"common-name")); - $LogPrintExit2 info $0 ("Issued a new certificate for \"" . $CertVal->"common-name" . \ - "\", exported to \"cert-issued/" . $CertVal->"common-name" . ".p12\".") false; - } else={ - $LogPrintExit2 warning $0 ("Failed creating directory, not exporting certificate.") false; - } - } else={ - $LogPrintExit2 info $0 ("Issued a new certificate for \"" . $CertVal->"common-name" . "\".") false; + :if ([ $ScriptLock $ScriptName ] = false) do={ + :set ExitOK true; + :error false; } + + :foreach Cert in=[ /certificate/find where issued expires-after<3w ] do={ + :local CertVal [ /certificate/get $Cert ]; + /certificate/issued-revoke $Cert; + /certificate/set name=($CertVal->"name" . "-revoked-" . [ /system/clock/get date ]) $Cert; + /certificate/add name=($CertVal->"name") common-name=($CertVal->"common-name") \ + key-usage=($CertVal->"key-usage") subject-alt-name=($CertVal->"subject-alt-name"); + /certificate/sign ($CertVal->"name") ca=($CertVal->"ca"); + :if ([ :typeof ($CertIssuedExportPass->($CertVal->"common-name")) ] = "str") do={ + :if ([ $MkDir "cert-issued" ] = true) do={ + /certificate/export-certificate ($CertVal->"name") type=pkcs12 \ + file-name=("cert-issued/" . $CertVal->"common-name") \ + export-passphrase=($CertIssuedExportPass->($CertVal->"common-name")); + $LogPrint info $ScriptName ("Issued a new certificate for '" . $CertVal->"common-name" . \ + "', exported to 'cert-issued/" . $CertVal->"common-name" . ".p12'."); + } else={ + $LogPrint warning $ScriptName ("Failed creating directory, not exporting certificate."); + } + } else={ + $LogPrint info $ScriptName ("Issued a new certificate for '" . $CertVal->"common-name" . "'."); + } + } +} on-error={ + :global ExitError; $ExitError $ExitOK [ :jobname ]; } diff --git a/certs/Certum-Trusted-Network-CA.pem b/certs/Certum-Trusted-Network-CA.pem new file mode 100644 index 0000000..a48e706 --- /dev/null +++ b/certs/Certum-Trusted-Network-CA.pem @@ -0,0 +1,29 @@ +# Issuer: CN=Certum Trusted Network CA O=Unizeto Technologies S.A. OU=Certum Certification Authority +# Subject: CN=Certum Trusted Network CA O=Unizeto Technologies S.A. OU=Certum Certification Authority +# Label: "Certum Trusted Network CA" +# Serial: 279744 +# MD5 Fingerprint: d5:e9:81:40:c5:18:69:fc:46:2c:89:75:62:0f:aa:78 +# SHA1 Fingerprint: 07:e0:32:e0:20:b7:2c:3f:19:2f:06:28:a2:59:3a:19:a7:0f:06:9e +# SHA256 Fingerprint: 5c:58:46:8d:55:f5:8e:49:7e:74:39:82:d2:b5:00:10:b6:d1:65:37:4a:cf:83:a7:d4:a3:2d:b7:68:c4:40:8e +-----BEGIN CERTIFICATE----- +MIIDuzCCAqOgAwIBAgIDBETAMA0GCSqGSIb3DQEBBQUAMH4xCzAJBgNVBAYTAlBM +MSIwIAYDVQQKExlVbml6ZXRvIFRlY2hub2xvZ2llcyBTLkEuMScwJQYDVQQLEx5D +ZXJ0dW0gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxIjAgBgNVBAMTGUNlcnR1bSBU +cnVzdGVkIE5ldHdvcmsgQ0EwHhcNMDgxMDIyMTIwNzM3WhcNMjkxMjMxMTIwNzM3 +WjB+MQswCQYDVQQGEwJQTDEiMCAGA1UEChMZVW5pemV0byBUZWNobm9sb2dpZXMg +Uy5BLjEnMCUGA1UECxMeQ2VydHVtIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MSIw +IAYDVQQDExlDZXJ0dW0gVHJ1c3RlZCBOZXR3b3JrIENBMIIBIjANBgkqhkiG9w0B +AQEFAAOCAQ8AMIIBCgKCAQEA4/t9o3K6wvDJFIf1awFO4W5AB7ptJ11/91sts1rH +UV+rpDKmYYe2bg+G0jACl/jXaVehGDldamR5xgFZrDwxSjh80gTSSyjoIF87B6LM +TXPb865Px1bVWqeWifrzq2jUI4ZZJ88JJ7ysbnKDHDBy3+Ci6dLhdHUZvSqeexVU +BBvXQzmtVSjF4hq79MDkrjhJM8x2hZ85RdKknvISjFH4fOQtf/WsX+sWn7Et0brM +kUJ3TCXJkDhv2/DM+44el1k+1WBO5gUo7Ul5E0u6SNsv+XLTOcr+H9g0cvW0QM8x +AcPs3hEtF10fuFDRXhmnad4HMyjKUJX5p1TLVIZQRan5SQIDAQABo0IwQDAPBgNV +HRMBAf8EBTADAQH/MB0GA1UdDgQWBBQIds3LB/8k9sXN7buQvOKEN0Z19zAOBgNV +HQ8BAf8EBAMCAQYwDQYJKoZIhvcNAQEFBQADggEBAKaorSLOAT2mo/9i0Eidi15y +sHhE49wcrwn9I0j6vSrEuVUEtRCjjSfeC4Jj0O7eDDd5QVsisrCaQVymcODU0HfL +I9MA4GxWL+FpDQ3Zqr8hgVDZBqWo/5U30Kr+4rP1mS1FhIrlQgnXdAIv94nYmem8 +J9RHjboNRhx3zxSkHLmkMcScKHQDNP8zGSal6Q10tz6XxnboJ5ajZt3hrvJBW8qY +VoNzcOSGGtIxQbovvi0TWnZvTuhOgQ4/WwMioBK+ZlgRSssDxLQqKi2WF+A5VLxI +03YnnZotBqbJ7DnSq9ufmgsnAjUpsUCV5/nonFWIGUbWtzT1fs45mtk48VH3Tyw= +-----END CERTIFICATE----- diff --git a/certs/Cloudflare Inc ECC CA-3.pem b/certs/Cloudflare Inc ECC CA-3.pem deleted file mode 100644 index fa91603..0000000 --- a/certs/Cloudflare Inc ECC CA-3.pem +++ /dev/null @@ -1,163 +0,0 @@ -Certificate: - Data: - Version: 3 (0x2) - Serial Number: - 0a:37:87:64:5e:5f:b4:8c:22:4e:fd:1b:ed:14:0c:3c - Signature Algorithm: sha256WithRSAEncryption - Issuer: C = IE, O = Baltimore, OU = CyberTrust, CN = Baltimore CyberTrust Root - Validity - Not Before: Jan 27 12:48:08 2020 GMT - Not After : Dec 31 23:59:59 2024 GMT - Subject: C = US, O = "Cloudflare, Inc.", CN = Cloudflare Inc ECC CA-3 - Subject Public Key Info: - Public Key Algorithm: id-ecPublicKey - Public-Key: (256 bit) - pub: - 04:b9:ad:4d:66:99:14:0b:46:ec:1f:81:d1:2a:50: - 1e:9d:03:15:2f:34:12:7d:2d:96:b8:88:38:9b:85: - 5f:8f:bf:bb:4d:ef:61:46:c4:c9:73:d4:24:4f:e0: - ee:1c:ce:6c:b3:51:71:2f:6a:ee:4c:05:09:77:d3: - 72:62:a4:9b:d7 - ASN1 OID: prime256v1 - NIST CURVE: P-256 - X509v3 extensions: - X509v3 Subject Key Identifier: - A5:CE:37:EA:EB:B0:75:0E:94:67:88:B4:45:FA:D9:24:10:87:96:1F - X509v3 Authority Key Identifier: - E5:9D:59:30:82:47:58:CC:AC:FA:08:54:36:86:7B:3A:B5:04:4D:F0 - X509v3 Key Usage: critical - Digital Signature, Certificate Sign, CRL Sign - X509v3 Extended Key Usage: - TLS Web Server Authentication, TLS Web Client Authentication - X509v3 Basic Constraints: critical - CA:TRUE, pathlen:0 - Authority Information Access: - OCSP - URI:http://ocsp.digicert.com - X509v3 CRL Distribution Points: - Full Name: - URI:http://crl3.digicert.com/Omniroot2025.crl - X509v3 Certificate Policies: - Policy: 2.16.840.1.114412.1.1 - CPS: https://www.digicert.com/CPS - Policy: 2.16.840.1.114412.1.2 - Policy: 2.23.140.1.2.1 - Policy: 2.23.140.1.2.2 - Policy: 2.23.140.1.2.3 - Signature Algorithm: sha256WithRSAEncryption - Signature Value: - 05:24:1d:dd:1b:b0:2a:eb:98:d6:85:e3:39:4d:5e:6b:57:9d: - 82:57:fc:eb:e8:31:a2:57:90:65:05:be:16:44:38:5a:77:02: - b9:cf:10:42:c6:e1:92:a4:e3:45:27:f8:00:47:2c:68:a8:56: - 99:53:54:8f:ad:9e:40:c1:d0:0f:b6:d7:0d:0b:38:48:6c:50: - 2c:49:90:06:5b:64:1d:8b:cc:48:30:2e:de:08:e2:9b:49:22: - c0:92:0c:11:5e:96:92:94:d5:fc:20:dc:56:6c:e5:92:93:bf: - 7a:1c:c0:37:e3:85:49:15:fa:2b:e1:74:39:18:0f:b7:da:f3: - a2:57:58:60:4f:cc:8e:94:00:fc:46:7b:34:31:3e:4d:47:82: - 81:3a:cb:f4:89:5d:0e:ef:4d:0d:6e:9c:1b:82:24:dd:32:25: - 5d:11:78:51:10:3d:a0:35:23:04:2f:65:6f:9c:c1:d1:43:d7: - d0:1e:f3:31:67:59:27:dd:6b:d2:75:09:93:11:24:24:14:cf: - 29:be:e6:23:c3:b8:8f:72:3f:e9:07:c8:24:44:53:7a:b3:b9: - 61:65:a1:4c:0e:c6:48:00:c9:75:63:05:87:70:45:52:83:d3: - 95:9d:45:ea:f0:e8:31:1d:7e:09:1f:0a:fe:3e:dd:aa:3c:5e: - 74:d2:ac:b1 ------BEGIN CERTIFICATE----- -MIIDzTCCArWgAwIBAgIQCjeHZF5ftIwiTv0b7RQMPDANBgkqhkiG9w0BAQsFADBa -MQswCQYDVQQGEwJJRTESMBAGA1UEChMJQmFsdGltb3JlMRMwEQYDVQQLEwpDeWJl -clRydXN0MSIwIAYDVQQDExlCYWx0aW1vcmUgQ3liZXJUcnVzdCBSb290MB4XDTIw -MDEyNzEyNDgwOFoXDTI0MTIzMTIzNTk1OVowSjELMAkGA1UEBhMCVVMxGTAXBgNV -BAoTEENsb3VkZmxhcmUsIEluYy4xIDAeBgNVBAMTF0Nsb3VkZmxhcmUgSW5jIEVD -QyBDQS0zMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEua1NZpkUC0bsH4HRKlAe -nQMVLzQSfS2WuIg4m4Vfj7+7Te9hRsTJc9QkT+DuHM5ss1FxL2ruTAUJd9NyYqSb -16OCAWgwggFkMB0GA1UdDgQWBBSlzjfq67B1DpRniLRF+tkkEIeWHzAfBgNVHSME -GDAWgBTlnVkwgkdYzKz6CFQ2hns6tQRN8DAOBgNVHQ8BAf8EBAMCAYYwHQYDVR0l -BBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMBIGA1UdEwEB/wQIMAYBAf8CAQAwNAYI -KwYBBQUHAQEEKDAmMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5j -b20wOgYDVR0fBDMwMTAvoC2gK4YpaHR0cDovL2NybDMuZGlnaWNlcnQuY29tL09t -bmlyb290MjAyNS5jcmwwbQYDVR0gBGYwZDA3BglghkgBhv1sAQEwKjAoBggrBgEF -BQcCARYcaHR0cHM6Ly93d3cuZGlnaWNlcnQuY29tL0NQUzALBglghkgBhv1sAQIw -CAYGZ4EMAQIBMAgGBmeBDAECAjAIBgZngQwBAgMwDQYJKoZIhvcNAQELBQADggEB -AAUkHd0bsCrrmNaF4zlNXmtXnYJX/OvoMaJXkGUFvhZEOFp3ArnPEELG4ZKk40Un -+ABHLGioVplTVI+tnkDB0A+21w0LOEhsUCxJkAZbZB2LzEgwLt4I4ptJIsCSDBFe -lpKU1fwg3FZs5ZKTv3ocwDfjhUkV+ivhdDkYD7fa86JXWGBPzI6UAPxGezQxPk1H -goE6y/SJXQ7vTQ1unBuCJN0yJV0ReFEQPaA1IwQvZW+cwdFD19Ae8zFnWSfda9J1 -CZMRJCQUzym+5iPDuI9yP+kHyCREU3qzuWFloUwOxkgAyXVjBYdwRVKD05WdRerw -6DEdfgkfCv4+3ao8XnTSrLE= ------END CERTIFICATE----- -Certificate: - Data: - Version: 3 (0x2) - Serial Number: 33554617 (0x20000b9) - Signature Algorithm: sha1WithRSAEncryption - Issuer: C = IE, O = Baltimore, OU = CyberTrust, CN = Baltimore CyberTrust Root - Validity - Not Before: May 12 18:46:00 2000 GMT - Not After : May 12 23:59:00 2025 GMT - Subject: C = IE, O = Baltimore, OU = CyberTrust, CN = Baltimore CyberTrust Root - Subject Public Key Info: - Public Key Algorithm: rsaEncryption - Public-Key: (2048 bit) - Modulus: - 00:a3:04:bb:22:ab:98:3d:57:e8:26:72:9a:b5:79: - d4:29:e2:e1:e8:95:80:b1:b0:e3:5b:8e:2b:29:9a: - 64:df:a1:5d:ed:b0:09:05:6d:db:28:2e:ce:62:a2: - 62:fe:b4:88:da:12:eb:38:eb:21:9d:c0:41:2b:01: - 52:7b:88:77:d3:1c:8f:c7:ba:b9:88:b5:6a:09:e7: - 73:e8:11:40:a7:d1:cc:ca:62:8d:2d:e5:8f:0b:a6: - 50:d2:a8:50:c3:28:ea:f5:ab:25:87:8a:9a:96:1c: - a9:67:b8:3f:0c:d5:f7:f9:52:13:2f:c2:1b:d5:70: - 70:f0:8f:c0:12:ca:06:cb:9a:e1:d9:ca:33:7a:77: - d6:f8:ec:b9:f1:68:44:42:48:13:d2:c0:c2:a4:ae: - 5e:60:fe:b6:a6:05:fc:b4:dd:07:59:02:d4:59:18: - 98:63:f5:a5:63:e0:90:0c:7d:5d:b2:06:7a:f3:85: - ea:eb:d4:03:ae:5e:84:3e:5f:ff:15:ed:69:bc:f9: - 39:36:72:75:cf:77:52:4d:f3:c9:90:2c:b9:3d:e5: - c9:23:53:3f:1f:24:98:21:5c:07:99:29:bd:c6:3a: - ec:e7:6e:86:3a:6b:97:74:63:33:bd:68:18:31:f0: - 78:8d:76:bf:fc:9e:8e:5d:2a:86:a7:4d:90:dc:27: - 1a:39 - Exponent: 65537 (0x10001) - X509v3 extensions: - X509v3 Subject Key Identifier: - E5:9D:59:30:82:47:58:CC:AC:FA:08:54:36:86:7B:3A:B5:04:4D:F0 - X509v3 Basic Constraints: critical - CA:TRUE, pathlen:3 - X509v3 Key Usage: critical - Certificate Sign, CRL Sign - Signature Algorithm: sha1WithRSAEncryption - Signature Value: - 85:0c:5d:8e:e4:6f:51:68:42:05:a0:dd:bb:4f:27:25:84:03: - bd:f7:64:fd:2d:d7:30:e3:a4:10:17:eb:da:29:29:b6:79:3f: - 76:f6:19:13:23:b8:10:0a:f9:58:a4:d4:61:70:bd:04:61:6a: - 12:8a:17:d5:0a:bd:c5:bc:30:7c:d6:e9:0c:25:8d:86:40:4f: - ec:cc:a3:7e:38:c6:37:11:4f:ed:dd:68:31:8e:4c:d2:b3:01: - 74:ee:be:75:5e:07:48:1a:7f:70:ff:16:5c:84:c0:79:85:b8: - 05:fd:7f:be:65:11:a3:0f:c0:02:b4:f8:52:37:39:04:d5:a9: - 31:7a:18:bf:a0:2a:f4:12:99:f7:a3:45:82:e3:3c:5e:f5:9d: - 9e:b5:c8:9e:7c:2e:c8:a4:9e:4e:08:14:4b:6d:fd:70:6d:6b: - 1a:63:bd:64:e6:1f:b7:ce:f0:f2:9f:2e:bb:1b:b7:f2:50:88: - 73:92:c2:e2:e3:16:8d:9a:32:02:ab:8e:18:dd:e9:10:11:ee: - 7e:35:ab:90:af:3e:30:94:7a:d0:33:3d:a7:65:0f:f5:fc:8e: - 9e:62:cf:47:44:2c:01:5d:bb:1d:b5:32:d2:47:d2:38:2e:d0: - fe:81:dc:32:6a:1e:b5:ee:3c:d5:fc:e7:81:1d:19:c3:24:42: - ea:63:39:a9 ------BEGIN CERTIFICATE----- -MIIDdzCCAl+gAwIBAgIEAgAAuTANBgkqhkiG9w0BAQUFADBaMQswCQYDVQQGEwJJ -RTESMBAGA1UEChMJQmFsdGltb3JlMRMwEQYDVQQLEwpDeWJlclRydXN0MSIwIAYD -VQQDExlCYWx0aW1vcmUgQ3liZXJUcnVzdCBSb290MB4XDTAwMDUxMjE4NDYwMFoX -DTI1MDUxMjIzNTkwMFowWjELMAkGA1UEBhMCSUUxEjAQBgNVBAoTCUJhbHRpbW9y -ZTETMBEGA1UECxMKQ3liZXJUcnVzdDEiMCAGA1UEAxMZQmFsdGltb3JlIEN5YmVy -VHJ1c3QgUm9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKMEuyKr -mD1X6CZymrV51Cni4eiVgLGw41uOKymaZN+hXe2wCQVt2yguzmKiYv60iNoS6zjr -IZ3AQSsBUnuId9Mcj8e6uYi1agnnc+gRQKfRzMpijS3ljwumUNKoUMMo6vWrJYeK -mpYcqWe4PwzV9/lSEy/CG9VwcPCPwBLKBsua4dnKM3p31vjsufFoREJIE9LAwqSu -XmD+tqYF/LTdB1kC1FkYmGP1pWPgkAx9XbIGevOF6uvUA65ehD5f/xXtabz5OTZy -dc93Uk3zyZAsuT3lySNTPx8kmCFcB5kpvcY67Oduhjprl3RjM71oGDHweI12v/ye -jl0qhqdNkNwnGjkCAwEAAaNFMEMwHQYDVR0OBBYEFOWdWTCCR1jMrPoIVDaGezq1 -BE3wMBIGA1UdEwEB/wQIMAYBAf8CAQMwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3 -DQEBBQUAA4IBAQCFDF2O5G9RaEIFoN27TyclhAO992T9Ldcw46QQF+vaKSm2eT92 -9hkTI7gQCvlYpNRhcL0EYWoSihfVCr3FvDB81ukMJY2GQE/szKN+OMY3EU/t3Wgx -jkzSswF07r51XgdIGn9w/xZchMB5hbgF/X++ZRGjD8ACtPhSNzkE1akxehi/oCr0 -Epn3o0WC4zxe9Z2etciefC7IpJ5OCBRLbf1wbWsaY71k5h+3zvDyny67G7fyUIhz -ksLi4xaNmjICq44Y3ekQEe5+NauQrz4wlHrQMz2nZQ/1/I6eYs9HRCwBXbsdtTLS -R9I4LtD+gdwyah617jzV/OeBHRnDJELqYzmp ------END CERTIFICATE----- diff --git a/certs/DigiCert TLS Hybrid ECC SHA384 2020 CA1.pem b/certs/DigiCert TLS Hybrid ECC SHA384 2020 CA1.pem deleted file mode 100644 index 446f56f..0000000 --- a/certs/DigiCert TLS Hybrid ECC SHA384 2020 CA1.pem +++ /dev/null @@ -1,174 +0,0 @@ -Certificate: - Data: - Version: 3 (0x2) - Serial Number: - 07:f2:f3:5c:87:a8:77:af:7a:ef:e9:47:99:35:25:bd - Signature Algorithm: sha384WithRSAEncryption - Issuer: C = US, O = DigiCert Inc, OU = www.digicert.com, CN = DigiCert Global Root CA - Validity - Not Before: Apr 14 00:00:00 2021 GMT - Not After : Apr 13 23:59:59 2031 GMT - Subject: C = US, O = DigiCert Inc, CN = DigiCert TLS Hybrid ECC SHA384 2020 CA1 - Subject Public Key Info: - Public Key Algorithm: id-ecPublicKey - Public-Key: (384 bit) - pub: - 04:c1:1b:c6:9a:5b:98:d9:a4:29:a0:e9:d4:04:b5: - db:eb:a6:b2:6c:55:c0:ff:ed:98:c6:49:2f:06:27: - 51:cb:bf:70:c1:05:7a:c3:b1:9d:87:89:ba:ad:b4: - 13:17:c9:a8:b4:83:c8:b8:90:d1:cc:74:35:36:3c: - 83:72:b0:b5:d0:f7:22:69:c8:f1:80:c4:7b:40:8f: - cf:68:87:26:5c:39:89:f1:4d:91:4d:da:89:8b:e4: - 03:c3:43:e5:bf:2f:73 - ASN1 OID: secp384r1 - NIST CURVE: P-384 - X509v3 extensions: - X509v3 Basic Constraints: critical - CA:TRUE, pathlen:0 - X509v3 Subject Key Identifier: - 0A:BC:08:29:17:8C:A5:39:6D:7A:0E:CE:33:C7:2E:B3:ED:FB:C3:7A - X509v3 Authority Key Identifier: - keyid:03:DE:50:35:56:D1:4C:BB:66:F0:A3:E2:1B:1B:C3:97:B2:3D:D1:55 - - X509v3 Key Usage: critical - Digital Signature, Certificate Sign, CRL Sign - X509v3 Extended Key Usage: - TLS Web Server Authentication, TLS Web Client Authentication - Authority Information Access: - OCSP - URI:http://ocsp.digicert.com - CA Issuers - URI:http://cacerts.digicert.com/DigiCertGlobalRootCA.crt - - X509v3 CRL Distribution Points: - - Full Name: - URI:http://crl3.digicert.com/DigiCertGlobalRootCA.crl - - X509v3 Certificate Policies: - Policy: 2.16.840.1.114412.2.1 - Policy: 2.23.140.1.1 - Policy: 2.23.140.1.2.1 - Policy: 2.23.140.1.2.2 - Policy: 2.23.140.1.2.3 - - Signature Algorithm: sha384WithRSAEncryption - 47:59:81:7f:d4:1b:1f:b0:71:f6:98:5d:18:ba:98:47:98:b0: - 7e:76:2b:ea:ff:1a:8b:ac:26:b3:42:8d:31:e6:4a:e8:19:d0: - ef:da:14:e7:d7:14:92:a1:92:f2:a7:2e:2d:af:fb:1d:f6:fb: - 53:b0:8a:3f:fc:d8:16:0a:e9:b0:2e:b6:a5:0b:18:90:35:26: - a2:da:f6:a8:b7:32:fc:95:23:4b:c6:45:b9:c4:cf:e4:7c:ee: - e6:c9:f8:90:bd:72:e3:99:c3:1d:0b:05:7c:6a:97:6d:b2:ab: - 02:36:d8:c2:bc:2c:01:92:3f:04:a3:8b:75:11:c7:b9:29:bc: - 11:d0:86:ba:92:bc:26:f9:65:c8:37:cd:26:f6:86:13:0c:04: - aa:89:e5:78:b1:c1:4e:79:bc:76:a3:0b:51:e4:c5:d0:9e:6a: - fe:1a:2c:56:ae:06:36:27:a3:73:1c:08:7d:93:32:d0:c2:44: - 19:da:8d:f4:0e:7b:1d:28:03:2b:09:8a:76:ca:77:dc:87:7a: - ac:7b:52:26:55:a7:72:0f:9d:d2:88:4f:fe:b1:21:c5:1a:a1: - aa:39:f5:56:db:c2:84:c4:35:1f:70:da:bb:46:f0:86:bf:64: - 00:c4:3e:f7:9f:46:1b:9d:23:05:b9:7d:b3:4f:0f:a9:45:3a: - e3:74:30:98 ------BEGIN CERTIFICATE----- -MIIEFzCCAv+gAwIBAgIQB/LzXIeod6967+lHmTUlvTANBgkqhkiG9w0BAQwFADBh -MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 -d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD -QTAeFw0yMTA0MTQwMDAwMDBaFw0zMTA0MTMyMzU5NTlaMFYxCzAJBgNVBAYTAlVT -MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxMDAuBgNVBAMTJ0RpZ2lDZXJ0IFRMUyBI -eWJyaWQgRUNDIFNIQTM4NCAyMDIwIENBMTB2MBAGByqGSM49AgEGBSuBBAAiA2IA -BMEbxppbmNmkKaDp1AS12+umsmxVwP/tmMZJLwYnUcu/cMEFesOxnYeJuq20ExfJ -qLSDyLiQ0cx0NTY8g3KwtdD3ImnI8YDEe0CPz2iHJlw5ifFNkU3aiYvkA8ND5b8v -c6OCAYIwggF+MBIGA1UdEwEB/wQIMAYBAf8CAQAwHQYDVR0OBBYEFAq8CCkXjKU5 -bXoOzjPHLrPt+8N6MB8GA1UdIwQYMBaAFAPeUDVW0Uy7ZvCj4hsbw5eyPdFVMA4G -A1UdDwEB/wQEAwIBhjAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwdgYI -KwYBBQUHAQEEajBoMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5j -b20wQAYIKwYBBQUHMAKGNGh0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdp -Q2VydEdsb2JhbFJvb3RDQS5jcnQwQgYDVR0fBDswOTA3oDWgM4YxaHR0cDovL2Ny -bDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0R2xvYmFsUm9vdENBLmNybDA9BgNVHSAE -NjA0MAsGCWCGSAGG/WwCATAHBgVngQwBATAIBgZngQwBAgEwCAYGZ4EMAQICMAgG -BmeBDAECAzANBgkqhkiG9w0BAQwFAAOCAQEAR1mBf9QbH7Bx9phdGLqYR5iwfnYr -6v8ai6wms0KNMeZK6BnQ79oU59cUkqGS8qcuLa/7Hfb7U7CKP/zYFgrpsC62pQsY -kDUmotr2qLcy/JUjS8ZFucTP5Hzu5sn4kL1y45nDHQsFfGqXbbKrAjbYwrwsAZI/ -BKOLdRHHuSm8EdCGupK8JvllyDfNJvaGEwwEqonleLHBTnm8dqMLUeTF0J5q/hos -Vq4GNiejcxwIfZMy0MJEGdqN9A57HSgDKwmKdsp33Id6rHtSJlWncg+d0ohP/rEh -xRqhqjn1VtvChMQ1H3Dau0bwhr9kAMQ+959GG50jBbl9s08PqUU643QwmA== ------END CERTIFICATE----- -Certificate: - Data: - Version: 3 (0x2) - Serial Number: - 08:3b:e0:56:90:42:46:b1:a1:75:6a:c9:59:91:c7:4a - Signature Algorithm: sha1WithRSAEncryption - Issuer: C = US, O = DigiCert Inc, OU = www.digicert.com, CN = DigiCert Global Root CA - Validity - Not Before: Nov 10 00:00:00 2006 GMT - Not After : Nov 10 00:00:00 2031 GMT - Subject: C = US, O = DigiCert Inc, OU = www.digicert.com, CN = DigiCert Global Root CA - Subject Public Key Info: - Public Key Algorithm: rsaEncryption - RSA Public-Key: (2048 bit) - Modulus: - 00:e2:3b:e1:11:72:de:a8:a4:d3:a3:57:aa:50:a2: - 8f:0b:77:90:c9:a2:a5:ee:12:ce:96:5b:01:09:20: - cc:01:93:a7:4e:30:b7:53:f7:43:c4:69:00:57:9d: - e2:8d:22:dd:87:06:40:00:81:09:ce:ce:1b:83:bf: - df:cd:3b:71:46:e2:d6:66:c7:05:b3:76:27:16:8f: - 7b:9e:1e:95:7d:ee:b7:48:a3:08:da:d6:af:7a:0c: - 39:06:65:7f:4a:5d:1f:bc:17:f8:ab:be:ee:28:d7: - 74:7f:7a:78:99:59:85:68:6e:5c:23:32:4b:bf:4e: - c0:e8:5a:6d:e3:70:bf:77:10:bf:fc:01:f6:85:d9: - a8:44:10:58:32:a9:75:18:d5:d1:a2:be:47:e2:27: - 6a:f4:9a:33:f8:49:08:60:8b:d4:5f:b4:3a:84:bf: - a1:aa:4a:4c:7d:3e:cf:4f:5f:6c:76:5e:a0:4b:37: - 91:9e:dc:22:e6:6d:ce:14:1a:8e:6a:cb:fe:cd:b3: - 14:64:17:c7:5b:29:9e:32:bf:f2:ee:fa:d3:0b:42: - d4:ab:b7:41:32:da:0c:d4:ef:f8:81:d5:bb:8d:58: - 3f:b5:1b:e8:49:28:a2:70:da:31:04:dd:f7:b2:16: - f2:4c:0a:4e:07:a8:ed:4a:3d:5e:b5:7f:a3:90:c3: - af:27 - Exponent: 65537 (0x10001) - X509v3 extensions: - X509v3 Key Usage: critical - Digital Signature, Certificate Sign, CRL Sign - X509v3 Basic Constraints: critical - CA:TRUE - X509v3 Subject Key Identifier: - 03:DE:50:35:56:D1:4C:BB:66:F0:A3:E2:1B:1B:C3:97:B2:3D:D1:55 - X509v3 Authority Key Identifier: - keyid:03:DE:50:35:56:D1:4C:BB:66:F0:A3:E2:1B:1B:C3:97:B2:3D:D1:55 - - Signature Algorithm: sha1WithRSAEncryption - cb:9c:37:aa:48:13:12:0a:fa:dd:44:9c:4f:52:b0:f4:df:ae: - 04:f5:79:79:08:a3:24:18:fc:4b:2b:84:c0:2d:b9:d5:c7:fe: - f4:c1:1f:58:cb:b8:6d:9c:7a:74:e7:98:29:ab:11:b5:e3:70: - a0:a1:cd:4c:88:99:93:8c:91:70:e2:ab:0f:1c:be:93:a9:ff: - 63:d5:e4:07:60:d3:a3:bf:9d:5b:09:f1:d5:8e:e3:53:f4:8e: - 63:fa:3f:a7:db:b4:66:df:62:66:d6:d1:6e:41:8d:f2:2d:b5: - ea:77:4a:9f:9d:58:e2:2b:59:c0:40:23:ed:2d:28:82:45:3e: - 79:54:92:26:98:e0:80:48:a8:37:ef:f0:d6:79:60:16:de:ac: - e8:0e:cd:6e:ac:44:17:38:2f:49:da:e1:45:3e:2a:b9:36:53: - cf:3a:50:06:f7:2e:e8:c4:57:49:6c:61:21:18:d5:04:ad:78: - 3c:2c:3a:80:6b:a7:eb:af:15:14:e9:d8:89:c1:b9:38:6c:e2: - 91:6c:8a:ff:64:b9:77:25:57:30:c0:1b:24:a3:e1:dc:e9:df: - 47:7c:b5:b4:24:08:05:30:ec:2d:bd:0b:bf:45:bf:50:b9:a9: - f3:eb:98:01:12:ad:c8:88:c6:98:34:5f:8d:0a:3c:c6:e9:d5: - 95:95:6d:de ------BEGIN CERTIFICATE----- -MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBh -MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 -d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD -QTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAwMDAwMDBaMGExCzAJBgNVBAYTAlVT -MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j -b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkqhkiG -9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsB -CSDMAZOnTjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97 -nh6Vfe63SKMI2tavegw5BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt -43C/dxC//AH2hdmoRBBYMql1GNXRor5H4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7P -T19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y7vrTC0LUq7dBMtoM1O/4 -gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQABo2MwYTAO -BgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbR -TLtm8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUw -DQYJKoZIhvcNAQEFBQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/Esr -hMAtudXH/vTBH1jLuG2cenTnmCmrEbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg -06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIttep3Sp+dWOIrWcBAI+0tKIJF -PnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886UAb3LujEV0ls -YSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk -CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4= ------END CERTIFICATE----- diff --git a/certs/DigiCert-Global-Root-G2.pem b/certs/DigiCert-Global-Root-G2.pem new file mode 100644 index 0000000..8af6c7a --- /dev/null +++ b/certs/DigiCert-Global-Root-G2.pem @@ -0,0 +1,29 @@ +# Issuer: CN=DigiCert Global Root G2 O=DigiCert Inc OU=www.digicert.com +# Subject: CN=DigiCert Global Root G2 O=DigiCert Inc OU=www.digicert.com +# Label: "DigiCert Global Root G2" +# Serial: 4293743540046975378534879503202253541 +# MD5 Fingerprint: e4:a6:8a:c8:54:ac:52:42:46:0a:fd:72:48:1b:2a:44 +# SHA1 Fingerprint: df:3c:24:f9:bf:d6:66:76:1b:26:80:73:fe:06:d1:cc:8d:4f:82:a4 +# SHA256 Fingerprint: cb:3c:cb:b7:60:31:e5:e0:13:8f:8d:d3:9a:23:f9:de:47:ff:c3:5e:43:c1:14:4c:ea:27:d4:6a:5a:b1:cb:5f +-----BEGIN CERTIFICATE----- +MIIDjjCCAnagAwIBAgIQAzrx5qcRqaC7KGSxHQn65TANBgkqhkiG9w0BAQsFADBh +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBH +MjAeFw0xMzA4MDExMjAwMDBaFw0zODAxMTUxMjAwMDBaMGExCzAJBgNVBAYTAlVT +MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j +b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IEcyMIIBIjANBgkqhkiG +9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuzfNNNx7a8myaJCtSnX/RrohCgiN9RlUyfuI +2/Ou8jqJkTx65qsGGmvPrC3oXgkkRLpimn7Wo6h+4FR1IAWsULecYxpsMNzaHxmx +1x7e/dfgy5SDN67sH0NO3Xss0r0upS/kqbitOtSZpLYl6ZtrAGCSYP9PIUkY92eQ +q2EGnI/yuum06ZIya7XzV+hdG82MHauVBJVJ8zUtluNJbd134/tJS7SsVQepj5Wz +tCO7TG1F8PapspUwtP1MVYwnSlcUfIKdzXOS0xZKBgyMUNGPHgm+F6HmIcr9g+UQ +vIOlCsRnKPZzFBQ9RnbDhxSJITRNrw9FDKZJobq7nMWxM4MphQIDAQABo0IwQDAP +BgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUTiJUIBiV +5uNu5g/6+rkS7QYXjzkwDQYJKoZIhvcNAQELBQADggEBAGBnKJRvDkhj6zHd6mcY +1Yl9PMWLSn/pvtsrF9+wX3N3KjITOYFnQoQj8kVnNeyIv/iPsGEMNKSuIEyExtv4 +NeF22d+mQrvHRAiGfzZ0JFrabA0UWTW98kndth/Jsw1HKj2ZL7tcu7XUIOGZX1NG +Fdtom/DzMNU+MeKNhJ7jitralj41E6Vf8PlwUHBHQRFXGU7Aj64GxJUTFy8bJZ91 +8rGOmaFvE7FBcf6IKshPECBV1/MUReXgRPTqh5Uykw7+U0b6LJ3/iyK5S9kJRaTe +pLiaWN0bfVKfjllDiIGknibVb63dDcY3fe0Dkhvld1927jyNxF1WW6LZZm6zNTfl +MrY= +-----END CERTIFICATE----- diff --git a/certs/DigiCert-Global-Root-G3.pem b/certs/DigiCert-Global-Root-G3.pem new file mode 100644 index 0000000..12324dc --- /dev/null +++ b/certs/DigiCert-Global-Root-G3.pem @@ -0,0 +1,22 @@ +# Issuer: CN=DigiCert Global Root G3 O=DigiCert Inc OU=www.digicert.com +# Subject: CN=DigiCert Global Root G3 O=DigiCert Inc OU=www.digicert.com +# Label: "DigiCert Global Root G3" +# Serial: 7089244469030293291760083333884364146 +# MD5 Fingerprint: f5:5d:a4:50:a5:fb:28:7e:1e:0f:0d:cc:96:57:56:ca +# SHA1 Fingerprint: 7e:04:de:89:6a:3e:66:6d:00:e6:87:d3:3f:fa:d9:3b:e8:3d:34:9e +# SHA256 Fingerprint: 31:ad:66:48:f8:10:41:38:c7:38:f3:9e:a4:32:01:33:39:3e:3a:18:cc:02:29:6e:f9:7c:2a:c9:ef:67:31:d0 +-----BEGIN CERTIFICATE----- +MIICPzCCAcWgAwIBAgIQBVVWvPJepDU1w6QP1atFcjAKBggqhkjOPQQDAzBhMQsw +CQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cu +ZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBHMzAe +Fw0xMzA4MDExMjAwMDBaFw0zODAxMTUxMjAwMDBaMGExCzAJBgNVBAYTAlVTMRUw +EwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20x +IDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IEczMHYwEAYHKoZIzj0CAQYF +K4EEACIDYgAE3afZu4q4C/sLfyHS8L6+c/MzXRq8NOrexpu80JX28MzQC7phW1FG +fp4tn+6OYwwX7Adw9c+ELkCDnOg/QW07rdOkFFk2eJ0DQ+4QE2xy3q6Ip6FrtUPO +Z9wj/wMco+I+o0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAd +BgNVHQ4EFgQUs9tIpPmhxdiuNkHMEWNpYim8S8YwCgYIKoZIzj0EAwMDaAAwZQIx +AK288mw/EkrRLTnDCgmXc/SINoyIJ7vmiI1Qhadj+Z4y3maTD/HMsQmP3Wyr+mt/ +oAIwOWZbwmSNuJ5Q3KjVSaLtx9zRSX8XAbjIho9OjIgrqJqpisXRAL34VOKa5Vt8 +sycX +-----END CERTIFICATE----- diff --git a/certs/E1.pem b/certs/E1.pem deleted file mode 100644 index a62fc03..0000000 --- a/certs/E1.pem +++ /dev/null @@ -1,124 +0,0 @@ -Certificate: - Data: - Version: 3 (0x2) - Serial Number: - b3:bd:df:f8:a7:84:5b:bc:e9:03:a0:41:35:b3:4a:45 - Signature Algorithm: ecdsa-with-SHA384 - Issuer: C = US, O = Internet Security Research Group, CN = ISRG Root X2 - Validity - Not Before: Sep 4 00:00:00 2020 GMT - Not After : Sep 15 16:00:00 2025 GMT - Subject: C = US, O = Let's Encrypt, CN = E1 - Subject Public Key Info: - Public Key Algorithm: id-ecPublicKey - Public-Key: (384 bit) - pub: - 04:24:5c:2d:a2:2a:fd:1c:4b:a6:5d:97:73:27:31: - ac:b2:a0:69:62:ef:65:e8:a6:b0:f0:ac:4b:9f:ff: - 1c:0b:70:0f:d3:98:2f:4d:fc:0f:00:9b:37:f0:74: - 05:57:32:97:2e:05:ef:2a:43:25:a3:fb:6e:34:27: - 13:f6:4f:7e:69:d3:02:99:5e:eb:24:47:92:c1:24: - 9b:e6:b1:21:8f:c1:24:81:fc:68:cc:1f:69:ba:58: - f5:19:22:f7:74:c6:16 - ASN1 OID: secp384r1 - NIST CURVE: P-384 - X509v3 extensions: - X509v3 Key Usage: critical - Digital Signature, Certificate Sign, CRL Sign - X509v3 Extended Key Usage: - TLS Web Client Authentication, TLS Web Server Authentication - X509v3 Basic Constraints: critical - CA:TRUE, pathlen:0 - X509v3 Subject Key Identifier: - 5A:F3:ED:2B:FC:36:C2:37:79:B9:52:30:EA:54:6F:CF:55:CB:2E:AC - X509v3 Authority Key Identifier: - keyid:7C:42:96:AE:DE:4B:48:3B:FA:92:F8:9E:8C:CF:6D:8B:A9:72:37:95 - - Authority Information Access: - CA Issuers - URI:http://x2.i.lencr.org/ - - X509v3 CRL Distribution Points: - - Full Name: - URI:http://x2.c.lencr.org/ - - X509v3 Certificate Policies: - Policy: 2.23.140.1.2.1 - Policy: 1.3.6.1.4.1.44947.1.1.1 - - Signature Algorithm: ecdsa-with-SHA384 - 30:64:02:30:7b:74:d5:52:13:8d:61:fe:0d:ba:3f:03:00:9d: - f3:d7:98:84:d9:57:2e:bd:e9:0f:9c:5c:48:04:21:f2:cb:b3: - 60:72:8e:97:d6:12:4f:ca:44:f6:42:c9:d3:7b:86:a9:02:30: - 5a:b1:b1:b4:ed:ea:60:99:20:b1:38:03:ca:3d:a0:26:b8:ee: - 6e:2d:4a:f6:c6:66:1f:33:9a:db:92:4a:d5:f5:29:13:c6:70: - 62:28:ba:23:8c:cf:3d:2f:cb:82:e9:7f ------BEGIN CERTIFICATE----- -MIICxjCCAk2gAwIBAgIRALO93/inhFu86QOgQTWzSkUwCgYIKoZIzj0EAwMwTzEL -MAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2VhcmNo -IEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDIwHhcNMjAwOTA0MDAwMDAwWhcN -MjUwOTE1MTYwMDAwWjAyMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNTGV0J3MgRW5j -cnlwdDELMAkGA1UEAxMCRTEwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQkXC2iKv0c -S6Zdl3MnMayyoGli72XoprDwrEuf/xwLcA/TmC9N/A8AmzfwdAVXMpcuBe8qQyWj -+240JxP2T35p0wKZXuskR5LBJJvmsSGPwSSB/GjMH2m6WPUZIvd0xhajggEIMIIB -BDAOBgNVHQ8BAf8EBAMCAYYwHQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMB -MBIGA1UdEwEB/wQIMAYBAf8CAQAwHQYDVR0OBBYEFFrz7Sv8NsI3eblSMOpUb89V -yy6sMB8GA1UdIwQYMBaAFHxClq7eS0g7+pL4nozPbYupcjeVMDIGCCsGAQUFBwEB -BCYwJDAiBggrBgEFBQcwAoYWaHR0cDovL3gyLmkubGVuY3Iub3JnLzAnBgNVHR8E -IDAeMBygGqAYhhZodHRwOi8veDIuYy5sZW5jci5vcmcvMCIGA1UdIAQbMBkwCAYG -Z4EMAQIBMA0GCysGAQQBgt8TAQEBMAoGCCqGSM49BAMDA2cAMGQCMHt01VITjWH+ -Dbo/AwCd89eYhNlXLr3pD5xcSAQh8suzYHKOl9YST8pE9kLJ03uGqQIwWrGxtO3q -YJkgsTgDyj2gJrjubi1K9sZmHzOa25JK1fUpE8ZwYii6I4zPPS/Lgul/ ------END CERTIFICATE----- -Certificate: - Data: - Version: 3 (0x2) - Serial Number: - 41:d2:9d:d1:72:ea:ee:a7:80:c1:2c:6c:e9:2f:87:52 - Signature Algorithm: ecdsa-with-SHA384 - Issuer: C = US, O = Internet Security Research Group, CN = ISRG Root X2 - Validity - Not Before: Sep 4 00:00:00 2020 GMT - Not After : Sep 17 16:00:00 2040 GMT - Subject: C = US, O = Internet Security Research Group, CN = ISRG Root X2 - Subject Public Key Info: - Public Key Algorithm: id-ecPublicKey - Public-Key: (384 bit) - pub: - 04:cd:9b:d5:9f:80:83:0a:ec:09:4a:f3:16:4a:3e: - 5c:cf:77:ac:de:67:05:0d:1d:07:b6:dc:16:fb:5a: - 8b:14:db:e2:71:60:c4:ba:45:95:11:89:8e:ea:06: - df:f7:2a:16:1c:a4:b9:c5:c5:32:e0:03:e0:1e:82: - 18:38:8b:d7:45:d8:0a:6a:6e:e6:00:77:fb:02:51: - 7d:22:d8:0a:6e:9a:5b:77:df:f0:fa:41:ec:39:dc: - 75:ca:68:07:0c:1f:ea - ASN1 OID: secp384r1 - NIST CURVE: P-384 - X509v3 extensions: - X509v3 Key Usage: critical - Certificate Sign, CRL Sign - X509v3 Basic Constraints: critical - CA:TRUE - X509v3 Subject Key Identifier: - 7C:42:96:AE:DE:4B:48:3B:FA:92:F8:9E:8C:CF:6D:8B:A9:72:37:95 - Signature Algorithm: ecdsa-with-SHA384 - 30:65:02:30:7b:79:4e:46:50:84:c2:44:87:46:1b:45:70:ff: - 58:99:de:f4:fd:a4:d2:55:a6:20:2d:74:d6:34:bc:41:a3:50: - 5f:01:27:56:b4:be:27:75:06:af:12:2e:75:98:8d:fc:02:31: - 00:8b:f5:77:6c:d4:c8:65:aa:e0:0b:2c:ee:14:9d:27:37:a4: - f9:53:a5:51:e4:29:83:d7:f8:90:31:5b:42:9f:0a:f5:fe:ae: - 00:68:e7:8c:49:0f:b6:6f:5b:5b:15:f2:e7 ------BEGIN CERTIFICATE----- -MIICGzCCAaGgAwIBAgIQQdKd0XLq7qeAwSxs6S+HUjAKBggqhkjOPQQDAzBPMQsw -CQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJuZXQgU2VjdXJpdHkgUmVzZWFyY2gg -R3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBYMjAeFw0yMDA5MDQwMDAwMDBaFw00 -MDA5MTcxNjAwMDBaME8xCzAJBgNVBAYTAlVTMSkwJwYDVQQKEyBJbnRlcm5ldCBT -ZWN1cml0eSBSZXNlYXJjaCBHcm91cDEVMBMGA1UEAxMMSVNSRyBSb290IFgyMHYw -EAYHKoZIzj0CAQYFK4EEACIDYgAEzZvVn4CDCuwJSvMWSj5cz3es3mcFDR0HttwW -+1qLFNvicWDEukWVEYmO6gbf9yoWHKS5xcUy4APgHoIYOIvXRdgKam7mAHf7AlF9 -ItgKbppbd9/w+kHsOdx1ymgHDB/qo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0T -AQH/BAUwAwEB/zAdBgNVHQ4EFgQUfEKWrt5LSDv6kviejM9ti6lyN5UwCgYIKoZI -zj0EAwMDaAAwZQIwe3lORlCEwkSHRhtFcP9Ymd70/aTSVaYgLXTWNLxBo1BfASdW -tL4ndQavEi51mI38AjEAi/V3bNTIZargCyzuFJ0nN6T5U6VR5CmD1/iQMVtCnwr1 -/q4AaOeMSQ+2b1tbFfLn ------END CERTIFICATE----- diff --git a/certs/GTS CA 1C3.pem b/certs/GTS CA 1C3.pem deleted file mode 100644 index a8432d2..0000000 --- a/certs/GTS CA 1C3.pem +++ /dev/null @@ -1,242 +0,0 @@ -Certificate: - Data: - Version: 3 (0x2) - Serial Number: - 02:03:bc:53:59:6b:34:c7:18:f5:01:50:66 - Signature Algorithm: sha256WithRSAEncryption - Issuer: C = US, O = Google Trust Services LLC, CN = GTS Root R1 - Validity - Not Before: Aug 13 00:00:42 2020 GMT - Not After : Sep 30 00:00:42 2027 GMT - Subject: C = US, O = Google Trust Services LLC, CN = GTS CA 1C3 - Subject Public Key Info: - Public Key Algorithm: rsaEncryption - RSA Public-Key: (2048 bit) - Modulus: - 00:f5:88:df:e7:62:8c:1e:37:f8:37:42:90:7f:6c: - 87:d0:fb:65:82:25:fd:e8:cb:6b:a4:ff:6d:e9:5a: - 23:e2:99:f6:1c:e9:92:03:99:13:7c:09:0a:8a:fa: - 42:d6:5e:56:24:aa:7a:33:84:1f:d1:e9:69:bb:b9: - 74:ec:57:4c:66:68:93:77:37:55:53:fe:39:10:4d: - b7:34:bb:5f:25:77:37:3b:17:94:ea:3c:e5:9d:d5: - bc:c3:b4:43:eb:2e:a7:47:ef:b0:44:11:63:d8:b4: - 41:85:dd:41:30:48:93:1b:bf:b7:f6:e0:45:02:21: - e0:96:42:17:cf:d9:2b:65:56:34:07:26:04:0d:a8: - fd:7d:ca:2e:ef:ea:48:7c:37:4d:3f:00:9f:83:df: - ef:75:84:2e:79:57:5c:fc:57:6e:1a:96:ff:fc:8c: - 9a:a6:99:be:25:d9:7f:96:2c:06:f7:11:2a:02:80: - 80:eb:63:18:3c:50:49:87:e5:8a:ca:5f:19:2b:59: - 96:81:00:a0:fb:51:db:ca:77:0b:0b:c9:96:4f:ef: - 70:49:c7:5c:6d:20:fd:99:b4:b4:e2:ca:2e:77:fd: - 2d:dc:0b:b6:6b:13:0c:8c:19:2b:17:96:98:b9:f0: - 8b:f6:a0:27:bb:b6:e3:8d:51:8f:bd:ae:c7:9b:b1: - 89:9d - Exponent: 65537 (0x10001) - X509v3 extensions: - X509v3 Key Usage: critical - Digital Signature, Certificate Sign, CRL Sign - X509v3 Extended Key Usage: - TLS Web Server Authentication, TLS Web Client Authentication - X509v3 Basic Constraints: critical - CA:TRUE, pathlen:0 - X509v3 Subject Key Identifier: - 8A:74:7F:AF:85:CD:EE:95:CD:3D:9C:D0:E2:46:14:F3:71:35:1D:27 - X509v3 Authority Key Identifier: - keyid:E4:AF:2B:26:71:1A:2B:48:27:85:2F:52:66:2C:EF:F0:89:13:71:3E - - Authority Information Access: - OCSP - URI:http://ocsp.pki.goog/gtsr1 - CA Issuers - URI:http://pki.goog/repo/certs/gtsr1.der - - X509v3 CRL Distribution Points: - - Full Name: - URI:http://crl.pki.goog/gtsr1/gtsr1.crl - - X509v3 Certificate Policies: - Policy: 1.3.6.1.4.1.11129.2.5.3 - CPS: https://pki.goog/repository/ - Policy: 2.23.140.1.2.1 - Policy: 2.23.140.1.2.2 - - Signature Algorithm: sha256WithRSAEncryption - 89:7d:ac:20:5c:0c:3c:be:9a:a8:57:95:1b:b4:ae:fa:ab:a5: - 72:71:b4:36:95:fd:df:40:11:03:4c:c2:46:14:bb:14:24:ab: - f0:50:71:22:db:ad:c4:6e:7f:cf:f1:6a:6f:c8:83:1b:d8:ce: - 89:5f:87:6c:87:b8:a9:0c:a3:9b:a1:62:94:93:95:df:5b:ae: - 66:19:0b:02:96:9e:fc:b5:e7:10:69:3e:7a:cb:46:49:5f:46: - e1:41:b1:d7:98:4d:65:34:00:80:1a:3f:4f:9f:6c:7f:49:00: - 81:53:41:a4:92:21:82:82:1a:f1:a3:44:5b:2a:50:12:13:4d: - c1:53:36:f3:42:08:af:54:fa:8e:77:53:1b:64:38:27:17:09: - bd:58:c9:1b:7c:39:2d:5b:f3:ce:d4:ed:97:db:14:03:bf:09: - 53:24:1f:c2:0c:04:79:98:26:f2:61:f1:53:52:fd:42:8c:1b: - 66:2b:3f:15:a1:bb:ff:f6:9b:e3:81:9a:01:06:71:89:35:28: - 24:dd:e1:bd:eb:19:2d:e1:48:cb:3d:59:83:51:b4:74:c6:9d: - 7c:c6:b1:86:5b:af:cc:34:c4:d3:cc:d4:81:11:95:00:a1:f4: - 12:22:01:fa:b4:83:71:af:8c:b7:8c:73:24:ac:37:53:c2:00: - 90:3f:11:fe:5c:ed:36:94:10:3b:bd:29:ae:e2:c7:3a:62:3b: - 6c:63:d9:80:bf:59:71:ac:63:27:b9:4c:17:a0:da:f6:73:15: - bf:2a:de:8f:f3:a5:6c:32:81:33:03:d0:86:51:71:99:34:ba: - 93:8d:5d:b5:51:58:f7:b2:93:e8:01:f6:59:be:71:9b:fd:4d: - 28:ce:cf:6d:c7:16:dc:f7:d1:d6:46:9b:a7:ca:6b:e9:77:0f: - fd:a0:b6:1b:23:83:1d:10:1a:d9:09:00:84:e0:44:d3:a2:75: - 23:b3:34:86:f6:20:b0:a4:5e:10:1d:e0:52:46:00:9d:b1:0f: - 1f:21:70:51:f5:9a:dd:06:fc:55:f4:2b:0e:33:77:c3:4b:42: - c2:f1:77:13:fc:73:80:94:eb:1f:bb:37:3f:ce:02:2a:66:b0: - 73:1d:32:a5:32:6c:32:b0:8e:e0:c4:23:ff:5b:7d:4d:65:70: - ac:2b:9b:3d:ce:db:e0:6d:8e:32:80:be:96:9f:92:63:bc:97: - bb:5d:b9:f4:e1:71:5e:2a:e4:ef:03:22:b1:8a:65:3a:8f:c0: - 93:65:d4:85:cd:0f:0f:5b:83:59:16:47:16:2d:9c:24:3a:c8: - 80:a6:26:14:85:9b:f6:37:9b:ac:6f:f9:c5:c3:06:51:f3:e2: - 7f:c5:b1:10:ba:51:f4:dd ------BEGIN CERTIFICATE----- -MIIFljCCA36gAwIBAgINAgO8U1lrNMcY9QFQZjANBgkqhkiG9w0BAQsFADBHMQsw -CQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEU -MBIGA1UEAxMLR1RTIFJvb3QgUjEwHhcNMjAwODEzMDAwMDQyWhcNMjcwOTMwMDAw -MDQyWjBGMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZp -Y2VzIExMQzETMBEGA1UEAxMKR1RTIENBIDFDMzCCASIwDQYJKoZIhvcNAQEBBQAD -ggEPADCCAQoCggEBAPWI3+dijB43+DdCkH9sh9D7ZYIl/ejLa6T/belaI+KZ9hzp -kgOZE3wJCor6QtZeViSqejOEH9Hpabu5dOxXTGZok3c3VVP+ORBNtzS7XyV3NzsX -lOo85Z3VvMO0Q+sup0fvsEQRY9i0QYXdQTBIkxu/t/bgRQIh4JZCF8/ZK2VWNAcm -BA2o/X3KLu/qSHw3TT8An4Pf73WELnlXXPxXbhqW//yMmqaZviXZf5YsBvcRKgKA -gOtjGDxQSYflispfGStZloEAoPtR28p3CwvJlk/vcEnHXG0g/Zm0tOLKLnf9LdwL -tmsTDIwZKxeWmLnwi/agJ7u2441Rj72ux5uxiZ0CAwEAAaOCAYAwggF8MA4GA1Ud -DwEB/wQEAwIBhjAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwEgYDVR0T -AQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQUinR/r4XN7pXNPZzQ4kYU83E1HScwHwYD -VR0jBBgwFoAU5K8rJnEaK0gnhS9SZizv8IkTcT4waAYIKwYBBQUHAQEEXDBaMCYG -CCsGAQUFBzABhhpodHRwOi8vb2NzcC5wa2kuZ29vZy9ndHNyMTAwBggrBgEFBQcw -AoYkaHR0cDovL3BraS5nb29nL3JlcG8vY2VydHMvZ3RzcjEuZGVyMDQGA1UdHwQt -MCswKaAnoCWGI2h0dHA6Ly9jcmwucGtpLmdvb2cvZ3RzcjEvZ3RzcjEuY3JsMFcG -A1UdIARQME4wOAYKKwYBBAHWeQIFAzAqMCgGCCsGAQUFBwIBFhxodHRwczovL3Br -aS5nb29nL3JlcG9zaXRvcnkvMAgGBmeBDAECATAIBgZngQwBAgIwDQYJKoZIhvcN -AQELBQADggIBAIl9rCBcDDy+mqhXlRu0rvqrpXJxtDaV/d9AEQNMwkYUuxQkq/BQ -cSLbrcRuf8/xam/IgxvYzolfh2yHuKkMo5uhYpSTld9brmYZCwKWnvy15xBpPnrL -RklfRuFBsdeYTWU0AIAaP0+fbH9JAIFTQaSSIYKCGvGjRFsqUBITTcFTNvNCCK9U -+o53UxtkOCcXCb1YyRt8OS1b887U7ZfbFAO/CVMkH8IMBHmYJvJh8VNS/UKMG2Yr -PxWhu//2m+OBmgEGcYk1KCTd4b3rGS3hSMs9WYNRtHTGnXzGsYZbr8w0xNPM1IER -lQCh9BIiAfq0g3GvjLeMcySsN1PCAJA/Ef5c7TaUEDu9Ka7ixzpiO2xj2YC/WXGs -Yye5TBeg2vZzFb8q3o/zpWwygTMD0IZRcZk0upONXbVRWPeyk+gB9lm+cZv9TSjO -z23HFtz30dZGm6fKa+l3D/2gthsjgx0QGtkJAITgRNOidSOzNIb2ILCkXhAd4FJG -AJ2xDx8hcFH1mt0G/FX0Kw4zd8NLQsLxdxP8c4CU6x+7Nz/OAipmsHMdMqUybDKw -juDEI/9bfU1lcKwrmz3O2+BtjjKAvpafkmO8l7tdufThcV4q5O8DIrGKZTqPwJNl -1IXNDw9bg1kWRxYtnCQ6yICmJhSFm/Y3m6xv+cXDBlHz4n/FsRC6UfTd ------END CERTIFICATE----- -Certificate: - Data: - Version: 3 (0x2) - Serial Number: - 6e:47:a9:c5:4b:47:0c:0d:ec:33:d0:89:b9:1c:f4:e1 - Signature Algorithm: sha384WithRSAEncryption - Issuer: C = US, O = Google Trust Services LLC, CN = GTS Root R1 - Validity - Not Before: Jun 22 00:00:00 2016 GMT - Not After : Jun 22 00:00:00 2036 GMT - Subject: C = US, O = Google Trust Services LLC, CN = GTS Root R1 - Subject Public Key Info: - Public Key Algorithm: rsaEncryption - RSA Public-Key: (4096 bit) - Modulus: - 00:b6:11:02:8b:1e:e3:a1:77:9b:3b:dc:bf:94:3e: - b7:95:a7:40:3c:a1:fd:82:f9:7d:32:06:82:71:f6: - f6:8c:7f:fb:e8:db:bc:6a:2e:97:97:a3:8c:4b:f9: - 2b:f6:b1:f9:ce:84:1d:b1:f9:c5:97:de:ef:b9:f2: - a3:e9:bc:12:89:5e:a7:aa:52:ab:f8:23:27:cb:a4: - b1:9c:63:db:d7:99:7e:f0:0a:5e:eb:68:a6:f4:c6: - 5a:47:0d:4d:10:33:e3:4e:b1:13:a3:c8:18:6c:4b: - ec:fc:09:90:df:9d:64:29:25:23:07:a1:b4:d2:3d: - 2e:60:e0:cf:d2:09:87:bb:cd:48:f0:4d:c2:c2:7a: - 88:8a:bb:ba:cf:59:19:d6:af:8f:b0:07:b0:9e:31: - f1:82:c1:c0:df:2e:a6:6d:6c:19:0e:b5:d8:7e:26: - 1a:45:03:3d:b0:79:a4:94:28:ad:0f:7f:26:e5:a8: - 08:fe:96:e8:3c:68:94:53:ee:83:3a:88:2b:15:96: - 09:b2:e0:7a:8c:2e:75:d6:9c:eb:a7:56:64:8f:96: - 4f:68:ae:3d:97:c2:84:8f:c0:bc:40:c0:0b:5c:bd: - f6:87:b3:35:6c:ac:18:50:7f:84:e0:4c:cd:92:d3: - 20:e9:33:bc:52:99:af:32:b5:29:b3:25:2a:b4:48: - f9:72:e1:ca:64:f7:e6:82:10:8d:e8:9d:c2:8a:88: - fa:38:66:8a:fc:63:f9:01:f9:78:fd:7b:5c:77:fa: - 76:87:fa:ec:df:b1:0e:79:95:57:b4:bd:26:ef:d6: - 01:d1:eb:16:0a:bb:8e:0b:b5:c5:c5:8a:55:ab:d3: - ac:ea:91:4b:29:cc:19:a4:32:25:4e:2a:f1:65:44: - d0:02:ce:aa:ce:49:b4:ea:9f:7c:83:b0:40:7b:e7: - 43:ab:a7:6c:a3:8f:7d:89:81:fa:4c:a5:ff:d5:8e: - c3:ce:4b:e0:b5:d8:b3:8e:45:cf:76:c0:ed:40:2b: - fd:53:0f:b0:a7:d5:3b:0d:b1:8a:a2:03:de:31:ad: - cc:77:ea:6f:7b:3e:d6:df:91:22:12:e6:be:fa:d8: - 32:fc:10:63:14:51:72:de:5d:d6:16:93:bd:29:68: - 33:ef:3a:66:ec:07:8a:26:df:13:d7:57:65:78:27: - de:5e:49:14:00:a2:00:7f:9a:a8:21:b6:a9:b1:95: - b0:a5:b9:0d:16:11:da:c7:6c:48:3c:40:e0:7e:0d: - 5a:cd:56:3c:d1:97:05:b9:cb:4b:ed:39:4b:9c:c4: - 3f:d2:55:13:6e:24:b0:d6:71:fa:f4:c1:ba:cc:ed: - 1b:f5:fe:81:41:d8:00:98:3d:3a:c8:ae:7a:98:37: - 18:05:95 - Exponent: 65537 (0x10001) - X509v3 extensions: - X509v3 Key Usage: critical - Certificate Sign, CRL Sign - X509v3 Basic Constraints: critical - CA:TRUE - X509v3 Subject Key Identifier: - E4:AF:2B:26:71:1A:2B:48:27:85:2F:52:66:2C:EF:F0:89:13:71:3E - Signature Algorithm: sha384WithRSAEncryption - 38:96:0a:ee:3d:b4:96:1e:5f:ef:9d:9c:0b:33:9f:2b:e0:ca: - fd:d2:8e:0a:1f:41:74:a5:7c:aa:84:d4:e5:f2:1e:e6:37:52: - 32:9c:0b:d1:61:1d:bf:28:c1:b6:44:29:35:75:77:98:b2:7c: - d9:bd:74:ac:8a:68:e3:a9:31:09:29:01:60:73:e3:47:7c:53: - a8:90:4a:27:ef:4b:d7:9f:93:e7:82:36:ce:9a:68:0c:82:e7: - cf:d4:10:16:6f:5f:0e:99:5c:f6:1f:71:7d:ef:ef:7b:2f:7e: - ea:36:d6:97:70:0b:15:ee:d7:5c:56:6a:33:a5:e3:49:38:0c: - b8:7d:fb:8d:85:a4:b1:59:5e:f4:6a:e1:dd:a1:f6:64:44:ae: - e6:51:83:21:66:c6:11:3e:f3:ce:47:ee:9c:28:1f:25:da:ff: - ac:66:95:dd:35:0f:5c:ef:20:2c:62:fd:91:ba:a9:cc:fc:5a: - 9c:93:81:83:29:97:4a:7c:5a:72:b4:39:d0:b7:77:cb:79:fd: - 69:3a:92:37:ed:6e:38:65:46:7e:e9:60:bd:79:88:97:5f:38: - 12:f4:ee:af:5b:82:c8:86:d5:e1:99:6d:8c:04:f2:76:ba:49: - f6:6e:e9:6d:1e:5f:a0:ef:27:82:76:40:f8:a6:d3:58:5c:0f: - 2c:42:da:42:c6:7b:88:34:c7:c1:d8:45:9b:c1:3e:c5:61:1d: - d9:63:50:49:f6:34:85:6a:e0:18:c5:6e:47:ab:41:42:29:9b: - f6:60:0d:d2:31:d3:63:98:23:93:5a:00:81:48:b4:ef:cd:8a: - cd:c9:cf:99:ee:d9:9e:aa:36:e1:68:4b:71:49:14:36:28:3a: - 3d:1d:ce:9a:8f:25:e6:80:71:61:2b:b5:7b:cc:f9:25:16:81: - e1:31:5f:a1:a3:7e:16:a4:9c:16:6a:97:18:bd:76:72:a5:0b: - 9e:1d:36:e6:2f:a1:2f:be:70:91:0f:a8:e6:da:f8:c4:92:40: - 6c:25:7e:7b:b3:09:dc:b2:17:ad:80:44:f0:68:a5:8f:94:75: - ff:74:5a:e8:a8:02:7c:0c:09:e2:a9:4b:0b:a0:85:0b:62:b9: - ef:a1:31:92:fb:ef:f6:51:04:89:6c:e8:a9:74:a1:bb:17:b3: - b5:fd:49:0f:7c:3c:ec:83:18:20:43:4e:d5:93:ba:b4:34:b1: - 1f:16:36:1f:0c:e6:64:39:16:4c:dc:e0:fe:1d:c8:a9:62:3d: - 40:ea:ca:c5:34:02:b4:ae:89:88:33:35:dc:2c:13:73:d8:27: - f1:d0:72:ee:75:3b:22:de:98:68:66:5b:f1:c6:63:47:55:1c: - ba:a5:08:51:75:a6:48:25 ------BEGIN CERTIFICATE----- -MIIFWjCCA0KgAwIBAgIQbkepxUtHDA3sM9CJuRz04TANBgkqhkiG9w0BAQwFADBH -MQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExM -QzEUMBIGA1UEAxMLR1RTIFJvb3QgUjEwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIy -MDAwMDAwWjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNl -cnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjEwggIiMA0GCSqGSIb3DQEB -AQUAA4ICDwAwggIKAoICAQC2EQKLHuOhd5s73L+UPreVp0A8of2C+X0yBoJx9vaM -f/vo27xqLpeXo4xL+Sv2sfnOhB2x+cWX3u+58qPpvBKJXqeqUqv4IyfLpLGcY9vX -mX7wCl7raKb0xlpHDU0QM+NOsROjyBhsS+z8CZDfnWQpJSMHobTSPS5g4M/SCYe7 -zUjwTcLCeoiKu7rPWRnWr4+wB7CeMfGCwcDfLqZtbBkOtdh+JhpFAz2weaSUKK0P -fyblqAj+lug8aJRT7oM6iCsVlgmy4HqMLnXWnOunVmSPlk9orj2XwoSPwLxAwAtc -vfaHszVsrBhQf4TgTM2S0yDpM7xSma8ytSmzJSq0SPly4cpk9+aCEI3oncKKiPo4 -Zor8Y/kB+Xj9e1x3+naH+uzfsQ55lVe0vSbv1gHR6xYKu44LtcXFilWr06zqkUsp -zBmkMiVOKvFlRNACzqrOSbTqn3yDsEB750Orp2yjj32JgfpMpf/VjsPOS+C12LOO -Rc92wO1AK/1TD7Cn1TsNsYqiA94xrcx36m97PtbfkSIS5r762DL8EGMUUXLeXdYW -k70paDPvOmbsB4om3xPXV2V4J95eSRQAogB/mqghtqmxlbCluQ0WEdrHbEg8QOB+ -DVrNVjzRlwW5y0vtOUucxD/SVRNuJLDWcfr0wbrM7Rv1/oFB2ACYPTrIrnqYNxgF -lQIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNV -HQ4EFgQU5K8rJnEaK0gnhS9SZizv8IkTcT4wDQYJKoZIhvcNAQEMBQADggIBADiW -Cu49tJYeX++dnAsznyvgyv3SjgofQXSlfKqE1OXyHuY3UjKcC9FhHb8owbZEKTV1 -d5iyfNm9dKyKaOOpMQkpAWBz40d8U6iQSifvS9efk+eCNs6aaAyC58/UEBZvXw6Z -XPYfcX3v73svfuo21pdwCxXu11xWajOl40k4DLh9+42FpLFZXvRq4d2h9mREruZR -gyFmxhE+885H7pwoHyXa/6xmld01D1zvICxi/ZG6qcz8WpyTgYMpl0p8WnK0OdC3 -d8t5/Wk6kjftbjhlRn7pYL15iJdfOBL07q9bgsiG1eGZbYwE8na6SfZu6W0eX6Dv -J4J2QPim01hcDyxC2kLGe4g0x8HYRZvBPsVhHdljUEn2NIVq4BjFbkerQUIpm/Zg -DdIx02OYI5NaAIFItO/Nis3Jz5nu2Z6qNuFoS3FJFDYoOj0dzpqPJeaAcWErtXvM -+SUWgeExX6GjfhaknBZqlxi9dnKlC54dNuYvoS++cJEPqOba+MSSQGwlfnuzCdyy -F62ARPBopY+Udf90WuioAnwMCeKpSwughQtiue+hMZL77/ZRBIls6Kl0obsXs7X9 -SQ98POyDGCBDTtWTurQ0sR8WNh8M5mQ5Fkzc4P4dyKliPUDqysU0ArSuiYgzNdws -E3PYJ/HQcu51OyLemGhmW/HGY0dVHLqlCFF1pkgl ------END CERTIFICATE----- diff --git a/certs/GTS-Root-R1.pem b/certs/GTS-Root-R1.pem new file mode 100644 index 0000000..a6095d2 --- /dev/null +++ b/certs/GTS-Root-R1.pem @@ -0,0 +1,38 @@ +# Issuer: CN=GTS Root R1 O=Google Trust Services LLC +# Subject: CN=GTS Root R1 O=Google Trust Services LLC +# Label: "GTS Root R1" +# Serial: 159662320309726417404178440727 +# MD5 Fingerprint: 05:fe:d0:bf:71:a8:a3:76:63:da:01:e0:d8:52:dc:40 +# SHA1 Fingerprint: e5:8c:1c:c4:91:3b:38:63:4b:e9:10:6e:e3:ad:8e:6b:9d:d9:81:4a +# SHA256 Fingerprint: d9:47:43:2a:bd:e7:b7:fa:90:fc:2e:6b:59:10:1b:12:80:e0:e1:c7:e4:e4:0f:a3:c6:88:7f:ff:57:a7:f4:cf +-----BEGIN CERTIFICATE----- +MIIFVzCCAz+gAwIBAgINAgPlk28xsBNJiGuiFzANBgkqhkiG9w0BAQwFADBHMQsw +CQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEU +MBIGA1UEAxMLR1RTIFJvb3QgUjEwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAw +MDAwWjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZp +Y2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjEwggIiMA0GCSqGSIb3DQEBAQUA +A4ICDwAwggIKAoICAQC2EQKLHuOhd5s73L+UPreVp0A8of2C+X0yBoJx9vaMf/vo +27xqLpeXo4xL+Sv2sfnOhB2x+cWX3u+58qPpvBKJXqeqUqv4IyfLpLGcY9vXmX7w +Cl7raKb0xlpHDU0QM+NOsROjyBhsS+z8CZDfnWQpJSMHobTSPS5g4M/SCYe7zUjw +TcLCeoiKu7rPWRnWr4+wB7CeMfGCwcDfLqZtbBkOtdh+JhpFAz2weaSUKK0Pfybl +qAj+lug8aJRT7oM6iCsVlgmy4HqMLnXWnOunVmSPlk9orj2XwoSPwLxAwAtcvfaH +szVsrBhQf4TgTM2S0yDpM7xSma8ytSmzJSq0SPly4cpk9+aCEI3oncKKiPo4Zor8 +Y/kB+Xj9e1x3+naH+uzfsQ55lVe0vSbv1gHR6xYKu44LtcXFilWr06zqkUspzBmk +MiVOKvFlRNACzqrOSbTqn3yDsEB750Orp2yjj32JgfpMpf/VjsPOS+C12LOORc92 +wO1AK/1TD7Cn1TsNsYqiA94xrcx36m97PtbfkSIS5r762DL8EGMUUXLeXdYWk70p +aDPvOmbsB4om3xPXV2V4J95eSRQAogB/mqghtqmxlbCluQ0WEdrHbEg8QOB+DVrN +VjzRlwW5y0vtOUucxD/SVRNuJLDWcfr0wbrM7Rv1/oFB2ACYPTrIrnqYNxgFlQID +AQABo0IwQDAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4E +FgQU5K8rJnEaK0gnhS9SZizv8IkTcT4wDQYJKoZIhvcNAQEMBQADggIBAJ+qQibb +C5u+/x6Wki4+omVKapi6Ist9wTrYggoGxval3sBOh2Z5ofmmWJyq+bXmYOfg6LEe +QkEzCzc9zolwFcq1JKjPa7XSQCGYzyI0zzvFIoTgxQ6KfF2I5DUkzps+GlQebtuy +h6f88/qBVRRiClmpIgUxPoLW7ttXNLwzldMXG+gnoot7TiYaelpkttGsN/H9oPM4 +7HLwEXWdyzRSjeZ2axfG34arJ45JK3VmgRAhpuo+9K4l/3wV3s6MJT/KYnAK9y8J +ZgfIPxz88NtFMN9iiMG1D53Dn0reWVlHxYciNuaCp+0KueIHoI17eko8cdLiA6Ef +MgfdG+RCzgwARWGAtQsgWSl4vflVy2PFPEz0tv/bal8xa5meLMFrUKTX5hgUvYU/ +Z6tGn6D/Qqc6f1zLXbBwHSs09dR2CQzreExZBfMzQsNhFRAbd03OIozUhfJFfbdT +6u9AWpQKXCBfTkBdYiJ23//OYb2MI3jSNwLgjt7RETeJ9r/tSQdirpLsQBqvFAnZ +0E6yove+7u7Y/9waLd64NnHi/Hm3lCXRSHNboTXns5lndcEZOitHTtNCjv0xyBZm +2tIMPNuzjsmhDYAPexZ3FL//2wmUspO8IFgV6dtxQ/PeEMMA3KgqlbbC1j+Qa3bb +bP6MvPJwNQzcmRk13NfIRmPVNnGuV/u3gm3c +-----END CERTIFICATE----- diff --git a/certs/GTS-Root-R4.pem b/certs/GTS-Root-R4.pem new file mode 100644 index 0000000..16a1c36 --- /dev/null +++ b/certs/GTS-Root-R4.pem @@ -0,0 +1,20 @@ +# Issuer: CN=GTS Root R4 O=Google Trust Services LLC +# Subject: CN=GTS Root R4 O=Google Trust Services LLC +# Label: "GTS Root R4" +# Serial: 159662532700760215368942768210 +# MD5 Fingerprint: 43:96:83:77:19:4d:76:b3:9d:65:52:e4:1d:22:a5:e8 +# SHA1 Fingerprint: 77:d3:03:67:b5:e0:0c:15:f6:0c:38:61:df:7c:e1:3b:92:46:4d:47 +# SHA256 Fingerprint: 34:9d:fa:40:58:c5:e2:63:12:3b:39:8a:e7:95:57:3c:4e:13:13:c8:3f:e6:8f:93:55:6c:d5:e8:03:1b:3c:7d +-----BEGIN CERTIFICATE----- +MIICCTCCAY6gAwIBAgINAgPlwGjvYxqccpBQUjAKBggqhkjOPQQDAzBHMQswCQYD +VQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIG +A1UEAxMLR1RTIFJvb3QgUjQwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAwMDAw +WjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2Vz +IExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjQwdjAQBgcqhkjOPQIBBgUrgQQAIgNi +AATzdHOnaItgrkO4NcWBMHtLSZ37wWHO5t5GvWvVYRg1rkDdc/eJkTBa6zzuhXyi +QHY7qca4R9gq55KRanPpsXI5nymfopjTX15YhmUPoYRlBtHci8nHc8iMai/lxKvR +HYqjQjBAMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQW +BBSATNbrdP9JNqPV2Py1PsVq8JQdjDAKBggqhkjOPQQDAwNpADBmAjEA6ED/g94D +9J+uHXqnLrmvT/aDHQ4thQEd0dlq7A/Cr8deVl5c1RxYIigL9zC2L7F8AjEA8GE8 +p/SgguMh1YQdc4acLa/KNJvxn7kjNuK8YAOdgLOaVsjh4rsUecrNIdSUtUlD +-----END CERTIFICATE----- diff --git a/certs/GlobalSign Atlas R3 DV TLS CA 2022 Q3.pem b/certs/GlobalSign Atlas R3 DV TLS CA 2022 Q3.pem deleted file mode 100644 index b514c11..0000000 --- a/certs/GlobalSign Atlas R3 DV TLS CA 2022 Q3.pem +++ /dev/null @@ -1,177 +0,0 @@ -Certificate: - Data: - Version: 3 (0x2) - Serial Number: - 7c:2a:0c:21:3f:c6:55:53:45:c9:1f:19:1f:b8:4e:fa - Signature Algorithm: sha256WithRSAEncryption - Issuer: OU = GlobalSign Root CA - R3, O = GlobalSign, CN = GlobalSign - Validity - Not Before: Apr 20 12:00:00 2022 GMT - Not After : Apr 20 00:00:00 2025 GMT - Subject: C = BE, O = GlobalSign nv-sa, CN = GlobalSign Atlas R3 DV TLS CA 2022 Q3 - Subject Public Key Info: - Public Key Algorithm: rsaEncryption - Public-Key: (2048 bit) - Modulus: - 00:b8:a8:7a:66:3c:4e:66:9c:ce:37:a5:54:35:4d: - 36:c7:99:d3:a8:27:36:f2:2f:c6:d5:18:3e:e9:09: - dd:05:d6:d7:2c:34:32:7c:08:63:49:d1:10:37:e5: - 78:5d:11:62:ce:6d:fb:2f:3f:37:94:db:8f:7b:30: - e9:5e:2c:d9:55:3f:b2:db:b9:a0:b5:60:37:8b:a4: - 06:32:35:50:a4:09:af:0a:45:ff:a8:1f:9b:65:8e: - dd:4a:e0:40:a1:e3:63:37:58:90:dd:75:3b:fc:0e: - 1c:82:40:98:bd:70:b1:c1:48:14:14:3c:04:4b:69: - dd:d4:9c:01:a6:e9:21:e3:82:0a:fe:e4:aa:bf:34: - a0:8c:cb:c9:79:6e:3e:5c:6a:52:9e:c4:ed:2b:c5: - 69:fe:50:3c:93:9d:b5:ff:2d:28:a8:6c:06:6c:9d: - c5:af:b2:59:fb:59:77:0d:74:7a:88:84:a4:d4:1d: - d4:ba:20:06:cc:b5:1e:48:4e:74:21:15:86:75:c0: - cc:5a:d1:05:cf:57:16:7a:13:17:ec:c2:4a:ae:d5: - 1e:72:aa:22:5a:8c:9c:82:32:c4:10:e6:42:6e:21: - 86:68:7c:80:23:30:35:d3:bd:b0:5e:0a:29:2b:f0: - 14:b1:18:37:d9:59:25:c3:e7:38:d9:e9:d4:2d:36: - 35:65 - Exponent: 65537 (0x10001) - X509v3 extensions: - X509v3 Key Usage: critical - Digital Signature, Certificate Sign, CRL Sign - X509v3 Extended Key Usage: - TLS Web Server Authentication, TLS Web Client Authentication - X509v3 Basic Constraints: critical - CA:TRUE, pathlen:0 - X509v3 Subject Key Identifier: - FA:91:39:63:9A:FB:AD:10:24:E5:BE:B5:B9:DA:AB:D9:C4:46:69:AB - X509v3 Authority Key Identifier: - 8F:F0:4B:7F:A8:2E:45:24:AE:4D:50:FA:63:9A:8B:DE:E2:DD:1B:BC - Authority Information Access: - OCSP - URI:http://ocsp2.globalsign.com/rootr3 - CA Issuers - URI:http://secure.globalsign.com/cacert/root-r3.crt - X509v3 CRL Distribution Points: - Full Name: - URI:http://crl.globalsign.com/root-r3.crl - X509v3 Certificate Policies: - Policy: 2.23.140.1.2.1 - Policy: 1.3.6.1.4.1.4146.10.1.3 - Signature Algorithm: sha256WithRSAEncryption - Signature Value: - 14:33:2c:79:e5:3f:82:c6:70:3f:da:59:38:a7:bb:a2:76:ac: - 61:18:05:68:57:d9:0d:fb:8a:46:bc:f1:a8:e8:0c:70:02:1d: - c6:2f:97:ed:36:3e:9e:52:86:2f:5c:62:d8:d5:47:43:9a:73: - d1:2b:25:87:9f:44:b4:14:eb:26:bc:21:47:74:20:bd:9f:a4: - bf:b3:80:1d:4d:35:7d:cd:b9:b5:da:55:f2:90:50:c8:b2:17: - 4e:0e:b4:61:88:29:5f:44:5d:03:7f:57:91:81:d0:eb:30:ae: - d5:2a:ec:82:20:ce:4e:d2:b0:8b:95:02:61:73:d8:69:34:f4: - ad:63:0e:5c:e4:20:1f:a9:7d:ed:8e:e5:1c:04:bb:22:9f:c7: - a9:22:ca:99:3d:02:a7:67:e8:06:2d:fa:04:6b:bb:49:d2:6c: - 99:57:63:6c:2d:c2:61:78:e1:20:b1:fb:f6:bf:e1:82:39:39: - 3c:7b:ef:7d:1a:95:4a:b2:72:da:55:90:ae:ed:dd:e2:70:90: - 7c:1a:ee:b5:32:5a:5d:cf:d6:fa:45:f2:9e:01:0c:31:2f:89: - 84:fe:31:60:0f:fd:ee:a6:5b:84:d5:c7:18:e6:a4:f9:40:30: - 29:18:1e:fe:fc:41:b5:b9:29:05:75:8b:62:1a:5b:22:2e:bf: - e4:59:6c:b0 ------BEGIN CERTIFICATE----- -MIIEjzCCA3egAwIBAgIQfCoMIT/GVVNFyR8ZH7hO+jANBgkqhkiG9w0BAQsFADBM -MSAwHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSMzETMBEGA1UEChMKR2xv -YmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjAeFw0yMjA0MjAxMjAwMDBaFw0y -NTA0MjAwMDAwMDBaMFgxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWdu -IG52LXNhMS4wLAYDVQQDEyVHbG9iYWxTaWduIEF0bGFzIFIzIERWIFRMUyBDQSAy -MDIyIFEzMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuKh6ZjxOZpzO -N6VUNU02x5nTqCc28i/G1Rg+6QndBdbXLDQyfAhjSdEQN+V4XRFizm37Lz83lNuP -ezDpXizZVT+y27mgtWA3i6QGMjVQpAmvCkX/qB+bZY7dSuBAoeNjN1iQ3XU7/A4c -gkCYvXCxwUgUFDwES2nd1JwBpukh44IK/uSqvzSgjMvJeW4+XGpSnsTtK8Vp/lA8 -k521/y0oqGwGbJ3Fr7JZ+1l3DXR6iISk1B3UuiAGzLUeSE50IRWGdcDMWtEFz1cW -ehMX7MJKrtUecqoiWoycgjLEEOZCbiGGaHyAIzA1072wXgopK/AUsRg32Vklw+c4 -2enULTY1ZQIDAQABo4IBXzCCAVswDgYDVR0PAQH/BAQDAgGGMB0GA1UdJQQWMBQG -CCsGAQUFBwMBBggrBgEFBQcDAjASBgNVHRMBAf8ECDAGAQH/AgEAMB0GA1UdDgQW -BBT6kTljmvutECTlvrW52qvZxEZpqzAfBgNVHSMEGDAWgBSP8Et/qC5FJK5NUPpj -move4t0bvDB7BggrBgEFBQcBAQRvMG0wLgYIKwYBBQUHMAGGImh0dHA6Ly9vY3Nw -Mi5nbG9iYWxzaWduLmNvbS9yb290cjMwOwYIKwYBBQUHMAKGL2h0dHA6Ly9zZWN1 -cmUuZ2xvYmFsc2lnbi5jb20vY2FjZXJ0L3Jvb3QtcjMuY3J0MDYGA1UdHwQvMC0w -K6ApoCeGJWh0dHA6Ly9jcmwuZ2xvYmFsc2lnbi5jb20vcm9vdC1yMy5jcmwwIQYD -VR0gBBowGDAIBgZngQwBAgEwDAYKKwYBBAGgMgoBAzANBgkqhkiG9w0BAQsFAAOC -AQEAFDMseeU/gsZwP9pZOKe7onasYRgFaFfZDfuKRrzxqOgMcAIdxi+X7TY+nlKG -L1xi2NVHQ5pz0Sslh59EtBTrJrwhR3QgvZ+kv7OAHU01fc25tdpV8pBQyLIXTg60 -YYgpX0RdA39XkYHQ6zCu1SrsgiDOTtKwi5UCYXPYaTT0rWMOXOQgH6l97Y7lHAS7 -Ip/HqSLKmT0Cp2foBi36BGu7SdJsmVdjbC3CYXjhILH79r/hgjk5PHvvfRqVSrJy -2lWQru3d4nCQfBrutTJaXc/W+kXyngEMMS+JhP4xYA/97qZbhNXHGOak+UAwKRge -/vxBtbkpBXWLYhpbIi6/5FlssA== ------END CERTIFICATE----- -Certificate: - Data: - Version: 3 (0x2) - Serial Number: - 04:00:00:00:00:01:21:58:53:08:a2 - Signature Algorithm: sha256WithRSAEncryption - Issuer: OU = GlobalSign Root CA - R3, O = GlobalSign, CN = GlobalSign - Validity - Not Before: Mar 18 10:00:00 2009 GMT - Not After : Mar 18 10:00:00 2029 GMT - Subject: OU = GlobalSign Root CA - R3, O = GlobalSign, CN = GlobalSign - Subject Public Key Info: - Public Key Algorithm: rsaEncryption - Public-Key: (2048 bit) - Modulus: - 00:cc:25:76:90:79:06:78:22:16:f5:c0:83:b6:84: - ca:28:9e:fd:05:76:11:c5:ad:88:72:fc:46:02:43: - c7:b2:8a:9d:04:5f:24:cb:2e:4b:e1:60:82:46:e1: - 52:ab:0c:81:47:70:6c:dd:64:d1:eb:f5:2c:a3:0f: - 82:3d:0c:2b:ae:97:d7:b6:14:86:10:79:bb:3b:13: - 80:77:8c:08:e1:49:d2:6a:62:2f:1f:5e:fa:96:68: - df:89:27:95:38:9f:06:d7:3e:c9:cb:26:59:0d:73: - de:b0:c8:e9:26:0e:83:15:c6:ef:5b:8b:d2:04:60: - ca:49:a6:28:f6:69:3b:f6:cb:c8:28:91:e5:9d:8a: - 61:57:37:ac:74:14:dc:74:e0:3a:ee:72:2f:2e:9c: - fb:d0:bb:bf:f5:3d:00:e1:06:33:e8:82:2b:ae:53: - a6:3a:16:73:8c:dd:41:0e:20:3a:c0:b4:a7:a1:e9: - b2:4f:90:2e:32:60:e9:57:cb:b9:04:92:68:68:e5: - 38:26:60:75:b2:9f:77:ff:91:14:ef:ae:20:49:fc: - ad:40:15:48:d1:02:31:61:19:5e:b8:97:ef:ad:77: - b7:64:9a:7a:bf:5f:c1:13:ef:9b:62:fb:0d:6c:e0: - 54:69:16:a9:03:da:6e:e9:83:93:71:76:c6:69:85: - 82:17 - Exponent: 65537 (0x10001) - X509v3 extensions: - X509v3 Key Usage: critical - Certificate Sign, CRL Sign - X509v3 Basic Constraints: critical - CA:TRUE - X509v3 Subject Key Identifier: - 8F:F0:4B:7F:A8:2E:45:24:AE:4D:50:FA:63:9A:8B:DE:E2:DD:1B:BC - Signature Algorithm: sha256WithRSAEncryption - Signature Value: - 4b:40:db:c0:50:aa:fe:c8:0c:ef:f7:96:54:45:49:bb:96:00: - 09:41:ac:b3:13:86:86:28:07:33:ca:6b:e6:74:b9:ba:00:2d: - ae:a4:0a:d3:f5:f1:f1:0f:8a:bf:73:67:4a:83:c7:44:7b:78: - e0:af:6e:6c:6f:03:29:8e:33:39:45:c3:8e:e4:b9:57:6c:aa: - fc:12:96:ec:53:c6:2d:e4:24:6c:b9:94:63:fb:dc:53:68:67: - 56:3e:83:b8:cf:35:21:c3:c9:68:fe:ce:da:c2:53:aa:cc:90: - 8a:e9:f0:5d:46:8c:95:dd:7a:58:28:1a:2f:1d:de:cd:00:37: - 41:8f:ed:44:6d:d7:53:28:97:7e:f3:67:04:1e:15:d7:8a:96: - b4:d3:de:4c:27:a4:4c:1b:73:73:76:f4:17:99:c2:1f:7a:0e: - e3:2d:08:ad:0a:1c:2c:ff:3c:ab:55:0e:0f:91:7e:36:eb:c3: - 57:49:be:e1:2e:2d:7c:60:8b:c3:41:51:13:23:9d:ce:f7:32: - 6b:94:01:a8:99:e7:2c:33:1f:3a:3b:25:d2:86:40:ce:3b:2c: - 86:78:c9:61:2f:14:ba:ee:db:55:6f:df:84:ee:05:09:4d:bd: - 28:d8:72:ce:d3:62:50:65:1e:eb:92:97:83:31:d9:b3:b5:ca: - 47:58:3f:5f ------BEGIN CERTIFICATE----- -MIIDXzCCAkegAwIBAgILBAAAAAABIVhTCKIwDQYJKoZIhvcNAQELBQAwTDEgMB4G -A1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjMxEzARBgNVBAoTCkdsb2JhbFNp -Z24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMDkwMzE4MTAwMDAwWhcNMjkwMzE4 -MTAwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSMzETMBEG -A1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjCCASIwDQYJKoZI -hvcNAQEBBQADggEPADCCAQoCggEBAMwldpB5BngiFvXAg7aEyiie/QV2EcWtiHL8 -RgJDx7KKnQRfJMsuS+FggkbhUqsMgUdwbN1k0ev1LKMPgj0MK66X17YUhhB5uzsT -gHeMCOFJ0mpiLx9e+pZo34knlTifBtc+ycsmWQ1z3rDI6SYOgxXG71uL0gRgykmm -KPZpO/bLyCiR5Z2KYVc3rHQU3HTgOu5yLy6c+9C7v/U9AOEGM+iCK65TpjoWc4zd -QQ4gOsC0p6Hpsk+QLjJg6VfLuQSSaGjlOCZgdbKfd/+RFO+uIEn8rUAVSNECMWEZ -XriX7613t2Saer9fwRPvm2L7DWzgVGkWqQPabumDk3F2xmmFghcCAwEAAaNCMEAw -DgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFI/wS3+o -LkUkrk1Q+mOai97i3Ru8MA0GCSqGSIb3DQEBCwUAA4IBAQBLQNvAUKr+yAzv95ZU -RUm7lgAJQayzE4aGKAczymvmdLm6AC2upArT9fHxD4q/c2dKg8dEe3jgr25sbwMp -jjM5RcOO5LlXbKr8EpbsU8Yt5CRsuZRj+9xTaGdWPoO4zzUhw8lo/s7awlOqzJCK -6fBdRoyV3XpYKBovHd7NADdBj+1EbddTKJd+82cEHhXXipa0095MJ6RMG3NzdvQX -mcIfeg7jLQitChws/zyrVQ4PkX4268NXSb7hLi18YIvDQVETI53O9zJrlAGomecs -Mx86OyXShkDOOyyGeMlhLxS67ttVb9+E7gUJTb0o2HLO02JQZR7rkpeDMdmztcpH -WD9f ------END CERTIFICATE----- diff --git a/certs/Go Daddy Secure Certificate Authority - G2.pem b/certs/Go Daddy Secure Certificate Authority - G2.pem deleted file mode 100644 index 4faba90..0000000 --- a/certs/Go Daddy Secure Certificate Authority - G2.pem +++ /dev/null @@ -1,178 +0,0 @@ -Certificate: - Data: - Version: 3 (0x2) - Serial Number: 7 (0x7) - Signature Algorithm: sha256WithRSAEncryption - Issuer: C = US, ST = Arizona, L = Scottsdale, O = "GoDaddy.com, Inc.", CN = Go Daddy Root Certificate Authority - G2 - Validity - Not Before: May 3 07:00:00 2011 GMT - Not After : May 3 07:00:00 2031 GMT - Subject: C = US, ST = Arizona, L = Scottsdale, O = "GoDaddy.com, Inc.", OU = http://certs.godaddy.com/repository/, CN = Go Daddy Secure Certificate Authority - G2 - Subject Public Key Info: - Public Key Algorithm: rsaEncryption - RSA Public-Key: (2048 bit) - Modulus: - 00:b9:e0:cb:10:d4:af:76:bd:d4:93:62:eb:30:64: - b8:81:08:6c:c3:04:d9:62:17:8e:2f:ff:3e:65:cf: - 8f:ce:62:e6:3c:52:1c:da:16:45:4b:55:ab:78:6b: - 63:83:62:90:ce:0f:69:6c:99:c8:1a:14:8b:4c:cc: - 45:33:ea:88:dc:9e:a3:af:2b:fe:80:61:9d:79:57: - c4:cf:2e:f4:3f:30:3c:5d:47:fc:9a:16:bc:c3:37: - 96:41:51:8e:11:4b:54:f8:28:be:d0:8c:be:f0:30: - 38:1e:f3:b0:26:f8:66:47:63:6d:de:71:26:47:8f: - 38:47:53:d1:46:1d:b4:e3:dc:00:ea:45:ac:bd:bc: - 71:d9:aa:6f:00:db:db:cd:30:3a:79:4f:5f:4c:47: - f8:1d:ef:5b:c2:c4:9d:60:3b:b1:b2:43:91:d8:a4: - 33:4e:ea:b3:d6:27:4f:ad:25:8a:a5:c6:f4:d5:d0: - a6:ae:74:05:64:57:88:b5:44:55:d4:2d:2a:3a:3e: - f8:b8:bd:e9:32:0a:02:94:64:c4:16:3a:50:f1:4a: - ae:e7:79:33:af:0c:20:07:7f:e8:df:04:39:c2:69: - 02:6c:63:52:fa:77:c1:1b:c8:74:87:c8:b9:93:18: - 50:54:35:4b:69:4e:bc:3b:d3:49:2e:1f:dc:c1:d2: - 52:fb - Exponent: 65537 (0x10001) - X509v3 extensions: - X509v3 Basic Constraints: critical - CA:TRUE - X509v3 Key Usage: critical - Certificate Sign, CRL Sign - X509v3 Subject Key Identifier: - 40:C2:BD:27:8E:CC:34:83:30:A2:33:D7:FB:6C:B3:F0:B4:2C:80:CE - X509v3 Authority Key Identifier: - keyid:3A:9A:85:07:10:67:28:B6:EF:F6:BD:05:41:6E:20:C1:94:DA:0F:DE - - Authority Information Access: - OCSP - URI:http://ocsp.godaddy.com/ - - X509v3 CRL Distribution Points: - - Full Name: - URI:http://crl.godaddy.com/gdroot-g2.crl - - X509v3 Certificate Policies: - Policy: X509v3 Any Policy - CPS: https://certs.godaddy.com/repository/ - - Signature Algorithm: sha256WithRSAEncryption - 08:7e:6c:93:10:c8:38:b8:96:a9:90:4b:ff:a1:5f:4f:04:ef: - 6c:3e:9c:88:06:c9:50:8f:a6:73:f7:57:31:1b:be:bc:e4:2f: - db:f8:ba:d3:5b:e0:b4:e7:e6:79:62:0e:0c:a2:d7:6a:63:73: - 31:b5:f5:a8:48:a4:3b:08:2d:a2:5d:90:d7:b4:7c:25:4f:11: - 56:30:c4:b6:44:9d:7b:2c:9d:e5:5e:e6:ef:0c:61:aa:bf:e4: - 2a:1b:ee:84:9e:b8:83:7d:c1:43:ce:44:a7:13:70:0d:91:1f: - f4:c8:13:ad:83:60:d9:d8:72:a8:73:24:1e:b5:ac:22:0e:ca: - 17:89:62:58:44:1b:ab:89:25:01:00:0f:cd:c4:1b:62:db:51: - b4:d3:0f:51:2a:9b:f4:bc:73:fc:76:ce:36:a4:cd:d9:d8:2c: - ea:ae:9b:f5:2a:b2:90:d1:4d:75:18:8a:3f:8a:41:90:23:7d: - 5b:4b:fe:a4:03:58:9b:46:b2:c3:60:60:83:f8:7d:50:41:ce: - c2:a1:90:c3:bb:ef:02:2f:d2:15:54:ee:44:15:d9:0a:ae:a7: - 8a:33:ed:b1:2d:76:36:26:dc:04:eb:9f:f7:61:1f:15:dc:87: - 6f:ee:46:96:28:ad:a1:26:7d:0a:09:a7:2e:04:a3:8d:bc:f8: - bc:04:30:01 ------BEGIN CERTIFICATE----- -MIIE0DCCA7igAwIBAgIBBzANBgkqhkiG9w0BAQsFADCBgzELMAkGA1UEBhMCVVMx -EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxGjAYBgNVBAoT -EUdvRGFkZHkuY29tLCBJbmMuMTEwLwYDVQQDEyhHbyBEYWRkeSBSb290IENlcnRp -ZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTExMDUwMzA3MDAwMFoXDTMxMDUwMzA3 -MDAwMFowgbQxCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6b25hMRMwEQYDVQQH -EwpTY290dHNkYWxlMRowGAYDVQQKExFHb0RhZGR5LmNvbSwgSW5jLjEtMCsGA1UE -CxMkaHR0cDovL2NlcnRzLmdvZGFkZHkuY29tL3JlcG9zaXRvcnkvMTMwMQYDVQQD -EypHbyBEYWRkeSBTZWN1cmUgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IC0gRzIwggEi -MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC54MsQ1K92vdSTYuswZLiBCGzD -BNliF44v/z5lz4/OYuY8UhzaFkVLVat4a2ODYpDOD2lsmcgaFItMzEUz6ojcnqOv -K/6AYZ15V8TPLvQ/MDxdR/yaFrzDN5ZBUY4RS1T4KL7QjL7wMDge87Am+GZHY23e -cSZHjzhHU9FGHbTj3ADqRay9vHHZqm8A29vNMDp5T19MR/gd71vCxJ1gO7GyQ5HY -pDNO6rPWJ0+tJYqlxvTV0KaudAVkV4i1RFXULSo6Pvi4vekyCgKUZMQWOlDxSq7n -eTOvDCAHf+jfBDnCaQJsY1L6d8EbyHSHyLmTGFBUNUtpTrw700kuH9zB0lL7AgMB -AAGjggEaMIIBFjAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNV -HQ4EFgQUQMK9J47MNIMwojPX+2yz8LQsgM4wHwYDVR0jBBgwFoAUOpqFBxBnKLbv -9r0FQW4gwZTaD94wNAYIKwYBBQUHAQEEKDAmMCQGCCsGAQUFBzABhhhodHRwOi8v -b2NzcC5nb2RhZGR5LmNvbS8wNQYDVR0fBC4wLDAqoCigJoYkaHR0cDovL2NybC5n -b2RhZGR5LmNvbS9nZHJvb3QtZzIuY3JsMEYGA1UdIAQ/MD0wOwYEVR0gADAzMDEG -CCsGAQUFBwIBFiVodHRwczovL2NlcnRzLmdvZGFkZHkuY29tL3JlcG9zaXRvcnkv -MA0GCSqGSIb3DQEBCwUAA4IBAQAIfmyTEMg4uJapkEv/oV9PBO9sPpyIBslQj6Zz -91cxG7685C/b+LrTW+C05+Z5Yg4MotdqY3MxtfWoSKQ7CC2iXZDXtHwlTxFWMMS2 -RJ17LJ3lXubvDGGqv+QqG+6EnriDfcFDzkSnE3ANkR/0yBOtg2DZ2HKocyQetawi -DsoXiWJYRBuriSUBAA/NxBti21G00w9RKpv0vHP8ds42pM3Z2Czqrpv1KrKQ0U11 -GIo/ikGQI31bS/6kA1ibRrLDYGCD+H1QQc7CoZDDu+8CL9IVVO5EFdkKrqeKM+2x -LXY2JtwE65/3YR8V3Idv7kaWKK2hJn0KCacuBKONvPi8BDAB ------END CERTIFICATE----- -Certificate: - Data: - Version: 3 (0x2) - Serial Number: 0 (0x0) - Signature Algorithm: sha256WithRSAEncryption - Issuer: C = US, ST = Arizona, L = Scottsdale, O = "GoDaddy.com, Inc.", CN = Go Daddy Root Certificate Authority - G2 - Validity - Not Before: Sep 1 00:00:00 2009 GMT - Not After : Dec 31 23:59:59 2037 GMT - Subject: C = US, ST = Arizona, L = Scottsdale, O = "GoDaddy.com, Inc.", CN = Go Daddy Root Certificate Authority - G2 - Subject Public Key Info: - Public Key Algorithm: rsaEncryption - RSA Public-Key: (2048 bit) - Modulus: - 00:bf:71:62:08:f1:fa:59:34:f7:1b:c9:18:a3:f7: - 80:49:58:e9:22:83:13:a6:c5:20:43:01:3b:84:f1: - e6:85:49:9f:27:ea:f6:84:1b:4e:a0:b4:db:70:98: - c7:32:01:b1:05:3e:07:4e:ee:f4:fa:4f:2f:59:30: - 22:e7:ab:19:56:6b:e2:80:07:fc:f3:16:75:80:39: - 51:7b:e5:f9:35:b6:74:4e:a9:8d:82:13:e4:b6:3f: - a9:03:83:fa:a2:be:8a:15:6a:7f:de:0b:c3:b6:19: - 14:05:ca:ea:c3:a8:04:94:3b:46:7c:32:0d:f3:00: - 66:22:c8:8d:69:6d:36:8c:11:18:b7:d3:b2:1c:60: - b4:38:fa:02:8c:ce:d3:dd:46:07:de:0a:3e:eb:5d: - 7c:c8:7c:fb:b0:2b:53:a4:92:62:69:51:25:05:61: - 1a:44:81:8c:2c:a9:43:96:23:df:ac:3a:81:9a:0e: - 29:c5:1c:a9:e9:5d:1e:b6:9e:9e:30:0a:39:ce:f1: - 88:80:fb:4b:5d:cc:32:ec:85:62:43:25:34:02:56: - 27:01:91:b4:3b:70:2a:3f:6e:b1:e8:9c:88:01:7d: - 9f:d4:f9:db:53:6d:60:9d:bf:2c:e7:58:ab:b8:5f: - 46:fc:ce:c4:1b:03:3c:09:eb:49:31:5c:69:46:b3: - e0:47 - Exponent: 65537 (0x10001) - X509v3 extensions: - X509v3 Basic Constraints: critical - CA:TRUE - X509v3 Key Usage: critical - Certificate Sign, CRL Sign - X509v3 Subject Key Identifier: - 3A:9A:85:07:10:67:28:B6:EF:F6:BD:05:41:6E:20:C1:94:DA:0F:DE - Signature Algorithm: sha256WithRSAEncryption - 99:db:5d:79:d5:f9:97:59:67:03:61:f1:7e:3b:06:31:75:2d: - a1:20:8e:4f:65:87:b4:f7:a6:9c:bc:d8:e9:2f:d0:db:5a:ee: - cf:74:8c:73:b4:38:42:da:05:7b:f8:02:75:b8:fd:a5:b1:d7: - ae:f6:d7:de:13:cb:53:10:7e:8a:46:d1:97:fa:b7:2e:2b:11: - ab:90:b0:27:80:f9:e8:9f:5a:e9:37:9f:ab:e4:df:6c:b3:85: - 17:9d:3d:d9:24:4f:79:91:35:d6:5f:04:eb:80:83:ab:9a:02: - 2d:b5:10:f4:d8:90:c7:04:73:40:ed:72:25:a0:a9:9f:ec:9e: - ab:68:12:99:57:c6:8f:12:3a:09:a4:bd:44:fd:06:15:37:c1: - 9b:e4:32:a3:ed:38:e8:d8:64:f3:2c:7e:14:fc:02:ea:9f:cd: - ff:07:68:17:db:22:90:38:2d:7a:8d:d1:54:f1:69:e3:5f:33: - ca:7a:3d:7b:0a:e3:ca:7f:5f:39:e5:e2:75:ba:c5:76:18:33: - ce:2c:f0:2f:4c:ad:f7:b1:e7:ce:4f:a8:c4:9b:4a:54:06:c5: - 7f:7d:d5:08:0f:e2:1c:fe:7e:17:b8:ac:5e:f6:d4:16:b2:43: - 09:0c:4d:f6:a7:6b:b4:99:84:65:ca:7a:88:e2:e2:44:be:5c: - f7:ea:1c:f5 ------BEGIN CERTIFICATE----- -MIIDxTCCAq2gAwIBAgIBADANBgkqhkiG9w0BAQsFADCBgzELMAkGA1UEBhMCVVMx -EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxGjAYBgNVBAoT -EUdvRGFkZHkuY29tLCBJbmMuMTEwLwYDVQQDEyhHbyBEYWRkeSBSb290IENlcnRp -ZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5MDkwMTAwMDAwMFoXDTM3MTIzMTIz -NTk1OVowgYMxCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6b25hMRMwEQYDVQQH -EwpTY290dHNkYWxlMRowGAYDVQQKExFHb0RhZGR5LmNvbSwgSW5jLjExMC8GA1UE -AxMoR28gRGFkZHkgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgLSBHMjCCASIw -DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL9xYgjx+lk09xvJGKP3gElY6SKD -E6bFIEMBO4Tx5oVJnyfq9oQbTqC023CYxzIBsQU+B07u9PpPL1kwIuerGVZr4oAH -/PMWdYA5UXvl+TW2dE6pjYIT5LY/qQOD+qK+ihVqf94Lw7YZFAXK6sOoBJQ7Rnwy -DfMAZiLIjWltNowRGLfTshxgtDj6AozO091GB94KPutdfMh8+7ArU6SSYmlRJQVh -GkSBjCypQ5Yj36w6gZoOKcUcqeldHraenjAKOc7xiID7S13MMuyFYkMlNAJWJwGR -tDtwKj9useiciAF9n9T521NtYJ2/LOdYq7hfRvzOxBsDPAnrSTFcaUaz4EcCAwEA -AaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYE -FDqahQcQZyi27/a9BUFuIMGU2g/eMA0GCSqGSIb3DQEBCwUAA4IBAQCZ21151fmX -WWcDYfF+OwYxdS2hII5PZYe096acvNjpL9DbWu7PdIxztDhC2gV7+AJ1uP2lsdeu -9tfeE8tTEH6KRtGX+rcuKxGrkLAngPnon1rpN5+r5N9ss4UXnT3ZJE95kTXWXwTr -gIOrmgIttRD02JDHBHNA7XIloKmf7J6raBKZV8aPEjoJpL1E/QYVN8Gb5DKj7Tjo -2GTzLH4U/ALqn83/B2gX2yKQOC16jdFU8WnjXzPKej17CuPKf1855eJ1usV2GDPO -LPAvTK33sefOT6jEm0pUBsV/fdUID+Ic/n4XuKxe9tQWskMJDE32p2u0mYRlynqI -4uJEvlz36hz1 ------END CERTIFICATE----- diff --git a/certs/Go-Daddy-Root-Certificate-Authority-G2.pem b/certs/Go-Daddy-Root-Certificate-Authority-G2.pem new file mode 100644 index 0000000..c61f300 --- /dev/null +++ b/certs/Go-Daddy-Root-Certificate-Authority-G2.pem @@ -0,0 +1,30 @@ +# Issuer: CN=Go Daddy Root Certificate Authority - G2 O=GoDaddy.com, Inc. +# Subject: CN=Go Daddy Root Certificate Authority - G2 O=GoDaddy.com, Inc. +# Label: "Go Daddy Root Certificate Authority - G2" +# Serial: 0 +# MD5 Fingerprint: 80:3a:bc:22:c1:e6:fb:8d:9b:3b:27:4a:32:1b:9a:01 +# SHA1 Fingerprint: 47:be:ab:c9:22:ea:e8:0e:78:78:34:62:a7:9f:45:c2:54:fd:e6:8b +# SHA256 Fingerprint: 45:14:0b:32:47:eb:9c:c8:c5:b4:f0:d7:b5:30:91:f7:32:92:08:9e:6e:5a:63:e2:74:9d:d3:ac:a9:19:8e:da +-----BEGIN CERTIFICATE----- +MIIDxTCCAq2gAwIBAgIBADANBgkqhkiG9w0BAQsFADCBgzELMAkGA1UEBhMCVVMx +EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxGjAYBgNVBAoT +EUdvRGFkZHkuY29tLCBJbmMuMTEwLwYDVQQDEyhHbyBEYWRkeSBSb290IENlcnRp +ZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5MDkwMTAwMDAwMFoXDTM3MTIzMTIz +NTk1OVowgYMxCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6b25hMRMwEQYDVQQH +EwpTY290dHNkYWxlMRowGAYDVQQKExFHb0RhZGR5LmNvbSwgSW5jLjExMC8GA1UE +AxMoR28gRGFkZHkgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgLSBHMjCCASIw +DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL9xYgjx+lk09xvJGKP3gElY6SKD +E6bFIEMBO4Tx5oVJnyfq9oQbTqC023CYxzIBsQU+B07u9PpPL1kwIuerGVZr4oAH +/PMWdYA5UXvl+TW2dE6pjYIT5LY/qQOD+qK+ihVqf94Lw7YZFAXK6sOoBJQ7Rnwy +DfMAZiLIjWltNowRGLfTshxgtDj6AozO091GB94KPutdfMh8+7ArU6SSYmlRJQVh +GkSBjCypQ5Yj36w6gZoOKcUcqeldHraenjAKOc7xiID7S13MMuyFYkMlNAJWJwGR +tDtwKj9useiciAF9n9T521NtYJ2/LOdYq7hfRvzOxBsDPAnrSTFcaUaz4EcCAwEA +AaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYE +FDqahQcQZyi27/a9BUFuIMGU2g/eMA0GCSqGSIb3DQEBCwUAA4IBAQCZ21151fmX +WWcDYfF+OwYxdS2hII5PZYe096acvNjpL9DbWu7PdIxztDhC2gV7+AJ1uP2lsdeu +9tfeE8tTEH6KRtGX+rcuKxGrkLAngPnon1rpN5+r5N9ss4UXnT3ZJE95kTXWXwTr +gIOrmgIttRD02JDHBHNA7XIloKmf7J6raBKZV8aPEjoJpL1E/QYVN8Gb5DKj7Tjo +2GTzLH4U/ALqn83/B2gX2yKQOC16jdFU8WnjXzPKej17CuPKf1855eJ1usV2GDPO +LPAvTK33sefOT6jEm0pUBsV/fdUID+Ic/n4XuKxe9tQWskMJDE32p2u0mYRlynqI +4uJEvlz36hz1 +-----END CERTIFICATE----- diff --git a/certs/ISRG-Root-X1.pem b/certs/ISRG-Root-X1.pem new file mode 100644 index 0000000..995c95d --- /dev/null +++ b/certs/ISRG-Root-X1.pem @@ -0,0 +1,38 @@ +# Issuer: CN=ISRG Root X1 O=Internet Security Research Group +# Subject: CN=ISRG Root X1 O=Internet Security Research Group +# Label: "ISRG Root X1" +# Serial: 172886928669790476064670243504169061120 +# MD5 Fingerprint: 0c:d2:f9:e0:da:17:73:e9:ed:86:4d:a5:e3:70:e7:4e +# SHA1 Fingerprint: ca:bd:2a:79:a1:07:6a:31:f2:1d:25:36:35:cb:03:9d:43:29:a5:e8 +# SHA256 Fingerprint: 96:bc:ec:06:26:49:76:f3:74:60:77:9a:cf:28:c5:a7:cf:e8:a3:c0:aa:e1:1a:8f:fc:ee:05:c0:bd:df:08:c6 +-----BEGIN CERTIFICATE----- +MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw +TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh +cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4 +WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu +ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY +MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc +h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+ +0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U +A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW +T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH +B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC +B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv +KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn +OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn +jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw +qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI +rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV +HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq +hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL +ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ +3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK +NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5 +ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur +TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC +jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc +oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq +4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA +mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d +emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc= +-----END CERTIFICATE----- diff --git a/certs/ISRG-Root-X2.pem b/certs/ISRG-Root-X2.pem new file mode 100644 index 0000000..9cca880 --- /dev/null +++ b/certs/ISRG-Root-X2.pem @@ -0,0 +1,21 @@ +# Issuer: CN=ISRG Root X2 O=Internet Security Research Group +# Subject: CN=ISRG Root X2 O=Internet Security Research Group +# Label: "ISRG Root X2" +# Serial: 87493402998870891108772069816698636114 +# MD5 Fingerprint: d3:9e:c4:1e:23:3c:a6:df:cf:a3:7e:6d:e0:14:e6:e5 +# SHA1 Fingerprint: bd:b1:b9:3c:d5:97:8d:45:c6:26:14:55:f8:db:95:c7:5a:d1:53:af +# SHA256 Fingerprint: 69:72:9b:8e:15:a8:6e:fc:17:7a:57:af:b7:17:1d:fc:64:ad:d2:8c:2f:ca:8c:f1:50:7e:34:45:3c:cb:14:70 +-----BEGIN CERTIFICATE----- +MIICGzCCAaGgAwIBAgIQQdKd0XLq7qeAwSxs6S+HUjAKBggqhkjOPQQDAzBPMQsw +CQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJuZXQgU2VjdXJpdHkgUmVzZWFyY2gg +R3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBYMjAeFw0yMDA5MDQwMDAwMDBaFw00 +MDA5MTcxNjAwMDBaME8xCzAJBgNVBAYTAlVTMSkwJwYDVQQKEyBJbnRlcm5ldCBT +ZWN1cml0eSBSZXNlYXJjaCBHcm91cDEVMBMGA1UEAxMMSVNSRyBSb290IFgyMHYw +EAYHKoZIzj0CAQYFK4EEACIDYgAEzZvVn4CDCuwJSvMWSj5cz3es3mcFDR0HttwW ++1qLFNvicWDEukWVEYmO6gbf9yoWHKS5xcUy4APgHoIYOIvXRdgKam7mAHf7AlF9 +ItgKbppbd9/w+kHsOdx1ymgHDB/qo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0T +AQH/BAUwAwEB/zAdBgNVHQ4EFgQUfEKWrt5LSDv6kviejM9ti6lyN5UwCgYIKoZI +zj0EAwMDaAAwZQIwe3lORlCEwkSHRhtFcP9Ymd70/aTSVaYgLXTWNLxBo1BfASdW +tL4ndQavEi51mI38AjEAi/V3bNTIZargCyzuFJ0nN6T5U6VR5CmD1/iQMVtCnwr1 +/q4AaOeMSQ+2b1tbFfLn +-----END CERTIFICATE----- diff --git a/certs/Makefile b/certs/Makefile new file mode 100644 index 0000000..3ccad6e --- /dev/null +++ b/certs/Makefile @@ -0,0 +1,58 @@ +# Makefile to check certificates + +CURL = curl \ + --capath /dev/null \ + --connect-timeout 5 \ + --output /dev/null \ + --silent + +DOMAINS_DUAL = \ + api.macvendors.com/GTS-Root-R4 \ + api.telegram.org/Go-Daddy-Root-Certificate-Authority-G2 \ + cloudflare-dns.com/DigiCert-Global-Root-G2 \ + dns.google/GTS-Root-R4 \ + dns.quad9.net/DigiCert-Global-Root-G3 \ + git.eworm.de/ISRG-Root-X2 \ + lists.blocklist.de/Certum-Trusted-Network-CA \ + matrix.org/GTS-Root-R4 \ + raw.githubusercontent.com/USERTrust-RSA-Certification-Authority \ + rsc.eworm.de/ISRG-Root-X2 \ + upgrade.mikrotik.com/ISRG-Root-X1 +DOMAINS_IPV4 = \ + 1.1.1.1/DigiCert-Global-Root-G2 \ + 8.8.8.8/GTS-Root-R1 \ + 9.9.9.9/DigiCert-Global-Root-G3 \ + api.mullvad.net/ISRG-Root-X1 \ + ipv4.showipv6.de/ISRG-Root-X1 \ + ipv4.tunnelbroker.net/Starfield-Root-Certificate-Authority-G2 \ + mkcert.org/ISRG-Root-X1 \ + ntfy.sh/ISRG-Root-X1 \ + www.dshield.org/ISRG-Root-X1 \ + www.spamhaus.org/GTS-Root-R4 +DOMAINS_IPV6 = \ + [2606\:4700\:4700\:\:1111]/DigiCert-Global-Root-G2 \ + [2001\:4860\:4860\:\:8888]/GTS-Root-R1 \ + [2620\:fe\:\:9]/DigiCert-Global-Root-G3 \ + ipv6.showipv6.de/ISRG-Root-X1 + +.PHONY: $(DOMAINS_DUAL) $(DOMAINS_IPV4) $(DOMAINS_IPV6) + +all: $(DOMAINS_DUAL) $(DOMAINS_IPV4) $(DOMAINS_IPV6) + +$(DOMAINS_DUAL): +ifndef NOIPV4 + $(CURL) -4 --cacert $(notdir $@).pem https://$(dir $@) +endif +ifndef NOIPV6 + $(CURL) -6 --cacert $(notdir $@).pem https://$(dir $@) +endif + +$(DOMAINS_IPV4): +ifndef NOIPV4 + $(CURL) -4 --cacert $(notdir $@).pem https://$(dir $@) +endif + +$(DOMAINS_IPV6): +ifndef NOIPV6 + $(CURL) -6 --cacert $(notdir $@).pem https://$(dir $@) +endif diff --git a/certs/R3.pem b/certs/R3.pem deleted file mode 100644 index 837b709..0000000 --- a/certs/R3.pem +++ /dev/null @@ -1,237 +0,0 @@ -Certificate: - Data: - Version: 3 (0x2) - Serial Number: - 91:2b:08:4a:cf:0c:18:a7:53:f6:d6:2e:25:a7:5f:5a - Signature Algorithm: sha256WithRSAEncryption - Issuer: C = US, O = Internet Security Research Group, CN = ISRG Root X1 - Validity - Not Before: Sep 4 00:00:00 2020 GMT - Not After : Sep 15 16:00:00 2025 GMT - Subject: C = US, O = Let's Encrypt, CN = R3 - Subject Public Key Info: - Public Key Algorithm: rsaEncryption - RSA Public-Key: (2048 bit) - Modulus: - 00:bb:02:15:28:cc:f6:a0:94:d3:0f:12:ec:8d:55: - 92:c3:f8:82:f1:99:a6:7a:42:88:a7:5d:26:aa:b5: - 2b:b9:c5:4c:b1:af:8e:6b:f9:75:c8:a3:d7:0f:47: - 94:14:55:35:57:8c:9e:a8:a2:39:19:f5:82:3c:42: - a9:4e:6e:f5:3b:c3:2e:db:8d:c0:b0:5c:f3:59:38: - e7:ed:cf:69:f0:5a:0b:1b:be:c0:94:24:25:87:fa: - 37:71:b3:13:e7:1c:ac:e1:9b:ef:db:e4:3b:45:52: - 45:96:a9:c1:53:ce:34:c8:52:ee:b5:ae:ed:8f:de: - 60:70:e2:a5:54:ab:b6:6d:0e:97:a5:40:34:6b:2b: - d3:bc:66:eb:66:34:7c:fa:6b:8b:8f:57:29:99:f8: - 30:17:5d:ba:72:6f:fb:81:c5:ad:d2:86:58:3d:17: - c7:e7:09:bb:f1:2b:f7:86:dc:c1:da:71:5d:d4:46: - e3:cc:ad:25:c1:88:bc:60:67:75:66:b3:f1:18:f7: - a2:5c:e6:53:ff:3a:88:b6:47:a5:ff:13:18:ea:98: - 09:77:3f:9d:53:f9:cf:01:e5:f5:a6:70:17:14:af: - 63:a4:ff:99:b3:93:9d:dc:53:a7:06:fe:48:85:1d: - a1:69:ae:25:75:bb:13:cc:52:03:f5:ed:51:a1:8b: - db:15 - Exponent: 65537 (0x10001) - X509v3 extensions: - X509v3 Key Usage: critical - Digital Signature, Certificate Sign, CRL Sign - X509v3 Extended Key Usage: - TLS Web Client Authentication, TLS Web Server Authentication - X509v3 Basic Constraints: critical - CA:TRUE, pathlen:0 - X509v3 Subject Key Identifier: - 14:2E:B3:17:B7:58:56:CB:AE:50:09:40:E6:1F:AF:9D:8B:14:C2:C6 - X509v3 Authority Key Identifier: - keyid:79:B4:59:E6:7B:B6:E5:E4:01:73:80:08:88:C8:1A:58:F6:E9:9B:6E - - Authority Information Access: - CA Issuers - URI:http://x1.i.lencr.org/ - - X509v3 CRL Distribution Points: - - Full Name: - URI:http://x1.c.lencr.org/ - - X509v3 Certificate Policies: - Policy: 2.23.140.1.2.1 - Policy: 1.3.6.1.4.1.44947.1.1.1 - - Signature Algorithm: sha256WithRSAEncryption - 85:ca:4e:47:3e:a3:f7:85:44:85:bc:d5:67:78:b2:98:63:ad: - 75:4d:1e:96:3d:33:65:72:54:2d:81:a0:ea:c3:ed:f8:20:bf: - 5f:cc:b7:70:00:b7:6e:3b:f6:5e:94:de:e4:20:9f:a6:ef:8b: - b2:03:e7:a2:b5:16:3c:91:ce:b4:ed:39:02:e7:7c:25:8a:47: - e6:65:6e:3f:46:f4:d9:f0:ce:94:2b:ee:54:ce:12:bc:8c:27: - 4b:b8:c1:98:2f:a2:af:cd:71:91:4a:08:b7:c8:b8:23:7b:04: - 2d:08:f9:08:57:3e:83:d9:04:33:0a:47:21:78:09:82:27:c3: - 2a:c8:9b:b9:ce:5c:f2:64:c8:c0:be:79:c0:4f:8e:6d:44:0c: - 5e:92:bb:2e:f7:8b:10:e1:e8:1d:44:29:db:59:20:ed:63:b9: - 21:f8:12:26:94:93:57:a0:1d:65:04:c1:0a:22:ae:10:0d:43: - 97:a1:18:1f:7e:e0:e0:86:37:b5:5a:b1:bd:30:bf:87:6e:2b: - 2a:ff:21:4e:1b:05:c3:f5:18:97:f0:5e:ac:c3:a5:b8:6a:f0: - 2e:bc:3b:33:b9:ee:4b:de:cc:fc:e4:af:84:0b:86:3f:c0:55: - 43:36:f6:68:e1:36:17:6a:8e:99:d1:ff:a5:40:a7:34:b7:c0: - d0:63:39:35:39:75:6e:f2:ba:76:c8:93:02:e9:a9:4b:6c:17: - ce:0c:02:d9:bd:81:fb:9f:b7:68:d4:06:65:b3:82:3d:77:53: - f8:8e:79:03:ad:0a:31:07:75:2a:43:d8:55:97:72:c4:29:0e: - f7:c4:5d:4e:c8:ae:46:84:30:d7:f2:85:5f:18:a1:79:bb:e7: - 5e:70:8b:07:e1:86:93:c3:b9:8f:dc:61:71:25:2a:af:df:ed: - 25:50:52:68:8b:92:dc:e5:d6:b5:e3:da:7d:d0:87:6c:84:21: - 31:ae:82:f5:fb:b9:ab:c8:89:17:3d:e1:4c:e5:38:0e:f6:bd: - 2b:bd:96:81:14:eb:d5:db:3d:20:a7:7e:59:d3:e2:f8:58:f9: - 5b:b8:48:cd:fe:5c:4f:16:29:fe:1e:55:23:af:c8:11:b0:8d: - ea:7c:93:90:17:2f:fd:ac:a2:09:47:46:3f:f0:e9:b0:b7:ff: - 28:4d:68:32:d6:67:5e:1e:69:a3:93:b8:f5:9d:8b:2f:0b:d2: - 52:43:a6:6f:32:57:65:4d:32:81:df:38:53:85:5d:7e:5d:66: - 29:ea:b8:dd:e4:95:b5:cd:b5:56:12:42:cd:c4:4e:c6:25:38: - 44:50:6d:ec:ce:00:55:18:fe:e9:49:64:d4:4e:ca:97:9c:b4: - 5b:c0:73:a8:ab:b8:47:c2 ------BEGIN CERTIFICATE----- -MIIFFjCCAv6gAwIBAgIRAJErCErPDBinU/bWLiWnX1owDQYJKoZIhvcNAQELBQAw -TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh -cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMjAwOTA0MDAwMDAw -WhcNMjUwOTE1MTYwMDAwWjAyMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNTGV0J3Mg -RW5jcnlwdDELMAkGA1UEAxMCUjMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK -AoIBAQC7AhUozPaglNMPEuyNVZLD+ILxmaZ6QoinXSaqtSu5xUyxr45r+XXIo9cP -R5QUVTVXjJ6oojkZ9YI8QqlObvU7wy7bjcCwXPNZOOftz2nwWgsbvsCUJCWH+jdx -sxPnHKzhm+/b5DtFUkWWqcFTzjTIUu61ru2P3mBw4qVUq7ZtDpelQDRrK9O8Zutm -NHz6a4uPVymZ+DAXXbpyb/uBxa3Shlg9F8fnCbvxK/eG3MHacV3URuPMrSXBiLxg -Z3Vms/EY96Jc5lP/Ooi2R6X/ExjqmAl3P51T+c8B5fWmcBcUr2Ok/5mzk53cU6cG -/kiFHaFpriV1uxPMUgP17VGhi9sVAgMBAAGjggEIMIIBBDAOBgNVHQ8BAf8EBAMC -AYYwHQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMBMBIGA1UdEwEB/wQIMAYB -Af8CAQAwHQYDVR0OBBYEFBQusxe3WFbLrlAJQOYfr52LFMLGMB8GA1UdIwQYMBaA -FHm0WeZ7tuXkAXOACIjIGlj26ZtuMDIGCCsGAQUFBwEBBCYwJDAiBggrBgEFBQcw -AoYWaHR0cDovL3gxLmkubGVuY3Iub3JnLzAnBgNVHR8EIDAeMBygGqAYhhZodHRw -Oi8veDEuYy5sZW5jci5vcmcvMCIGA1UdIAQbMBkwCAYGZ4EMAQIBMA0GCysGAQQB -gt8TAQEBMA0GCSqGSIb3DQEBCwUAA4ICAQCFyk5HPqP3hUSFvNVneLKYY611TR6W -PTNlclQtgaDqw+34IL9fzLdwALduO/ZelN7kIJ+m74uyA+eitRY8kc607TkC53wl -ikfmZW4/RvTZ8M6UK+5UzhK8jCdLuMGYL6KvzXGRSgi3yLgjewQtCPkIVz6D2QQz -CkcheAmCJ8MqyJu5zlzyZMjAvnnAT45tRAxekrsu94sQ4egdRCnbWSDtY7kh+BIm -lJNXoB1lBMEKIq4QDUOXoRgffuDghje1WrG9ML+Hbisq/yFOGwXD9RiX8F6sw6W4 -avAuvDszue5L3sz85K+EC4Y/wFVDNvZo4TYXao6Z0f+lQKc0t8DQYzk1OXVu8rp2 -yJMC6alLbBfODALZvYH7n7do1AZls4I9d1P4jnkDrQoxB3UqQ9hVl3LEKQ73xF1O -yK5GhDDX8oVfGKF5u+decIsH4YaTw7mP3GFxJSqv3+0lUFJoi5Lc5da149p90Ids -hCExroL1+7mryIkXPeFM5TgO9r0rvZaBFOvV2z0gp35Z0+L4WPlbuEjN/lxPFin+ -HlUjr8gRsI3qfJOQFy/9rKIJR0Y/8Omwt/8oTWgy1mdeHmmjk7j1nYsvC9JSQ6Zv -MldlTTKB3zhThV1+XWYp6rjd5JW1zbVWEkLNxE7GJThEUG3szgBVGP7pSWTUTsqX -nLRbwHOoq7hHwg== ------END CERTIFICATE----- -Certificate: - Data: - Version: 3 (0x2) - Serial Number: - 82:10:cf:b0:d2:40:e3:59:44:63:e0:bb:63:82:8b:00 - Signature Algorithm: sha256WithRSAEncryption - Issuer: C = US, O = Internet Security Research Group, CN = ISRG Root X1 - Validity - Not Before: Jun 4 11:04:38 2015 GMT - Not After : Jun 4 11:04:38 2035 GMT - Subject: C = US, O = Internet Security Research Group, CN = ISRG Root X1 - Subject Public Key Info: - Public Key Algorithm: rsaEncryption - RSA Public-Key: (4096 bit) - Modulus: - 00:ad:e8:24:73:f4:14:37:f3:9b:9e:2b:57:28:1c: - 87:be:dc:b7:df:38:90:8c:6e:3c:e6:57:a0:78:f7: - 75:c2:a2:fe:f5:6a:6e:f6:00:4f:28:db:de:68:86: - 6c:44:93:b6:b1:63:fd:14:12:6b:bf:1f:d2:ea:31: - 9b:21:7e:d1:33:3c:ba:48:f5:dd:79:df:b3:b8:ff: - 12:f1:21:9a:4b:c1:8a:86:71:69:4a:66:66:6c:8f: - 7e:3c:70:bf:ad:29:22:06:f3:e4:c0:e6:80:ae:e2: - 4b:8f:b7:99:7e:94:03:9f:d3:47:97:7c:99:48:23: - 53:e8:38:ae:4f:0a:6f:83:2e:d1:49:57:8c:80:74: - b6:da:2f:d0:38:8d:7b:03:70:21:1b:75:f2:30:3c: - fa:8f:ae:dd:da:63:ab:eb:16:4f:c2:8e:11:4b:7e: - cf:0b:e8:ff:b5:77:2e:f4:b2:7b:4a:e0:4c:12:25: - 0c:70:8d:03:29:a0:e1:53:24:ec:13:d9:ee:19:bf: - 10:b3:4a:8c:3f:89:a3:61:51:de:ac:87:07:94:f4: - 63:71:ec:2e:e2:6f:5b:98:81:e1:89:5c:34:79:6c: - 76:ef:3b:90:62:79:e6:db:a4:9a:2f:26:c5:d0:10: - e1:0e:de:d9:10:8e:16:fb:b7:f7:a8:f7:c7:e5:02: - 07:98:8f:36:08:95:e7:e2:37:96:0d:36:75:9e:fb: - 0e:72:b1:1d:9b:bc:03:f9:49:05:d8:81:dd:05:b4: - 2a:d6:41:e9:ac:01:76:95:0a:0f:d8:df:d5:bd:12: - 1f:35:2f:28:17:6c:d2:98:c1:a8:09:64:77:6e:47: - 37:ba:ce:ac:59:5e:68:9d:7f:72:d6:89:c5:06:41: - 29:3e:59:3e:dd:26:f5:24:c9:11:a7:5a:a3:4c:40: - 1f:46:a1:99:b5:a7:3a:51:6e:86:3b:9e:7d:72:a7: - 12:05:78:59:ed:3e:51:78:15:0b:03:8f:8d:d0:2f: - 05:b2:3e:7b:4a:1c:4b:73:05:12:fc:c6:ea:e0:50: - 13:7c:43:93:74:b3:ca:74:e7:8e:1f:01:08:d0:30: - d4:5b:71:36:b4:07:ba:c1:30:30:5c:48:b7:82:3b: - 98:a6:7d:60:8a:a2:a3:29:82:cc:ba:bd:83:04:1b: - a2:83:03:41:a1:d6:05:f1:1b:c2:b6:f0:a8:7c:86: - 3b:46:a8:48:2a:88:dc:76:9a:76:bf:1f:6a:a5:3d: - 19:8f:eb:38:f3:64:de:c8:2b:0d:0a:28:ff:f7:db: - e2:15:42:d4:22:d0:27:5d:e1:79:fe:18:e7:70:88: - ad:4e:e6:d9:8b:3a:c6:dd:27:51:6e:ff:bc:64:f5: - 33:43:4f - Exponent: 65537 (0x10001) - X509v3 extensions: - X509v3 Key Usage: critical - Certificate Sign, CRL Sign - X509v3 Basic Constraints: critical - CA:TRUE - X509v3 Subject Key Identifier: - 79:B4:59:E6:7B:B6:E5:E4:01:73:80:08:88:C8:1A:58:F6:E9:9B:6E - Signature Algorithm: sha256WithRSAEncryption - 55:1f:58:a9:bc:b2:a8:50:d0:0c:b1:d8:1a:69:20:27:29:08: - ac:61:75:5c:8a:6e:f8:82:e5:69:2f:d5:f6:56:4b:b9:b8:73: - 10:59:d3:21:97:7e:e7:4c:71:fb:b2:d2:60:ad:39:a8:0b:ea: - 17:21:56:85:f1:50:0e:59:eb:ce:e0:59:e9:ba:c9:15:ef:86: - 9d:8f:84:80:f6:e4:e9:91:90:dc:17:9b:62:1b:45:f0:66:95: - d2:7c:6f:c2:ea:3b:ef:1f:cf:cb:d6:ae:27:f1:a9:b0:c8:ae: - fd:7d:7e:9a:fa:22:04:eb:ff:d9:7f:ea:91:2b:22:b1:17:0e: - 8f:f2:8a:34:5b:58:d8:fc:01:c9:54:b9:b8:26:cc:8a:88:33: - 89:4c:2d:84:3c:82:df:ee:96:57:05:ba:2c:bb:f7:c4:b7:c7: - 4e:3b:82:be:31:c8:22:73:73:92:d1:c2:80:a4:39:39:10:33: - 23:82:4c:3c:9f:86:b2:55:98:1d:be:29:86:8c:22:9b:9e:e2: - 6b:3b:57:3a:82:70:4d:dc:09:c7:89:cb:0a:07:4d:6c:e8:5d: - 8e:c9:ef:ce:ab:c7:bb:b5:2b:4e:45:d6:4a:d0:26:cc:e5:72: - ca:08:6a:a5:95:e3:15:a1:f7:a4:ed:c9:2c:5f:a5:fb:ff:ac: - 28:02:2e:be:d7:7b:bb:e3:71:7b:90:16:d3:07:5e:46:53:7c: - 37:07:42:8c:d3:c4:96:9c:d5:99:b5:2a:e0:95:1a:80:48:ae: - 4c:39:07:ce:cc:47:a4:52:95:2b:ba:b8:fb:ad:d2:33:53:7d: - e5:1d:4d:6d:d5:a1:b1:c7:42:6f:e6:40:27:35:5c:a3:28:b7: - 07:8d:e7:8d:33:90:e7:23:9f:fb:50:9c:79:6c:46:d5:b4:15: - b3:96:6e:7e:9b:0c:96:3a:b8:52:2d:3f:d6:5b:e1:fb:08:c2: - 84:fe:24:a8:a3:89:da:ac:6a:e1:18:2a:b1:a8:43:61:5b:d3: - 1f:dc:3b:8d:76:f2:2d:e8:8d:75:df:17:33:6c:3d:53:fb:7b: - cb:41:5f:ff:dc:a2:d0:61:38:e1:96:b8:ac:5d:8b:37:d7:75: - d5:33:c0:99:11:ae:9d:41:c1:72:75:84:be:02:41:42:5f:67: - 24:48:94:d1:9b:27:be:07:3f:b9:b8:4f:81:74:51:e1:7a:b7: - ed:9d:23:e2:be:e0:d5:28:04:13:3c:31:03:9e:dd:7a:6c:8f: - c6:07:18:c6:7f:de:47:8e:3f:28:9e:04:06:cf:a5:54:34:77: - bd:ec:89:9b:e9:17:43:df:5b:db:5f:fe:8e:1e:57:a2:cd:40: - 9d:7e:62:22:da:de:18:27 ------BEGIN CERTIFICATE----- -MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw -TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh -cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4 -WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu -ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY -MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc -h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+ -0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U -A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW -T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH -B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC -B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv -KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn -OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn -jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw -qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI -rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV -HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq -hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL -ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ -3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK -NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5 -ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur -TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC -jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc -oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq -4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA -mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d -emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc= ------END CERTIFICATE----- diff --git a/certs/Starfield Secure Certificate Authority - G2.pem b/certs/Starfield Secure Certificate Authority - G2.pem deleted file mode 100644 index 7772e6b..0000000 --- a/certs/Starfield Secure Certificate Authority - G2.pem +++ /dev/null @@ -1,179 +0,0 @@ -Certificate: - Data: - Version: 3 (0x2) - Serial Number: 7 (0x7) - Signature Algorithm: sha256WithRSAEncryption - Issuer: C = US, ST = Arizona, L = Scottsdale, O = "Starfield Technologies, Inc.", CN = Starfield Root Certificate Authority - G2 - Validity - Not Before: May 3 07:00:00 2011 GMT - Not After : May 3 07:00:00 2031 GMT - Subject: C = US, ST = Arizona, L = Scottsdale, O = "Starfield Technologies, Inc.", OU = http://certs.starfieldtech.com/repository/, CN = Starfield Secure Certificate Authority - G2 - Subject Public Key Info: - Public Key Algorithm: rsaEncryption - RSA Public-Key: (2048 bit) - Modulus: - 00:e5:90:66:4b:ec:f9:46:71:a9:20:83:be:e9:6c: - bf:4a:c9:48:69:81:75:4e:6d:24:f6:cb:17:13:f8: - b0:71:59:84:7a:6b:2b:85:a4:34:b5:16:e5:cb:cc: - e9:41:70:2c:a4:2e:d6:fa:32:7d:e1:a8:de:94:10: - ac:31:c1:c0:d8:6a:ff:59:27:ab:76:d6:fc:0b:74: - 6b:b8:a7:ae:3f:c4:54:f4:b4:31:44:dd:93:56:8c: - a4:4c:5e:9b:89:cb:24:83:9b:e2:57:7d:b7:d8:12: - 1f:c9:85:6d:f4:d1:80:f1:50:9b:87:ae:d4:0b:10: - 05:fb:27:ba:28:6d:17:e9:0e:d6:4d:b9:39:55:06: - ff:0a:24:05:7e:2f:c6:1d:72:6c:d4:8b:29:8c:57: - 7d:da:d9:eb:66:1a:d3:4f:a7:df:7f:52:c4:30:c5: - a5:c9:0e:02:c5:53:bf:77:38:68:06:24:c3:66:c8: - 37:7e:30:1e:45:71:23:35:ff:90:d8:2a:9d:8d:e7: - b0:92:4d:3c:7f:2a:0a:93:dc:cd:16:46:65:f7:60: - 84:8b:76:4b:91:27:73:14:92:e0:ea:ee:8f:16:ea: - 8d:0e:3e:76:17:bf:7d:89:80:80:44:43:e7:2d:e0: - 43:09:75:da:36:e8:ad:db:89:3a:f5:5d:12:8e:23: - 04:83 - Exponent: 65537 (0x10001) - X509v3 extensions: - X509v3 Basic Constraints: critical - CA:TRUE - X509v3 Key Usage: critical - Certificate Sign, CRL Sign - X509v3 Subject Key Identifier: - 25:45:81:68:50:26:38:3D:3B:2D:2C:BE:CD:6A:D9:B6:3D:B3:66:63 - X509v3 Authority Key Identifier: - keyid:7C:0C:32:1F:A7:D9:30:7F:C4:7D:68:A3:62:A8:A1:CE:AB:07:5B:27 - - Authority Information Access: - OCSP - URI:http://ocsp.starfieldtech.com/ - - X509v3 CRL Distribution Points: - - Full Name: - URI:http://crl.starfieldtech.com/sfroot-g2.crl - - X509v3 Certificate Policies: - Policy: X509v3 Any Policy - CPS: https://certs.starfieldtech.com/repository/ - - Signature Algorithm: sha256WithRSAEncryption - 56:65:ca:fe:f3:3f:0a:a8:93:8b:18:c7:de:43:69:13:34:20: - be:4e:5f:78:a8:6b:9c:db:6a:4d:41:db:c1:13:ec:dc:31:00: - 22:5e:f7:00:9e:0c:e0:34:65:34:f9:b1:3a:4e:48:c8:12:81: - 88:5c:5b:3e:08:53:7a:f7:1a:64:df:b8:50:61:cc:53:51:40: - 29:4b:c2:f4:ae:3a:5f:e4:ca:ad:26:cc:4e:61:43:e5:fd:57: - a6:37:70:ce:43:2b:b0:94:c3:92:e9:e1:5f:aa:10:49:b7:69: - e4:e0:d0:1f:64:a4:2b:cd:1f:6f:a0:f8:84:24:18:ce:79:3d: - a9:91:bf:54:18:13:89:99:54:11:0d:55:c5:26:0b:79:4f:5a: - 1c:6e:f9:63:db:14:80:a4:07:ab:fa:b2:a5:b9:88:dd:91:fe: - 65:3b:a4:a3:79:be:89:4d:e1:d0:b0:f4:c8:17:0c:0a:96:14: - 7c:09:b7:6c:e1:c2:d8:55:d4:18:a0:aa:41:69:70:24:a3:b9: - ef:e9:5a:dc:3e:eb:94:4a:f0:b7:de:5f:0e:76:fa:fb:fb:69: - 03:45:40:50:ee:72:0c:a4:12:86:81:cd:13:d1:4e:c4:3c:ca: - 4e:0d:d2:26:f1:00:b7:b4:a6:a2:e1:6e:7a:81:fd:30:ac:7a: - 1f:c7:59:7b ------BEGIN CERTIFICATE----- -MIIFADCCA+igAwIBAgIBBzANBgkqhkiG9w0BAQsFADCBjzELMAkGA1UEBhMCVVMx -EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoT -HFN0YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xMjAwBgNVBAMTKVN0YXJmaWVs -ZCBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTExMDUwMzA3MDAw -MFoXDTMxMDUwMzA3MDAwMFowgcYxCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6 -b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxTdGFyZmllbGQgVGVj -aG5vbG9naWVzLCBJbmMuMTMwMQYDVQQLEypodHRwOi8vY2VydHMuc3RhcmZpZWxk -dGVjaC5jb20vcmVwb3NpdG9yeS8xNDAyBgNVBAMTK1N0YXJmaWVsZCBTZWN1cmUg -Q2VydGlmaWNhdGUgQXV0aG9yaXR5IC0gRzIwggEiMA0GCSqGSIb3DQEBAQUAA4IB -DwAwggEKAoIBAQDlkGZL7PlGcakgg77pbL9KyUhpgXVObST2yxcT+LBxWYR6ayuF -pDS1FuXLzOlBcCykLtb6Mn3hqN6UEKwxwcDYav9ZJ6t21vwLdGu4p64/xFT0tDFE -3ZNWjKRMXpuJyySDm+JXfbfYEh/JhW300YDxUJuHrtQLEAX7J7oobRfpDtZNuTlV -Bv8KJAV+L8YdcmzUiymMV33a2etmGtNPp99/UsQwxaXJDgLFU793OGgGJMNmyDd+ -MB5FcSM1/5DYKp2N57CSTTx/KgqT3M0WRmX3YISLdkuRJ3MUkuDq7o8W6o0OPnYX -v32JgIBEQ+ct4EMJddo26K3biTr1XRKOIwSDAgMBAAGjggEsMIIBKDAPBgNVHRMB -Af8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUJUWBaFAmOD07LSy+ -zWrZtj2zZmMwHwYDVR0jBBgwFoAUfAwyH6fZMH/EfWijYqihzqsHWycwOgYIKwYB -BQUHAQEELjAsMCoGCCsGAQUFBzABhh5odHRwOi8vb2NzcC5zdGFyZmllbGR0ZWNo -LmNvbS8wOwYDVR0fBDQwMjAwoC6gLIYqaHR0cDovL2NybC5zdGFyZmllbGR0ZWNo -LmNvbS9zZnJvb3QtZzIuY3JsMEwGA1UdIARFMEMwQQYEVR0gADA5MDcGCCsGAQUF -BwIBFitodHRwczovL2NlcnRzLnN0YXJmaWVsZHRlY2guY29tL3JlcG9zaXRvcnkv -MA0GCSqGSIb3DQEBCwUAA4IBAQBWZcr+8z8KqJOLGMfeQ2kTNCC+Tl94qGuc22pN -QdvBE+zcMQAiXvcAngzgNGU0+bE6TkjIEoGIXFs+CFN69xpk37hQYcxTUUApS8L0 -rjpf5MqtJsxOYUPl/VemN3DOQyuwlMOS6eFfqhBJt2nk4NAfZKQrzR9voPiEJBjO -eT2pkb9UGBOJmVQRDVXFJgt5T1ocbvlj2xSApAer+rKluYjdkf5lO6Sjeb6JTeHQ -sPTIFwwKlhR8Cbds4cLYVdQYoKpBaXAko7nv6VrcPuuUSvC33l8Odvr7+2kDRUBQ -7nIMpBKGgc0T0U7EPMpODdIm8QC3tKai4W56gf0wrHofx1l7 ------END CERTIFICATE----- -Certificate: - Data: - Version: 3 (0x2) - Serial Number: 0 (0x0) - Signature Algorithm: sha256WithRSAEncryption - Issuer: C = US, ST = Arizona, L = Scottsdale, O = "Starfield Technologies, Inc.", CN = Starfield Root Certificate Authority - G2 - Validity - Not Before: Sep 1 00:00:00 2009 GMT - Not After : Dec 31 23:59:59 2037 GMT - Subject: C = US, ST = Arizona, L = Scottsdale, O = "Starfield Technologies, Inc.", CN = Starfield Root Certificate Authority - G2 - Subject Public Key Info: - Public Key Algorithm: rsaEncryption - RSA Public-Key: (2048 bit) - Modulus: - 00:bd:ed:c1:03:fc:f6:8f:fc:02:b1:6f:5b:9f:48: - d9:9d:79:e2:a2:b7:03:61:56:18:c3:47:b6:d7:ca: - 3d:35:2e:89:43:f7:a1:69:9b:de:8a:1a:fd:13:20: - 9c:b4:49:77:32:29:56:fd:b9:ec:8c:dd:22:fa:72: - dc:27:61:97:ee:f6:5a:84:ec:6e:19:b9:89:2c:dc: - 84:5b:d5:74:fb:6b:5f:c5:89:a5:10:52:89:46:55: - f4:b8:75:1c:e6:7f:e4:54:ae:4b:f8:55:72:57:02: - 19:f8:17:71:59:eb:1e:28:07:74:c5:9d:48:be:6c: - b4:f4:a4:b0:f3:64:37:79:92:c0:ec:46:5e:7f:e1: - 6d:53:4c:62:af:cd:1f:0b:63:bb:3a:9d:fb:fc:79: - 00:98:61:74:cf:26:82:40:63:f3:b2:72:6a:19:0d: - 99:ca:d4:0e:75:cc:37:fb:8b:89:c1:59:f1:62:7f: - 5f:b3:5f:65:30:f8:a7:b7:4d:76:5a:1e:76:5e:34: - c0:e8:96:56:99:8a:b3:f0:7f:a4:cd:bd:dc:32:31: - 7c:91:cf:e0:5f:11:f8:6b:aa:49:5c:d1:99:94:d1: - a2:e3:63:5b:09:76:b5:56:62:e1:4b:74:1d:96:d4: - 26:d4:08:04:59:d0:98:0e:0e:e6:de:fc:c3:ec:1f: - 90:f1 - Exponent: 65537 (0x10001) - X509v3 extensions: - X509v3 Basic Constraints: critical - CA:TRUE - X509v3 Key Usage: critical - Certificate Sign, CRL Sign - X509v3 Subject Key Identifier: - 7C:0C:32:1F:A7:D9:30:7F:C4:7D:68:A3:62:A8:A1:CE:AB:07:5B:27 - Signature Algorithm: sha256WithRSAEncryption - 11:59:fa:25:4f:03:6f:94:99:3b:9a:1f:82:85:39:d4:76:05: - 94:5e:e1:28:93:6d:62:5d:09:c2:a0:a8:d4:b0:75:38:f1:34: - 6a:9d:e4:9f:8a:86:26:51:e6:2c:d1:c6:2d:6e:95:20:4a:92: - 01:ec:b8:8a:67:7b:31:e2:67:2e:8c:95:03:26:2e:43:9d:4a: - 31:f6:0e:b5:0c:bb:b7:e2:37:7f:22:ba:00:a3:0e:7b:52:fb: - 6b:bb:3b:c4:d3:79:51:4e:cd:90:f4:67:07:19:c8:3c:46:7a: - 0d:01:7d:c5:58:e7:6d:e6:85:30:17:9a:24:c4:10:e0:04:f7: - e0:f2:7f:d4:aa:0a:ff:42:1d:37:ed:94:e5:64:59:12:20:77: - 38:d3:32:3e:38:81:75:96:73:fa:68:8f:b1:cb:ce:1f:c5:ec: - fa:9c:7e:cf:7e:b1:f1:07:2d:b6:fc:bf:ca:a4:bf:d0:97:05: - 4a:bc:ea:18:28:02:90:bd:54:78:09:21:71:d3:d1:7d:1d:d9: - 16:b0:a9:61:3d:d0:0a:00:22:fc:c7:7b:cb:09:64:45:0b:3b: - 40:81:f7:7d:7c:32:f5:98:ca:58:8e:7d:2a:ee:90:59:73:64: - f9:36:74:5e:25:a1:f5:66:05:2e:7f:39:15:a9:2a:fb:50:8b: - 8e:85:69:f4 ------BEGIN CERTIFICATE----- -MIID3TCCAsWgAwIBAgIBADANBgkqhkiG9w0BAQsFADCBjzELMAkGA1UEBhMCVVMx -EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoT -HFN0YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xMjAwBgNVBAMTKVN0YXJmaWVs -ZCBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5MDkwMTAwMDAw -MFoXDTM3MTIzMTIzNTk1OVowgY8xCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6 -b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxTdGFyZmllbGQgVGVj -aG5vbG9naWVzLCBJbmMuMTIwMAYDVQQDEylTdGFyZmllbGQgUm9vdCBDZXJ0aWZp -Y2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC -ggEBAL3twQP89o/8ArFvW59I2Z154qK3A2FWGMNHttfKPTUuiUP3oWmb3ooa/RMg -nLRJdzIpVv257IzdIvpy3Cdhl+72WoTsbhm5iSzchFvVdPtrX8WJpRBSiUZV9Lh1 -HOZ/5FSuS/hVclcCGfgXcVnrHigHdMWdSL5stPSksPNkN3mSwOxGXn/hbVNMYq/N -Hwtjuzqd+/x5AJhhdM8mgkBj87JyahkNmcrUDnXMN/uLicFZ8WJ/X7NfZTD4p7dN -dloedl40wOiWVpmKs/B/pM293DIxfJHP4F8R+GuqSVzRmZTRouNjWwl2tVZi4Ut0 -HZbUJtQIBFnQmA4O5t78w+wfkPECAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAO -BgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFHwMMh+n2TB/xH1oo2Kooc6rB1snMA0G -CSqGSIb3DQEBCwUAA4IBAQARWfolTwNvlJk7mh+ChTnUdgWUXuEok21iXQnCoKjU -sHU48TRqneSfioYmUeYs0cYtbpUgSpIB7LiKZ3sx4mcujJUDJi5DnUox9g61DLu3 -4jd/IroAow57UvtruzvE03lRTs2Q9GcHGcg8RnoNAX3FWOdt5oUwF5okxBDgBPfg -8n/Uqgr/Qh037ZTlZFkSIHc40zI+OIF1lnP6aI+xy84fxez6nH7PfrHxBy22/L/K -pL/QlwVKvOoYKAKQvVR4CSFx09F9HdkWsKlhPdAKACL8x3vLCWRFCztAgfd9fDL1 -mMpYjn0q7pBZc2T5NnReJaH1ZgUufzkVqSr7UIuOhWn0 ------END CERTIFICATE----- diff --git a/certs/Starfield-Root-Certificate-Authority-G2.pem b/certs/Starfield-Root-Certificate-Authority-G2.pem new file mode 100644 index 0000000..4e6774d --- /dev/null +++ b/certs/Starfield-Root-Certificate-Authority-G2.pem @@ -0,0 +1,30 @@ +# Issuer: CN=Starfield Root Certificate Authority - G2 O=Starfield Technologies, Inc. +# Subject: CN=Starfield Root Certificate Authority - G2 O=Starfield Technologies, Inc. +# Label: "Starfield Root Certificate Authority - G2" +# Serial: 0 +# MD5 Fingerprint: d6:39:81:c6:52:7e:96:69:fc:fc:ca:66:ed:05:f2:96 +# SHA1 Fingerprint: b5:1c:06:7c:ee:2b:0c:3d:f8:55:ab:2d:92:f4:fe:39:d4:e7:0f:0e +# SHA256 Fingerprint: 2c:e1:cb:0b:f9:d2:f9:e1:02:99:3f:be:21:51:52:c3:b2:dd:0c:ab:de:1c:68:e5:31:9b:83:91:54:db:b7:f5 +-----BEGIN CERTIFICATE----- +MIID3TCCAsWgAwIBAgIBADANBgkqhkiG9w0BAQsFADCBjzELMAkGA1UEBhMCVVMx +EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoT +HFN0YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xMjAwBgNVBAMTKVN0YXJmaWVs +ZCBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5MDkwMTAwMDAw +MFoXDTM3MTIzMTIzNTk1OVowgY8xCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6 +b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxTdGFyZmllbGQgVGVj +aG5vbG9naWVzLCBJbmMuMTIwMAYDVQQDEylTdGFyZmllbGQgUm9vdCBDZXJ0aWZp +Y2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC +ggEBAL3twQP89o/8ArFvW59I2Z154qK3A2FWGMNHttfKPTUuiUP3oWmb3ooa/RMg +nLRJdzIpVv257IzdIvpy3Cdhl+72WoTsbhm5iSzchFvVdPtrX8WJpRBSiUZV9Lh1 +HOZ/5FSuS/hVclcCGfgXcVnrHigHdMWdSL5stPSksPNkN3mSwOxGXn/hbVNMYq/N +Hwtjuzqd+/x5AJhhdM8mgkBj87JyahkNmcrUDnXMN/uLicFZ8WJ/X7NfZTD4p7dN +dloedl40wOiWVpmKs/B/pM293DIxfJHP4F8R+GuqSVzRmZTRouNjWwl2tVZi4Ut0 +HZbUJtQIBFnQmA4O5t78w+wfkPECAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAO +BgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFHwMMh+n2TB/xH1oo2Kooc6rB1snMA0G +CSqGSIb3DQEBCwUAA4IBAQARWfolTwNvlJk7mh+ChTnUdgWUXuEok21iXQnCoKjU +sHU48TRqneSfioYmUeYs0cYtbpUgSpIB7LiKZ3sx4mcujJUDJi5DnUox9g61DLu3 +4jd/IroAow57UvtruzvE03lRTs2Q9GcHGcg8RnoNAX3FWOdt5oUwF5okxBDgBPfg +8n/Uqgr/Qh037ZTlZFkSIHc40zI+OIF1lnP6aI+xy84fxez6nH7PfrHxBy22/L/K +pL/QlwVKvOoYKAKQvVR4CSFx09F9HdkWsKlhPdAKACL8x3vLCWRFCztAgfd9fDL1 +mMpYjn0q7pBZc2T5NnReJaH1ZgUufzkVqSr7UIuOhWn0 +-----END CERTIFICATE----- diff --git a/certs/USERTrust-RSA-Certification-Authority.pem b/certs/USERTrust-RSA-Certification-Authority.pem new file mode 100644 index 0000000..0fbeef6 --- /dev/null +++ b/certs/USERTrust-RSA-Certification-Authority.pem @@ -0,0 +1,41 @@ +# Issuer: CN=USERTrust RSA Certification Authority O=The USERTRUST Network +# Subject: CN=USERTrust RSA Certification Authority O=The USERTRUST Network +# Label: "USERTrust RSA Certification Authority" +# Serial: 2645093764781058787591871645665788717 +# MD5 Fingerprint: 1b:fe:69:d1:91:b7:19:33:a3:72:a8:0f:e1:55:e5:b5 +# SHA1 Fingerprint: 2b:8f:1b:57:33:0d:bb:a2:d0:7a:6c:51:f7:0e:e9:0d:da:b9:ad:8e +# SHA256 Fingerprint: e7:93:c9:b0:2f:d8:aa:13:e2:1c:31:22:8a:cc:b0:81:19:64:3b:74:9c:89:89:64:b1:74:6d:46:c3:d4:cb:d2 +-----BEGIN CERTIFICATE----- +MIIF3jCCA8agAwIBAgIQAf1tMPyjylGoG7xkDjUDLTANBgkqhkiG9w0BAQwFADCB +iDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0pl +cnNleSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNV +BAMTJVVTRVJUcnVzdCBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAw +MjAxMDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBiDELMAkGA1UEBhMCVVMxEzARBgNV +BAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQKExVU +aGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBSU0EgQ2Vy +dGlmaWNhdGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK +AoICAQCAEmUXNg7D2wiz0KxXDXbtzSfTTK1Qg2HiqiBNCS1kCdzOiZ/MPans9s/B +3PHTsdZ7NygRK0faOca8Ohm0X6a9fZ2jY0K2dvKpOyuR+OJv0OwWIJAJPuLodMkY +tJHUYmTbf6MG8YgYapAiPLz+E/CHFHv25B+O1ORRxhFnRghRy4YUVD+8M/5+bJz/ +Fp0YvVGONaanZshyZ9shZrHUm3gDwFA66Mzw3LyeTP6vBZY1H1dat//O+T23LLb2 +VN3I5xI6Ta5MirdcmrS3ID3KfyI0rn47aGYBROcBTkZTmzNg95S+UzeQc0PzMsNT +79uq/nROacdrjGCT3sTHDN/hMq7MkztReJVni+49Vv4M0GkPGw/zJSZrM233bkf6 +c0Plfg6lZrEpfDKEY1WJxA3Bk1QwGROs0303p+tdOmw1XNtB1xLaqUkL39iAigmT +Yo61Zs8liM2EuLE/pDkP2QKe6xJMlXzzawWpXhaDzLhn4ugTncxbgtNMs+1b/97l +c6wjOy0AvzVVdAlJ2ElYGn+SNuZRkg7zJn0cTRe8yexDJtC/QV9AqURE9JnnV4ee +UB9XVKg+/XRjL7FQZQnmWEIuQxpMtPAlR1n6BB6T1CZGSlCBst6+eLf8ZxXhyVeE +Hg9j1uliutZfVS7qXMYoCAQlObgOK6nyTJccBz8NUvXt7y+CDwIDAQABo0IwQDAd +BgNVHQ4EFgQUU3m/WqorSs9UgOHYm8Cd8rIDZsswDgYDVR0PAQH/BAQDAgEGMA8G +A1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEMBQADggIBAFzUfA3P9wF9QZllDHPF +Up/L+M+ZBn8b2kMVn54CVVeWFPFSPCeHlCjtHzoBN6J2/FNQwISbxmtOuowhT6KO +VWKR82kV2LyI48SqC/3vqOlLVSoGIG1VeCkZ7l8wXEskEVX/JJpuXior7gtNn3/3 +ATiUFJVDBwn7YKnuHKsSjKCaXqeYalltiz8I+8jRRa8YFWSQEg9zKC7F4iRO/Fjs +8PRF/iKz6y+O0tlFYQXBl2+odnKPi4w2r78NBc5xjeambx9spnFixdjQg3IM8WcR +iQycE0xyNN+81XHfqnHd4blsjDwSXWXavVcStkNr/+XeTWYRUc+ZruwXtuhxkYze +Sf7dNXGiFSeUHM9h4ya7b6NnJSFd5t0dCy5oGzuCr+yDZ4XUmFF0sbmZgIn/f3gZ +XHlKYC6SQK5MNyosycdiyA5d9zZbyuAlJQG03RoHnHcAP9Dc1ew91Pq7P8yF1m9/ +qS3fuQL39ZeatTXaw2ewh0qpKJ4jjv9cJ2vhsE/zB+4ALtRZh8tSQZXq9EfX7mRB +VXyNWQKV3WKdwrnuWih0hKWbt5DHDAff9Yk2dDLWKMGwsAvgnEzDHNb842m1R0aB +L6KCq9NjRHDEjf8tM7qtj3u1cIiuPhnPQCjY/MiQu12ZIvVS5ljFH4gxQ+6IHdfG +jjxDah2nGN59PRbxYvnKkKj9 +-----END CERTIFICATE----- diff --git a/check-certificates.rsc b/check-certificates.rsc index 3410d4d..be8e4df 100644 --- a/check-certificates.rsc +++ b/check-certificates.rsc @@ -1,183 +1,242 @@ #!rsc by RouterOS # RouterOS script: check-certificates -# Copyright (c) 2013-2023 Christian Hesse -# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md +# Copyright (c) 2013-2025 Christian Hesse +# https://rsc.eworm.de/COPYING.md +# +# requires RouterOS, version=7.15 +# requires device-mode, fetch # # check for certificate validity -# https://git.eworm.de/cgit/routeros-scripts/about/doc/check-certificates.md +# https://rsc.eworm.de/doc/check-certificates.md -:local 0 "check-certificates"; :global GlobalFunctionsReady; :while ($GlobalFunctionsReady != true) do={ :delay 500ms; } -:global CertRenewTime; -:global CertRenewUrl; -:global CertWarnTime; -:global Identity; - -:global CertificateAvailable -:global EscapeForRegEx; -:global IfThenElse; -:global LogPrintExit2; -:global ParseKeyValueStore; -:global ScriptLock; -:global SendNotification2; -:global SymbolForNotification; -:global UrlEncode; -:global WaitFullyConnected; - -:local CheckCertificatesDownloadImport do={ - :local Name [ :tostr $1 ]; +:local ExitOK false; +:do { + :local ScriptName [ :jobname ]; + :global CertRenewTime; :global CertRenewUrl; - :global CertRenewPass; + :global CertWarnTime; + :global Identity; - :global CertificateNameByCN; + :global CertificateAvailable :global EscapeForRegEx; - :global LogPrintExit2; + :global IfThenElse; + :global LogPrint; + :global ParseKeyValueStore; + :global ScriptLock; + :global SendNotification2; + :global SymbolForNotification; :global UrlEncode; - :global WaitForFile; + :global WaitFullyConnected; - :local Return false; + :local CheckCertificatesDownloadImport do={ + :local ScriptName [ :tostr $1 ]; + :local CertName [ :tostr $2 ]; + :local FetchName [ :tostr $3 ]; + + :global CertRenewUrl; + :global CertRenewPass; + + :global CertificateNameByCN; + :global EscapeForRegEx; + :global FetchUserAgentStr; + :global LogPrint; + :global RmFile; + :global UrlEncode; + :global WaitForFile; + + :foreach Type in={ "p12"; "pem" } do={ + :local CertFileName ([ $UrlEncode $FetchName ] . "." . $Type); + $LogPrint debug $ScriptName ("Trying type '" . $Type . "' for '" . $CertName . \ + "' (file '" . $CertFileName . "')..."); + + :do { + /tool/fetch check-certificate=yes-without-crl http-header-field=({ [ $FetchUserAgentStr $ScriptName ] }) \ + ($CertRenewUrl . $CertFileName) dst-path=$CertFileName as-value; + $WaitForFile $CertFileName; + + :local DecryptionFailed true; + :foreach I,PassPhrase in=$CertRenewPass do={ + :do { + $LogPrint debug $ScriptName ("Trying " . $I . ". passphrase... "); + :local Result [ /certificate/import file-name=$CertFileName passphrase=$PassPhrase as-value ]; + :if ($Result->"decryption-failures" = 0) do={ + $LogPrint debug $ScriptName ("Success!"); + :set DecryptionFailed false; + } + } on-error={ } + } + $RmFile $CertFileName; + + :if ($DecryptionFailed = true) do={ + $LogPrint warning $ScriptName ("Decryption failed for certificate file '" . $CertFileName . "'."); + } + + :foreach CertInChain in=[ /certificate/find where common-name!=$CertName !private-key \ + name~("^" . [ $EscapeForRegEx $CertFileName ] . "_[0-9]+\$") \ + !(subject-alt-name~("(^|\\W)(DNS|IP):" . [ $EscapeForRegEx $CertName ] . "(\\W|\$)")) \ + !(common-name=[]) ] do={ + $CertificateNameByCN [ /certificate/get $CertInChain common-name ]; + } + + :return true; + } on-error={ + $LogPrint debug $ScriptName ("Could not download certificate file '" . $CertFileName . "'."); + } + } + + :return false; + } + + :local FormatInfo do={ + :local Cert $1; + + :global FormatLine; + :global FormatMultiLines; + :global IfThenElse; + + :local FormatExpire do={ + :global CharacterReplace; + :return [ $CharacterReplace [ $CharacterReplace [ :tostr $1 ] "w" "w " ] "d" "d " ]; + } + + :local FormatCertChain do={ + :local Cert $1; + + :global EitherOr; + :global ParseKeyValueStore; + + :local CertVal [ /certificate/get $Cert ]; + + :if ([ :typeof ($CertVal->"issuer") ] = "nothing") do={ + :return "self-signed"; + } + + :local Return ""; + :for I from=0 to=5 do={ + :set Return ($Return . [ $EitherOr ([ $ParseKeyValueStore ($CertVal->"issuer") ]->"CN") \ + ([ $ParseKeyValueStore (($CertVal->"issuer")->0) ]->"CN") ]); + :set CertVal [ /certificate/get [ find where skid=($CertVal->"akid") ] ]; + :if (($CertVal->"akid") = "" || ($CertVal->"akid") = ($CertVal->"skid")) do={ + :return $Return; + } + :set Return ($Return . " -> "); + } + :return ($Return . "..."); + } + + :local CertVal [ /certificate/get $Cert ]; + + :return ( \ + [ $FormatLine "Name" ($CertVal->"name") ] . "\n" . \ + [ $IfThenElse ([ :len ($CertVal->"common-name") ] > 0) ([ $FormatLine "CommonName" ($CertVal->"common-name") ] . "\n") ] . \ + [ $IfThenElse ([ :len ($CertVal->"subject-alt-name") ] > 0) ([ $FormatMultiLines "SubjectAltNames" ($CertVal->"subject-alt-name") ] . "\n") ] . \ + [ $FormatLine "Private key" [ $IfThenElse (($CertVal->"private-key") = true) "available" "missing" ] ] . "\n" . \ + [ $FormatLine "Fingerprint" ($CertVal->"fingerprint") ] . "\n" . \ + [ $IfThenElse ([ :len ($CertVal->"ca") ] > 0) [ $FormatLine "Issuer" ($CertVal->"ca") ] [ $FormatLine "Issuer chain" [ $FormatCertChain $Cert ] ] ] . "\n" . \ + "Validity:\n" . \ + [ $FormatLine " from" ($CertVal->"invalid-before") ] . "\n" . \ + [ $FormatLine " to" ($CertVal->"invalid-after") ] . "\n" . \ + [ $FormatLine "Expires in" [ $IfThenElse (($CertVal->"expired") = true) "expired" [ $FormatExpire ($CertVal->"expires-after") ] ] ]); + } + + :if ([ $ScriptLock $ScriptName ] = false) do={ + :set ExitOK true; + :error false; + } + $WaitFullyConnected; + + :foreach Cert in=[ /certificate/find where !revoked !ca !scep-url expires-after<$CertRenewTime ] do={ + :local CertVal [ /certificate/get $Cert ]; + :local LastName; + :local FetchName; - :foreach Type in={ ".pem"; ".p12" } do={ - :local CertFileName ([ $UrlEncode $Name ] . $Type); :do { - /tool/fetch check-certificate=yes-without-crl \ - ($CertRenewUrl . $CertFileName) dst-path=$CertFileName as-value; - $WaitForFile $CertFileName; + :if ([ :len $CertRenewUrl ] = 0) do={ + $LogPrint info $ScriptName ("No CertRenewUrl given."); + :error false; + } + $LogPrint info $ScriptName ("Attempting to renew certificate '" . ($CertVal->"name") . "'."); - :local DecryptionFailed true; - :foreach PassPhrase in=$CertRenewPass do={ - :local Result [ /certificate/import file-name=$CertFileName passphrase=$PassPhrase as-value ]; - :if ($Result->"decryption-failures" = 0) do={ - :set DecryptionFailed false; + :local ImportSuccess false; + :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 ] ]; + :set FetchName $LastName; + :set ImportSuccess [ $CheckCertificatesDownloadImport $ScriptName $LastName $FetchName ]; + :if ($ImportSuccess = false && [ :pick $LastName 0 2 ] = "*.") do={ + :set FetchName ("star." . [ :pick $LastName 2 [ :len $LastName ] ]); + :set ImportSuccess [ $CheckCertificatesDownloadImport $ScriptName $LastName $FetchName ]; + } } } - /file/remove [ find where name=$CertFileName ]; + :if ($ImportSuccess = false) do={ :error false; } - :if ($DecryptionFailed = true) do={ - $LogPrintExit2 warning $0 ("Decryption failed for certificate file " . $CertFileName) false; + :if ([ :len ($CertVal->"fingerprint") ] > 0 && $CertVal->"fingerprint" != [ /certificate/get $Cert fingerprint ]) do={ + $LogPrint debug $ScriptName ("Certificate '" . $CertVal->"name" . "' was updated in place."); + :set CertVal [ /certificate/get $Cert ]; + } else={ + $LogPrint debug $ScriptName ("Certificate '" . $CertVal->"name" . "' was not updated, but replaced."); + + :local CertNew [ /certificate/find where name~("^" . [ $EscapeForRegEx [ $UrlEncode $FetchName ] ] . "\\.(p12|pem)_[0-9]+\$") \ + (common-name=($CertVal->"common-name") or subject-alt-name~("(^|\\W)(DNS|IP):" . [ $EscapeForRegEx $LastName ] . "(\\W|\$)")) \ + fingerprint!=[ :tostr ($CertVal->"fingerprint") ] expires-after>$CertRenewTime ]; + :local CertNewVal [ /certificate/get $CertNew ]; + + :if ([ $CertificateAvailable ([ $ParseKeyValueStore ($CertNewVal->"issuer") ]->"CN") ] = false) do={ + $LogPrint warning $ScriptName ("The certificate chain is not available!"); + } + + :if (($CertVal->"private-key") = true && ($CertVal->"private-key") != ($CertNewVal->"private-key")) do={ + /certificate/remove $CertNew; + $LogPrint warning $ScriptName ("Old certificate '" . ($CertVal->"name") . "' has a private key, new certificate does not. Aborting renew."); + :error false; + } + + /ip/service/set certificate=($CertNewVal->"name") [ find where certificate=($CertVal->"name") ]; + + /ip/ipsec/identity/set certificate=($CertNewVal->"name") [ find where certificate=($CertVal->"name") ]; + /ip/ipsec/identity/set remote-certificate=($CertNewVal->"name") [ find where remote-certificate=($CertVal->"name") ]; + + /ip/hotspot/profile/set ssl-certificate=($CertNewVal->"name") [ find where ssl-certificate=($CertVal->"name") ]; + + /certificate/remove $Cert; + /certificate/set $CertNew name=($CertVal->"name"); + :set Cert $CertNew; + :set CertVal [ /certificate/get $CertNew ]; } - :foreach CertInChain in=[ /certificate/find where name~("^" . [ $EscapeForRegEx $CertFileName ] . "_[0-9]+\$") \ - common-name!=$Name !(subject-alt-name~("(^|\\W)(DNS|IP):" . [ $EscapeForRegEx $Name ] . "(\\W|\$)")) !(common-name=[]) ] do={ - $CertificateNameByCN [ /certificate/get $CertInChain common-name ]; - } - - :set Return true; + $SendNotification2 ({ origin=$ScriptName; silent=true; \ + subject=([ $SymbolForNotification "lock-with-ink-pen" ] . "Certificate renewed: " . ($CertVal->"name")); \ + message=("A certificate on " . $Identity . " has been renewed.\n\n" . [ $FormatInfo $Cert ]) }); + $LogPrint info $ScriptName ("The certificate '" . ($CertVal->"name") . "' has been renewed."); } on-error={ - $LogPrintExit2 debug $0 ("Could not download certificate file " . $CertFileName) false; + $LogPrint debug $ScriptName ("Could not renew certificate '" . ($CertVal->"name") . "'."); } } - :return $Return; -} + :foreach Cert in=[ /certificate/find where !revoked !scep-url !(expires-after=[]) \ + expires-after<$CertWarnTime !(fingerprint=[]) ] do={ + :local CertVal [ /certificate/get $Cert ]; -:local FormatInfo do={ - :local CertVal $1; + :if ([ :len [ /certificate/scep-server/find where ca-cert=($CertVal->"ca") ] ] > 0) do={ + $LogPrint debug $ScriptName ("Certificate '" . ($CertVal->"name") . "' is handled by SCEP, skipping."); + } else={ + :local State [ $IfThenElse (($CertVal->"expired") = true) "expired" "is about to expire" ]; - :global FormatLine; - :global FormatMultiLines; - :global IfThenElse; - :global ParseKeyValueStore; - - :local FormatExpire do={ - :global CharacterReplace; - :return [ $CharacterReplace [ $CharacterReplace [ :tostr $1 ] "w" "w " ] "d" "d " ]; - } - - :return ( \ - [ $FormatLine "Name" ($CertVal->"name") ] . "\n" . \ - [ $IfThenElse ([ :len ($CertVal->"common-name") ] > 0) ([ $FormatLine "CommonName" ($CertVal->"common-name") ] . "\n") ] . \ - [ $IfThenElse ([ :len ($CertVal->"subject-alt-name") ] > 0) ([ $FormatMultiLines "SubjectAltNames" ($CertVal->"subject-alt-name") ] . "\n") ] . \ - [ $FormatLine "Private key" [ $IfThenElse (($CertVal->"private-key") = true) "available" "missing" ] ] . "\n" . \ - [ $FormatLine "Fingerprint" ($CertVal->"fingerprint") ] . "\n" . \ - [ $FormatLine "Issuer" ($CertVal->"ca" . ([ $ParseKeyValueStore ($CertVal->"issuer") ]->"CN")) ] . "\n" . \ - "Validity:\n" . \ - [ $FormatLine " from" ($CertVal->"invalid-before") ] . "\n" . \ - [ $FormatLine " to" ($CertVal->"invalid-after") ] . "\n" . \ - [ $FormatLine "Expires in" [ $IfThenElse (($CertVal->"expired") = true) "expired" [ $FormatExpire ($CertVal->"expires-after") ] ] ]); -} - -$ScriptLock $0; -$WaitFullyConnected; - -:foreach Cert in=[ /certificate/find where !revoked !ca !scep-url expires-after<$CertRenewTime ] do={ - :local CertVal [ /certificate/get $Cert ]; - :local LastName; - - :do { - :if ([ :len $CertRenewUrl ] = 0) do={ - $LogPrintExit2 info $0 ("No CertRenewUrl given.") true; + $SendNotification2 ({ origin=$ScriptName; \ + subject=([ $SymbolForNotification "warning-sign" ] . "Certificate warning: " . ($CertVal->"name")); \ + message=("A certificate on " . $Identity . " " . $State . ".\n\n" . [ $FormatInfo $Cert ]) }); + $LogPrint info $ScriptName ("The certificate '" . ($CertVal->"name") . "' " . $State . \ + ", it is invalid after " . ($CertVal->"invalid-after") . "."); } - $LogPrintExit2 info $0 ("Attempting to renew certificate " . ($CertVal->"name") . ".") false; - - :local ImportSuccess false; - :set LastName ($CertVal->"common-name"); - :set ImportSuccess [ $CheckCertificatesDownloadImport $LastName ]; - :foreach SAN in=($CertVal->"subject-alt-name") do={ - :if ($ImportSuccess = false) do={ - :set LastName [ :pick $SAN ([ :find $SAN ":" ] + 1) [ :len $SAN ] ]; - :set ImportSuccess [ $CheckCertificatesDownloadImport $LastName ]; - } - } - - :if ($CertVal->"fingerprint" != [ /certificate/get $Cert fingerprint ]) do={ - $LogPrintExit2 debug $0 ("Certificate '" . $CertVal->"name" . "' was updated in place.") false; - :set CertVal [ /certificate/get $Cert ]; - } else { - $LogPrintExit2 debug $0 ("Certificate '" . $CertVal->"name" . "' was not updated, but replaced.") false; - - :local CertNew [ /certificate/find where name~("^" . [ $EscapeForRegEx [ $UrlEncode $LastName ] ] . "\\.(p12|pem)_[0-9]+\$") \ - (common-name=($CertVal->"common-name") or subject-alt-name~("(^|\\W)(DNS|IP):" . [ $EscapeForRegEx $LastName ] . "(\\W|\$)")) \ - fingerprint!=[ :tostr ($CertVal->"fingerprint") ] expires-after>$CertRenewTime ]; - :local CertNewVal [ /certificate/get $CertNew ]; - - :if ([ $CertificateAvailable ([ $ParseKeyValueStore ($CertNewVal->"issuer") ]->"CN") ] = false) do={ - $LogPrintExit2 warning $0 ("The certificate chain is not available!") false; - } - - :if (($CertVal->"private-key") = true && ($CertVal->"private-key") != ($CertNewVal->"private-key")) do={ - /certificate/remove $CertNew; - $LogPrintExit2 warning $0 ("Old certificate '" . ($CertVal->"name") . "' has a private key, new certificate does not. Aborting renew.") true; - } - - /ip/service/set certificate=($CertNewVal->"name") [ find where certificate=($CertVal->"name") ]; - - /ip/ipsec/identity/set certificate=($CertNewVal->"name") [ find where certificate=($CertVal->"name") ]; - /ip/ipsec/identity/set remote-certificate=($CertNewVal->"name") [ find where remote-certificate=($CertVal->"name") ]; - - /ip/hotspot/profile/set ssl-certificate=($CertNewVal->"name") [ find where ssl-certificate=($CertVal->"name") ]; - - /certificate/remove $Cert; - /certificate/set $CertNew name=($CertVal->"name"); - :set CertNewVal; - :set CertVal [ /certificate/get $CertNew ];; - } - - $SendNotification2 ({ origin=$0; silent=true; \ - subject=([ $SymbolForNotification "lock-with-ink-pen" ] . "Certificate renewed: " . ($CertVal->"name")); \ - message=("A certificate on " . $Identity . " has been renewed.\n\n" . [ $FormatInfo $CertVal ]) }); - $LogPrintExit2 info $0 ("The certificate " . ($CertVal->"name") . " has been renewed.") false; - } on-error={ - $LogPrintExit2 debug $0 ("Could not renew certificate " . ($CertVal->"name") . ".") false; - } -} - -:foreach Cert in=[ /certificate/find where !revoked !scep-url !(expires-after=[]) \ - expires-after<$CertWarnTime !(fingerprint=[]) ] do={ - :local CertVal [ /certificate/get $Cert ]; - - :if ([ :len [ /certificate/scep-server/find where ca-cert=($CertVal->"ca") ] ] > 0) do={ - $LogPrintExit2 debug $0 ("Certificate \"" . ($CertVal->"name") . "\" is handled by SCEP, skipping.") false; - } else={ - :local State [ $IfThenElse (($CertVal->"expired") = true) "expired" "is about to expire" ]; - - $SendNotification2 ({ origin=$0; \ - subject=([ $SymbolForNotification "warning-sign" ] . "Certificate warning: " . ($CertVal->"name")); \ - message=("A certificate on " . $Identity . " " . $State . ".\n\n" . [ $FormatInfo $CertVal ]) }); - $LogPrintExit2 info $0 ("The certificate " . ($CertVal->"name") . " " . $State . \ - ", it is invalid after " . ($CertVal->"invalid-after") . ".") false; } +} on-error={ + :global ExitError; $ExitError $ExitOK [ :jobname ]; } diff --git a/check-health.d/state.rsc b/check-health.d/state.rsc new file mode 100644 index 0000000..2991935 --- /dev/null +++ b/check-health.d/state.rsc @@ -0,0 +1,48 @@ +#!rsc by RouterOS +# RouterOS script: check-health.d/state +# Copyright (c) 2019-2025 Christian Hesse +# https://rsc.eworm.de/COPYING.md +# +# requires RouterOS, version=7.15 +# +# check for RouterOS health state - state plugin +# https://rsc.eworm.de/doc/check-health.md + +:global CheckHealthPlugins; + +:set ($CheckHealthPlugins->[ :jobname ]) do={ + :local FuncName [ :tostr $0 ]; + + :global CheckHealthLast; + :global Identity; + + :global LogPrint; + :global SendNotification2; + :global SymbolForNotification; + + :if ([ :len [ /system/health/find where type="" name~"-state\$"] ] = 0) do={ + $LogPrint debug $FuncName ("Your device does not provide any state health values."); + :return false; + } + + :foreach State in=[ /system/health/find where type="" name~"-state\$" ] do={ + :local Name [ /system/health/get $State name ]; + :local Value [ /system/health/get $State value ]; + + :if ([ :typeof ($CheckHealthLast->$Name) ] != "nothing") do={ + :if ($CheckHealthLast->$Name = "ok" && \ + $Value != "ok") do={ + $SendNotification2 ({ origin=$FuncName; \ + subject=([ $SymbolForNotification "cross-mark" ] . "Health warning: " . $Name); \ + message=("The device '" . $Name . "' on " . $Identity . " failed!") }); + } + :if ($CheckHealthLast->$Name != "ok" && \ + $Value = "ok") do={ + $SendNotification2 ({ origin=$FuncName; \ + subject=([ $SymbolForNotification "white-heavy-check-mark" ] . "Health recovery: " . $Name); \ + message=("The device '" . $Name . "' on " . $Identity . " recovered!") }); + } + } + :set ($CheckHealthLast->$Name) $Value; + } +} diff --git a/check-health.d/temperature.rsc b/check-health.d/temperature.rsc new file mode 100644 index 0000000..a2f632d --- /dev/null +++ b/check-health.d/temperature.rsc @@ -0,0 +1,74 @@ +#!rsc by RouterOS +# RouterOS script: check-health.d/temperature +# Copyright (c) 2019-2025 Christian Hesse +# https://rsc.eworm.de/COPYING.md +# +# requires RouterOS, version=7.15 +# +# check for RouterOS health state - temperature plugin +# https://rsc.eworm.de/doc/check-health.md + +:global CheckHealthPlugins; + +:set ($CheckHealthPlugins->[ :jobname ]) do={ + :local FuncName [ :tostr $0 ]; + + :global CheckHealthLast; + :global CheckHealthTemperature; + :global CheckHealthTemperatureDeviation; + :global CheckHealthTemperatureNotified; + :global Identity; + + :global LogPrint; + :global SendNotification2; + :global SymbolForNotification; + + :if ([ :len [ /system/health/find where type="C" ] ] = 0) do={ + $LogPrint debug $FuncName ("Your device does not provide any voltage health values."); + :return false; + } + + :local TempToNum do={ + :global CharacterReplace; + :local T [ :toarray [ $CharacterReplace $1 "." "," ] ]; + :return ($T->0 * 10 + $T->1); + } + + :if ([ :typeof $CheckHealthTemperatureNotified ] != "array") do={ + :set CheckHealthTemperatureNotified ({}); + } + + :foreach Temperature in=[ /system/health/find where type="C" ] do={ + :local Name [ /system/health/get $Temperature name ]; + :local Value [ /system/health/get $Temperature value ]; + + :if ([ :typeof ($CheckHealthLast->$Name) ] != "nothing") do={ + :if ([ :typeof ($CheckHealthTemperature->$Name) ] != "num" ) do={ + $LogPrint info $FuncName ("No threshold given for " . $Name . ", assuming 50C."); + :set ($CheckHealthTemperature->$Name) 50; + } + :local Validate [ /system/health/get [ find where name=$Name ] value ]; + :while ($Value != $Validate) do={ + :set Value $Validate; + :set Validate [ /system/health/get [ find where name=$Name ] value ]; + } + :if ($Value > $CheckHealthTemperature->$Name && \ + $CheckHealthTemperatureNotified->$Name != true) do={ + $SendNotification2 ({ origin=$FuncName; \ + subject=([ $SymbolForNotification "fire" ] . "Health warning: " . $Name); \ + message=("The " . $Name . " on " . $Identity . " is above threshold: " . \ + $Value . "\C2\B0" . "C") }); + :set ($CheckHealthTemperatureNotified->$Name) true; + } + :if ($Value <= ($CheckHealthTemperature->$Name - $CheckHealthTemperatureDeviation) && \ + $CheckHealthTemperatureNotified->$Name = true) do={ + $SendNotification2 ({ origin=$FuncName; \ + subject=([ $SymbolForNotification "white-heavy-check-mark" ] . "Health recovery: " . $Name); \ + message=("The " . $Name . " on " . $Identity . " dropped below threshold: " . \ + $Value . "\C2\B0" . "C") }); + :set ($CheckHealthTemperatureNotified->$Name) false; + } + } + :set ($CheckHealthLast->$Name) $Value; + } +} diff --git a/check-health.d/voltage.rsc b/check-health.d/voltage.rsc new file mode 100644 index 0000000..9071c88 --- /dev/null +++ b/check-health.d/voltage.rsc @@ -0,0 +1,63 @@ +#!rsc by RouterOS +# RouterOS script: check-health.d/voltage +# Copyright (c) 2019-2025 Christian Hesse +# https://rsc.eworm.de/COPYING.md +# +# requires RouterOS, version=7.15 +# +# check for RouterOS health state - voltage plugin +# https://rsc.eworm.de/doc/check-health.md + +:global CheckHealthPlugins; + +:set ($CheckHealthPlugins->[ :jobname ]) do={ + :local FuncName [ :tostr $0 ]; + + :global CheckHealthLast; + :global CheckHealthVoltageLow; + :global CheckHealthVoltagePercent; + :global Identity; + + :global FormatLine; + :global IfThenElse; + :global LogPrint; + :global SendNotification2; + :global SymbolForNotification; + + :if ([ :len [ /system/health/find where type="V" ] ] = 0) do={ + $LogPrint debug $FuncName ("Your device does not provide any voltage health values."); + :return false; + } + + :foreach Voltage in=[ /system/health/find where type="V" ] do={ + :local Name [ /system/health/get $Voltage name ]; + :local Value [ /system/health/get $Voltage value ]; + + :if ([ :typeof ($CheckHealthLast->$Name) ] != "nothing") do={ + :local NumCurr [ $TempToNum $Value ]; + :local NumLast [ $TempToNum ($CheckHealthLast->$Name) ]; + + :if ($NumLast * (100 + $CheckHealthVoltagePercent) < $NumCurr * 100 || \ + $NumLast * 100 > $NumCurr * (100 + $CheckHealthVoltagePercent)) do={ + $SendNotification2 ({ origin=$FuncName; \ + subject=([ $SymbolForNotification ("high-voltage-sign,chart-" . [ $IfThenElse ($NumLast < \ + $NumCurr) "in" "de" ] . "creasing") ] . "Health warning: " . $Name); \ + message=("The " . $Name . " on " . $Identity . " jumped more than " . $CheckHealthVoltagePercent . "%.\n\n" . \ + [ $FormatLine "old value" ($CheckHealthLast->$Name . " V") 12 ] . "\n" . \ + [ $FormatLine "new value" ($Value . " V") 12 ]) }); + } else={ + :if ($NumCurr <= $CheckHealthVoltageLow && $NumLast > $CheckHealthVoltageLow) do={ + $SendNotification2 ({ origin=$FuncName; \ + subject=([ $SymbolForNotification "high-voltage-sign,chart-decreasing" ] . "Health warning: Low " . $Name); \ + message=("The " . $Name . " on " . $Identity . " dropped to " . $Value . " V below hard limit.") }); + } + :if ($NumCurr > $CheckHealthVoltageLow && $NumLast <= $CheckHealthVoltageLow) do={ + $SendNotification2 ({ origin=$FuncName; \ + subject=([ $SymbolForNotification "high-voltage-sign,chart-increasing" ] . "Health recovery: Low " . $Name); \ + message=("The " . $Name . " on " . $Identity . " recovered to " . $Value . " V above hard limit.") }); + } + } + } + :set ($CheckHealthLast->$Name) $Value; + } +} diff --git a/check-health.rsc b/check-health.rsc index 3ada4c8..f02a249 100644 --- a/check-health.rsc +++ b/check-health.rsc @@ -1,169 +1,110 @@ #!rsc by RouterOS # RouterOS script: check-health -# Copyright (c) 2019-2023 Christian Hesse -# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md +# Copyright (c) 2019-2025 Christian Hesse +# https://rsc.eworm.de/COPYING.md +# +# requires RouterOS, version=7.15 # # check for RouterOS health state -# https://git.eworm.de/cgit/routeros-scripts/about/doc/check-health.md +# https://rsc.eworm.de/doc/check-health.md -:local 0 "check-health"; :global GlobalFunctionsReady; :while ($GlobalFunctionsReady != true) do={ :delay 500ms; } -:global CheckHealthCPUUtilization; -:global CheckHealthCPUUtilizationNotified; -:global CheckHealthLast; -:global CheckHealthRAMUtilizationNotified; -:global CheckHealthTemperature; -:global CheckHealthTemperatureDeviation; -:global CheckHealthTemperatureNotified; -:global CheckHealthVoltageLow; -:global CheckHealthVoltagePercent; -:global Identity; +:local ExitOK false; +:do { + :local ScriptName [ :jobname ]; -:global FormatLine; -:global IfThenElse; -:global LogPrintExit2; -:global ScriptLock; -:global SendNotification2; -:global SymbolForNotification; + :global CheckHealthCPUUtilization; + :global CheckHealthCPUUtilizationNotified; + :global CheckHealthLast; + :global CheckHealthRAMUtilizationNotified; + :global Identity; -:local TempToNum do={ - :global CharacterReplace; - :local T [ :toarray [ $CharacterReplace $1 "." "," ] ]; - :return ($T->0 * 10 + $T->1); -} + :global FormatLine; + :global HumanReadableNum; + :global IfThenElse; + :global LogPrint; + :global ScriptLock; + :global SendNotification2; + :global SymbolForNotification; + :global ValidateSyntax; -$ScriptLock $0; + :local TempToNum do={ + :global CharacterReplace; + :local T [ :toarray [ $CharacterReplace $1 "." "," ] ]; + :return ($T->0 * 10 + $T->1); + } -:local Resource [ /system/resource/get ]; + :if ([ $ScriptLock $ScriptName ] = false) do={ + :set ExitOK true; + :error false; + } -:set CheckHealthCPUUtilization (($CheckHealthCPUUtilization * 4 + ($Resource->"cpu-load") * 10) / 5); -:if ($CheckHealthCPUUtilization > 750 && $CheckHealthCPUUtilizationNotified != true) do={ - $SendNotification2 ({ origin=$0; \ - subject=([ $SymbolForNotification "abacus,chart-increasing" ] . "Health warning: CPU utilization"); \ - message=("The average CPU utilization on " . $Identity . " is at " . ($CheckHealthCPUUtilization / 10) . "%!") }); - :set CheckHealthCPUUtilizationNotified true; -} -:if ($CheckHealthCPUUtilization < 650 && $CheckHealthCPUUtilizationNotified = true) do={ - $SendNotification2 ({ origin=$0; \ - subject=([ $SymbolForNotification "abacus,chart-decreasing" ] . "Health recovery: CPU utilization"); \ - message=("The average CPU utilization on " . $Identity . " decreased to " . ($CheckHealthCPUUtilization / 10) . "%.") }); - :set CheckHealthCPUUtilizationNotified false; -} + :local Resource [ /system/resource/get ]; -:local CheckHealthRAMUtilization (($Resource->"total-memory" - $Resource->"free-memory") * 100 / $Resource->"total-memory"); -:if ($CheckHealthRAMUtilization >=80 && $CheckHealthRAMUtilizationNotified != true) do={ - $SendNotification2 ({ origin=$0; \ - subject=([ $SymbolForNotification "card-file-box,chart-increasing" ] . "Health warning: RAM utilization"); \ - message=("The RAM utilization on " . $Identity . " is at " . $CheckHealthRAMUtilization . "%!\n\n" . \ - [ $FormatLine "total" (($Resource->"total-memory" / 1024 / 1024) . " MiB") ] . "\n" . \ - [ $FormatLine "used" ((($Resource->"total-memory" - $Resource->"free-memory") / 1024 / 1024) . " MiB") ] . "\n" . \ - [ $FormatLine "free" (($Resource->"free-memory" / 1024 / 1024) . " MiB") ]) }); - :set CheckHealthRAMUtilizationNotified true; -} -:if ($CheckHealthRAMUtilization < 70 && $CheckHealthRAMUtilizationNotified = true) do={ - $SendNotification2 ({ origin=$0; \ - subject=([ $SymbolForNotification "card-file-box,chart-decreasing" ] . "Health recovery: RAM utilization"); \ - message=("The RAM utilization on " . $Identity . " decreased to " . $CheckHealthRAMUtilization . "%.") }); - :set CheckHealthRAMUtilizationNotified false; -} + :set CheckHealthCPUUtilization (($CheckHealthCPUUtilization * 4 + ($Resource->"cpu-load") * 10) / 5); + :if ($CheckHealthCPUUtilization > 750 && $CheckHealthCPUUtilizationNotified != true) do={ + $SendNotification2 ({ origin=$ScriptName; \ + subject=([ $SymbolForNotification "abacus,chart-increasing" ] . "Health warning: CPU utilization"); \ + message=("The average CPU utilization on " . $Identity . " is at " . ($CheckHealthCPUUtilization / 10) . "%!") }); + :set CheckHealthCPUUtilizationNotified true; + } + :if ($CheckHealthCPUUtilization < 650 && $CheckHealthCPUUtilizationNotified = true) do={ + $SendNotification2 ({ origin=$ScriptName; \ + subject=([ $SymbolForNotification "abacus,chart-decreasing" ] . "Health recovery: CPU utilization"); \ + message=("The average CPU utilization on " . $Identity . " decreased to " . ($CheckHealthCPUUtilization / 10) . "%.") }); + :set CheckHealthCPUUtilizationNotified false; + } -:if ([ :len [ /system/health/find ] ] = 0) do={ - $LogPrintExit2 debug $0 ("Your device does not provide any health values.") true; -} + :local CheckHealthRAMUtilization (($Resource->"total-memory" - $Resource->"free-memory") * 100 / $Resource->"total-memory"); + :if ($CheckHealthRAMUtilization >=80 && $CheckHealthRAMUtilizationNotified != true) do={ + $SendNotification2 ({ origin=$ScriptName; \ + subject=([ $SymbolForNotification "card-file-box,chart-increasing" ] . "Health warning: RAM utilization"); \ + message=("The RAM utilization on " . $Identity . " is at " . $CheckHealthRAMUtilization . "%!\n\n" . \ + [ $FormatLine "total" ([ $HumanReadableNum ($Resource->"total-memory") 1024 ] . "B") 8 ] . "\n" . \ + [ $FormatLine "used" ([ $HumanReadableNum ($Resource->"total-memory" - $Resource->"free-memory") 1024 ] . "B") 8 ] . "\n" . \ + [ $FormatLine "free" ([ $HumanReadableNum ($Resource->"free-memory") 1024 ] . "B") 8 ]) }); + :set CheckHealthRAMUtilizationNotified true; + } + :if ($CheckHealthRAMUtilization < 70 && $CheckHealthRAMUtilizationNotified = true) do={ + $SendNotification2 ({ origin=$ScriptName; \ + subject=([ $SymbolForNotification "card-file-box,chart-decreasing" ] . "Health recovery: RAM utilization"); \ + message=("The RAM utilization on " . $Identity . " decreased to " . $CheckHealthRAMUtilization . "%.") }); + :set CheckHealthRAMUtilizationNotified false; + } -:if ([ :typeof $CheckHealthLast ] != "array") do={ - :set CheckHealthLast ({}); -} -:if ([ :typeof $CheckHealthTemperatureNotified ] != "array") do={ - :set CheckHealthTemperatureNotified ({}); -} + :local Plugins [ /system/script/find where name~"^check-health.d/." ]; + :if ([ :len $Plugins ] = 0) do={ + $LogPrint debug $ScriptName ("No plugins installed."); + :set ExitOK true; + :error true; + } + :global CheckHealthPlugins ({}); + :if ([ :typeof $CheckHealthLast ] != "array") do={ + :set CheckHealthLast ({}); + } -:foreach Voltage in=[ /system/health/find where type="V" ] do={ - :local Name [ /system/health/get $Voltage name ]; - :local Value [ /system/health/get $Voltage value ]; - - :if ([ :typeof ($CheckHealthLast->$Name) ] != "nothing") do={ - :local NumCurr [ $TempToNum $Value ]; - :local NumLast [ $TempToNum ($CheckHealthLast->$Name) ]; - - :if ($NumLast * (100 + $CheckHealthVoltagePercent) < $NumCurr * 100 || \ - $NumLast * 100 > $NumCurr * (100 + $CheckHealthVoltagePercent)) do={ - $SendNotification2 ({ origin=$0; \ - subject=([ $SymbolForNotification ("high-voltage-sign,chart-" . [ $IfThenElse ($NumLast < \ - $NumCurr) "in" "de" ] . "creasing") ] . "Health warning: " . $Name); \ - message=("The " . $Name . " on " . $Identity . " jumped more than " . $CheckHealthVoltagePercent . "%.\n\n" . \ - [ $FormatLine "old value" ($CheckHealthLast->$Name . " V") ] . "\n" . \ - [ $FormatLine "new value" ($Value . " V") ]) }); - } else={ - :if ($NumCurr <= $CheckHealthVoltageLow && $NumLast > $CheckHealthVoltageLow) do={ - $SendNotification2 ({ origin=$0; \ - subject=([ $SymbolForNotification "high-voltage-sign,chart-decreasing" ] . "Health warning: Low " . $Name); \ - message=("The " . $Name . " on " . $Identity . " dropped to " . $Value . " V below hard limit.") }); - } - :if ($NumCurr > $CheckHealthVoltageLow && $NumLast <= $CheckHealthVoltageLow) do={ - $SendNotification2 ({ origin=$0; \ - subject=([ $SymbolForNotification "high-voltage-sign,chart-increasing" ] . "Health recovery: Low " . $Name); \ - message=("The " . $Name . " on " . $Identity . " recovered to " . $Value . " V above hard limit.") }); + :foreach Plugin in=$Plugins do={ + :local PluginVal [ /system/script/get $Plugin ]; + :if ([ $ValidateSyntax ($PluginVal->"source") ] = true) do={ + :do { + /system/script/run $Plugin; + } on-error={ + $LogPrint error $ScriptName ("Plugin '" . $ScriptVal->"name" . "' failed to run."); } + } else={ + $LogPrint error $ScriptName ("Plugin '" . $ScriptVal->"name" . "' failed syntax validation, skipping."); } } - :set ($CheckHealthLast->$Name) $Value; -} -:foreach PSU in=[ /system/health/find where name~"^psu.*-state\$" ] do={ - :local Name [ /system/health/get $PSU name ]; - :local Value [ /system/health/get $PSU value ]; - - :if ([ :typeof ($CheckHealthLast->$Name) ] != "nothing") do={ - :if ($CheckHealthLast->$Name = "ok" && \ - $Value != "ok") do={ - $SendNotification2 ({ origin=$0; \ - subject=([ $SymbolForNotification "cross-mark" ] . "Health warning: " . $Name); \ - message=("The power supply unit '" . $Name . "' on " . $Identity . " failed!") }); - } - :if ($CheckHealthLast->$Name != "ok" && \ - $Value = "ok") do={ - $SendNotification2 ({ origin=$0; \ - subject=([ $SymbolForNotification "white-heavy-check-mark" ] . "Health recovery: " . $Name); \ - message=("The power supply unit '" . $Name . "' on " . $Identity . " recovered!") }); - } + :foreach PluginName,Discard in=$CheckHealthPlugins do={ + ($CheckHealthPlugins->$PluginName) \ + ("\$CheckHealthPlugins->\"" . $PluginName . "\""); } - :set ($CheckHealthLast->$Name) $Value; -} -:foreach Temperature in=[ /system/health/find where type="C" ] do={ - :local Name [ /system/health/get $Temperature name ]; - :local Value [ /system/health/get $Temperature value ]; - - :if ([ :typeof ($CheckHealthLast->$Name) ] != "nothing") do={ - :if ([ :typeof ($CheckHealthTemperature->$Name) ] != "num" ) do={ - $LogPrintExit2 info $0 ("No threshold given for " . $Name . ", assuming 50C.") false; - :set ($CheckHealthTemperature->$Name) 50; - } - :local Validate [ /system/health/get [ find where name=$Name ] value ]; - :while ($Value != $Validate) do={ - :set Value $Validate; - :set Validate [ /system/health/get [ find where name=$Name ] value ]; - } - :if ($Value > $CheckHealthTemperature->$Name && \ - $CheckHealthTemperatureNotified->$Name != true) do={ - $SendNotification2 ({ origin=$0; \ - subject=([ $SymbolForNotification "fire" ] . "Health warning: " . $Name); \ - message=("The " . $Name . " on " . $Identity . " is above threshold: " . \ - $Value . "\C2\B0" . "C") }); - :set ($CheckHealthTemperatureNotified->$Name) true; - } - :if ($Value <= ($CheckHealthTemperature->$Name - $CheckHealthTemperatureDeviation) && \ - $CheckHealthTemperatureNotified->$Name = true) do={ - $SendNotification2 ({ origin=$0; \ - subject=([ $SymbolForNotification "white-heavy-check-mark" ] . "Health recovery: " . $Name); \ - message=("The " . $Name . " on " . $Identity . " dropped below threshold: " . \ - $Value . "\C2\B0" . "C") }); - :set ($CheckHealthTemperatureNotified->$Name) false; - } - } - :set ($CheckHealthLast->$Name) $Value; + :set CheckHealthPlugins; +} on-error={ + :global ExitError; $ExitError $ExitOK [ :jobname ]; } diff --git a/check-lte-firmware-upgrade.rsc b/check-lte-firmware-upgrade.rsc index a2aec01..c5b6cb5 100644 --- a/check-lte-firmware-upgrade.rsc +++ b/check-lte-firmware-upgrade.rsc @@ -1,88 +1,107 @@ #!rsc by RouterOS # RouterOS script: check-lte-firmware-upgrade -# Copyright (c) 2018-2023 Christian Hesse -# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md +# Copyright (c) 2018-2025 Christian Hesse +# https://rsc.eworm.de/COPYING.md +# +# requires RouterOS, version=7.15 # # check for LTE firmware upgrade, send notification -# https://git.eworm.de/cgit/routeros-scripts/about/doc/check-lte-firmware-upgrade.md +# https://rsc.eworm.de/doc/check-lte-firmware-upgrade.md -:local 0 "check-lte-firmware-upgrade"; :global GlobalFunctionsReady; :while ($GlobalFunctionsReady != true) do={ :delay 500ms; } -:global SentLteFirmwareUpgradeNotification; +:local ExitOK false; +:do { + :local ScriptName [ :jobname ]; -:global ScriptLock; - -$ScriptLock $0; - -:if ([ :typeof $SentLteFirmwareUpgradeNotification ] != "array") do={ - :global SentLteFirmwareUpgradeNotification ({}); -} - -:local CheckInterface do={ - :local Interface $1; - - :global Identity; :global SentLteFirmwareUpgradeNotification; - :global CharacterReplace; - :global FormatLine; - :global LogPrintExit2; - :global ScriptFromTerminal; - :global SendNotification2; - :global SymbolForNotification; + :global ScriptLock; - :local IntName [ /interface/lte/get $Interface name ]; - :local Firmware; - :local Info; - :do { - :set Firmware [ /interface/lte/firmware-upgrade $Interface once as-value ]; - :set Info [ /interface/lte/monitor $Interface once as-value ]; - } on-error={ - $LogPrintExit2 debug $0 ("Could not get latest LTE firmware version for interface " . \ - $IntName . ".") false; - :return false; + :if ([ $ScriptLock $ScriptName ] = false) do={ + :set ExitOK true; + :error false; } - :if (($Firmware->"installed") = ($Firmware->"latest")) do={ - :if ([ $ScriptFromTerminal $0 ] = true) do={ - $LogPrintExit2 info $0 ("No firmware upgrade available for LTE interface " . $IntName . ".") false; + :if ([ :typeof $SentLteFirmwareUpgradeNotification ] != "array") do={ + :global SentLteFirmwareUpgradeNotification ({}); + } + + :local CheckInterface do={ + :local ScriptName $1; + :local Interface $2; + + :global Identity; + :global SentLteFirmwareUpgradeNotification; + + :global FormatLine; + :global IfThenElse; + :global LogPrint; + :global ScriptFromTerminal; + :global SendNotification2; + :global SymbolForNotification; + + :local IntName [ /interface/lte/get $Interface name ]; + :local Firmware; + :local Info; + :do { + :set Firmware [ /interface/lte/firmware-upgrade $Interface as-value ]; + :set Info [ /interface/lte/monitor $Interface once as-value ]; + } on-error={ + $LogPrint debug $ScriptName ("Could not get latest LTE firmware version for interface " . \ + $IntName . "."); + :return false; } - :return true; - } - :if ([ $ScriptFromTerminal $0 ] = true && \ - [ :len [ /system/script/find where name="unattended-lte-firmware-upgrade" ] ] > 0) do={ - :put ("Do you want to start unattended lte firmware upgrade for interface " . $IntName . "? [y/N]"); - :if (([ /terminal/inkey timeout=60 ] % 32) = 25) do={ - /system/script/run unattended-lte-firmware-upgrade; - $LogPrintExit2 info $0 ("Scheduled lte firmware upgrade for interface " . $IntName . "...") false; + :if ([ :len ($Firmware->"latest") ] = 0) do={ + $LogPrint info $ScriptName ("An empty string is not a valid version."); + :return false; + } + + :if (($Firmware->"installed") = ($Firmware->"latest")) do={ + :if ([ $ScriptFromTerminal $ScriptName ] = true) do={ + $LogPrint info $ScriptName ("No firmware upgrade available for LTE interface " . $IntName . "."); + } :return true; - } else={ - :put "Canceled..."; } + + :if ([ $ScriptFromTerminal $ScriptName ] = true && \ + [ :len [ /system/script/find where name="unattended-lte-firmware-upgrade" ] ] > 0) do={ + :put ("Do you want to start unattended lte firmware upgrade for interface " . $IntName . "? [y/N]"); + :if (([ /terminal/inkey timeout=60 ] % 32) = 25) do={ + /system/script/run unattended-lte-firmware-upgrade; + $LogPrint info $ScriptName ("Scheduled lte firmware upgrade for interface " . $IntName . "..."); + :return true; + } else={ + :put "Canceled..."; + } + } + + :if (($SentLteFirmwareUpgradeNotification->$IntName) = ($Firmware->"latest")) do={ + $LogPrint debug $ScriptName ("Already sent the LTE firmware upgrade notification for version " . \ + ($Firmware->"latest") . "."); + :return false; + } + + $LogPrint info $ScriptName ("A new firmware version " . ($Firmware->"latest") . " is available for " . \ + "LTE interface " . $IntName . "."); + $SendNotification2 ({ origin=$ScriptName; \ + subject=([ $SymbolForNotification "sparkles" ] . "LTE firmware upgrade"); \ + message=("A new firmware version " . ($Firmware->"latest") . " is available for " . \ + "LTE interface " . $IntName . " on " . $Identity . ".\n\n" . \ + [ $IfThenElse ([ :len ($Info->"manufacturer") ] > 0) ([ $FormatLine "Manufacturer" ($Info->"manufacturer") ] . "\n") ] . \ + [ $IfThenElse ([ :len ($Info->"model") ] > 0) ([ $FormatLine "Model" ($Info->"model") ] . "\n") ] . \ + [ $IfThenElse ([ :len ($Info->"revision") ] > 0) ([ $FormatLine "Revision" ($Info->"revision") ] . "\n") ] . \ + "Firmware version:\n" . \ + [ $FormatLine " Installed" ($Firmware->"installed") ] . "\n" . \ + [ $FormatLine " Available" ($Firmware->"latest") ]); silent=true }); + :set ($SentLteFirmwareUpgradeNotification->$IntName) ($Firmware->"latest"); } - :if (($SentLteFirmwareUpgradeNotification->$IntName) = ($Firmware->"latest")) do={ - $LogPrintExit2 debug $0 ("Already sent the LTE firmware upgrade notification for version " . \ - ($Firmware->"latest") . ".") false; - :return false; + :foreach Interface in=[ /interface/lte/find ] do={ + $CheckInterface $ScriptName $Interface; } - - $LogPrintExit2 info $0 ("A new firmware version " . ($Firmware->"latest") . " is available for " . \ - "LTE interface " . $IntName . ".") false; - $SendNotification2 ({ origin=$0; \ - subject=([ $SymbolForNotification "sparkles" ] . "LTE firmware upgrade"); \ - message=("A new firmware version " . ($Firmware->"latest") . " is available for " . \ - "LTE interface " . $IntName . " on " . $Identity . ".\n\n" . \ - [ $FormatLine "Interface" [ $CharacterReplace ($Info->"manufacturer" . " " . $Info->"model") ("\"") "" ] ] . "\n" . \ - "Firmware version:\n" . \ - [ $FormatLine " Installed" ($Firmware->"installed") ] . "\n" . \ - [ $FormatLine " Available" ($Firmware->"latest") ]); silent=true }); - :set ($SentLteFirmwareUpgradeNotification->$IntName) ($Firmware->"latest"); -} - -:foreach Interface in=[ /interface/lte/find ] do={ - $CheckInterface $Interface; +} on-error={ + :global ExitError; $ExitError $ExitOK [ :jobname ]; } diff --git a/check-routeros-update.rsc b/check-routeros-update.rsc index d77164d..78161e4 100644 --- a/check-routeros-update.rsc +++ b/check-routeros-update.rsc @@ -1,154 +1,239 @@ #!rsc by RouterOS # RouterOS script: check-routeros-update -# Copyright (c) 2013-2023 Christian Hesse -# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md +# Copyright (c) 2013-2025 Christian Hesse +# https://rsc.eworm.de/COPYING.md +# +# requires RouterOS, version=7.15 +# requires device-mode, fetch, scheduler # # check for RouterOS update, send notification and/or install -# https://git.eworm.de/cgit/routeros-scripts/about/doc/check-routeros-update.md +# https://rsc.eworm.de/doc/check-routeros-update.md -:local 0 "check-routeros-update"; :global GlobalFunctionsReady; :while ($GlobalFunctionsReady != true) do={ :delay 500ms; } -:global Identity; -:global SafeUpdateAll; -:global SafeUpdateNeighbor; -:global SafeUpdateNeighborIdentity; -:global SafeUpdatePatch; -:global SafeUpdateUrl; -:global SentRouterosUpdateNotification; +:local ExitOK false; +:do { + :local ScriptName [ :jobname ]; -:global DeviceInfo; -:global EscapeForRegEx; -:global LogPrintExit2; -:global ScriptFromTerminal; -:global ScriptLock; -:global SendNotification2; -:global SymbolForNotification; -:global VersionToNum; -:global WaitFullyConnected; + :global Identity; + :global SafeUpdateAll; + :global SafeUpdateNeighbor; + :global SafeUpdateNeighborIdentity; + :global SafeUpdatePatch; + :global SafeUpdateUrl; + :global SentRouterosUpdateNotification; -:local DoUpdate do={ - :if ([ :len [ /system/script/find where name="packages-update" ] ] > 0) do={ - /system/script/run packages-update; - } else={ - /system/package/update/install without-paging; - } - :error "Waiting for system to reboot."; -} + :global DeviceInfo; + :global EscapeForRegEx; + :global FetchUserAgentStr; + :global LogPrint; + :global ScriptFromTerminal; + :global ScriptLock; + :global SendNotification2; + :global SymbolForNotification; + :global VersionToNum; + :global WaitFullyConnected; -$ScriptLock $0; + :local DoUpdate do={ + :local ScriptName [ :tostr $1 ]; -$WaitFullyConnected; + :global LogPrint; -:if ([ :len [ /system/scheduler/find where name="\$RebootForUpdate" ] ] > 0) do={ - :error "A reboot for update is already scheduled."; -} - -$LogPrintExit2 debug $0 ("Checking for updates...") false; -/system/package/update/check-for-updates without-paging as-value; -:local Update [ /system/package/update/get ]; - -:if ([ $ScriptFromTerminal $0 ] = true && ($Update->"installed-version") = ($Update->"latest-version")) do={ - $LogPrintExit2 info $0 ("System is already up to date.") true; -} - -:local NumInstalled [ $VersionToNum ($Update->"installed-version") ]; -:local NumLatest [ $VersionToNum ($Update->"latest-version") ]; -:local Link ("https://mikrotik.com/download/changelogs/" . $Update->"channel" . "-release-tree"); - -:if ($NumLatest < 117505792) do={ - $LogPrintExit2 info $0 ("The version '" . ($Update->"latest-version") . "' is not a valid version.") true; -} - -:if ($NumInstalled < $NumLatest) do={ - :if ($SafeUpdateAll ~ "^YES,? ?PLEASE!?\$") do={ - $LogPrintExit2 info $0 ("Installing ALL versions automatically, including " . \ - $Update->"latest-version" . "...") false; - $SendNotification2 ({ origin=$0; \ - subject=([ $SymbolForNotification "sparkles" ] . "RouterOS update: " . $Update->"latest-version"); \ - message=("Installing ALL versions automatically, including " . $Update->"latest-version" . \ - "... Updating on " . $Identity . "..."); link=$Link; silent=true }); - $DoUpdate; - } - - :if ($SafeUpdatePatch = true && ($NumInstalled & 0xffff0000) = ($NumLatest & 0xffff0000)) do={ - $LogPrintExit2 info $0 ("Version " . $Update->"latest-version" . " is a patch release, updating...") false; - $SendNotification2 ({ origin=$0; \ - subject=([ $SymbolForNotification "sparkles" ] . "RouterOS update: " . $Update->"latest-version"); \ - message=("Version " . $Update->"latest-version" . " is a patch update for " . $Update->"channel" . \ - ", updating on " . $Identity . "..."); link=$Link; silent=true }); - $DoUpdate; - } - - :if ($SafeUpdateNeighbor = true) do={ - :local Neighbors [ /ip/neighbor/find where platform="MikroTik" identity~$SafeUpdateNeighborIdentity \ - version~("^" . [ $EscapeForRegEx ($Update->"latest-version") ] . "\\b") ]; - :if ([ :len $Neighbors ] > 0) do={ - :local Neighbor [ /ip/neighbor/get ($Neighbors->0) identity ]; - $LogPrintExit2 info $0 ("Seen a neighbor (" . $Neighbor . ") running version " . \ - $Update->"latest-version" . " from " . $Update->"channel" . ", updating...") false; - $SendNotification2 ({ origin=$0; \ - subject=([ $SymbolForNotification "sparkles" ] . "RouterOS update: " . $Update->"latest-version"); \ - message=("Seen a neighbor (" . $Neighbor . ") running version " . $Update->"latest-version" . \ - " from " . $Update->"channel" . ", updating on " . $Identity . "..."); link=$Link; silent=true }); - $DoUpdate; - } - } - - :if ([ :len $SafeUpdateUrl ] > 0) do={ - :local Result; - :do { - :set Result [ /tool/fetch check-certificate=yes-without-crl \ - ($SafeUpdateUrl . $Update->"channel" . "?installed=" . $Update->"installed-version" . \ - "&latest=" . $Update->"latest-version") output=user as-value ]; - } on-error={ - $LogPrintExit2 warning $0 ("Failed receiving safe version for " . $Update->"channel" . ".") false; - } - :if ($Result->"status" = "finished" && $Result->"data" = $Update->"latest-version") do={ - $LogPrintExit2 info $0 ("Version " . $Update->"latest-version" . " is considered safe, updating...") false; - $SendNotification2 ({ origin=$0; \ - subject=([ $SymbolForNotification "sparkles" ] . "RouterOS update: " . $Update->"latest-version"); \ - message=("Version " . $Update->"latest-version" . " is considered safe for " . $Update->"channel" . \ - ", updating on " . $Identity . "..."); link=$Link; silent=true }); - $DoUpdate; - } - } - - :if ([ $ScriptFromTerminal $0 ] = true) do={ - :put ("Do you want to install RouterOS version " . $Update->"latest-version" . "? [y/N]"); - :if (([ /terminal/inkey timeout=60 ] % 32) = 25) do={ - $DoUpdate; + :if ([ :len [ /system/script/find where name="packages-update" ] ] > 0) do={ + /system/script/run packages-update; } else={ - :put "Canceled..."; + /system/package/update/install without-paging; + } + $LogPrint info $ScriptName ("Waiting for system to reboot."); + } + + :if ([ $ScriptLock $ScriptName ] = false) do={ + :set ExitOK true; + :error false; + } + + :if ([ :len [ /system/scheduler/find where name="running-from-backup-partition" ] ] > 0) do={ + $LogPrint warning $ScriptName ("Running from backup partition, refusing to act."); + :set ExitOK true; + :error false; + } + + $WaitFullyConnected; + + :if ([ :len [ /system/scheduler/find where name="_RebootForUpdate" ] ] > 0) do={ + :set ExitOK true; + :error "A reboot for update is already scheduled."; + } + + :local License [ /system/license/get ]; + :if ([ :typeof ($License->"deadline-at") ] = "str") do={ + :if ([ :len ($License->"next-renewal-at") ] = 0 && ($License->"limited-upgrades") = true) do={ + $LogPrint warning $ScriptName ("Your license expired on " . ($License->"deadline-at") . "!"); + $SendNotification2 ({ origin=$ScriptName; \ + subject=([ $SymbolForNotification "warning-sign" ] . "License expired!"); \ + message=("Your license expired on " . ($License->"deadline-at") . \ + ", can no longer update RouterOS on " . $Identity . "...") }); + :set ExitOK true; + :error false; + } + + :if ([ :totime ($License->"deadline-at") ] - 3w < [ :timestamp ]) do={ + $LogPrint warning $ScriptName ("Your license will expire on " . ($License->"deadline-at") . "!"); + $SendNotification2 ({ origin=$ScriptName; \ + subject=([ $SymbolForNotification "warning-sign" ] . "License about to expire!"); \ + message=("Your license failed to renew and is about to expire on " . \ + ($License->"deadline-at") . " on " . $Identity . "...") }); } } - :if ($SentRouterosUpdateNotification = $Update->"latest-version") do={ - $LogPrintExit2 info $0 ("Already sent the RouterOS update notification for version " . \ - $Update->"latest-version" . ".") true; + $LogPrint debug $ScriptName ("Checking for updates..."); + /system/package/update/check-for-updates without-paging as-value; + :local Update [ /system/package/update/get ]; + + :if (($Update->"installed-version") = ($Update->"latest-version")) do={ + :if ([ $ScriptFromTerminal $ScriptName ] = true) do={ + $LogPrint info $ScriptName ("System is already up to date."); + } + :set ExitOK true; + :error true; } - $SendNotification2 ({ origin=$0; \ - subject=([ $SymbolForNotification "sparkles" ] . "RouterOS update: " . $Update->"latest-version"); \ - message=("A new RouterOS version " . ($Update->"latest-version") . \ - " is available for " . $Identity . ".\n\n" . \ - [ $DeviceInfo ]); link=$Link; silent=true }); - :set SentRouterosUpdateNotification ($Update->"latest-version"); -} - -:if ($NumInstalled > $NumLatest) do={ - :if ($SentRouterosUpdateNotification = $Update->"latest-version") do={ - $LogPrintExit2 info $0 ("Already sent the RouterOS downgrade notification for version " . \ - $Update->"latest-version" . ".") true; + :if ([ :len ($Update->"latest-version") ] = 0) do={ + $LogPrint info $ScriptName ("Received an empty version string from server."); + :set ExitOK true; + :error false; } - $SendNotification2 ({ origin=$0; \ - subject=([ $SymbolForNotification "warning-sign" ] . "RouterOS version: " . $Update->"latest-version"); \ - message=("A different RouterOS version " . ($Update->"latest-version") . \ - " is available for " . $Identity . ", but it is a downgrade.\n\n" . \ - [ $DeviceInfo ]); link=$Link; silent=true }); - $LogPrintExit2 info $0 ("A different RouterOS version " . ($Update->"latest-version") . \ - " is available for downgrade.") false; - :set SentRouterosUpdateNotification ($Update->"latest-version"); + :local NumInstalled [ $VersionToNum ($Update->"installed-version") ]; + :local NumLatest [ $VersionToNum ($Update->"latest-version") ]; + :local BitMask [ $VersionToNum "255.255zero0" ]; + :local NumInstalledFeature ($NumInstalled & $BitMask); + :local NumLatestFeature ($NumLatest & $BitMask); + :local Link ("https://mikrotik.com/download/changelogs/" . $Update->"channel" . "-release-tree"); + + :if ($NumLatest < [ $VersionToNum "7.0" ]) do={ + $LogPrint warning $ScriptName ("The version '" . ($Update->"latest-version") . "' is not a valid version."); + :set ExitOK true; + :error false; + } + + :if ($NumInstalled < $NumLatest) do={ + :if ($SafeUpdateAll ~ "^YES,? ?PLEASE!?\$") do={ + $LogPrint info $ScriptName ("Installing ALL versions automatically, including " . \ + $Update->"latest-version" . "..."); + $SendNotification2 ({ origin=$ScriptName; \ + subject=([ $SymbolForNotification "sparkles" ] . "RouterOS update: " . $Update->"latest-version"); \ + message=("Installing ALL versions automatically, including " . $Update->"latest-version" . \ + "... Updating on " . $Identity . "..."); link=$Link; silent=true }); + $DoUpdate $ScriptName; + :set ExitOK true; + :error true; + } + + :if ($SafeUpdatePatch = true && $NumInstalledFeature = $NumLatestFeature) do={ + $LogPrint info $ScriptName ("Version " . $Update->"latest-version" . " is a patch release, updating..."); + $SendNotification2 ({ origin=$ScriptName; \ + subject=([ $SymbolForNotification "sparkles" ] . "RouterOS update: " . $Update->"latest-version"); \ + message=("Version " . $Update->"latest-version" . " is a patch update for " . $Update->"channel" . \ + ", updating on " . $Identity . "..."); link=$Link; silent=true }); + $DoUpdate $ScriptName; + :set ExitOK true; + :error true; + } + + :if ($SafeUpdateNeighbor = true) do={ + :local Neighbors [ /ip/neighbor/find where platform="MikroTik" identity~$SafeUpdateNeighborIdentity \ + version~("^" . [ $EscapeForRegEx ($Update->"latest-version") ] . "\\b") ]; + :if ([ :len $Neighbors ] > 0) do={ + :local Neighbor [ /ip/neighbor/get ($Neighbors->0) identity ]; + $LogPrint info $ScriptName ("Seen a neighbor (" . $Neighbor . ") running version " . \ + $Update->"latest-version" . " from " . $Update->"channel" . ", updating..."); + $SendNotification2 ({ origin=$ScriptName; \ + subject=([ $SymbolForNotification "sparkles" ] . "RouterOS update: " . $Update->"latest-version"); \ + message=("Seen a neighbor (" . $Neighbor . ") running version " . $Update->"latest-version" . \ + " from " . $Update->"channel" . ", updating on " . $Identity . "..."); link=$Link; silent=true }); + $DoUpdate $ScriptName; + :set ExitOK true; + :error true; + } + } + + :if ([ :len $SafeUpdateUrl ] > 0) do={ + :local Result; + :do { + :set Result [ /tool/fetch check-certificate=yes-without-crl \ + ($SafeUpdateUrl . $Update->"channel" . "?installed=" . $Update->"installed-version" . \ + "&latest=" . $Update->"latest-version") http-header-field=({ [ $FetchUserAgentStr $ScriptName ] }) \ + output=user as-value ]; + } on-error={ + $LogPrint warning $ScriptName ("Failed receiving safe version for " . $Update->"channel" . "."); + } + :if ($Result->"status" = "finished" && $Result->"data" = $Update->"latest-version") do={ + $LogPrint info $ScriptName ("Version " . $Update->"latest-version" . " is considered safe, updating..."); + $SendNotification2 ({ origin=$ScriptName; \ + subject=([ $SymbolForNotification "sparkles" ] . "RouterOS update: " . $Update->"latest-version"); \ + message=("Version " . $Update->"latest-version" . " is considered safe for " . $Update->"channel" . \ + ", updating on " . $Identity . "..."); link=$Link; silent=true }); + $DoUpdate $ScriptName; + :set ExitOK true; + :error true; + } + } + + :if ([ $ScriptFromTerminal $ScriptName ] = true) do={ + :if (($Update->"channel") = "testing" && $NumInstalledFeature < $NumLatestFeature) do={ + :put ("This is a feature update in testing channel. Switch to channel 'stable'? [y/N]"); + :if (([ /terminal/inkey timeout=60 ] % 32) = 25) do={ + /system/package/update/set channel=stable; + $LogPrint info $ScriptName ("Switched to channel 'stable', please re-run!"); + :set ExitOK true; + :error true; + } + } + + :put ("Do you want to install RouterOS version " . $Update->"latest-version" . "? [y/N]"); + :if (([ /terminal/inkey timeout=60 ] % 32) = 25) do={ + $DoUpdate $ScriptName; + :set ExitOK true; + :error true; + } else={ + :put "Canceled..."; + } + } + + :if ($SentRouterosUpdateNotification = $Update->"latest-version") do={ + $LogPrint info $ScriptName ("Already sent the RouterOS update notification for version " . \ + $Update->"latest-version" . "."); + :set ExitOK true; + :error true; + } + + $SendNotification2 ({ origin=$ScriptName; \ + subject=([ $SymbolForNotification "sparkles" ] . "RouterOS update: " . $Update->"latest-version"); \ + message=("A new RouterOS version " . ($Update->"latest-version") . \ + " is available for " . $Identity . ".\n\n" . \ + [ $DeviceInfo ]); link=$Link; silent=true }); + :set SentRouterosUpdateNotification ($Update->"latest-version"); + } + + :if ($NumInstalled > $NumLatest) do={ + :if ($SentRouterosUpdateNotification = $Update->"latest-version") do={ + $LogPrint info $ScriptName ("Already sent the RouterOS downgrade notification for version " . \ + $Update->"latest-version" . "."); + :set ExitOK true; + :error true; + } + + $SendNotification2 ({ origin=$ScriptName; \ + subject=([ $SymbolForNotification "warning-sign" ] . "RouterOS version: " . $Update->"latest-version"); \ + message=("A different RouterOS version " . ($Update->"latest-version") . \ + " is available for " . $Identity . ", but it is a downgrade.\n\n" . \ + [ $DeviceInfo ]); link=$Link; silent=true }); + $LogPrint info $ScriptName ("A different RouterOS version " . ($Update->"latest-version") . \ + " is available for downgrade."); + :set SentRouterosUpdateNotification ($Update->"latest-version"); + } +} on-error={ + :global ExitError; $ExitError $ExitOK [ :jobname ]; } diff --git a/collect-wireless-mac.capsman.rsc b/collect-wireless-mac.capsman.rsc index 65effa8..17e09e3 100644 --- a/collect-wireless-mac.capsman.rsc +++ b/collect-wireless-mac.capsman.rsc @@ -1,90 +1,100 @@ #!rsc by RouterOS # RouterOS script: collect-wireless-mac.capsman -# Copyright (c) 2013-2023 Christian Hesse -# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md +# Copyright (c) 2013-2025 Christian Hesse +# https://rsc.eworm.de/COPYING.md # # provides: lease-script, order=40 +# requires RouterOS, version=7.15 # # collect wireless mac adresses in access list -# https://git.eworm.de/cgit/routeros-scripts/about/doc/collect-wireless-mac.md +# https://rsc.eworm.de/doc/collect-wireless-mac.md # # !! Do not edit this file, it is generated from template! -:local 0 "collect-wireless-mac.capsman"; :global GlobalFunctionsReady; :while ($GlobalFunctionsReady != true) do={ :delay 500ms; } -:global Identity; +:local ExitOK false; +:do { + :local ScriptName [ :jobname ]; -:global EitherOr; -:global FormatLine; -:global FormatMultiLines; -:global GetMacVendor; -:global LogPrintExit2; -:global ScriptLock; -:global SendNotification2; -:global SymbolForNotification; + :global Identity; -$ScriptLock $0 false 10; + :global EitherOr; + :global FormatLine; + :global FormatMultiLines; + :global GetMacVendor; + :global LogPrint; + :global ScriptLock; + :global SendNotification2; + :global SymbolForNotification; -:if ([ :len [ /caps-man/access-list/find where comment="--- collected above ---" disabled ] ] = 0) do={ - /caps-man/access-list/add comment="--- collected above ---" disabled=yes; - $LogPrintExit2 warning $0 ("Added disabled access-list entry with comment '--- collected above ---'.") false; -} -:local PlaceBefore ([ /caps-man/access-list/find where comment="--- collected above ---" disabled ]->0); - -:foreach Reg in=[ /caps-man/registration-table/find ] do={ - :local RegVal; - :do { - :set RegVal [ /caps-man/registration-table/get $Reg ]; - } on-error={ - $LogPrintExit2 debug $0 ("Device already gone... Ignoring.") false; + :if ([ $ScriptLock $ScriptName 10 ] = false) do={ + :set ExitOK true; + :error false; } - :if ([ :len ($RegVal->"mac-address") ] > 0) do={ - :local AccessList ([ /caps-man/access-list/find where mac-address=($RegVal->"mac-address") ]->0); - :if ([ :len $AccessList ] > 0) do={ - $LogPrintExit2 debug $0 ("MAC address " . $RegVal->"mac-address" . " already known: " . \ - [ /caps-man/access-list/get $AccessList comment ]) false; + :if ([ :len [ /caps-man/access-list/find where comment="--- collected above ---" disabled ] ] = 0) do={ + /caps-man/access-list/add comment="--- collected above ---" disabled=yes; + $LogPrint warning $ScriptName ("Added disabled access-list entry with comment '--- collected above ---'."); + } + :local PlaceBefore ([ /caps-man/access-list/find where comment="--- collected above ---" disabled ]->0); + + :foreach Reg in=[ /caps-man/registration-table/find ] do={ + :local RegVal; + :do { + :set RegVal [ /caps-man/registration-table/get $Reg ]; + } on-error={ + $LogPrint debug $ScriptName ("Device already gone... Ignoring."); } - :if ([ :len $AccessList ] = 0) do={ - :local Address "no dhcp lease"; - :local DnsName "no dhcp lease"; - :local HostName "no dhcp lease"; - :local Lease ([ /ip/dhcp-server/lease/find where active-mac-address=($RegVal->"mac-address") dynamic=yes status=bound ]->0); - :if ([ :len $Lease ] > 0) do={ - :set Address [ /ip/dhcp-server/lease/get $Lease active-address ]; - :set HostName [ $EitherOr [ /ip/dhcp-server/lease/get $Lease host-name ] "no hostname" ]; - :set DnsName "no dns name"; - :local DnsRec ([ /ip/dns/static/find where address=$Address ]->0); - :if ([ :len $DnsRec ] > 0) do={ - :set DnsName ({ [ /ip/dns/static/get $DnsRec name ] }); - :foreach CName in=[ /ip/dns/static/find where type=CNAME cname=($DnsName->0) ] do={ - :set DnsName ($DnsName, [ /ip/dns/static/get $CName name ]); + :if ([ :len ($RegVal->"mac-address") ] > 0) do={ + :local AccessList ([ /caps-man/access-list/find where mac-address=($RegVal->"mac-address") ]->0); + :if ([ :len $AccessList ] > 0) do={ + $LogPrint debug $ScriptName ("MAC address " . $RegVal->"mac-address" . " already known: " . \ + [ /caps-man/access-list/get $AccessList comment ]); + } + + :if ([ :len $AccessList ] = 0) do={ + :local Address "no dhcp lease"; + :local DnsName "no dhcp lease"; + :local HostName "no dhcp lease"; + :local Lease ([ /ip/dhcp-server/lease/find where active-mac-address=($RegVal->"mac-address") dynamic=yes status=bound ]->0); + :if ([ :len $Lease ] > 0) do={ + :set Address [ /ip/dhcp-server/lease/get $Lease active-address ]; + :set HostName [ $EitherOr [ /ip/dhcp-server/lease/get $Lease host-name ] "no hostname" ]; + :set DnsName "no dns name"; + :local DnsRec ([ /ip/dns/static/find where address=$Address ]->0); + :if ([ :len $DnsRec ] > 0) do={ + :set DnsName ({ [ /ip/dns/static/get $DnsRec name ] }); + :foreach CName in=[ /ip/dns/static/find where type=CNAME cname=($DnsName->0) ] do={ + :set DnsName ($DnsName, [ /ip/dns/static/get $CName name ]); + } } } + :local DateTime ([ /system/clock/get date ] . " " . [ /system/clock/get time ]); + :local Vendor [ $GetMacVendor ($RegVal->"mac-address") ]; + :local Message ("MAC address " . $RegVal->"mac-address" . " (" . $Vendor . ", " . $HostName . ") " . \ + "first seen on " . $DateTime . " connected to SSID " . $RegVal->"ssid" . ", interface " . $RegVal->"interface"); + $LogPrint info $ScriptName $Message; + /caps-man/access-list/add place-before=$PlaceBefore comment=$Message mac-address=($RegVal->"mac-address") disabled=yes; + $SendNotification2 ({ origin=$ScriptName; \ + subject=([ $SymbolForNotification "mobile-phone" ] . $RegVal->"mac-address" . " connected to " . $RegVal->"ssid"); \ + message=("A device with unknown MAC address connected to " . $RegVal->"ssid" . " on " . $Identity . ".\n\n" . \ + [ $FormatLine "Controller" $Identity ] . "\n" . \ + [ $FormatLine "Interface" ($RegVal->"interface") ] . "\n" . \ + [ $FormatLine "SSID" ($RegVal->"ssid") ] . "\n" . \ + [ $FormatLine "MAC" ($RegVal->"mac-address") ] . "\n" . \ + [ $FormatLine "Vendor" $Vendor ] . "\n" . \ + [ $FormatLine "Hostname" $HostName ] . "\n" . \ + [ $FormatLine "Address" $Address ] . "\n" . \ + [ $FormatMultiLines "DNS name" $DnsName ] . "\n" . \ + [ $FormatLine "Date" $DateTime ]) }); } - :local DateTime ([ /system/clock/get date ] . " " . [ /system/clock/get time ]); - :local Vendor [ $GetMacVendor ($RegVal->"mac-address") ]; - :local Message ("MAC address " . $RegVal->"mac-address" . " (" . $Vendor . ", " . $HostName . ") " . \ - "first seen on " . $DateTime . " connected to SSID " . $RegVal->"ssid" . ", interface " . $RegVal->"interface"); - $LogPrintExit2 info $0 $Message false; - /caps-man/access-list/add place-before=$PlaceBefore comment=$Message mac-address=($RegVal->"mac-address") disabled=yes; - $SendNotification2 ({ origin=$0; \ - subject=([ $SymbolForNotification "mobile-phone" ] . $RegVal->"mac-address" . " connected to " . $RegVal->"ssid"); \ - message=("A device with unknown MAC address connected to " . $RegVal->"ssid" . " on " . $Identity . ".\n\n" . \ - [ $FormatLine "Controller" $Identity ] . "\n" . \ - [ $FormatLine "Interface" ($RegVal->"interface") ] . "\n" . \ - [ $FormatLine "SSID" ($RegVal->"ssid") ] . "\n" . \ - [ $FormatLine "MAC" ($RegVal->"mac-address") ] . "\n" . \ - [ $FormatLine "Vendor" $Vendor ] . "\n" . \ - [ $FormatLine "Hostname" $HostName ] . "\n" . \ - [ $FormatLine "Address" $Address ] . "\n" . \ - [ $FormatMultiLines "DNS name" $DnsName ] . "\n" . \ - [ $FormatLine "Date" $DateTime ]) }); + } else={ + $LogPrint debug $ScriptName ("No mac address available... Ignoring."); } - } else={ - $LogPrintExit2 debug $0 ("No mac address available... Ignoring.") false; } +} on-error={ + :global ExitError; $ExitError $ExitOK [ :jobname ]; } diff --git a/collect-wireless-mac.local.rsc b/collect-wireless-mac.local.rsc index 0594ceb..4a38bfa 100644 --- a/collect-wireless-mac.local.rsc +++ b/collect-wireless-mac.local.rsc @@ -1,91 +1,101 @@ #!rsc by RouterOS # RouterOS script: collect-wireless-mac.local -# Copyright (c) 2013-2023 Christian Hesse -# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md +# Copyright (c) 2013-2025 Christian Hesse +# https://rsc.eworm.de/COPYING.md # # provides: lease-script, order=40 +# requires RouterOS, version=7.15 # # collect wireless mac adresses in access list -# https://git.eworm.de/cgit/routeros-scripts/about/doc/collect-wireless-mac.md +# https://rsc.eworm.de/doc/collect-wireless-mac.md # # !! Do not edit this file, it is generated from template! -:local 0 "collect-wireless-mac.local"; :global GlobalFunctionsReady; :while ($GlobalFunctionsReady != true) do={ :delay 500ms; } -:global Identity; +:local ExitOK false; +:do { + :local ScriptName [ :jobname ]; -:global EitherOr; -:global FormatLine; -:global FormatMultiLines; -:global GetMacVendor; -:global LogPrintExit2; -:global ScriptLock; -:global SendNotification2; -:global SymbolForNotification; + :global Identity; -$ScriptLock $0 false 10; + :global EitherOr; + :global FormatLine; + :global FormatMultiLines; + :global GetMacVendor; + :global LogPrint; + :global ScriptLock; + :global SendNotification2; + :global SymbolForNotification; -:if ([ :len [ /interface/wireless/access-list/find where comment="--- collected above ---" disabled ] ] = 0) do={ - /interface/wireless/access-list/add comment="--- collected above ---" disabled=yes; - $LogPrintExit2 warning $0 ("Added disabled access-list entry with comment '--- collected above ---'.") false; -} -:local PlaceBefore ([ /interface/wireless/access-list/find where comment="--- collected above ---" disabled ]->0); - -:foreach Reg in=[ /interface/wireless/registration-table/find where ap=no ] do={ - :local RegVal; - :do { - :set RegVal [ /interface/wireless/registration-table/get $Reg ]; - } on-error={ - $LogPrintExit2 debug $0 ("Device already gone... Ignoring.") false; + :if ([ $ScriptLock $ScriptName 10 ] = false) do={ + :set ExitOK true; + :error false; } - :if ([ :len ($RegVal->"mac-address") ] > 0) do={ - :local AccessList ([ /interface/wireless/access-list/find where mac-address=($RegVal->"mac-address") ]->0); - :if ([ :len $AccessList ] > 0) do={ - $LogPrintExit2 debug $0 ("MAC address " . $RegVal->"mac-address" . " already known: " . \ - [ /interface/wireless/access-list/get $AccessList comment ]) false; + :if ([ :len [ /interface/wireless/access-list/find where comment="--- collected above ---" disabled ] ] = 0) do={ + /interface/wireless/access-list/add comment="--- collected above ---" disabled=yes; + $LogPrint warning $ScriptName ("Added disabled access-list entry with comment '--- collected above ---'."); + } + :local PlaceBefore ([ /interface/wireless/access-list/find where comment="--- collected above ---" disabled ]->0); + + :foreach Reg in=[ /interface/wireless/registration-table/find where ap=no ] do={ + :local RegVal; + :do { + :set RegVal [ /interface/wireless/registration-table/get $Reg ]; + } on-error={ + $LogPrint debug $ScriptName ("Device already gone... Ignoring."); } - :if ([ :len $AccessList ] = 0) do={ - :local Address "no dhcp lease"; - :local DnsName "no dhcp lease"; - :local HostName "no dhcp lease"; - :local Lease ([ /ip/dhcp-server/lease/find where active-mac-address=($RegVal->"mac-address") dynamic=yes status=bound ]->0); - :if ([ :len $Lease ] > 0) do={ - :set Address [ /ip/dhcp-server/lease/get $Lease active-address ]; - :set HostName [ $EitherOr [ /ip/dhcp-server/lease/get $Lease host-name ] "no hostname" ]; - :set DnsName "no dns name"; - :local DnsRec ([ /ip/dns/static/find where address=$Address ]->0); - :if ([ :len $DnsRec ] > 0) do={ - :set DnsName ({ [ /ip/dns/static/get $DnsRec name ] }); - :foreach CName in=[ /ip/dns/static/find where type=CNAME cname=($DnsName->0) ] do={ - :set DnsName ($DnsName, [ /ip/dns/static/get $CName name ]); + :if ([ :len ($RegVal->"mac-address") ] > 0) do={ + :local AccessList ([ /interface/wireless/access-list/find where mac-address=($RegVal->"mac-address") ]->0); + :if ([ :len $AccessList ] > 0) do={ + $LogPrint debug $ScriptName ("MAC address " . $RegVal->"mac-address" . " already known: " . \ + [ /interface/wireless/access-list/get $AccessList comment ]); + } + + :if ([ :len $AccessList ] = 0) do={ + :local Address "no dhcp lease"; + :local DnsName "no dhcp lease"; + :local HostName "no dhcp lease"; + :local Lease ([ /ip/dhcp-server/lease/find where active-mac-address=($RegVal->"mac-address") dynamic=yes status=bound ]->0); + :if ([ :len $Lease ] > 0) do={ + :set Address [ /ip/dhcp-server/lease/get $Lease active-address ]; + :set HostName [ $EitherOr [ /ip/dhcp-server/lease/get $Lease host-name ] "no hostname" ]; + :set DnsName "no dns name"; + :local DnsRec ([ /ip/dns/static/find where address=$Address ]->0); + :if ([ :len $DnsRec ] > 0) do={ + :set DnsName ({ [ /ip/dns/static/get $DnsRec name ] }); + :foreach CName in=[ /ip/dns/static/find where type=CNAME cname=($DnsName->0) ] do={ + :set DnsName ($DnsName, [ /ip/dns/static/get $CName name ]); + } } } + :set ($RegVal->"ssid") [ /interface/wireless/get [ find where name=($RegVal->"interface") ] ssid ]; + :local DateTime ([ /system/clock/get date ] . " " . [ /system/clock/get time ]); + :local Vendor [ $GetMacVendor ($RegVal->"mac-address") ]; + :local Message ("MAC address " . $RegVal->"mac-address" . " (" . $Vendor . ", " . $HostName . ") " . \ + "first seen on " . $DateTime . " connected to SSID " . $RegVal->"ssid" . ", interface " . $RegVal->"interface"); + $LogPrint info $ScriptName $Message; + /interface/wireless/access-list/add place-before=$PlaceBefore comment=$Message mac-address=($RegVal->"mac-address") disabled=yes; + $SendNotification2 ({ origin=$ScriptName; \ + subject=([ $SymbolForNotification "mobile-phone" ] . $RegVal->"mac-address" . " connected to " . $RegVal->"ssid"); \ + message=("A device with unknown MAC address connected to " . $RegVal->"ssid" . " on " . $Identity . ".\n\n" . \ + [ $FormatLine "Controller" $Identity ] . "\n" . \ + [ $FormatLine "Interface" ($RegVal->"interface") ] . "\n" . \ + [ $FormatLine "SSID" ($RegVal->"ssid") ] . "\n" . \ + [ $FormatLine "MAC" ($RegVal->"mac-address") ] . "\n" . \ + [ $FormatLine "Vendor" $Vendor ] . "\n" . \ + [ $FormatLine "Hostname" $HostName ] . "\n" . \ + [ $FormatLine "Address" $Address ] . "\n" . \ + [ $FormatMultiLines "DNS name" $DnsName ] . "\n" . \ + [ $FormatLine "Date" $DateTime ]) }); } - :set ($RegVal->"ssid") [ /interface/wireless/get [ find where name=($RegVal->"interface") ] ssid ]; - :local DateTime ([ /system/clock/get date ] . " " . [ /system/clock/get time ]); - :local Vendor [ $GetMacVendor ($RegVal->"mac-address") ]; - :local Message ("MAC address " . $RegVal->"mac-address" . " (" . $Vendor . ", " . $HostName . ") " . \ - "first seen on " . $DateTime . " connected to SSID " . $RegVal->"ssid" . ", interface " . $RegVal->"interface"); - $LogPrintExit2 info $0 $Message false; - /interface/wireless/access-list/add place-before=$PlaceBefore comment=$Message mac-address=($RegVal->"mac-address") disabled=yes; - $SendNotification2 ({ origin=$0; \ - subject=([ $SymbolForNotification "mobile-phone" ] . $RegVal->"mac-address" . " connected to " . $RegVal->"ssid"); \ - message=("A device with unknown MAC address connected to " . $RegVal->"ssid" . " on " . $Identity . ".\n\n" . \ - [ $FormatLine "Controller" $Identity ] . "\n" . \ - [ $FormatLine "Interface" ($RegVal->"interface") ] . "\n" . \ - [ $FormatLine "SSID" ($RegVal->"ssid") ] . "\n" . \ - [ $FormatLine "MAC" ($RegVal->"mac-address") ] . "\n" . \ - [ $FormatLine "Vendor" $Vendor ] . "\n" . \ - [ $FormatLine "Hostname" $HostName ] . "\n" . \ - [ $FormatLine "Address" $Address ] . "\n" . \ - [ $FormatMultiLines "DNS name" $DnsName ] . "\n" . \ - [ $FormatLine "Date" $DateTime ]) }); + } else={ + $LogPrint debug $ScriptName ("No mac address available... Ignoring."); } - } else={ - $LogPrintExit2 debug $0 ("No mac address available... Ignoring.") false; } +} on-error={ + :global ExitError; $ExitError $ExitOK [ :jobname ]; } diff --git a/collect-wireless-mac.template.rsc b/collect-wireless-mac.template.rsc index 75073a4..da901be 100644 --- a/collect-wireless-mac.template.rsc +++ b/collect-wireless-mac.template.rsc @@ -1,108 +1,118 @@ #!rsc by RouterOS # RouterOS script: collect-wireless-mac%TEMPL% -# Copyright (c) 2013-2023 Christian Hesse -# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md +# Copyright (c) 2013-2025 Christian Hesse +# https://rsc.eworm.de/COPYING.md # # provides: lease-script, order=40 +# requires RouterOS, version=7.15 # # collect wireless mac adresses in access list -# https://git.eworm.de/cgit/routeros-scripts/about/doc/collect-wireless-mac.md +# https://rsc.eworm.de/doc/collect-wireless-mac.md # # !! This is just a template to generate the real script! # !! Pattern '%TEMPL%' is replaced, paths are filtered. -:local 0 "collect-wireless-mac%TEMPL%"; :global GlobalFunctionsReady; :while ($GlobalFunctionsReady != true) do={ :delay 500ms; } -:global Identity; +:local ExitOK false; +:do { + :local ScriptName [ :jobname ]; -:global EitherOr; -:global FormatLine; -:global FormatMultiLines; -:global GetMacVendor; -:global LogPrintExit2; -:global ScriptLock; -:global SendNotification2; -:global SymbolForNotification; + :global Identity; -$ScriptLock $0 false 10; + :global EitherOr; + :global FormatLine; + :global FormatMultiLines; + :global GetMacVendor; + :global LogPrint; + :global ScriptLock; + :global SendNotification2; + :global SymbolForNotification; -:if ([ :len [ /caps-man/access-list/find where comment="--- collected above ---" disabled ] ] = 0) do={ -:if ([ :len [ /interface/wifiwave2/access-list/find where comment="--- collected above ---" disabled ] ] = 0) do={ -:if ([ :len [ /interface/wireless/access-list/find where comment="--- collected above ---" disabled ] ] = 0) do={ - /caps-man/access-list/add comment="--- collected above ---" disabled=yes; - /interface/wifiwave2/access-list/add comment="--- collected above ---" disabled=yes; - /interface/wireless/access-list/add comment="--- collected above ---" disabled=yes; - $LogPrintExit2 warning $0 ("Added disabled access-list entry with comment '--- collected above ---'.") false; -} -:local PlaceBefore ([ /caps-man/access-list/find where comment="--- collected above ---" disabled ]->0); -:local PlaceBefore ([ /interface/wifiwave2/access-list/find where comment="--- collected above ---" disabled ]->0); -:local PlaceBefore ([ /interface/wireless/access-list/find where comment="--- collected above ---" disabled ]->0); - -:foreach Reg in=[ /caps-man/registration-table/find ] do={ -:foreach Reg in=[ /interface/wifiwave2/registration-table/find ] do={ -:foreach Reg in=[ /interface/wireless/registration-table/find where ap=no ] do={ - :local RegVal; - :do { - :set RegVal [ /caps-man/registration-table/get $Reg ]; - :set RegVal [ /interface/wifiwave2/registration-table/get $Reg ]; - :set RegVal [ /interface/wireless/registration-table/get $Reg ]; - } on-error={ - $LogPrintExit2 debug $0 ("Device already gone... Ignoring.") false; + :if ([ $ScriptLock $ScriptName 10 ] = false) do={ + :set ExitOK true; + :error false; } - :if ([ :len ($RegVal->"mac-address") ] > 0) do={ - :local AccessList ([ /caps-man/access-list/find where mac-address=($RegVal->"mac-address") ]->0); - :local AccessList ([ /interface/wifiwave2/access-list/find where mac-address=($RegVal->"mac-address") ]->0); - :local AccessList ([ /interface/wireless/access-list/find where mac-address=($RegVal->"mac-address") ]->0); - :if ([ :len $AccessList ] > 0) do={ - $LogPrintExit2 debug $0 ("MAC address " . $RegVal->"mac-address" . " already known: " . \ - [ /caps-man/access-list/get $AccessList comment ]) false; - [ /interface/wifiwave2/access-list/get $AccessList comment ]) false; - [ /interface/wireless/access-list/get $AccessList comment ]) false; + :if ([ :len [ /caps-man/access-list/find where comment="--- collected above ---" disabled ] ] = 0) do={ + :if ([ :len [ /interface/wifi/access-list/find where comment="--- collected above ---" disabled ] ] = 0) do={ + :if ([ :len [ /interface/wireless/access-list/find where comment="--- collected above ---" disabled ] ] = 0) do={ + /caps-man/access-list/add comment="--- collected above ---" disabled=yes; + /interface/wifi/access-list/add comment="--- collected above ---" disabled=yes; + /interface/wireless/access-list/add comment="--- collected above ---" disabled=yes; + $LogPrint warning $ScriptName ("Added disabled access-list entry with comment '--- collected above ---'."); + } + :local PlaceBefore ([ /caps-man/access-list/find where comment="--- collected above ---" disabled ]->0); + :local PlaceBefore ([ /interface/wifi/access-list/find where comment="--- collected above ---" disabled ]->0); + :local PlaceBefore ([ /interface/wireless/access-list/find where comment="--- collected above ---" disabled ]->0); + + :foreach Reg in=[ /caps-man/registration-table/find ] do={ + :foreach Reg in=[ /interface/wifi/registration-table/find ] do={ + :foreach Reg in=[ /interface/wireless/registration-table/find where ap=no ] do={ + :local RegVal; + :do { + :set RegVal [ /caps-man/registration-table/get $Reg ]; + :set RegVal [ /interface/wifi/registration-table/get $Reg ]; + :set RegVal [ /interface/wireless/registration-table/get $Reg ]; + } on-error={ + $LogPrint debug $ScriptName ("Device already gone... Ignoring."); } - :if ([ :len $AccessList ] = 0) do={ - :local Address "no dhcp lease"; - :local DnsName "no dhcp lease"; - :local HostName "no dhcp lease"; - :local Lease ([ /ip/dhcp-server/lease/find where active-mac-address=($RegVal->"mac-address") dynamic=yes status=bound ]->0); - :if ([ :len $Lease ] > 0) do={ - :set Address [ /ip/dhcp-server/lease/get $Lease active-address ]; - :set HostName [ $EitherOr [ /ip/dhcp-server/lease/get $Lease host-name ] "no hostname" ]; - :set DnsName "no dns name"; - :local DnsRec ([ /ip/dns/static/find where address=$Address ]->0); - :if ([ :len $DnsRec ] > 0) do={ - :set DnsName ({ [ /ip/dns/static/get $DnsRec name ] }); - :foreach CName in=[ /ip/dns/static/find where type=CNAME cname=($DnsName->0) ] do={ - :set DnsName ($DnsName, [ /ip/dns/static/get $CName name ]); + :if ([ :len ($RegVal->"mac-address") ] > 0) do={ + :local AccessList ([ /caps-man/access-list/find where mac-address=($RegVal->"mac-address") ]->0); + :local AccessList ([ /interface/wifi/access-list/find where mac-address=($RegVal->"mac-address") ]->0); + :local AccessList ([ /interface/wireless/access-list/find where mac-address=($RegVal->"mac-address") ]->0); + :if ([ :len $AccessList ] > 0) do={ + $LogPrint debug $ScriptName ("MAC address " . $RegVal->"mac-address" . " already known: " . \ + [ /caps-man/access-list/get $AccessList comment ]); + [ /interface/wifi/access-list/get $AccessList comment ]); + [ /interface/wireless/access-list/get $AccessList comment ]); + } + + :if ([ :len $AccessList ] = 0) do={ + :local Address "no dhcp lease"; + :local DnsName "no dhcp lease"; + :local HostName "no dhcp lease"; + :local Lease ([ /ip/dhcp-server/lease/find where active-mac-address=($RegVal->"mac-address") dynamic=yes status=bound ]->0); + :if ([ :len $Lease ] > 0) do={ + :set Address [ /ip/dhcp-server/lease/get $Lease active-address ]; + :set HostName [ $EitherOr [ /ip/dhcp-server/lease/get $Lease host-name ] "no hostname" ]; + :set DnsName "no dns name"; + :local DnsRec ([ /ip/dns/static/find where address=$Address ]->0); + :if ([ :len $DnsRec ] > 0) do={ + :set DnsName ({ [ /ip/dns/static/get $DnsRec name ] }); + :foreach CName in=[ /ip/dns/static/find where type=CNAME cname=($DnsName->0) ] do={ + :set DnsName ($DnsName, [ /ip/dns/static/get $CName name ]); + } } } + :set ($RegVal->"ssid") [ /interface/wireless/get [ find where name=($RegVal->"interface") ] ssid ]; + :local DateTime ([ /system/clock/get date ] . " " . [ /system/clock/get time ]); + :local Vendor [ $GetMacVendor ($RegVal->"mac-address") ]; + :local Message ("MAC address " . $RegVal->"mac-address" . " (" . $Vendor . ", " . $HostName . ") " . \ + "first seen on " . $DateTime . " connected to SSID " . $RegVal->"ssid" . ", interface " . $RegVal->"interface"); + $LogPrint info $ScriptName $Message; + /caps-man/access-list/add place-before=$PlaceBefore comment=$Message mac-address=($RegVal->"mac-address") disabled=yes; + /interface/wifi/access-list/add place-before=$PlaceBefore comment=$Message mac-address=($RegVal->"mac-address") disabled=yes; + /interface/wireless/access-list/add place-before=$PlaceBefore comment=$Message mac-address=($RegVal->"mac-address") disabled=yes; + $SendNotification2 ({ origin=$ScriptName; \ + subject=([ $SymbolForNotification "mobile-phone" ] . $RegVal->"mac-address" . " connected to " . $RegVal->"ssid"); \ + message=("A device with unknown MAC address connected to " . $RegVal->"ssid" . " on " . $Identity . ".\n\n" . \ + [ $FormatLine "Controller" $Identity ] . "\n" . \ + [ $FormatLine "Interface" ($RegVal->"interface") ] . "\n" . \ + [ $FormatLine "SSID" ($RegVal->"ssid") ] . "\n" . \ + [ $FormatLine "MAC" ($RegVal->"mac-address") ] . "\n" . \ + [ $FormatLine "Vendor" $Vendor ] . "\n" . \ + [ $FormatLine "Hostname" $HostName ] . "\n" . \ + [ $FormatLine "Address" $Address ] . "\n" . \ + [ $FormatMultiLines "DNS name" $DnsName ] . "\n" . \ + [ $FormatLine "Date" $DateTime ]) }); } - :set ($RegVal->"ssid") [ /interface/wireless/get [ find where name=($RegVal->"interface") ] ssid ]; - :local DateTime ([ /system/clock/get date ] . " " . [ /system/clock/get time ]); - :local Vendor [ $GetMacVendor ($RegVal->"mac-address") ]; - :local Message ("MAC address " . $RegVal->"mac-address" . " (" . $Vendor . ", " . $HostName . ") " . \ - "first seen on " . $DateTime . " connected to SSID " . $RegVal->"ssid" . ", interface " . $RegVal->"interface"); - $LogPrintExit2 info $0 $Message false; - /caps-man/access-list/add place-before=$PlaceBefore comment=$Message mac-address=($RegVal->"mac-address") disabled=yes; - /interface/wifiwave2/access-list/add place-before=$PlaceBefore comment=$Message mac-address=($RegVal->"mac-address") disabled=yes; - /interface/wireless/access-list/add place-before=$PlaceBefore comment=$Message mac-address=($RegVal->"mac-address") disabled=yes; - $SendNotification2 ({ origin=$0; \ - subject=([ $SymbolForNotification "mobile-phone" ] . $RegVal->"mac-address" . " connected to " . $RegVal->"ssid"); \ - message=("A device with unknown MAC address connected to " . $RegVal->"ssid" . " on " . $Identity . ".\n\n" . \ - [ $FormatLine "Controller" $Identity ] . "\n" . \ - [ $FormatLine "Interface" ($RegVal->"interface") ] . "\n" . \ - [ $FormatLine "SSID" ($RegVal->"ssid") ] . "\n" . \ - [ $FormatLine "MAC" ($RegVal->"mac-address") ] . "\n" . \ - [ $FormatLine "Vendor" $Vendor ] . "\n" . \ - [ $FormatLine "Hostname" $HostName ] . "\n" . \ - [ $FormatLine "Address" $Address ] . "\n" . \ - [ $FormatMultiLines "DNS name" $DnsName ] . "\n" . \ - [ $FormatLine "Date" $DateTime ]) }); + } else={ + $LogPrint debug $ScriptName ("No mac address available... Ignoring."); } - } else={ - $LogPrintExit2 debug $0 ("No mac address available... Ignoring.") false; } +} on-error={ + :global ExitError; $ExitError $ExitOK [ :jobname ]; } diff --git a/collect-wireless-mac.wifi.rsc b/collect-wireless-mac.wifi.rsc new file mode 100644 index 0000000..cb217ce --- /dev/null +++ b/collect-wireless-mac.wifi.rsc @@ -0,0 +1,100 @@ +#!rsc by RouterOS +# RouterOS script: collect-wireless-mac.wifi +# Copyright (c) 2013-2025 Christian Hesse +# https://rsc.eworm.de/COPYING.md +# +# provides: lease-script, order=40 +# requires RouterOS, version=7.15 +# +# collect wireless mac adresses in access list +# https://rsc.eworm.de/doc/collect-wireless-mac.md +# +# !! Do not edit this file, it is generated from template! + +:global GlobalFunctionsReady; +:while ($GlobalFunctionsReady != true) do={ :delay 500ms; } + +:local ExitOK false; +:do { + :local ScriptName [ :jobname ]; + + :global Identity; + + :global EitherOr; + :global FormatLine; + :global FormatMultiLines; + :global GetMacVendor; + :global LogPrint; + :global ScriptLock; + :global SendNotification2; + :global SymbolForNotification; + + :if ([ $ScriptLock $ScriptName 10 ] = false) do={ + :set ExitOK true; + :error false; + } + + :if ([ :len [ /interface/wifi/access-list/find where comment="--- collected above ---" disabled ] ] = 0) do={ + /interface/wifi/access-list/add comment="--- collected above ---" disabled=yes; + $LogPrint warning $ScriptName ("Added disabled access-list entry with comment '--- collected above ---'."); + } + :local PlaceBefore ([ /interface/wifi/access-list/find where comment="--- collected above ---" disabled ]->0); + + :foreach Reg in=[ /interface/wifi/registration-table/find ] do={ + :local RegVal; + :do { + :set RegVal [ /interface/wifi/registration-table/get $Reg ]; + } on-error={ + $LogPrint debug $ScriptName ("Device already gone... Ignoring."); + } + + :if ([ :len ($RegVal->"mac-address") ] > 0) do={ + :local AccessList ([ /interface/wifi/access-list/find where mac-address=($RegVal->"mac-address") ]->0); + :if ([ :len $AccessList ] > 0) do={ + $LogPrint debug $ScriptName ("MAC address " . $RegVal->"mac-address" . " already known: " . \ + [ /interface/wifi/access-list/get $AccessList comment ]); + } + + :if ([ :len $AccessList ] = 0) do={ + :local Address "no dhcp lease"; + :local DnsName "no dhcp lease"; + :local HostName "no dhcp lease"; + :local Lease ([ /ip/dhcp-server/lease/find where active-mac-address=($RegVal->"mac-address") dynamic=yes status=bound ]->0); + :if ([ :len $Lease ] > 0) do={ + :set Address [ /ip/dhcp-server/lease/get $Lease active-address ]; + :set HostName [ $EitherOr [ /ip/dhcp-server/lease/get $Lease host-name ] "no hostname" ]; + :set DnsName "no dns name"; + :local DnsRec ([ /ip/dns/static/find where address=$Address ]->0); + :if ([ :len $DnsRec ] > 0) do={ + :set DnsName ({ [ /ip/dns/static/get $DnsRec name ] }); + :foreach CName in=[ /ip/dns/static/find where type=CNAME cname=($DnsName->0) ] do={ + :set DnsName ($DnsName, [ /ip/dns/static/get $CName name ]); + } + } + } + :local DateTime ([ /system/clock/get date ] . " " . [ /system/clock/get time ]); + :local Vendor [ $GetMacVendor ($RegVal->"mac-address") ]; + :local Message ("MAC address " . $RegVal->"mac-address" . " (" . $Vendor . ", " . $HostName . ") " . \ + "first seen on " . $DateTime . " connected to SSID " . $RegVal->"ssid" . ", interface " . $RegVal->"interface"); + $LogPrint info $ScriptName $Message; + /interface/wifi/access-list/add place-before=$PlaceBefore comment=$Message mac-address=($RegVal->"mac-address") disabled=yes; + $SendNotification2 ({ origin=$ScriptName; \ + subject=([ $SymbolForNotification "mobile-phone" ] . $RegVal->"mac-address" . " connected to " . $RegVal->"ssid"); \ + message=("A device with unknown MAC address connected to " . $RegVal->"ssid" . " on " . $Identity . ".\n\n" . \ + [ $FormatLine "Controller" $Identity ] . "\n" . \ + [ $FormatLine "Interface" ($RegVal->"interface") ] . "\n" . \ + [ $FormatLine "SSID" ($RegVal->"ssid") ] . "\n" . \ + [ $FormatLine "MAC" ($RegVal->"mac-address") ] . "\n" . \ + [ $FormatLine "Vendor" $Vendor ] . "\n" . \ + [ $FormatLine "Hostname" $HostName ] . "\n" . \ + [ $FormatLine "Address" $Address ] . "\n" . \ + [ $FormatMultiLines "DNS name" $DnsName ] . "\n" . \ + [ $FormatLine "Date" $DateTime ]) }); + } + } else={ + $LogPrint debug $ScriptName ("No mac address available... Ignoring."); + } + } +} on-error={ + :global ExitError; $ExitError $ExitOK [ :jobname ]; +} diff --git a/collect-wireless-mac.wifiwave2.rsc b/collect-wireless-mac.wifiwave2.rsc deleted file mode 100644 index 86cfd64..0000000 --- a/collect-wireless-mac.wifiwave2.rsc +++ /dev/null @@ -1,90 +0,0 @@ -#!rsc by RouterOS -# RouterOS script: collect-wireless-mac.wifiwave2 -# Copyright (c) 2013-2023 Christian Hesse -# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md -# -# provides: lease-script, order=40 -# -# collect wireless mac adresses in access list -# https://git.eworm.de/cgit/routeros-scripts/about/doc/collect-wireless-mac.md -# -# !! Do not edit this file, it is generated from template! - -:local 0 "collect-wireless-mac.wifiwave2"; -:global GlobalFunctionsReady; -:while ($GlobalFunctionsReady != true) do={ :delay 500ms; } - -:global Identity; - -:global EitherOr; -:global FormatLine; -:global FormatMultiLines; -:global GetMacVendor; -:global LogPrintExit2; -:global ScriptLock; -:global SendNotification2; -:global SymbolForNotification; - -$ScriptLock $0 false 10; - -:if ([ :len [ /interface/wifiwave2/access-list/find where comment="--- collected above ---" disabled ] ] = 0) do={ - /interface/wifiwave2/access-list/add comment="--- collected above ---" disabled=yes; - $LogPrintExit2 warning $0 ("Added disabled access-list entry with comment '--- collected above ---'.") false; -} -:local PlaceBefore ([ /interface/wifiwave2/access-list/find where comment="--- collected above ---" disabled ]->0); - -:foreach Reg in=[ /interface/wifiwave2/registration-table/find ] do={ - :local RegVal; - :do { - :set RegVal [ /interface/wifiwave2/registration-table/get $Reg ]; - } on-error={ - $LogPrintExit2 debug $0 ("Device already gone... Ignoring.") false; - } - - :if ([ :len ($RegVal->"mac-address") ] > 0) do={ - :local AccessList ([ /interface/wifiwave2/access-list/find where mac-address=($RegVal->"mac-address") ]->0); - :if ([ :len $AccessList ] > 0) do={ - $LogPrintExit2 debug $0 ("MAC address " . $RegVal->"mac-address" . " already known: " . \ - [ /interface/wifiwave2/access-list/get $AccessList comment ]) false; - } - - :if ([ :len $AccessList ] = 0) do={ - :local Address "no dhcp lease"; - :local DnsName "no dhcp lease"; - :local HostName "no dhcp lease"; - :local Lease ([ /ip/dhcp-server/lease/find where active-mac-address=($RegVal->"mac-address") dynamic=yes status=bound ]->0); - :if ([ :len $Lease ] > 0) do={ - :set Address [ /ip/dhcp-server/lease/get $Lease active-address ]; - :set HostName [ $EitherOr [ /ip/dhcp-server/lease/get $Lease host-name ] "no hostname" ]; - :set DnsName "no dns name"; - :local DnsRec ([ /ip/dns/static/find where address=$Address ]->0); - :if ([ :len $DnsRec ] > 0) do={ - :set DnsName ({ [ /ip/dns/static/get $DnsRec name ] }); - :foreach CName in=[ /ip/dns/static/find where type=CNAME cname=($DnsName->0) ] do={ - :set DnsName ($DnsName, [ /ip/dns/static/get $CName name ]); - } - } - } - :local DateTime ([ /system/clock/get date ] . " " . [ /system/clock/get time ]); - :local Vendor [ $GetMacVendor ($RegVal->"mac-address") ]; - :local Message ("MAC address " . $RegVal->"mac-address" . " (" . $Vendor . ", " . $HostName . ") " . \ - "first seen on " . $DateTime . " connected to SSID " . $RegVal->"ssid" . ", interface " . $RegVal->"interface"); - $LogPrintExit2 info $0 $Message false; - /interface/wifiwave2/access-list/add place-before=$PlaceBefore comment=$Message mac-address=($RegVal->"mac-address") disabled=yes; - $SendNotification2 ({ origin=$0; \ - subject=([ $SymbolForNotification "mobile-phone" ] . $RegVal->"mac-address" . " connected to " . $RegVal->"ssid"); \ - message=("A device with unknown MAC address connected to " . $RegVal->"ssid" . " on " . $Identity . ".\n\n" . \ - [ $FormatLine "Controller" $Identity ] . "\n" . \ - [ $FormatLine "Interface" ($RegVal->"interface") ] . "\n" . \ - [ $FormatLine "SSID" ($RegVal->"ssid") ] . "\n" . \ - [ $FormatLine "MAC" ($RegVal->"mac-address") ] . "\n" . \ - [ $FormatLine "Vendor" $Vendor ] . "\n" . \ - [ $FormatLine "Hostname" $HostName ] . "\n" . \ - [ $FormatLine "Address" $Address ] . "\n" . \ - [ $FormatMultiLines "DNS name" $DnsName ] . "\n" . \ - [ $FormatLine "Date" $DateTime ]) }); - } - } else={ - $LogPrintExit2 debug $0 ("No mac address available... Ignoring.") false; - } -} diff --git a/daily-psk.capsman.rsc b/daily-psk.capsman.rsc index b228d86..5672931 100644 --- a/daily-psk.capsman.rsc +++ b/daily-psk.capsman.rsc @@ -1,85 +1,96 @@ #!rsc by RouterOS # RouterOS script: daily-psk.capsman -# Copyright (c) 2013-2023 Christian Hesse +# Copyright (c) 2013-2025 Christian Hesse # Michael Gisbers -# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md +# https://rsc.eworm.de/COPYING.md +# +# requires RouterOS, version=7.15 # # update daily PSK (pre shared key) -# https://git.eworm.de/cgit/routeros-scripts/about/doc/daily-psk.md +# https://rsc.eworm.de/doc/daily-psk.md # # !! Do not edit this file, it is generated from template! -:local 0 "daily-psk.capsman"; :global GlobalFunctionsReady; :while ($GlobalFunctionsReady != true) do={ :delay 500ms; } -:global DailyPskMatchComment; -:global DailyPskQrCodeUrl; -:global Identity; +:local ExitOK false; +:do { + :local ScriptName [ :jobname ]; -:global FormatLine; -:global LogPrintExit2; -:global ScriptLock; -:global SendNotification2; -:global SymbolForNotification; -:global UrlEncode; -:global WaitForFile; -:global WaitFullyConnected; + :global DailyPskMatchComment; + :global DailyPskQrCodeUrl; + :global Identity; -$ScriptLock $0; -$WaitFullyConnected; + :global FormatLine; + :global LogPrint; + :global ScriptLock; + :global SendNotification2; + :global SymbolForNotification; + :global UrlEncode; + :global WaitForFile; + :global WaitFullyConnected; -# return pseudo-random string for PSK -:local GeneratePSK do={ - :local Date [ :tostr $1 ]; + :if ([ $ScriptLock $ScriptName ] = false) do={ + :set ExitOK true; + :error false; + } + $WaitFullyConnected; - :global DailyPskSecrets; + # return pseudo-random string for PSK + :local GeneratePSK do={ + :local Date [ :tostr $1 ]; - :global ParseDate; + :global DailyPskSecrets; - :set Date [ $ParseDate $Date ]; + :global ParseDate; - :local A ((14 - ($Date->"month")) / 12); - :local B (($Date->"year") - $A); - :local C (($Date->"month") + 12 * $A - 2); - :local WeekDay (7000 + ($Date->"day") + $B + ($B / 4) - ($B / 100) + ($B / 400) + ((31 * $C) / 12)); - :set WeekDay ($WeekDay - (($WeekDay / 7) * 7)); + :set Date [ $ParseDate $Date ]; - :return (($DailyPskSecrets->0->(($Date->"day") - 1)) . \ - ($DailyPskSecrets->1->(($Date->"month") - 1)) . \ - ($DailyPskSecrets->2->$WeekDay)); -} + :local A ((14 - ($Date->"month")) / 12); + :local B (($Date->"year") - $A); + :local C (($Date->"month") + 12 * $A - 2); + :local WeekDay (7000 + ($Date->"day") + $B + ($B / 4) - ($B / 100) + ($B / 400) + ((31 * $C) / 12)); + :set WeekDay ($WeekDay - (($WeekDay / 7) * 7)); -:local Seen ({}); -:local Date [ /system/clock/get date ]; -:local NewPsk [ $GeneratePSK $Date ]; + :return (($DailyPskSecrets->0->(($Date->"day") - 1)) . \ + ($DailyPskSecrets->1->(($Date->"month") - 1)) . \ + ($DailyPskSecrets->2->$WeekDay)); + } -:foreach AccList in=[ /caps-man/access-list/find where comment~$DailyPskMatchComment ] do={ - :local SsidRegExp [ /caps-man/access-list/get $AccList ssid-regexp ]; - :local Configuration ([ /caps-man/configuration/find where ssid~$SsidRegExp ]->0); - :local Ssid [ /caps-man/configuration/get $Configuration ssid ]; - :local OldPsk [ /caps-man/access-list/get $AccList private-passphrase ]; - :local Skip 0; + :local Seen ({}); + :local Date [ /system/clock/get date ]; + :local NewPsk [ $GeneratePSK $Date ]; - :if ($NewPsk != $OldPsk) do={ - $LogPrintExit2 info $0 ("Updating daily PSK for " . $Ssid . " to " . $NewPsk . " (was " . $OldPsk . ")") false; - /caps-man/access-list/set $AccList private-passphrase=$NewPsk; + :foreach AccList in=[ /caps-man/access-list/find where comment~$DailyPskMatchComment ] do={ + :local SsidRegExp [ /caps-man/access-list/get $AccList ssid-regexp ]; + :local Configuration ([ /caps-man/configuration/find where ssid~$SsidRegExp ]->0); + :local Ssid [ /caps-man/configuration/get $Configuration ssid ]; + :local OldPsk [ /caps-man/access-list/get $AccList private-passphrase ]; + :local Skip 0; - :if ([ :len [ /caps-man/actual-interface-configuration/find where configuration.ssid=$Ssid !disabled ] ] > 0) do={ - :if ($Seen->$Ssid = 1) do={ - $LogPrintExit2 debug $0 ("Already sent a mail for SSID " . $Ssid . ", skipping.") false; - } else={ - :local Link ($DailyPskQrCodeUrl . \ - "?scale=8&level=1&ssid=" . [ $UrlEncode $Ssid ] . "&pass=" . [ $UrlEncode $NewPsk ]); - $SendNotification2 ({ origin=$0; \ - subject=([ $SymbolForNotification "calendar" ] . "daily PSK " . $Ssid); \ - message=("This is the daily PSK on " . $Identity . ":\n\n" . \ - [ $FormatLine "SSID" $Ssid ] . "\n" . \ - [ $FormatLine "PSK" $NewPsk ] . "\n" . \ - [ $FormatLine "Date" $Date ] . "\n\n" . \ - "A client device specific rule must not exist!"); link=$Link }); - :set ($Seen->$Ssid) 1; + :if ($NewPsk != $OldPsk) do={ + $LogPrint info $ScriptName ("Updating daily PSK for '" . $Ssid . "' to '" . $NewPsk . "' (was '" . $OldPsk . "')"); + /caps-man/access-list/set $AccList private-passphrase=$NewPsk; + + :if ([ :len [ /caps-man/actual-interface-configuration/find where configuration.ssid=$Ssid !disabled ] ] > 0) do={ + :if ($Seen->$Ssid = 1) do={ + $LogPrint debug $ScriptName ("Already sent a mail for SSID " . $Ssid . ", skipping."); + } else={ + :local Link ($DailyPskQrCodeUrl . \ + "?scale=8&level=1&ssid=" . [ $UrlEncode $Ssid ] . "&pass=" . [ $UrlEncode $NewPsk ]); + $SendNotification2 ({ origin=$ScriptName; \ + subject=([ $SymbolForNotification "calendar" ] . "daily PSK " . $Ssid); \ + message=("This is the daily PSK on " . $Identity . ":\n\n" . \ + [ $FormatLine "SSID" $Ssid 8 ] . "\n" . \ + [ $FormatLine "PSK" $NewPsk 8 ] . "\n" . \ + [ $FormatLine "Date" $Date 8 ] . "\n\n" . \ + "A client device specific rule must not exist!"); link=$Link }); + :set ($Seen->$Ssid) 1; + } } } } +} on-error={ + :global ExitError; $ExitError $ExitOK [ :jobname ]; } diff --git a/daily-psk.local.rsc b/daily-psk.local.rsc index 523e026..9dea469 100644 --- a/daily-psk.local.rsc +++ b/daily-psk.local.rsc @@ -1,84 +1,95 @@ #!rsc by RouterOS # RouterOS script: daily-psk.local -# Copyright (c) 2013-2023 Christian Hesse +# Copyright (c) 2013-2025 Christian Hesse # Michael Gisbers -# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md +# https://rsc.eworm.de/COPYING.md +# +# requires RouterOS, version=7.15 # # update daily PSK (pre shared key) -# https://git.eworm.de/cgit/routeros-scripts/about/doc/daily-psk.md +# https://rsc.eworm.de/doc/daily-psk.md # # !! Do not edit this file, it is generated from template! -:local 0 "daily-psk.local"; :global GlobalFunctionsReady; :while ($GlobalFunctionsReady != true) do={ :delay 500ms; } -:global DailyPskMatchComment; -:global DailyPskQrCodeUrl; -:global Identity; +:local ExitOK false; +:do { + :local ScriptName [ :jobname ]; -:global FormatLine; -:global LogPrintExit2; -:global ScriptLock; -:global SendNotification2; -:global SymbolForNotification; -:global UrlEncode; -:global WaitForFile; -:global WaitFullyConnected; + :global DailyPskMatchComment; + :global DailyPskQrCodeUrl; + :global Identity; -$ScriptLock $0; -$WaitFullyConnected; + :global FormatLine; + :global LogPrint; + :global ScriptLock; + :global SendNotification2; + :global SymbolForNotification; + :global UrlEncode; + :global WaitForFile; + :global WaitFullyConnected; -# return pseudo-random string for PSK -:local GeneratePSK do={ - :local Date [ :tostr $1 ]; + :if ([ $ScriptLock $ScriptName ] = false) do={ + :set ExitOK true; + :error false; + } + $WaitFullyConnected; - :global DailyPskSecrets; + # return pseudo-random string for PSK + :local GeneratePSK do={ + :local Date [ :tostr $1 ]; - :global ParseDate; + :global DailyPskSecrets; - :set Date [ $ParseDate $Date ]; + :global ParseDate; - :local A ((14 - ($Date->"month")) / 12); - :local B (($Date->"year") - $A); - :local C (($Date->"month") + 12 * $A - 2); - :local WeekDay (7000 + ($Date->"day") + $B + ($B / 4) - ($B / 100) + ($B / 400) + ((31 * $C) / 12)); - :set WeekDay ($WeekDay - (($WeekDay / 7) * 7)); + :set Date [ $ParseDate $Date ]; - :return (($DailyPskSecrets->0->(($Date->"day") - 1)) . \ - ($DailyPskSecrets->1->(($Date->"month") - 1)) . \ - ($DailyPskSecrets->2->$WeekDay)); -} + :local A ((14 - ($Date->"month")) / 12); + :local B (($Date->"year") - $A); + :local C (($Date->"month") + 12 * $A - 2); + :local WeekDay (7000 + ($Date->"day") + $B + ($B / 4) - ($B / 100) + ($B / 400) + ((31 * $C) / 12)); + :set WeekDay ($WeekDay - (($WeekDay / 7) * 7)); -:local Seen ({}); -:local Date [ /system/clock/get date ]; -:local NewPsk [ $GeneratePSK $Date ]; + :return (($DailyPskSecrets->0->(($Date->"day") - 1)) . \ + ($DailyPskSecrets->1->(($Date->"month") - 1)) . \ + ($DailyPskSecrets->2->$WeekDay)); + } -:foreach AccList in=[ /interface/wireless/access-list/find where comment~$DailyPskMatchComment ] do={ - :local IntName [ /interface/wireless/access-list/get $AccList interface ]; - :local Ssid [ /interface/wireless/get $IntName ssid ]; - :local OldPsk [ /interface/wireless/access-list/get $AccList private-pre-shared-key ]; - :local Skip 0; + :local Seen ({}); + :local Date [ /system/clock/get date ]; + :local NewPsk [ $GeneratePSK $Date ]; - :if ($NewPsk != $OldPsk) do={ - $LogPrintExit2 info $0 ("Updating daily PSK for " . $Ssid . " to " . $NewPsk . " (was " . $OldPsk . ")") false; - /interface/wireless/access-list/set $AccList private-pre-shared-key=$NewPsk; + :foreach AccList in=[ /interface/wireless/access-list/find where comment~$DailyPskMatchComment ] do={ + :local IntName [ /interface/wireless/access-list/get $AccList interface ]; + :local Ssid [ /interface/wireless/get $IntName ssid ]; + :local OldPsk [ /interface/wireless/access-list/get $AccList private-pre-shared-key ]; + :local Skip 0; - :if ([ :len [ /interface/wireless/find where name=$IntName !disabled ] ] = 1) do={ - :if ($Seen->$Ssid = 1) do={ - $LogPrintExit2 debug $0 ("Already sent a mail for SSID " . $Ssid . ", skipping.") false; - } else={ - :local Link ($DailyPskQrCodeUrl . \ - "?scale=8&level=1&ssid=" . [ $UrlEncode $Ssid ] . "&pass=" . [ $UrlEncode $NewPsk ]); - $SendNotification2 ({ origin=$0; \ - subject=([ $SymbolForNotification "calendar" ] . "daily PSK " . $Ssid); \ - message=("This is the daily PSK on " . $Identity . ":\n\n" . \ - [ $FormatLine "SSID" $Ssid ] . "\n" . \ - [ $FormatLine "PSK" $NewPsk ] . "\n" . \ - [ $FormatLine "Date" $Date ] . "\n\n" . \ - "A client device specific rule must not exist!"); link=$Link }); - :set ($Seen->$Ssid) 1; + :if ($NewPsk != $OldPsk) do={ + $LogPrint info $ScriptName ("Updating daily PSK for '" . $Ssid . "' to '" . $NewPsk . "' (was '" . $OldPsk . "')"); + /interface/wireless/access-list/set $AccList private-pre-shared-key=$NewPsk; + + :if ([ :len [ /interface/wireless/find where name=$IntName !disabled ] ] = 1) do={ + :if ($Seen->$Ssid = 1) do={ + $LogPrint debug $ScriptName ("Already sent a mail for SSID " . $Ssid . ", skipping."); + } else={ + :local Link ($DailyPskQrCodeUrl . \ + "?scale=8&level=1&ssid=" . [ $UrlEncode $Ssid ] . "&pass=" . [ $UrlEncode $NewPsk ]); + $SendNotification2 ({ origin=$ScriptName; \ + subject=([ $SymbolForNotification "calendar" ] . "daily PSK " . $Ssid); \ + message=("This is the daily PSK on " . $Identity . ":\n\n" . \ + [ $FormatLine "SSID" $Ssid 8 ] . "\n" . \ + [ $FormatLine "PSK" $NewPsk 8 ] . "\n" . \ + [ $FormatLine "Date" $Date 8 ] . "\n\n" . \ + "A client device specific rule must not exist!"); link=$Link }); + :set ($Seen->$Ssid) 1; + } } } } +} on-error={ + :global ExitError; $ExitError $ExitOK [ :jobname ]; } diff --git a/daily-psk.template.rsc b/daily-psk.template.rsc index ad9198a..8202eeb 100644 --- a/daily-psk.template.rsc +++ b/daily-psk.template.rsc @@ -1,100 +1,111 @@ #!rsc by RouterOS # RouterOS script: daily-psk%TEMPL% -# Copyright (c) 2013-2023 Christian Hesse +# Copyright (c) 2013-2025 Christian Hesse # Michael Gisbers -# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md +# https://rsc.eworm.de/COPYING.md +# +# requires RouterOS, version=7.15 # # update daily PSK (pre shared key) -# https://git.eworm.de/cgit/routeros-scripts/about/doc/daily-psk.md +# https://rsc.eworm.de/doc/daily-psk.md # # !! This is just a template to generate the real script! # !! Pattern '%TEMPL%' is replaced, paths are filtered. -:local 0 "daily-psk%TEMPL%"; :global GlobalFunctionsReady; :while ($GlobalFunctionsReady != true) do={ :delay 500ms; } -:global DailyPskMatchComment; -:global DailyPskQrCodeUrl; -:global Identity; +:local ExitOK false; +:do { + :local ScriptName [ :jobname ]; -:global FormatLine; -:global LogPrintExit2; -:global ScriptLock; -:global SendNotification2; -:global SymbolForNotification; -:global UrlEncode; -:global WaitForFile; -:global WaitFullyConnected; + :global DailyPskMatchComment; + :global DailyPskQrCodeUrl; + :global Identity; -$ScriptLock $0; -$WaitFullyConnected; + :global FormatLine; + :global LogPrint; + :global ScriptLock; + :global SendNotification2; + :global SymbolForNotification; + :global UrlEncode; + :global WaitForFile; + :global WaitFullyConnected; -# return pseudo-random string for PSK -:local GeneratePSK do={ - :local Date [ :tostr $1 ]; + :if ([ $ScriptLock $ScriptName ] = false) do={ + :set ExitOK true; + :error false; + } + $WaitFullyConnected; - :global DailyPskSecrets; + # return pseudo-random string for PSK + :local GeneratePSK do={ + :local Date [ :tostr $1 ]; - :global ParseDate; + :global DailyPskSecrets; - :set Date [ $ParseDate $Date ]; + :global ParseDate; - :local A ((14 - ($Date->"month")) / 12); - :local B (($Date->"year") - $A); - :local C (($Date->"month") + 12 * $A - 2); - :local WeekDay (7000 + ($Date->"day") + $B + ($B / 4) - ($B / 100) + ($B / 400) + ((31 * $C) / 12)); - :set WeekDay ($WeekDay - (($WeekDay / 7) * 7)); + :set Date [ $ParseDate $Date ]; - :return (($DailyPskSecrets->0->(($Date->"day") - 1)) . \ - ($DailyPskSecrets->1->(($Date->"month") - 1)) . \ - ($DailyPskSecrets->2->$WeekDay)); -} + :local A ((14 - ($Date->"month")) / 12); + :local B (($Date->"year") - $A); + :local C (($Date->"month") + 12 * $A - 2); + :local WeekDay (7000 + ($Date->"day") + $B + ($B / 4) - ($B / 100) + ($B / 400) + ((31 * $C) / 12)); + :set WeekDay ($WeekDay - (($WeekDay / 7) * 7)); -:local Seen ({}); -:local Date [ /system/clock/get date ]; -:local NewPsk [ $GeneratePSK $Date ]; + :return (($DailyPskSecrets->0->(($Date->"day") - 1)) . \ + ($DailyPskSecrets->1->(($Date->"month") - 1)) . \ + ($DailyPskSecrets->2->$WeekDay)); + } -:foreach AccList in=[ /caps-man/access-list/find where comment~$DailyPskMatchComment ] do={ -:foreach AccList in=[ /interface/wifiwave2/access-list/find where comment~$DailyPskMatchComment ] do={ -:foreach AccList in=[ /interface/wireless/access-list/find where comment~$DailyPskMatchComment ] do={ - :local SsidRegExp [ /caps-man/access-list/get $AccList ssid-regexp ]; - :local SsidRegExp [ /interface/wifiwave2/access-list/get $AccList ssid-regexp ]; - :local Configuration ([ /caps-man/configuration/find where ssid~$SsidRegExp ]->0); - :local Configuration ([ /interface/wifiwave2/configuration/find where ssid~$SsidRegExp ]->0); - :local Ssid [ /caps-man/configuration/get $Configuration ssid ]; - :local Ssid [ /interface/wifiwave2/configuration/get $Configuration ssid ]; - :local OldPsk [ /caps-man/access-list/get $AccList private-passphrase ]; - :local OldPsk [ /interface/wifiwave2/access-list/get $AccList passphrase ]; - # /caps-man /interface/wifiwave2 above - /interface/wireless below - :local IntName [ /interface/wireless/access-list/get $AccList interface ]; - :local Ssid [ /interface/wireless/get $IntName ssid ]; - :local OldPsk [ /interface/wireless/access-list/get $AccList private-pre-shared-key ]; - :local Skip 0; + :local Seen ({}); + :local Date [ /system/clock/get date ]; + :local NewPsk [ $GeneratePSK $Date ]; - :if ($NewPsk != $OldPsk) do={ - $LogPrintExit2 info $0 ("Updating daily PSK for " . $Ssid . " to " . $NewPsk . " (was " . $OldPsk . ")") false; - /caps-man/access-list/set $AccList private-passphrase=$NewPsk; - /interface/wifiwave2/access-list/set $AccList passphrase=$NewPsk; - /interface/wireless/access-list/set $AccList private-pre-shared-key=$NewPsk; + :foreach AccList in=[ /caps-man/access-list/find where comment~$DailyPskMatchComment ] do={ + :foreach AccList in=[ /interface/wifi/access-list/find where comment~$DailyPskMatchComment ] do={ + :foreach AccList in=[ /interface/wireless/access-list/find where comment~$DailyPskMatchComment ] do={ + :local SsidRegExp [ /caps-man/access-list/get $AccList ssid-regexp ]; + :local SsidRegExp [ /interface/wifi/access-list/get $AccList ssid-regexp ]; + :local Configuration ([ /caps-man/configuration/find where ssid~$SsidRegExp ]->0); + :local Configuration ([ /interface/wifi/configuration/find where ssid~$SsidRegExp ]->0); + :local Ssid [ /caps-man/configuration/get $Configuration ssid ]; + :local Ssid [ /interface/wifi/configuration/get $Configuration ssid ]; + :local OldPsk [ /caps-man/access-list/get $AccList private-passphrase ]; + :local OldPsk [ /interface/wifi/access-list/get $AccList passphrase ]; + # /caps-man/ /interface/wifi/ above - /interface/wireless/ below + :local IntName [ /interface/wireless/access-list/get $AccList interface ]; + :local Ssid [ /interface/wireless/get $IntName ssid ]; + :local OldPsk [ /interface/wireless/access-list/get $AccList private-pre-shared-key ]; + :local Skip 0; - :if ([ :len [ /caps-man/actual-interface-configuration/find where configuration.ssid=$Ssid !disabled ] ] > 0) do={ - :if ([ :len [ /interface/wifiwave2/actual-configuration/find where configuration.ssid=$Ssid ] ] > 0) do={ - :if ([ :len [ /interface/wireless/find where name=$IntName !disabled ] ] = 1) do={ - :if ($Seen->$Ssid = 1) do={ - $LogPrintExit2 debug $0 ("Already sent a mail for SSID " . $Ssid . ", skipping.") false; - } else={ - :local Link ($DailyPskQrCodeUrl . \ - "?scale=8&level=1&ssid=" . [ $UrlEncode $Ssid ] . "&pass=" . [ $UrlEncode $NewPsk ]); - $SendNotification2 ({ origin=$0; \ - subject=([ $SymbolForNotification "calendar" ] . "daily PSK " . $Ssid); \ - message=("This is the daily PSK on " . $Identity . ":\n\n" . \ - [ $FormatLine "SSID" $Ssid ] . "\n" . \ - [ $FormatLine "PSK" $NewPsk ] . "\n" . \ - [ $FormatLine "Date" $Date ] . "\n\n" . \ - "A client device specific rule must not exist!"); link=$Link }); - :set ($Seen->$Ssid) 1; + :if ($NewPsk != $OldPsk) do={ + $LogPrint info $ScriptName ("Updating daily PSK for '" . $Ssid . "' to '" . $NewPsk . "' (was '" . $OldPsk . "')"); + /caps-man/access-list/set $AccList private-passphrase=$NewPsk; + /interface/wifi/access-list/set $AccList passphrase=$NewPsk; + /interface/wireless/access-list/set $AccList private-pre-shared-key=$NewPsk; + + :if ([ :len [ /caps-man/actual-interface-configuration/find where configuration.ssid=$Ssid !disabled ] ] > 0) do={ + :if ([ :len [ /interface/wifi/find where configuration.ssid=$Ssid !disabled ] ] > 0) do={ + :if ([ :len [ /interface/wireless/find where name=$IntName !disabled ] ] = 1) do={ + :if ($Seen->$Ssid = 1) do={ + $LogPrint debug $ScriptName ("Already sent a mail for SSID " . $Ssid . ", skipping."); + } else={ + :local Link ($DailyPskQrCodeUrl . \ + "?scale=8&level=1&ssid=" . [ $UrlEncode $Ssid ] . "&pass=" . [ $UrlEncode $NewPsk ]); + $SendNotification2 ({ origin=$ScriptName; \ + subject=([ $SymbolForNotification "calendar" ] . "daily PSK " . $Ssid); \ + message=("This is the daily PSK on " . $Identity . ":\n\n" . \ + [ $FormatLine "SSID" $Ssid 8 ] . "\n" . \ + [ $FormatLine "PSK" $NewPsk 8 ] . "\n" . \ + [ $FormatLine "Date" $Date 8 ] . "\n\n" . \ + "A client device specific rule must not exist!"); link=$Link }); + :set ($Seen->$Ssid) 1; + } } } } +} on-error={ + :global ExitError; $ExitError $ExitOK [ :jobname ]; } diff --git a/daily-psk.wifi.rsc b/daily-psk.wifi.rsc new file mode 100644 index 0000000..3de3c5b --- /dev/null +++ b/daily-psk.wifi.rsc @@ -0,0 +1,96 @@ +#!rsc by RouterOS +# RouterOS script: daily-psk.wifi +# Copyright (c) 2013-2025 Christian Hesse +# Michael Gisbers +# https://rsc.eworm.de/COPYING.md +# +# requires RouterOS, version=7.15 +# +# update daily PSK (pre shared key) +# https://rsc.eworm.de/doc/daily-psk.md +# +# !! Do not edit this file, it is generated from template! + +:global GlobalFunctionsReady; +:while ($GlobalFunctionsReady != true) do={ :delay 500ms; } + +:local ExitOK false; +:do { + :local ScriptName [ :jobname ]; + + :global DailyPskMatchComment; + :global DailyPskQrCodeUrl; + :global Identity; + + :global FormatLine; + :global LogPrint; + :global ScriptLock; + :global SendNotification2; + :global SymbolForNotification; + :global UrlEncode; + :global WaitForFile; + :global WaitFullyConnected; + + :if ([ $ScriptLock $ScriptName ] = false) do={ + :set ExitOK true; + :error false; + } + $WaitFullyConnected; + + # return pseudo-random string for PSK + :local GeneratePSK do={ + :local Date [ :tostr $1 ]; + + :global DailyPskSecrets; + + :global ParseDate; + + :set Date [ $ParseDate $Date ]; + + :local A ((14 - ($Date->"month")) / 12); + :local B (($Date->"year") - $A); + :local C (($Date->"month") + 12 * $A - 2); + :local WeekDay (7000 + ($Date->"day") + $B + ($B / 4) - ($B / 100) + ($B / 400) + ((31 * $C) / 12)); + :set WeekDay ($WeekDay - (($WeekDay / 7) * 7)); + + :return (($DailyPskSecrets->0->(($Date->"day") - 1)) . \ + ($DailyPskSecrets->1->(($Date->"month") - 1)) . \ + ($DailyPskSecrets->2->$WeekDay)); + } + + :local Seen ({}); + :local Date [ /system/clock/get date ]; + :local NewPsk [ $GeneratePSK $Date ]; + + :foreach AccList in=[ /interface/wifi/access-list/find where comment~$DailyPskMatchComment ] do={ + :local SsidRegExp [ /interface/wifi/access-list/get $AccList ssid-regexp ]; + :local Configuration ([ /interface/wifi/configuration/find where ssid~$SsidRegExp ]->0); + :local Ssid [ /interface/wifi/configuration/get $Configuration ssid ]; + :local OldPsk [ /interface/wifi/access-list/get $AccList passphrase ]; + :local Skip 0; + + :if ($NewPsk != $OldPsk) do={ + $LogPrint info $ScriptName ("Updating daily PSK for '" . $Ssid . "' to '" . $NewPsk . "' (was '" . $OldPsk . "')"); + /interface/wifi/access-list/set $AccList passphrase=$NewPsk; + + :if ([ :len [ /interface/wifi/find where configuration.ssid=$Ssid !disabled ] ] > 0) do={ + :if ($Seen->$Ssid = 1) do={ + $LogPrint debug $ScriptName ("Already sent a mail for SSID " . $Ssid . ", skipping."); + } else={ + :local Link ($DailyPskQrCodeUrl . \ + "?scale=8&level=1&ssid=" . [ $UrlEncode $Ssid ] . "&pass=" . [ $UrlEncode $NewPsk ]); + $SendNotification2 ({ origin=$ScriptName; \ + subject=([ $SymbolForNotification "calendar" ] . "daily PSK " . $Ssid); \ + message=("This is the daily PSK on " . $Identity . ":\n\n" . \ + [ $FormatLine "SSID" $Ssid 8 ] . "\n" . \ + [ $FormatLine "PSK" $NewPsk 8 ] . "\n" . \ + [ $FormatLine "Date" $Date 8 ] . "\n\n" . \ + "A client device specific rule must not exist!"); link=$Link }); + :set ($Seen->$Ssid) 1; + } + } + } + } +} on-error={ + :global ExitError; $ExitError $ExitOK [ :jobname ]; +} diff --git a/daily-psk.wifiwave2.rsc b/daily-psk.wifiwave2.rsc deleted file mode 100644 index 1be000d..0000000 --- a/daily-psk.wifiwave2.rsc +++ /dev/null @@ -1,85 +0,0 @@ -#!rsc by RouterOS -# RouterOS script: daily-psk.wifiwave2 -# Copyright (c) 2013-2023 Christian Hesse -# Michael Gisbers -# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md -# -# update daily PSK (pre shared key) -# https://git.eworm.de/cgit/routeros-scripts/about/doc/daily-psk.md -# -# !! Do not edit this file, it is generated from template! - -:local 0 "daily-psk.wifiwave2"; -:global GlobalFunctionsReady; -:while ($GlobalFunctionsReady != true) do={ :delay 500ms; } - -:global DailyPskMatchComment; -:global DailyPskQrCodeUrl; -:global Identity; - -:global FormatLine; -:global LogPrintExit2; -:global ScriptLock; -:global SendNotification2; -:global SymbolForNotification; -:global UrlEncode; -:global WaitForFile; -:global WaitFullyConnected; - -$ScriptLock $0; -$WaitFullyConnected; - -# return pseudo-random string for PSK -:local GeneratePSK do={ - :local Date [ :tostr $1 ]; - - :global DailyPskSecrets; - - :global ParseDate; - - :set Date [ $ParseDate $Date ]; - - :local A ((14 - ($Date->"month")) / 12); - :local B (($Date->"year") - $A); - :local C (($Date->"month") + 12 * $A - 2); - :local WeekDay (7000 + ($Date->"day") + $B + ($B / 4) - ($B / 100) + ($B / 400) + ((31 * $C) / 12)); - :set WeekDay ($WeekDay - (($WeekDay / 7) * 7)); - - :return (($DailyPskSecrets->0->(($Date->"day") - 1)) . \ - ($DailyPskSecrets->1->(($Date->"month") - 1)) . \ - ($DailyPskSecrets->2->$WeekDay)); -} - -:local Seen ({}); -:local Date [ /system/clock/get date ]; -:local NewPsk [ $GeneratePSK $Date ]; - -:foreach AccList in=[ /interface/wifiwave2/access-list/find where comment~$DailyPskMatchComment ] do={ - :local SsidRegExp [ /interface/wifiwave2/access-list/get $AccList ssid-regexp ]; - :local Configuration ([ /interface/wifiwave2/configuration/find where ssid~$SsidRegExp ]->0); - :local Ssid [ /interface/wifiwave2/configuration/get $Configuration ssid ]; - :local OldPsk [ /interface/wifiwave2/access-list/get $AccList passphrase ]; - :local Skip 0; - - :if ($NewPsk != $OldPsk) do={ - $LogPrintExit2 info $0 ("Updating daily PSK for " . $Ssid . " to " . $NewPsk . " (was " . $OldPsk . ")") false; - /interface/wifiwave2/access-list/set $AccList passphrase=$NewPsk; - - :if ([ :len [ /interface/wifiwave2/actual-configuration/find where configuration.ssid=$Ssid ] ] > 0) do={ - :if ($Seen->$Ssid = 1) do={ - $LogPrintExit2 debug $0 ("Already sent a mail for SSID " . $Ssid . ", skipping.") false; - } else={ - :local Link ($DailyPskQrCodeUrl . \ - "?scale=8&level=1&ssid=" . [ $UrlEncode $Ssid ] . "&pass=" . [ $UrlEncode $NewPsk ]); - $SendNotification2 ({ origin=$0; \ - subject=([ $SymbolForNotification "calendar" ] . "daily PSK " . $Ssid); \ - message=("This is the daily PSK on " . $Identity . ":\n\n" . \ - [ $FormatLine "SSID" $Ssid ] . "\n" . \ - [ $FormatLine "PSK" $NewPsk ] . "\n" . \ - [ $FormatLine "Date" $Date ] . "\n\n" . \ - "A client device specific rule must not exist!"); link=$Link }); - :set ($Seen->$Ssid) 1; - } - } - } -} diff --git a/dhcp-lease-comment.capsman.rsc b/dhcp-lease-comment.capsman.rsc index 89d2c4e..36b31c8 100644 --- a/dhcp-lease-comment.capsman.rsc +++ b/dhcp-lease-comment.capsman.rsc @@ -1,33 +1,43 @@ #!rsc by RouterOS # RouterOS script: dhcp-lease-comment.capsman -# Copyright (c) 2013-2023 Christian Hesse -# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md +# Copyright (c) 2013-2025 Christian Hesse +# https://rsc.eworm.de/COPYING.md # # provides: lease-script, order=60 +# requires RouterOS, version=7.15 # # update dhcp-server lease comment with infos from access-list -# https://git.eworm.de/cgit/routeros-scripts/about/doc/dhcp-lease-comment.md +# https://rsc.eworm.de/doc/dhcp-lease-comment.md # # !! Do not edit this file, it is generated from template! -:local 0 "dhcp-lease-comment.capsman"; :global GlobalFunctionsReady; :while ($GlobalFunctionsReady != true) do={ :delay 500ms; } -:global LogPrintExit2; -:global ScriptLock; +:local ExitOK false; +:do { + :local ScriptName [ :jobname ]; -$ScriptLock $0; + :global LogPrint; + :global ScriptLock; -:foreach Lease in=[ /ip/dhcp-server/lease/find where dynamic=yes status=bound ] do={ - :local LeaseVal [ /ip/dhcp-server/lease/get $Lease ]; - :local NewComment; - :local AccessList ([ /caps-man/access-list/find where mac-address=($LeaseVal->"active-mac-address") ]->0); - :if ([ :len $AccessList ] > 0) do={ - :set NewComment [ /caps-man/access-list/get $AccessList comment ]; + :if ([ $ScriptLock $ScriptName ] = false) do={ + :set ExitOK true; + :error false; } - :if ([ :len $NewComment ] != 0 && $LeaseVal->"comment" != $NewComment) do={ - $LogPrintExit2 info $0 ("Updating comment for DHCP lease " . $LeaseVal->"active-mac-address" . ": " . $NewComment) false; - /ip/dhcp-server/lease/set comment=$NewComment $Lease; + + :foreach Lease in=[ /ip/dhcp-server/lease/find where dynamic=yes status=bound ] do={ + :local LeaseVal [ /ip/dhcp-server/lease/get $Lease ]; + :local NewComment; + :local AccessList ([ /caps-man/access-list/find where mac-address=($LeaseVal->"active-mac-address") ]->0); + :if ([ :len $AccessList ] > 0) do={ + :set NewComment [ /caps-man/access-list/get $AccessList comment ]; + } + :if ([ :len $NewComment ] != 0 && $LeaseVal->"comment" != $NewComment) do={ + $LogPrint info $ScriptName ("Updating comment for DHCP lease " . $LeaseVal->"active-mac-address" . ": " . $NewComment); + /ip/dhcp-server/lease/set comment=$NewComment $Lease; + } } +} on-error={ + :global ExitError; $ExitError $ExitOK [ :jobname ]; } diff --git a/dhcp-lease-comment.local.rsc b/dhcp-lease-comment.local.rsc index 11e92cc..35dc6f6 100644 --- a/dhcp-lease-comment.local.rsc +++ b/dhcp-lease-comment.local.rsc @@ -1,33 +1,43 @@ #!rsc by RouterOS # RouterOS script: dhcp-lease-comment.local -# Copyright (c) 2013-2023 Christian Hesse -# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md +# Copyright (c) 2013-2025 Christian Hesse +# https://rsc.eworm.de/COPYING.md # # provides: lease-script, order=60 +# requires RouterOS, version=7.15 # # update dhcp-server lease comment with infos from access-list -# https://git.eworm.de/cgit/routeros-scripts/about/doc/dhcp-lease-comment.md +# https://rsc.eworm.de/doc/dhcp-lease-comment.md # # !! Do not edit this file, it is generated from template! -:local 0 "dhcp-lease-comment.local"; :global GlobalFunctionsReady; :while ($GlobalFunctionsReady != true) do={ :delay 500ms; } -:global LogPrintExit2; -:global ScriptLock; +:local ExitOK false; +:do { + :local ScriptName [ :jobname ]; -$ScriptLock $0; + :global LogPrint; + :global ScriptLock; -:foreach Lease in=[ /ip/dhcp-server/lease/find where dynamic=yes status=bound ] do={ - :local LeaseVal [ /ip/dhcp-server/lease/get $Lease ]; - :local NewComment; - :local AccessList ([ /interface/wireless/access-list/find where mac-address=($LeaseVal->"active-mac-address") ]->0); - :if ([ :len $AccessList ] > 0) do={ - :set NewComment [ /interface/wireless/access-list/get $AccessList comment ]; + :if ([ $ScriptLock $ScriptName ] = false) do={ + :set ExitOK true; + :error false; } - :if ([ :len $NewComment ] != 0 && $LeaseVal->"comment" != $NewComment) do={ - $LogPrintExit2 info $0 ("Updating comment for DHCP lease " . $LeaseVal->"active-mac-address" . ": " . $NewComment) false; - /ip/dhcp-server/lease/set comment=$NewComment $Lease; + + :foreach Lease in=[ /ip/dhcp-server/lease/find where dynamic=yes status=bound ] do={ + :local LeaseVal [ /ip/dhcp-server/lease/get $Lease ]; + :local NewComment; + :local AccessList ([ /interface/wireless/access-list/find where mac-address=($LeaseVal->"active-mac-address") ]->0); + :if ([ :len $AccessList ] > 0) do={ + :set NewComment [ /interface/wireless/access-list/get $AccessList comment ]; + } + :if ([ :len $NewComment ] != 0 && $LeaseVal->"comment" != $NewComment) do={ + $LogPrint info $ScriptName ("Updating comment for DHCP lease " . $LeaseVal->"active-mac-address" . ": " . $NewComment); + /ip/dhcp-server/lease/set comment=$NewComment $Lease; + } } +} on-error={ + :global ExitError; $ExitError $ExitOK [ :jobname ]; } diff --git a/dhcp-lease-comment.template.rsc b/dhcp-lease-comment.template.rsc index 0f16285..47a8554 100644 --- a/dhcp-lease-comment.template.rsc +++ b/dhcp-lease-comment.template.rsc @@ -1,38 +1,48 @@ #!rsc by RouterOS # RouterOS script: dhcp-lease-comment%TEMPL% -# Copyright (c) 2013-2023 Christian Hesse -# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md +# Copyright (c) 2013-2025 Christian Hesse +# https://rsc.eworm.de/COPYING.md # # provides: lease-script, order=60 +# requires RouterOS, version=7.15 # # update dhcp-server lease comment with infos from access-list -# https://git.eworm.de/cgit/routeros-scripts/about/doc/dhcp-lease-comment.md +# https://rsc.eworm.de/doc/dhcp-lease-comment.md # # !! This is just a template to generate the real script! # !! Pattern '%TEMPL%' is replaced, paths are filtered. -:local 0 "dhcp-lease-comment%TEMPL%"; :global GlobalFunctionsReady; :while ($GlobalFunctionsReady != true) do={ :delay 500ms; } -:global LogPrintExit2; -:global ScriptLock; +:local ExitOK false; +:do { + :local ScriptName [ :jobname ]; -$ScriptLock $0; + :global LogPrint; + :global ScriptLock; -:foreach Lease in=[ /ip/dhcp-server/lease/find where dynamic=yes status=bound ] do={ - :local LeaseVal [ /ip/dhcp-server/lease/get $Lease ]; - :local NewComment; - :local AccessList ([ /caps-man/access-list/find where mac-address=($LeaseVal->"active-mac-address") ]->0); - :local AccessList ([ /interface/wifiwave2/access-list/find where mac-address=($LeaseVal->"active-mac-address") ]->0); - :local AccessList ([ /interface/wireless/access-list/find where mac-address=($LeaseVal->"active-mac-address") ]->0); - :if ([ :len $AccessList ] > 0) do={ - :set NewComment [ /caps-man/access-list/get $AccessList comment ]; - :set NewComment [ /interface/wifiwave2/access-list/get $AccessList comment ]; - :set NewComment [ /interface/wireless/access-list/get $AccessList comment ]; + :if ([ $ScriptLock $ScriptName ] = false) do={ + :set ExitOK true; + :error false; } - :if ([ :len $NewComment ] != 0 && $LeaseVal->"comment" != $NewComment) do={ - $LogPrintExit2 info $0 ("Updating comment for DHCP lease " . $LeaseVal->"active-mac-address" . ": " . $NewComment) false; - /ip/dhcp-server/lease/set comment=$NewComment $Lease; + + :foreach Lease in=[ /ip/dhcp-server/lease/find where dynamic=yes status=bound ] do={ + :local LeaseVal [ /ip/dhcp-server/lease/get $Lease ]; + :local NewComment; + :local AccessList ([ /caps-man/access-list/find where mac-address=($LeaseVal->"active-mac-address") ]->0); + :local AccessList ([ /interface/wifi/access-list/find where mac-address=($LeaseVal->"active-mac-address") ]->0); + :local AccessList ([ /interface/wireless/access-list/find where mac-address=($LeaseVal->"active-mac-address") ]->0); + :if ([ :len $AccessList ] > 0) do={ + :set NewComment [ /caps-man/access-list/get $AccessList comment ]; + :set NewComment [ /interface/wifi/access-list/get $AccessList comment ]; + :set NewComment [ /interface/wireless/access-list/get $AccessList comment ]; + } + :if ([ :len $NewComment ] != 0 && $LeaseVal->"comment" != $NewComment) do={ + $LogPrint info $ScriptName ("Updating comment for DHCP lease " . $LeaseVal->"active-mac-address" . ": " . $NewComment); + /ip/dhcp-server/lease/set comment=$NewComment $Lease; + } } +} on-error={ + :global ExitError; $ExitError $ExitOK [ :jobname ]; } diff --git a/dhcp-lease-comment.wifi.rsc b/dhcp-lease-comment.wifi.rsc new file mode 100644 index 0000000..e0f9785 --- /dev/null +++ b/dhcp-lease-comment.wifi.rsc @@ -0,0 +1,43 @@ +#!rsc by RouterOS +# RouterOS script: dhcp-lease-comment.wifi +# Copyright (c) 2013-2025 Christian Hesse +# https://rsc.eworm.de/COPYING.md +# +# provides: lease-script, order=60 +# requires RouterOS, version=7.15 +# +# update dhcp-server lease comment with infos from access-list +# https://rsc.eworm.de/doc/dhcp-lease-comment.md +# +# !! Do not edit this file, it is generated from template! + +:global GlobalFunctionsReady; +:while ($GlobalFunctionsReady != true) do={ :delay 500ms; } + +:local ExitOK false; +:do { + :local ScriptName [ :jobname ]; + + :global LogPrint; + :global ScriptLock; + + :if ([ $ScriptLock $ScriptName ] = false) do={ + :set ExitOK true; + :error false; + } + + :foreach Lease in=[ /ip/dhcp-server/lease/find where dynamic=yes status=bound ] do={ + :local LeaseVal [ /ip/dhcp-server/lease/get $Lease ]; + :local NewComment; + :local AccessList ([ /interface/wifi/access-list/find where mac-address=($LeaseVal->"active-mac-address") ]->0); + :if ([ :len $AccessList ] > 0) do={ + :set NewComment [ /interface/wifi/access-list/get $AccessList comment ]; + } + :if ([ :len $NewComment ] != 0 && $LeaseVal->"comment" != $NewComment) do={ + $LogPrint info $ScriptName ("Updating comment for DHCP lease " . $LeaseVal->"active-mac-address" . ": " . $NewComment); + /ip/dhcp-server/lease/set comment=$NewComment $Lease; + } + } +} on-error={ + :global ExitError; $ExitError $ExitOK [ :jobname ]; +} diff --git a/dhcp-lease-comment.wifiwave2.rsc b/dhcp-lease-comment.wifiwave2.rsc deleted file mode 100644 index 200c53b..0000000 --- a/dhcp-lease-comment.wifiwave2.rsc +++ /dev/null @@ -1,33 +0,0 @@ -#!rsc by RouterOS -# RouterOS script: dhcp-lease-comment.wifiwave2 -# Copyright (c) 2013-2023 Christian Hesse -# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md -# -# provides: lease-script, order=60 -# -# update dhcp-server lease comment with infos from access-list -# https://git.eworm.de/cgit/routeros-scripts/about/doc/dhcp-lease-comment.md -# -# !! Do not edit this file, it is generated from template! - -:local 0 "dhcp-lease-comment.wifiwave2"; -:global GlobalFunctionsReady; -:while ($GlobalFunctionsReady != true) do={ :delay 500ms; } - -:global LogPrintExit2; -:global ScriptLock; - -$ScriptLock $0; - -:foreach Lease in=[ /ip/dhcp-server/lease/find where dynamic=yes status=bound ] do={ - :local LeaseVal [ /ip/dhcp-server/lease/get $Lease ]; - :local NewComment; - :local AccessList ([ /interface/wifiwave2/access-list/find where mac-address=($LeaseVal->"active-mac-address") ]->0); - :if ([ :len $AccessList ] > 0) do={ - :set NewComment [ /interface/wifiwave2/access-list/get $AccessList comment ]; - } - :if ([ :len $NewComment ] != 0 && $LeaseVal->"comment" != $NewComment) do={ - $LogPrintExit2 info $0 ("Updating comment for DHCP lease " . $LeaseVal->"active-mac-address" . ": " . $NewComment) false; - /ip/dhcp-server/lease/set comment=$NewComment $Lease; - } -} diff --git a/dhcp-to-dns.rsc b/dhcp-to-dns.rsc index 57488c4..9b94098 100644 --- a/dhcp-to-dns.rsc +++ b/dhcp-to-dns.rsc @@ -1,120 +1,130 @@ #!rsc by RouterOS # RouterOS script: dhcp-to-dns -# Copyright (c) 2013-2023 Christian Hesse -# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md +# Copyright (c) 2013-2025 Christian Hesse +# https://rsc.eworm.de/COPYING.md # # provides: lease-script, order=20 +# requires RouterOS, version=7.16 # # check DHCP leases and add/remove/update DNS entries -# https://git.eworm.de/cgit/routeros-scripts/about/doc/dhcp-to-dns.md +# https://rsc.eworm.de/doc/dhcp-to-dns.md -:local 0 "dhcp-to-dns"; :global GlobalFunctionsReady; :while ($GlobalFunctionsReady != true) do={ :delay 500ms; } -:global Domain; -:global Identity; +:local ExitOK false; +:do { + :local ScriptName [ :jobname ]; -:global CharacterReplace; -:global EitherOr; -:global IfThenElse; -:global LogPrintExit2; -:global LogPrintOnce; -:global ParseKeyValueStore; -:global ScriptLock; + :global Domain; + :global Identity; -$ScriptLock $0 false 10; + :global CleanName; + :global EitherOr; + :global IfThenElse; + :global LogPrint; + :global LogPrintOnce; + :global ParseKeyValueStore; + :global ScriptLock; -:local Ttl 5m; -:local CommentPrefix ("managed by " . $0); -:local CommentString ("--- " . $0 . " above ---"); - -:if ([ :len [ /ip/dns/static/find where (name=$CommentString or (comment=$CommentString and name=-)) type=NXDOMAIN disabled ] ] = 0) do={ - /ip/dns/static/add name=$CommentString type=NXDOMAIN disabled=yes; - $LogPrintExit2 warning $0 ("Added disabled static dns record with name '" . $CommentString . "'.") false; -} -:local PlaceBefore ([ /ip/dns/static/find where (name=$CommentString or (comment=$CommentString and name=-)) type=NXDOMAIN disabled ]->0); - -:foreach DnsRecord in=[ /ip/dns/static/find where comment~("^" . $CommentPrefix . "\\b") (!type or type=A) ] do={ - :local DnsRecordVal [ /ip/dns/static/get $DnsRecord ]; - :local DnsRecordInfo [ $ParseKeyValueStore ($DnsRecordVal->"comment") ]; - :local MacInServer ($DnsRecordInfo->"macaddress" . " in " . $DnsRecordInfo->"server"); - - :if ([ :len [ /ip/dhcp-server/lease/find where active-mac-address=($DnsRecordInfo->"macaddress") \ - active-address=($DnsRecordVal->"address") server=($DnsRecordInfo->"server") status=bound ] ] > 0) do={ - $LogPrintExit2 debug $0 ("Lease for " . $MacInServer . " (" . $DnsRecordVal->"name" . ") still exists. Not deleting record.") false; - } else={ - :local Found false; - $LogPrintExit2 info $0 ("Lease expired for " . $MacInServer . ", deleting record (" . $DnsRecordVal->"name" . ").") false; - /ip/dns/static/remove $DnsRecord; - /ip/dns/static/remove [ find where type=CNAME comment=($DnsRecordVal->"comment") ]; - } -} - -:foreach Lease in=[ /ip/dhcp-server/lease/find where status=bound ] do={ - :local LeaseVal; - :do { - :set LeaseVal [ /ip/dhcp-server/lease/get $Lease ]; - :if ([ :len [ /ip/dhcp-server/lease/find where active-mac-address=($LeaseVal->"active-mac-address") status=bound ] ] > 1) do={ - $LogPrintOnce info $0 ("Multiple bound leases found for mac-address " . ($LeaseVal->"active-mac-address") . "!"); - } - } on-error={ - $LogPrintExit2 debug $0 ("A lease just vanished, ignoring.") false; + :if ([ $ScriptLock $ScriptName 10 ] = false) do={ + :set ExitOK true; + :error false; } - :if ([ :len ($LeaseVal->"active-address") ] > 0) do={ - :local Comment ($CommentPrefix . ", macaddress=" . $LeaseVal->"active-mac-address" . ", server=" . $LeaseVal->"server"); - :local MacDash [ $CharacterReplace ($LeaseVal->"active-mac-address") ":" "-" ]; - :local HostName [ $CharacterReplace [ $EitherOr ([ $ParseKeyValueStore ($LeaseVal->"comment") ]->"hostname") ($LeaseVal->"host-name") ] " " "" ]; - :local Network [ /ip/dhcp-server/network/find where ($LeaseVal->"active-address") in address ]; - :local NetworkVal; - :if ([ :len $Network ] > 0) do={ - :set NetworkVal [ /ip/dhcp-server/network/get ($Network->0) ]; + :local Ttl 5m; + :local CommentPrefix ("managed by " . $ScriptName); + :local CommentString ("--- " . $ScriptName . " above ---"); + + :if ([ :len [ /ip/dns/static/find where (name=$CommentString or (comment=$CommentString and name=-)) type=NXDOMAIN disabled ] ] = 0) do={ + /ip/dns/static/add name=$CommentString type=NXDOMAIN disabled=yes; + $LogPrint warning $ScriptName ("Added disabled static dns record with name '" . $CommentString . "'."); + } + :local PlaceBefore ([ /ip/dns/static/find where (name=$CommentString or (comment=$CommentString and name=-)) type=NXDOMAIN disabled ]->0); + + :foreach DnsRecord in=[ /ip/dns/static/find where comment~("^" . $CommentPrefix . "\\b") type=A ] do={ + :local DnsRecordVal [ /ip/dns/static/get $DnsRecord ]; + :local DnsRecordInfo [ $ParseKeyValueStore ($DnsRecordVal->"comment") ]; + :local MacInServer ($DnsRecordInfo->"macaddress" . " in " . $DnsRecordInfo->"server"); + + :if ([ :len [ /ip/dhcp-server/lease/find where active-mac-address=($DnsRecordInfo->"macaddress") \ + 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") ]; } - :local NetworkInfo [ $ParseKeyValueStore ($NetworkVal->"comment") ]; - :local NetDomain ([ $IfThenElse ([ :len ($NetworkInfo->"name-extra") ] > 0) ($NetworkInfo->"name-extra" . ".") ] . \ - [ $EitherOr [ $EitherOr ($NetworkInfo->"domain") ($NetworkVal->"domain") ] $Domain ]); - :local FullA ($MacDash . "." . $NetDomain); - :local FullCN ($HostName . "." . $NetDomain); - :local MacInServer ($LeaseVal->"active-mac-address" . " in " . $LeaseVal->"server"); + } - :local DnsRecord [ /ip/dns/static/find where comment=$Comment (!type or type=A) ]; - :if ([ :len $DnsRecord ] > 0) do={ - :local DnsRecordVal [ /ip/dns/static/get $DnsRecord ]; - - :if ($DnsRecordVal->"address" = $LeaseVal->"active-address" && $DnsRecordVal->"name" = $FullA) do={ - $LogPrintExit2 debug $0 ("The A record for " . $MacInServer . " (" . $FullA . ") does not need updating.") false; - } else={ - $LogPrintExit2 info $0 ("Updating A record for " . $MacInServer . " (" . $FullA . " -> " . $LeaseVal->"active-address" . ").") false; - /ip/dns/static/set address=($LeaseVal->"active-address") name=$FullA $DnsRecord; + :foreach Lease in=[ /ip/dhcp-server/lease/find where status=bound ] do={ + :local LeaseVal; + :do { + :set LeaseVal [ /ip/dhcp-server/lease/get $Lease ]; + :if ([ :len [ /ip/dhcp-server/lease/find where active-mac-address=($LeaseVal->"active-mac-address") status=bound ] ] > 1) do={ + $LogPrintOnce info $ScriptName ("Multiple bound leases found for mac-address " . ($LeaseVal->"active-mac-address") . "!"); } + } on-error={ + $LogPrint debug $ScriptName ("A lease just vanished, ignoring."); + } - :local CName [ /ip/dns/static/find where comment=$Comment type=CNAME ]; - :if ([ :len $CName ] > 0) do={ - :local CNameVal [ /ip/dns/static/get $CName ]; - :if ($CNameVal->"name" != $FullCN || $CNameVal->"cname" != $FullA) do={ - $LogPrintExit2 info $0 ("Deleting CNAME record with wrong data for " . $MacInServer . ".") false; - /ip/dns/static/remove $CName; + :if ([ :len ($LeaseVal->"active-address") ] > 0) do={ + :local Comment ($CommentPrefix . ", macaddress=" . $LeaseVal->"active-mac-address" . ", server=" . $LeaseVal->"server"); + :local MacDash [ $CleanName ($LeaseVal->"active-mac-address") ]; + :local HostName [ $CleanName [ $EitherOr ([ $ParseKeyValueStore ($LeaseVal->"comment") ]->"hostname") ($LeaseVal->"host-name") ] ]; + :local Network [ /ip/dhcp-server/network/find where ($LeaseVal->"active-address") in address ]; + :local NetworkVal; + :if ([ :len $Network ] > 0) do={ + :set NetworkVal [ /ip/dhcp-server/network/get ($Network->0) ]; + } + :local NetworkInfo [ $ParseKeyValueStore ($NetworkVal->"comment") ]; + :local NetDomain ([ $IfThenElse ([ :len ($NetworkInfo->"name-extra") ] > 0) ($NetworkInfo->"name-extra" . ".") ] . \ + [ $EitherOr [ $EitherOr ($NetworkInfo->"domain") ($NetworkVal->"domain") ] $Domain ]); + :local FullA ($MacDash . "." . $NetDomain); + :local FullCN ($HostName . "." . $NetDomain); + :local MacInServer ($LeaseVal->"active-mac-address" . " in " . $LeaseVal->"server"); + + :local DnsRecord [ /ip/dns/static/find where comment=$Comment type=A ]; + :if ([ :len $DnsRecord ] > 0) do={ + :local DnsRecordVal [ /ip/dns/static/get $DnsRecord ]; + + :if ($DnsRecordVal->"address" = $LeaseVal->"active-address" && $DnsRecordVal->"name" = $FullA) do={ + $LogPrint debug $ScriptName ("The A record for " . $MacInServer . " (" . $FullA . ") does not need updating."); + } else={ + $LogPrint info $ScriptName ("Updating A record for " . $MacInServer . " (" . $FullA . " -> " . $LeaseVal->"active-address" . ")."); + /ip/dns/static/set address=($LeaseVal->"active-address") name=$FullA $DnsRecord; + } + + :local CName [ /ip/dns/static/find where comment=$Comment type=CNAME ]; + :if ([ :len $CName ] > 0) do={ + :local CNameVal [ /ip/dns/static/get $CName ]; + :if ($CNameVal->"name" != $FullCN || $CNameVal->"cname" != $FullA) do={ + $LogPrint info $ScriptName ("Deleting CNAME record with wrong data for " . $MacInServer . "."); + /ip/dns/static/remove $CName; + } + } + :if ([ :len $HostName ] > 0 && [ :len [ /ip/dns/static/find where name=$FullCN type=CNAME ] ] = 0) do={ + $LogPrint info $ScriptName ("Adding CNAME record for " . $MacInServer . " (" . $FullCN . " -> " . $FullA . ")."); + /ip/dns/static/add name=$FullCN type=CNAME cname=$FullA ttl=$Ttl comment=$Comment place-before=$PlaceBefore; + } + + } else={ + $LogPrint info $ScriptName ("Adding A record for " . $MacInServer . " (" . $FullA . " -> " . $LeaseVal->"active-address" . ")."); + /ip/dns/static/add name=$FullA type=A address=($LeaseVal->"active-address") ttl=$Ttl comment=$Comment place-before=$PlaceBefore; + :if ([ :len $HostName ] > 0 && [ :len [ /ip/dns/static/find where name=$FullCN type=CNAME ] ] = 0) do={ + $LogPrint info $ScriptName ("Adding CNAME record for " . $MacInServer . " (" . $FullCN . " -> " . $FullA . ")."); + /ip/dns/static/add name=$FullCN type=CNAME cname=$FullA ttl=$Ttl comment=$Comment place-before=$PlaceBefore; } } - :if ([ :len $HostName ] > 0 && [ :len [ /ip/dns/static/find where name=$FullCN type=CNAME ] ] = 0) do={ - $LogPrintExit2 info $0 ("Adding CNAME record for " . $MacInServer . " (" . $FullCN . " -> " . $FullA . ").") false; - /ip/dns/static/add name=$FullCN type=CNAME cname=$FullA ttl=$Ttl comment=$Comment place-before=$PlaceBefore; - } + :if ([ :len [ /ip/dns/static/find where name=$FullA type=A ] ] > 1) do={ + $LogPrintOnce warning $ScriptName ("The name '" . $FullA . "' appeared in more than one A record!"); + } } else={ - $LogPrintExit2 info $0 ("Adding A record for " . $MacInServer . " (" . $FullA . " -> " . $LeaseVal->"active-address" . ").") false; - /ip/dns/static/add name=$FullA type=A address=($LeaseVal->"active-address") ttl=$Ttl comment=$Comment place-before=$PlaceBefore; - :if ([ :len $HostName ] > 0 && [ :len [ /ip/dns/static/find where name=$FullCN type=CNAME ] ] = 0) do={ - $LogPrintExit2 info $0 ("Adding CNAME record for " . $MacInServer . " (" . $FullCN . " -> " . $FullA . ").") false; - /ip/dns/static/add name=$FullCN type=CNAME cname=$FullA ttl=$Ttl comment=$Comment place-before=$PlaceBefore; - } + $LogPrint debug $ScriptName ("No address available... Ignoring."); } - - :if ([ :len [ /ip/dns/static/find where name=$FullA (!type or type=A) ] ] > 1) do={ - $LogPrintOnce warning $0 ("The name '" . $FullA . "' appeared in more than one A record!"); - } - } else={ - $LogPrintExit2 debug $0 ("No address available... Ignoring.") false; } +} on-error={ + :global ExitError; $ExitError $ExitOK [ :jobname ]; } diff --git a/doc/accesslist-duplicates.md b/doc/accesslist-duplicates.md index e3767e7..e4d0c7f 100644 --- a/doc/accesslist-duplicates.md +++ b/doc/accesslist-duplicates.md @@ -1,6 +1,13 @@ Find and remove access list duplicates ====================================== +[![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.15-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 @@ -15,13 +22,13 @@ entries in wireless access list. Requirements and installation ----------------------------- -Depending on whether you use `wifiwave2` package (`/interface/wifiwave2`) -or legacy wifi with CAPsMAN (`/caps-man`) or local wireless interface +Depending on whether you use `wifi` package (`/interface/wifi`), legacy +wifi with CAPsMAN (`/caps-man`) or local wireless interface (`/interface/wireless`) you need to install a different script. -For `wifiwave2`: +For `wifi`: - $ScriptInstallUpdate accesslist-duplicates.wifiwave2; + $ScriptInstallUpdate accesslist-duplicates.wifi; For legacy CAPsMAN: @@ -36,7 +43,7 @@ Usage and invocation Run this script from a terminal: - /system/script/run accesslist-duplicates.local; + /system/script/run accesslist-duplicates.wifi; ![screenshot: example](accesslist-duplicates.d/01-example.avif) diff --git a/doc/backup-cloud.md b/doc/backup-cloud.md index f084d5b..7286960 100644 --- a/doc/backup-cloud.md +++ b/doc/backup-cloud.md @@ -1,6 +1,13 @@ Upload backup to Mikrotik cloud =============================== +[![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.15-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 @@ -12,10 +19,10 @@ Description This script uploads [binary backup to Mikrotik cloud](https://wiki.mikrotik.com/wiki/Manual:IP/Cloud#Backup). -> ⚠️ **Warning**: The used command can hit errors that a script can not handle. -> This may result in script termination (where no notification is sent) or -> malfunction of fetch command (where all up- and downloads break) for some -> time. Failed notifications are queued then. +> ⚠️ **Warning**: The used command can hit errors that a script can with +> workaround only. A notification *should* be sent anyway. But it can result +> in malfunction of fetch command (where all up- and downloads break) for +> some time. Failed notifications are queued then. ### Sample notification @@ -61,7 +68,7 @@ See also -------- * [Send backup via e-mail](backup-email.md) -* [Save configuration to fallback partition](doc/backup-partition.md) +* [Save configuration to fallback partition](backup-partition.md) * [Upload backup to server](backup-upload.md) --- diff --git a/doc/backup-email.md b/doc/backup-email.md index 67564e7..7b8bcfe 100644 --- a/doc/backup-email.md +++ b/doc/backup-email.md @@ -1,6 +1,13 @@ Send backup via e-mail ====================== +[![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.15-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 @@ -52,7 +59,7 @@ See also -------- * [Upload backup to Mikrotik cloud](backup-cloud.md) -* [Save configuration to fallback partition](doc/backup-partition.md) +* [Save configuration to fallback partition](backup-partition.md) * [Send notifications via e-mail](mod/notification-email.md) * [Upload backup to server](backup-upload.md) diff --git a/doc/backup-partition.md b/doc/backup-partition.md index e95422c..9d615a5 100644 --- a/doc/backup-partition.md +++ b/doc/backup-partition.md @@ -1,6 +1,13 @@ Save configuration to fallback partition ======================================== +[![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.15-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 @@ -11,10 +18,20 @@ Description This script saves the current configuration to fallback [partition](https://wiki.mikrotik.com/wiki/Manual:Partitions). +It can also copy-over the RouterOS installation when run interactively +or just before a feature update. For this to work you need a device with sufficient flash storage that is properly partitioned. +To make you aware of a possible issue a scheduler logging a warning is +added in the backup partition's configuration. You may want to use +[log-forward](log-forward.md) to be notified. + +> ⚠️ **Warning**: By default only the configuration is saved to backup +> partition. Every now and then you should copy your installation over +> for a recent RouterOS version! See below for options. + Requirements and installation ----------------------------- @@ -22,6 +39,18 @@ Just install the script: $ScriptInstallUpdate backup-partition; +Configuration +------------- + +The configuration goes to `global-config-overlay`, the only parameter is: + +* `BackupPartitionCopyBeforeFeatureUpdate`: copy-over the RouterOS + installation when a feature update is pending + +> ℹ️ **Info**: Copy relevant configuration from +> [`global-config`](../global-config.rsc) (the one without `-overlay`) to +> your local `global-config-overlay` and modify it to your specific needs. + Usage and invocation -------------------- @@ -29,6 +58,9 @@ Just run the script: /system/script/run backup-partition; +When run interactively from terminal it supports to copy-over the RouterOS +installation when versions differ. + Creating a scheduler may be an option: /system/scheduler/add interval=1w name=backup-partition on-event="/system/script/run backup-partition;" start-time=09:30:00; @@ -39,6 +71,7 @@ See also * [Upload backup to Mikrotik cloud](backup-cloud.md) * [Send backup via e-mail](backup-email.md) * [Upload backup to server](backup-upload.md) +* [Forward log messages via notification](log-forward.md) --- [⬅️ Go back to main README](../README.md) diff --git a/doc/backup-upload.md b/doc/backup-upload.md index b132e56..6a5b0e4 100644 --- a/doc/backup-upload.md +++ b/doc/backup-upload.md @@ -1,6 +1,13 @@ Upload backup to server ======================= +[![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.15-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 @@ -78,7 +85,7 @@ See also * [Upload backup to Mikrotik cloud](backup-cloud.md) * [Send backup via e-mail](backup-email.md) -* [Save configuration to fallback partition](doc/backup-partition.md) +* [Save configuration to fallback partition](backup-partition.md) --- [⬅️ Go back to main README](../README.md) diff --git a/doc/capsman-download-packages.md b/doc/capsman-download-packages.md index 8174c13..5722227 100644 --- a/doc/capsman-download-packages.md +++ b/doc/capsman-download-packages.md @@ -1,6 +1,13 @@ Download packages for CAP upgrade from CAPsMAN ============================================= +[![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.15-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 @@ -18,21 +25,25 @@ This script automatically downloads these packages. Requirements and installation ----------------------------- -Just install the script on CAPsMAN device. Depending on whether you use -`wifiwave2` package (`/interface/wifiwave2`) or legacy wifi with CAPsMAN -(`/caps-man`) you need to install a different script. +Make sure you have the `package-path` set in your CAPsMAN configuration, +as that is where packages are downloaded to and where the system expects +them. -For `wifiwave2`: +Then just install the script on CAPsMAN device. +Depending on whether you use `wifi` package (`/interface/wifi`) or legacy +wifi with CAPsMAN (`/caps-man`) you need to install a different script. - $ScriptInstallUpdate capsman-download-packages.wifiwave2; +For `wifi`: + + $ScriptInstallUpdate capsman-download-packages.wifi; For legacy CAPsMAN: $ScriptInstallUpdate capsman-download-packages.capsman; -Optionally add a scheduler to run after startup. For `wifiwave2`: +Optionally add a scheduler to run after startup. For `wifi`: - /system/scheduler/add name=capsman-download-packages on-event="/system/script/run capsman-download-packages.wifiwave2;" start-time=startup; + /system/scheduler/add name=capsman-download-packages on-event="/system/script/run capsman-download-packages.wifi;" start-time=startup; For legacy CAPsMAN: @@ -41,16 +52,20 @@ For legacy CAPsMAN: Packages available in local storage in older version are downloaded unconditionally. -If no packages are found the script tries to download missing packages for -legacy CAPsMAN by guessing from system log. For `wifiwave2` a default set -of packages (`routeros` and `wifiwave2` for *arm* and *arm64*) is downloaded. +If no packages are found the script downloads a default set of packages: + + * `wifi`: `routeros` and `wifi-qcom` for *arm* and *arm64*, `wifi-qcom-ac` for *arm* + * legacy CAPsMAN: `routeros` and `wireless` for *arm* and *mipsbe* + +> ℹ️ **Info**: If you have packages in the directory and things go wrong for +> what ever unknown reason: Remove **all** packages and start over. Usage and invocation -------------------- Run the script manually: - /system/script/run capsman-download-packages.wifiwave2; + /system/script/run capsman-download-packages.wifi; ... or from scheduler. diff --git a/doc/capsman-rolling-upgrade.md b/doc/capsman-rolling-upgrade.md index 5c038e9..d277db6 100644 --- a/doc/capsman-rolling-upgrade.md +++ b/doc/capsman-rolling-upgrade.md @@ -1,6 +1,13 @@ Run rolling CAP upgrades from CAPsMAN ===================================== +[![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.15-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 @@ -20,13 +27,13 @@ parallel. Requirements and installation ----------------------------- -Just install the script on CAPsMAN device. Depending on whether you use -`wifiwave2` package (`/interface/wifiwave2`) or legacy wifi with CAPsMAN -(`/caps-man`) you need to install a different script. +Just install the script on CAPsMAN device. +Depending on whether you use `wifi` package (`/interface/wifi`) or legacy +wifi with CAPsMAN (`/caps-man`) you need to install a different script. -For `wifiwave2`: +For `wifi`: - $ScriptInstallUpdate capsman-rolling-upgrade.wifiwave2; + $ScriptInstallUpdate capsman-rolling-upgrade.wifi; For legacy CAPsMAN: @@ -41,7 +48,7 @@ that script when required. Alternatively run it manually: - /system/script/run capsman-rolling-upgrade.wifiwave2; + /system/script/run capsman-rolling-upgrade.wifi; See also -------- diff --git a/doc/certificate-renew-issued.md b/doc/certificate-renew-issued.md index bb8e18c..c4615b5 100644 --- a/doc/certificate-renew-issued.md +++ b/doc/certificate-renew-issued.md @@ -1,6 +1,13 @@ Renew locally issued certificates ================================= +[![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.15-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 diff --git a/doc/check-certificates.md b/doc/check-certificates.md index 95ff534..4c144ba 100644 --- a/doc/check-certificates.md +++ b/doc/check-certificates.md @@ -1,6 +1,13 @@ Renew certificates and notify on expiration =========================================== +[![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.15-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 @@ -63,11 +70,23 @@ Just run the script: Tips & Tricks ------------- +### Schedule at startup + The script checks for full connectivity before acting, so scheduling at startup is perfectly valid: /system/scheduler/add name=check-certificates@startup on-event="/system/script/run check-certificates;" start-time=startup; +### Initial import + +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/sign example.com; + /system/script/run check-certificates; + See also -------- diff --git a/doc/check-health.d/notification-08-psu-fail.avif b/doc/check-health.d/notification-08-state-fail.avif similarity index 100% rename from doc/check-health.d/notification-08-psu-fail.avif rename to doc/check-health.d/notification-08-state-fail.avif diff --git a/doc/check-health.d/notification-09-psu-ok.avif b/doc/check-health.d/notification-09-state-ok.avif similarity index 100% rename from doc/check-health.d/notification-09-psu-ok.avif rename to doc/check-health.d/notification-09-state-ok.avif diff --git a/doc/check-health.md b/doc/check-health.md index 3bde4e9..7cf0c33 100644 --- a/doc/check-health.md +++ b/doc/check-health.md @@ -1,6 +1,13 @@ Notify about health state ========================= +[![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.15-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 @@ -10,22 +17,24 @@ Description ----------- This script is run from scheduler periodically, sending notification on -health related events: +health related events. Monitoring CPU and RAM utilization (available +processing and memory resources) works on all devices: * high CPU utilization * high RAM utilization (low available RAM) + +With additional plugins functionality can be extended, depending on +sensors available in hardware: + * voltage jumps up or down more than configured threshold * voltage drops below hard lower limit +* fan failed or recovered * power supply failed or recovered * temperature is above or below threshold -Note that bad initial state will not trigger an event. - -Monitoring CPU and RAM utilization (available processing and memory -resources) works on all devices. Other than that only sensors available -in hardware can be checked. See what your hardware supports: - - /system/health/print; +> ⚠️ **Warning**: Note that bad initial state will not trigger an event! For +> example rebooting a device that is already too hot will not trigger an +> alert on high temperature. ### Sample notifications @@ -50,8 +59,8 @@ in hardware can be checked. See what your hardware supports: #### PSU state -![check-health notification psu fail](check-health.d/notification-08-psu-fail.avif) -![check-health notification psu ok](check-health.d/notification-09-psu-ok.avif) +![check-health notification state fail](check-health.d/notification-08-state-fail.avif) +![check-health notification state ok](check-health.d/notification-09-state-ok.avif) Requirements and installation ----------------------------- @@ -65,6 +74,30 @@ Just install the script and create a scheduler: > precision of cpu utilization, escpecially on devices with limited > resources. Thus an unusual interval is used here. +### Plugins + +Additional plugins are available for sensors available in hardware. First +check what your hardware supports: + + /system/health/print; + +Then install the plugin for *fan* and *power supply unit* *state*: + + $ScriptInstallUpdate check-health,check-health.d/state; + +... or *temperature*: + + $ScriptInstallUpdate check-health,check-health.d/temperature; + +... or *voltage*: + + $ScriptInstallUpdate check-health,check-health.d/voltage; + +You can also combine the commands and install all or a subset of plugins +in one go: + + $ScriptInstallUpdate check-health,check-health.d/state,check-health.d/temperature,check-health.d/voltage; + Configuration ------------- diff --git a/doc/check-lte-firmware-upgrade.md b/doc/check-lte-firmware-upgrade.md index a81ca86..3693b71 100644 --- a/doc/check-lte-firmware-upgrade.md +++ b/doc/check-lte-firmware-upgrade.md @@ -1,6 +1,13 @@ Notify on LTE firmware upgrade ============================== +[![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.15-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 diff --git a/doc/check-routeros-update.md b/doc/check-routeros-update.md index dbe83b8..926b4aa 100644 --- a/doc/check-routeros-update.md +++ b/doc/check-routeros-update.md @@ -1,6 +1,13 @@ Notify on RouterOS update ========================= +[![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.15-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 diff --git a/doc/collect-wireless-mac.md b/doc/collect-wireless-mac.md index 2b17055..0197522 100644 --- a/doc/collect-wireless-mac.md +++ b/doc/collect-wireless-mac.md @@ -1,6 +1,13 @@ Collect MAC addresses in wireless access list ============================================= +[![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.15-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 @@ -22,13 +29,13 @@ and modify it to your needs. Requirements and installation ----------------------------- -Depending on whether you use `wifiwave2` package (`/interface/wifiwave2`) -or legacy wifi with CAPsMAN (`/caps-man`) or local wireless interface +Depending on whether you use `wifi` package (`/interface/wifi`), legacy +wifi with CAPsMAN (`/caps-man`) or local wireless interface (`/interface/wireless`) you need to install a different script. -For `wifiwave2`: +For `wifi`: - $ScriptInstallUpdate collect-wireless-mac.capsman.wifiwave2; + $ScriptInstallUpdate collect-wireless-mac.wifi; For legacy CAPsMAN: diff --git a/doc/daily-psk.md b/doc/daily-psk.md index f551e46..4a3de64 100644 --- a/doc/daily-psk.md +++ b/doc/daily-psk.md @@ -1,6 +1,13 @@ Use wireless network with daily psk =================================== +[![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.15-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 @@ -21,16 +28,16 @@ Requirements and installation Just install this script. -Depending on whether you use `wifiwave2` package (`/interface/wifiwave2`) -or legacy wifi with CAPsMAN (`/caps-man`) or local wireless interface +Depending on whether you use `wifi` package (`/interface/wifi`), legacy +wifi with CAPsMAN (`/caps-man`) or local wireless interface (`/interface/wireless`) you need to install a different script and add schedulers to run the script: -For `wifiwave2`: +For `wifi`: - $ScriptInstallUpdate daily-psk.wifiwave2; - /system/scheduler/add interval=1d name=daily-psk on-event="/system/script/run daily-psk.wifiwave2;" start-time=03:00:00; - /system/scheduler/add name=daily-psk@startup on-event="/system/script/run daily-psk.wifiwave2;" start-time=startup; + $ScriptInstallUpdate daily-psk.wifi; + /system/scheduler/add interval=1d name=daily-psk on-event="/system/script/run daily-psk.wifi;" start-time=03:00:00; + /system/scheduler/add name=daily-psk@startup on-event="/system/script/run daily-psk.wifi;" start-time=startup; For legacy CAPsMAN: @@ -58,9 +65,9 @@ The configuration goes to `global-config-overlay`, these are the parameters: > [`global-config`](../global-config.rsc) (the one without `-overlay`) to > your local `global-config-overlay` and modify it to your specific needs. -Then add an access list entry. For `wifiwave2`: +Then add an access list entry. For `wifi`: - /interface/wifiwave2/access-list/add comment="Daily PSK" ssid-regexp="-guest\$" passphrase="ToBeChangedDaily"; + /interface/wifi/access-list/add comment="Daily PSK" ssid-regexp="-guest\$" passphrase="ToBeChangedDaily"; For legacy CAPsMAN: diff --git a/doc/dhcp-lease-comment.md b/doc/dhcp-lease-comment.md index cd29641..b02f199 100644 --- a/doc/dhcp-lease-comment.md +++ b/doc/dhcp-lease-comment.md @@ -1,6 +1,13 @@ Comment DHCP leases with info from access list ============================================== +[![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.15-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 @@ -15,13 +22,13 @@ from wireless access list. Requirements and installation ----------------------------- -Depending on whether you use `wifiwave2` package (`/interface/wifiwave2`) -or legacy wifi with CAPsMAN (`/caps-man`) or local wireless interface +Depending on whether you use `wifi` package (`/interface/wifi`), legacy +wifi with CAPsMAN (`/caps-man`) or local wireless interface (`/interface/wireless`) you need to install a different script. -For `wifiwave2`: +For `wifi`: - $ScriptInstallUpdate dhcp-lease-comment.wifiwave2; + $ScriptInstallUpdate dhcp-lease-comment.wifi; For legacy CAPsMAN: diff --git a/doc/dhcp-to-dns.md b/doc/dhcp-to-dns.md index 772704e..4211d85 100644 --- a/doc/dhcp-to-dns.md +++ b/doc/dhcp-to-dns.md @@ -1,6 +1,13 @@ Create DNS records for DHCP leases ================================== +[![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.16-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 diff --git a/doc/firmware-upgrade-reboot.md b/doc/firmware-upgrade-reboot.md index 8bf4d19..54f1da0 100644 --- a/doc/firmware-upgrade-reboot.md +++ b/doc/firmware-upgrade-reboot.md @@ -1,6 +1,13 @@ Automatically upgrade firmware and reboot ========================================= +[![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.15-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 diff --git a/doc/fw-addr-lists.md b/doc/fw-addr-lists.md index 5805905..cb560d7 100644 --- a/doc/fw-addr-lists.md +++ b/doc/fw-addr-lists.md @@ -1,6 +1,13 @@ Download, import and update firewall address-lists ================================================== +[![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.16-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 @@ -11,10 +18,11 @@ Description This script downloads, imports and updates firewall address-lists. Its main purpose is to block attacking ip addresses, spam hosts, command-and-control -servers and similar malicious entities. The default configuration contains -lists from [abuse.ch](https://abuse.ch/) and -[dshield.org](https://dshield.org/), and -lists from [spamhaus.org](https://spamhaus.org/) are prepared. +servers and similar malicious entities. The default configuration contains a +[collective list by GitHub user @stamparm](https://github.com/stamparm/ipsum), +lists from [dshield.org](https://dshield.org/) and +[blocklist.de](https://www.blocklist.de/), and lists from +[spamhaus.org](https://spamhaus.org/) are prepared. The address-lists are updated in place, so after initial import you will not see situation when the lists are not populated. @@ -22,6 +30,10 @@ see situation when the lists are not populated. To mitigate man-in-the-middle attacks with altered lists the server's certificate is checked. +> ⚠️ **Warning**: The script does not limit the size of a list, but keep in +> mind that huge lists can exhaust your device's resources (RAM and CPU), +> and may take a long time to process. + Requirements and installation ----------------------------- @@ -51,13 +63,16 @@ The configuration goes to `global-config-overlay`, these are the parameters: > your local `global-config-overlay` and modify it to your specific needs. Naming a certificate for a list makes the script verify the server -certificate, so you should add that if possible. Some certificates are -available in my repository and downloaded automatically. Import it manually -(menu `/certificate/`) if missing. +certificate, so you should add that if possible. You may want to find the +[certificate name from browser](../CERTIFICATES.md). Create firewall rules to process the packets that are related to addresses -from address-lists. This rejects the packets from and to ip addresses listed -in address-list `block`. +from address-lists. + +### IPv4 rules + +This rejects the packets from and to IPv4 addresses listed in +address-list `block`. /ip/firewall/filter/add chain=input src-address-list=block action=reject reject-with=icmp-admin-prohibited; /ip/firewall/filter/add chain=forward src-address-list=block action=reject reject-with=icmp-admin-prohibited; @@ -85,6 +100,38 @@ Alternatively handle the packets in firewall's raw section if you prefer: > ⚠️ **Warning**: Just again... The order of firewall rules is important. Make > sure they actually take effect as expected! +### IPv6 rules + +These are the same rules, but for IPv6. + +Reject packets in address-list `block`: + + /ipv6/firewall/filter/add chain=input src-address-list=block action=reject reject-with=icmp-admin-prohibited; + /ipv6/firewall/filter/add chain=forward src-address-list=block action=reject reject-with=icmp-admin-prohibited; + /ipv6/firewall/filter/add chain=forward dst-address-list=block action=reject reject-with=icmp-admin-prohibited; + /ipv6/firewall/filter/add chain=output dst-address-list=block action=reject reject-with=icmp-admin-prohibited; + +Allow packets in address-list `allow`: + + /ipv6/firewall/filter/add chain=input src-address-list=allow action=accept; + /ipv6/firewall/filter/add chain=forward src-address-list=allow action=accept; + /ipv6/firewall/filter/add chain=forward dst-address-list=allow action=accept; + /ipv6/firewall/filter/add chain=output dst-address-list=allow action=accept; + +Drop packets in firewall's raw section: + + /ipv6/firewall/raw/add chain=prerouting src-address-list=block action=drop; + /ipv6/firewall/raw/add chain=prerouting dst-address-list=block action=drop; + /ipv6/firewall/raw/add chain=output dst-address-list=block action=drop; + +> ⚠️ **Warning**: Just again... The order of firewall rules is important. Make +> sure they actually take effect as expected! + +See also +-------- + +* [Certificate name from browser](../CERTIFICATES.md) + --- [⬅️ Go back to main README](../README.md) [⬆️ Go back to top](#top) diff --git a/doc/global-wait.md b/doc/global-wait.md index 39921db..799cae7 100644 --- a/doc/global-wait.md +++ b/doc/global-wait.md @@ -1,6 +1,13 @@ Wait for global functions and modules ===================================== +[![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.15-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 diff --git a/doc/gps-track.md b/doc/gps-track.md index 721b075..5e4878f 100644 --- a/doc/gps-track.md +++ b/doc/gps-track.md @@ -1,6 +1,13 @@ Send GPS position to server =========================== +[![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.15-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 diff --git a/doc/hotspot-to-wpa.md b/doc/hotspot-to-wpa.md index 4909eef..a2e9748 100644 --- a/doc/hotspot-to-wpa.md +++ b/doc/hotspot-to-wpa.md @@ -1,5 +1,12 @@ -Use WPA2 network with hotspot credentials -========================================= +Use WPA network with hotspot credentials +======================================== + +[![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.15-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) @@ -10,23 +17,24 @@ Description ----------- RouterOS supports an unlimited number of MAC address specific passphrases -for WPA2 encrypted wifi networks via access list. The idea of this script -is to transfer hotspot credentials to MAC address specific WPA2 passphrase. +for WPA encrypted wifi networks via access list. The idea of this script +is to transfer hotspot credentials to MAC address specific WPA passphrase. Requirements and installation ----------------------------- -You need a properly configured hotspot on one (open) SSID and a WP2 enabled +You need a properly configured hotspot on one (open) SSID and a WPA enabled SSID with suffix "`-wpa`". -Then install the script. Depending on whether you use `wifiwave2` package -(`/interface/wifiwave2`) or legacy wifi with CAPsMAN (`/caps-man`) you need -to install a different script and set it as `on-login` script in hotspot. +Then install the script. +Depending on whether you use `wifi` package (`/interface/wifi`)or legacy +wifi with CAPsMAN (`/caps-man`) you need to install a different script and +set it as `on-login` script in hotspot. -For `wifiwave2`: +For `wifi`: - $ScriptInstallUpdate hotspot-to-wpa.wifiwave2; - /ip/hotspot/user/profile/set on-login="hotspot-to-wpa.wifiwave2" [ find ]; + $ScriptInstallUpdate hotspot-to-wpa.wifi; + /ip/hotspot/user/profile/set on-login="hotspot-to-wpa.wifi" [ find ]; For legacy CAPsMAN: @@ -39,10 +47,10 @@ With just `hotspot-to-wpa` installed the mac addresses will last in the access list forever. Install the optional script for automatic cleanup and add a scheduler. -For `wifiwave2`: +For `wifi`: - $ScriptInstallUpdate hotspot-to-wpa-cleanup.wifiwave2,lease-script; - /system/scheduler/add interval=1d name=hotspot-to-wpa-cleanup on-event="/system/script/run hotspot-to-wpa-cleanup.wifiwave2;" start-time=startup; + $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; For legacy CAPsMAN: @@ -78,17 +86,17 @@ Additional information is not available, including the password. Additionally templates can be created to give more options for access list: * `action`: set to `reject` to ignore logins on that hotspot -* `private-passphrase`: do **not** use passphrase from hotspot's user - credentials, but given one - or unset (use default passphrase) with - special word `ignore` +* `passphrase` or `private-passphrase`: do **not** use passphrase from + hotspot's user credentials, but given one - or unset (use default + passphrase) with special word `ignore` * `ssid-regexp`: set a different SSID regular expression to match * `vlan-id`: connect device to specific VLAN * `vlan-mode`: set the VLAN mode for device -For a hotspot called `example` the template could look like this. For -`wifiwave2`: +For a hotspot called `example` the template could look like this. +For `wifi`: - /interface/wifiwave2/access-list/add comment="hotspot-to-wpa template example" disabled=yes private-passphrase="ignore" ssid-regexp="^example\$" vlan-id=10; + /interface/wifi/access-list/add comment="hotspot-to-wpa template example" disabled=yes passphrase="ignore" ssid-regexp="^example\$" vlan-id=10; For legacy CAPsMAN: @@ -103,7 +111,7 @@ Usage and invocation -------------------- Now let the users connect and login to the hotspot. After that the devices -(identified by MAC address) can connect to the WPA2 network, using the +(identified by MAC address) can connect to the WPA network, using the passphrase from hotspot credentials. See also diff --git a/doc/ip-addr-bridge.md b/doc/ip-addr-bridge.md index a759829..f9f98e3 100644 --- a/doc/ip-addr-bridge.md +++ b/doc/ip-addr-bridge.md @@ -1,6 +1,13 @@ Manage IP addresses with bridge status ====================================== +[![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.15-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) Description diff --git a/doc/ipsec-to-dns.md b/doc/ipsec-to-dns.md index 04500a3..123656c 100644 --- a/doc/ipsec-to-dns.md +++ b/doc/ipsec-to-dns.md @@ -1,6 +1,13 @@ Create DNS records for IPSec peers ================================== +[![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.15-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 diff --git a/doc/ipv6-update.md b/doc/ipv6-update.md index f49cfaa..1f009b1 100644 --- a/doc/ipv6-update.md +++ b/doc/ipv6-update.md @@ -1,6 +1,13 @@ Update configuration on IPv6 prefix change ========================================== +[![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.15-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 @@ -38,13 +45,17 @@ Installing [ppp-on-up](ppp-on-up.md) may solve this. Configuration ------------- -An address list entry is updated with current prefix and can be used in -firewall rules, comment has to be "`ipv6-pool-`" and actual pool name: +As an address-list entry is mandatory a dynamic one is created automatically. +It is updated with current prefix and can be used in firewall rules. + +Alternatively a static address-list entry can be used, where comment has to +be "`ipv6-pool-`" and actual pool name. Use what ever list is desired, and +create it with: /ipv6/firewall/address-list/add address=2003:cf:2f0f:de00::/56 comment=ipv6-pool-isp list=extern; -As this entry is mandatory it is created automatically if it does not exist, -with the comment also set for list. +If the dynamic entry exists already you need to remove it before creating +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 diff --git a/doc/lease-script.md b/doc/lease-script.md index 55928fc..f83c383 100644 --- a/doc/lease-script.md +++ b/doc/lease-script.md @@ -1,6 +1,13 @@ 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.15-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 @@ -40,7 +47,7 @@ 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 WPA2 network with hotspot credentials](hotspot-to-wpa.md) +* [Use WPA network with hotspot credentials](hotspot-to-wpa.md) --- [⬅️ Go back to main README](../README.md) diff --git a/doc/leds-mode.md b/doc/leds-mode.md index 5710d85..a194396 100644 --- a/doc/leds-mode.md +++ b/doc/leds-mode.md @@ -1,6 +1,13 @@ Manage LEDs dark mode ===================== +[![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.15-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) Description diff --git a/doc/log-forward.md b/doc/log-forward.md index 4212381..3c19569 100644 --- a/doc/log-forward.md +++ b/doc/log-forward.md @@ -1,6 +1,13 @@ Forward log messages via notification ===================================== +[![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.15-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 @@ -15,15 +22,15 @@ server (see `/system/logging`). This has some limitation, however: * does not work early after boot if network connectivity is not yet established, or breaks intermittently * lots of messages generate a flood of mails -* Matrix and Telegram are not supported +* Matrix, Ntfy and Telegram are not supported The script works around the limitations, for example it does: * read from `/log`, including messages from early boot * skip multi-repeated messages * rate-limit itself to mitigate flooding -* forward via notification (which includes *e-mail*, *Matrix* and *Telegram* - when installed and configured, see below) +* forward via notification (which includes *e-mail*, *Matrix*, *Ntfy* and + *Telegram* when installed and configured, see below) It is intended to be run periodically from scheduler, then collects new log messages and forwards them via notification. @@ -46,6 +53,12 @@ Just install the script: Configuration ------------- +The default configuration should provide reasonable presets, filtering +*info*, and effectively forwarding *warning* and *error*. + +> 💡️ **Hint**: Please try with defaults first, especially if you are not +> familiar with regular expressions! + The configuration goes to `global-config-overlay`, these are the parameters: * `LogForwardFilter`: define topics *not* to be forwarded diff --git a/doc/mod/bridge-port-to.md b/doc/mod/bridge-port-to.md index a956de4..629c526 100644 --- a/doc/mod/bridge-port-to.md +++ b/doc/mod/bridge-port-to.md @@ -1,6 +1,13 @@ Manage ports in bridge ====================== +[![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.15-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 module can not be used on its own but requires the base diff --git a/doc/mod/bridge-port-vlan.md b/doc/mod/bridge-port-vlan.md index 068f050..cf29199 100644 --- a/doc/mod/bridge-port-vlan.md +++ b/doc/mod/bridge-port-vlan.md @@ -1,6 +1,13 @@ Manage VLANs on bridge ports ============================ +[![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.15-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 module can not be used on its own but requires the base diff --git a/doc/mod/inspectvar.md b/doc/mod/inspectvar.md index 1cc49a2..7daba15 100644 --- a/doc/mod/inspectvar.md +++ b/doc/mod/inspectvar.md @@ -1,6 +1,13 @@ Inspect variables ================= +[![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.15-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 module can not be used on its own but requires the base diff --git a/doc/mod/ipcalc.md b/doc/mod/ipcalc.md index f2f1140..c07853e 100644 --- a/doc/mod/ipcalc.md +++ b/doc/mod/ipcalc.md @@ -1,6 +1,13 @@ IP address calculation ====================== +[![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.15-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 module can not be used on its own but requires the base diff --git a/doc/mod/notification-email.md b/doc/mod/notification-email.md index 373f8fe..34d1c09 100644 --- a/doc/mod/notification-email.md +++ b/doc/mod/notification-email.md @@ -1,6 +1,13 @@ Send notifications via e-mail ============================= +[![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.15-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 module can not be used on its own but requires the base diff --git a/doc/mod/notification-matrix.d/01-authenticate.avif b/doc/mod/notification-matrix.d/01-authenticate.avif index 1db516b..b897943 100644 Binary files a/doc/mod/notification-matrix.d/01-authenticate.avif and b/doc/mod/notification-matrix.d/01-authenticate.avif differ diff --git a/doc/mod/notification-matrix.d/02-join-room.avif b/doc/mod/notification-matrix.d/02-join-room.avif index edd6c81..ad99ffd 100644 Binary files a/doc/mod/notification-matrix.d/02-join-room.avif and b/doc/mod/notification-matrix.d/02-join-room.avif differ diff --git a/doc/mod/notification-matrix.md b/doc/mod/notification-matrix.md index b309d3d..89c1b01 100644 --- a/doc/mod/notification-matrix.md +++ b/doc/mod/notification-matrix.md @@ -1,6 +1,13 @@ Send notifications via Matrix ============================= +[![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.15-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 module can not be used on its own but requires the base @@ -39,8 +46,14 @@ The Matrix server is connected via encrypted https, and certificate verification is applied. So make sure you have the certificate chain for your server in device's certificate store. -> ℹ️ **Info**: The *matrix.org* server uses a Cloudflare certificate. You can -> install that with: `$CertificateAvailable "Cloudflare Inc ECC CA-3"` +The example below is for `matrix.org`, which uses a trust chain from *Google +Trust Services*. Run this to import the required certificate: + + $CertificateAvailable "GTS Root R4"; + +Replace the CA certificate name with what ever is needed for your server. +You may want to find the +[certificate name from browser](../../CERTIFICATES.md). ### From other device @@ -63,6 +76,9 @@ and write first part of the configuration: ![authenticate](notification-matrix.d/01-authenticate.avif) +The configuration is written to a new configuration snippet +`global-config-overlay.d/mod/notification-matrix`. + #### Join Room Every Matix chat is a room, so we have to create one. Do that with your @@ -80,8 +96,8 @@ the invite. ![join room](notification-matrix.d/02-join-room.avif) -The settings have been appended to `global-config-overlay`. You may want to -edit to move it to an appropriate place. +The configuration is appended to the configuration snippet +`global-config-overlay.d/mod/notification-matrix`. Usage and invocation -------------------- @@ -113,6 +129,7 @@ function available: See also -------- +* [Certificate name from browser](../../CERTIFICATES.md) * [Send notifications via e-mail](notification-email.md) * [Send notifications via Ntfy](notification-ntfy.md) * [Send notifications via Telegram](notification-telegram.md) diff --git a/doc/mod/notification-ntfy.md b/doc/mod/notification-ntfy.md index f4cad8a..51756ac 100644 --- a/doc/mod/notification-ntfy.md +++ b/doc/mod/notification-ntfy.md @@ -1,6 +1,13 @@ Send notifications via Ntfy =========================== +[![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.15-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 module can not be used on its own but requires the base @@ -40,6 +47,17 @@ Then reload the configuration. > [`global-config`](../../global-config.rsc) (the one without `-overlay`) to > your local `global-config-overlay` and modify it to your specific needs. +Using a paid account or running a server on-premises allows to add additional +basic authentication. Configure `NtfyServerUser` and `NtfyServerPass` for this. +Even authentication via access token is possible, adding it as password with +a blank username. + +Also available is `NtfyServerToken` to add a bearer token for authentication. + +For a custom service installing an additional certificate may be required. +You may want to install that certificate manually, after finding the +[certificate name from browser](../../CERTIFICATES.md). + Usage and invocation -------------------- @@ -70,6 +88,7 @@ function available: See also -------- +* [Certificate name from browser](../../CERTIFICATES.md) * [Send notifications via e-mail](notification-email.md) * [Send notifications via Matrix](notification-matrix.md) * [Send notifications via Telegram](notification-telegram.md) diff --git a/doc/mod/notification-telegram.d/getchatid.avif b/doc/mod/notification-telegram.d/getchatid.avif new file mode 100644 index 0000000..7792969 Binary files /dev/null and b/doc/mod/notification-telegram.d/getchatid.avif differ diff --git a/doc/mod/notification-telegram.md b/doc/mod/notification-telegram.md index 444938f..2d00116 100644 --- a/doc/mod/notification-telegram.md +++ b/doc/mod/notification-telegram.md @@ -1,6 +1,13 @@ Send notifications via Telegram =============================== +[![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.15-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 module can not be used on its own but requires the base @@ -31,14 +38,21 @@ create your own bot: ![create new bot](notification-telegram.d/newbot.avif) -Now open a chat with your bot and start it by clicking the `START` button. +Set that token from *BotFather* (use your own!) to `TelegramTokenId`, for +now just temporarily: -Open just another chat with [GetIDs Bot](https://t.me/getidsbot), again start -with the `START` button. It will send you some information, including the -`id`, just below `You`. + :set TelegramTokenId "5214364459:AAHLwf1o7ybbKDo6pY24Kd2bZ5rjCakDXTc"; + +Now open a chat with your bot and start it by clicking the `START` button, +then send your first message. Any text will do. On your device run +`$GetTelegramChatId` to retrieve the chat id: + + $GetTelegramChatId; + +![get chat id](notification-telegram.d/getchatid.avif) Finally edit `global-config-overlay`, add `TelegramTokenId` with the token -from *BotFather* and `TelegramChatId` with your id from *GetIDs Bot*. Then +from *BotFather* and `TelegramChatId` with your retrieved chat id. Then reload the configuration. > ℹ️ **Info**: Copy relevant configuration from @@ -47,9 +61,13 @@ reload the configuration. ### Notifications to a group -Sending notifications to a group is possible as well. Add your bot and the -*GetIDs Bot* to a group, then use the group's id (which starts with a dash) -for `TelegramChatId`. Then remove *GetIDs Bot* from group. +Sending notifications to a group is possible as well. Add your bot to a group +and make it an admin (required for read access!) and send a message and run +`$GetTelegramChatId` again. Then use that chat id (which starts with a dash) +for `TelegramChatId`. + +Groups can enable the `Topics` feature. Use `TelegramThreadId` to send to a +specific topic in a group. Usage and invocation -------------------- diff --git a/doc/mod/scriptrunonce.md b/doc/mod/scriptrunonce.md index 895c20c..955d12e 100644 --- a/doc/mod/scriptrunonce.md +++ b/doc/mod/scriptrunonce.md @@ -1,6 +1,13 @@ Download script and run it once =============================== +[![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.15-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 module can not be used on its own but requires the base diff --git a/doc/mod/ssh-keys-import.md b/doc/mod/ssh-keys-import.md index 5fd95af..344f4bc 100644 --- a/doc/mod/ssh-keys-import.md +++ b/doc/mod/ssh-keys-import.md @@ -1,9 +1,14 @@ Import ssh keys for public key authentication ============================================= -[⬅️ Go back to main README](../../README.md) +[![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.16-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) -[![required RouterOS version](https://img.shields.io/badge/RouterOS-7.12-yellow?style=flat)](https://mikrotik.com/download/changelogs/) +[⬅️ Go back to main README](../../README.md) > ℹ️️ **Info**: This module can not be used on its own but requires the base > installation. See [main README](../../README.md) for details. @@ -29,12 +34,8 @@ Usage and invocation Call the function `$SSHKeysImport` with key and user as parameter to import that key: - $SSHKeysImport "ssh-rsa AAAAB3Nza...QYZk8= user" admin; - -Starting with RouterOS *7.12beta1* support for keys of type `ed25519` has -been added: - $SSHKeysImport "ssh-ed25519 AAAAC3Nza...ZVugJT user" admin; + $SSHKeysImport "ssh-rsa AAAAB3Nza...QYZk8= user" admin; The third part of the key (`user` in this example) is inherited as `key-owner` in RouterOS. Also the `MD5` fingerprint is recorded, this helps diff --git a/doc/mode-button.md b/doc/mode-button.md index 6374b83..be15bc9 100644 --- a/doc/mode-button.md +++ b/doc/mode-button.md @@ -1,6 +1,13 @@ Mode button with multiple presses ================================= +[![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.15-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 @@ -52,7 +59,7 @@ Configuration The configuration goes to `global-config-overlay`, these are the parameters: * `ModeButton`: an array with defined actions -* `ModeButtonLED`: led to give visual feedback +* `ModeButtonLED`: led to give visual feedback, `type` must be `on` or `off` > ℹ️ **Info**: Copy relevant configuration from > [`global-config`](../global-config.rsc) (the one without `-overlay`) to diff --git a/doc/netwatch-dns.md b/doc/netwatch-dns.md index a208d00..0d94918 100644 --- a/doc/netwatch-dns.md +++ b/doc/netwatch-dns.md @@ -1,6 +1,13 @@ Manage DNS and DoH servers from netwatch ======================================== +[![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.16-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 @@ -43,12 +50,25 @@ Giving a specific query url for DoH is possible: Note that using a name in DoH url may introduce a chicken-and-egg issue! -Importing a certificate automatically is possible, at least if available in -the repository (see `certs` sub directory). +Adding a static DNS record has the same result for the url, but always +resolves to the same address. - /tool/netwatch/add comment="doh, doh-cert=DigiCert TLS Hybrid ECC SHA384 2020 CA1" host=1.1.1.1; - /tool/netwatch/add comment="doh, doh-cert=DigiCert TLS Hybrid ECC SHA384 2020 CA1" host=9.9.9.9; - /tool/netwatch/add comment="doh, doh-cert=GTS CA 1C3" host=8.8.8.8; + /ip/dns/static/add name="cloudflare-dns.com" address=1.1.1.1; + /tool/netwatch/add comment="doh" host=1.1.1.1; + +Be aware that you have to keep the ip address in sync with real world +manually! + +Importing a certificate automatically is possible. You may want to find the +[certificate name from browser](../CERTIFICATES.md). + + /tool/netwatch/add comment="doh, doh-cert=DigiCert Global Root G2" host=1.1.1.1; + /tool/netwatch/add comment="doh, doh-cert=DigiCert Global Root G3" host=9.9.9.9; + /tool/netwatch/add comment="doh, doh-cert=GTS Root R1" host=8.8.8.8; + +> ⚠️ **Warning**: Combining these techniques can cause some confusion and +> troubles! Chances are that a service uses different certificates based +> on indicated server name. Sometimes using just one specific (possibly internal) DNS server may be desired, with fallback in case it fails. This is possible as well: @@ -71,6 +91,7 @@ Also this allows to update host address, see option `resolve`. See also -------- +* [Certificate name from browser](../CERTIFICATES.md) * [Notify on host up and down](netwatch-notify.md) --- diff --git a/doc/netwatch-notify.md b/doc/netwatch-notify.md index b673af8..81adfe9 100644 --- a/doc/netwatch-notify.md +++ b/doc/netwatch-notify.md @@ -1,6 +1,13 @@ Notify on host up and down ========================== +[![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.15-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 @@ -59,9 +66,9 @@ notification is sent. Getting the escaping right may be troublesome. Please consider adding a script in `/system/script`, then running that from hook. -### Count threshould +### Count threshold -The count threshould (default is 5 checks) is configurable as well: +The count threshold (default is 5 checks) is configurable as well: /tool/netwatch/add comment="notify, name=example.com, count=10" host=104.18.144.11; @@ -74,18 +81,23 @@ suppress notification if the parent host is down: /tool/netwatch/add comment="notify, name=example.com, parent=gateway" host=93.184.216.34; Note that every configured parent in a chain increases the check count -threshould by one. +threshold by one. ### Update from DNS The host address can be updated dynamically. Give extra parameter `resolve` with a resolvable name: - /tool/netwatch/add comment="notify, name=example.com, resolve=example.com"; + /tool/netwatch/add comment="notify, name=example.com, resolve=example.com" host=0.0; -But be warned: Dynamic updates will probably cause issues if the name has -more than one record in dns - a high rate of configuration changes (and flash -writes) at least. +This supports multiple A records for a name just fine, even a CNAME +to those. An update happens only if no more record with the configured host +address is found. + +The address family is preserved, so if you want AAAA records (for IPv6) +use this: + + /tool/netwatch/add comment="notify, name=example.com, resolve=example.com" host=::; ### No notification on host down @@ -97,6 +109,15 @@ powered off, but accessibility is of interest. Go and get your coffee ☕️ before sending the print job. +### No log on failed resolve + +A message is writting to log after three failed attemts to resolve a host. +However this can cause some noise for hosts that are expected to have +failures, for example when the name is dynamically added by +[`dhcp-to-dns`](dhcp-to-dns.md). This can be suppressed: + + /tool/netwatch/add comment="notify, name=client, resolve=client.dhcp.example.com, no-resolve-fail" host=10.0.0.0; + ### Add a note in notification For some extra information it is possible to add a text note. This is @@ -104,6 +125,13 @@ included verbatim into the notification. /tool/netwatch/add comment="notify, name=example, note=Do not touch!" host=10.0.0.31; +### Add a link in notification + +It is possible to add a link in notification, that is added below the +formatted notification text. + + /tool/netwatch/add comment="notify, name=example.com, resolve=example.com, link=https://example.com/" host=0.0; + Tips & Tricks ------------- diff --git a/doc/ospf-to-leds.md b/doc/ospf-to-leds.md index ce7f9ff..3694d35 100644 --- a/doc/ospf-to-leds.md +++ b/doc/ospf-to-leds.md @@ -1,6 +1,13 @@ Visualize OSPF state via LEDs ============================= +[![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.15-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 diff --git a/doc/packages-update.md b/doc/packages-update.md index 653d233..75225fe 100644 --- a/doc/packages-update.md +++ b/doc/packages-update.md @@ -1,6 +1,13 @@ Manage system update ==================== +[![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.15-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 @@ -34,6 +41,21 @@ Just install the script: It is automatically run by [check-routeros-update](check-routeros-update.md) if available. +Configuration +------------- + +The configuration goes to `global-config-overlay`, this is the only parameter: + +* `PackagesUpdateDeferReboot`: defer the reboot for night (between 3 AM + and 5 AM) + +By modifying the scheduler's `start-time` you can force the reboot at +different time. + +> ℹ️ **Info**: Copy relevant configuration from +> [`global-config`](../global-config.rsc) (the one without `-overlay`) to +> your local `global-config-overlay` and modify it to your specific needs. + Usage and invocation -------------------- diff --git a/doc/ppp-on-up.md b/doc/ppp-on-up.md index d5d0ae5..305afc1 100644 --- a/doc/ppp-on-up.md +++ b/doc/ppp-on-up.md @@ -1,6 +1,13 @@ Run scripts on ppp connection ============================= +[![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.15-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 diff --git a/doc/sms-action.md b/doc/sms-action.md index 3391902..b696c85 100644 --- a/doc/sms-action.md +++ b/doc/sms-action.md @@ -1,6 +1,13 @@ Act on received SMS =================== +[![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.15-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 diff --git a/doc/sms-forward.md b/doc/sms-forward.md index 823805e..ccb6482 100644 --- a/doc/sms-forward.md +++ b/doc/sms-forward.md @@ -1,6 +1,13 @@ Forward received SMS ==================== +[![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.15-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 diff --git a/doc/super-mario-theme.md b/doc/super-mario-theme.md index b3100e8..c72f220 100644 --- a/doc/super-mario-theme.md +++ b/doc/super-mario-theme.md @@ -1,6 +1,13 @@ Play Super Mario theme ====================== +[![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.15-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) Description diff --git a/doc/telegram-chat.md b/doc/telegram-chat.md index 397920a..1e6f70f 100644 --- a/doc/telegram-chat.md +++ b/doc/telegram-chat.md @@ -1,6 +1,13 @@ Chat with your router and send commands via Telegram bot ======================================================== +[![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.15-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 @@ -100,6 +107,14 @@ scripting capabilities. Try to print what you want to act on... /ip/address/remove [ find where interface=eth ]; +What does work is using the persistent ids: + + /ip/address/print show-ids; + +The output contains an id starting with asterisk that can be used: + + /ip/address/remove *E; + ### Mind command runtime The command is run in background while the script waits for it - about @@ -115,9 +130,8 @@ send information on its own. Something like this should do the job: ### Output size -RouterOS is limited in reading file content to a size of about four -kilobytes. Reading larger files does just fail, and that is also the limit -for command output. +Telegram messages have a limit of 4096 characters. If output is too large it +is truncated, and a warning is added to the message. ### Sending commands to a group diff --git a/doc/unattended-lte-firmware-upgrade.md b/doc/unattended-lte-firmware-upgrade.md index 8bc9e71..cb96aa1 100644 --- a/doc/unattended-lte-firmware-upgrade.md +++ b/doc/unattended-lte-firmware-upgrade.md @@ -1,6 +1,13 @@ Install LTE firmware upgrade ============================ +[![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.15-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) Description @@ -12,6 +19,7 @@ This script upgrades LTE firmware on compatible devices: * R11e-LTE-US * R11e-4G * R11e-LTE6 +* ... and more - probably what ever Mikrotik builds into their devices A temporary scheduler is created to be independent from terminal. Thus starting the upgrade process over the broadband connection is supported. diff --git a/doc/update-gre-address.md b/doc/update-gre-address.md index 5bf95bd..de9f622 100644 --- a/doc/update-gre-address.md +++ b/doc/update-gre-address.md @@ -1,6 +1,13 @@ Update GRE configuration with dynamic addresses =============================================== +[![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.15-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 diff --git a/doc/update-tunnelbroker.md b/doc/update-tunnelbroker.md index 4d6f1ba..ee0fe98 100644 --- a/doc/update-tunnelbroker.md +++ b/doc/update-tunnelbroker.md @@ -1,6 +1,13 @@ Update tunnelbroker configuration ================================= +[![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.15-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 diff --git a/firmware-upgrade-reboot.rsc b/firmware-upgrade-reboot.rsc index ec2babc..86a9a8c 100644 --- a/firmware-upgrade-reboot.rsc +++ b/firmware-upgrade-reboot.rsc @@ -1,45 +1,60 @@ #!rsc by RouterOS # RouterOS script: firmware-upgrade-reboot -# Copyright (c) 2022-2023 Christian Hesse -# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md +# Copyright (c) 2022-2025 Christian Hesse +# https://rsc.eworm.de/COPYING.md +# +# requires RouterOS, version=7.15 # # install firmware upgrade, and reboot -# https://git.eworm.de/cgit/routeros-scripts/about/doc/firmware-upgrade-reboot.md +# https://rsc.eworm.de/doc/firmware-upgrade-reboot.md -:local 0 "firmware-upgrade-reboot"; :global GlobalFunctionsReady; :while ($GlobalFunctionsReady != true) do={ :delay 500ms; } -:global LogPrintExit2; -:global ScriptLock; -:global VersionToNum; +:local ExitOK false; +:do { + :local ScriptName [ :jobname ]; -$ScriptLock $0; + :global LogPrint; + :global ScriptLock; + :global VersionToNum; -:local RouterBoard [ /system/routerboard/get ]; -:if ($RouterBoard->"current-firmware" = $RouterBoard->"upgrade-firmware") do={ - $LogPrintExit2 info $0 ("Current and upgrade firmware match with version " . \ - $RouterBoard->"current-firmware" . ".") true; + :if ([ $ScriptLock $ScriptName ] = false) do={ + :set ExitOK true; + :error false; + } + + :local RouterBoard [ /system/routerboard/get ]; + :if ($RouterBoard->"current-firmware" = $RouterBoard->"upgrade-firmware") do={ + $LogPrint info $ScriptName ("Current and upgrade firmware match with version " . \ + $RouterBoard->"current-firmware" . "."); + :set ExitOK true; + :error true; + } + :if ([ $VersionToNum ($RouterBoard->"current-firmware") ] > [ $VersionToNum ($RouterBoard->"upgrade-firmware") ]) do={ + $LogPrint info $ScriptName ("Different firmware version is available, but it is a downgrade. Ignoring."); + :set ExitOK true; + :error true; + } + + :if ([ /system/routerboard/settings/get auto-upgrade ] = false) do={ + $LogPrint info $ScriptName ("Firmware version " . $RouterBoard->"upgrade-firmware" . \ + " is available, upgrading."); + /system/routerboard/upgrade; + } + + :while ([ :len [ /log/find where topics=({"system";"info";"critical"}) \ + message="Firmware upgraded successfully, please reboot for changes to take effect!" ] ] = 0) do={ + :delay 1s; + } + + :local Uptime [ /system/resource/get uptime ]; + :if ($Uptime < 1m) do={ + :delay $Uptime; + } + + $LogPrint info $ScriptName ("Firmware upgrade successful, rebooting."); + /system/reboot; +} on-error={ + :global ExitError; $ExitError $ExitOK [ :jobname ]; } -:if ([ $VersionToNum ($RouterBoard->"current-firmware") ] > [ $VersionToNum ($RouterBoard->"upgrade-firmware") ]) do={ - $LogPrintExit2 info $0 ("Different firmware version is available, but it is a downgrade. Ignoring.") true; -} - -:if ([ /system/routerboard/settings/get auto-upgrade ] = false) do={ - $LogPrintExit2 info $0 ("Firmware version " . $RouterBoard->"upgrade-firmware" . \ - " is available, upgrading.") false; - /system/routerboard/upgrade; -} - -:while ([ :len [ /log/find where topics=({"system";"info";"critical"}) \ - message="Firmware upgraded successfully, please reboot for changes to take effect!" ] ] = 0) do={ - :delay 1s; -} - -:local Uptime [ /system/resource/get uptime ]; -:if ($Uptime < 1m) do={ - :delay $Uptime; -} - -$LogPrintExit2 info $0 ("Firmware upgrade successful, rebooting.") false; -/system/reboot; diff --git a/fw-addr-lists.d/allow b/fw-addr-lists.d/allow new file mode 100644 index 0000000..8b59ed7 --- /dev/null +++ b/fw-addr-lists.d/allow @@ -0,0 +1,3 @@ +# an ip address list for use with fw-addr-lists script +# https://git.eworm.de/cgit/routeros-scripts/about/doc/fw-addr-lists.md +git.eworm.de diff --git a/fw-addr-lists.d/block b/fw-addr-lists.d/block new file mode 100644 index 0000000..5e9fef2 --- /dev/null +++ b/fw-addr-lists.d/block @@ -0,0 +1,5 @@ +# an ip address list for use with fw-addr-lists script +# https://git.eworm.de/cgit/routeros-scripts/about/doc/fw-addr-lists.md + +# example.net +93.184.216.34 diff --git a/fw-addr-lists.d/mikrotik b/fw-addr-lists.d/mikrotik new file mode 100644 index 0000000..3b31a94 --- /dev/null +++ b/fw-addr-lists.d/mikrotik @@ -0,0 +1,5 @@ +# AS51894 Mikrotikls SIA +# https://bgp.he.net/AS51894 +159.148.147.0/24 +159.148.172.0/24 +2a02:610:7501::/48 diff --git a/fw-addr-lists.rsc b/fw-addr-lists.rsc index 26e9f98..f0940fe 100644 --- a/fw-addr-lists.rsc +++ b/fw-addr-lists.rsc @@ -1,112 +1,214 @@ #!rsc by RouterOS # RouterOS script: fw-addr-lists -# Copyright (c) 2023 Christian Hesse -# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md +# Copyright (c) 2023-2025 Christian Hesse +# https://rsc.eworm.de/COPYING.md +# +# requires RouterOS, version=7.16 # # download, import and update firewall address-lists -# https://git.eworm.de/cgit/routeros-scripts/about/doc/fw-addr-lists.md +# https://rsc.eworm.de/doc/fw-addr-lists.md -:local 0 "fw-addr-lists"; :global GlobalFunctionsReady; :while ($GlobalFunctionsReady != true) do={ :delay 500ms; } -:global FwAddrLists; -:global FwAddrListTimeOut; +:local ExitOK false; +:do { + :local ScriptName [ :jobname ]; -:global CertificateAvailable; -:global LogPrintExit2; -:global ScriptLock; -:global WaitFullyConnected; + :global FwAddrLists; + :global FwAddrListTimeOut; -:local FindDelim do={ - :local ValidChars "0123456789./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-"; - :for I from=0 to=[ :len $1 ] do={ - :if ([ :typeof [ :find $ValidChars [ :pick ($1 . " ") $I ] ] ] != "num") do={ - :return $I; + :global CertificateAvailable; + :global EitherOr; + :global FetchHuge; + :global HumanReadableNum; + :global LogPrint; + :global LogPrintOnce; + :global LogPrintVerbose; + :global ScriptLock; + :global WaitFullyConnected; + + :local FindDelim do={ + :local ValidChars "0123456789.:/ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-"; + :for I from=0 to=[ :len $1 ] do={ + :if ([ :typeof [ :find $ValidChars [ :pick ($1 . " ") $I ] ] ] != "num") do={ + :return $I; + } } } -} -$ScriptLock $0; -$WaitFullyConnected; + :local GetBranch do={ + :global EitherOr; + :return [ :pick [ :convert transform=md5 to=hex [ :pick $1 0 [ $EitherOr [ :find $1 "/" ] [ :len $1 ] ] ] ] 0 2 ]; + } -:local ListComment ("managed by " . $0); + :if ([ $ScriptLock $ScriptName ] = false) do={ + :set ExitOK true; + :error false; + } + $WaitFullyConnected; -:foreach FwListName,FwList in=$FwAddrLists do={ - :local Addresses ({}); - :local CntAdd 0; - :local CntRenew 0; - :local CntRemove 0; - :local Failure false; + :local ListComment ("managed by " . $ScriptName); - :foreach List in=$FwList do={ - :local CheckCertificate "no"; - :local Data false; + :foreach FwListName,FwList in=$FwAddrLists do={ + :local CntAdd 0; + :local CntRenew 0; + :local CntRemove 0; + :local IPv4Addresses ({}); + :local IPv6Addresses ({}); + :local Failure false; - :if ([ :len ($List->"cert") ] > 0) do={ - :set CheckCertificate "yes-without-crl"; - :if ([ $CertificateAvailable ($List->"cert") ] = false) do={ - $LogPrintExit2 warning $0 ("Downloading required certificate failed, trying anyway.") false; + :foreach List in=$FwList do={ + :local CheckCertificate false; + :local Data false; + :local TimeOut [ $EitherOr [ :totime ($List->"timeout") ] $FwAddrListTimeOut ]; + + :if ([ :len ($List->"cert") ] > 0) do={ + :set CheckCertificate true; + :if ([ $CertificateAvailable ($List->"cert") ] = false) do={ + $LogPrint warning $ScriptName ("Downloading required certificate (" . $FwListName . \ + " / " . $List->"url" . ") failed, trying anyway."); + } + } + + :for I from=1 to=5 do={ + :if ($Data = false) do={ + :set Data [ :tolf [ $FetchHuge $ScriptName ($List->"url") $CheckCertificate ] ]; + :if ($Data = false) do={ + :if ($I < 5) do={ + $LogPrint debug $ScriptName ("Failed downloading for list '" . $FwListName . \ + "', " . $I . ". try from: " . $List->"url"); + :delay (($I * $I) . "s"); + } + } + } + } + + :if ($Data = false) do={ + :set Data ""; + :set Failure true; + $LogPrint warning $ScriptName ("Failed downloading for list '" . $FwListName . \ + "' from: " . $List->"url"); + } else={ + $LogPrint debug $ScriptName ("Downloaded " . [ $HumanReadableNum [ :len $Data ] 1024 ] . \ + "B for list '" . $FwListName . "' from: " . $List->"url"); + } + + :foreach Line in=[ :deserialize $Data delimiter="\n" from=dsv options=dsv.plain ] do={ + :set Line ($Line->0); + :local Address; + :if ([ :pick $Line 0 1 ] = "{") do={ + :do { + :set Address [ :tostr ([ :deserialize from=json $Line ]->"cidr") ]; + } on-error={ } + } else={ + :set Address ([ :pick $Line 0 [ $FindDelim $Line ] ] . ($List->"cidr")); + } + :do { + :local Branch [ $GetBranch $Address ]; + :if ($Address ~ "^[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}(/[0-9]{1,2})?\$") do={ + :if ($Address ~ "/32\$") do={ + :set Address [ :pick $Address 0 ([ :len $Address ] - 3) ]; + } + :set ($IPv4Addresses->$Branch->$Address) $TimeOut; + :error true; + } + :if ($Address ~ "^[0-9a-zA-Z]*:[0-9a-zA-Z:\\.]+(/[0-9]{1,3})?\$") do={ + :if ($Address ~ "/128\$") do={ + :set Address [ :pick $Address 0 ([ :len $Address ] - 4) ]; + } + :set ($IPv6Addresses->$Branch->$Address) $TimeOut; + :error true; + } + :if ($Address ~ "^[\\.a-zA-Z0-9-]+\\.[a-zA-Z]{2,}\$") do={ + :set ($IPv4Addresses->$Branch->$Address) $TimeOut; + :set ($IPv6Addresses->$Branch->$Address) $TimeOut; + :error true; + } + } on-error={ } } } - :for I from=1 to=4 do={ - :if ($Data = false) do={ - :do { - :set Data ([ /tool/fetch check-certificate=$CheckCertificate output=user \ - ($List->"url") as-value ]->"data"); - } on-error={ - :if ($I < 4) do={ - $LogPrintExit2 debug $0 ("Failed downloading, " . $I . ". try: " . $List->"url") false; - :delay (($I * $I) . "s"); - } + :foreach Entry in=[ /ip/firewall/address-list/find where \ + list=$FwListName comment=$ListComment ] do={ + :local Address [ /ip/firewall/address-list/get $Entry address ]; + :local Branch [ $GetBranch $Address ]; + :local TimeOut ($IPv4Addresses->$Branch->$Address); + :if ([ :typeof $TimeOut ] = "time") do={ + $LogPrintVerbose debug $ScriptName ("Renewing IPv4 address in list '" . $FwListName . \ + "' with " . $TimeOut . ": " . $Address); + /ip/firewall/address-list/set $Entry timeout=$TimeOut; + :set ($IPv4Addresses->$Branch->$Address); + :set CntRenew ($CntRenew + 1); + } else={ + :if ($Failure = false) do={ + $LogPrintVerbose debug $ScriptName ("Removing IPv4 address from list '" . $FwListName . \ + "': " . $Address); + /ip/firewall/address-list/remove $Entry; + :set CntRemove ($CntRemove + 1); } } } - :if ($Data = false) do={ - :set Data ""; - :set Failure true; - $LogPrintExit2 warning $0 ("Failed downloading list from: " . $List->"url") false; - } - - :while ([ :len $Data ] != 0) do={ - :local Line [ :pick $Data 0 [ :find $Data "\n" ] ]; - :local Address ([ :pick $Line 0 [ $FindDelim $Line ] ] . ($List->"cidr")); - :if ($Address ~ "^[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}(/[0-9]{1,2})?\$" || \ - $Address ~ "^[\\.a-zA-Z0-9-]+\\.[a-zA-Z]{2,}\$") do={ - :set ($Addresses->$Address) 1; - } - :set Data [ :pick $Data ([ :len $Line ] + 1) [ :len $Data ] ]; - } - } - - :foreach Entry in=[ /ip/firewall/address-list/find where list=$FwListName comment=$ListComment ] do={ - :local Address [ /ip/firewall/address-list/get $Entry address ]; - :if (($Addresses->$Address) = 1) do={ - $LogPrintExit2 debug $0 ("Renewing: " . $Address) false; - /ip/firewall/address-list/set $Entry timeout=$FwAddrListTimeOut; - :set ($Addresses->$Address); - :set CntRenew ($CntRenew + 1); - } else={ - :if ($Failure = false) do={ - $LogPrintExit2 debug $0 ("Removing: " . $Address) false; - /ip/firewall/address-list/remove $Entry; - :set CntRemove ($CntRemove + 1); + :foreach Entry in=[ /ipv6/firewall/address-list/find where \ + list=$FwListName comment=$ListComment ] do={ + :local Address [ /ipv6/firewall/address-list/get $Entry address ]; + :local Branch [ $GetBranch $Address ]; + :local TimeOut ($IPv6Addresses->$Branch->$Address); + :if ([ :typeof $TimeOut ] = "time") do={ + $LogPrintVerbose debug $ScriptName ("Renewing IPv6 address in list '" . $FwListName . \ + "' with " . $TimeOut . ": " . $Address); + /ipv6/firewall/address-list/set $Entry timeout=$TimeOut; + :set ($IPv6Addresses->$Branch->$Address); + :set CntRenew ($CntRenew + 1); + } else={ + :if ($Failure = false) do={ + $LogPrintVerbose debug $ScriptName ("Removing IPv6 address from list '" . $FwListName . \ + "': " . $Address); + /ipv6/firewall/address-list/remove $Entry; + :set CntRemove ($CntRemove + 1); + } } } - } - :foreach Address,Ignore in=$Addresses do={ - $LogPrintExit2 debug $0 ("Adding: " . $Address) false; - :do { - /ip/firewall/address-list/add list=$FwListName comment=$ListComment address=$Address timeout=$FwAddrListTimeOut; - :set ($Addresses->$Address); - :set CntAdd ($CntAdd + 1); - } on-error={ - $LogPrintExit2 warning $0 ("Failed to add address " . $Address . " to list '" . $FwListName . "'.") false; + :foreach BranchName,Branch in=$IPv4Addresses do={ + $LogPrintVerbose debug $ScriptName ("Handling branch: " . $BranchName); + :foreach Address,Timeout in=$Branch do={ + $LogPrintVerbose debug $ScriptName ("Adding IPv4 address to list '" . $FwListName . \ + "' with " . $Timeout . ": " . $Address); + :do { + /ip/firewall/address-list/add list=$FwListName comment=$ListComment \ + address=$Address timeout=$Timeout; + :set CntAdd ($CntAdd + 1); + } on-error={ + $LogPrint warning $ScriptName ("Failed to add IPv4 address to list '" . $FwListName . \ + "': " . $Address); + } + } } - } - $LogPrintExit2 info $0 ("list: " . $FwListName . " -- added: " . $CntAdd . " - renewed: " . $CntRenew . " - removed: " . $CntRemove) false; + :foreach BranchName,Branch in=$IPv6Addresses do={ + $LogPrintVerbose debug $ScriptName ("Handling branch: " . $BranchName); + :foreach Address,Timeout in=$Branch do={ + $LogPrintVerbose debug $ScriptName ("Adding IPv6 address to list '" . $FwListName . \ + "' with " . $Timeout . ": " . $Address); + :do { + /ipv6/firewall/address-list/add list=$FwListName comment=$ListComment \ + address=$Address timeout=$Timeout; + :set CntAdd ($CntAdd + 1); + } on-error={ + $LogPrint warning $ScriptName ("Failed to add IPv6 address to list '" . $FwListName . \ + "': " . $Address); + } + } + } + + $LogPrint info $ScriptName ("list: " . $FwListName . \ + " (" . [ $HumanReadableNum ($CntAdd + $CntRenew) 1000 ] . ")" . \ + " -- added: " . [ $HumanReadableNum $CntAdd 1000 ] . \ + " - renewed: " . [ $HumanReadableNum $CntRenew 1000 ] . \ + " - removed: " . [ $HumanReadableNum $CntRemove 1000 ]); + } +} on-error={ + :global ExitError; $ExitError $ExitOK [ :jobname ]; } diff --git a/global-config-overlay.rsc b/global-config-overlay.rsc index af4b52c..9afaceb 100644 --- a/global-config-overlay.rsc +++ b/global-config-overlay.rsc @@ -1,12 +1,12 @@ # Overlay for global configuration by RouterOS Scripts -# Copyright (c) 2013-2023 Christian Hesse -# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md +# Copyright (c) 2013-2025 Christian Hesse +# https://rsc.eworm.de/COPYING.md # # global configuration, custom overlay -# https://git.eworm.de/cgit/routeros-scripts/about/#editing-configuration +# https://rsc.eworm.de/#editing-configuration # Copy relevant configuration from global-config, paste and modify it here. -# https://git.eworm.de/cgit/routeros-scripts/about/global-config.rsc +# https://rsc.eworm.de/global-config.rsc # End of global-config-overlay diff --git a/global-config.changes b/global-config.changes deleted file mode 100644 index 47cfbc6..0000000 --- a/global-config.changes +++ /dev/null @@ -1,123 +0,0 @@ -# News, changes and migration by RouterOS Scripts -# Copyright (c) 2019-2023 Christian Hesse -# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md - -:global IfThenElse; -:global RequiredRouterOS; - -# Changes for global-config to be added to notification on script updates -:global GlobalConfigChanges { - 1="Moved variables from 'global-config' to 'global-functions' for independence"; - 2="Variable names became CamelCase to work around scripting issues"; - 3="Variable for certificate renew passphrase became an array to support multiple passphrases"; - 4="Added option to ignore global-config changes"; - 5="Split off new script 'cloud-backup' from 'email-backup'"; - 6="Introduced script 'upload-backup' with new configuration parameters"; - 7="Introduced script 'check-health' with new configuration parameters"; - 8="Added donation hint and option to silence it"; - 9="Introduced configuration overlay 'global-config-overlay'"; - 10="Made health threshold for voltage configurable"; - 11="Introduced function '\$ScriptInstallUpdate' to install new and update existing scripts"; - 12="Removed '\$ScriptUpdatesConfigChangesIgnore', comment '\$GlobalConfigVersion' in 'global-config-overlay' to disable change notifications"; - 13="Configuration for script 'bridge-port-to-default' changed with new syntax in comment"; - 14="Dropped script 'script-updates', use '\$ScriptInstallUpdate' exclusively!"; - 15="New documentation is online! https://git.eworm.de/cgit/routeros-scripts/about/#available-scripts"; - 16="Happy with RouterOS Scripts and have a GitHub and/or GitLab account? Please star!"; - 17="Introduced script 'early-errors'"; - 18=("Added a simple IP calculation function, try: \$IPCalc " . [ /ip/address/get ([ find ]->0) address ]); - 19="Commenting scripts with 'ignore', 'base-url=...' and 'url-suffix=...' is honored on update"; - 20="Added support for hooks to 'netwatch-notify'"; - 21="Added support for installing patch updates automatically by 'check-routeros-update'"; - 22="Dropped '\$ScriptUpdatesIgnore' from global configuration, auto-migrating to ignore flag in comment" - 23="Added 'log-forward' with configurable filter, which replaces 'early-errors'"; - 24="Made symbols in notifications configurable."; - 25="Added support for DHCP server name in DNS FQDN via '\$ServerNameInZone'"; - 26="Made check count threshold in 'netwatch-notify' configurable."; - 27="Added queue for Telegram notifications to resend later on error."; - 28="Made 'dhcp-to-dns' act on all bound leases, not just dynamic ones."; - 29="Added filter on log message text for 'log-forward'."; - 30="Implemented simple rate limit for 'log-forward' to prevent flooding."; - 31="Switched Telegram notifications to fixed-width font, with opt-out."; - 32="Merged mode (& reset) button scripts in single new script 'mode-button'."; - 33="Added configurable deviation on health temperature recovery threshold against notification flooding."; - 34="Introduced script 'ospf-to-leds' to visualize OSPF instance state via LEDs."; - 35="Implemented visual feedback for 'mode-button' with configurable LED."; - 36="Added support for installing updates automatically if seen in neighbor list."; - 37="Implemented simple dependency model in 'netwatch-notify'."; - 38="Imported new Let's Encrypt intermediate certificate 'R3'."; - 39="Added support for interface specific address list entries in 'ipv6-update'."; - 40="Made the certificate renewal time configurable."; - 41="Implemented migration mechanism for script updates."; - 42="Made severity in terminal output colorful, with opt-out."; - 43="Added queue for e-mail notifications to resend later on error."; - 44="Dropped script 'global-wait', all scripts wait on their own now."; - 45="We have a Telegram Group! Come along and say hello: https://t.me/routeros_scripts"; - 46="Added configurable random delay in backup scripts to stretch execution and prevent resource congestion."; - 47="Removed obsolete intermediate certificate 'Let's Encrypt Authority X3' from store."; - 48="Added support for overriding e-mail and Telegram settings for every script."; - 49="Dropped '\$EmailBackupTo' & '\$EmailBackupCc' from configuration, use settings override if required."; - 50="Added support for dynamic address update in 'netwatch-notify'."; - 51="Added 'ipsec-to-dns' to add DNS records for IPSec peers from mode-config."; - 52="Updated Let's Encrypt trust chain to use root certificate 'ISRG Root X1'. Do not re-import the old chain!"; - 53="Added support to send notifications via Matrix."; - 54="Support for Telegram notifications moved to a module. It is installed automatically if required."; - 55="Added reverse logic in 'log-forward', so messages can be included even if filtered before."; - 56="Added tags in all backup, lease and ppp-on-up scripts. These are used by 'packages-update', 'lease-script' and 'ppp-on-up' to find the scripts."; - 57="Celebrating the 1.000th commit - Hooray!"; - 58="Added a cleanup script for 'hotspot-to-wpa' to purge old access list entries."; - 59="Updating CAP with 'check-routeros-update' is now possible with opt-in."; - 60="Implemented a pre-down hook in 'netwatch-notify' that fires at two thirds of failed checks."; - 61="Finally removed old scripts."; - 62="Added '\$ScriptRunOnce' to run a script from URL once without installation, intended to aid configuration management and the like."; - 63="Moved optional functions '\$IPCalc' and '\$ScriptRunOnce' to modules."; - 64="Implemented '\$InspectVar' in module to inspect variables."; - 65="Added module to manage VLANs on bridge ports."; - 66="Moved script 'bridge-port-to-default' to new module."; - 67="Moved modules to directory with shorter name."; - 68="Reintroduced 'global-wait' for functions in scheduler."; - 69="Support hard lower limit for voltage in 'check-health'."; - 70="MikroTik started pushing RouterOS v7. Changes are no longer required."; - 71="MikroTik is pushing RouterOS v7 even more, in parallel branches. If you want to keep RouterOS v6 for some time see https://git.eworm.de/cgit/routeros-scripts/about/#requirements"; - 72="Introduced new script 'netwatch-dns' to manage DNS and DoH servers from netwatch."; - 73="Renamed backup scripts ('cloud-backup' -> 'backup-cloud', 'email-backup' -> 'backup-email', 'upload-backup' -> 'backup-upload')."; - 74="Extended 'hotspot-to-wpa', it can now read additional configuration from templates and hotspot users."; - 75=("Finally merged the RouterOS v7 code into the main branch. " . [ $IfThenElse ([ $RequiredRouterOS "global-config.changes" "7.0" false ] = true) \ - ("You may now drop '\$ScriptUpdatesUrlSuffix' from 'global-config-overlay'.") \ - ("Still running RouterOS v6, so last reminder to see https://git.eworm.de/cgit/routeros-scripts/about/#requirements") ]); - 76="Added an option to suppress notifications on host down with 'netwatch-notify'."; - 77="Introduced new script 'firmware-upgrade-reboot'. Handle with care!"; - 78="New documentation is online for notifications via Telegram & Matrix, variable inspection, ip address calculation and running scripts once."; - 79="Introduced new script 'backup-partition' to save configuration to fallback partition."; - 80="The 'routeros-v7' branch will now freeze, and vanish any time in future. You already switched to 'main' branch, well done!"; - 81="Dropped script 'rotate-ntp', as the limitation does no longer exist."; - 82="Renamed the comment parameter 'hostname' to just 'name' for 'netwatch-notify'."; - 83="Introduced new setting to disable news and change notifications, dropped version from configuration."; - 84="Support for e-mail notifications moved to a module. It is installed automatically if required."; - 85="Dropped 'netwatch-syslog', filtering in firewall is advised."; - 86="Added support for hooks in 'sms-forward'. This now provides similar functionality to 'sms-action', but is more flexible."; - 87="Added support for extra text (or emojis \F0\9F\9A\80) in notification tags."; - 88="Added support for monitoring CPU utilization and available free RAM in 'check-health'."; - 89="Made the warning time for 'check-certificates' configurable."; - 90="Chat with your router! Introduced 'telegram-chat' to chat via Telegram bot and send commands to your router."; - 91="Dropped check for CAP in 'check-routeros-update' to solve issues with wifiwave2 package."; - 92="Made qr-code url configurable for 'daily-psk'."; - 93="Added support to backup global-config-overlay in 'backup-email' and 'backup-upload'."; - 94="Added support for host addresses in address-list for 'ipv6-update'."; - 95="Renamed script files in repository, running migration. Warnings (one per script) are expected, no user interaction is required."; -}; - -# Migration steps to be applied on script updates -:global GlobalConfigMigration { - 41=":global SendNotification; \$SendNotification (\"Migration mechanism\") (\"Congratulations!\nSuccessfully tested the new migration mechanism.\");"; - 47="/certificate/remove [ find where fingerprint=\"731d3d9cfaa061487a1d71445a42f67df0afca2a6c2d2f98ff7b3ce112b1f568\" or fingerprint=\"25847d668eb4f04fdd40b12b6b0740c567da7d024308eb6c2c96fe41d9de218d\" ];"; - 52=":global CertificateDownload; :if ([ :len [ /certificate/find where fingerprint=\"67add1166b020ae61b8f5fc96813c04c2aa589960796865572a3c7e737613dfd\" or fingerprint=\"96bcec06264976f37460779acf28c5a7cfe8a3c0aae11a8ffcee05c0bddf08c6\" ] ] < 2) do={ \$CertificateDownload \"R3\"; }; /certificate/remove [ find where fingerprint=\"0687260331a72403d909f105e69bcf0d32e1bd2493ffc6d9206d11bcd6770739\" ];"; - 54=":global ScriptInstallUpdate; :global TelegramTokenId; :global TelegramChatId; :if ([ :len \$TelegramTokenId ] > 0 && [ :len \$TelegramChatId ] > 0) do={ \$ScriptInstallUpdate mod/notification-telegram; }"; - 61="/system/script/remove [ find where name~\"^(early-errors|mode-button-(event|scheduler)|script-updates)\\\$\" source~\"^#!rsc by RouterOS\\n\" ];"; - 66=":global ScriptInstallUpdate; :if ([ :len [ /system/script/find where name=\"bridge-port-to-default\" ] ] > 0) do={ /system/script/remove [ find where name~\"^bridge-port-to(-default|ggle)\\\$\" ]; \$ScriptInstallUpdate mod/bridge-port-to; }"; - 67=":global ScriptInstallUpdate; :global CharacterReplace; :foreach Script in=[ /system/script/find where name~\"^global-functions.d/\" ] do={ /system/script/set name=[ \$CharacterReplace [ /system/script/get \$Script name ] \"global-functions.d/\" \"mod/\" ] \$Script; }; \$ScriptInstallUpdate;"; - 73=":global ScriptInstallUpdate; :global CharacterReplace; :foreach Old,New in={ \"cloud-backup\"=\"backup-cloud\"; \"email-backup\"=\"backup-email\"; \"upload-backup\"=\"backup-upload\" } do={ /system/script/set name=\$New [ find where name=\$Old ]; :foreach Scheduler in=[ /system/scheduler/find where on-event~\$Old ] do={ /system/scheduler/set \$Scheduler name=[ \$CharacterReplace [ get \$Scheduler name ] \$Old \$New ] on-event=[ \$CharacterReplace [ get \$Scheduler on-event ] \$Old \$New ]; }; }; \$ScriptInstallUpdate;"; - 81=":global NtpPool; :if ([ :len [ /system/script/find where name=\"rotate-ntp\" ] ] > 0) do={ /system/script/remove [ find where name=\"rotate-ntp\" ]; /system/scheduler/remove [ find where name=\"rotate-ntp\" ]; /system/ntp/client/set servers=\$NtpPool; };"; - 82=":global CharacterReplace; :foreach Netwatch in=[ /tool/netwatch/find where comment~\"notify\" !disabled ] do={ /tool/netwatch/set \$Netwatch comment=[ \$CharacterReplace [ get \$Netwatch comment ] \"hostname=\" \"name=\" ]; };"; - 84=":global ScriptInstallUpdate; :global EmailGeneralTo; :if ([ /tool/e-mail/get address ] != \"0.0.0.0\" && [ :len \$EmailGeneralTo ] > 0) do={ \$ScriptInstallUpdate mod/notification-email; }"; - 95=":global ScriptInstallUpdate; :global CharacterReplace; :foreach Script in=[ /system/script/find where name~\"\\\\.rsc\\\$\" source~\"^#!rsc by RouterOS\\n\" ] do={ /system/script/set \$Script name=[ \$CharacterReplace [ get \$Script name ] \".rsc\" \"\" ]; }; \$ScriptInstallUpdate;"; -}; diff --git a/global-config.rsc b/global-config.rsc index dfb25ec..fa32b16 100644 --- a/global-config.rsc +++ b/global-config.rsc @@ -1,10 +1,10 @@ #!rsc by RouterOS # RouterOS script: global-config -# Copyright (c) 2013-2023 Christian Hesse -# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md +# Copyright (c) 2013-2025 Christian Hesse +# https://rsc.eworm.de/COPYING.md # # global configuration -# https://git.eworm.de/cgit/routeros-scripts/about/ +# https://rsc.eworm.de/ # Set this to 'true' to disable news and change notifications. :global NoNewsAndChangesNotification false; @@ -33,6 +33,8 @@ :global TelegramChatId ""; #:global TelegramTokenId "123456:ABCDEF-GHI"; #:global TelegramChatId "12345678"; +# Use this to send notifications to a specific topic in group. +:global TelegramThreadId ""; # Using telegram-chat you have to define trusted chat ids (not group ids!) # or user names. Groups allow to chat with devices simultaneously. #:global TelegramChatIdsTrusted { @@ -56,6 +58,9 @@ # install the module: # $ScriptInstallUpdate mod/notification-ntfy :global NtfyServer "ntfy.sh"; +:global NtfyServerUser ""; +:global NtfyServerPass ""; +:global NtfyServerToken ""; :global NtfyTopic ""; # It is possible to override e-mail, Telegram, Matrix and Ntfy setting @@ -83,42 +88,50 @@ :global BackupUploadUrl "sftp://example.com/backup/"; :global BackupUploadUser "mikrotik"; :global BackupUploadPass "v3ry-s3cr3t"; +# Copy the RouterOS installation to backup partition before feature update. +:global BackupPartitionCopyBeforeFeatureUpdate false; # This defines the settings for firewall address-lists (fw-addr-lists). +# Warning: Mind your device's resources - memory and processing! :global FwAddrLists { # "allow"={ -# { url="https://eworm.de/ros/fw-addr-lists/allow"; -# cert="E1" }; +# { url="https://rsc.eworm.de/main/fw-addr-lists.d/allow"; +# cert="ISRG Root X2"; timeout=1w }; # }; "block"={ -# { url="https://eworm.de/ros/fw-addr-lists/block"; -# cert="E1" }; - { url="https://feodotracker.abuse.ch/downloads/ipblocklist_recommended.txt"; - cert="GlobalSign Atlas R3 DV TLS CA 2022 Q3" }; - { url="https://sslbl.abuse.ch/blacklist/sslipblacklist.txt"; - cert="GlobalSign Atlas R3 DV TLS CA 2022 Q3" }; +# { url="https://rsc.eworm.de/main/fw-addr-lists.d/block"; +# cert="ISRG Root X2" }; + { 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" }; { url="https://www.dshield.org/block.txt"; cidr="/24"; - cert="R3" }; -# { url="https://www.spamhaus.org/drop/drop.txt"; -# cert="Cloudflare Inc ECC CA-3" }; -# { url="https://www.spamhaus.org/drop/edrop.txt"; -# cert="Cloudflare Inc ECC CA-3" }; + cert="ISRG Root X1" }; + { url="https://lists.blocklist.de/lists/strongips.txt"; + cert="Certum Trusted Network CA" }; +# { url="https://www.spamhaus.org/drop/drop_v4.json"; +# cert="GTS Root R4" }; +# { url="https://www.spamhaus.org/drop/drop_v6.json"; +# cert="GTS Root R4" }; }; +# "mikrotik"={ +# { url="https://rsc.eworm.de/main/fw-addr-lists.d/mikrotik"; +# cert="ISRG Root X2"; timeout=1w }; +# }; }; :global FwAddrListTimeOut 1d; # This defines what log messages to filter or include by topic or message -# text. Regular expressions are supported. Do *NOT* set an empty string, -# that will filter or include everything! +# text. Regular expressions are supported. An empty string has a special +# meaning not to filter or include anything. # These are filters, so excluding messages from forwarding. :global LogForwardFilter "(debug|info|packet|raw)"; -:global LogForwardFilterMessage []; +:global LogForwardFilterMessage ""; #:global LogForwardFilterMessage "message text"; #:global LogForwardFilterMessage "(message text|another text|...)"; # ... and another setting with reverse logic. This includes messages even # if filtered above. -:global LogForwardInclude []; -:global LogForwardIncludeMessage []; +:global LogForwardInclude ""; +:global LogForwardIncludeMessage ""; #:global LogForwardInclude "account"; #:global LogForwardIncludeMessage "message text"; @@ -135,6 +148,9 @@ # Set to all upper-case "Yes, please!" to enable. :global SafeUpdateAll "no"; +# Defer the reboot for night on automatic (non-interactive) update +:global PackagesUpdateDeferReboot false; + # These thresholds control when to send health notification # on temperature and voltage. :global CheckHealthTemperature { @@ -173,7 +189,7 @@ # Run different commands with multiple mode-button presses. :global ModeButton { - 1="/system/script/run leds-toggle-mode;"; + 1="/system/leds/settings/set all-leds-off=(({ \"never\"=\"immediate\"; \"immediate\"=\"never\" })->[ get all-leds-off ]);"; 2=":global Identity; :global SendNotification; :global SymbolForNotification; \$SendNotification ([ \$SymbolForNotification \"earth\" ] . \"Hello...\") (\"Hello world, \" . \$Identity . \" calling!\");"; 3="/system/shutdown;"; 4="/system/reboot;"; @@ -203,14 +219,16 @@ :global GpsTrackUrl "https://example.com/index.php"; # This is the base url to fetch scripts from. -:global ScriptUpdatesBaseUrl "https://git.eworm.de/cgit/routeros-scripts/plain/"; +:global ScriptUpdatesBaseUrl "https://rsc.eworm.de/main/"; # alternative urls - main: stable code - next: currently in development +#:global ScriptUpdatesBaseUrl "https://rsc.eworm.de/next/"; +#:global ScriptUpdatesBaseUrl "https://git.eworm.de/cgit/routeros-scripts/plain/"; #:global ScriptUpdatesBaseUrl "https://raw.githubusercontent.com/eworm-de/routeros-scripts/main/"; #:global ScriptUpdatesBaseUrl "https://raw.githubusercontent.com/eworm-de/routeros-scripts/next/"; #:global ScriptUpdatesBaseUrl "https://gitlab.com/eworm-de/routeros-scripts/raw/main/"; #:global ScriptUpdatesBaseUrl "https://gitlab.com/eworm-de/routeros-scripts/raw/next/"; :global ScriptUpdatesUrlSuffix ""; -# use next branch with default url (git.eworm.de) +# use next branch with my git url (git.eworm.de) #:global ScriptUpdatesUrlSuffix "?h=next"; # Use this for defaults with $ScriptRunOnce @@ -222,7 +240,7 @@ # This project is developed in private spare time and usage is free of charge # for you. If you like the scripts and think this is of value for you or your # business please consider a donation: -# https://git.eworm.de/cgit/routeros-scripts/about/#donate +# https://rsc.eworm.de/#donate # Enable this to silence donation hint. :global IDonate false; @@ -240,10 +258,14 @@ "cert2-cn"="4n0th3r-s3cr3t"; }; -# load custom settings from overlay +# load custom settings from overlay and snippets # Warning: Do *NOT* copy this code to overlay! -:do { - /system/script/run global-config-overlay; -} on-error={ - :log error ("Loading configuration from overlay failed!"); +:foreach Script in=([ /system/script/find where name="global-config-overlay" ], \ + [ /system/script/find where name~"^global-config-overlay.d/" ]) do={ + :do { + /system/script/run $Script; + } on-error={ + :log error ("Loading configuration from overlay or snippet " . \ + [ /system/script/get $Script name ] . " failed!"); + } } diff --git a/global-functions b/global-functions deleted file mode 100644 index 431a343..0000000 --- a/global-functions +++ /dev/null @@ -1,257 +0,0 @@ -#!rsc by RouterOS -# RouterOS script: global-functions -# Copyright (c) 2013-2023 Christian Hesse -# Michael Gisbers -# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md -# -# requires RouterOS, version=7.7 -# -# WARNING: If you find this stripped version of global-functions -# on your Router something went wrong and migration failed. To -# recover run this function: $RouterOSScriptsRecover - -# expected configuration version -:global ExpectedConfigVersion 95; - -# global functions -:global RouterOSScriptsRecover; -:global ScriptInstallUpdate; - -# recover from failed migration -:set RouterOSScriptsRecover do={ - :global ScriptInstallUpdate; - - :foreach Script in={ "global-config"; "global-functions" } do={ - /system/script/set name=$Script source=([ /tool/fetch check-certificate=yes-without-crl ("https://git.eworm.de/cgit/routeros-scripts/plain/" . $Script . ".rsc") output=user as-value]->"data"); - /system/script/run $Script; - } - - $ScriptInstallUpdate; -} - -# install new scripts, update existing scripts -:set ScriptInstallUpdate do={ - :local Scripts [ :toarray $1 ]; - :local NewComment [ :tostr $2 ]; - - :global ExpectedConfigVersion; - :global Identity; - :global IDonate; - :global NoNewsAndChangesNotification; - :global NotificationsWithSymbols; - :global ScriptUpdatesBaseUrl; - :global ScriptUpdatesFetch; - :global ScriptUpdatesUrlSuffix; - - :global CertificateAvailable; - :global EitherOr; - :global Grep; - :global IfThenElse; - :global LogPrintExit2; - :global ParseKeyValueStore; - :global RequiredRouterOS; - :global SendNotification2; - :global SymbolForNotification; - :global ValidateSyntax; - - :if ([ $CertificateAvailable "E1" ] = false) do={ - $LogPrintExit2 warning $0 ("Downloading certificate failed, trying without.") false; - } - - :foreach Script in=$Scripts do={ - :if ([ :len [ /system/script/find where name=$Script ] ] = 0) do={ - $LogPrintExit2 info $0 ("Adding new script: " . $Script) false; - /system/script/add name=$Script owner=$Script source="#!rsc by RouterOS\n" comment=$NewComment; - } - } - - :local ExpectedConfigVersionBefore $ExpectedConfigVersion; - :local ReloadGlobalFunctions false; - :local ReloadGlobalConfig false; - - :foreach Script in=[ /system/script/find where source~"^#!rsc by RouterOS\n" ] do={ - :local ScriptVal [ /system/script/get $Script ]; - :local ScriptFile [ /file/find where name=("script-updates/" . $ScriptVal->"name") ]; - :local SourceNew; - :if ([ :len $ScriptFile ] > 0) do={ - :set SourceNew [ /file/get $ScriptFile contents ]; - /file/remove $ScriptFile; - } - - :foreach Scheduler in=[ /system/scheduler/find where on-event~("\\b" . $ScriptVal->"name" . "\\b") ] do={ - :local SchedulerVal [ /system/scheduler/get $Scheduler ]; - :if ($ScriptVal->"policy" != $SchedulerVal->"policy") do={ - $LogPrintExit2 warning $0 ("Policies differ for script '" . $ScriptVal->"name" . \ - "' and its scheduler '" . $SchedulerVal->"name" . "'!") false; - } - } - - :if ([ :len $SourceNew ] = 0 && $ScriptUpdatesFetch = true) do={ - :local Comment [ $ParseKeyValueStore ($ScriptVal->"comment") ]; - :if (!($Comment->"ignore" = true)) do={ - :do { - :local BaseUrl $ScriptUpdatesBaseUrl; - :local UrlSuffix $ScriptUpdatesUrlSuffix; - :if ([ :typeof ($Comment->"base-url") ] = "str") do={ :set BaseUrl ($Comment->"base-url"); } - :if ([ :typeof ($Comment->"url-suffix") ] = "str") do={ :set UrlSuffix ($Comment->"url-suffix"); } - :local Url ($BaseUrl . $ScriptVal->"name" . ".rsc" . $UrlSuffix); - - $LogPrintExit2 debug $0 ("Fetching script '" . $ScriptVal->"name" . "' from url: " . $Url) false; - :local Result [ /tool/fetch check-certificate=yes-without-crl $Url output=user as-value ]; - :if ($Result->"status" = "finished") do={ - :set SourceNew ($Result->"data"); - } - } on-error={ - :if ($ScriptVal->"source" = "#!rsc by RouterOS\n") do={ - $LogPrintExit2 warning $0 ("Failed fetching script '" . $ScriptVal->"name" . \ - "', removing dummy. Typo on installation?") false; - /system/script/remove $Script; - } else={ - $LogPrintExit2 warning $0 ("Failed fetching script '" . $ScriptVal->"name" . "'!") false; - } - } - } - } - - :if ([ :len $SourceNew ] > 0) do={ - :if ($SourceNew != $ScriptVal->"source") do={ - :if ([ :pick $SourceNew 0 18 ] = "#!rsc by RouterOS\n") do={ - :local Required ([ $ParseKeyValueStore [ $Grep $SourceNew "# requires RouterOS, " ] ]->"version"); - :if ([ $RequiredRouterOS $0 [ $EitherOr $Required "0.0" ] false ] = true) do={ - :if ([ $ValidateSyntax $SourceNew ] = true) do={ - $LogPrintExit2 info $0 ("Updating script: " . $ScriptVal->"name") false; - /system/script/set owner=($ScriptVal->"name") source=$SourceNew $Script; - :if ($ScriptVal->"name" = "global-config") do={ - :set ReloadGlobalConfig true; - } - :if ($ScriptVal->"name" = "global-functions" || $ScriptVal->"name" ~ ("^mod/.")) do={ - :set ReloadGlobalFunctions true; - } - } else={ - $LogPrintExit2 warning $0 ("Syntax validation for script '" . $ScriptVal->"name" . \ - "' failed! Ignoring!") false; - } - } else={ - $LogPrintExit2 warning $0 ("The script '" . $ScriptVal->"name" . "' requires RouterOS " . \ - $Required . ", which is not met by your installation. Ignoring!") false; - } - } else={ - $LogPrintExit2 warning $0 ("Looks like new script '" . $ScriptVal->"name" . \ - "' is not valid (missing shebang). Ignoring!") false; - } - } else={ - $LogPrintExit2 debug $0 ("Script '" . $ScriptVal->"name" . "' did not change.") false; - } - } else={ - $LogPrintExit2 debug $0 ("No update for script '" . $ScriptVal->"name" . "'.") false; - } - } - - :if ($ReloadGlobalFunctions = true) do={ - $LogPrintExit2 info $0 ("Reloading global functions.") false; - :do { - /system/script/run global-functions; - } on-error={ - $LogPrintExit2 error $0 ("Reloading global functions failed!") false; - } - } - - :if ($ReloadGlobalConfig = true) do={ - $LogPrintExit2 info $0 ("Reloading global configuration.") false; - :do { - /system/script/run global-config; - } on-error={ - $LogPrintExit2 error $0 ("Reloading global configuration failed!" . \ - " Syntax error or missing overlay?") false; - } - } - - :if ($ExpectedConfigVersionBefore > $ExpectedConfigVersion) do={ - $LogPrintExit2 warning $0 ("The configuration version decreased from " . \ - $ExpectedConfigVersionBefore . " to " . $ExpectedConfigVersion . \ - ". Installed an older version?") false; - } - - :if ($ExpectedConfigVersionBefore < $ExpectedConfigVersion) do={ - :global GlobalConfigChanges; - :global GlobalConfigMigration; - :local ChangeLogCode; - - :do { - :local Url ($ScriptUpdatesBaseUrl . "news-and-changes.rsc" . $ScriptUpdatesUrlSuffix); - $LogPrintExit2 debug $0 ("Fetching news, changes and migration: " . $Url) false; - :local Result [ /tool/fetch check-certificate=yes-without-crl $Url output=user as-value ]; - :if ($Result->"status" = "finished") do={ - :set ChangeLogCode ($Result->"data"); - } - } on-error={ - $LogPrintExit2 warning $0 ("Failed fetching news, changes and migration!") false; - } - - :if ([ :len $ChangeLogCode ] > 0) do={ - :if ([ $ValidateSyntax $ChangeLogCode ] = true) do={ - :do { - [ :parse $ChangeLogCode ]; - } on-error={ - $LogPrintExit2 warning $0 ("The changelog failed to run!") false; - } - } else={ - $LogPrintExit2 warning $0 ("The changelog failed syntax validation!") false; - } - } - - :if ([ :len $GlobalConfigMigration ] > 0) do={ - :for I from=($ExpectedConfigVersionBefore + 1) to=$ExpectedConfigVersion do={ - :local Migration ($GlobalConfigMigration->[ :tostr $I ]); - :if ([ :typeof $Migration ] = "str") do={ - :if ([ $ValidateSyntax $Migration ] = true) do={ - $LogPrintExit2 info $0 ("Applying migration for change " . $I . ": " . $Migration) false; - :do { - [ :parse $Migration ]; - } on-error={ - $LogPrintExit2 warning $0 ("Migration code for change " . $I . " failed to run!") false; - } - } else={ - $LogPrintExit2 warning $0 ("Migration code for change " . $I . " failed syntax validation!") false; - } - } - } - } - - :local NotificationMessage ("The configuration version on " . $Identity . " increased " . \ - "to " . $ExpectedConfigVersion . ", current configuration may need modification. " . \ - "Please review and update global-config-overlay, then re-run global-config."); - $LogPrintExit2 info $0 ($NotificationMessage) false; - - :if ([ :len $GlobalConfigChanges ] > 0) do={ - :set NotificationMessage ($NotificationMessage . "\n\nChanges:"); - :for I from=($ExpectedConfigVersionBefore + 1) to=$ExpectedConfigVersion do={ - :local Change ($GlobalConfigChanges->[ :tostr $I ]); - :set NotificationMessage ($NotificationMessage . "\n " . \ - [ $IfThenElse ($NotificationsWithSymbols = true) ("\E2\97\8F") "*" ] . " " . $Change); - $LogPrintExit2 info $0 ("Change " . $I . ": " . $Change) false; - } - } else={ - :set NotificationMessage ($NotificationMessage . "\n\nNews and changes are not available."); - } - - :if ($NoNewsAndChangesNotification != true) do={ - :local Link; - :if ($IDonate != true) do={ - :set NotificationMessage ($NotificationMessage . \ - "\n\n==== donation hint ====\n" . \ - "This project is developed in private spare time and usage is " . \ - "free of charge for you. If you like the scripts and think this is " . \ - "of value for you or your business please consider a donation."); - :set Link "https://git.eworm.de/cgit/routeros-scripts/about/#donate"; - } - - $SendNotification2 ({ origin=$0; \ - subject=([ $SymbolForNotification "pushpin" ] . "News and configuration changes"); \ - message=$NotificationMessage; link=$Link }); - } - - :set GlobalConfigChanges; - :set GlobalConfigMigration; - } -} diff --git a/global-functions.rsc b/global-functions.rsc index 5c6e3c5..8ae7bb8 100644 --- a/global-functions.rsc +++ b/global-functions.rsc @@ -1,35 +1,43 @@ #!rsc by RouterOS # RouterOS script: global-functions -# Copyright (c) 2013-2023 Christian Hesse +# Copyright (c) 2013-2025 Christian Hesse # Michael Gisbers -# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md +# https://rsc.eworm.de/COPYING.md # -# requires RouterOS, version=7.10beta5 +# requires RouterOS, version=7.15 +# requires device-mode, fetch, scheduler # # global functions -# https://git.eworm.de/cgit/routeros-scripts/about/ +# https://rsc.eworm.de/ -:local 0 "global-functions"; +:local ScriptName [ :jobname ]; -# expected configuration version -:global ExpectedConfigVersion 113; +# Git commit id & info, expected configuration version +:global CommitId "unknown"; +:global CommitInfo "unknown"; +:global ExpectedConfigVersion 135; # global variables not to be changed by user :global GlobalFunctionsReady false; -:global FetchUserAgent ("User-Agent: Mikrotik/" . [ /system/resource/get version ] . " Fetch"); :global Identity [ /system/identity/get name ]; # global functions +:global AlignRight; :global CertificateAvailable; :global CertificateDownload; :global CertificateNameByCN; +:global CharacterMultiply; :global CharacterReplace; :global CleanFilePath; +:global CleanName; :global DeviceInfo; :global Dos2Unix; :global DownloadPackage; :global EitherOr; :global EscapeForRegEx; +:global ExitError; +:global FetchHuge; +:global FetchUserAgentStr; :global FormatLine; :global FormatMultiLines; :global GetMacVendor; @@ -38,23 +46,28 @@ :global GetRandomNumber; :global Grep; :global HexToNum; +:global HumanReadableNum; :global IfThenElse; :global IsDefaultRouteReachable; :global IsDNSResolving; :global IsFullyConnected; :global IsMacLocallyAdministered; :global IsTimeSync; -:global LogPrintExit2; +:global LogPrint; :global LogPrintOnce; +:global LogPrintVerbose; +:global MAX; +:global MIN; :global MkDir; :global NotificationFunctions; :global ParseDate; -:global ParseJson; :global ParseKeyValueStore; :global PrettyPrint; +:global ProtocolStrip; :global RandomDelay; -:global Read; :global RequiredRouterOS; +:global RmDir; +:global RmFile; :global ScriptFromTerminal; :global ScriptInstallUpdate; :global ScriptLock; @@ -72,23 +85,42 @@ :global WaitFullyConnected; :global WaitTimeSync; +# align string to the right +:set AlignRight do={ + :local Input [ :tostr $1 ]; + :local Len [ :tonum $2 ]; + + :global CharacterMultiply; + :global EitherOr; + + :set Len [ $EitherOr $Len 8 ]; + :local Spaces [ $CharacterMultiply " " $Len ]; + + :return ([ :pick $Spaces 0 ($Len - [ :len $Input ]) ] . $Input); +} + # check and download required certificate :set CertificateAvailable do={ :local CommonName [ :tostr $1 ]; :global CertificateDownload; - :global LogPrintExit2; + :global LogPrint; :global ParseKeyValueStore; :if ([ /system/resource/get free-hdd-space ] < 8388608 && \ [ /certificate/settings/get crl-download ] = true && \ [ /certificate/settings/get crl-store ] = "system") do={ - $LogPrintExit2 warning $0 ("This system has low free flash space but " . \ - "is configured to download certificate CRLs to system!") false; + $LogPrint warning $0 ("This system has low free flash space but " . \ + "is configured to download certificate CRLs to system!"); + } + + :if ([ :len $CommonName ] = 0) do={ + $LogPrint warning $0 ("No CommonName given!"); + :return false; } :if ([ :len [ /certificate/find where common-name=$CommonName ] ] = 0) do={ - $LogPrintExit2 info $0 ("Certificate with CommonName \"" . $CommonName . "\" not available.") false; + $LogPrint info $0 ("Certificate with CommonName '" . $CommonName . "' not available."); :if ([ $CertificateDownload $CommonName ] = false) do={ :return false; } @@ -97,8 +129,8 @@ :local CertVal [ /certificate/get [ find where common-name=$CommonName ] ]; :while (($CertVal->"akid") != "" && ($CertVal->"akid") != ($CertVal->"skid")) do={ :if ([ :len [ /certificate/find where skid=($CertVal->"akid") ] ] = 0) do={ - $LogPrintExit2 info $0 ("Certificate chain for \"" . $CommonName . \ - "\" is incomplete, missing \"" . ([ $ParseKeyValueStore ($CertVal->"issuer") ]->"CN") . "\".") false; + $LogPrint info $0 ("Certificate chain for '" . $CommonName . \ + "' is incomplete, missing '" . ([ $ParseKeyValueStore ($CertVal->"issuer") ]->"CN") . "'."); :if ([ $CertificateDownload $CommonName ] = false) do={ :return false; } @@ -112,36 +144,60 @@ :set CertificateDownload do={ :local CommonName [ :tostr $1 ]; - :global FetchUserAgent; :global ScriptUpdatesBaseUrl; :global ScriptUpdatesUrlSuffix; + :global CertificateAvailable; :global CertificateNameByCN; - :global LogPrintExit2; - :global UrlEncode; + :global CleanName; + :global FetchUserAgentStr; + :global LogPrint; + :global RmFile; :global WaitForFile; - $LogPrintExit2 info $0 ("Downloading and importing certificate with " . \ - "CommonName \"" . $CommonName . "\".") false; + $LogPrint info $0 ("Downloading and importing certificate with " . \ + "CommonName '" . $CommonName . "'."); + :local FileName ([ $CleanName $CommonName ] . ".pem"); :do { - :local LocalFileName ($CommonName . ".pem"); - :local UrlFileName ([ $UrlEncode $CommonName ] . ".pem"); - /tool/fetch check-certificate=yes-without-crl http-header-field=({ $FetchUserAgent }) \ - ($ScriptUpdatesBaseUrl . "certs/" . $UrlFileName . $ScriptUpdatesUrlSuffix) \ - dst-path=$LocalFileName as-value; - $WaitForFile $LocalFileName; - /certificate/import file-name=$LocalFileName passphrase="" as-value; - /file/remove $LocalFileName; - - :foreach Cert in=[ /certificate/find where name~("^" . $LocalFileName . "_[0-9]+\$") ] do={ - $CertificateNameByCN [ /certificate/get $Cert common-name ]; - } + /tool/fetch check-certificate=yes-without-crl http-header-field=({ [ $FetchUserAgentStr $0 ] }) \ + ($ScriptUpdatesBaseUrl . "certs/" . $FileName . $ScriptUpdatesUrlSuffix) \ + dst-path=$FileName as-value; + $WaitForFile $FileName; } on-error={ - $LogPrintExit2 warning $0 ("Failed importing certificate with " . \ - "CommonName \"" . $CommonName . "\"!") false; + $LogPrint warning $0 ("Failed downloading certificate with CommonName '" . $CommonName . \ + "' from repository! Trying fallback to mkcert.org..."); + :do { + :if ([ $CertificateAvailable "ISRG Root X1" ] = false) do={ + $LogPrint error $0 ("Downloading required certificate failed."); + :return false; + } + /tool/fetch check-certificate=yes-without-crl http-header-field=({ [ $FetchUserAgentStr $0 ] }) \ + "https://mkcert.org/generate/" http-data=[ :serialize to=json ({ $CommonName }) ] \ + dst-path=$FileName as-value; + $WaitForFile $FileName; + :if ([ /file/get $FileName size ] = 0) do={ + $RmFile $FileName; + :error false; + } + } on-error={ + $LogPrint warning $0 ("Failed downloading certificate with CommonName '" . $CommonName . "'!"); + :return false; + } + } + + /certificate/import file-name=$FileName passphrase="" as-value; + :delay 1s; + $RmFile $FileName; + + :if ([ :len [ /certificate/find where common-name=$CommonName ] ] = 0) do={ + /certificate/remove [ find where name~("^" . $FileName . "_[0-9]+\$") ]; + $LogPrint warning $0 ("Certificate with CommonName '" . $CommonName . "' still unavailable!"); :return false; } - :delay 1s; + + :foreach Cert in=[ /certificate/find where name~("^" . $FileName . "_[0-9]+\$") ] do={ + $CertificateNameByCN [ /certificate/get $Cert common-name ]; + } :return true; } @@ -149,11 +205,19 @@ :set CertificateNameByCN do={ :local CommonName [ :tostr $1 ]; - :global CharacterReplace; + :global CleanName; :local Cert [ /certificate/find where common-name=$CommonName ]; - /certificate/set $Cert \ - name=[ $CharacterReplace [ $CharacterReplace [ $CharacterReplace $CommonName "'" "-" ] " " "-" ] "---" "-" ]; + /certificate/set $Cert name=[ $CleanName $CommonName ]; +} + +# multiply given character(s) +:set CharacterMultiply do={ + :local Return ""; + :for I from=1 to=$2 do={ + :set Return ($Return . $1); + } + :return $Return; } # character replace @@ -195,34 +259,70 @@ :return $Path; } +# clean name for DNS, file and more +:set CleanName do={ + :local Input [ :tostr $1 ]; + + :local Return ""; + + :for I from=0 to=([ :len $Input ] - 1) do={ + :local Char [ :pick $Input $I ]; + :if ([ :typeof [ find "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" $Char ] ] = "nil") do={ + :do { + :if ([ :len $Return ] = 0) do={ + :error true; + } + :if ([ :pick $Return ([ :len $Return ] - 1) ] = "-") do={ + :error true; + } + :set Char "-"; + } on-error={ + :set Char ""; + } + } + :set Return ($Return . $Char); + } + :return $Return; +} + # get readable device info :set DeviceInfo do={ + :global CommitId; + :global CommitInfo; :global ExpectedConfigVersion; :global Identity; :global IfThenElse; :global FormatLine; + :local License [ /system/license/get ]; :local Resource [ /system/resource/get ]; :local RouterBoard; :do { :set RouterBoard [[ :parse "/system/routerboard/get" ]]; } on-error={ } - :local License [ /system/license/get ]; + :local Snmp [ /snmp/get ]; :local Update [ /system/package/update/get ]; :return ( \ [ $FormatLine "Hostname" $Identity ] . "\n" . \ - [ $FormatLine "Board name" ($Resource->"board-name") ] . "\n" . \ - [ $FormatLine "Architecture" ($Resource->"architecture-name") ] . "\n" . \ + [ $IfThenElse ([ :len ($Snmp->"location") ] > 0) \ + ([ $FormatLine "Location" ($Snmp->"location") ] . "\n") ] . \ + [ $IfThenElse ([ :len ($Snmp->"contact") ] > 0) \ + ([ $FormatLine "Contact" ($Snmp->"contact") ] . "\n") ] . \ + "Hardware:\n" . \ + [ $FormatLine " Board" ($Resource->"board-name") ] . "\n" . \ + [ $FormatLine " Arch" ($Resource->"architecture-name") ] . "\n" . \ [ $IfThenElse ($RouterBoard->"routerboard" = true) \ - ([ $FormatLine "Model" ($RouterBoard->"model") ] . \ + ([ $FormatLine " Model" ($RouterBoard->"model") ] . \ [ $IfThenElse ([ :len ($RouterBoard->"revision") ] > 0) \ (" " . $RouterBoard->"revision") ] . "\n" . \ - [ $FormatLine "Serial number" ($RouterBoard->"serial-number") ] . "\n") ] . \ - [ $IfThenElse ([ :len ($License->"level") ] > 0) \ - ([ $FormatLine "License" ($License->"level") ] . "\n") ] . \ + [ $FormatLine " Serial" ($RouterBoard->"serial-number") ] . "\n") ] . \ + [ $IfThenElse ([ :len ($License->"nlevel") ] > 0) \ + ([ $FormatLine " License" ("level " . ($License->"nlevel")) ] . "\n") ] . \ "RouterOS:\n" . \ + [ $IfThenElse ([ :len ($License->"level") ] > 0) \ + ([ $FormatLine " License" ("level " . ($License->"level")) ] . "\n") ] . \ [ $FormatLine " Channel" ($Update->"channel") ] . "\n" . \ [ $FormatLine " Installed" ($Update->"installed-version") ] . "\n" . \ [ $IfThenElse ([ :typeof ($Update->"latest-version") ] != "nothing" && \ @@ -232,16 +332,14 @@ $RouterBoard->"current-firmware" != $RouterBoard->"upgrade-firmware") \ ([ $FormatLine " Firmware" ($RouterBoard->"current-firmware") ] . "\n") ] . \ "RouterOS-Scripts:\n" . \ + [ $IfThenElse ($CommitId != "unknown") \ + ([ $FormatLine " Commit" ($CommitInfo . "/" . [ :pick $CommitId 0 8 ]) ] . "\n") ] . \ [ $FormatLine " Version" $ExpectedConfigVersion ]); } # convert line endings, DOS -> UNIX :set Dos2Unix do={ - :local Input [ :tostr $1 ]; - - :global CharacterReplace; - - :return [ $CharacterReplace $Input ("\r\n") ("\n") ]; + :return [ :tolf [ :tostr $1 ] ]; } # download package from upgrade server @@ -253,8 +351,9 @@ :global CertificateAvailable; :global CleanFilePath; - :global LogPrintExit2; + :global LogPrint; :global MkDir; + :global RmFile; :global WaitForFile; :if ([ :len $PkgName ] = 0) do={ :return false; } @@ -268,22 +367,23 @@ :local PkgDest [ $CleanFilePath ($PkgDir . "/" . $PkgFile) ]; :if ([ $MkDir $PkgDir ] = false) do={ - $LogPrintExit2 warning $0 ("Failed creating directory, not downloading package.") false; + $LogPrint warning $0 ("Failed creating directory, not downloading package."); :return false; } :if ([ :len [ /file/find where name=$PkgDest type="package" ] ] > 0) do={ - $LogPrintExit2 info $0 ("Package file " . $PkgName . " already exists.") false; + $LogPrint info $0 ("Package file " . $PkgName . " already exists."); :return true; } - :if ([ $CertificateAvailable "R3" ] = false) do={ - $LogPrintExit2 error $0 ("Downloading required certificate failed.") true; + :if ([ $CertificateAvailable "ISRG Root X1" ] = false) do={ + $LogPrint error $0 ("Downloading required certificate failed."); + :return false; } :local Url ("https://upgrade.mikrotik.com/routeros/" . $PkgVer . "/" . $PkgFile); - $LogPrintExit2 info $0 ("Downloading package file '" . $PkgName . "'...") false; - $LogPrintExit2 debug $0 ("... from url: " . $Url) false; + $LogPrint info $0 ("Downloading package file '" . $PkgName . "'..."); + $LogPrint debug $0 ("... from url: " . $Url); :local Retry 3; :while ($Retry > 0) do={ :do { @@ -294,14 +394,14 @@ :return true; } } on-error={ - $LogPrintExit2 debug $0 ("Downloading package file failed.") false; + $LogPrint debug $0 ("Downloading package file failed."); } - /file/remove [ find where name=$PkgDest ]; + $RmFile $PkgDest; :set Retry ($Retry - 1); } - $LogPrintExit2 warning $0 ("Downloading package file '" . $PkgName . "' failed.") false; + $LogPrint warning $0 ("Downloading package file '" . $PkgName . "' failed."); :return false; } @@ -315,6 +415,7 @@ :if ([ :typeof $1 ] = "time") do={ :return [ $IfThenElse ($1 > 0s) $1 $2 ]; } + # this works for boolean values, literal ones with parentheses :return [ $IfThenElse ([ :len [ :tostr $1 ] ] > 0) $1 $2 ]; } @@ -340,17 +441,96 @@ :return $Return; } +# simple macro to print error message on unintentional error +:set ExitError do={ + :local ExitOK [ :tostr $1 ]; + :local Name [ :tostr $2 ]; + + :global IfThenElse; + :global LogPrint; + + :if ($ExitOK = "false") do={ + $LogPrint error $Name ([ $IfThenElse ([ :pick $Name 0 1 ] = "\$") \ + "Function" "Script" ] . " '" . $Name . "' exited with error."); + } +} + +# fetch huge data to file, read in chunks +:set FetchHuge do={ + :local ScriptName [ :tostr $1 ]; + :local Url [ :tostr $2 ]; + :local CheckCert [ :tostr $3 ]; + + :global CleanName; + :global FetchUserAgentStr; + :global GetRandom20CharAlNum; + :global IfThenElse; + :global LogPrint; + :global MkDir; + :global RmDir; + :global RmFile; + :global WaitForFile; + + :set CheckCert [ $IfThenElse ($CheckCert = "false") "no" "yes-without-crl" ]; + + :local DirName ("tmpfs/" . [ $CleanName $ScriptName ]); + :if ([ $MkDir $DirName ] = false) do={ + $LogPrint error $0 ("Failed creating directory!"); + :return false; + } + + :local FileName ($DirName . "/" . [ $CleanName $0 ] . "-" . [ $GetRandom20CharAlNum ]); + :do { + /tool/fetch check-certificate=$CheckCert $Url dst-path=$FileName \ + http-header-field=({ [ $FetchUserAgentStr $ScriptName ] }) as-value; + } on-error={ + :if ([ $WaitForFile $FileName 500ms ] = true) do={ + $RmFile $FileName; + } + $LogPrint debug $0 ("Failed downloading from: " . $Url); + $RmDir $DirName; + :return false; + } + $WaitForFile $FileName; + + :local FileSize [ /file/get $FileName size ]; + :local Return ""; + :local VarSize 0; + :while ($VarSize != $FileSize) do={ + :set Return ($Return . ([ /file/read offset=$VarSize chunk-size=32768 file=$FileName as-value ]->"data")); + :set FileSize [ /file/get $FileName size ]; + :set VarSize [ :len $Return ]; + :if ($VarSize > $FileSize) do={ + :delay 100ms; + } + } + $RmDir $DirName; + :return $Return; +} + +# generate user agent string for fetch +:set FetchUserAgentStr do={ + :local Caller [ :tostr $1 ]; + + :local Resource [ /system/resource/get ]; + + :return ("User-Agent: Mikrotik/" . $Resource->"version" . " " . \ + $Resource->"architecture-name" . " " . $Caller . "/Fetch (https://rsc.eworm.de/)"); +} + # format a line for output :set FormatLine do={ :local Key [ :tostr $1 ]; :local Value [ :tostr $2 ]; :local Indent [ :tonum $3 ]; - :local Spaces " "; + :local Spaces; :local Return ""; + :global CharacterMultiply; :global EitherOr; :set Indent [ $EitherOr $Indent 16 ]; + :local Spaces [ $CharacterMultiply " " $Indent ]; :if ([ :len $Key ] > 0) do={ :set Return ($Key . ":"); } :if ([ :len $Key ] > ($Indent - 2)) do={ @@ -385,15 +565,16 @@ :global CertificateAvailable; :global IsMacLocallyAdministered; - :global LogPrintExit2; + :global LogPrint; :if ([ $IsMacLocallyAdministered $Mac ] = true) do={ :return "locally administered"; } :do { - :if ([ $CertificateAvailable "R3" ] = false) do={ - $LogPrintExit2 warning $0 ("Downloading required certificate failed.") true; + :if ([ $CertificateAvailable "GTS Root R4" ] = false) do={ + $LogPrint warning $0 ("Downloading required certificate failed."); + :error false; } :local Vendor ([ /tool/fetch check-certificate=yes-without-crl \ ("https://api.macvendors.com/" . [ :pick $Mac 0 8 ]) output=user as-value ]->"data"); @@ -402,9 +583,9 @@ :do { /tool/fetch check-certificate=yes-without-crl ("https://api.macvendors.com/") \ output=none as-value; - $LogPrintExit2 debug $0 ("The mac vendor is not known in database.") false; + $LogPrint debug $0 ("The mac vendor is not known in database."); } on-error={ - $LogPrintExit2 warning $0 ("Failed getting mac vendor.") false; + $LogPrint warning $0 ("Failed getting mac vendor."); } :return "unknown vendor"; } @@ -454,16 +635,48 @@ # convert from hex (string) to num :set HexToNum do={ :local Input [ :tostr $1 ]; - :local Hex "0123456789abcdef0123456789ABCDEF"; - :local Multi 1; - :local Return 0; - :for I from=([ :len $Input ] - 1) to=0 do={ - :set Return ($Return + (([ :find $Hex [ :pick $Input $I ] ] % 16) * $Multi)); - :set Multi ($Multi * 16); + :global HexToNum; + + :if ([ :pick $Input 0 ] = "*") do={ + :return [ $HexToNum [ :pick $Input 1 [ :len $Input ] ] ]; } - :return $Return; + :return [ :tonum ("0x" . $Input) ]; +} + +# return human readable number +:set HumanReadableNum do={ + :local Input [ :tonum $1 ]; + :local Base [ :tonum $2 ]; + + :global EitherOr; + :global IfThenElse; + + :local Prefix "kMGTPE"; + :local Pow 1; + + :set Base [ $EitherOr $Base 1024 ]; + :local Bin [ $IfThenElse ($Base = 1024) "i" "" ]; + + :if ($Input < $Base) do={ + :return $Input; + } + + :for I from=0 to=[ :len $Prefix ] do={ + :set Pow ($Pow * $Base); + :if ($Input / $Base < $Pow) do={ + :set Prefix [ :pick $Prefix $I ]; + :local Tmp1 ($Input * 100 / $Pow); + :local Tmp2 ($Tmp1 / 100); + :if ($Tmp2 >= 100) do={ + :return ($Tmp2 . $Prefix . $Bin); + } + :return ($Tmp2 . "." . \ + [ :pick $Tmp1 [ :len $Tmp2 ] ([ :len $Tmp1 ] - [ :len $Tmp2 ] + 1) ] . \ + $Prefix . $Bin); + } + } } # mimic conditional/ternary operator (condition ? consequent : alternative) @@ -484,8 +697,6 @@ # check if DNS is resolving :set IsDNSResolving do={ - :global CharacterReplace; - :do { :resolve "low-ttl.eworm.de"; } on-error={ @@ -525,7 +736,7 @@ :global IsTimeSyncCached; :global IsTimeSyncResetNtp; - :global LogPrintExit2; + :global LogPrintOnce; :if ($IsTimeSyncCached = true) do={ :return true; @@ -537,14 +748,15 @@ :return true; } - :if ([ :typeof $IsTimeSyncResetNtp ] = "nothing") do={ - :set IsTimeSyncResetNtp 0s; - } :local Uptime [ /system/resource/get uptime ]; + :if ([ :typeof $IsTimeSyncResetNtp ] = "nothing") do={ + :set IsTimeSyncResetNtp $Uptime; + } :if ($Uptime - $IsTimeSyncResetNtp < 3m) do={ :return false; } + $LogPrintOnce warning $0 ("The ntp client is configured, but did not sync."); :set IsTimeSyncResetNtp $Uptime; /system/ntp/client/set enabled=no; :delay 20ms; @@ -554,7 +766,7 @@ :if ([ /system/license/get ]->"level" = "free" || \ [ /system/resource/get ]->"board-name" = "x86") do={ - $LogPrintExit2 debug $0 ("No ntp client configured, relying on RTC for CHR free license and x86.") false; + $LogPrintOnce debug $0 ("No ntp client configured, relying on RTC for CHR free license and x86."); :return true; } @@ -566,16 +778,15 @@ :return false; } - $LogPrintExit2 debug $0 ("No time source configured! Returning gracefully...") false; + $LogPrintOnce debug $0 ("No time source configured! Returning gracefully..."); :return true; } -# log and print with same text, optionally exit -:set LogPrintExit2 do={ +# log and print with same text +:set LogPrint do={ :local Severity [ :tostr $1 ]; :local Name [ :tostr $2 ]; :local Message [ :tostr $3 ]; - :local Exit [ :tostr $4 ]; :global PrintDebug; :global PrintDebugOverride; @@ -608,10 +819,6 @@ :if ($Severity != "debug" || $Debug = true) do={ :put ([ $PrintSeverity $Severity ] . ": " . $Message); } - - :if ($Exit = "true") do={ - :error ("Hard error to exit."); - } } # log and print, once until reboot @@ -620,7 +827,7 @@ :local Name [ :tostr $2 ]; :local Message [ :tostr $3 ]; - :global LogPrintExit2; + :global LogPrint; :global LogPrintOnceMessages; @@ -629,38 +836,63 @@ } :if ($LogPrintOnceMessages->$Message = 1) do={ - :return true; + :return false; + } + + :if ([ :len [ /log/find where message=($Name . ": " . $Message) ] ] > 0) do={ + $LogPrint warning $0 \ + ("The message is already in log, scripting subsystem may have crashed before!"); } :set ($LogPrintOnceMessages->$Message) 1; - $LogPrintExit2 $Severity $Name $Message false; + $LogPrint $Severity $Name $Message; + :return true; +} + +# The function $LogPrintVerbose is declared, but has no code, intentionally. +# https://rsc.eworm.de/DEBUG.md#verbose-output + +# get max value +:set MAX do={ + :if ($1 > $2) do={ :return $1; } + :return $2; +} + +# get min value +:set MIN do={ + :if ($1 < $2) do={ :return $1; } + :return $2; } # create directory :set MkDir do={ :local Path [ :tostr $1 ]; - :global CharacterReplace; :global CleanFilePath; - :global GetRandom20CharAlNum; - :global LogPrintExit2; + :global LogPrint; + :global RmDir; :global WaitForFile; :local MkTmpfs do={ - :global LogPrintExit2; + :global LogPrint; :global WaitForFile; - :if ([ :len [ /disk/find where slot=tmpfs type=tmpfs ] ] = 1) do={ + :local TmpFs [ /disk/find where slot=tmpfs type=tmpfs ]; + :if ([ :len $TmpFs ] = 1) do={ + :if ([ /disk/get $TmpFs disabled ] = true) do={ + $LogPrint info $0 ("The tmpfs is disabled, enabling."); + /disk/enable $TmpFs; + } :return true; } - $LogPrintExit2 info $0 ("Creating disk of type tmpfs.") false; - /file/remove [ find where name="tmpfs" type="directory" ]; + $LogPrint info $0 ("Creating disk of type tmpfs."); + $RmDir "tmpfs"; :do { /disk/add slot=tmpfs type=tmpfs tmpfs-max-size=([ /system/resource/get total-memory ] / 3); $WaitForFile "tmpfs"; } on-error={ - $LogPrintExit2 warning $0 ("Creating disk of type tmpfs failed!") false; + $LogPrint warning $0 ("Creating disk of type tmpfs failed!"); :return false; } :return true; @@ -672,7 +904,10 @@ :return true; } + $LogPrint debug $0 ("Making directory: " . $Path); + :if ([ :len [ /file/find where name=$Path type="directory" ] ] = 1) do={ + $LogPrint debug $0 ("... which already exists."); :return true; } @@ -683,12 +918,10 @@ } :do { - :local File ($Path . "/file"); - /file/add name=$File; - $WaitForFile $File; - /file/remove $File; + /file/add type="directory" name=$Path; + $WaitForFile $Path; } on-error={ - $LogPrintExit2 warning $0 ("Making directory '" . $Path . "' failed!") false; + $LogPrint warning $0 ("Making directory '" . $Path . "' failed!"); :return false; } @@ -709,77 +942,27 @@ "day"=[ :tonum [ :pick $Date 8 10 ] ] }); } -# parse JSON into array -# Warning: This is not a complete parser! -:set ParseJson do={ - :local Input [ :tostr $1 ]; - - :local InLen; - :local Return ({}); - :local Skip 0; - - :if ([ :pick $Input 0 ] = "{") do={ - :set Input [ :pick $Input 1 ([ :len $Input ] - 1) ]; - } - :set Input [ :toarray $Input ]; - :set InLen [ :len $Input ]; - - :for I from=0 to=$InLen do={ - :if ($Skip > 0 || $Input->$I = "\n" || $Input->$I = "\r\n") do={ - :if ($Skip > 0) do={ - :set $Skip ($Skip - 1); - } - } else={ - :local Done false; - :local Key ($Input->$I); - :local Val1 ($Input->($I + 1)); - :local Val2 ($Input->($I + 2)); - :if ($Val1 = ":") do={ - :set Skip 2; - :set ($Return->$Key) $Val2; - :set Done true; - } - :if ($Done = false && $Val1 = ":[") do={ - :local Last false; - :set Skip 1; - :set ($Return->$Key) ({}); - :do { - :set Skip ($Skip + 1); - :local ValX ($Input->($I + $Skip)); - :if ([ :pick $ValX ([ :len $ValX ] - 1) ] = "]") do={ - :set Last true; - :set ValX [ :pick $ValX 0 ([ :len $ValX ] - 1) ]; - } - :set ($Return->$Key) (($Return->$Key), $ValX); - } while=($Last = false && $I + $Skip < $InLen); - :set Done true; - } - :if ($Done = false && $Val1 = ":[]") do={ - :set Skip 1; - :set ($Return->$Key) ({}); - :set Done true; - } - :if ($Done = false) do={ - :set Skip 1; - :set ($Return->$Key) [ :pick $Val1 1 [ :len $Val1 ] ]; - } - } - } - - :return $Return; -} - # parse key value store :set ParseKeyValueStore do={ :local Source $1; + + :if ([ :pick $Source 0 1 ] = "{") do={ + :do { + :return [ :deserialize from=json $Source ]; + } on-error={ } + } + :if ([ :typeof $Source ] != "array") do={ :set Source [ :tostr $1 ]; } :local Result ({}); :foreach KeyValue in=[ :toarray $Source ] do={ :if ([ :find $KeyValue "=" ]) do={ - :set ($Result->[ :pick $KeyValue 0 [ :find $KeyValue "=" ] ]) \ - [ :pick $KeyValue ([ :find $KeyValue "=" ] + 1) [ :len $KeyValue ] ]; + :local Key [ :pick $KeyValue 0 [ :find $KeyValue "=" ] ]; + :local Value [ :pick $KeyValue ([ :find $KeyValue "=" ] + 1) [ :len $KeyValue ] ]; + :if ($Value="true") do={ :set Value true; } + :if ($Value="false") do={ :set Value false; } + :set ($Result->$Key) $Value; } else={ :set ($Result->$KeyValue) true; } @@ -789,24 +972,34 @@ # print lines with trailing carriage return :set PrettyPrint do={ + :put [ :tocrlf [ :tostr $1 ] ]; +} + +# strip protocol from from url string +:set ProtocolStrip do={ :local Input [ :tostr $1 ]; - :global Unix2Dos; - - :put [ $Unix2Dos $Input ]; + :local Pos [ :find $Input "://" ]; + :if ([ :typeof $Pos ] = "nil") do={ + :return $Input; + } + :return [ :pick $Input ($Pos + 3) [ :len $Input ] ]; } # delay a random amount of seconds :set RandomDelay do={ + :local Time [ :tonum $1 ]; + :local Unit [ :tostr $2 ]; + :global EitherOr; :global GetRandomNumber; + :global MAX; - :delay ([ $GetRandomNumber $1 ] . [ $EitherOr $2 "s" ]); -} + :if ($Time = 0) do={ + :return false; + } -# read input from user -:set Read do={ - :return; + :delay ([ $MAX 10 [ $GetRandomNumber ([ :tonsec [ :totime ($Time . [ $EitherOr $Unit "s" ]) ] ] / 1000000) ] ] . "ms"); } # check for required RouterOS version @@ -816,29 +1009,90 @@ :local Warn [ :tostr $3 ]; :global IfThenElse; - :global LogPrintExit2; + :global LogPrint; :global VersionToNum; :if (!($Required ~ "^\\d+\\.\\d+((alpha|beta|rc|\\.)\\d+|)\$")) do={ - $LogPrintExit2 error $0 ("No valid RouterOS version: " . $Required) false; + $LogPrint error $0 ("No valid RouterOS version: " . $Required); :return false; } :if ([ $VersionToNum $Required ] > [ $VersionToNum [ /system/package/update/get installed-version ] ]) do={ :if ($Warn = "true") do={ - $LogPrintExit2 warning $0 ("This " . [ $IfThenElse ([ :pick $Caller 0 ] = ("\$")) "function" "script" ] . \ - " '" . $Caller . "' (at least specific functionality) requires RouterOS " . $Required . ". Please update!") false; + $LogPrint warning $0 ("This " . [ $IfThenElse ([ :pick $Caller 0 ] = ("\$")) "function" "script" ] . \ + " '" . $Caller . "' (at least specific functionality) requires RouterOS " . $Required . ". Please update!"); } :return false; } :return true; } +# remove directory +:set RmDir do={ + :local DirName [ :tostr $1 ]; + + :global LogPrint; + + $LogPrint debug $0 ("Removing directory: ". $DirName); + + :if ([ :len [ /file/find where name=$DirName type!=directory ] ] > 0) do={ + $LogPrint error $0 ("Directory '" . $DirName . "' is not a directory."); + :return false; + } + + :local Dir [ /file/find where name=$DirName type=directory ]; + :if ([ :len $Dir ] = 0) do={ + $LogPrint debug $0 ("... which does not exist."); + :return true; + } + + :do { + /file/remove $Dir; + } on-error={ + $LogPrint error $0 ("Removing directory '" . $DirName . "' (" . $Dir . ") failed."); + :return false; + } + :return true; +} + +# remove file +:set RmFile do={ + :local FileName [ :tostr $1 ]; + + :global LogPrint; + + $LogPrint debug $0 ("Removing file: ". $FileName); + + :if ([ :len [ /file/find where name=$FileName (type=directory or type=disk) ] ] > 0) do={ + $LogPrint error $0 ("File '" . $FileName . "' is not a file."); + :return false; + } + + :local File [ /file/find where name=$FileName !(type=directory or type=disk) ]; + :if ([ :len $File ] = 0) do={ + $LogPrint debug $0 ("... which does not exist."); + :return true; + } + + :do { + /file/remove $File; + } on-error={ + $LogPrint error $0 ("Removing file '" . $FileName . "' (" . $File . ") failed."); + :return false; + } + :return true; +} + # check if script is run from terminal :set ScriptFromTerminal do={ :local Script [ :tostr $1 ]; - :global LogPrintExit2; + :global LogPrint; + :global ScriptLock; + + :if ([ $ScriptLock $Script ] = false) do={ + :return false; + } :foreach Job in=[ /system/script/job/find where script=$Script ] do={ :set Job [ /system/script/job/get $Job ]; @@ -846,53 +1100,67 @@ :set Job [ /system/script/job/get [ find where .id=($Job->"parent") ] ]; } :if (($Job->"type") = "login") do={ - $LogPrintExit2 debug $0 ("Script " . $Script . " started from terminal.") false; + $LogPrint debug $0 ("Script " . $Script . " started from terminal."); :return true; } } - $LogPrintExit2 debug $0 ("Script " . $Script . " NOT started from terminal.") false; + $LogPrint debug $0 ("Script " . $Script . " NOT started from terminal."); :return false; } # install new scripts, update existing scripts -:set ScriptInstallUpdate do={ +:set ScriptInstallUpdate do={ :do { :local Scripts [ :toarray $1 ]; :local NewComment [ :tostr $2 ]; + :global CommitId; + :global CommitInfo; :global ExpectedConfigVersion; - :global FetchUserAgent; :global Identity; :global IDonate; :global NoNewsAndChangesNotification; :global ScriptUpdatesBaseUrl; + :global ScriptUpdatesCRLF; :global ScriptUpdatesUrlSuffix; :global CertificateAvailable; :global EitherOr; + :global FetchUserAgentStr; :global Grep; :global IfThenElse; - :global LogPrintExit2; + :global LogPrint; + :global LogPrintOnce; :global ParseKeyValueStore; :global RequiredRouterOS; :global SendNotification2; :global SymbolForNotification; :global ValidateSyntax; - :if ([ $CertificateAvailable "E1" ] = false) do={ - $LogPrintExit2 warning $0 ("Downloading certificate failed, trying without.") false; + :if ([ $CertificateAvailable "ISRG Root X2" ] = 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={ - $LogPrintExit2 info $0 ("Adding new script: " . $Script) false; + $LogPrint info $0 ("Adding new script: " . $Script); /system/script/add name=$Script owner=$Script source="#!rsc by RouterOS\n" comment=$NewComment; } } + :local CommitIdBefore $CommitId; :local ExpectedConfigVersionBefore $ExpectedConfigVersion; :local ReloadGlobalFunctions false; :local ReloadGlobalConfig false; + :local DeviceMode [ /system/device-mode/get ]; + + :local CheckSums ({}); + :do { + :local Url ($ScriptUpdatesBaseUrl . "checksums.json" . $ScriptUpdatesUrlSuffix); + $LogPrint debug $0 ("Fetching checksums from url: " . $Url); + :set CheckSums [ :deserialize from=json ([ /tool/fetch check-certificate=yes-without-crl \ + http-header-field=({ [ $FetchUserAgentStr $0 ] }) $Url output=user as-value ]->"data") ]; + } on-error={ } :foreach Script in=[ /system/script/find where source~"^#!rsc by RouterOS\r?\n" ] do={ :local ScriptVal [ /system/script/get $Script ]; @@ -902,90 +1170,126 @@ :foreach Scheduler in=[ /system/scheduler/find where on-event~("\\b" . $ScriptVal->"name" . "\\b") ] do={ :local SchedulerVal [ /system/scheduler/get $Scheduler ]; :if ($ScriptVal->"policy" != $SchedulerVal->"policy") do={ - $LogPrintExit2 warning $0 ("Policies differ for script '" . $ScriptVal->"name" . \ - "' and its scheduler '" . $SchedulerVal->"name" . "'!") false; + $LogPrint warning $0 ("Policies differ for script '" . $ScriptVal->"name" . \ + "' and its scheduler '" . $SchedulerVal->"name" . "'!"); } } - :if (!($ScriptInfo->"ignore" = true)) do={ + :do { + :if ($ScriptInfo->"ignore" = true) do={ + $LogPrint debug $0 ("Ignoring script '" . $ScriptVal->"name" . "', as requested."); + :error true; + } + + :local CheckSum ($CheckSums->($ScriptVal->"name")); + :if ([ :len ($ScriptInfo->"base-url") ] = 0 && [ :len ($ScriptInfo->"url-suffix") ] = 0 && \ + [ :convert transform=md5 to=hex [ :tolf ($ScriptVal->"source") ] ] = $CheckSum) do={ + $LogPrint debug $0 ("Checksum for script '" . $ScriptVal->"name" . "' matches, ignoring."); + :error true; + } + :do { :local BaseUrl [ $EitherOr ($ScriptInfo->"base-url") $ScriptUpdatesBaseUrl ]; :local UrlSuffix [ $EitherOr ($ScriptInfo->"url-suffix") $ScriptUpdatesUrlSuffix ]; :local Url ($BaseUrl . $ScriptVal->"name" . ".rsc" . $UrlSuffix); - $LogPrintExit2 debug $0 ("Fetching script '" . $ScriptVal->"name" . "' from url: " . $Url) false; + $LogPrint debug $0 ("Fetching script '" . $ScriptVal->"name" . "' from url: " . $Url); :local Result [ /tool/fetch check-certificate=yes-without-crl \ - http-header-field=({ $FetchUserAgent }) $Url output=user as-value ]; + http-header-field=({ [ $FetchUserAgentStr $0 ] }) $Url output=user as-value ]; :if ($Result->"status" = "finished") do={ - :set SourceNew ($Result->"data"); + :set SourceNew [ :tolf ($Result->"data") ]; } } on-error={ :if ($ScriptVal->"source" = "#!rsc by RouterOS\n") do={ - $LogPrintExit2 warning $0 ("Failed fetching script '" . $ScriptVal->"name" . \ - "', removing dummy. Typo on installation?") false; + $LogPrint warning $0 ("Failed fetching script '" . $ScriptVal->"name" . \ + "', removing dummy. Typo on installation?"); /system/script/remove $Script; } else={ - $LogPrintExit2 warning $0 ("Failed fetching script '" . $ScriptVal->"name" . "'!") false; + $LogPrint warning $0 ("Failed fetching script '" . $ScriptVal->"name" . "'!"); } + :error false; } - } - :if ([ :len $SourceNew ] > 0) do={ - :if ($SourceNew != $ScriptVal->"source") do={ - :if ([ :pick $SourceNew 0 18 ] = "#!rsc by RouterOS\n") do={ - :local Required ([ $ParseKeyValueStore [ $Grep $SourceNew ("\23 requires RouterOS, ") ] ]->"version"); - :if ([ $RequiredRouterOS $0 [ $EitherOr $Required "0.0" ] false ] = true) do={ - :if ([ $ValidateSyntax $SourceNew ] = true) do={ - $LogPrintExit2 info $0 ("Updating script: " . $ScriptVal->"name") false; - /system/script/set owner=($ScriptVal->"name") source=$SourceNew $Script; - :if ($ScriptVal->"name" = "global-config") do={ - :set ReloadGlobalConfig true; - } - :if ($ScriptVal->"name" = "global-functions" || $ScriptVal->"name" ~ ("^mod/.")) do={ - :set ReloadGlobalFunctions true; - } - } else={ - $LogPrintExit2 warning $0 ("Syntax validation for script '" . $ScriptVal->"name" . \ - "' failed! Ignoring!") false; - } - } else={ - $LogPrintExit2 warning $0 ("The script '" . $ScriptVal->"name" . "' requires RouterOS " . \ - $Required . ", which is not met by your installation. Ignoring!") false; - } - } else={ - $LogPrintExit2 warning $0 ("Looks like new script '" . $ScriptVal->"name" . \ - "' is not valid (missing shebang). Ignoring!") false; - } - } else={ - $LogPrintExit2 debug $0 ("Script '" . $ScriptVal->"name" . "' did not change.") false; + :if ([ :len $SourceNew ] = 0) do={ + $LogPrint debug $0 ("No update for script '" . $ScriptVal->"name" . "'."); + :error false; } - } else={ - $LogPrintExit2 debug $0 ("No update for script '" . $ScriptVal->"name" . "'.") false; - } + + :local SourceCRLF [ :tocrlf $SourceNew ]; + :if ($SourceNew = $ScriptVal->"source" || $SourceCRLF = $ScriptVal->"source") do={ + $LogPrint debug $0 ("Script '" . $ScriptVal->"name" . "' did not change."); + :error false; + } + + :if ([ :pick $SourceNew 0 18 ] != "#!rsc by RouterOS\n") do={ + $LogPrint warning $0 ("Looks like new script '" . $ScriptVal->"name" . \ + "' is not valid (missing shebang). Ignoring!"); + :error false; + } + + :local RequiredROS ([ $ParseKeyValueStore [ $Grep $SourceNew ("\23 requires RouterOS, ") ] ]->"version"); + :if ([ $RequiredRouterOS $0 [ $EitherOr $RequiredROS "0.0" ] false ] = false) do={ + $LogPrintOnce warning $0 ("The script '" . $ScriptVal->"name" . "' requires RouterOS " . \ + $RequiredROS . ", which is not met by your installation. Ignoring!"); + :error false; + } + + :local RequiredDM [ $ParseKeyValueStore [ $Grep $SourceNew ("\23 requires device-mode, ") ] ]; + :local MissingDM ({}); + :foreach Feature,Value in=$RequiredDM do={ + :if ([ :typeof ($DeviceMode->$Feature) ] = "bool" && ($DeviceMode->$Feature) = false) do={ + :set MissingDM ($MissingDM, $Feature); + } + } + :if ([ :len $MissingDM ] > 0) do={ + $LogPrintOnce warning $0 ("The script '" . $ScriptVal->"name" . "' requires disabled " . \ + "device-mode features (" . [ :tostr $MissingDM ] . "). Ignoring!"); + :error false; + } + + :if ([ $ValidateSyntax $SourceNew ] = false) do={ + $LogPrint warning $0 ("Syntax validation for script '" . $ScriptVal->"name" . "' failed! Ignoring!"); + :error false; + } + + $LogPrint info $0 ("Updating script: " . $ScriptVal->"name"); + /system/script/set owner=($ScriptVal->"name") \ + source=[ $IfThenElse ($ScriptUpdatesCRLF = true) $SourceCRLF $SourceNew ] $Script; + :if ($ScriptVal->"name" = "global-config") do={ + :set ReloadGlobalConfig true; + } + :if ($ScriptVal->"name" = "global-functions" || $ScriptVal->"name" ~ ("^mod/.")) do={ + :set ReloadGlobalFunctions true; + } + } on-error={ } } :if ($ReloadGlobalFunctions = true) do={ - $LogPrintExit2 info $0 ("Reloading global functions.") false; + $LogPrint info $0 ("Reloading global functions."); :do { /system/script/run global-functions; } on-error={ - $LogPrintExit2 error $0 ("Reloading global functions failed!") false; + $LogPrint error $0 ("Reloading global functions failed!"); } } :if ($ReloadGlobalConfig = true) do={ - $LogPrintExit2 info $0 ("Reloading global configuration.") false; + $LogPrint info $0 ("Reloading global configuration."); :do { /system/script/run global-config; } on-error={ - $LogPrintExit2 error $0 ("Reloading global configuration failed!" . \ - " Syntax error or missing overlay?") false; + $LogPrint error $0 ("Reloading global configuration failed!" . \ + " Syntax error or missing overlay?"); } } + :if ($CommitId != "unknown" && $CommitIdBefore != $CommitId) do={ + $LogPrint info $0 ("Updated to commit: " . $CommitInfo . "/" . [ :pick $CommitId 0 8 ]); + } + :if ($ExpectedConfigVersionBefore > $ExpectedConfigVersion) do={ - $LogPrintExit2 warning $0 ("The configuration version decreased from " . \ + $LogPrint warning $0 ("The configuration version decreased from " . \ $ExpectedConfigVersionBefore . " to " . $ExpectedConfigVersion . \ - ". Installed an older version?") false; + ". Installed an older version?"); } :if ($ExpectedConfigVersionBefore < $ExpectedConfigVersion) do={ @@ -995,14 +1299,14 @@ :do { :local Url ($ScriptUpdatesBaseUrl . "news-and-changes.rsc" . $ScriptUpdatesUrlSuffix); - $LogPrintExit2 debug $0 ("Fetching news, changes and migration: " . $Url) false; + $LogPrint debug $0 ("Fetching news, changes and migration: " . $Url); :local Result [ /tool/fetch check-certificate=yes-without-crl \ - http-header-field=({ $FetchUserAgent }) $Url output=user as-value ]; + http-header-field=({ [ $FetchUserAgentStr $0 ] }) $Url output=user as-value ]; :if ($Result->"status" = "finished") do={ :set ChangeLogCode ($Result->"data"); } } on-error={ - $LogPrintExit2 warning $0 ("Failed fetching news, changes and migration!") false; + $LogPrint warning $0 ("Failed fetching news, changes and migration!"); } :if ([ :len $ChangeLogCode ] > 0) do={ @@ -1010,43 +1314,49 @@ :do { [ :parse $ChangeLogCode ]; } on-error={ - $LogPrintExit2 warning $0 ("The changelog failed to run!") false; + $LogPrint warning $0 ("The changelog failed to run!"); } } else={ - $LogPrintExit2 warning $0 ("The changelog failed syntax validation!") false; + $LogPrint warning $0 ("The changelog failed syntax validation!"); } } :if ([ :len $GlobalConfigMigration ] > 0) do={ :for I from=($ExpectedConfigVersionBefore + 1) to=$ExpectedConfigVersion do={ :local Migration ($GlobalConfigMigration->[ :tostr $I ]); - :if ([ :typeof $Migration ] = "str") do={ - :if ([ $ValidateSyntax $Migration ] = true) do={ - $LogPrintExit2 info $0 ("Applying migration for change " . $I . ": " . $Migration) false; - :do { - [ :parse $Migration ]; - } on-error={ - $LogPrintExit2 warning $0 ("Migration code for change " . $I . " failed to run!") false; - } - } else={ - $LogPrintExit2 warning $0 ("Migration code for change " . $I . " failed syntax validation!") false; + :do { + :if ([ :typeof $Migration ] != "str") do={ + $LogPrint debug $0 ("Migration code for change " . $I . " is not available."); + :error false; } - } + + :if ([ $ValidateSyntax $Migration ] = false) do={ + $LogPrint warning $0 ("Migration code for change " . $I . " failed syntax validation!"); + :error false; + } + + $LogPrint info $0 ("Applying migration for change " . $I . ": " . $Migration); + :do { + [ :parse $Migration ]; + } on-error={ + $LogPrint warning $0 ("Migration code for change " . $I . " failed to run!"); + } + } on-error={ } } } :local NotificationMessage ("The configuration version on " . $Identity . " increased " . \ "to " . $ExpectedConfigVersion . ", current configuration may need modification. " . \ "Please review and update global-config-overlay, then re-run global-config."); - $LogPrintExit2 info $0 ($NotificationMessage) false; + $LogPrint info $0 ($NotificationMessage); :if ([ :len $GlobalConfigChanges ] > 0) do={ :set NotificationMessage ($NotificationMessage . "\n\nChanges:"); :for I from=($ExpectedConfigVersionBefore + 1) to=$ExpectedConfigVersion do={ :local Change ($GlobalConfigChanges->[ :tostr $I ]); :set NotificationMessage ($NotificationMessage . "\n " . \ - [ $SymbolForNotification "pushpin" "* " ] . $Change); - $LogPrintExit2 info $0 ("Change " . $I . ": " . $Change) false; + [ $SymbolForNotification "pushpin" "*" ] . $Change); + $LogPrint info $0 ("Change " . $I . ": " . $Change); } } else={ :set NotificationMessage ($NotificationMessage . "\n\nNews and changes are not available."); @@ -1060,7 +1370,7 @@ "This project is developed in private spare time and usage is " . \ "free of charge for you. If you like the scripts and think this is " . \ "of value for you or your business please consider a donation."); - :set Link "https://git.eworm.de/cgit/routeros-scripts/about/#donate"; + :set Link "https://rsc.eworm.de/#donate"; } $SendNotification2 ({ origin=$0; \ @@ -1071,17 +1381,18 @@ :set GlobalConfigChanges; :set GlobalConfigMigration; } -} +} on-error={ + :global ExitError; $ExitError false $0; +} } # lock script against multiple invocation :set ScriptLock do={ :local Script [ :tostr $1 ]; - :local DoReturn $2; - :local WaitMax ([ :tonum $3 ] * 10); + :local WaitMax ([ :tonum $3 ] * 10); :global GetRandom20CharAlNum; :global IfThenElse; - :global LogPrintExit2; + :global LogPrint; :global ScriptLockOrder; :if ([ :typeof $ScriptLockOrder ] = "nothing") do={ @@ -1167,15 +1478,17 @@ } :if ([ :len [ /system/script/find where name=$Script ] ] = 0) do={ - $LogPrintExit2 error $0 ("A script named '" . $Script . "' does not exist!") true; + $LogPrint error $0 ("A script named '" . $Script . "' does not exist!"); + :error false; } :if ([ $JobCount $Script ] = 0) do={ - $LogPrintExit2 error $0 ("No script '" . $Script . "' is running!") true; + $LogPrint error $0 ("No script '" . $Script . "' is running!"); + :error false; } :if ([ $TicketCount $Script ] >= [ $JobCount $Script ]) do={ - $LogPrintExit2 error $0 ("More tickets than running scripts '" . $Script . "', resetting!") false; + $LogPrint error $0 ("More tickets than running scripts '" . $Script . "', resetting!"); :set ($ScriptLockOrder->$Script) ({}); /system/script/job/remove [ find where script=$Script ]; } @@ -1184,29 +1497,34 @@ $AddTicket $Script $MyTicket; :local WaitCount 0; - :while ($WaitMax > $WaitCount && ([ $IsFirstTicket $Script $MyTicket ] = false || [ $TicketCount $Script ] < [ $JobCount $Script ])) do={ + :while ($WaitMax > $WaitCount && \ + ([ $IsFirstTicket $Script $MyTicket ] = false || \ + [ $TicketCount $Script ] < [ $JobCount $Script ])) do={ :set WaitCount ($WaitCount + 1); :delay 100ms; } - :if ([ $IsFirstTicket $Script $MyTicket ] = true && [ $TicketCount $Script ] = [ $JobCount $Script ]) do={ + :if ([ $IsFirstTicket $Script $MyTicket ] = true && \ + [ $TicketCount $Script ] = [ $JobCount $Script ]) do={ $RemoveTicket $Script $MyTicket; $CleanupTickets $Script; - :return false; + :return true; } $RemoveTicket $Script $MyTicket; - $LogPrintExit2 info $0 ("Script '" . $Script . "' started more than once" . [ $IfThenElse ($WaitCount > 0) \ - " and timed out waiting for lock" "" ] . "... Aborting.") [ $IfThenElse ($DoReturn = true) false true ]; - :return true; + $LogPrint debug $0 ("Script '" . $Script . "' started more than once" . \ + [ $IfThenElse ($WaitCount > 0) " and timed out waiting for lock" "" ] . "..."); + :return false; } # send notification via NotificationFunctions - expects at least two string arguments -:set SendNotification do={ +:set SendNotification do={ :do { :global SendNotification2; - $SendNotification2 ({ subject=$1; message=$2; link=$3; silent=$4 }); -} + $SendNotification2 ({ origin=$0; subject=$1; message=$2; link=$3; silent=$4 }); +} on-error={ + :global ExitError; $ExitError false $0; +} } # send notification via NotificationFunctions - expects one array argument :set SendNotification2 do={ @@ -1223,9 +1541,15 @@ # return UTF-8 symbol for unicode name :set SymbolByUnicodeName do={ + :local Name [ :tostr $1 ]; + + :global LogPrintOnce; + :local Symbols { "abacus"="\F0\9F\A7\AE"; "alarm-clock"="\E2\8F\B0"; + "arrow-down"="\E2\AC\87"; + "arrow-up"="\E2\AC\86"; "calendar"="\F0\9F\93\85"; "card-file-box"="\F0\9F\97\83"; "chart-decreasing"="\F0\9F\93\89"; @@ -1235,6 +1559,8 @@ "earth"="\F0\9F\8C\8D"; "fire"="\F0\9F\94\A5"; "floppy-disk"="\F0\9F\92\BE"; + "gear"="\E2\9A\99"; + "heart"="\E2\99\A5"; "high-voltage-sign"="\E2\9A\A1"; "incoming-envelope"="\F0\9F\93\A8"; "information"="\E2\84\B9"; @@ -1246,23 +1572,32 @@ "mobile-phone"="\F0\9F\93\B1"; "pushpin"="\F0\9F\93\8C"; "scissors"="\E2\9C\82"; + "smiley-partying-face"="\F0\9F\A5\B3"; + "smiley-smiling-face"="\E2\98\BA"; + "smiley-winking-face-with-tongue"="\F0\9F\98\9C"; "sparkles"="\E2\9C\A8"; "speech-balloon"="\F0\9F\92\AC"; - "up-arrow"="\E2\AC\86"; + "star"="\E2\AD\90"; "warning-sign"="\E2\9A\A0"; "white-heavy-check-mark"="\E2\9C\85" } - :return (($Symbols->$1) . "\EF\B8\8F"); + :if ([ :len ($Symbols->$Name) ] = 0) do={ + $LogPrintOnce warning $0 ("No symbol available for name '" . $Name . "'!"); + :return ""; + } + + :return (($Symbols->$Name) . "\EF\B8\8F"); } # return symbol for notification :set SymbolForNotification do={ :global NotificationsWithSymbols; :global SymbolByUnicodeName; + :global IfThenElse; :if ($NotificationsWithSymbols != true) do={ - :return [ :tostr $2 ]; + :return [ $IfThenElse ([ :len $2 ] > 0) ([ :tostr $2 ] . " ") "" ]; } :local Return ""; :foreach Symbol in=[ :toarray $1 ] do={ @@ -1273,12 +1608,7 @@ # convert line endings, UNIX -> DOS :set Unix2Dos do={ - :local Input [ :tostr $1 ]; - - :global CharacterReplace; - - :return [ $CharacterReplace [ $CharacterReplace $Input \ - ("\n") ("\r\n") ] ("\r\r\n") ("\r\n") ]; + :return [ :tocrlf [ :tostr $1 ] ]; } # url encoding @@ -1329,7 +1659,7 @@ :global CharacterReplace; :set Input [ $CharacterReplace $Input "." "," ]; - :foreach I in={ "alpha"; "beta"; "rc" } do={ + :foreach I in={ "zero"; "alpha"; "beta"; "rc" } do={ :set Input [ $CharacterReplace $Input $I ("," . $I . ",") ]; } @@ -1340,6 +1670,7 @@ :set Return ($Return + 0xff00); :set Multi ($Multi / 0x100); } else={ + :if ($Value = "zero") do={ } :if ($Value = "alpha") do={ :set Return ($Return + 0x3f00); } :if ($Value = "beta") do={ :set Return ($Return + 0x5f00); } :if ($Value = "rc") do={ :set Return ($Return + 0x7f00); } @@ -1377,19 +1708,30 @@ :global CleanFilePath; :global EitherOr; + :global MAX; :set FileName [ $CleanFilePath $FileName ]; :local I 1; - :local Delay ([ :totime [ $EitherOr $WaitTime 2s ] ] / 20); + :local Delay ([ $MAX [ $EitherOr $WaitTime 2s ] 100ms ] / 10); :while ([ :len [ /file/find where name=$FileName ] ] = 0) do={ - :if ($I >= 20) do={ + :if ($I >= 10) do={ :return false; } :delay $Delay; :set I ($I + 1); } - :return true; + + :while ([ :len [ /file/find where name=$FileName ] ] > 0) do={ + :do { + /file/get $FileName; + :return true; + } on-error={ } + :delay $Delay; + :set Delay ($Delay * 3 / 2); + } + + :return false; } # wait to be fully connected (default route is reachable, time is sync, DNS resolves) @@ -1419,12 +1761,17 @@ :do { /system/script/run $Script; } on-error={ - $LogPrintExit2 error $0 ("Module '" . $ScriptVal->"name" . "' failed to run.") false; + $LogPrint error $0 ("Module '" . $ScriptVal->"name" . "' failed to run."); } } else={ - $LogPrintExit2 error $0 ("Module '" . $ScriptVal->"name" . "' failed syntax validation, skipping.") false; + $LogPrint error $0 ("Module '" . $ScriptVal->"name" . "' failed syntax validation, skipping."); } } +# Log success +:local Resource [ /system/resource/get ]; +$LogPrintOnce info $ScriptName ("Loaded on " . $Resource->"board-name" . \ + " with RouterOS " . $Resource->"version" . "."); + # signal we are ready :set GlobalFunctionsReady true; diff --git a/global-wait.rsc b/global-wait.rsc index fe1928b..ca3fc0c 100644 --- a/global-wait.rsc +++ b/global-wait.rsc @@ -1,11 +1,12 @@ #!rsc by RouterOS # RouterOS script: global-wait -# Copyright (c) 2020-2023 Christian Hesse -# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md +# Copyright (c) 2020-2025 Christian Hesse +# https://rsc.eworm.de/COPYING.md +# +# requires RouterOS, version=7.15 # # wait for global-functions to finish -# https://git.eworm.de/cgit/routeros-scripts/about/doc/global-wait.md +# https://rsc.eworm.de/doc/global-wait.md -:local 0 "global-wait"; :global GlobalFunctionsReady; :while ($GlobalFunctionsReady != true) do={ :delay 500ms; } diff --git a/gps-track.rsc b/gps-track.rsc index 86506b8..dea56d2 100644 --- a/gps-track.rsc +++ b/gps-track.rsc @@ -1,43 +1,53 @@ #!rsc by RouterOS # RouterOS script: gps-track -# Copyright (c) 2018-2023 Christian Hesse -# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md +# Copyright (c) 2018-2025 Christian Hesse +# https://rsc.eworm.de/COPYING.md +# +# requires RouterOS, version=7.15 +# requires device-mode, fetch # # track gps data by sending json data to http server -# https://git.eworm.de/cgit/routeros-scripts/about/doc/gps-track.md +# https://rsc.eworm.de/doc/gps-track.md -:local 0 "gps-track"; :global GlobalFunctionsReady; :while ($GlobalFunctionsReady != true) do={ :delay 500ms; } -:global GpsTrackUrl; -:global Identity; +:local ExitOK false; +:do { + :local ScriptName [ :jobname ]; -:global LogPrintExit2; -:global ScriptLock; -:global WaitFullyConnected; + :global GpsTrackUrl; + :global Identity; -$ScriptLock $0; -$WaitFullyConnected; + :global FetchUserAgentStr; + :global LogPrint; + :global ScriptLock; + :global WaitFullyConnected; -:local CoordinateFormat [ /system/gps/get coordinate-format ]; -:local Gps [ /system/gps/monitor once as-value ]; - -:if ($Gps->"valid" = true) do={ - :do { - /tool/fetch check-certificate=yes-without-crl $GpsTrackUrl output=none \ - http-method=post http-header-field=({ "Content-Type: application/json" }) \ - http-data=("{" . \ - "\"lat\":\"" . ($Gps->"latitude") . "\"," . \ - "\"lon\":\"" . ($Gps->"longitude") . "\"," . \ - "\"identity\":\"" . $Identity . "\"" . \ - "}") as-value; - $LogPrintExit2 debug $0 ("Sending GPS data in " . $CoordinateFormat . " format: " . \ - "lat: " . ($Gps->"latitude") . " " . \ - "lon: " . ($Gps->"longitude")) false; - } on-error={ - $LogPrintExit2 warning $0 ("Failed sending GPS data!") false; + :if ([ $ScriptLock $ScriptName ] = false) do={ + :set ExitOK true; + :error false; } -} else={ - $LogPrintExit2 debug $0 ("GPS data not valid.") false; + $WaitFullyConnected; + + :local CoordinateFormat [ /system/gps/get coordinate-format ]; + :local Gps [ /system/gps/monitor once as-value ]; + + :if ($Gps->"valid" = true) do={ + :do { + /tool/fetch check-certificate=yes-without-crl output=none http-method=post \ + http-header-field=({ [ $FetchUserAgentStr $ScriptName ]; "Content-Type: application/json" }) \ + http-data=[ :serialize to=json { "identity"=$Identity; \ + "lat"=($Gps->"latitude"); "lon"=($Gps->"longitude") } ] $GpsTrackUrl as-value; + $LogPrint debug $ScriptName ("Sending GPS data in " . $CoordinateFormat . " format: " . \ + "lat: " . ($Gps->"latitude") . " " . \ + "lon: " . ($Gps->"longitude")); + } on-error={ + $LogPrint warning $ScriptName ("Failed sending GPS data!"); + } + } else={ + $LogPrint debug $ScriptName ("GPS data not valid."); + } +} on-error={ + :global ExitError; $ExitError $ExitOK [ :jobname ]; } diff --git a/hotspot-to-wpa-cleanup.capsman.rsc b/hotspot-to-wpa-cleanup.capsman.rsc index b9a6620..033d0e7 100644 --- a/hotspot-to-wpa-cleanup.capsman.rsc +++ b/hotspot-to-wpa-cleanup.capsman.rsc @@ -1,68 +1,80 @@ #!rsc by RouterOS # RouterOS script: hotspot-to-wpa-cleanup.capsman -# Copyright (c) 2021-2023 Christian Hesse -# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md +# Copyright (c) 2021-2025 Christian Hesse +# https://rsc.eworm.de/COPYING.md # # provides: lease-script, order=80 +# requires RouterOS, version=7.15 +# requires device-mode, hotspot # # manage and clean up private WPA passphrase after hotspot login -# https://git.eworm.de/cgit/routeros-scripts/about/doc/hotspot-to-wpa.md +# https://rsc.eworm.de/doc/hotspot-to-wpa.md # # !! Do not edit this file, it is generated from template! -:local 0 "hotspot-to-wpa-cleanup.capsman"; :global GlobalFunctionsReady; :while ($GlobalFunctionsReady != true) do={ :delay 500ms; } -:global EitherOr; -:global LogPrintExit2; -:global ParseKeyValueStore; -:global ScriptLock; +:local ExitOK false; +:do { + :local ScriptName [ :jobname ]; -$ScriptLock $0 false 10; + :global EitherOr; + :global LogPrint; + :global ParseKeyValueStore; + :global ScriptLock; -:local DHCPServers ({}); -:foreach Server in=[ /ip/dhcp-server/find where comment~"hotspot-to-wpa" ] do={ - :local ServerVal [ /ip/dhcp-server/get $Server ] - :local ServerInfo [ $ParseKeyValueStore ($ServerVal->"comment") ]; - :if (($ServerInfo->"hotspot-to-wpa") = "wpa") do={ - :set ($DHCPServers->($ServerVal->"name")) \ - [ :totime [ $EitherOr ($ServerInfo->"timeout") 4w ] ]; + :if ([ $ScriptLock $ScriptName 10 ] = false) do={ + :set ExitOK true; + :error false; } -} -:foreach Client in=[ /caps-man/registration-table/find where comment~"^hotspot-to-wpa:" ] do={ - :local ClientVal [ /caps-man/registration-table/get $Client ]; - :foreach Lease in=[ /ip/dhcp-server/lease/find where dynamic \ - mac-address=($ClientVal->"mac-address") ] do={ - :if (($DHCPServers->[ /ip/dhcp-server/lease/get $Lease server ]) > 0s) do={ - $LogPrintExit2 info $0 ("Client with mac address " . ($ClientVal->"mac-address") . \ - " connected to WPA, making lease static.") false; - /ip/dhcp-server/lease/make-static $Lease; - /ip/dhcp-server/lease/set comment=($ClientVal->"comment") $Lease; + :local DHCPServers ({}); + :foreach Server in=[ /ip/dhcp-server/find where comment~"hotspot-to-wpa" ] do={ + :local ServerVal [ /ip/dhcp-server/get $Server ] + :local ServerInfo [ $ParseKeyValueStore ($ServerVal->"comment") ]; + :if (($ServerInfo->"hotspot-to-wpa") = "wpa") do={ + :set ($DHCPServers->($ServerVal->"name")) \ + [ :totime [ $EitherOr ($ServerInfo->"timeout") 4w ] ]; } } -} -:foreach Client in=[ /caps-man/access-list/find where comment~"^hotspot-to-wpa:" \ - !(comment~[ /system/clock/get date ]) ] do={ - :local ClientVal [ /caps-man/access-list/get $Client ]; - :if ([ :len [ /ip/dhcp-server/lease/find where !dynamic comment~"^hotspot-to-wpa:" \ - mac-address=($ClientVal->"mac-address") ] ] = 0) do={ - $LogPrintExit2 info $0 ("Client with mac address " . ($ClientVal->"mac-address") . \ - " did not connect to WPA, removing from access list.") false; - /caps-man/access-list/remove $Client; + :foreach Client in=[ /caps-man/registration-table/find where comment~"^hotspot-to-wpa:" ] do={ + :local ClientVal [ /caps-man/registration-table/get $Client ]; + :foreach Lease in=[ /ip/dhcp-server/lease/find where dynamic \ + mac-address=($ClientVal->"mac-address") ] do={ + :if (($DHCPServers->[ /ip/dhcp-server/lease/get $Lease server ]) > 0s) do={ + $LogPrint info $ScriptName ("Client with mac address " . ($ClientVal->"mac-address") . \ + " connected to WPA, making lease static."); + /ip/dhcp-server/lease/make-static $Lease; + /ip/dhcp-server/lease/set comment=($ClientVal->"comment") $Lease; + } + } } -} -:foreach Server,Timeout in=$DHCPServers do={ - :foreach Lease in=[ /ip/dhcp-server/lease/find where !dynamic status="waiting" \ - server=$Server last-seen>$Timeout comment~"^hotspot-to-wpa:" ] do={ - :local LeaseVal [ /ip/dhcp-server/lease/get $Lease ]; - $LogPrintExit2 info $0 ("Client with mac address " . ($LeaseVal->"mac-address") . \ - " was not seen for " . ($LeaseVal->"last-seen") . ", removing.") false; - /caps-man/access-list/remove [ find where comment~"^hotspot-to-wpa:" \ - mac-address=($LeaseVal->"mac-address") ]; - /ip/dhcp-server/lease/remove $Lease; + :foreach Client in=[ /caps-man/access-list/find where comment~"^hotspot-to-wpa:" \ + !(comment~[ /system/clock/get date ]) mac-address ] do={ + :local ClientVal [ /caps-man/access-list/get $Client ]; + :if ([ :len [ /ip/dhcp-server/lease/find where !dynamic comment~"^hotspot-to-wpa:" \ + mac-address=($ClientVal->"mac-address") ] ] = 0) do={ + $LogPrint info $ScriptName ("Client with mac address " . ($ClientVal->"mac-address") . \ + " did not connect to WPA, removing from access list."); + /caps-man/access-list/remove $Client; + } } + + :foreach Server,Timeout in=$DHCPServers do={ + :local TimeoutExtra ($Timeout + [ /system/clock/get time ]); + :foreach Lease in=[ /ip/dhcp-server/lease/find where !dynamic status="waiting" \ + server=$Server last-seen>$TimeoutExtra comment~"^hotspot-to-wpa:" ] do={ + :local LeaseVal [ /ip/dhcp-server/lease/get $Lease ]; + $LogPrint info $ScriptName ("Client with mac address " . ($LeaseVal->"mac-address") . \ + " was not seen for " . ($LeaseVal->"last-seen") . ", removing."); + /caps-man/access-list/remove [ find where comment~"^hotspot-to-wpa:" \ + mac-address=($LeaseVal->"mac-address") ]; + /ip/dhcp-server/lease/remove $Lease; + } + } +} on-error={ + :global ExitError; $ExitError $ExitOK [ :jobname ]; } diff --git a/hotspot-to-wpa-cleanup.template.rsc b/hotspot-to-wpa-cleanup.template.rsc index 8cc732b..0f8c490 100644 --- a/hotspot-to-wpa-cleanup.template.rsc +++ b/hotspot-to-wpa-cleanup.template.rsc @@ -1,78 +1,87 @@ #!rsc by RouterOS # RouterOS script: hotspot-to-wpa-cleanup%TEMPL% -# Copyright (c) 2021-2023 Christian Hesse -# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md +# Copyright (c) 2021-2025 Christian Hesse +# https://rsc.eworm.de/COPYING.md # # provides: lease-script, order=80 -# NOT /caps-man # -# requires RouterOS, version=7.12beta3 -# NOT /caps-man # +# requires RouterOS, version=7.15 +# requires device-mode, hotspot # # manage and clean up private WPA passphrase after hotspot login -# https://git.eworm.de/cgit/routeros-scripts/about/doc/hotspot-to-wpa.md +# https://rsc.eworm.de/doc/hotspot-to-wpa.md # # !! This is just a template to generate the real script! # !! Pattern '%TEMPL%' is replaced, paths are filtered. -:local 0 "hotspot-to-wpa-cleanup%TEMPL%"; :global GlobalFunctionsReady; :while ($GlobalFunctionsReady != true) do={ :delay 500ms; } -:global EitherOr; -:global LogPrintExit2; -:global ParseKeyValueStore; -:global ScriptLock; +:local ExitOK false; +:do { + :local ScriptName [ :jobname ]; -$ScriptLock $0 false 10; + :global EitherOr; + :global LogPrint; + :global ParseKeyValueStore; + :global ScriptLock; -:local DHCPServers ({}); -:foreach Server in=[ /ip/dhcp-server/find where comment~"hotspot-to-wpa" ] do={ - :local ServerVal [ /ip/dhcp-server/get $Server ] - :local ServerInfo [ $ParseKeyValueStore ($ServerVal->"comment") ]; - :if (($ServerInfo->"hotspot-to-wpa") = "wpa") do={ - :set ($DHCPServers->($ServerVal->"name")) \ - [ :totime [ $EitherOr ($ServerInfo->"timeout") 4w ] ]; + :if ([ $ScriptLock $ScriptName 10 ] = false) do={ + :set ExitOK true; + :error false; } -} -:foreach Client in=[ /caps-man/registration-table/find where comment~"^hotspot-to-wpa:" ] do={ -:foreach Client in=[ /interface/wifiwave2/registration-table/find where comment~"^hotspot-to-wpa:" ] do={ - :local ClientVal [ /caps-man/registration-table/get $Client ]; - :local ClientVal [ /interface/wifiwave2/registration-table/get $Client ]; - :foreach Lease in=[ /ip/dhcp-server/lease/find where dynamic \ - mac-address=($ClientVal->"mac-address") ] do={ - :if (($DHCPServers->[ /ip/dhcp-server/lease/get $Lease server ]) > 0s) do={ - $LogPrintExit2 info $0 ("Client with mac address " . ($ClientVal->"mac-address") . \ - " connected to WPA, making lease static.") false; - /ip/dhcp-server/lease/make-static $Lease; - /ip/dhcp-server/lease/set comment=($ClientVal->"comment") $Lease; + :local DHCPServers ({}); + :foreach Server in=[ /ip/dhcp-server/find where comment~"hotspot-to-wpa" ] do={ + :local ServerVal [ /ip/dhcp-server/get $Server ] + :local ServerInfo [ $ParseKeyValueStore ($ServerVal->"comment") ]; + :if (($ServerInfo->"hotspot-to-wpa") = "wpa") do={ + :set ($DHCPServers->($ServerVal->"name")) \ + [ :totime [ $EitherOr ($ServerInfo->"timeout") 4w ] ]; } } -} -:foreach Client in=[ /caps-man/access-list/find where comment~"^hotspot-to-wpa:" \ -:foreach Client in=[ /interface/wifiwave2/access-list/find where comment~"^hotspot-to-wpa:" \ - !(comment~[ /system/clock/get date ]) ] do={ - :local ClientVal [ /caps-man/access-list/get $Client ]; - :local ClientVal [ /interface/wifiwave2/access-list/get $Client ]; - :if ([ :len [ /ip/dhcp-server/lease/find where !dynamic comment~"^hotspot-to-wpa:" \ - mac-address=($ClientVal->"mac-address") ] ] = 0) do={ - $LogPrintExit2 info $0 ("Client with mac address " . ($ClientVal->"mac-address") . \ - " did not connect to WPA, removing from access list.") false; - /caps-man/access-list/remove $Client; - /interface/wifiwave2/access-list/remove $Client; + :foreach Client in=[ /caps-man/registration-table/find where comment~"^hotspot-to-wpa:" ] do={ + :foreach Client in=[ /interface/wifi/registration-table/find where comment~"^hotspot-to-wpa:" ] do={ + :local ClientVal [ /caps-man/registration-table/get $Client ]; + :local ClientVal [ /interface/wifi/registration-table/get $Client ]; + :foreach Lease in=[ /ip/dhcp-server/lease/find where dynamic \ + mac-address=($ClientVal->"mac-address") ] do={ + :if (($DHCPServers->[ /ip/dhcp-server/lease/get $Lease server ]) > 0s) do={ + $LogPrint info $ScriptName ("Client with mac address " . ($ClientVal->"mac-address") . \ + " connected to WPA, making lease static."); + /ip/dhcp-server/lease/make-static $Lease; + /ip/dhcp-server/lease/set comment=($ClientVal->"comment") $Lease; + } + } } -} -:foreach Server,Timeout in=$DHCPServers do={ - :foreach Lease in=[ /ip/dhcp-server/lease/find where !dynamic status="waiting" \ - server=$Server last-seen>$Timeout comment~"^hotspot-to-wpa:" ] do={ - :local LeaseVal [ /ip/dhcp-server/lease/get $Lease ]; - $LogPrintExit2 info $0 ("Client with mac address " . ($LeaseVal->"mac-address") . \ - " was not seen for " . ($LeaseVal->"last-seen") . ", removing.") false; - /caps-man/access-list/remove [ find where comment~"^hotspot-to-wpa:" \ - /interface/wifiwave2/access-list/remove [ find where comment~"^hotspot-to-wpa:" \ - mac-address=($LeaseVal->"mac-address") ]; - /ip/dhcp-server/lease/remove $Lease; + :foreach Client in=[ /caps-man/access-list/find where comment~"^hotspot-to-wpa:" \ + :foreach Client in=[ /interface/wifi/access-list/find where comment~"^hotspot-to-wpa:" \ + !(comment~[ /system/clock/get date ]) mac-address ] do={ + :local ClientVal [ /caps-man/access-list/get $Client ]; + :local ClientVal [ /interface/wifi/access-list/get $Client ]; + :if ([ :len [ /ip/dhcp-server/lease/find where !dynamic comment~"^hotspot-to-wpa:" \ + mac-address=($ClientVal->"mac-address") ] ] = 0) do={ + $LogPrint info $ScriptName ("Client with mac address " . ($ClientVal->"mac-address") . \ + " did not connect to WPA, removing from access list."); + /caps-man/access-list/remove $Client; + /interface/wifi/access-list/remove $Client; + } } + + :foreach Server,Timeout in=$DHCPServers do={ + :local TimeoutExtra ($Timeout + [ /system/clock/get time ]); + :foreach Lease in=[ /ip/dhcp-server/lease/find where !dynamic status="waiting" \ + server=$Server last-seen>$TimeoutExtra comment~"^hotspot-to-wpa:" ] do={ + :local LeaseVal [ /ip/dhcp-server/lease/get $Lease ]; + $LogPrint info $ScriptName ("Client with mac address " . ($LeaseVal->"mac-address") . \ + " was not seen for " . ($LeaseVal->"last-seen") . ", removing."); + /caps-man/access-list/remove [ find where comment~"^hotspot-to-wpa:" \ + /interface/wifi/access-list/remove [ find where comment~"^hotspot-to-wpa:" \ + mac-address=($LeaseVal->"mac-address") ]; + /ip/dhcp-server/lease/remove $Lease; + } + } +} on-error={ + :global ExitError; $ExitError $ExitOK [ :jobname ]; } diff --git a/hotspot-to-wpa-cleanup.wifi.rsc b/hotspot-to-wpa-cleanup.wifi.rsc new file mode 100644 index 0000000..dfec697 --- /dev/null +++ b/hotspot-to-wpa-cleanup.wifi.rsc @@ -0,0 +1,80 @@ +#!rsc by RouterOS +# RouterOS script: hotspot-to-wpa-cleanup.wifi +# Copyright (c) 2021-2025 Christian Hesse +# https://rsc.eworm.de/COPYING.md +# +# provides: lease-script, order=80 +# requires RouterOS, version=7.15 +# requires device-mode, hotspot +# +# manage and clean up private WPA passphrase after hotspot login +# https://rsc.eworm.de/doc/hotspot-to-wpa.md +# +# !! Do not edit this file, it is generated from template! + +:global GlobalFunctionsReady; +:while ($GlobalFunctionsReady != true) do={ :delay 500ms; } + +:local ExitOK false; +:do { + :local ScriptName [ :jobname ]; + + :global EitherOr; + :global LogPrint; + :global ParseKeyValueStore; + :global ScriptLock; + + :if ([ $ScriptLock $ScriptName 10 ] = false) do={ + :set ExitOK true; + :error false; + } + + :local DHCPServers ({}); + :foreach Server in=[ /ip/dhcp-server/find where comment~"hotspot-to-wpa" ] do={ + :local ServerVal [ /ip/dhcp-server/get $Server ] + :local ServerInfo [ $ParseKeyValueStore ($ServerVal->"comment") ]; + :if (($ServerInfo->"hotspot-to-wpa") = "wpa") do={ + :set ($DHCPServers->($ServerVal->"name")) \ + [ :totime [ $EitherOr ($ServerInfo->"timeout") 4w ] ]; + } + } + + :foreach Client in=[ /interface/wifi/registration-table/find where comment~"^hotspot-to-wpa:" ] do={ + :local ClientVal [ /interface/wifi/registration-table/get $Client ]; + :foreach Lease in=[ /ip/dhcp-server/lease/find where dynamic \ + mac-address=($ClientVal->"mac-address") ] do={ + :if (($DHCPServers->[ /ip/dhcp-server/lease/get $Lease server ]) > 0s) do={ + $LogPrint info $ScriptName ("Client with mac address " . ($ClientVal->"mac-address") . \ + " connected to WPA, making lease static."); + /ip/dhcp-server/lease/make-static $Lease; + /ip/dhcp-server/lease/set comment=($ClientVal->"comment") $Lease; + } + } + } + + :foreach Client in=[ /interface/wifi/access-list/find where comment~"^hotspot-to-wpa:" \ + !(comment~[ /system/clock/get date ]) mac-address ] do={ + :local ClientVal [ /interface/wifi/access-list/get $Client ]; + :if ([ :len [ /ip/dhcp-server/lease/find where !dynamic comment~"^hotspot-to-wpa:" \ + mac-address=($ClientVal->"mac-address") ] ] = 0) do={ + $LogPrint info $ScriptName ("Client with mac address " . ($ClientVal->"mac-address") . \ + " did not connect to WPA, removing from access list."); + /interface/wifi/access-list/remove $Client; + } + } + + :foreach Server,Timeout in=$DHCPServers do={ + :local TimeoutExtra ($Timeout + [ /system/clock/get time ]); + :foreach Lease in=[ /ip/dhcp-server/lease/find where !dynamic status="waiting" \ + server=$Server last-seen>$TimeoutExtra comment~"^hotspot-to-wpa:" ] do={ + :local LeaseVal [ /ip/dhcp-server/lease/get $Lease ]; + $LogPrint info $ScriptName ("Client with mac address " . ($LeaseVal->"mac-address") . \ + " was not seen for " . ($LeaseVal->"last-seen") . ", removing."); + /interface/wifi/access-list/remove [ find where comment~"^hotspot-to-wpa:" \ + mac-address=($LeaseVal->"mac-address") ]; + /ip/dhcp-server/lease/remove $Lease; + } + } +} on-error={ + :global ExitError; $ExitError $ExitOK [ :jobname ]; +} diff --git a/hotspot-to-wpa-cleanup.wifiwave2.rsc b/hotspot-to-wpa-cleanup.wifiwave2.rsc deleted file mode 100644 index e316c17..0000000 --- a/hotspot-to-wpa-cleanup.wifiwave2.rsc +++ /dev/null @@ -1,69 +0,0 @@ -#!rsc by RouterOS -# RouterOS script: hotspot-to-wpa-cleanup.wifiwave2 -# Copyright (c) 2021-2023 Christian Hesse -# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md -# -# provides: lease-script, order=80 -# requires RouterOS, version=7.12beta3 -# -# manage and clean up private WPA passphrase after hotspot login -# https://git.eworm.de/cgit/routeros-scripts/about/doc/hotspot-to-wpa.md -# -# !! Do not edit this file, it is generated from template! - -:local 0 "hotspot-to-wpa-cleanup.wifiwave2"; -:global GlobalFunctionsReady; -:while ($GlobalFunctionsReady != true) do={ :delay 500ms; } - -:global EitherOr; -:global LogPrintExit2; -:global ParseKeyValueStore; -:global ScriptLock; - -$ScriptLock $0 false 10; - -:local DHCPServers ({}); -:foreach Server in=[ /ip/dhcp-server/find where comment~"hotspot-to-wpa" ] do={ - :local ServerVal [ /ip/dhcp-server/get $Server ] - :local ServerInfo [ $ParseKeyValueStore ($ServerVal->"comment") ]; - :if (($ServerInfo->"hotspot-to-wpa") = "wpa") do={ - :set ($DHCPServers->($ServerVal->"name")) \ - [ :totime [ $EitherOr ($ServerInfo->"timeout") 4w ] ]; - } -} - -:foreach Client in=[ /interface/wifiwave2/registration-table/find where comment~"^hotspot-to-wpa:" ] do={ - :local ClientVal [ /interface/wifiwave2/registration-table/get $Client ]; - :foreach Lease in=[ /ip/dhcp-server/lease/find where dynamic \ - mac-address=($ClientVal->"mac-address") ] do={ - :if (($DHCPServers->[ /ip/dhcp-server/lease/get $Lease server ]) > 0s) do={ - $LogPrintExit2 info $0 ("Client with mac address " . ($ClientVal->"mac-address") . \ - " connected to WPA, making lease static.") false; - /ip/dhcp-server/lease/make-static $Lease; - /ip/dhcp-server/lease/set comment=($ClientVal->"comment") $Lease; - } - } -} - -:foreach Client in=[ /interface/wifiwave2/access-list/find where comment~"^hotspot-to-wpa:" \ - !(comment~[ /system/clock/get date ]) ] do={ - :local ClientVal [ /interface/wifiwave2/access-list/get $Client ]; - :if ([ :len [ /ip/dhcp-server/lease/find where !dynamic comment~"^hotspot-to-wpa:" \ - mac-address=($ClientVal->"mac-address") ] ] = 0) do={ - $LogPrintExit2 info $0 ("Client with mac address " . ($ClientVal->"mac-address") . \ - " did not connect to WPA, removing from access list.") false; - /interface/wifiwave2/access-list/remove $Client; - } -} - -:foreach Server,Timeout in=$DHCPServers do={ - :foreach Lease in=[ /ip/dhcp-server/lease/find where !dynamic status="waiting" \ - server=$Server last-seen>$Timeout comment~"^hotspot-to-wpa:" ] do={ - :local LeaseVal [ /ip/dhcp-server/lease/get $Lease ]; - $LogPrintExit2 info $0 ("Client with mac address " . ($LeaseVal->"mac-address") . \ - " was not seen for " . ($LeaseVal->"last-seen") . ", removing.") false; - /interface/wifiwave2/access-list/remove [ find where comment~"^hotspot-to-wpa:" \ - mac-address=($LeaseVal->"mac-address") ]; - /ip/dhcp-server/lease/remove $Lease; - } -} diff --git a/hotspot-to-wpa.capsman.rsc b/hotspot-to-wpa.capsman.rsc index a7ebafb..3f51475 100644 --- a/hotspot-to-wpa.capsman.rsc +++ b/hotspot-to-wpa.capsman.rsc @@ -1,89 +1,105 @@ #!rsc by RouterOS # RouterOS script: hotspot-to-wpa.capsman -# Copyright (c) 2019-2023 Christian Hesse -# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md +# Copyright (c) 2019-2025 Christian Hesse +# https://rsc.eworm.de/COPYING.md +# +# requires RouterOS, version=7.15 +# requires device-mode, hotspot # # add private WPA passphrase after hotspot login -# https://git.eworm.de/cgit/routeros-scripts/about/doc/hotspot-to-wpa.md +# https://rsc.eworm.de/doc/hotspot-to-wpa.md # # !! Do not edit this file, it is generated from template! -:local 0 "hotspot-to-wpa.capsman"; :global GlobalFunctionsReady; :while ($GlobalFunctionsReady != true) do={ :delay 500ms; } -:global EitherOr; -:global LogPrintExit2; -:global ParseKeyValueStore; -:global ScriptLock; +:local ExitOK false; +:do { + :local ScriptName [ :jobname ]; -$ScriptLock $0; + :global EitherOr; + :global LogPrint; + :global ParseKeyValueStore; + :global ScriptLock; -:local MacAddress $"mac-address"; -:local UserName $username; + :local MacAddress $"mac-address"; + :local UserName $username; -:if ([ :typeof $MacAddress ] = "nothing" || [ :typeof $UserName ] = "nothing") do={ - $LogPrintExit2 error $0 ("This script is supposed to run from hotspot on login.") true; -} - -:local Date [ /system/clock/get date ]; -:local UserVal ({}); -:if ([ :len [ /ip/hotspot/user/find where name=$UserName ] ] > 0) do={ - :set UserVal [ /ip/hotspot/user/get [ find where name=$UserName ] ]; -} -:local UserInfo [ $ParseKeyValueStore ($UserVal->"comment") ]; -:local Hotspot [ /ip/hotspot/host/get [ find where mac-address=$MacAddress authorized ] server ]; - -:if ([ :len [ /caps-man/access-list/find where comment="--- hotspot-to-wpa above ---" disabled ] ] = 0) do={ - /caps-man/access-list/add comment="--- hotspot-to-wpa above ---" disabled=yes; - $LogPrintExit2 warning $0 ("Added disabled access-list entry with comment '--- hotspot-to-wpa above ---'.") false; -} -:local PlaceBefore ([ /caps-man/access-list/find where comment="--- hotspot-to-wpa above ---" disabled ]->0); - -:if ([ :len [ /caps-man/access-list/find where \ - comment=("hotspot-to-wpa template " . $Hotspot) disabled ] ] = 0) do={ - /caps-man/access-list/add comment=("hotspot-to-wpa template " . $Hotspot) disabled=yes place-before=$PlaceBefore; - $LogPrintExit2 warning $0 ("Added template in access-list for hotspot '" . $Hotspot . "'.") false; -} -:local Template [ /caps-man/access-list/get ([ find where \ - comment=("hotspot-to-wpa template " . $Hotspot) disabled ]->0) ]; - -:if ($Template->"action" = "reject") do={ - $LogPrintExit2 info $0 ("Ignoring login for hotspot '" . $Hotspot . "'.") true; -} - -# allow login page to load -:delay 1s; - -$LogPrintExit2 info $0 ("Adding/updating access-list entry for mac address " . $MacAddress . \ - " (user " . $UserName . ").") false; -/caps-man/access-list/remove [ find where mac-address=$MacAddress comment~"^hotspot-to-wpa: " ]; -/caps-man/access-list/add private-passphrase=($UserVal->"password") ssid-regexp="-wpa\$" \ - mac-address=$MacAddress comment=("hotspot-to-wpa: " . $UserName . ", " . $MacAddress . ", " . $Date) \ - action=reject place-before=$PlaceBefore; - -:local Entry [ /caps-man/access-list/find where mac-address=$MacAddress \ - comment=("hotspot-to-wpa: " . $UserName . ", " . $MacAddress . ", " . $Date) ]; -:local PrivatePassphrase [ $EitherOr ($UserInfo->"private-passphrase") ($Template->"private-passphrase") ]; -:if ([ :len $PrivatePassphrase ] > 0) do={ - :if ($PrivatePassphrase = "ignore") do={ - /caps-man/access-list/set $Entry !private-passphrase; - } else={ - /caps-man/access-list/set $Entry private-passphrase=$PrivatePassphrase; + :if ([ $ScriptLock $ScriptName ] = false) do={ + :set ExitOK true; + :error false; } -} -:local SsidRegexp [ $EitherOr ($UserInfo->"ssid-regexp") ($Template->"ssid-regexp") ]; -:if ([ :len $SsidRegexp ] > 0) do={ - /caps-man/access-list/set $Entry ssid-regexp=$SsidRegexp; -} -:local VlanId [ $EitherOr ($UserInfo->"vlan-id") ($Template->"vlan-id") ]; -:if ([ :len $VlanId ] > 0) do={ - /caps-man/access-list/set $Entry vlan-id=$VlanId; -} -:local VlanMode [ $EitherOr ($UserInfo->"vlan-mode") ($Template->"vlan-mode") ]; -:if ([ :len $VlanMode] > 0) do={ - /caps-man/access-list/set $Entry vlan-mode=$VlanMode; -} -:delay 2s; -/caps-man/access-list/set $Entry action=accept; + :if ([ :typeof $MacAddress ] = "nothing" || [ :typeof $UserName ] = "nothing") do={ + $LogPrint error $ScriptName ("This script is supposed to run from hotspot on login."); + :set ExitOK true; + :error false; + } + + :local Date [ /system/clock/get date ]; + :local UserVal ({}); + :if ([ :len [ /ip/hotspot/user/find where name=$UserName ] ] > 0) do={ + :set UserVal [ /ip/hotspot/user/get [ find where name=$UserName ] ]; + } + :local UserInfo [ $ParseKeyValueStore ($UserVal->"comment") ]; + :local Hotspot [ /ip/hotspot/host/get [ find where mac-address=$MacAddress authorized ] server ]; + + :if ([ :len [ /caps-man/access-list/find where comment="--- hotspot-to-wpa above ---" disabled ] ] = 0) do={ + /caps-man/access-list/add comment="--- hotspot-to-wpa above ---" disabled=yes; + $LogPrint warning $ScriptName ("Added disabled access-list entry with comment '--- hotspot-to-wpa above ---'."); + } + :local PlaceBefore ([ /caps-man/access-list/find where comment="--- hotspot-to-wpa above ---" disabled ]->0); + + :if ([ :len [ /caps-man/access-list/find where \ + comment=("hotspot-to-wpa template " . $Hotspot) disabled ] ] = 0) do={ + /caps-man/access-list/add comment=("hotspot-to-wpa template " . $Hotspot) disabled=yes place-before=$PlaceBefore; + $LogPrint warning $ScriptName ("Added template in access-list for hotspot '" . $Hotspot . "'."); + } + :local Template [ /caps-man/access-list/get ([ find where \ + comment=("hotspot-to-wpa template " . $Hotspot) disabled ]->0) ]; + + :if ($Template->"action" = "reject") do={ + $LogPrint info $ScriptName ("Ignoring login for hotspot '" . $Hotspot . "'."); + :set ExitOK true; + :error true; + } + + # allow login page to load + :delay 1s; + + $LogPrint info $ScriptName ("Adding/updating access-list entry for mac address " . $MacAddress . \ + " (user " . $UserName . ")."); + /caps-man/access-list/remove [ find where mac-address=$MacAddress comment~"^hotspot-to-wpa: " ]; + /caps-man/access-list/add private-passphrase=($UserVal->"password") ssid-regexp="-wpa\$" \ + mac-address=$MacAddress comment=("hotspot-to-wpa: " . $UserName . ", " . $MacAddress . ", " . $Date) \ + action=reject place-before=$PlaceBefore; + + :local Entry [ /caps-man/access-list/find where mac-address=$MacAddress \ + comment=("hotspot-to-wpa: " . $UserName . ", " . $MacAddress . ", " . $Date) ]; + :local PrivatePassphrase [ $EitherOr ($UserInfo->"private-passphrase") ($Template->"private-passphrase") ]; + :if ([ :len $PrivatePassphrase ] > 0) do={ + :if ($PrivatePassphrase = "ignore") do={ + /caps-man/access-list/set $Entry !private-passphrase; + } else={ + /caps-man/access-list/set $Entry private-passphrase=$PrivatePassphrase; + } + } + :local SsidRegexp [ $EitherOr ($UserInfo->"ssid-regexp") ($Template->"ssid-regexp") ]; + :if ([ :len $SsidRegexp ] > 0) do={ + /caps-man/access-list/set $Entry ssid-regexp=$SsidRegexp; + } + :local VlanId [ $EitherOr ($UserInfo->"vlan-id") ($Template->"vlan-id") ]; + :if ([ :len $VlanId ] > 0) do={ + /caps-man/access-list/set $Entry vlan-id=$VlanId; + } + :local VlanMode [ $EitherOr ($UserInfo->"vlan-mode") ($Template->"vlan-mode") ]; + :if ([ :len $VlanMode] > 0) do={ + /caps-man/access-list/set $Entry vlan-mode=$VlanMode; + } + + :delay 2s; + /caps-man/access-list/set $Entry action=accept; +} on-error={ + :global ExitError; $ExitError $ExitOK [ :jobname ]; +} diff --git a/hotspot-to-wpa.template.rsc b/hotspot-to-wpa.template.rsc index f465722..068241d 100644 --- a/hotspot-to-wpa.template.rsc +++ b/hotspot-to-wpa.template.rsc @@ -1,110 +1,125 @@ #!rsc by RouterOS # RouterOS script: hotspot-to-wpa%TEMPL% -# Copyright (c) 2019-2023 Christian Hesse -# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md +# Copyright (c) 2019-2025 Christian Hesse +# https://rsc.eworm.de/COPYING.md +# +# requires RouterOS, version=7.15 +# requires device-mode, hotspot # # add private WPA passphrase after hotspot login -# https://git.eworm.de/cgit/routeros-scripts/about/doc/hotspot-to-wpa.md +# https://rsc.eworm.de/doc/hotspot-to-wpa.md # # !! This is just a template to generate the real script! # !! Pattern '%TEMPL%' is replaced, paths are filtered. -:local 0 "hotspot-to-wpa%TEMPL%"; :global GlobalFunctionsReady; :while ($GlobalFunctionsReady != true) do={ :delay 500ms; } -:global EitherOr; -:global LogPrintExit2; -:global ParseKeyValueStore; -:global ScriptLock; +:local ExitOK false; +:do { + :local ScriptName [ :jobname ]; -$ScriptLock $0; + :global EitherOr; + :global LogPrint; + :global ParseKeyValueStore; + :global ScriptLock; -:local MacAddress $"mac-address"; -:local UserName $username; + :local MacAddress $"mac-address"; + :local UserName $username; -:if ([ :typeof $MacAddress ] = "nothing" || [ :typeof $UserName ] = "nothing") do={ - $LogPrintExit2 error $0 ("This script is supposed to run from hotspot on login.") true; -} - -:local Date [ /system/clock/get date ]; -:local UserVal ({}); -:if ([ :len [ /ip/hotspot/user/find where name=$UserName ] ] > 0) do={ - :set UserVal [ /ip/hotspot/user/get [ find where name=$UserName ] ]; -} -:local UserInfo [ $ParseKeyValueStore ($UserVal->"comment") ]; -:local Hotspot [ /ip/hotspot/host/get [ find where mac-address=$MacAddress authorized ] server ]; - -:if ([ :len [ /caps-man/access-list/find where comment="--- hotspot-to-wpa above ---" disabled ] ] = 0) do={ -:if ([ :len [ /interface/wifiwave2/access-list/find where comment="--- hotspot-to-wpa above ---" disabled ] ] = 0) do={ - /caps-man/access-list/add comment="--- hotspot-to-wpa above ---" disabled=yes; - /interface/wifiwave2/access-list/add comment="--- hotspot-to-wpa above ---" disabled=yes; - $LogPrintExit2 warning $0 ("Added disabled access-list entry with comment '--- hotspot-to-wpa above ---'.") false; -} -:local PlaceBefore ([ /caps-man/access-list/find where comment="--- hotspot-to-wpa above ---" disabled ]->0); -:local PlaceBefore ([ /interface/wifiwave2/access-list/find where comment="--- hotspot-to-wpa above ---" disabled ]->0); - -:if ([ :len [ /caps-man/access-list/find where \ -:if ([ :len [ /interface/wifiwave2/access-list/find where \ - comment=("hotspot-to-wpa template " . $Hotspot) disabled ] ] = 0) do={ - /caps-man/access-list/add comment=("hotspot-to-wpa template " . $Hotspot) disabled=yes place-before=$PlaceBefore; - /interface/wifiwave2/access-list/add comment=("hotspot-to-wpa template " . $Hotspot) disabled=yes place-before=$PlaceBefore; - $LogPrintExit2 warning $0 ("Added template in access-list for hotspot '" . $Hotspot . "'.") false; -} -:local Template [ /caps-man/access-list/get ([ find where \ -:local Template [ /interface/wifiwave2/access-list/get ([ find where \ - comment=("hotspot-to-wpa template " . $Hotspot) disabled ]->0) ]; - -:if ($Template->"action" = "reject") do={ - $LogPrintExit2 info $0 ("Ignoring login for hotspot '" . $Hotspot . "'.") true; -} - -# allow login page to load -:delay 1s; - -$LogPrintExit2 info $0 ("Adding/updating access-list entry for mac address " . $MacAddress . \ - " (user " . $UserName . ").") false; -/caps-man/access-list/remove [ find where mac-address=$MacAddress comment~"^hotspot-to-wpa: " ]; -/interface/wifiwave2/access-list/remove [ find where mac-address=$MacAddress comment~"^hotspot-to-wpa: " ]; -/caps-man/access-list/add private-passphrase=($UserVal->"password") ssid-regexp="-wpa\$" \ -/interface/wifiwave2/access-list/add passphrase=($UserVal->"password") ssid-regexp="-wpa\$" \ - mac-address=$MacAddress comment=("hotspot-to-wpa: " . $UserName . ", " . $MacAddress . ", " . $Date) \ - action=reject place-before=$PlaceBefore; - -:local Entry [ /caps-man/access-list/find where mac-address=$MacAddress \ -:local Entry [ /interface/wifiwave2/access-list/find where mac-address=$MacAddress \ - comment=("hotspot-to-wpa: " . $UserName . ", " . $MacAddress . ", " . $Date) ]; -# NOT /caps-man # -:set ($Template->"private-passphrase") ($Template->"passphrase"); -# NOT /caps-man # -:local PrivatePassphrase [ $EitherOr ($UserInfo->"private-passphrase") ($Template->"private-passphrase") ]; -:if ([ :len $PrivatePassphrase ] > 0) do={ - :if ($PrivatePassphrase = "ignore") do={ - /caps-man/access-list/set $Entry !private-passphrase; - /interface/wifiwave2/access-list/set $Entry !passphrase; - } else={ - /caps-man/access-list/set $Entry private-passphrase=$PrivatePassphrase; - /interface/wifiwave2/access-list/set $Entry passphrase=$PrivatePassphrase; + :if ([ $ScriptLock $ScriptName ] = false) do={ + :set ExitOK true; + :error false; } -} -:local SsidRegexp [ $EitherOr ($UserInfo->"ssid-regexp") ($Template->"ssid-regexp") ]; -:if ([ :len $SsidRegexp ] > 0) do={ - /caps-man/access-list/set $Entry ssid-regexp=$SsidRegexp; - /interface/wifiwave2/access-list/set $Entry ssid-regexp=$SsidRegexp; -} -:local VlanId [ $EitherOr ($UserInfo->"vlan-id") ($Template->"vlan-id") ]; -:if ([ :len $VlanId ] > 0) do={ - /caps-man/access-list/set $Entry vlan-id=$VlanId; - /interface/wifiwave2/access-list/set $Entry vlan-id=$VlanId; -} -# NOT /interface/wifiwave2 # -:local VlanMode [ $EitherOr ($UserInfo->"vlan-mode") ($Template->"vlan-mode") ]; -:if ([ :len $VlanMode] > 0) do={ - /caps-man/access-list/set $Entry vlan-mode=$VlanMode; - /interface/wifiwave2/access-list/set $Entry vlan-mode=$VlanMode; -} -# NOT /interface/wifiwave2 # -:delay 2s; -/caps-man/access-list/set $Entry action=accept; -/interface/wifiwave2/access-list/set $Entry action=accept; + :if ([ :typeof $MacAddress ] = "nothing" || [ :typeof $UserName ] = "nothing") do={ + $LogPrint error $ScriptName ("This script is supposed to run from hotspot on login."); + :set ExitOK true; + :error false; + } + + :local Date [ /system/clock/get date ]; + :local UserVal ({}); + :if ([ :len [ /ip/hotspot/user/find where name=$UserName ] ] > 0) do={ + :set UserVal [ /ip/hotspot/user/get [ find where name=$UserName ] ]; + } + :local UserInfo [ $ParseKeyValueStore ($UserVal->"comment") ]; + :local Hotspot [ /ip/hotspot/host/get [ find where mac-address=$MacAddress authorized ] server ]; + + :if ([ :len [ /caps-man/access-list/find where comment="--- hotspot-to-wpa above ---" disabled ] ] = 0) do={ + :if ([ :len [ /interface/wifi/access-list/find where comment="--- hotspot-to-wpa above ---" disabled ] ] = 0) do={ + /caps-man/access-list/add comment="--- hotspot-to-wpa above ---" disabled=yes; + /interface/wifi/access-list/add comment="--- hotspot-to-wpa above ---" disabled=yes; + $LogPrint warning $ScriptName ("Added disabled access-list entry with comment '--- hotspot-to-wpa above ---'."); + } + :local PlaceBefore ([ /caps-man/access-list/find where comment="--- hotspot-to-wpa above ---" disabled ]->0); + :local PlaceBefore ([ /interface/wifi/access-list/find where comment="--- hotspot-to-wpa above ---" disabled ]->0); + + :if ([ :len [ /caps-man/access-list/find where \ + :if ([ :len [ /interface/wifi/access-list/find where \ + comment=("hotspot-to-wpa template " . $Hotspot) disabled ] ] = 0) do={ + /caps-man/access-list/add comment=("hotspot-to-wpa template " . $Hotspot) disabled=yes place-before=$PlaceBefore; + /interface/wifi/access-list/add comment=("hotspot-to-wpa template " . $Hotspot) disabled=yes place-before=$PlaceBefore; + $LogPrint warning $ScriptName ("Added template in access-list for hotspot '" . $Hotspot . "'."); + } + :local Template [ /caps-man/access-list/get ([ find where \ + :local Template [ /interface/wifi/access-list/get ([ find where \ + comment=("hotspot-to-wpa template " . $Hotspot) disabled ]->0) ]; + + :if ($Template->"action" = "reject") do={ + $LogPrint info $ScriptName ("Ignoring login for hotspot '" . $Hotspot . "'."); + :set ExitOK true; + :error true; + } + + # allow login page to load + :delay 1s; + + $LogPrint info $ScriptName ("Adding/updating access-list entry for mac address " . $MacAddress . \ + " (user " . $UserName . ")."); + /caps-man/access-list/remove [ find where mac-address=$MacAddress comment~"^hotspot-to-wpa: " ]; + /interface/wifi/access-list/remove [ find where mac-address=$MacAddress comment~"^hotspot-to-wpa: " ]; + /caps-man/access-list/add private-passphrase=($UserVal->"password") ssid-regexp="-wpa\$" \ + /interface/wifi/access-list/add passphrase=($UserVal->"password") ssid-regexp="-wpa\$" \ + mac-address=$MacAddress comment=("hotspot-to-wpa: " . $UserName . ", " . $MacAddress . ", " . $Date) \ + action=reject place-before=$PlaceBefore; + + :local Entry [ /caps-man/access-list/find where mac-address=$MacAddress \ + :local Entry [ /interface/wifi/access-list/find where mac-address=$MacAddress \ + comment=("hotspot-to-wpa: " . $UserName . ", " . $MacAddress . ", " . $Date) ]; +# NOT /caps-man/ # + :set ($Template->"private-passphrase") ($Template->"passphrase"); +# NOT /caps-man/ # + :local PrivatePassphrase [ $EitherOr ($UserInfo->"private-passphrase") ($Template->"private-passphrase") ]; + :if ([ :len $PrivatePassphrase ] > 0) do={ + :if ($PrivatePassphrase = "ignore") do={ + /caps-man/access-list/set $Entry !private-passphrase; + /interface/wifi/access-list/set $Entry !passphrase; + } else={ + /caps-man/access-list/set $Entry private-passphrase=$PrivatePassphrase; + /interface/wifi/access-list/set $Entry passphrase=$PrivatePassphrase; + } + } + :local SsidRegexp [ $EitherOr ($UserInfo->"ssid-regexp") ($Template->"ssid-regexp") ]; + :if ([ :len $SsidRegexp ] > 0) do={ + /caps-man/access-list/set $Entry ssid-regexp=$SsidRegexp; + /interface/wifi/access-list/set $Entry ssid-regexp=$SsidRegexp; + } + :local VlanId [ $EitherOr ($UserInfo->"vlan-id") ($Template->"vlan-id") ]; + :if ([ :len $VlanId ] > 0) do={ + /caps-man/access-list/set $Entry vlan-id=$VlanId; + /interface/wifi/access-list/set $Entry vlan-id=$VlanId; + } +# NOT /interface/wifi/ # + :local VlanMode [ $EitherOr ($UserInfo->"vlan-mode") ($Template->"vlan-mode") ]; + :if ([ :len $VlanMode] > 0) do={ + /caps-man/access-list/set $Entry vlan-mode=$VlanMode; + } +# NOT /interface/wifi/ # + + :delay 2s; + /caps-man/access-list/set $Entry action=accept; + /interface/wifi/access-list/set $Entry action=accept; +} on-error={ + :global ExitError; $ExitError $ExitOK [ :jobname ]; +} diff --git a/hotspot-to-wpa.wifi.rsc b/hotspot-to-wpa.wifi.rsc new file mode 100644 index 0000000..cc5e2fc --- /dev/null +++ b/hotspot-to-wpa.wifi.rsc @@ -0,0 +1,102 @@ +#!rsc by RouterOS +# RouterOS script: hotspot-to-wpa.wifi +# Copyright (c) 2019-2025 Christian Hesse +# https://rsc.eworm.de/COPYING.md +# +# requires RouterOS, version=7.15 +# requires device-mode, hotspot +# +# add private WPA passphrase after hotspot login +# https://rsc.eworm.de/doc/hotspot-to-wpa.md +# +# !! Do not edit this file, it is generated from template! + +:global GlobalFunctionsReady; +:while ($GlobalFunctionsReady != true) do={ :delay 500ms; } + +:local ExitOK false; +:do { + :local ScriptName [ :jobname ]; + + :global EitherOr; + :global LogPrint; + :global ParseKeyValueStore; + :global ScriptLock; + + :local MacAddress $"mac-address"; + :local UserName $username; + + :if ([ $ScriptLock $ScriptName ] = false) do={ + :set ExitOK true; + :error false; + } + + :if ([ :typeof $MacAddress ] = "nothing" || [ :typeof $UserName ] = "nothing") do={ + $LogPrint error $ScriptName ("This script is supposed to run from hotspot on login."); + :set ExitOK true; + :error false; + } + + :local Date [ /system/clock/get date ]; + :local UserVal ({}); + :if ([ :len [ /ip/hotspot/user/find where name=$UserName ] ] > 0) do={ + :set UserVal [ /ip/hotspot/user/get [ find where name=$UserName ] ]; + } + :local UserInfo [ $ParseKeyValueStore ($UserVal->"comment") ]; + :local Hotspot [ /ip/hotspot/host/get [ find where mac-address=$MacAddress authorized ] server ]; + + :if ([ :len [ /interface/wifi/access-list/find where comment="--- hotspot-to-wpa above ---" disabled ] ] = 0) do={ + /interface/wifi/access-list/add comment="--- hotspot-to-wpa above ---" disabled=yes; + $LogPrint warning $ScriptName ("Added disabled access-list entry with comment '--- hotspot-to-wpa above ---'."); + } + :local PlaceBefore ([ /interface/wifi/access-list/find where comment="--- hotspot-to-wpa above ---" disabled ]->0); + + :if ([ :len [ /interface/wifi/access-list/find where \ + comment=("hotspot-to-wpa template " . $Hotspot) disabled ] ] = 0) do={ + /interface/wifi/access-list/add comment=("hotspot-to-wpa template " . $Hotspot) disabled=yes place-before=$PlaceBefore; + $LogPrint warning $ScriptName ("Added template in access-list for hotspot '" . $Hotspot . "'."); + } + :local Template [ /interface/wifi/access-list/get ([ find where \ + comment=("hotspot-to-wpa template " . $Hotspot) disabled ]->0) ]; + + :if ($Template->"action" = "reject") do={ + $LogPrint info $ScriptName ("Ignoring login for hotspot '" . $Hotspot . "'."); + :set ExitOK true; + :error true; + } + + # allow login page to load + :delay 1s; + + $LogPrint info $ScriptName ("Adding/updating access-list entry for mac address " . $MacAddress . \ + " (user " . $UserName . ")."); + /interface/wifi/access-list/remove [ find where mac-address=$MacAddress comment~"^hotspot-to-wpa: " ]; + /interface/wifi/access-list/add passphrase=($UserVal->"password") ssid-regexp="-wpa\$" \ + mac-address=$MacAddress comment=("hotspot-to-wpa: " . $UserName . ", " . $MacAddress . ", " . $Date) \ + action=reject place-before=$PlaceBefore; + + :local Entry [ /interface/wifi/access-list/find where mac-address=$MacAddress \ + comment=("hotspot-to-wpa: " . $UserName . ", " . $MacAddress . ", " . $Date) ]; + :set ($Template->"private-passphrase") ($Template->"passphrase"); + :local PrivatePassphrase [ $EitherOr ($UserInfo->"private-passphrase") ($Template->"private-passphrase") ]; + :if ([ :len $PrivatePassphrase ] > 0) do={ + :if ($PrivatePassphrase = "ignore") do={ + /interface/wifi/access-list/set $Entry !passphrase; + } else={ + /interface/wifi/access-list/set $Entry passphrase=$PrivatePassphrase; + } + } + :local SsidRegexp [ $EitherOr ($UserInfo->"ssid-regexp") ($Template->"ssid-regexp") ]; + :if ([ :len $SsidRegexp ] > 0) do={ + /interface/wifi/access-list/set $Entry ssid-regexp=$SsidRegexp; + } + :local VlanId [ $EitherOr ($UserInfo->"vlan-id") ($Template->"vlan-id") ]; + :if ([ :len $VlanId ] > 0) do={ + /interface/wifi/access-list/set $Entry vlan-id=$VlanId; + } + + :delay 2s; + /interface/wifi/access-list/set $Entry action=accept; +} on-error={ + :global ExitError; $ExitError $ExitOK [ :jobname ]; +} diff --git a/hotspot-to-wpa.wifiwave2.rsc b/hotspot-to-wpa.wifiwave2.rsc deleted file mode 100644 index ea44a9d..0000000 --- a/hotspot-to-wpa.wifiwave2.rsc +++ /dev/null @@ -1,86 +0,0 @@ -#!rsc by RouterOS -# RouterOS script: hotspot-to-wpa.wifiwave2 -# Copyright (c) 2019-2023 Christian Hesse -# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md -# -# add private WPA passphrase after hotspot login -# https://git.eworm.de/cgit/routeros-scripts/about/doc/hotspot-to-wpa.md -# -# !! Do not edit this file, it is generated from template! - -:local 0 "hotspot-to-wpa.wifiwave2"; -:global GlobalFunctionsReady; -:while ($GlobalFunctionsReady != true) do={ :delay 500ms; } - -:global EitherOr; -:global LogPrintExit2; -:global ParseKeyValueStore; -:global ScriptLock; - -$ScriptLock $0; - -:local MacAddress $"mac-address"; -:local UserName $username; - -:if ([ :typeof $MacAddress ] = "nothing" || [ :typeof $UserName ] = "nothing") do={ - $LogPrintExit2 error $0 ("This script is supposed to run from hotspot on login.") true; -} - -:local Date [ /system/clock/get date ]; -:local UserVal ({}); -:if ([ :len [ /ip/hotspot/user/find where name=$UserName ] ] > 0) do={ - :set UserVal [ /ip/hotspot/user/get [ find where name=$UserName ] ]; -} -:local UserInfo [ $ParseKeyValueStore ($UserVal->"comment") ]; -:local Hotspot [ /ip/hotspot/host/get [ find where mac-address=$MacAddress authorized ] server ]; - -:if ([ :len [ /interface/wifiwave2/access-list/find where comment="--- hotspot-to-wpa above ---" disabled ] ] = 0) do={ - /interface/wifiwave2/access-list/add comment="--- hotspot-to-wpa above ---" disabled=yes; - $LogPrintExit2 warning $0 ("Added disabled access-list entry with comment '--- hotspot-to-wpa above ---'.") false; -} -:local PlaceBefore ([ /interface/wifiwave2/access-list/find where comment="--- hotspot-to-wpa above ---" disabled ]->0); - -:if ([ :len [ /interface/wifiwave2/access-list/find where \ - comment=("hotspot-to-wpa template " . $Hotspot) disabled ] ] = 0) do={ - /interface/wifiwave2/access-list/add comment=("hotspot-to-wpa template " . $Hotspot) disabled=yes place-before=$PlaceBefore; - $LogPrintExit2 warning $0 ("Added template in access-list for hotspot '" . $Hotspot . "'.") false; -} -:local Template [ /interface/wifiwave2/access-list/get ([ find where \ - comment=("hotspot-to-wpa template " . $Hotspot) disabled ]->0) ]; - -:if ($Template->"action" = "reject") do={ - $LogPrintExit2 info $0 ("Ignoring login for hotspot '" . $Hotspot . "'.") true; -} - -# allow login page to load -:delay 1s; - -$LogPrintExit2 info $0 ("Adding/updating access-list entry for mac address " . $MacAddress . \ - " (user " . $UserName . ").") false; -/interface/wifiwave2/access-list/remove [ find where mac-address=$MacAddress comment~"^hotspot-to-wpa: " ]; -/interface/wifiwave2/access-list/add passphrase=($UserVal->"password") ssid-regexp="-wpa\$" \ - mac-address=$MacAddress comment=("hotspot-to-wpa: " . $UserName . ", " . $MacAddress . ", " . $Date) \ - action=reject place-before=$PlaceBefore; - -:local Entry [ /interface/wifiwave2/access-list/find where mac-address=$MacAddress \ - comment=("hotspot-to-wpa: " . $UserName . ", " . $MacAddress . ", " . $Date) ]; -:set ($Template->"private-passphrase") ($Template->"passphrase"); -:local PrivatePassphrase [ $EitherOr ($UserInfo->"private-passphrase") ($Template->"private-passphrase") ]; -:if ([ :len $PrivatePassphrase ] > 0) do={ - :if ($PrivatePassphrase = "ignore") do={ - /interface/wifiwave2/access-list/set $Entry !passphrase; - } else={ - /interface/wifiwave2/access-list/set $Entry passphrase=$PrivatePassphrase; - } -} -:local SsidRegexp [ $EitherOr ($UserInfo->"ssid-regexp") ($Template->"ssid-regexp") ]; -:if ([ :len $SsidRegexp ] > 0) do={ - /interface/wifiwave2/access-list/set $Entry ssid-regexp=$SsidRegexp; -} -:local VlanId [ $EitherOr ($UserInfo->"vlan-id") ($Template->"vlan-id") ]; -:if ([ :len $VlanId ] > 0) do={ - /interface/wifiwave2/access-list/set $Entry vlan-id=$VlanId; -} - -:delay 2s; -/interface/wifiwave2/access-list/set $Entry action=accept; diff --git a/ip-addr-bridge.rsc b/ip-addr-bridge.rsc index 99fcba5..68ff4a4 100644 --- a/ip-addr-bridge.rsc +++ b/ip-addr-bridge.rsc @@ -1,10 +1,10 @@ #!rsc by RouterOS # RouterOS script: ip-addr-bridge -# Copyright (c) 2018-2023 Christian Hesse -# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md +# Copyright (c) 2018-2025 Christian Hesse +# https://rsc.eworm.de/COPYING.md # # enable or disable ip addresses based on bridge port state -# https://git.eworm.de/cgit/routeros-scripts/about/doc/ip-addr-bridge.md +# https://rsc.eworm.de/doc/ip-addr-bridge.md :foreach Bridge in=[ /interface/bridge/find ] do={ :local BrName [ /interface/bridge/get $Bridge name ]; diff --git a/ipsec-to-dns.rsc b/ipsec-to-dns.rsc index d961865..26dab0a 100644 --- a/ipsec-to-dns.rsc +++ b/ipsec-to-dns.rsc @@ -1,72 +1,84 @@ #!rsc by RouterOS # RouterOS script: ipsec-to-dns -# Copyright (c) 2021-2023 Christian Hesse -# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md +# Copyright (c) 2021-2025 Christian Hesse +# https://rsc.eworm.de/COPYING.md +# +# requires RouterOS, version=7.15 +# requires device-mode, ipsec # # and add/remove/update DNS entries from IPSec mode-config -# https://git.eworm.de/cgit/routeros-scripts/about/doc/ipsec-to-dns.md +# https://rsc.eworm.de/doc/ipsec-to-dns.md -:local 0 "ipsec-to-dns"; :global GlobalFunctionsReady; :while ($GlobalFunctionsReady != true) do={ :delay 500ms; } -:global Domain; -:global HostNameInZone; -:global Identity; -:global PrefixInZone; +:local ExitOK false; +:do { + :local ScriptName [ :jobname ]; -:global CharacterReplace; -:global EscapeForRegEx; -:global IfThenElse; -:global LogPrintExit2; -:global ScriptLock; + :global Domain; + :global HostNameInZone; + :global Identity; + :global PrefixInZone; -$ScriptLock $0; + :global CharacterReplace; + :global EscapeForRegEx; + :global IfThenElse; + :global LogPrint; + :global ScriptLock; -:local Zone \ - ([ $IfThenElse ($PrefixInZone = true) "ipsec." ] . \ - [ $IfThenElse ($HostNameInZone = true) ($Identity . ".") ] . $Domain); -:local Ttl 5m; -:local CommentPrefix ("managed by " . $0 . " for "); -:local CommentString ("--- " . $0 . " above ---"); - -:if ([ :len [ /ip/dns/static/find where (name=$CommentString or (comment=$CommentString and name=-)) type=NXDOMAIN disabled ] ] = 0) do={ - /ip/dns/static/add name=$CommentString type=NXDOMAIN disabled=yes; - $LogPrintExit2 warning $0 ("Added disabled static dns record with name '" . $CommentString . "'.") false; -} -:local PlaceBefore ([ /ip/dns/static/find where (name=$CommentString or (comment=$CommentString and name=-)) type=NXDOMAIN disabled ]->0); - -:foreach DnsRecord in=[ /ip/dns/static/find where comment~("^" . $CommentPrefix) ] do={ - :local DnsRecordVal [ /ip/dns/static/get $DnsRecord ]; - :local PeerId [ $CharacterReplace ($DnsRecordVal->"comment") $CommentPrefix "" ]; - :if ([ :len [ /ip/ipsec/active-peers/find where id~("^(CN=)?" . [ $EscapeForRegEx $PeerId ] . "\$") \ - dynamic-address=($DnsRecordVal->"address") ] ] > 0) do={ - $LogPrintExit2 debug $0 ("Peer " . $PeerId . " (" . $DnsRecordVal->"name" . ") still exists. Not deleting DNS entry.") false; - } else={ - :local Found false; - $LogPrintExit2 info $0 ("Peer " . $PeerId . " (" . $DnsRecordVal->"name" . ") has gone, deleting DNS entry.") false; - /ip/dns/static/remove $DnsRecord; + :if ([ $ScriptLock $ScriptName ] = false) do={ + :set ExitOK true; + :error false; } -} -:foreach Peer in=[ /ip/ipsec/active-peers/find where !(dynamic-address=[]) ] do={ - :local PeerVal [ /ip/ipsec/active-peers/get $Peer ]; - :local PeerId [ $CharacterReplace ($PeerVal->"id") "CN=" "" ]; - :local Comment ($CommentPrefix . $PeerId); - :local HostName [ :pick $PeerId 0 [ :find ($PeerId . ".") "." ] ]; + :local Zone \ + ([ $IfThenElse ($PrefixInZone = true) "ipsec." ] . \ + [ $IfThenElse ($HostNameInZone = true) ($Identity . ".") ] . $Domain); + :local Ttl 5m; + :local CommentPrefix ("managed by " . $ScriptName . " for "); + :local CommentString ("--- " . $ScriptName . " above ---"); - :local Fqdn ($HostName . "." . $Zone); - :local DnsRecord [ /ip/dns/static/find where name=$Fqdn ]; - :if ([ :len $DnsRecord ] > 0) do={ - :local DnsIp [ /ip/dns/static/get $DnsRecord address ]; - :if ($DnsIp = $PeerVal->"dynamic-address") do={ - $LogPrintExit2 debug $0 ("DNS entry for " . $Fqdn . " does not need updating.") false; + :if ([ :len [ /ip/dns/static/find where (name=$CommentString or (comment=$CommentString and name=-)) type=NXDOMAIN disabled ] ] = 0) do={ + /ip/dns/static/add name=$CommentString type=NXDOMAIN disabled=yes; + $LogPrint warning $ScriptName ("Added disabled static dns record with name '" . $CommentString . "'."); + } + :local PlaceBefore ([ /ip/dns/static/find where (name=$CommentString or (comment=$CommentString and name=-)) type=NXDOMAIN disabled ]->0); + + :foreach DnsRecord in=[ /ip/dns/static/find where comment~("^" . $CommentPrefix) ] do={ + :local DnsRecordVal [ /ip/dns/static/get $DnsRecord ]; + :local PeerId [ $CharacterReplace ($DnsRecordVal->"comment") $CommentPrefix "" ]; + :if ([ :len [ /ip/ipsec/active-peers/find where id~("^(CN=)?" . [ $EscapeForRegEx $PeerId ] . "\$") \ + dynamic-address=($DnsRecordVal->"address") ] ] > 0) do={ + $LogPrint debug $ScriptName ("Peer " . $PeerId . " (" . $DnsRecordVal->"name" . ") still exists. Not deleting DNS entry."); } else={ - $LogPrintExit2 info $0 ("Replacing DNS entry for " . $Fqdn . ", new address is " . $PeerVal->"dynamic-address" . ".") false; - /ip/dns/static/set name=$Fqdn address=($PeerVal->"dynamic-address") ttl=$Ttl comment=$Comment $DnsRecord; + :local Found false; + $LogPrint info $ScriptName ("Peer " . $PeerId . " (" . $DnsRecordVal->"name" . ") has gone, deleting DNS entry."); + /ip/dns/static/remove $DnsRecord; } - } else={ - $LogPrintExit2 info $0 ("Adding new DNS entry for " . $Fqdn . ", address is " . $PeerVal->"dynamic-address" . ".") false; - /ip/dns/static/add name=$Fqdn address=($PeerVal->"dynamic-address") ttl=$Ttl comment=$Comment place-before=$PlaceBefore; } + + :foreach Peer in=[ /ip/ipsec/active-peers/find where !(dynamic-address=[]) ] do={ + :local PeerVal [ /ip/ipsec/active-peers/get $Peer ]; + :local PeerId [ $CharacterReplace ($PeerVal->"id") "CN=" "" ]; + :local Comment ($CommentPrefix . $PeerId); + :local HostName [ :pick $PeerId 0 [ :find ($PeerId . ".") "." ] ]; + + :local Fqdn ($HostName . "." . $Zone); + :local DnsRecord [ /ip/dns/static/find where name=$Fqdn ]; + :if ([ :len $DnsRecord ] > 0) do={ + :local DnsIp [ /ip/dns/static/get $DnsRecord address ]; + :if ($DnsIp = $PeerVal->"dynamic-address") do={ + $LogPrint debug $ScriptName ("DNS entry for " . $Fqdn . " does not need updating."); + } else={ + $LogPrint info $ScriptName ("Replacing DNS entry for " . $Fqdn . ", new address is " . $PeerVal->"dynamic-address" . "."); + /ip/dns/static/set name=$Fqdn address=($PeerVal->"dynamic-address") ttl=$Ttl comment=$Comment $DnsRecord; + } + } else={ + $LogPrint info $ScriptName ("Adding new DNS entry for " . $Fqdn . ", address is " . $PeerVal->"dynamic-address" . "."); + /ip/dns/static/add name=$Fqdn address=($PeerVal->"dynamic-address") ttl=$Ttl comment=$Comment place-before=$PlaceBefore; + } + } +} on-error={ + :global ExitError; $ExitError $ExitOK [ :jobname ]; } diff --git a/ipv6-update.rsc b/ipv6-update.rsc index 7e1d34f..94bd1bc 100644 --- a/ipv6-update.rsc +++ b/ipv6-update.rsc @@ -1,79 +1,107 @@ #!rsc by RouterOS # RouterOS script: ipv6-update -# Copyright (c) 2013-2023 Christian Hesse -# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md +# Copyright (c) 2013-2025 Christian Hesse +# https://rsc.eworm.de/COPYING.md +# +# requires RouterOS, version=7.15 # # update firewall and dns settings on IPv6 prefix change -# https://git.eworm.de/cgit/routeros-scripts/about/doc/ipv6-update.md +# https://rsc.eworm.de/doc/ipv6-update.md -:local 0 "ipv6-update"; :global GlobalFunctionsReady; :while ($GlobalFunctionsReady != true) do={ :delay 500ms; } -:local PdPrefix $"pd-prefix"; +:local ExitOK false; +:do { + :local ScriptName [ :jobname ]; -:global LogPrintExit2; -:global ParseKeyValueStore; -:global ScriptLock; + :global LogPrint; + :global ParseKeyValueStore; + :global ScriptLock; -$ScriptLock $0; + :local NaAddress $"na-address"; + :local NaValid $"na-valid"; + :local PdPrefix $"pd-prefix"; + :local PdValid $"pd-valid"; -:if ([ :typeof $PdPrefix ] = "nothing") do={ - $LogPrintExit2 error $0 ("This script is supposed to run from ipv6 dhcp-client.") true; -} + :if ([ $ScriptLock $ScriptName ] = false) do={ + :set ExitOK true; + :error false; + } -: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); - $LogPrintExit2 warning $0 ("Added ipv6 address list entry for ipv6-pool-" . $Pool) false; -} -:local AddrList [ /ipv6/firewall/address-list/find where comment=("ipv6-pool-" . $Pool) ]; -:local OldPrefix [ /ipv6/firewall/address-list/get ($AddrList->0) address ]; + :if ([ :typeof $NaAddress ] = "str") do={ + $LogPrint info $ScriptName ("An address (" . $NaAddress . ") was acquired, not a prefix. Ignoring."); + :set ExitOK true; + :error false; + } -:if ($OldPrefix != $PdPrefix) do={ - $LogPrintExit2 info $0 ("Updating IPv6 address list with new IPv6 prefix " . $PdPrefix) false; - /ipv6/firewall/address-list/set address=$PdPrefix $AddrList; + :if ([ :typeof $PdPrefix ] = "nothing" || [ :typeof $PdValid ] = "nothing") do={ + $LogPrint error $ScriptName ("This script is supposed to run from ipv6 dhcp-client."); + :set ExitOK true; + :error false; + } - # give the interfaces a moment to receive their addresses - :delay 2s; + :if ($PdValid != 1) do={ + $LogPrint info $ScriptName ("The prefix " . $PdPrefix . " is no longer valid. Ignoring."); + :set ExitOK true; + :error false; + } - :foreach ListEntry in=[ /ipv6/firewall/address-list/find where comment~("^ipv6-pool-" . $Pool . ",") ] do={ - :local ListEntryVal [ /ipv6/firewall/address-list/get $ListEntry ]; - :local Comment [ $ParseKeyValueStore ($ListEntryVal->"comment") ]; + :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); + } + :local AddrList [ /ipv6/firewall/address-list/find where comment=("ipv6-pool-" . $Pool) ]; + :local OldPrefix [ /ipv6/firewall/address-list/get ($AddrList->0) address ]; - :local Prefix [ /ipv6/address/find where from-pool=$Pool interface=($Comment->"interface") global ]; - :if ([ :len $Prefix ] = 1) do={ - :set Prefix [ /ipv6/address/get $Prefix address ]; + :if ($OldPrefix != $PdPrefix) do={ + $LogPrint info $ScriptName ("Updating IPv6 address list with new IPv6 prefix " . $PdPrefix); + /ipv6/firewall/address-list/set address=$PdPrefix $AddrList; - :if ([ :typeof [ :find ($ListEntryVal->"address") "/128" ] ] = "num" ) do={ + # give the interfaces a moment to receive their addresses + :delay 2s; + + :foreach ListEntry in=[ /ipv6/firewall/address-list/find where comment~("^ipv6-pool-" . $Pool . ",") ] do={ + :local ListEntryVal [ /ipv6/firewall/address-list/get $ListEntry ]; + :local Comment [ $ParseKeyValueStore ($ListEntryVal->"comment") ]; + + :local Prefix [ /ipv6/address/find where from-pool=$Pool interface=($Comment->"interface") global ]; + :if ([ :len $Prefix ] = 1) do={ + :set Prefix [ /ipv6/address/get $Prefix address ]; + + :if ([ :typeof [ :find ($ListEntryVal->"address") "/128" ] ] = "num" ) do={ + :set Prefix ([ :toip6 [ :pick $Prefix 0 [ :find $Prefix "/64" ] ] ] & ffff:ffff:ffff:ffff::); + :local Address ($ListEntryVal->"address"); + :local Address ($Prefix | ([ :toip6 [ :pick $Address 0 [ :find $Address "/128" ] ] ] & ::ffff:ffff:ffff:ffff)); + + $LogPrint info $ScriptName ("Updating IPv6 address list with new IPv6 host address " . $Address . \ + " from interface " . ($Comment->"interface")); + /ipv6/firewall/address-list/set address=$Address $ListEntry; + } else={ + $LogPrint info $ScriptName ("Updating IPv6 address list with new IPv6 prefix " . $Prefix . \ + " from interface " . ($Comment->"interface")); + /ipv6/firewall/address-list/set address=$Prefix $ListEntry; + } + } + } + + :foreach Record in=[ /ip/dns/static/find where comment~("^ipv6-pool-" . $Pool . ",") ] do={ + :local RecordVal [ /ip/dns/static/get $Record ]; + :local Comment [ $ParseKeyValueStore ($RecordVal->"comment") ]; + + :local Prefix [ /ipv6/address/find where from-pool=$Pool interface=($Comment->"interface") global ]; + :if ([ :len $Prefix ] = 1) do={ + :set Prefix [ /ipv6/address/get $Prefix address ]; :set Prefix ([ :toip6 [ :pick $Prefix 0 [ :find $Prefix "/64" ] ] ] & ffff:ffff:ffff:ffff::); - :local Address ($ListEntryVal->"address"); - :local Address ($Prefix | ([ :toip6 [ :pick $Address 0 [ :find $Address "/128" ] ] ] & ::ffff:ffff:ffff:ffff)); + :local Address ($Prefix | ([ :toip6 ($RecordVal->"address") ] & ::ffff:ffff:ffff:ffff)); - $LogPrintExit2 info $0 ("Updating IPv6 address list with new IPv6 host address " . $Address . \ - " from interface " . ($Comment->"interface")) false; - /ipv6/firewall/address-list/set address=$Address $ListEntry; - } else={ - $LogPrintExit2 info $0 ("Updating IPv6 address list with new IPv6 prefix " . $Prefix . \ - " from interface " . ($Comment->"interface")) false; - /ipv6/firewall/address-list/set address=$Prefix $ListEntry; + $LogPrint info $ScriptName ("Updating DNS record for " . ($RecordVal->"name") . \ + ($RecordVal->"regexp") . " to " . $Address); + /ip/dns/static/set address=$Address $Record; } } } - - :foreach Record in=[ /ip/dns/static/find where comment~("^ipv6-pool-" . $Pool . ",") ] do={ - :local RecordVal [ /ip/dns/static/get $Record ]; - :local Comment [ $ParseKeyValueStore ($RecordVal->"comment") ]; - - :local Prefix [ /ipv6/address/find where from-pool=$Pool interface=($Comment->"interface") global ]; - :if ([ :len $Prefix ] = 1) do={ - :set Prefix [ /ipv6/address/get $Prefix address ]; - :set Prefix ([ :toip6 [ :pick $Prefix 0 [ :find $Prefix "/64" ] ] ] & ffff:ffff:ffff:ffff::); - :local Address ($Prefix | ([ :toip6 ($RecordVal->"address") ] & ::ffff:ffff:ffff:ffff)); - - $LogPrintExit2 info $0 ("Updating DNS record for " . ($RecordVal->"name") . \ - ($RecordVal->"regexp") . " to " . $Address) false; - /ip/dns/static/set address=$Address $Record; - } - } +} on-error={ + :global ExitError; $ExitError $ExitOK [ :jobname ]; } diff --git a/lease-script.rsc b/lease-script.rsc index f8bc129..bf27fda 100644 --- a/lease-script.rsc +++ b/lease-script.rsc @@ -1,50 +1,65 @@ #!rsc by RouterOS # RouterOS script: lease-script -# Copyright (c) 2013-2023 Christian Hesse -# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md +# Copyright (c) 2013-2025 Christian Hesse +# https://rsc.eworm.de/COPYING.md +# +# requires RouterOS, version=7.15 # # run scripts on DHCP lease -# https://git.eworm.de/cgit/routeros-scripts/about/doc/lease-script.md +# https://rsc.eworm.de/doc/lease-script.md -:local 0 "lease-script"; :global GlobalFunctionsReady; :while ($GlobalFunctionsReady != true) do={ :delay 500ms; } -:global Grep; -:global IfThenElse; -:global LogPrintExit2; -:global ParseKeyValueStore; -:global ScriptLock; +:local ExitOK false; +:do { + :local ScriptName [ :jobname ]; -:if ([ :typeof $leaseActIP ] = "nothing" || \ - [ :typeof $leaseActMAC ] = "nothing" || \ - [ :typeof $leaseServerName ] = "nothing" || \ - [ :typeof $leaseBound ] = "nothing") do={ - $LogPrintExit2 error $0 ("This script is supposed to run from ip dhcp-server.") true; -} + :global Grep; + :global IfThenElse; + :global LogPrint; + :global ParseKeyValueStore; + :global ScriptLock; -$LogPrintExit2 debug $0 ("DHCP Server " . $leaseServerName . " " . [ $IfThenElse ($leaseBound = 0) \ - "de" "" ] . "assigned lease " . $leaseActIP . " to " . $leaseActMAC) false; - -$ScriptLock $0 false 10; - -:if ([ :len [ /system/script/job/find where script=$0 ] ] > 1) do={ - $LogPrintExit2 debug $0 ("More invocations are waiting, exiting early.") 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={ - :do { - $LogPrintExit2 debug $0 ("Running script with order " . $Order . ": " . $Script) false; - /system/script/run $Script; - } on-error={ - $LogPrintExit2 warning $0 ("Running script '" . $Script . "' failed!") false; + :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={ + :do { + $LogPrint debug $ScriptName ("Running script with order " . $Order . ": " . $Script); + /system/script/run $Script; + } on-error={ + $LogPrint warning $ScriptName ("Running script '" . $Script . "' failed!"); + } + } +} on-error={ + :global ExitError; $ExitError $ExitOK [ :jobname ]; } diff --git a/leds-day-mode.rsc b/leds-day-mode.rsc index ca2e8d8..7344fde 100644 --- a/leds-day-mode.rsc +++ b/leds-day-mode.rsc @@ -1,9 +1,9 @@ #!rsc by RouterOS # RouterOS script: leds-day-mode -# Copyright (c) 2013-2023 Christian Hesse -# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md +# Copyright (c) 2013-2025 Christian Hesse +# https://rsc.eworm.de/COPYING.md # # enable LEDs -# https://git.eworm.de/cgit/routeros-scripts/about/doc/leds-mode.md +# https://rsc.eworm.de/doc/leds-mode.md /system/leds/settings/set all-leds-off=never; diff --git a/leds-night-mode.rsc b/leds-night-mode.rsc index cdd8127..8bd028e 100644 --- a/leds-night-mode.rsc +++ b/leds-night-mode.rsc @@ -1,9 +1,9 @@ #!rsc by RouterOS # RouterOS script: leds-night-mode -# Copyright (c) 2013-2023 Christian Hesse -# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md +# Copyright (c) 2013-2025 Christian Hesse +# https://rsc.eworm.de/COPYING.md # # disable LEDs -# https://git.eworm.de/cgit/routeros-scripts/about/doc/leds-mode.md +# https://rsc.eworm.de/doc/leds-mode.md /system/leds/settings/set all-leds-off=immediate; diff --git a/leds-toggle-mode.rsc b/leds-toggle-mode.rsc index da972b7..b55e351 100644 --- a/leds-toggle-mode.rsc +++ b/leds-toggle-mode.rsc @@ -1,13 +1,9 @@ #!rsc by RouterOS # RouterOS script: leds-toggle-mode -# Copyright (c) 2018-2023 Christian Hesse -# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md +# Copyright (c) 2018-2025 Christian Hesse +# https://rsc.eworm.de/COPYING.md # # toggle LEDs mode -# https://git.eworm.de/cgit/routeros-scripts/about/doc/leds-mode.md +# https://rsc.eworm.de/doc/leds-mode.md -:if ([ /system/leds/settings/get all-leds-off ] = "never") do={ - /system/leds/settings/set all-leds-off=immediate; -} else={ - /system/leds/settings/set all-leds-off=never; -} +/system/leds/settings/set all-leds-off=(({ "never"="immediate"; "immediate"="never" })->[ get all-leds-off ]); diff --git a/log-forward.rsc b/log-forward.rsc index 6cf61a9..afeb3f2 100644 --- a/log-forward.rsc +++ b/log-forward.rsc @@ -1,95 +1,113 @@ #!rsc by RouterOS # RouterOS script: log-forward -# Copyright (c) 2020-2023 Christian Hesse -# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md +# Copyright (c) 2020-2025 Christian Hesse +# https://rsc.eworm.de/COPYING.md +# +# requires RouterOS, version=7.15 # # forward log messages via notification -# https://git.eworm.de/cgit/routeros-scripts/about/doc/log-forward.md +# https://rsc.eworm.de/doc/log-forward.md -:local 0 "log-forward"; :global GlobalFunctionsReady; :while ($GlobalFunctionsReady != true) do={ :delay 500ms; } -:global Identity; -:global LogForwardFilter; -:global LogForwardFilterMessage; -:global LogForwardInclude; -:global LogForwardIncludeMessage; -:global LogForwardLast; -:global LogForwardRateLimit; +:local ExitOK false; +:do { + :local ScriptName [ :jobname ]; -:global EitherOr; -:global HexToNum; -:global IfThenElse; -:global LogForwardFilterLogForwarding; -:global LogPrintExit2; -:global ScriptLock; -:global SendNotification2; -:global SymbolForNotification; + :global Identity; + :global LogForwardFilter; + :global LogForwardFilterMessage; + :global LogForwardInclude; + :global LogForwardIncludeMessage; + :global LogForwardLast; + :global LogForwardRateLimit; -$ScriptLock $0; + :global EitherOr; + :global HexToNum; + :global IfThenElse; + :global LogForwardFilterLogForwarding; + :global LogPrint; + :global MAX; + :global ScriptLock; + :global SendNotification2; + :global SymbolForNotification; -:if ([ :typeof $LogForwardRateLimit ] = "nothing") do={ - :set LogForwardRateLimit 0; -} - -:if ($LogForwardRateLimit > 30) do={ - :set LogForwardRateLimit ($LogForwardRateLimit - 1); - $LogPrintExit2 info $0 ("Rate limit in action, not forwarding logs, if any!") true; -} - -:local Count 0; -:local Duplicates false; -:local Last [ $IfThenElse ([ :len $LogForwardLast ] > 0) [ $HexToNum $LogForwardLast ] -1 ]; -:local Messages ""; -:local Warning false; -:local MessageVal; -:local MessageDups ({}); - -:local LogForwardFilterLogForwardingCached [ $EitherOr [ $LogForwardFilterLogForwarding ] ("\$^") ]; -:foreach Message in=[ /log/find where (!(message="") and \ - !(message~$LogForwardFilterLogForwardingCached) and \ - !(topics~$LogForwardFilter) and !(message~$LogForwardFilterMessage)) or \ - topics~$LogForwardInclude or message~$LogForwardIncludeMessage ] do={ - :set MessageVal [ /log/get $Message ]; - :local Bullet "information"; - - :if ($Last < [ $HexToNum ($MessageVal->".id") ]) do={ - :local DupCount ($MessageDups->($MessageVal->"message")); - :if ($MessageVal->"topics" ~ "(warning)") do={ - :set Warning true; - :set Bullet "large-orange-circle"; - } - :if ($MessageVal->"topics" ~ "(emergency|alert|critical|error)") do={ - :set Warning true; - :set Bullet "large-red-circle"; - } - :if ($DupCount < 3) do={ - :set Messages ($Messages . "\n" . [ $SymbolForNotification $Bullet ] . \ - $MessageVal->"time" . " " . [ :tostr ($MessageVal->"topics") ] . " " . $MessageVal->"message"); - } else={ - :set Duplicates true; - } - :set ($MessageDups->($MessageVal->"message")) ($DupCount + 1); - :set Count ($Count + 1); + :if ([ $ScriptLock $ScriptName ] = false) do={ + :set ExitOK true; + :error false; } -} -:if ($Count > 0) do={ - :set LogForwardRateLimit ($LogForwardRateLimit + 10); + :if ([ :typeof $LogForwardRateLimit ] = "nothing") do={ + :set LogForwardRateLimit 0; + } - $SendNotification2 ({ origin=$0; \ - subject=([ $SymbolForNotification [ $IfThenElse ($Warning = true) "warning-sign" "memo" ] ] . \ - "Log Forwarding"); \ - message=("The log on " . $Identity . " contains " . [ $IfThenElse ($Count = 1) "this message" \ - ("these " . $Count . " messages") ] . " after " . [ /system/resource/get uptime ] . " uptime." . \ - [ $IfThenElse ($Duplicates = true) (" Multi-repeated messages have been skipped.") ] . \ - [ $IfThenElse ($LogForwardRateLimit > 30) ("\nRate limit in action, delaying forwarding.") ] . \ - "\n" . $Messages) }); - - :set LogForwardLast ($MessageVal->".id"); -} else={ - :if ($LogForwardRateLimit > 0) do={ + :if ($LogForwardRateLimit > 30) do={ :set LogForwardRateLimit ($LogForwardRateLimit - 1); + $LogPrint info $ScriptName ("Rate limit in action, not forwarding logs, if any!"); + :set ExitOK true; + :error false; } + + :local Count 0; + :local Duplicates false; + :local Last [ $IfThenElse ([ :len $LogForwardLast ] > 0) [ $HexToNum $LogForwardLast ] -1 ]; + :local Messages ""; + :local Warning false; + :local MessageVal; + :local MessageDups ({}); + + :set LogForwardFilter [ $EitherOr $LogForwardFilter [] ]; + :set LogForwardFilterMessage [ $EitherOr $LogForwardFilterMessage [] ]; + :set LogForwardInclude [ $EitherOr $LogForwardInclude [] ]; + :set LogForwardIncludeMessage [ $EitherOr $LogForwardIncludeMessage [] ]; + + :local LogForwardFilterLogForwardingCached [ $EitherOr [ $LogForwardFilterLogForwarding ] ("\$^") ]; + :foreach Message in=[ /log/find where (!(message="") and \ + !(message~$LogForwardFilterLogForwardingCached) and \ + !(topics~$LogForwardFilter) and !(message~$LogForwardFilterMessage)) or \ + topics~$LogForwardInclude or message~$LogForwardIncludeMessage ] do={ + :set MessageVal [ /log/get $Message ]; + :local Bullet "information"; + + :if ($Last < [ $HexToNum ($MessageVal->".id") ]) do={ + :local DupCount ($MessageDups->($MessageVal->"message")); + :if ($MessageVal->"topics" ~ "(warning)") do={ + :set Warning true; + :set Bullet "large-orange-circle"; + } + :if ($MessageVal->"topics" ~ "(emergency|alert|critical|error)") do={ + :set Warning true; + :set Bullet "large-red-circle"; + } + :if ($DupCount < 3) do={ + :set Messages ($Messages . "\n" . [ $SymbolForNotification $Bullet ] . \ + $MessageVal->"time" . " " . [ :tostr ($MessageVal->"topics") ] . " " . $MessageVal->"message"); + } else={ + :set Duplicates true; + } + :set ($MessageDups->($MessageVal->"message")) ($DupCount + 1); + :set Count ($Count + 1); + } + } + + :if ($Count > 0) do={ + :set LogForwardRateLimit ($LogForwardRateLimit + 10); + + $SendNotification2 ({ origin=$ScriptName; \ + subject=([ $SymbolForNotification [ $IfThenElse ($Warning = true) "warning-sign" "memo" ] ] . \ + "Log Forwarding"); \ + message=("The log on " . $Identity . " contains " . [ $IfThenElse ($Count = 1) "this message" \ + ("these " . $Count . " messages") ] . " after " . [ /system/resource/get uptime ] . " uptime." . \ + [ $IfThenElse ($Duplicates = true) (" Multi-repeated messages have been skipped.") ] . \ + [ $IfThenElse ($LogForwardRateLimit > 30) ("\nRate limit in action, delaying forwarding.") ] . \ + "\n" . $Messages) }); + } else={ + :set LogForwardRateLimit [ $MAX 0 ($LogForwardRateLimit - 1) ]; + } + + :local LogAll [ /log/find ]; + :set LogForwardLast ($LogAll->([ :len $LogAll ] - 1) ); +} on-error={ + :global ExitError; $ExitError $ExitOK [ :jobname ]; } diff --git a/mod/bridge-port-to.rsc b/mod/bridge-port-to.rsc index 86689c9..39a036e 100644 --- a/mod/bridge-port-to.rsc +++ b/mod/bridge-port-to.rsc @@ -1,18 +1,20 @@ #!rsc by RouterOS # RouterOS script: mod/bridge-port-to -# Copyright (c) 2013-2023 Christian Hesse -# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md +# Copyright (c) 2013-2025 Christian Hesse +# https://rsc.eworm.de/COPYING.md +# +# requires RouterOS, version=7.15 # # reset bridge ports to default bridge -# https://git.eworm.de/cgit/routeros-scripts/about/doc/mod/bridge-port-to.md +# https://rsc.eworm.de/doc/mod/bridge-port-to.md :global BridgePortTo; -:set BridgePortTo do={ +:set BridgePortTo do={ :do { :local BridgePortTo [ :tostr $1 ]; :global IfThenElse; - :global LogPrintExit2; + :global LogPrint; :global ParseKeyValueStore; :local InterfaceReEnable ({}); @@ -24,21 +26,22 @@ :if ($BridgeDefault = "dhcp-client") do={ :if ([ :len $DHCPClient ] != 1) do={ - $LogPrintExit2 warning $0 ([ $IfThenElse ([ :len $DHCPClient ] = 0) "Missing" "Duplicate" ] . \ - " dhcp client configuration for interface " . $BridgePortVal->"interface" . "!") true; + $LogPrint warning $0 ([ $IfThenElse ([ :len $DHCPClient ] = 0) "Missing" "Duplicate" ] . \ + " dhcp client configuration for interface " . $BridgePortVal->"interface" . "!"); + :return false; } :local DHCPClientDisabled [ /ip/dhcp-client/get $DHCPClient disabled ]; :if ($BridgePortVal->"disabled" = false || $DHCPClientDisabled = true) do={ - $LogPrintExit2 info $0 ("Disabling bridge port for interface " . $BridgePortVal->"interface" . ", enabling dhcp client.") false; + $LogPrint info $0 ("Disabling bridge port for interface " . $BridgePortVal->"interface" . ", enabling dhcp client."); /interface/bridge/port/disable $BridgePort; :delay 200ms; /ip/dhcp-client/enable $DHCPClient; } } else={ :if ($BridgePortVal->"disabled" = true || $BridgeDefault != $BridgePortVal->"bridge") do={ - $LogPrintExit2 info $0 ("Enabling bridge port for interface " . $BridgePortVal->"interface" . ", changing to " . $BridgePortTo . \ - " bridge " . $BridgeDefault . ", disabling dhcp client.") false; + $LogPrint info $0 ("Enabling bridge port for interface " . $BridgePortVal->"interface" . ", changing to " . $BridgePortTo . \ + " bridge " . $BridgeDefault . ", disabling dhcp client."); :if ([ :len $DHCPClient ] = 1) do={ /ip/dhcp-client/disable $DHCPClient; :delay 200ms; @@ -50,8 +53,8 @@ } /interface/bridge/port/set disabled=no bridge=$BridgeDefault $BridgePort; } else={ - $LogPrintExit2 debug $0 ("Interface " . $BridgePortVal->"interface" . " already connected to " . $BridgePortTo . \ - " bridge " . $BridgeDefault . ".") false; + $LogPrint debug $0 ("Interface " . $BridgePortVal->"interface" . " already connected to " . $BridgePortTo . \ + " bridge " . $BridgeDefault . "."); } } } @@ -59,7 +62,9 @@ } :if ([ :len $InterfaceReEnable ] > 0) do={ :delay 5s; - $LogPrintExit2 info $0 ("Re-enabling interfaces...") false; + $LogPrint info $0 ("Re-enabling interfaces..."); /interface/ethernet/enable $InterfaceReEnable; } -} +} on-error={ + :global ExitError; $ExitError false $0; +} } diff --git a/mod/bridge-port-vlan.rsc b/mod/bridge-port-vlan.rsc index 792a6c4..0eeb9b5 100644 --- a/mod/bridge-port-vlan.rsc +++ b/mod/bridge-port-vlan.rsc @@ -1,18 +1,20 @@ #!rsc by RouterOS # RouterOS script: mod/bridge-port-vlan -# Copyright (c) 2013-2023 Christian Hesse -# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md +# Copyright (c) 2013-2025 Christian Hesse +# https://rsc.eworm.de/COPYING.md +# +# requires RouterOS, version=7.15 # # manage VLANs on bridge ports -# https://git.eworm.de/cgit/routeros-scripts/about/doc/mod/bridge-port-vlan.md +# https://rsc.eworm.de/doc/mod/bridge-port-vlan.md :global BridgePortVlan; -:global BridgePortVlan do={ +:global BridgePortVlan do={ :do { :local ConfigTo [ :tostr $1 ]; :global IfThenElse; - :global LogPrintExit2; + :global LogPrint; :global ParseKeyValueStore; :local InterfaceReEnable ({}); @@ -24,13 +26,14 @@ :if ($Vlan = "dhcp-client") do={ :if ([ :len $DHCPClient ] != 1) do={ - $LogPrintExit2 warning $0 ([ $IfThenElse ([ :len $DHCPClient ] = 0) "Missing" "Duplicate" ] . \ - " dhcp client configuration for interface " . $BridgePortVal->"interface" . "!") true; + $LogPrint warning $0 ([ $IfThenElse ([ :len $DHCPClient ] = 0) "Missing" "Duplicate" ] . \ + " dhcp client configuration for interface " . $BridgePortVal->"interface" . "!"); + :return false; } :local DHCPClientDisabled [ /ip/dhcp-client/get $DHCPClient disabled ]; :if ($BridgePortVal->"disabled" = false || $DHCPClientDisabled = true) do={ - $LogPrintExit2 info $0 ("Disabling bridge port for interface " . $BridgePortVal->"interface" . ", enabling dhcp client.") false; + $LogPrint info $0 ("Disabling bridge port for interface " . $BridgePortVal->"interface" . ", enabling dhcp client."); /interface/bridge/port/disable $BridgePort; :delay 200ms; /ip/dhcp-client/enable $DHCPClient; @@ -41,12 +44,13 @@ :do { :set $Vlan ([ /interface/bridge/vlan/get [ find where comment=$Vlan ] vlan-ids ]->0); } on-error={ - $LogPrintExit2 warning $0 ("Could not find VLAN '" . $Vlan . "' for interface " . $BridgePortVal->"interface" . "!") true; + $LogPrint warning $0 ("Could not find VLAN '" . $Vlan . "' for interface " . $BridgePortVal->"interface" . "!"); + :return false; } } :if ($BridgePortVal->"disabled" = true || $Vlan != $BridgePortVal->"pvid") do={ - $LogPrintExit2 info $0 ("Enabling bridge port for interface " . $BridgePortVal->"interface" . ", changing to " . $ConfigTo . \ - " vlan " . $Vlan . [ $IfThenElse ($Vlan != $VlanName) (" (" . $VlanName . ")") ] . ", disabling dhcp client.") false; + $LogPrint info $0 ("Enabling bridge port for interface " . $BridgePortVal->"interface" . ", changing to " . $ConfigTo . \ + " vlan " . $Vlan . [ $IfThenElse ($Vlan != $VlanName) (" (" . $VlanName . ")") ] . ", disabling dhcp client."); :if ([ :len $DHCPClient ] = 1) do={ /ip/dhcp-client/disable $DHCPClient; :delay 200ms; @@ -58,8 +62,8 @@ } /interface/bridge/port/set disabled=no pvid=$Vlan $BridgePort; } else={ - $LogPrintExit2 debug $0 ("Interface " . $BridgePortVal->"interface" . " already connected to " . $ConfigTo . \ - " vlan " . $Vlan . ".") false; + $LogPrint debug $0 ("Interface " . $BridgePortVal->"interface" . " already connected to " . $ConfigTo . \ + " vlan " . $Vlan . "."); } } } @@ -67,7 +71,9 @@ } :if ([ :len $InterfaceReEnable ] > 0) do={ :delay 5s; - $LogPrintExit2 info $0 ("Re-enabling interfaces...") false; + $LogPrint info $0 ("Re-enabling interfaces..."); /interface/ethernet/enable $InterfaceReEnable; } -} +} on-error={ + :global ExitError; $ExitError false $0; +} } diff --git a/mod/inspectvar.rsc b/mod/inspectvar.rsc index e06d799..c861557 100644 --- a/mod/inspectvar.rsc +++ b/mod/inspectvar.rsc @@ -1,21 +1,24 @@ #!rsc by RouterOS # RouterOS script: mod/inspectvar -# Copyright (c) 2020-2023 Christian Hesse -# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md +# Copyright (c) 2020-2025 Christian Hesse +# https://rsc.eworm.de/COPYING.md +# +# requires RouterOS, version=7.15 # # inspect variables -# https://git.eworm.de/cgit/routeros-scripts/about/doc/mod/inspectvar.md +# https://rsc.eworm.de/doc/mod/inspectvar.md :global InspectVar; :global InspectVarReturn; # inspect variable and print on terminal -:set InspectVar do={ +:set InspectVar do={ :do { :global InspectVarReturn; - :global PrettyPrint; - $PrettyPrint [ $InspectVarReturn $1 ]; -} + :put [ :tocrlf [ $InspectVarReturn $1 ] ]; +} on-error={ + :global ExitError; $ExitError false $0; +} } # inspect variable and return formatted string :set InspectVarReturn do={ @@ -39,7 +42,7 @@ :local TypeOf [ :typeof $Input ]; :local Return [ $IndentReturn "type" $TypeOf $Level ]; - + :if ($TypeOf = "array") do={ :foreach Key,Value in=$Input do={ :set $Return ($Return . "\n" . \ diff --git a/mod/ipcalc.rsc b/mod/ipcalc.rsc index eaa5c97..477cf4a 100644 --- a/mod/ipcalc.rsc +++ b/mod/ipcalc.rsc @@ -1,32 +1,35 @@ #!rsc by RouterOS # RouterOS script: mod/ipcalc -# Copyright (c) 2020-2023 Christian Hesse -# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md +# Copyright (c) 2020-2025 Christian Hesse +# https://rsc.eworm.de/COPYING.md +# +# requires RouterOS, version=7.15 # # ip address calculation -# https://git.eworm.de/cgit/routeros-scripts/about/doc/mod/ipcalc.md +# https://rsc.eworm.de/doc/mod/ipcalc.md :global IPCalc; :global IPCalcReturn; # print netmask, network, min host, max host and broadcast -:set IPCalc do={ +:set IPCalc do={ :do { :local Input [ :tostr $1 ]; :global FormatLine; :global IPCalcReturn; - :global PrettyPrint; :local Values [ $IPCalcReturn $1 ]; - $PrettyPrint ( \ + :put [ :tocrlf ( \ [ $FormatLine "Address" ($Values->"address") ] . "\n" . \ [ $FormatLine "Netmask" ($Values->"netmask") ] . "\n" . \ [ $FormatLine "Network" ($Values->"network") ] . "\n" . \ [ $FormatLine "HostMin" ($Values->"hostmin") ] . "\n" . \ [ $FormatLine "HostMax" ($Values->"hostmax") ] . "\n" . \ - [ $FormatLine "Broadcast" ($Values->"broadcast") ]); -} + [ $FormatLine "Broadcast" ($Values->"broadcast") ]) ]; +} on-error={ + :global ExitError; $ExitError false $0; +} } # calculate and return netmask, network, min host, max host and broadcast :set IPCalcReturn do={ diff --git a/mod/notification-email.rsc b/mod/notification-email.rsc index a505a50..7b89d98 100644 --- a/mod/notification-email.rsc +++ b/mod/notification-email.rsc @@ -1,11 +1,15 @@ #!rsc by RouterOS # RouterOS script: mod/notification-email -# Copyright (c) 2013-2023 Christian Hesse -# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md +# Copyright (c) 2013-2025 Christian Hesse +# https://rsc.eworm.de/COPYING.md +# +# requires RouterOS, version=7.15 +# requires device-mode, email, scheduler # # send notifications via e-mail -# https://git.eworm.de/cgit/routeros-scripts/about/doc/mod/notification-email.md +# https://rsc.eworm.de/doc/mod/notification-email.md +:global EMailGenerateFrom; :global FlushEmailQueue; :global LogForwardFilterLogForwarding; :global NotificationEMailSubject; @@ -15,44 +19,73 @@ :global SendEMail; :global SendEMail2; +# generate from-property with display name +:set EMailGenerateFrom do={ + :global Identity; + + :global CleanName; + + :local From [ /tool/e-mail/get from ]; + + :if ($From ~ "<.*>\$") do={ + :return $From; + } + + :return ([ $CleanName $Identity ] . " via routeros-scripts <" . $From . ">"); +} + # flush e-mail queue -:set FlushEmailQueue do={ +:set FlushEmailQueue do={ :do { :global EmailQueue; :global EitherOr; + :global EMailGenerateFrom; :global IsDNSResolving; :global IsTimeSync; - :global LogPrintExit2; + :global LogPrint; :local AllDone true; :local QueueLen [ :len $EmailQueue ]; - :local Scheduler [ /system/scheduler/find where name=$0 ]; - - :if ([ :len $Scheduler ] > 0 && [ /system/scheduler/get $Scheduler interval ] < 1m) do={ + :local Scheduler [ /system/scheduler/find where name="_FlushEmailQueue" ]; + + :if ([ :len $Scheduler ] > 0 && $QueueLen = 0) do={ + $LogPrint warning $0 ("Flushing E-Mail messages from scheduler, but queue is empty."); + /system/scheduler/remove $Scheduler; + :return false; + } + + :if ($QueueLen = 0) do={ + :return true; + } + + :if ([ :len $Scheduler ] < 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; } :if ([ /tool/e-mail/get last-status ] = "in-progress") do={ - $LogPrintExit2 debug $0 ("Sending mail is currently in progress, not flushing.") false; + $LogPrint debug $0 ("Sending mail is currently in progress, not flushing."); :return false; } :if ([ $IsTimeSync ] = false) do={ - $LogPrintExit2 debug $0 ("Time is not synced, not flushing.") false; + $LogPrint debug $0 ("Time is not synced, not flushing."); :return false; } :local EMailSettings [ /tool/e-mail/get ]; - :if ([ :typeof [ :toip [ $EitherOr ($EMailSettings->"server") ($EMailSettings->"address") ] ] ] != "ip" && [ $IsDNSResolving ] = false) do={ - $LogPrintExit2 debug $0 ("Server address is a DNS name and resolving fails, not flushing.") false; + :if ([ :typeof [ :toip ($EMailSettings->"server") ] ] != "ip" && [ $IsDNSResolving ] = false) do={ + $LogPrint debug $0 ("Server address is a DNS name and resolving fails, not flushing."); :return false; } - :if ([ :len $Scheduler ] > 0 && $QueueLen = 0) do={ - $LogPrintExit2 warning $0 ("Flushing E-Mail messages from scheduler, but queue is empty.") false; - } - - /system/scheduler/set interval=([ $EitherOr $QueueLen 1 ] . "m") comment="Sending..." $Scheduler; + /system/scheduler/set interval=($QueueLen . "m") comment="Sending..." $Scheduler; :foreach Id,Message in=$EmailQueue do={ :if ([ :typeof $Message ] = "array" ) do={ @@ -62,11 +95,11 @@ :if ([ :len [ /file/find where name=$File ] ] = 1) do={ :set Attach ($Attach, $File); } else={ - $LogPrintExit2 warning $0 ("File '" . $File . "' does not exist, can not attach.") false; + $LogPrint warning $0 ("File '" . $File . "' does not exist, can not attach."); } } - /tool/e-mail/send to=($Message->"to") cc=($Message->"cc") subject=($Message->"subject") \ - body=($Message->"body") file=$Attach; + /tool/e-mail/send from=[ $EMailGenerateFrom ] to=($Message->"to") cc=($Message->"cc") \ + subject=($Message->"subject") body=($Message->"body") file=$Attach; :local Wait true; :do { :delay 1s; @@ -91,10 +124,20 @@ :if ($AllDone = true && $QueueLen = [ :len $EmailQueue ]) do={ /system/scheduler/remove $Scheduler; :set EmailQueue; - } else={ - /system/scheduler/set interval=1m comment="Waiting for retry..." $Scheduler; + :return true; } -} + + :if ([ :len [ /system/scheduler/find where name="_FlushEmailQueue" ] ] = 0 && \ + [ :typeof $EmailQueue ] = "nothing") do={ + $LogPrint info $0 ("Queue was purged? Exiting."); + :return false; + } + + /system/scheduler/set interval=(($SchedVal->"run-count") . "m") \ + comment="Waiting for retry..." $Scheduler; +} on-error={ + :global ExitError; $ExitError false $0; +} } # generate filter for log-forward :set LogForwardFilterLogForwarding do={ @@ -131,20 +174,21 @@ :global EitherOr; :global IfThenElse; + :global NotificationEMailSignature; :global NotificationEMailSubject; :local To [ $EitherOr ($EmailGeneralToOverride->($Notification->"origin")) $EmailGeneralTo ]; :local Cc [ $EitherOr ($EmailGeneralCcOverride->($Notification->"origin")) $EmailGeneralCc ]; :local EMailSettings [ /tool/e-mail/get ]; - :if ([ :len $To ] = 0 || [ $EitherOr ($EMailSettings->"server") ($EMailSettings->"address") ] = "0.0.0.0" || ($EMailSettings->"from") = "<>") do={ + :if ([ :len $To ] = 0 || ($EMailSettings->"server") = "0.0.0.0" || ($EMailSettings->"from") = "<>") do={ :return false; } :if ([ :typeof $EmailQueue ] = "nothing") do={ :set EmailQueue ({}); } - :local Signature [ /system/note/get note ]; + :local Signature [ $EitherOr [ $NotificationEMailSignature ] [ /system/note/get note ] ]; :set ($EmailQueue->[ :len $EmailQueue ]) { to=$To; cc=$Cc; subject=[ $NotificationEMailSubject ($Notification->"subject") ]; @@ -152,8 +196,8 @@ [ $IfThenElse ([ :len ($Notification->"link") ] > 0) ("\n\n" . ($Notification->"link")) "" ] . \ [ $IfThenElse ([ :len $Signature ] > 0) ("\n-- \n" . $Signature) "" ]); \ attach=($Notification->"attach"); remove-attach=($Notification->"remove-attach") }; - :if ([ :len [ /system/scheduler/find where name="\$FlushEmailQueue" ] ] = 0) do={ - /system/scheduler/add name="\$FlushEmailQueue" interval=1s start-time=startup \ + :if ([ :len [ /system/scheduler/find where name="_FlushEmailQueue" ] ] = 0) do={ + /system/scheduler/add name="_FlushEmailQueue" interval=1s start-time=startup \ comment="Queuing new mail..." on-event=(":global FlushEmailQueue; \$FlushEmailQueue;"); } } @@ -162,7 +206,7 @@ :set PurgeEMailQueue do={ :global EmailQueue; - /system/scheduler/remove [ find where name="\$FlushEmailQueue" ]; + /system/scheduler/remove [ find where name="_FlushEmailQueue" ]; :set EmailQueue; } @@ -170,27 +214,28 @@ :global QuotedPrintable do={ :local Input [ :tostr $1 ]; + :global CharacterMultiply; + :if ([ :len $Input ] = 0) do={ :return $Input; } :local Return ""; - :local Chars ("\80\81\82\83\84\85\86\87\88\89\8A\8B\8C\8D\8E\8F\90\91\92\93\94\95\96\97" . \ - "\98\99\9A\9B\9C\9D\9E\9F\A0\A1\A2\A3\A4\A5\A6\A7\A8\A9\AA\AB\AC\AD\AE\AF\B0\B1\B2\B3" . \ - "\B4\B5\B6\B7\B8\B9\BA\BB\BC\BD\BE\BF\C0\C1\C2\C3\C4\C5\C6\C7\C8\C9\CA\CB\CC\CD\CE\CF" . \ - "\D0\D1\D2\D3\D4\D5\D6\D7\D8\D9\DA\DB\DC\DD\DE\DF\E0\E1\E2\E3\E4\E5\E6\E7\E8\E9\EA\EB" . \ - "\EC\ED\EE\EF\F0\F1\F2\F3\F4\F5\F6\F7\F8\F9\FA\FB\FC\FD\FE\FF"); - :local Hex { "0"; "1"; "2"; "3"; "4"; "5"; "6"; "7"; "8"; "9"; "A"; "B"; "C"; "D"; "E"; "F" }; + :local Chars ( \ + "\00\01\02\03\04\05\06\07\08\09\0A\0B\0C\0D\0E\0F\10\11\12\13\14\15\16\17\18\19\1A\1B\1C\1D\1E\1F" . \ + [ $CharacterMultiply ("\00") 29 ] . "=\00?" . [ $CharacterMultiply ("\00") 63 ] . "\7F" . \ + "\80\81\82\83\84\85\86\87\88\89\8A\8B\8C\8D\8E\8F\90\91\92\93\94\95\96\97\98\99\9A\9B\9C\9D\9E\9F" . \ + "\A0\A1\A2\A3\A4\A5\A6\A7\A8\A9\AA\AB\AC\AD\AE\AF\B0\B1\B2\B3\B4\B5\B6\B7\B8\B9\BA\BB\BC\BD\BE\BF" . \ + "\C0\C1\C2\C3\C4\C5\C6\C7\C8\C9\CA\CB\CC\CD\CE\CF\D0\D1\D2\D3\D4\D5\D6\D7\D8\D9\DA\DB\DC\DD\DE\DF" . \ + "\E0\E1\E2\E3\E4\E5\E6\E7\E8\E9\EA\EB\EC\ED\EE\EF\F0\F1\F2\F3\F4\F5\F6\F7\F8\F9\FA\FB\FC\FD\FE\FF"); + :local Hex "0123456789ABCDEF"; :for I from=0 to=([ :len $Input ] - 1) do={ :local Char [ :pick $Input $I ]; :local Replace [ :find $Chars $Char ]; - :if ($Char = "=") do={ - :set Char "=3D"; - } :if ([ :typeof $Replace ] = "num") do={ - :set Char ("=" . ($Hex->($Replace / 16 + 8)) . ($Hex->($Replace % 16))); + :set Char ("=" . [ :pick $Hex ($Replace / 16)] . [ :pick $Hex ($Replace % 16) ]); } :set Return ($Return . $Char); } @@ -203,11 +248,13 @@ } # send notification via e-mail - expects at least two string arguments -:set SendEMail do={ +:set SendEMail do={ :do { :global SendEMail2; - $SendEMail2 ({ subject=$1; message=$2; link=$3 }); -} + $SendEMail2 ({ origin=$0; subject=$1; message=$2; link=$3 }); +} on-error={ + :global ExitError; $ExitError false $0; +} } # send notification via e-mail - expects one array argument :set SendEMail2 do={ diff --git a/mod/notification-matrix.rsc b/mod/notification-matrix.rsc index a8983a7..e989ee0 100644 --- a/mod/notification-matrix.rsc +++ b/mod/notification-matrix.rsc @@ -1,11 +1,14 @@ #!rsc by RouterOS # RouterOS script: mod/notification-matrix -# Copyright (c) 2013-2023 Michael Gisbers +# Copyright (c) 2013-2025 Michael Gisbers # Christian Hesse -# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md +# https://rsc.eworm.de/COPYING.md +# +# requires RouterOS, version=7.15 +# requires device-mode, fetch, scheduler # # send notifications via Matrix -# https://git.eworm.de/cgit/routeros-scripts/about/doc/mod/notification-matrix.md +# https://rsc.eworm.de/doc/mod/notification-matrix.md :global FlushMatrixQueue; :global NotificationFunctions; @@ -16,46 +19,48 @@ :global SetupMatrixJoinRoom; # flush Matrix queue -:set FlushMatrixQueue do={ +:set FlushMatrixQueue do={ :do { :global MatrixQueue; :global IsFullyConnected; - :global LogPrintExit2; + :global LogPrint; :if ([ $IsFullyConnected ] = false) do={ - $LogPrintExit2 debug $0 ("System is not fully connected, not flushing.") false; + $LogPrint debug $0 ("System is not fully connected, not flushing."); :return false; } :local AllDone true; :local QueueLen [ :len $MatrixQueue ]; - :if ([ :len [ /system/scheduler/find where name=$0 ] ] > 0 && $QueueLen = 0) do={ - $LogPrintExit2 warning $0 ("Flushing Matrix messages from scheduler, but queue is empty.") false; + :if ([ :len [ /system/scheduler/find where name="_FlushMatrixQueue" ] ] > 0 && $QueueLen = 0) do={ + $LogPrint warning $0 ("Flushing Matrix messages from scheduler, but queue is empty."); } :foreach Id,Message in=$MatrixQueue do={ :if ([ :typeof $Message ] = "array" ) do={ :do { - /tool/fetch check-certificate=yes-without-crl output=none http-method=post \ - ("https://" . $Message->"homeserver" . "/_matrix/client/r0/rooms/" . $Message->"room" . \ - "/send/m.room.message?access_token=" . $Message->"accesstoken") \ - http-data=("{ \"msgtype\": \"m.text\", \"body\": \"" . $Message->"plain" . "\"," . \ - "\"format\": \"org.matrix.custom.html\", \"formatted_body\": \"" . \ - $Message->"formatted" . "\" }") as-value; + /tool/fetch check-certificate=yes-without-crl output=none \ + http-header-field=($Message->"headers") http-method=post \ + http-data=[ :serialize to=json { "msgtype"="m.text"; "body"=($Message->"plain"); + "format"="org.matrix.custom.html"; "formatted_body"=($Message->"formatted") } ] \ + ("https://" . $Message->"homeserver" . "/_matrix/client/r0/rooms/" . $Message->"room" . \ + "/send/m.room.message?access_token=" . $Message->"accesstoken") as-value; :set ($MatrixQueue->$Id); } on-error={ - $LogPrintExit2 debug $0 ("Sending queued Matrix message failed.") false; + $LogPrint debug $0 ("Sending queued Matrix message failed."); :set AllDone false; } } } :if ($AllDone = true && $QueueLen = [ :len $MatrixQueue ]) do={ - /system/scheduler/remove [ find where name=$0 ]; + /system/scheduler/remove [ find where name="_FlushMatrixQueue" ]; :set MatrixQueue; } -} +} on-error={ + :global ExitError; $ExitError false $0; +} } # send notification via Matrix - expects one array argument :set ($NotificationFunctions->"matrix") do={ @@ -72,7 +77,9 @@ :global MatrixRoomOverride; :global EitherOr; - :global LogPrintExit2; + :global FetchUserAgentStr; + :global LogPrint; + :global ProtocolStrip; :global SymbolForNotification; :local PrepareText do={ @@ -83,21 +90,15 @@ } :local Return ""; - :local Chars { - "plain"={ "\\"; "\""; "\n" }; - "format"={ "\\"; "\""; "\n"; "&"; "<"; ">" }; - } - :local Subs { - "plain"={ "\\\\"; "\\\""; "\\n" }; - "format"={ "\\\\"; """; "
"; "&"; "<"; ">" }; - } + :local Chars { "\""; "\n"; "&"; "<"; ">" }; + :local Subs { """; "
"; "&"; "<"; ">" }; :for I from=0 to=([ :len $Input ] - 1) do={ :local Char [ :pick $Input $I ]; - :local Replace [ :find ($Chars->$2) $Char ]; + :local Replace [ :find $Chars $Char ]; :if ([ :typeof $Replace ] = "num") do={ - :set Char ($Subs->$2->$Replace); + :set Char ($Subs->$Replace); } :set Return ($Return . $Char); } @@ -113,42 +114,45 @@ :return false; } - :local Plain [ $PrepareText ("## [" . $IdentityExtra . $Identity . "] " . \ - ($Notification->"subject") . "\n```\n" . ($Notification->"message") . "\n```") "plain" ]; + :local Headers ({ [ $FetchUserAgentStr ($Notification->"origin") ] }); + :local Plain ("## [" . $IdentityExtra . $Identity . "] " . \ + ($Notification->"subject") . "\n```\n" . ($Notification->"message") . "\n```"); :local Formatted ("

" . [ $PrepareText ("[" . $IdentityExtra . $Identity . "] " . \ - ($Notification->"subject")) "format" ] . "

" . "
" . \
-    [ $PrepareText ($Notification->"message") "format" ] . "
"); + ($Notification->"subject")) ] . "" . "
" . \
+    [ $PrepareText ($Notification->"message") ] . "
"); :if ([ :len ($Notification->"link") ] > 0) do={ - :set Plain ($Plain . "\\n" . [ $SymbolForNotification "link" ] . \ - [ $PrepareText ("[" . $Notification->"link" . "](" . $Notification->"link" . ")") "plain" ]); + :local Label [ $ProtocolStrip ($Notification->"link") ]; + :set Plain ($Plain . "\n" . [ $SymbolForNotification "link" ] . \ + "[" . $Label . "](" . $Notification->"link" . ")"); :set Formatted ($Formatted . "
" . [ $SymbolForNotification "link" ] . \ - ""link") "format" ] . "\\\">" . \ - [ $PrepareText ($Notification->"link") "format" ] . ""); + ""link") ] . "\">" . \ + [ $PrepareText $Label ] . ""); } :do { - /tool/fetch check-certificate=yes-without-crl output=none http-method=post \ - ("https://" . $HomeServer . "/_matrix/client/r0/rooms/" . $Room . \ - "/send/m.room.message?access_token=" . $AccessToken) \ - http-data=("{ \"msgtype\": \"m.text\", \"body\": \"" . $Plain . "\"," . \ - "\"format\": \"org.matrix.custom.html\", \"formatted_body\": \"" . \ - $Formatted . "\" }") as-value; + /tool/fetch check-certificate=yes-without-crl output=none \ + http-header-field=$Headers http-method=post \ + http-data=[ :serialize to=json { "msgtype"="m.text"; "body"=$Plain; + "format"="org.matrix.custom.html"; "formatted_body"=$Formatted } ] \ + ("https://" . $HomeServer . "/_matrix/client/r0/rooms/" . $Room . \ + "/send/m.room.message?access_token=" . $AccessToken) as-value; } on-error={ - $LogPrintExit2 info $0 ("Failed sending Matrix notification! Queuing...") false; + $LogPrint info $0 ("Failed sending Matrix notification! Queuing..."); :if ([ :typeof $MatrixQueue ] = "nothing") do={ :set MatrixQueue ({}); } - :local Text ([ $SymbolForNotification "alarm-clock" ] . \ - "This message was queued since " . [ /system/clock/get date ] . \ - " " . [ /system/clock/get time ] . " and may be obsolete."); - :set Plain ($Plain . "\\n" . $Text); - :set Formatted ($Formatted . "
" . $Text); - :set ($MatrixQueue->[ :len $MatrixQueue ]) { room=$Room; \ - accesstoken=$AccessToken; homeserver=$HomeServer; \ - plain=$Plain; formatted=$Formatted }; - :if ([ :len [ /system/scheduler/find where name="\$FlushMatrixQueue" ] ] = 0) do={ - /system/scheduler/add name="\$FlushMatrixQueue" interval=1m start-time=startup \ + :local Symbol [ $SymbolForNotification "alarm-clock" ]; + :local DateTime ([ /system/clock/get date ] . " " . [ /system/clock/get time ]); + :set Plain ($Plain . "\n" . $Symbol . "This message was queued since *" . \ + $DateTime . "* and may be obsolete."); + :set Formatted ($Formatted . "
" . $Symbol . "This message was queued since " . \ + $DateTime . " and may be obsolete."); + :set ($MatrixQueue->[ :len $MatrixQueue ]) { headers=$Headers; \ + accesstoken=$AccessToken; homeserver=$HomeServer; room=$Room; \ + plain=$Plain; formatted=$Formatted }; + :if ([ :len [ /system/scheduler/find where name="_FlushMatrixQueue" ] ] = 0) do={ + /system/scheduler/add name="_FlushMatrixQueue" interval=1m start-time=startup \ on-event=(":global FlushMatrixQueue; \$FlushMatrixQueue;"); } } @@ -158,16 +162,18 @@ :set PurgeMatrixQueue do={ :global MatrixQueue; - /system/scheduler/remove [ find where name="\$FlushMatrixQueue" ]; + /system/scheduler/remove [ find where name="_FlushMatrixQueue" ]; :set MatrixQueue; } # send notification via Matrix - expects at least two string arguments -:set SendMatrix do={ +:set SendMatrix do={ :do { :global SendMatrix2; - $SendMatrix2 ({ subject=$1; message=$2; link=$3 }); -} + $SendMatrix2 ({ origin=$0; subject=$1; message=$2; link=$3 }); +} on-error={ + :global ExitError; $ExitError false $0; +} } # send notification via Matrix - expects one array argument :set SendMatrix2 do={ @@ -183,10 +189,8 @@ :local User [ :tostr $1 ]; :local Pass [ :tostr $2 ]; - :global CharacterReplace; - :global LogPrintExit2; - :global ParseJson; - :global UrlEncode; + :global FetchUserAgentStr; + :global LogPrint; :global MatrixAccessToken; :global MatrixHomeServer; @@ -194,11 +198,13 @@ :local Domain [ :pick $User ([ :find $User ":" ] + 1) [ :len $User] ]; :do { :local Data ([ /tool/fetch check-certificate=yes-without-crl output=user \ + http-header-field=({ [ $FetchUserAgentStr $0 ] }) \ ("https://" . $Domain . "/.well-known/matrix/client") as-value ]->"data"); - :set MatrixHomeServer ([ $ParseJson ([ $ParseJson [ $CharacterReplace $Data " " "" ] ]->"m.homeserver") ]->"base_url"); - $LogPrintExit2 debug $0 ("Home server is: " . $MatrixHomeServer) false; + :set MatrixHomeServer ([ :deserialize from=json value=$Data ]->"m.homeserver"->"base_url"); + $LogPrint debug $0 ("Home server is: " . $MatrixHomeServer); } on-error={ - $LogPrintExit2 error $0 ("Failed getting home server!") true; + $LogPrint error $0 ("Failed getting home server!"); + :return false; } :if ([ :pick $MatrixHomeServer 0 8 ] = "https://") do={ @@ -207,21 +213,26 @@ :do { :local Data ([ /tool/fetch check-certificate=yes-without-crl output=user \ - http-method=post http-data=("{\"type\":\"m.login.password\", \"user\":\"" . $User . "\", \"password\":\"" . $Pass . "\"}") \ + http-header-field=({ [ $FetchUserAgentStr $0 ] }) http-method=post \ + http-data=[ :serialize to=json { "type"="m.login.password"; "user"=$User; "password"=$Pass } ] \ ("https://" . $MatrixHomeServer . "/_matrix/client/r0/login") as-value ]->"data"); - :set MatrixAccessToken ([ $ParseJson $Data ]->"access_token"); - $LogPrintExit2 debug $0 ("Access token is: " . $MatrixAccessToken) false; + :set MatrixAccessToken ([ :deserialize from=json value=$Data ]->"access_token"); + $LogPrint debug $0 ("Access token is: " . $MatrixAccessToken); } on-error={ - $LogPrintExit2 error $0 ("Failed logging in (and getting access token)!") true; + $LogPrint error $0 ("Failed logging in (and getting access token)!"); + :return false; } :do { - /system/script/set global-config-overlay source=([ get global-config-overlay source ] . "\n" . \ + /system/script/remove [ find where name="global-config-overlay.d/mod/notification-matrix" ]; + /system/script/add name="global-config-overlay.d/mod/notification-matrix" source=( \ + "# configuration snippet: mod/notification-matrix\n\n" . \ ":global MatrixHomeServer \"" . $MatrixHomeServer . "\";\n" . \ ":global MatrixAccessToken \"" . $MatrixAccessToken . "\";\n"); - $LogPrintExit2 info $0 ("Appended configuration to global-config-overlay. Now create and join a room, please!") false; + $LogPrint info $0 ("Added configuration snippet. Now create and join a room, please!"); } on-error={ - $LogPrintExit2 error $0 ("Failed appending configuration to global-config-overlay!") true; + $LogPrint error $0 ("Failed adding configuration snippet!"); + :return false; } } @@ -229,7 +240,8 @@ :set SetupMatrixJoinRoom do={ :global MatrixRoom [ :tostr $1 ]; - :global LogPrintExit2; + :global FetchUserAgentStr; + :global LogPrint; :global UrlEncode; :global MatrixAccessToken; @@ -238,19 +250,22 @@ :do { /tool/fetch check-certificate=yes-without-crl output=none \ - http-method=post http-data="" \ + http-header-field=({ [ $FetchUserAgentStr $0 ] }) http-method=post http-data="" \ ("https://" . $MatrixHomeServer . "/_matrix/client/r0/rooms/" . [ $UrlEncode $MatrixRoom ] . \ "/join?access_token=" . [ $UrlEncode $MatrixAccessToken ]) as-value; - $LogPrintExit2 debug $0 ("Joined the room.") false; + $LogPrint debug $0 ("Joined the room."); } on-error={ - $LogPrintExit2 error $0 ("Failed joining the room!") true; + $LogPrint error $0 ("Failed joining the room!"); + :return false; } :do { - /system/script/set global-config-overlay source=([ get global-config-overlay source ] . "\n" . \ + :local Snippet [ /system/script/find where name="global-config-overlay.d/mod/notification-matrix" ]; + /system/script/set $Snippet source=([ get $Snippet source ] . \ ":global MatrixRoom \"" . $MatrixRoom . "\";\n"); - $LogPrintExit2 info $0 ("Appended configuration to global-config-overlay. Please review and cleanup!") false; + $LogPrint info $0 ("Appended configuration to configuration snippet. Please review!"); } on-error={ - $LogPrintExit2 error $0 ("Failed appending configuration to global-config-overlay!") true; + $LogPrint error $0 ("Failed appending configuration to snippet!"); + :return false; } } diff --git a/mod/notification-ntfy.rsc b/mod/notification-ntfy.rsc index e2fc558..aac6d6c 100644 --- a/mod/notification-ntfy.rsc +++ b/mod/notification-ntfy.rsc @@ -1,10 +1,13 @@ #!rsc by RouterOS # RouterOS script: mod/notification-ntfy -# Copyright (c) 2013-2023 Christian Hesse -# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md +# Copyright (c) 2013-2025 Christian Hesse +# https://rsc.eworm.de/COPYING.md +# +# requires RouterOS, version=7.15 +# requires device-mode, fetch, scheduler # # send notifications via Ntfy (ntfy.sh) -# https://git.eworm.de/cgit/routeros-scripts/about/doc/mod/notification-ntfy.md +# https://rsc.eworm.de/doc/mod/notification-ntfy.md :global FlushNtfyQueue; :global NotificationFunctions; @@ -13,43 +16,46 @@ :global SendNtfy2; # flush ntfy queue -:set FlushNtfyQueue do={ +:set FlushNtfyQueue do={ :do { :global NtfyQueue; :global NtfyMessageIDs; :global IsFullyConnected; - :global LogPrintExit2; + :global LogPrint; :if ([ $IsFullyConnected ] = false) do={ - $LogPrintExit2 debug $0 ("System is not fully connected, not flushing.") false; + $LogPrint debug $0 ("System is not fully connected, not flushing."); :return false; } :local AllDone true; :local QueueLen [ :len $NtfyQueue ]; - :if ([ :len [ /system/scheduler/find where name=$0 ] ] > 0 && $QueueLen = 0) do={ - $LogPrintExit2 warning $0 ("Flushing Ntfy messages from scheduler, but queue is empty.") false; + :if ([ :len [ /system/scheduler/find where name="_FlushNtfyQueue" ] ] > 0 && $QueueLen = 0) do={ + $LogPrint warning $0 ("Flushing Ntfy messages from scheduler, but queue is empty."); } :foreach Id,Message in=$NtfyQueue do={ :if ([ :typeof $Message ] = "array" ) do={ :do { /tool/fetch check-certificate=yes-without-crl output=none http-method=post \ - ($Message->"url") http-header-field=($Message->"headers") http-data=($Message->"text") as-value; + http-header-field=($Message->"headers") http-data=($Message->"text") \ + ($Message->"url") as-value; :set ($NtfyQueue->$Id); } on-error={ - $LogPrintExit2 debug $0 ("Sending queued Ntfy message failed.") false; + $LogPrint debug $0 ("Sending queued Ntfy message failed."); :set AllDone false; } } } :if ($AllDone = true && $QueueLen = [ :len $NtfyQueue ]) do={ - /system/scheduler/remove [ find where name=$0 ]; + /system/scheduler/remove [ find where name="_FlushNtfyQueue" ]; :set NtfyQueue; } -} +} on-error={ + :global ExitError; $ExitError false $0; +} } # send notification via ntfy - expects one array argument :set ($NotificationFunctions->"ntfy") do={ @@ -60,41 +66,59 @@ :global NtfyQueue; :global NtfyServer; :global NtfyServerOverride; + :global NtfyServerPass; + :global NtfyServerPassOverride; + :global NtfyServerToken; + :global NtfyServerTokenOverride; + :global NtfyServerUser; + :global NtfyServerUserOverride; :global NtfyTopic; :global NtfyTopicOverride; :global CertificateAvailable; :global EitherOr; + :global FetchUserAgentStr; :global IfThenElse; - :global LogPrintExit2; + :global LogPrint; :global SymbolForNotification; :global UrlEncode; :local Server [ $EitherOr ($NtfyServerOverride->($Notification->"origin")) $NtfyServer ]; + :local User [ $EitherOr ($NtfyServerUserOverride->($Notification->"origin")) $NtfyServerUser ]; + :local Pass [ $EitherOr ($NtfyServerPassOverride->($Notification->"origin")) $NtfyServerPass ]; + :local Token [ $EitherOr ($NtfyServerTokenOverride->($Notification->"origin")) $NtfyServerToken ]; :local Topic [ $EitherOr ($NtfyTopicOverride->($Notification->"origin")) $NtfyTopic ]; :if ([ :len $Topic ] = 0) do={ :return false; } - :local Url ("https://" . $NtfyServer . "/" . [ $UrlEncode $NtfyTopic ]); - :local Headers ({ ("Priority: " . [ $IfThenElse ($Notification->"silent") "low" "default" ]); \ + :local Url ("https://" . $Server . "/" . [ $UrlEncode $Topic ]); + :local Headers ({ [ $FetchUserAgentStr ($Notification->"origin") ]; \ + ("Priority: " . [ $IfThenElse ($Notification->"silent") "low" "default" ]); \ ("Title: " . "[" . $IdentityExtra . $Identity . "] " . ($Notification->"subject")) }); + :if ([ :len $User ] > 0 || [ :len $Pass ] > 0) do={ + :set Headers ($Headers, ("Authorization: Basic " . [ :convert to=base64 ($User . ":" . $Pass) ])); + } + :if ([ :len $Token ] > 0) do={ + :set Headers ($Headers, ("Authorization: Bearer " . $Token)); + } :local Text (($Notification->"message") . "\n"); :if ([ :len ($Notification->"link") ] > 0) do={ :set Text ($Text . "\n" . [ $SymbolForNotification "link" ] . ($Notification->"link")); } :do { - :if ($NtfyServer = "ntfy.sh") do={ - :if ([ $CertificateAvailable "R3" ] = false) do={ - $LogPrintExit2 warning $0 ("Downloading required certificate failed.") true; + :if ($Server = "ntfy.sh") do={ + :if ([ $CertificateAvailable "ISRG Root X1" ] = false) do={ + $LogPrint warning $0 ("Downloading required certificate failed."); + :error false; } } /tool/fetch check-certificate=yes-without-crl output=none http-method=post \ - $Url http-header-field=$Headers http-data=$Text as-value; + http-header-field=$Headers http-data=$Text $Url as-value; } on-error={ - $LogPrintExit2 info $0 ("Failed sending ntfy notification! Queuing...") false; + $LogPrint info $0 ("Failed sending ntfy notification! Queuing..."); :if ([ :typeof $NtfyQueue ] = "nothing") do={ :set NtfyQueue ({}); @@ -102,9 +126,10 @@ :set Text ($Text . "\n" . [ $SymbolForNotification "alarm-clock" ] . \ "This message was queued since " . [ /system/clock/get date ] . " " . \ [ /system/clock/get time ] . " and may be obsolete."); - :set ($NtfyQueue->[ :len $NtfyQueue ]) { url=$Url; headers=$Headers; text=$Text }; - :if ([ :len [ /system/scheduler/find where name="\$FlushNtfyQueue" ] ] = 0) do={ - /system/scheduler/add name="\$FlushNtfyQueue" interval=1m start-time=startup \ + :set ($NtfyQueue->[ :len $NtfyQueue ]) \ + { url=$Url; headers=$Headers; text=$Text }; + :if ([ :len [ /system/scheduler/find where name="_FlushNtfyQueue" ] ] = 0) do={ + /system/scheduler/add name="_FlushNtfyQueue" interval=1m start-time=startup \ on-event=(":global FlushNtfyQueue; \$FlushNtfyQueue;"); } } @@ -114,16 +139,18 @@ :set PurgeNtfyQueue do={ :global NtfyQueue; - /system/scheduler/remove [ find where name="\$FlushNtfyQueue" ]; + /system/scheduler/remove [ find where name="_FlushNtfyQueue" ]; :set NtfyQueue; } # send notification via ntfy - expects at least two string arguments -:set SendNtfy do={ +:set SendNtfy do={ :do { :global SendNtfy2; - $SendNtfy2 ({ subject=$1; message=$2; link=$3; silent=$4 }); -} + $SendNtfy2 ({ origin=$0; subject=$1; message=$2; link=$3; silent=$4 }); +} on-error={ + :global ExitError; $ExitError false $0; +} } # send notification via ntfy - expects one array argument :set SendNtfy2 do={ diff --git a/mod/notification-telegram.rsc b/mod/notification-telegram.rsc index b5729a4..68e913f 100644 --- a/mod/notification-telegram.rsc +++ b/mod/notification-telegram.rsc @@ -1,36 +1,39 @@ #!rsc by RouterOS # RouterOS script: mod/notification-telegram -# Copyright (c) 2013-2023 Christian Hesse -# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md +# Copyright (c) 2013-2025 Christian Hesse +# https://rsc.eworm.de/COPYING.md +# +# requires RouterOS, version=7.15 +# requires device-mode, fetch, scheduler # # send notifications via Telegram -# https://git.eworm.de/cgit/routeros-scripts/about/doc/mod/notification-telegram.md +# https://rsc.eworm.de/doc/mod/notification-telegram.md :global FlushTelegramQueue; +:global GetTelegramChatId; :global NotificationFunctions; :global PurgeTelegramQueue; :global SendTelegram; :global SendTelegram2; # flush telegram queue -:set FlushTelegramQueue do={ +:set FlushTelegramQueue do={ :do { :global TelegramQueue; :global TelegramMessageIDs; :global IsFullyConnected; - :global LogPrintExit2; - :global ParseJson; + :global LogPrint; :if ([ $IsFullyConnected ] = false) do={ - $LogPrintExit2 debug $0 ("System is not fully connected, not flushing.") false; + $LogPrint debug $0 ("System is not fully connected, not flushing."); :return false; } :local AllDone true; :local QueueLen [ :len $TelegramQueue ]; - :if ([ :len [ /system/scheduler/find where name=$0 ] ] > 0 && $QueueLen = 0) do={ - $LogPrintExit2 warning $0 ("Flushing Telegram messages from scheduler, but queue is empty.") false; + :if ([ :len [ /system/scheduler/find where name="_FlushTelegramQueue" ] ] > 0 && $QueueLen = 0) do={ + $LogPrint warning $0 ("Flushing Telegram messages from scheduler, but queue is empty."); } :foreach Id,Message in=$TelegramQueue do={ @@ -38,23 +41,62 @@ :do { :local Data ([ /tool/fetch check-certificate=yes-without-crl output=user http-method=post \ ("https://api.telegram.org/bot" . ($Message->"tokenid") . "/sendMessage") \ - http-data=("chat_id=" . ($Message->"chatid") . "&disable_notification=" . ($Message->"silent") . \ - "&reply_to_message_id=" . ($Message->"replyto") . "&disable_web_page_preview=true" . \ - "&parse_mode=MarkdownV2&text=" . ($Message->"text")) as-value ]->"data"); + http-data=($Message->"http-data") as-value ]->"data"); :set ($TelegramQueue->$Id); - :set ($TelegramMessageIDs->([ $ParseJson ([ $ParseJson $Data ]->"result") ]->"message_id")) 1; + :set ($TelegramMessageIDs->[ :tostr ([ :deserialize from=json value=$Data ]->"result"->"message_id") ]) 1; } on-error={ - $LogPrintExit2 debug $0 ("Sending queued Telegram message failed.") false; + $LogPrint debug $0 ("Sending queued Telegram message failed."); :set AllDone false; } } } :if ($AllDone = true && $QueueLen = [ :len $TelegramQueue ]) do={ - /system/scheduler/remove [ find where name=$0 ]; + /system/scheduler/remove [ find where name="_FlushTelegramQueue" ]; :set TelegramQueue; } -} +} on-error={ + :global ExitError; $ExitError false $0; +} } + +# get the chat id +:set GetTelegramChatId do={ :do { + :global TelegramTokenId; + + :global CertificateAvailable; + :global LogPrint; + + :if ([ $CertificateAvailable "Go Daddy Root Certificate Authority - G2" ] = false) do={ + $LogPrint warning $0 ("Downloading required certificate failed."); + :return false; + } + + :local Data; + :do { + :set Data ([ /tool/fetch check-certificate=yes-without-crl output=user \ + ("https://api.telegram.org/bot" . $TelegramTokenId . "/getUpdates?offset=0" . \ + "&allowed_updates=%5B%22message%22%5D") as-value ]->"data"); + } on-error={ + $LogPrint warning $0 ("Fetching data failed!"); + :return false; + } + + :local JSON [ :deserialize from=json value=$Data ]; + :local Count [ :len ($JSON->"result") ]; + + :if ($Count = 0) do={ + $LogPrint info $0 ("No message received."); + :return false; + } + + :local Message ($JSON->"result"->($Count - 1)->"message"); + $LogPrint info $0 ("The chat id is: " . ($Message->"chat"->"id")); + :if (($Message->"is_topic_message") = true) do={ + $LogPrint info $0 ("The thread id is: " . ($Message->"message_thread_id")); + } +} on-error={ + :global ExitError; $ExitError false $0; +} } # send notification via telegram - expects one array argument :set ($NotificationFunctions->"telegram") do={ @@ -66,6 +108,8 @@ :global TelegramChatIdOverride; :global TelegramMessageIDs; :global TelegramQueue; + :global TelegramThreadId; + :global TelegramThreadIdOverride; :global TelegramTokenId; :global TelegramTokenIdOverride; @@ -73,34 +117,41 @@ :global CharacterReplace; :global EitherOr; :global IfThenElse; - :global LogPrintExit2; - :global ParseJson; + :global LogPrint; + :global ProtocolStrip; :global SymbolForNotification; :global UrlEncode; :local EscapeMD do={ + :local Text [ :tostr $1 ]; + :local Mode [ :tostr $2 ]; + :local Excl [ :tostr $3 ]; + :global CharacterReplace; :global IfThenElse; - :local Return $1; :local Chars { - "body"={ "\\"; "`" }; + "body"={ "\\"; "`" }; "plain"={ "_"; "*"; "["; "]"; "("; ")"; "~"; "`"; ">"; "#"; "+"; "-"; "="; "|"; "{"; "}"; "."; "!" }; } - :foreach Char in=($Chars->$2) do={ - :set Return [ $CharacterReplace $Return $Char ("\\" . $Char) ]; + :foreach Char in=($Chars->$Mode) do={ + :if ([ :typeof [ :find $Excl $Char ] ] = "nil") do={ + :set Text [ $CharacterReplace $Text $Char ("\\" . $Char) ]; + } } - :if ($2 = "body") do={ - :return ("```\n" . $Return . "\n```"); + :if ($Mode = "body") do={ + :return ("```\n" . $Text . "\n```"); } - :return $Return; + :return $Text; } :local ChatId [ $EitherOr ($Notification->"chatid") \ [ $EitherOr ($TelegramChatIdOverride->($Notification->"origin")) $TelegramChatId ] ]; + :local ThreadId [ $EitherOr ($Notification->"threadid") \ + [ $EitherOr ($TelegramThreadIdOverride->($Notification->"origin")) $TelegramThreadId ] ]; :local TokenId [ $EitherOr ($TelegramTokenIdOverride->($Notification->"origin")) $TelegramTokenId ]; :if ([ :len $TokenId ] = 0 || [ :len $ChatId ] = 0) do={ @@ -116,7 +167,7 @@ ($Notification->"subject")) "plain" ] . "__*\n\n"); :local LenSubject [ :len $Text ]; :local LenMessage [ :len ($Notification->"message") ]; - :local LenLink [ :len ($Notification->"link") ]; + :local LenLink ([ :len ($Notification->"link") ] * 2); :local LenSum ($LenSubject + $LenMessage + $LenLink); :if ($LenSum > 3968) do={ :set Text ($Text . [ $EscapeMD ([ :pick ($Notification->"message") 0 (3840 - $LenSubject - $LenLink) ] . "...") "body" ]); @@ -125,38 +176,41 @@ :set Text ($Text . [ $EscapeMD ($Notification->"message") "body" ]); } :if ($LenLink > 0) do={ - :set Text ($Text . "\n" . [ $SymbolForNotification "link" ] . [ $EscapeMD ($Notification->"link") "plain" ]); + :set Text ($Text . "\n" . [ $SymbolForNotification "link" ] . \ + "[" . [ $EscapeMD [ $ProtocolStrip ($Notification->"link") ] "plain" ] . "]" . \ + "(" . [ $EscapeMD ($Notification->"link") "plain" ] . ")"); } :if ($Truncated = true) do={ :set Text ($Text . "\n" . [ $SymbolForNotification "scissors" ] . \ - [ $EscapeMD ("The message was too long and has been truncated, cut off " . \ - (($LenSum - [ :len $Text ]) * 100 / $LenSum) . "%!") "plain" ]); + [ $EscapeMD ("The message was too long and has been truncated, cut off _" . \ + (($LenSum - [ :len $Text ]) * 100 / $LenSum) . "%_!") "plain" "_" ]); } - :set Text [ $UrlEncode $Text ]; + :local HTTPData ("chat_id=" . $ChatId . "&disable_notification=" . ($Notification->"silent") . \ + "&reply_to_message_id=" . ($Notification->"replyto") . "&message_thread_id=" . $ThreadId . \ + "&disable_web_page_preview=true&parse_mode=MarkdownV2"); :do { - :if ([ $CertificateAvailable "Go Daddy Secure Certificate Authority - G2" ] = false) do={ - $LogPrintExit2 warning $0 ("Downloading required certificate failed.") true; + :if ([ $CertificateAvailable "Go Daddy Root Certificate Authority - G2" ] = false) do={ + $LogPrint warning $0 ("Downloading required certificate failed."); + :error false; } :local Data ([ /tool/fetch check-certificate=yes-without-crl output=user http-method=post \ ("https://api.telegram.org/bot" . $TokenId . "/sendMessage") \ - http-data=("chat_id=" . $ChatId . "&disable_notification=" . ($Notification->"silent") . \ - "&reply_to_message_id=" . ($Notification->"replyto") . "&disable_web_page_preview=true" . \ - "&parse_mode=MarkdownV2&text=" . $Text) as-value ]->"data"); - :set ($TelegramMessageIDs->([ $ParseJson ([ $ParseJson $Data ]->"result") ]->"message_id")) 1; + http-data=($HTTPData . "&text=" . [ $UrlEncode $Text ]) as-value ]->"data"); + :set ($TelegramMessageIDs->[ :tostr ([ :deserialize from=json value=$Data ]->"result"->"message_id") ]) 1; } on-error={ - $LogPrintExit2 info $0 ("Failed sending telegram notification! Queuing...") false; + $LogPrint info $0 ("Failed sending Telegram notification! Queuing..."); :if ([ :typeof $TelegramQueue ] = "nothing") do={ :set TelegramQueue ({}); } - :set Text ($Text . [ $UrlEncode ("\n" . [ $SymbolForNotification "alarm-clock" ] . \ - [ $EscapeMD ("This message was queued since " . [ /system/clock/get date ] . \ - " " . [ /system/clock/get time ] . " and may be obsolete.") "plain" ]) ]); - :set ($TelegramQueue->[ :len $TelegramQueue ]) { chatid=$ChatId; tokenid=$TokenId; - text=$Text; silent=($Notification->"silent"); replyto=($Notification->"replyto") }; - :if ([ :len [ /system/scheduler/find where name="\$FlushTelegramQueue" ] ] = 0) do={ - /system/scheduler/add name="\$FlushTelegramQueue" interval=1m start-time=startup \ + :set Text ($Text . "\n" . [ $SymbolForNotification "alarm-clock" ] . \ + [ $EscapeMD ("This message was queued since _" . [ /system/clock/get date ] . \ + " " . [ /system/clock/get time ] . "_ and may be obsolete.") "plain" "_" ]); + :set ($TelegramQueue->[ :len $TelegramQueue ]) { tokenid=$TokenId; + http-data=($HTTPData . "&text=" . [ $UrlEncode $Text ]) }; + :if ([ :len [ /system/scheduler/find where name="_FlushTelegramQueue" ] ] = 0) do={ + /system/scheduler/add name="_FlushTelegramQueue" interval=1m start-time=startup \ on-event=(":global FlushTelegramQueue; \$FlushTelegramQueue;"); } } @@ -166,16 +220,18 @@ :set PurgeTelegramQueue do={ :global TelegramQueue; - /system/scheduler/remove [ find where name="\$FlushTelegramQueue" ]; + /system/scheduler/remove [ find where name="_FlushTelegramQueue" ]; :set TelegramQueue; } # send notification via telegram - expects at least two string arguments -:set SendTelegram do={ +:set SendTelegram do={ :do { :global SendTelegram2; - $SendTelegram2 ({ subject=$1; message=$2; link=$3; silent=$4 }); -} + $SendTelegram2 ({ origin=$0; subject=$1; message=$2; link=$3; silent=$4 }); +} on-error={ + :global ExitError; $ExitError false $0; +} } # send notification via telegram - expects one array argument :set SendTelegram2 do={ diff --git a/mod/scriptrunonce.rsc b/mod/scriptrunonce.rsc index 199d852..7fcd5b5 100644 --- a/mod/scriptrunonce.rsc +++ b/mod/scriptrunonce.rsc @@ -1,49 +1,56 @@ #!rsc by RouterOS # RouterOS script: mod/scriptrunonece -# Copyright (c) 2020-2023 Christian Hesse -# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md +# Copyright (c) 2020-2025 Christian Hesse +# https://rsc.eworm.de/COPYING.md +# +# requires RouterOS, version=7.15 # # download script and run it once -# https://git.eworm.de/cgit/routeros-scripts/about/doc/mod/scriptrunonce.md +# https://rsc.eworm.de/doc/mod/scriptrunonce.md :global ScriptRunOnce; # fetch and run script(s) once -:set ScriptRunOnce do={ +:set ScriptRunOnce do={ :do { :local Scripts [ :toarray $1 ]; :global ScriptRunOnceBaseUrl; :global ScriptRunOnceUrlSuffix; - :global LogPrintExit2; + :global FetchHuge; + :global LogPrint; :global ValidateSyntax; :foreach Script in=$Scripts do={ :if (!($Script ~ "^(ftp|https?|sftp)://")) do={ :if ([ :len $ScriptRunOnceBaseUrl ] = 0) do={ - $LogPrintExit2 warning $0 ("Script '" . $Script . "' is not an url and base url is not available.") true; + $LogPrint warning $0 ("Script '" . $Script . "' is not an url and base url is not available."); + :return false; } :set Script ($ScriptRunOnceBaseUrl . $Script . ".rsc" . $ScriptRunOnceUrlSuffix); } - :local Source; - :do { - :set Source ([ /tool/fetch check-certificate=yes-without-crl $Script output=user as-value ]->"data"); - } on-error={ - $LogPrintExit2 warning $0 ("Failed fetching script '" . $Script . "'!") false; + :local Source [ $FetchHuge $0 $Script true ]; + :if ($Source = false) do={ + $LogPrint warning $0 ("Failed fetching script '" . $Script . "'!"); + :return false; } - :if ([ :len $Source ] > 0) do={ - :if ([ $ValidateSyntax $Source ] = true) do={ - :do { - $LogPrintExit2 info $0 ("Running script '" . $Script . "' now.") false; - [ :parse $Source ]; - } on-error={ - $LogPrintExit2 warning $0 ("The script '" . $Script . "' failed to run!") false; - } - } else={ - $LogPrintExit2 warning $0 ("The script '" . $Script . "' failed syntax validation!") false; - } + :if ([ $ValidateSyntax $Source ] = false) do={ + $LogPrint warning $0 ("The script '" . $Script . "' failed syntax validation!"); + :return false; } + + :do { + $LogPrint info $0 ("Running script '" . $Script . "' now."); + [ :parse $Source ]; + } on-error={ + $LogPrint warning $0 ("The script '" . $Script . "' failed to run!"); + :return false; + } + + :return true; } -} +} on-error={ + :global ExitError; $ExitError false $0; +} } diff --git a/mod/ssh-keys-import.rsc b/mod/ssh-keys-import.rsc index 1513114..2fae4b1 100644 --- a/mod/ssh-keys-import.rsc +++ b/mod/ssh-keys-import.rsc @@ -1,49 +1,53 @@ #!rsc by RouterOS # RouterOS script: mod/ssh-keys-import -# Copyright (c) 2020-2023 Christian Hesse -# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md +# Copyright (c) 2020-2025 Christian Hesse +# https://rsc.eworm.de/COPYING.md # -# requires RouterOS, version=7.12beta1 +# requires RouterOS, version=7.16 # # import ssh keys for public key authentication -# https://git.eworm.de/cgit/routeros-scripts/about/doc/mod/ssh-keys-import.md +# https://rsc.eworm.de/doc/mod/ssh-keys-import.md :global SSHKeysImport; :global SSHKeysImportFile; # import single key passed as string -:set SSHKeysImport do={ +:set SSHKeysImport do={ :do { :local Key [ :tostr $1 ]; :local User [ :tostr $2 ]; - :global CharacterReplace; :global GetRandom20CharAlNum; - :global LogPrintExit2; + :global LogPrint; :global MkDir; + :global RmDir; :global WaitForFile; :if ([ :len $Key ] = 0 || [ :len $User ] = 0) do={ - $LogPrintExit2 warning $0 ("Missing argument(s), please pass key and user!") true; + $LogPrint warning $0 ("Missing argument(s), please pass key and user!"); + :return false; } :if ([ :len [ /user/find where name=$User ] ] = 0) do={ - $LogPrintExit2 warning $0 ("User '" . $User . "' does not exist.") true; + $LogPrint warning $0 ("User '" . $User . "' does not exist."); + :return false; } - :local KeyVal [ :toarray [ $CharacterReplace $Key " " "," ] ]; + :local KeyVal ([ :deserialize $Key delimiter=" " from=dsv options=dsv.plain ]->0); :if (!($KeyVal->0 = "ssh-ed25519" || $KeyVal->0 = "ssh-rsa")) do={ - $LogPrintExit2 warning $0 ("SSH key of type '" . $KeyVal->0 . "' is not supported.") true; - } - - :if ([ $MkDir "tmpfs/ssh-keys-import" ] = false) do={ - $LogPrintExit2 warning $0 ("Creating directory 'tmpfs/ssh-keys-import' failed!") true; + $LogPrint warning $0 ("SSH key of type '" . $KeyVal->0 . "' is not supported."); + :return false; } :local FingerPrintMD5 [ :convert from=base64 transform=md5 to=hex ($KeyVal->1) ]; :if ([ :len [ /user/ssh-keys/find where user=$User key-owner~("\\bmd5=" . $FingerPrintMD5 . "\\b") ] ] > 0) do={ - $LogPrintExit2 warning $0 ("The ssh public key (MD5:" . $FingerPrintMD5 . \ - ") is already available for user '" . $User . "'.") false; + $LogPrint warning $0 ("The ssh public key (MD5:" . $FingerPrintMD5 . \ + ") is already available for user '" . $User . "'."); + :return false; + } + + :if ([ $MkDir "tmpfs/ssh-keys-import" ] = false) do={ + $LogPrint warning $0 ("Creating directory 'tmpfs/ssh-keys-import' failed!"); :return false; } @@ -53,53 +57,58 @@ :do { /user/ssh-keys/import public-key-file=$FileName user=$User; - $LogPrintExit2 info $0 ("Imported ssh public key (" . $KeyVal->2 . ", " . $KeyVal->0 . ", " . \ - "MD5:" . $FingerPrintMD5 . ") for user '" . $User . "'.") false; + $LogPrint info $0 ("Imported ssh public key (" . $KeyVal->2 . ", " . $KeyVal->0 . ", " . \ + "MD5:" . $FingerPrintMD5 . ") for user '" . $User . "'."); + $RmDir "tmpfs/ssh-keys-import"; } on-error={ - $LogPrintExit2 warning $0 ("Failed importing key.") true; + $LogPrint warning $0 ("Failed importing key."); + $RmDir "tmpfs/ssh-keys-import"; + :return false; } -} +} on-error={ + :global ExitError; $ExitError false $0; +} } # import keys from a file -:set SSHKeysImportFile do={ +:set SSHKeysImportFile do={ :do { :local FileName [ :tostr $1 ]; :local User [ :tostr $2 ]; - :global CharacterReplace; :global EitherOr; - :global LogPrintExit2; + :global LogPrint; :global ParseKeyValueStore; :global SSHKeysImport; :if ([ :len $FileName ] = 0 || [ :len $User ] = 0) do={ - $LogPrintExit2 warning $0 ("Missing argument(s), please pass file name and user!") true; + $LogPrint warning $0 ("Missing argument(s), please pass file name and user!"); + :return false; } :local File [ /file/find where name=$FileName ]; :if ([ :len $File ] = 0) do={ - $LogPrintExit2 warning $0 ("File '" . $FileName . "' does not exist.") true; + $LogPrint warning $0 ("File '" . $FileName . "' does not exist."); + :return false; } - :local Keys ([ /file/get $FileName contents ] . "\n"); + :local Keys [ :tolf [ /file/get $FileName contents ] ]; - :do { + :foreach KeyVal in=[ :deserialize $Keys delimiter=" " from=dsv options=dsv.plain ] do={ :local Continue false; - :local Line [ :pick $Keys 0 [ :find $Keys "\n" ] ]; - :set Keys [ :pick $Keys ([ :find $Keys "\n" ] + 1) [ :len $Keys ] ]; - :local KeyVal [ :toarray [ $CharacterReplace $Key " " "," ] ]; :if ($KeyVal->0 = "ssh-ed25519" || $KeyVal->0 = "ssh-rsa") do={ :do { - $SSHKeysImport $Line $User; + $SSHKeysImport ($KeyVal->0 . " " . $KeyVal->1 . " " . $KeyVal->2) $User; } on-error={ - $LogPrintExit2 warning $0 ("Failed importing key for user '" . $User . "'.") false; + $LogPrint warning $0 ("Failed importing key for user '" . $User . "'."); } :set Continue true; } :if ($Continue = false && $KeyVal->0 = "#") do={ - :set User [ $EitherOr ([ $ParseKeyValueStore [ :pick $Line 2 [ :len $Line ] ] ]->"user") $User ]; + :set User [ $EitherOr ([ $ParseKeyValueStore ($KeyVal->1) ]->"user") $User ]; :set Continue true; } :if ($Continue = false && [ :len ($KeyVal->0) ] > 0) do={ - $LogPrintExit2 warning $0 ("SSH key of type '" . $KeyVal->0 . "' is not supported.") false; + $LogPrint warning $0 ("SSH key of type '" . $KeyVal->0 . "' is not supported."); } - } while=([ :len $Keys ] > 0); -} + } +} on-error={ + :global ExitError; $ExitError false $0; +} } diff --git a/mode-button.rsc b/mode-button.rsc index 87e8740..edc5f40 100644 --- a/mode-button.rsc +++ b/mode-button.rsc @@ -1,76 +1,96 @@ #!rsc by RouterOS # RouterOS script: mode-button -# Copyright (c) 2018-2023 Christian Hesse -# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md +# Copyright (c) 2018-2025 Christian Hesse +# https://rsc.eworm.de/COPYING.md +# +# requires RouterOS, version=7.15 +# requires device-mode, scheduler # # act on multiple mode and reset button presses -# https://git.eworm.de/cgit/routeros-scripts/about/doc/mode-button.md +# https://rsc.eworm.de/doc/mode-button.md -:local 0 "mode-button"; :global GlobalFunctionsReady; :while ($GlobalFunctionsReady != true) do={ :delay 500ms; } -:global ModeButton; +:local ExitOK false; +:do { + :local ScriptName [ :jobname ]; -:global LogPrintExit2; + :global ModeButton; -:set ($ModeButton->"count") ($ModeButton->"count" + 1); + :global LogPrint; -:local Scheduler [ /system/scheduler/find where name="\$ModeButtonScheduler" ]; + :set ($ModeButton->"count") ($ModeButton->"count" + 1); -:if ([ :len $Scheduler ] = 0) do={ - $LogPrintExit2 info $0 ("Creating scheduler \$ModeButtonScheduler, counting presses...") false; - :global ModeButtonScheduler do={ - :global ModeButton; + :local Scheduler [ /system/scheduler/find where name="_ModeButtonScheduler" ]; - :global LogPrintExit2; - :global ModeButtonScheduler; - :global ValidateSyntax; + :if ([ :len $Scheduler ] = 0) do={ + $LogPrint info $ScriptName ("Creating scheduler _ModeButtonScheduler, counting presses..."); + :global ModeButtonScheduler do={ :do { + :local FuncName $0; - :local LEDInvert do={ - :global ModeButtonLED; + :global ModeButton; - :global IfThenElse; + :global LogPrint; + :global ModeButtonScheduler; + :global ValidateSyntax; - :local LED [ /system/leds/find where leds=$ModeButtonLED type~"^(on|off)\$" interface=[] ]; - :if ([ :len $LED ] = 0) do={ - :return false; - } - /system/leds/set type=[ $IfThenElse ([ get $LED type ] = "on") "off" "on" ] $LED; - } + :local LEDInvert do={ + :global ModeButtonLED; - :local Count ($ModeButton->"count"); - :local Code ($ModeButton->[ :tostr $Count ]); + :global IfThenElse; - :set ($ModeButton->"count") 0; - :set ModeButtonScheduler; - /system/scheduler/remove [ find where name=$0 ]; - - :if ([ :len $Code ] > 0) do={ - :if ([ $ValidateSyntax $Code ] = true) do={ - $LogPrintExit2 info $0 ("Acting on " . $Count . " mode-button presses: " . $Code) false; - - :for I from=1 to=$Count do={ - $LEDInvert; - :if ([ /system/routerboard/settings/get silent-boot ] = false) do={ - :beep length=200ms; - } - :delay 200ms; - $LEDInvert; - :delay 200ms; + :local LED [ /system/leds/find where leds=$ModeButtonLED \ + !disabled type~"^(on|off)\$" interface=[] ]; + :if ([ :len $LED ] = 0) do={ + :return false; } - - [ :parse $Code ]; - } else={ - $LogPrintExit2 warning $0 ("The code for " . $Count . " mode-button presses failed syntax validation!") false; + /system/leds/set type=[ $IfThenElse ([ get $LED type ] = "on") "off" "on" ] $LED; } - } else={ - $LogPrintExit2 info $0 ("No action defined for " . $Count . " mode-button presses.") false; - } + + :local Count ($ModeButton->"count"); + :local Code ($ModeButton->[ :tostr $Count ]); + + :set ($ModeButton->"count") 0; + :set ModeButtonScheduler; + /system/scheduler/remove [ find where name="_ModeButtonScheduler" ]; + + :if ([ :len $Code ] > 0) do={ + :if ([ $ValidateSyntax $Code ] = true) do={ + $LogPrint info $FuncName ("Acting on " . $Count . " mode-button presses: " . $Code); + + :for I from=1 to=$Count do={ + $LEDInvert; + :if ([ /system/routerboard/settings/get silent-boot ] = false) do={ + :beep length=200ms; + } + :delay 200ms; + $LEDInvert; + :delay 200ms; + } + + :do { + [ :parse $Code ]; + } on-error={ + $LogPrint warning $FuncName \ + ("The code for " . $Count . " mode-button presses failed with runtime error!"); + } + } else={ + $LogPrint warning $FuncName \ + ("The code for " . $Count . " mode-button presses failed syntax validation!"); + } + } else={ + $LogPrint info $FuncName ("No action defined for " . $Count . " mode-button presses."); + } + } on-error={ + :global ExitError; $ExitError false $0; + } } + /system/scheduler/add name="_ModeButtonScheduler" \ + on-event=":global ModeButtonScheduler; \$ModeButtonScheduler;" interval=3s; + } else={ + $LogPrint debug $ScriptName ("Updating scheduler _ModeButtonScheduler..."); + /system/scheduler/set $Scheduler start-time=[ /system/clock/get time ]; } - /system/scheduler/add name="\$ModeButtonScheduler" \ - on-event=":global ModeButtonScheduler; \$ModeButtonScheduler;" interval=3s; -} else={ - $LogPrintExit2 debug $0 ("Updating scheduler \$ModeButtonScheduler...") false; - /system/scheduler/set $Scheduler start-time=[ /system/clock/get time ]; +} on-error={ + :global ExitError; $ExitError $ExitOK [ :jobname ]; } diff --git a/netwatch-dns.rsc b/netwatch-dns.rsc index 3e3e27c..467d636 100644 --- a/netwatch-dns.rsc +++ b/netwatch-dns.rsc @@ -1,93 +1,150 @@ #!rsc by RouterOS # RouterOS script: netwatch-dns -# Copyright (c) 2022-2023 Christian Hesse -# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md +# Copyright (c) 2022-2025 Christian Hesse +# https://rsc.eworm.de/COPYING.md +# +# requires RouterOS, version=7.16 +# requires device-mode, fetch # # monitor and manage dns/doh with netwatch -# https://git.eworm.de/cgit/routeros-scripts/about/doc/netwatch-dns.md +# https://rsc.eworm.de/doc/netwatch-dns.md -:local 0 "netwatch-dns"; :global GlobalFunctionsReady; :while ($GlobalFunctionsReady != true) do={ :delay 500ms; } -:global CertificateAvailable; -:global EitherOr; -:global LogPrintExit2; -:global ParseKeyValueStore; -:global ScriptLock; +:local ExitOK false; +:do { + :local ScriptName [ :jobname ]; -$ScriptLock $0; + :global CertificateAvailable; + :global EitherOr; + :global IsDNSResolving; + :global IsTimeSync; + :global LogPrint; + :global LogPrintOnce; + :global ParseKeyValueStore; + :global ScriptLock; -:if ([ /system/resource/get uptime ] < 5m30s) do={ - $LogPrintExit2 info $0 ("System just booted, giving netwatch some time to settle.") true; -} - -:local DnsServers ({}); -:local DnsFallback ({}); -:local DnsCurrent [ /ip/dns/get servers ]; - -:foreach Host in=[ /tool/netwatch/find where comment~"\\bdns\\b" status="up" ] do={ - :local HostVal [ /tool/netwatch/get $Host ]; - :local HostInfo [ $ParseKeyValueStore ($HostVal->"comment") ]; - - :if ($HostInfo->"disabled" != true) do={ - :if ($HostInfo->"dns" = true) do={ - :set DnsServers ($DnsServers, $HostVal->"host"); - } - :if ($HostInfo->"dns-fallback" = true) do={ - :set DnsFallback ($DnsFallback, $HostVal->"host"); - } + :if ([ $ScriptLock $ScriptName ] = false) do={ + :set ExitOK true; + :error false; } -} -:if ([ :len $DnsServers ] > 0) do={ - :if ($DnsServers != $DnsCurrent) do={ - $LogPrintExit2 info $0 ("Updating DNS servers: " . [ :tostr $DnsServers ]) false; - /ip/dns/set servers=$DnsServers; - /ip/dns/cache/flush; + :local SettleTime (5m30s - [ /system/resource/get uptime ]); + :if ($SettleTime > 0s) do={ + $LogPrint info $ScriptName ("System just booted, giving netwatch " . $SettleTime . " to settle."); + :set ExitOK true; + :error true; } -} else={ - :if ([ :len $DnsFallback ] > 0) do={ - :if ($DnsFallback != $DnsCurrent) do={ - $LogPrintExit2 info $0 ("Updating DNS servers to fallback: " . \ - [ :tostr $DnsFallback ]) false; - /ip/dns/set servers=$DnsFallback; - /ip/dns/cache/flush; - } - } -} -:local DohServer ""; -:local DohCert ""; -:local DohCurrent [ /ip/dns/get use-doh-server ]; + :local DnsServers ({}); + :local DnsFallback ({}); + :local DnsCurrent [ /ip/dns/get servers ]; -:foreach Host in=[ /tool/netwatch/find where comment~"\\bdoh\\b" status="up" ] do={ - :local HostVal [ /tool/netwatch/get $Host ]; - :local HostInfo [ $ParseKeyValueStore ($HostVal->"comment") ]; + :foreach Host in=[ /tool/netwatch/find where comment~"\\bdns\\b" status="up" ] do={ + :local HostVal [ /tool/netwatch/get $Host ]; + :local HostInfo [ $ParseKeyValueStore ($HostVal->"comment") ]; - :if ($HostInfo->"doh" = true && $HostInfo->"disabled" != true && $DohServer = "") do={ - :set DohServer [ $EitherOr ($HostInfo->"doh-url") \ - ("https://" . $HostVal->"host" . "/dns-query") ]; - :set DohCert ($HostInfo->"doh-cert"); - } -} - -:if ($DohServer != "") do={ - :if ($DohServer != $DohCurrent) do={ - $LogPrintExit2 info $0 ("Updating DoH server: " . $DohServer) false; - :if ([ :len $DohCert ] > 0) do={ - /ip/dns/set use-doh-server=""; - :if ([ $CertificateAvailable $DohCert ] = false) do={ - $LogPrintExit2 warning $0 ("Downloading certificate failed, trying without.") false; + :if ($HostInfo->"disabled" != true) do={ + :if ($HostInfo->"dns" = true) do={ + :set DnsServers ($DnsServers, $HostVal->"host"); + } + :if ($HostInfo->"dns-fallback" = true) do={ + :set DnsFallback ($DnsFallback, $HostVal->"host"); } } - /ip/dns/set use-doh-server=$DohServer; - /ip/dns/cache/flush; } -} else={ - :if ($DohCurrent != "") do={ - $LogPrintExit2 info $0 ("DoH server (" . $DohCurrent . ") is down, disabling.") false; + + :if ([ :len $DnsServers ] > 0) do={ + :if ($DnsServers != $DnsCurrent) do={ + $LogPrint info $ScriptName ("Updating DNS servers: " . [ :tostr $DnsServers ]); + /ip/dns/set servers=$DnsServers; + /ip/dns/cache/flush; + } + } else={ + :if ([ :len $DnsFallback ] > 0) do={ + :if ($DnsFallback != $DnsCurrent) do={ + $LogPrint info $ScriptName ("Updating DNS servers to fallback: " . [ :tostr $DnsFallback ]); + /ip/dns/set servers=$DnsFallback; + /ip/dns/cache/flush; + } + } + } + + :local DohCurrent [ /ip/dns/get use-doh-server ]; + :local DohServers ({}); + + :if ([ :len $DohCurrent ] > 0 && [ $IsDNSResolving ] = false && [ $IsTimeSync ] = false) do={ + $LogPrint info $ScriptName ("Time is not sync, disabling DoH: " . $DohCurrent); + /ip/dns/set use-doh-server=""; + :set DohCurrent ""; + } + + :foreach Host in=[ /tool/netwatch/find where comment~"\\bdoh\\b" status="up" ] do={ + :local HostVal [ /tool/netwatch/get $Host ]; + :local HostInfo [ $ParseKeyValueStore ($HostVal->"comment") ]; + :local HostName [ /ip/dns/static/find where name address=($HostVal->"host") \ + (type="A" or type="AAAA") !disabled !dynamic ]; + :if ([ :len $HostName ] > 0) do={ + :set HostName [ /ip/dns/static/get ($HostName->0) name ]; + } + + :if ($HostInfo->"doh" = true && $HostInfo->"disabled" != true) do={ + :if ([ :len ($HostInfo->"doh-url") ] = 0) do={ + :set ($HostInfo->"doh-url") ("https://" . [ $EitherOr $HostName ($HostVal->"host") ] . "/dns-query"); + } + + :if ($DohCurrent = $HostInfo->"doh-url") do={ + $LogPrint debug $ScriptName ("Current DoH server is still up: " . $DohCurrent); + :set ExitOK true; + :error true; + } + + :set ($DohServers->[ :len $DohServers ]) $HostInfo; + } + } + + :if ([ :len $DohCurrent ] > 0) do={ + $LogPrint info $ScriptName ("Current DoH server is down, disabling: " . $DohCurrent); /ip/dns/set use-doh-server=""; /ip/dns/cache/flush; } + + :foreach DohServer in=$DohServers do={ + :if ([ :len ($DohServer->"doh-cert") ] > 0) do={ + :if ([ $CertificateAvailable ($DohServer->"doh-cert") ] = false) do={ + $LogPrint warning $ScriptName ("Downloading certificate failed, trying without."); + } + } + + :local Data false; + :do { + :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"); + } on-error={ + $LogPrint warning $ScriptName ("Request to DoH server failed (network or certificate issue): " . \ + ($DohServer->"doh-url")); + } + + :if ($Data != false) do={ + :if ([ :typeof [ :find $Data "doh-check-OK" ] ] = "num") do={ + /ip/dns/set use-doh-server=($DohServer->"doh-url") verify-doh-cert=yes; + :if ([ /certificate/settings/get crl-use ] = true) do={ + $LogPrintOnce warning $ScriptName ("Configured to use CRL, that can cause severe issue!"); + } + /ip/dns/cache/flush; + $LogPrint info $ScriptName ("Setting DoH server: " . ($DohServer->"doh-url")); + :set ExitOK true; + :error true; + } else={ + $LogPrint warning $ScriptName ("Received unexpected response from DoH server: " . \ + ($DohServer->"doh-url")); + } + } + } +} on-error={ + :global ExitError; $ExitError $ExitOK [ :jobname ]; } diff --git a/netwatch-notify.rsc b/netwatch-notify.rsc index 28bde24..0b8a8dc 100644 --- a/netwatch-notify.rsc +++ b/netwatch-notify.rsc @@ -1,186 +1,229 @@ #!rsc by RouterOS # RouterOS script: netwatch-notify -# Copyright (c) 2020-2023 Christian Hesse -# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md +# Copyright (c) 2020-2025 Christian Hesse +# https://rsc.eworm.de/COPYING.md +# +# requires RouterOS, version=7.15 # # monitor netwatch and send notifications -# https://git.eworm.de/cgit/routeros-scripts/about/doc/netwatch-notify.md +# https://rsc.eworm.de/doc/netwatch-notify.md -:local 0 "netwatch-notify"; :global GlobalFunctionsReady; :while ($GlobalFunctionsReady != true) do={ :delay 500ms; } -:global NetwatchNotify; +:local ExitOK false; +:do { + :local ScriptName [ :jobname ]; -:global EitherOr; -:global IfThenElse; -:global IsDNSResolving; -:global LogPrintExit2; -:global ParseKeyValueStore; -:global ScriptFromTerminal; -:global ScriptLock; -:global SendNotification2; -:global SymbolForNotification; + :global NetwatchNotify; -:local NetwatchNotifyHook do={ - :local Name [ :tostr $1 ]; - :local Type [ :tostr $2 ]; - :local State [ :tostr $3 ]; - :local Hook [ :tostr $4 ]; + :global EitherOr; + :global IfThenElse; + :global IsDNSResolving; + :global LogPrint; + :global ParseKeyValueStore; + :global ScriptFromTerminal; + :global ScriptLock; + :global SendNotification2; + :global SymbolForNotification; - :global LogPrintExit2; - :global ValidateSyntax; + :local NetwatchNotifyHook do={ + :local ScriptName [ :tostr $1 ]; + :local Name [ :tostr $2 ]; + :local Type [ :tostr $3 ]; + :local State [ :tostr $4 ]; + :local Hook [ :tostr $5 ]; - :if ([ $ValidateSyntax $Hook ] = true) do={ - :do { - [ :parse $Hook ]; - } on-error={ - $LogPrintExit2 warning $0 ("The " . $State . "-hook for " . $Type . " '" . $Name . \ - "' failed to run.") false; - :return ("The hook failed to run."); + :global LogPrint; + :global ValidateSyntax; + + :if ([ $ValidateSyntax $Hook ] = true) do={ + :do { + [ :parse $Hook ]; + } on-error={ + $LogPrint warning $ScriptName ("The " . $State . "-hook for " . $Type . " '" . $Name . "' failed to run."); + :return ("The hook failed to run."); + } + } else={ + $LogPrint warning $ScriptName ("The " . $State . "-hook for " . $Type . " '" . $Name . "' failed syntax validation."); + :return ("The hook failed syntax validation."); } - } else={ - $LogPrintExit2 warning $0 ("The " . $State . "-hook for " . $Type . " '" . $Name . \ - "' failed syntax validation.") false; - :return ("The hook failed syntax validation."); + + $LogPrint info $ScriptName ("Ran hook on " . $Type . " '" . $Name . "' " . $State . ": " . $Hook); + :return ("Ran hook:\n" . $Hook); } - $LogPrintExit2 info $0 ("Ran hook on " . $Type . " '" . $Name . "' " . $State . ": " . \ - $Hook) false; - :return ("Ran hook:\n" . $Hook); -} + :local ResolveExpected do={ + :local ScriptName [ :tostr $1 ]; + :local Name [ :tostr $2 ]; + :local Expected [ :tostr $3 ]; -$ScriptLock $0; + :global GetRandom20CharAlNum; -:local ScriptFromTerminalCached [ $ScriptFromTerminal $0 ]; - -:if ([ :typeof $NetwatchNotify ] = "nothing") do={ - :set NetwatchNotify ({}); -} - -:foreach Host in=[ /tool/netwatch/find where comment~"\\bnotify\\b" !disabled status!="unknown" ] do={ - :local HostVal [ /tool/netwatch/get $Host ]; - :local Type [ $IfThenElse ($HostVal->"type" ~ "^(https?-get|tcp-conn)\$") "service" "host" ]; - :local HostInfo [ $ParseKeyValueStore ($HostVal->"comment") ]; - :local HostDetails ($HostVal->"host" . \ - [ $IfThenElse ([ :len ($HostInfo->"resolve") ] > 0) (", " . $HostInfo->"resolve") ]); - - :if ($HostInfo->"notify" = true && $HostInfo->"disabled" != true) do={ - :local Name [ $EitherOr ($HostInfo->"name") ($HostVal->"name") ]; - - :local Metric { "count-down"=0; "count-up"=0; "notified"=false; "resolve-failcnt"=0 }; - :if ([ :typeof ($NetwatchNotify->$Name) ] = "array") do={ - :set $Metric ($NetwatchNotify->$Name); + :local FwAddrList ($ScriptName . "-" . [ $GetRandom20CharAlNum ]); + :if ([ :typeof [ :toip $Expected ] ] = "ip") do={ + /ip/firewall/address-list/add address=$Name list=$FwAddrList dynamic=yes timeout=10s; + :delay 20ms; + :if ([ :len [ /ip/firewall/address-list/find where list=$FwAddrList address=$Expected ] ] > 0) do={ + :return true; + } } - - :if ([ :typeof ($HostInfo->"resolve") ] = "str") do={ - :if ([ $IsDNSResolving ] = true) do={ - :do { - :local Resolve [ :resolve ($HostInfo->"resolve") ]; - :if ($Resolve != $HostVal->"host") do={ - $LogPrintExit2 info $0 ("Name '" . $HostInfo->"resolve" . [ $IfThenElse \ - ($HostInfo->"resolve" != $HostInfo->"name") ("' for " . $Type . " '" . \ - $HostInfo->"name") "" ] . "' resolves to different address " . $Resolve . \ - ", updating.") false; - /tool/netwatch/set host=$Resolve $Host; - :set ($Metric->"resolve-failcnt") 0; - } - } on-error={ - :set ($Metric->"resolve-failcnt") ($Metric->"resolve-failcnt" + 1); - :if ($Metric->"resolve-failcnt" = 3) do={ - $LogPrintExit2 warning $0 ("Resolving name '" . $HostInfo->"resolve" . [ $IfThenElse \ - ($HostInfo->"resolve" != $HostInfo->"name") ("' for " . $Type . " '" . \ - $HostInfo->"name") "" ] . "' failed.") false; - } - } + :if ([ :typeof [ :toip6 $Expected ] ] = "ip6") do={ + /ipv6/firewall/address-list/add address=$Name list=$FwAddrList dynamic=yes timeout=10s; + :delay 20ms; + :if ([ :len [ /ipv6/firewall/address-list/find where list=$FwAddrList address=$Expected ] ] > 0) do={ + :return true; } } - :if ($HostVal->"status" = "up") do={ - :local CountDown ($Metric->"count-down"); - :if ($CountDown > 0) do={ - $LogPrintExit2 info $0 \ - ("The " . $Type . " '" . $Name . "' (" . $HostDetails . ") is up.") false; - :set ($Metric->"count-down") 0; + :return false; + } + + :if ([ $ScriptLock $ScriptName ] = false) do={ + :set ExitOK true; + :error false; + } + + :local ScriptFromTerminalCached [ $ScriptFromTerminal $ScriptName ]; + + :if ([ :typeof $NetwatchNotify ] = "nothing") do={ + :set NetwatchNotify ({}); + } + + :foreach Host in=[ /tool/netwatch/find where comment~"\\bnotify\\b" !disabled status!="unknown" ] do={ + :local HostVal [ /tool/netwatch/get $Host ]; + :local Type [ $IfThenElse ($HostVal->"type" ~ "^(https?-get|tcp-conn)\$") "service" "host" ]; + :local HostInfo [ $ParseKeyValueStore ($HostVal->"comment") ]; + :local HostDetails ($HostVal->"host" . \ + [ $IfThenElse ([ :len ($HostInfo->"resolve") ] > 0) (", " . $HostInfo->"resolve") ]); + + :if ($HostInfo->"notify" = true && $HostInfo->"disabled" != true) do={ + :local Name [ $EitherOr ($HostInfo->"name") ($HostVal->"name") ]; + + :local Metric { "count-down"=0; "count-up"=0; "notified"=false; "resolve-failcnt"=0 }; + :if ([ :typeof ($NetwatchNotify->$Name) ] = "array") do={ + :set $Metric ($NetwatchNotify->$Name); } - :set ($Metric->"count-up") ($Metric->"count-up" + 1); - :if ($Metric->"notified" = true) do={ - :local Message ("The " . $Type . " '" . $Name . "' (" . $HostDetails . \ - ") is up since " . $HostVal->"since" . ".\n" . \ - "It was down for " . $CountDown . " checks since " . ($Metric->"since") . "."); - :if ([ :typeof ($HostInfo->"note") ] = "str") do={ - :set Message ($Message . "\n\nNote:\n" . ($HostInfo->"note")); + + :if ([ :typeof ($HostInfo->"resolve") ] = "str") do={ + :if ([ $IsDNSResolving ] = true) do={ + :do { + :local Resolve [ :resolve type=[ $IfThenElse ([ :typeof ($HostVal->"host") ] = "ip") \ + "ipv4" "ipv6" ] ($HostInfo->"resolve") ]; + :if ($Resolve != $HostVal->"host") do={ + :if ([ $ResolveExpected $ScriptName ($HostInfo->"resolve") ($HostVal->"host") ] = false) do={ + $LogPrint info $ScriptName ("Name '" . $HostInfo->"resolve" . [ $IfThenElse \ + ($HostInfo->"resolve" != $HostInfo->"name") ("' for " . $Type . " '" . \ + $HostInfo->"name") "" ] . "' resolves to different address " . $Resolve . \ + ", updating."); + /tool/netwatch/set host=$Resolve $Host; + :set ($Metric->"resolve-failcnt") 0; + :set ($HostVal->"status") "unknown"; + } + } + } on-error={ + :set ($Metric->"resolve-failcnt") ($Metric->"resolve-failcnt" + 1); + :if ($Metric->"resolve-failcnt" = 3) do={ + $LogPrint [ $IfThenElse ($HostInfo->"no-resolve-fail" != true) warning debug ] \ + $ScriptName ("Resolving name '" . $HostInfo->"resolve" . [ $IfThenElse \ + ($HostInfo->"resolve" != $HostInfo->"name") ("' for " . $Type . " '" . \ + $HostInfo->"name") "" ] . "' failed."); + } + } } - :if ([ :typeof ($HostInfo->"up-hook") ] = "str") do={ - :set Message ($Message . "\n\n" . [ $NetwatchNotifyHook $Name $Type "up" \ - ($HostInfo->"up-hook") ]); + } + + :if ($HostVal->"status" = "up") do={ + :local CountDown ($Metric->"count-down"); + :if ($CountDown > 0) do={ + $LogPrint info $ScriptName \ + ("The " . $Type . " '" . $Name . "' (" . $HostDetails . ") is up."); + :set ($Metric->"count-down") 0; } - $SendNotification2 ({ origin=[ $EitherOr ($HostInfo->"origin") $0 ]; silent=($HostInfo->"silent"); \ - subject=([ $SymbolForNotification "white-heavy-check-mark" ] . "Netwatch Notify: " . $Name . " up"); \ - message=$Message }); + :set ($Metric->"count-up") ($Metric->"count-up" + 1); + :if ($Metric->"notified" = true) do={ + :local Message ("The " . $Type . " '" . $Name . "' (" . $HostDetails . \ + ") is up since " . $HostVal->"since" . ".\n" . \ + "It was down for " . $CountDown . " checks since " . ($Metric->"since") . "."); + :if ([ :typeof ($HostInfo->"note") ] = "str") do={ + :set Message ($Message . "\n\nNote:\n" . ($HostInfo->"note")); + } + :if ([ :typeof ($HostInfo->"up-hook") ] = "str") do={ + :set Message ($Message . "\n\n" . [ $NetwatchNotifyHook $ScriptName $Name $Type "up" \ + ($HostInfo->"up-hook") ]); + } + $SendNotification2 ({ origin=[ $EitherOr ($HostInfo->"origin") $ScriptName ]; silent=($HostInfo->"silent"); \ + subject=([ $SymbolForNotification "white-heavy-check-mark" ] . "Netwatch Notify: " . $Name . " up"); \ + message=$Message; link=($HostInfo->"link") }); + } + :set ($Metric->"notified") false; + :set ($Metric->"parent") ($HostInfo->"parent"); + :set ($Metric->"since"); } - :set ($Metric->"notified") false; - :set ($Metric->"parent") ($HostInfo->"parent"); - :set ($Metric->"since"); - } else={ - :set ($Metric->"count-down") ($Metric->"count-down" + 1); - :set ($Metric->"count-up") 0; - :set ($Metric->"parent") ($HostInfo->"parent"); - :set ($Metric->"since") ($HostVal->"since"); - :local CountDown [ $IfThenElse ([ :tonum ($HostInfo->"count") ] > 0) ($HostInfo->"count") 5 ]; - :local Parent ($HostInfo->"parent"); - :local ParentUp false; - :while ([ :len $Parent ] > 0) do={ - :set CountDown ($CountDown + 1); - :set Parent ($NetwatchNotify->$Parent->"parent"); - } - :set Parent ($HostInfo->"parent"); - :local ParentNotified false; - :while ($ParentNotified = false && [ :len $Parent ] > 0) do={ - :set ParentNotified [ $IfThenElse (($NetwatchNotify->$Parent->"notified") = true) \ - true false ]; - :set ParentUp ($NetwatchNotify->$Parent->"count-up"); - :if ($ParentNotified = false) do={ + + :if ($HostVal->"status" = "down") do={ + :set ($Metric->"count-down") ($Metric->"count-down" + 1); + :set ($Metric->"count-up") 0; + :set ($Metric->"parent") ($HostInfo->"parent"); + :set ($Metric->"since") ($HostVal->"since"); + :local CountDown [ $IfThenElse ([ :tonum ($HostInfo->"count") ] > 0) ($HostInfo->"count") 5 ]; + :local Parent ($HostInfo->"parent"); + :local ParentUp false; + :while ([ :len $Parent ] > 0) do={ + :set CountDown ($CountDown + 1); :set Parent ($NetwatchNotify->$Parent->"parent"); } - } - :if ($Metric->"notified" = false || $Metric->"count-down" % 120 = 0 || \ - $ScriptFromTerminalCached = true) do={ - $LogPrintExit2 [ $IfThenElse ($HostInfo->"no-down-notification" != true) info debug ] $0 \ - ("The " . $Type . " '" . $Name . "' (" . $HostDetails . ") is down for " . \ - $Metric->"count-down" . " checks, " . [ $IfThenElse ($ParentNotified = false) [ $IfThenElse \ - ($Metric->"notified" = true) ("already notified.") ($CountDown - $Metric->"count-down" . \ - " to go.") ] ("parent " . $Type . " " . $Parent . " is down.") ]) false; - } - :if ((($CountDown * 2) - ($Metric->"count-down" * 3)) / 2 = 0 && \ - [ :typeof ($HostInfo->"pre-down-hook") ] = "str") do={ - $NetwatchNotifyHook $Name $Type "pre-down" ($HostInfo->"pre-down-hook"); - } - :if ($ParentNotified = false && $Metric->"count-down" >= $CountDown && \ - ($ParentUp = false || $ParentUp > 2) && $Metric->"notified" != true) do={ - :local Message ("The " . $Type . " '" . $Name . "' (" . $HostDetails . \ - ") is down since " . $HostVal->"since" . "."); - :if ([ :typeof ($HostInfo->"note") ] = "str") do={ - :set Message ($Message . "\n\nNote:\n" . ($HostInfo->"note")); + :set Parent ($HostInfo->"parent"); + :local ParentNotified false; + :while ($ParentNotified = false && [ :len $Parent ] > 0) do={ + :set ParentNotified [ $IfThenElse (($NetwatchNotify->$Parent->"notified") = true) \ + true false ]; + :set ParentUp ($NetwatchNotify->$Parent->"count-up"); + :if ($ParentNotified = false) do={ + :set Parent ($NetwatchNotify->$Parent->"parent"); + } } - :if ([ :typeof ($HostInfo->"down-hook") ] = "str") do={ - :set Message ($Message . "\n\n" . [ $NetwatchNotifyHook $Name $Type "down" \ - ($HostInfo->"down-hook") ]); + :if ($Metric->"notified" = false || $Metric->"count-down" % 120 = 0 || \ + $ScriptFromTerminalCached = true) do={ + $LogPrint [ $IfThenElse ($HostInfo->"no-down-notification" != true) info debug ] $ScriptName \ + ("The " . $Type . " '" . $Name . "' (" . $HostDetails . ") is down for " . \ + $Metric->"count-down" . " checks, " . [ $IfThenElse ($ParentNotified = false) [ $IfThenElse \ + ($Metric->"notified" = true) ("already notified.") ($CountDown - $Metric->"count-down" . \ + " to go.") ] ("parent " . $Type . " " . $Parent . " is down.") ]); } - :if ($HostInfo->"no-down-notification" != true) do={ - $SendNotification2 ({ origin=[ $EitherOr ($HostInfo->"origin") $0 ]; silent=($HostInfo->"silent"); \ - subject=([ $SymbolForNotification "cross-mark" ] . "Netwatch Notify: " . $Name . " down"); \ - message=$Message }); + :if ((($CountDown * 2) - ($Metric->"count-down" * 3)) / 2 = 0 && \ + [ :typeof ($HostInfo->"pre-down-hook") ] = "str") do={ + $NetwatchNotifyHook $ScriptName $Name $Type "pre-down" ($HostInfo->"pre-down-hook"); + } + :if ($ParentNotified = false && $Metric->"count-down" >= $CountDown && \ + ($ParentUp = false || $ParentUp > 2) && $Metric->"notified" != true) do={ + :local Message ("The " . $Type . " '" . $Name . "' (" . $HostDetails . \ + ") is down since " . $HostVal->"since" . "."); + :if ([ :typeof ($HostInfo->"note") ] = "str") do={ + :set Message ($Message . "\n\nNote:\n" . ($HostInfo->"note")); + } + :if ([ :typeof ($HostInfo->"down-hook") ] = "str") do={ + :set Message ($Message . "\n\n" . [ $NetwatchNotifyHook $ScriptName $Name $Type "down" \ + ($HostInfo->"down-hook") ]); + } + :if ($HostInfo->"no-down-notification" != true) do={ + $SendNotification2 ({ origin=[ $EitherOr ($HostInfo->"origin") $ScriptName ]; silent=($HostInfo->"silent"); \ + subject=([ $SymbolForNotification "cross-mark" ] . "Netwatch Notify: " . $Name . " down"); \ + message=$Message; link=($HostInfo->"link") }); + } + :set ($Metric->"notified") true; } - :set ($Metric->"notified") true; } + + :set ($NetwatchNotify->$Name) { + "count-down"=($Metric->"count-down"); + "count-up"=($Metric->"count-up"); + "notified"=($Metric->"notified"); + "parent"=($Metric->"parent"); + "resolve-failcnt"=($Metric->"resolve-failcnt"); + "since"=($Metric->"since") }; } - :set ($NetwatchNotify->$Name) { - "count-down"=($Metric->"count-down"); - "count-up"=($Metric->"count-up"); - "notified"=($Metric->"notified"); - "parent"=($Metric->"parent"); - "resolve-failcnt"=($Metric->"resolve-failcnt"); - "since"=($Metric->"since") }; } +} on-error={ + :global ExitError; $ExitError $ExitOK [ :jobname ]; } diff --git a/news-and-changes.rsc b/news-and-changes.rsc index 64e114f..459326f 100644 --- a/news-and-changes.rsc +++ b/news-and-changes.rsc @@ -1,11 +1,17 @@ # News, changes and migration by RouterOS Scripts -# Copyright (c) 2019-2023 Christian Hesse -# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md +# Copyright (c) 2019-2025 Christian Hesse +# https://rsc.eworm.de/COPYING.md + +:global IDonate; :global IfThenElse; :global RequiredRouterOS; +:global SymbolForNotification; -# News, changes and migration up to change 95 are in global-config.changes! +:local Resource [ /system/resource/get ]; + +# News, changes and migration up to change 95: +# https://git.eworm.de/cgit/routeros-scripts/plain/global-config.changes?h=change-95 # Changes for global-config to be added to notification on script updates :global GlobalConfigChanges { @@ -27,12 +33,40 @@ 111="Modified 'dhcp-to-dns' to allow multiple records for one mac address."; 112="Enhanced 'mod/ssh-keys-import' to record the fingerprint of keys."; 113="Added helper functions for easier setup to Matrix notification module."; + 114="All relevant scripts were ported to new wifi package for RouterOS 7.13 and later. Migration is complex and thus not done automatically!"; + 115=("Celebrating " . [ $SymbolForNotification "sparkles,star" ] . "1.000 stars " . [ $SymbolForNotification "star,sparkles" ] . "on Github! Please continue starring..."); + 116=("... and also please keep in mind that it takes a huge amount of time maintaining these scripts. " . [ $IfThenElse ($IDonate != true) \ + ("Following the donation hint " . [ $SymbolForNotification "arrow-down" "below" ] . "to keep me motivated is much appreciated. Thanks!") \ + ("Looks like you did donate already. " . [ $SymbolForNotification "heart" "<3" ] . "Much appreciated, thanks!") ]); + 117="Enhanced 'packages-update' to support deferred reboot on automatically installed updates."; + 118=("RouterOS packages increase in size with each release. This becomes a problem for devices with 16MB storage and below. " . \ + [ $IfThenElse ($Resource->"total-hdd-space" < 16000000) ("Your " . $Resource->"board-name" . " is specifically affected! ") \ + [ $IfThenElse ($Resource->"free-hdd-space" > 4000000) ("(Your " . $Resource->"board-name" . " does not suffer this issue.) ") ] ] . \ + "Huge configuration and lots of scripts give an extra risk. Take care!"); + 119="Added support for IPv6 to script 'fw-addr-lists'."; + 120="Implemented a workaround in 'backup-cloud'. Now script should no longer just crash, but send notification with error."; + 121="The 'wifiwave2' scripts are finally gone. Development continues with 'wifi' in RouterOS 7.13 and later."; + 122="The global configuration was enhanced to support loading snippets. Configuration can be split off to scripts where name starts with 'global-config-overlay.d/'."; + 123="Introduced new function '\$LogPrint', and deprecated '\$LogPrintExit2'. Please update custom scripts if you use it."; + 124="Added support for links in 'netwatch-notify', these are added below the formatted notification text."; + 125=("April's Fool! " . [ $SymbolForNotification "smiley-partying-face" ] . "Well, you missed it... - no charge nor fees. (Anyway... Donations are much appreciated, " . [ $SymbolForNotification "smiley-smiling-face" ] . "thanks!)"); + 126="Made 'telegram-chat' capable of handling large command output. Telegram messages still limit the size, so it is truncated now."; + 127="Added support for authentication to Ntfy notification module."; + 128="Added another list from blocklist.de to default configuration for 'fw-addr-lists'."; + 129="Extended 'backup-partition' to support RouterOS copy-over - interactively or before feature update."; + 130="Dropped intermediate certificates, depending on just root certificates now."; + 131="Enhanced certificate download to fallback to mkcert.org, so all (commonly trusted) root certificates are available now."; + 132="Split off plugins from 'check-health', so the script works on all devices to monitor CPU and RAM. The supported plugins for sensors in hardware are installed automatically."; + 133="Updated the default configuration for 'fw-addr-lists', deprecated lists were removed, a collective list was added."; + 134="Enhanced 'mod/notification-telegram' and 'telegram-chat' to support topics in groups."; + 135="Introduced helper function '\$GetTelegramChatId' for 'mod/notification-telegram' which helps retrieve information."; }; # 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\\n\" ] ] > 0) do={ /system/script/set name=\"mod/ssh-keys-import\" ssh-keys-import; \$ScriptInstallUpdate; }"; + 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={ }; }; }"; }; diff --git a/ospf-to-leds.rsc b/ospf-to-leds.rsc index 2dc8448..a8662b3 100644 --- a/ospf-to-leds.rsc +++ b/ospf-to-leds.rsc @@ -1,38 +1,49 @@ #!rsc by RouterOS # RouterOS script: ospf-to-leds -# Copyright (c) 2020-2023 Christian Hesse -# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md +# Copyright (c) 2020-2025 Christian Hesse +# https://rsc.eworm.de/COPYING.md +# +# requires RouterOS, version=7.15 # # visualize ospf instance state via leds -# https://git.eworm.de/cgit/routeros-scripts/about/doc/ospf-to-leds.md +# https://rsc.eworm.de/doc/ospf-to-leds.md -:local 0 "ospf-to-leds"; :global GlobalFunctionsReady; :while ($GlobalFunctionsReady != true) do={ :delay 500ms; } -:global LogPrintExit2; -:global ParseKeyValueStore; -:global ScriptLock; +:local ExitOK false; +:do { + :local ScriptName [ :jobname ]; -$ScriptLock $0; + :global LogPrint; + :global ParseKeyValueStore; + :global ScriptLock; -:foreach Instance in=[ /routing/ospf/instance/find where comment~"^ospf-to-leds," ] do={ - :local InstanceVal [ /routing/ospf/instance/get $Instance ]; - :local LED ([ $ParseKeyValueStore ($InstanceVal->"comment") ]->"leds"); - :local LEDType [ /system/leds/get [ find where leds=$LED ] type ]; - - :local NeighborCount 0; - :foreach Area in=[ /routing/ospf/area/find where instance=($InstanceVal->"name") ] do={ - :local AreaName [ /routing/ospf/area/get $Area name ]; - :set NeighborCount ($NeighborCount + [ :len [ /routing/ospf/neighbor/find where area=$AreaName ] ]); + :if ([ $ScriptLock $ScriptName ] = false) do={ + :set ExitOK true; + :error false; } - :if ($NeighborCount > 0 && $LEDType = "off") do={ - $LogPrintExit2 info $0 ("OSPF instance " . $InstanceVal->"name" . " has " . $NeighborCount . " neighbors, led on!") false; - /system/leds/set type=on [ find where leds=$LED ]; - } - :if ($NeighborCount = 0 && $LEDType = "on") do={ - $LogPrintExit2 info $0 ("OSPF instance " . $InstanceVal->"name" . " has no neighbors, led off!") false; - /system/leds/set type=off [ find where leds=$LED ]; + :foreach Instance in=[ /routing/ospf/instance/find where comment~"^ospf-to-leds," ] do={ + :local InstanceVal [ /routing/ospf/instance/get $Instance ]; + :local LED ([ $ParseKeyValueStore ($InstanceVal->"comment") ]->"leds"); + :local LEDType [ /system/leds/get [ find where leds=$LED ] type ]; + + :local NeighborCount 0; + :foreach Area in=[ /routing/ospf/area/find where instance=($InstanceVal->"name") ] do={ + :local AreaName [ /routing/ospf/area/get $Area name ]; + :set NeighborCount ($NeighborCount + [ :len [ /routing/ospf/neighbor/find where area=$AreaName ] ]); + } + + :if ($NeighborCount > 0 && $LEDType = "off") do={ + $LogPrint info $ScriptName ("OSPF instance " . $InstanceVal->"name" . " has " . $NeighborCount . " neighbors, led on!"); + /system/leds/set type=on [ find where leds=$LED ]; + } + :if ($NeighborCount = 0 && $LEDType = "on") do={ + $LogPrint info $ScriptName ("OSPF instance " . $InstanceVal->"name" . " has no neighbors, led off!"); + /system/leds/set type=off [ find where leds=$LED ]; + } } +} on-error={ + :global ExitError; $ExitError $ExitOK [ :jobname ]; } diff --git a/packages-update.rsc b/packages-update.rsc index caaac6a..b11596e 100644 --- a/packages-update.rsc +++ b/packages-update.rsc @@ -1,107 +1,168 @@ #!rsc by RouterOS # RouterOS script: packages-update -# Copyright (c) 2019-2023 Christian Hesse -# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md +# Copyright (c) 2019-2025 Christian Hesse +# https://rsc.eworm.de/COPYING.md +# +# requires RouterOS, version=7.15 +# requires device-mode, scheduler # # download packages and reboot for installation -# https://git.eworm.de/cgit/routeros-scripts/about/doc/packages-update.md +# https://rsc.eworm.de/doc/packages-update.md -:local 0 "packages-update"; :global GlobalFunctionsReady; :while ($GlobalFunctionsReady != true) do={ :delay 500ms; } -:global DownloadPackage; -:global Grep; -:global LogPrintExit2; -:global ParseKeyValueStore; -:global ScriptFromTerminal; -:global ScriptLock; -:global VersionToNum; +:local ExitOK false; +:do { + :local ScriptName [ :jobname ]; -$ScriptLock $0; + :global BackupRandomDelay; + :global PackagesUpdateDeferReboot; + :global PackagesUpdateBackupFailure; -:local Update [ /system/package/update/get ]; + :global DownloadPackage; + :global Grep; + :global LogPrint; + :global ParseKeyValueStore; + :global ScriptFromTerminal; + :global ScriptLock; + :global VersionToNum; -:if ([ :typeof ($Update->"latest-version") ] = "nothing") do={ - $LogPrintExit2 warning $0 ("Latest version is not known.") true; -} + :local Schedule do={ + :local ScriptName [ :tostr $1 ]; -:if ($Update->"installed-version" = $Update->"latest-version") do={ - $LogPrintExit2 info $0 ("Version " . $Update->"latest-version" . " is already installed.") true; -} + :global GetRandomNumber; + :global LogPrint; -:local NumInstalled [ $VersionToNum ($Update->"installed-version") ]; -:local NumLatest [ $VersionToNum ($Update->"latest-version") ]; - -:local DoDowngrade false; -:if ($NumInstalled > $NumLatest) do={ - :if ([ $ScriptFromTerminal $0 ] = true) do={ - :put "Latest version is older than installed one. Want to downgrade? [y/N]"; - :if (([ /terminal/inkey timeout=60 ] % 32) = 25) do={ - :set DoDowngrade true; - } else={ - :put "Canceled..."; - } - } else={ - $LogPrintExit2 warning $0 ("Not installing downgrade automatically.") true; - } -} - -:foreach Package in=[ /system/package/find where !bundle ] do={ - :local PkgName [ /system/package/get $Package name ]; - :if ([ $DownloadPackage $PkgName ($Update->"latest-version") ] = false) do={ - $LogPrintExit2 error $0 ("Download for package " . $PkgName . " failed, update aborted.") true; - } -} - -:local RunOrder ({}); -:foreach Script in=[ /system/script/find where source~("\n# provides: backup-script\\b") ] do={ - :local ScriptVal [ /system/script/get $Script ]; - :local Store [ $ParseKeyValueStore [ $Grep ($ScriptVal->"source") ("\23 provides: backup-script, ") ] ]; - - :set ($RunOrder->($Store->"order" . "-" . $ScriptVal->"name")) ($ScriptVal->"name"); -} - -:foreach Order,Script in=$RunOrder do={ - :do { - $LogPrintExit2 info $0 ("Running backup script " . $Script . " before update.") false; - /system/script/run $Script; - } on-error={ - $LogPrintExit2 warning $0 ("Running backup script " . $Script . " before update failed!") false; - :if ([ $ScriptFromTerminal $0 ] = true) do={ - :put "Do you want to continue anyway? [y/N]"; - :if (([ /terminal/inkey timeout=60 ] % 32) = 25) do={ - $LogPrintExit2 info $0 ("User requested to continue anyway.") false; - } else={ - $LogPrintExit2 info $0 ("Canceled update...") true; - } - } else={ - $LogPrintExit2 info $0 ("Canceled non-interactive update.") true; - } - } -} - -:if ($DoDowngrade = true) do={ - $LogPrintExit2 info $0 ("Rebooting for downgrade.") false; - :delay 1s; - /system/package/downgrade; -} - -:if ([ $ScriptFromTerminal $0 ] = true) do={ - :put "Do you want to (s)chedule reboot or (r)eboot now? [s/R]"; - :if (([ /terminal/inkey timeout=60 ] % 32) = 19) do={ :global RebootForUpdate do={ - :global RandomDelay; - $RandomDelay 3600; /system/reboot; } - /system/scheduler/add name="\$RebootForUpdate" start-time=03:00:00 interval=1d \ - on-event=("/system/scheduler/remove \"\\\$RebootForUpdate\"; " . \ - ":global RebootForUpdate; \$RebootForUpdate;"); - $LogPrintExit2 info $0 ("Scheduled reboot for update between 03:00 and 04:00.") true; - } -} -$LogPrintExit2 info $0 ("Rebooting for update.") false; -:delay 1s; -/system/reboot; + :local StartTime [ :tostr [ :totime (10800 + [ $GetRandomNumber 7200 ]) ] ]; + /system/scheduler/add name="_RebootForUpdate" start-time=$StartTime interval=1d \ + on-event=("/system/scheduler/remove \"_RebootForUpdate\"; " . \ + ":global RebootForUpdate; \$RebootForUpdate;"); + $LogPrint info $ScriptName ("Scheduled reboot for update at " . $StartTime . \ + " local time (" . [ /system/clock/get time-zone-name ] . ")."); + :return true; + } + + :if ([ $ScriptLock $ScriptName ] = false) do={ + :set ExitOK true; + :error false; + } + + :if ([ :len [ /system/scheduler/find where name="running-from-backup-partition" ] ] > 0) do={ + $LogPrint warning $ScriptName ("Running from backup partition, refusing to act."); + :set ExitOK true; + :error false; + } + + :local Update [ /system/package/update/get ]; + + :if ([ :typeof ($Update->"latest-version") ] = "nothing") do={ + $LogPrint warning $ScriptName ("Latest version is not known."); + :set ExitOK true; + :error false; + } + + :if ($Update->"installed-version" = $Update->"latest-version") do={ + $LogPrint info $ScriptName ("Version " . $Update->"latest-version" . " is already installed."); + :set ExitOK true; + :error true; + } + + :local RunOrder ({}); + :foreach Script in=[ /system/script/find where source~("\n# provides: backup-script\\b") ] do={ + :local ScriptVal [ /system/script/get $Script ]; + :local Store [ $ParseKeyValueStore [ $Grep ($ScriptVal->"source") ("\23 provides: backup-script, ") ] ]; + + :set ($RunOrder->($Store->"order" . "-" . $ScriptVal->"name")) ($ScriptVal->"name"); + } + + :local BackupRandomDelayBefore $BackupRandomDelay; + :foreach Order,Script in=$RunOrder do={ + :set BackupRandomDelay 0; + :set PackagesUpdateBackupFailure false; + :do { + $LogPrint info $ScriptName ("Running backup script " . $Script . " before update."); + /system/script/run $Script; + } on-error={ + :set PackagesUpdateBackupFailure true; + } + :set BackupRandomDelay $BackupRandomDelayBefore; + + :if ($PackagesUpdateBackupFailure = true) do={ + $LogPrint warning $ScriptName ("Running backup script " . $Script . " before update failed!"); + :if ([ $ScriptFromTerminal $ScriptName ] = true) do={ + :put "Do you want to continue anyway? [y/N]"; + :if (([ /terminal/inkey timeout=60 ] % 32) = 25) do={ + $LogPrint info $ScriptName ("User requested to continue anyway."); + } else={ + $LogPrint info $ScriptName ("Canceled update..."); + :set ExitOK true; + :error false; + } + } else={ + $LogPrint warning $ScriptName ("Canceled non-interactive update."); + :set ExitOK true; + :error false; + } + } + } + + :local NumInstalled [ $VersionToNum ($Update->"installed-version") ]; + :local NumLatest [ $VersionToNum ($Update->"latest-version") ]; + + :local DoDowngrade false; + :if ($NumInstalled > $NumLatest) do={ + :if ([ $ScriptFromTerminal $ScriptName ] = true) do={ + :put "Latest version is older than installed one. Want to downgrade? [y/N]"; + :if (([ /terminal/inkey timeout=60 ] % 32) = 25) do={ + :set DoDowngrade true; + } else={ + :put "Canceled..."; + } + } else={ + $LogPrint warning $ScriptName ("Not installing downgrade automatically."); + :set ExitOK true; + :error false; + } + } + + :foreach Package in=[ /system/package/find where !bundle !available ] do={ + :local PkgName [ /system/package/get $Package name ]; + :if ([ $DownloadPackage $PkgName ($Update->"latest-version") ] = false) do={ + $LogPrint error $ScriptName ("Download for package " . $PkgName . " failed, update aborted."); + :set ExitOK true; + :error false; + } + } + + :if ($DoDowngrade = true) do={ + $LogPrint info $ScriptName ("Rebooting for downgrade."); + :delay 1s; + /system/package/downgrade; + } + + :if ([ $ScriptFromTerminal $ScriptName ] = true) do={ + :put "Do you want to (s)chedule reboot or (r)eboot now? [s/R]"; + :if (([ /terminal/inkey timeout=60 ] % 32) = 19) do={ + $Schedule $ScriptName; + :set ExitOK true; + :error true; + } + } else={ + :if ($PackagesUpdateDeferReboot = true) do={ + $Schedule $ScriptName; + :set ExitOK true; + :error true; + } + } + + $LogPrint info $ScriptName ("Rebooting for update."); + :delay 1s; + /system/reboot; +} on-error={ + :global ExitError; $ExitError $ExitOK [ :jobname ]; +} diff --git a/ppp-on-up.rsc b/ppp-on-up.rsc index ac01c97..e09bd9d 100644 --- a/ppp-on-up.rsc +++ b/ppp-on-up.rsc @@ -1,34 +1,44 @@ #!rsc by RouterOS # RouterOS script: ppp-on-up -# Copyright (c) 2013-2023 Christian Hesse -# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md +# Copyright (c) 2013-2025 Christian Hesse +# https://rsc.eworm.de/COPYING.md +# +# requires RouterOS, version=7.15 # # run scripts on ppp up -# https://git.eworm.de/cgit/routeros-scripts/about/doc/ppp-on-up.md +# https://rsc.eworm.de/doc/ppp-on-up.md -:local 0 "ppp-on-up"; :global GlobalFunctionsReady; :while ($GlobalFunctionsReady != true) do={ :delay 500ms; } -:global LogPrintExit2; +:local ExitOK false; +:do { + :local ScriptName [ :jobname ]; -:local Interface $interface; + :global LogPrint; -:if ([ :typeof $Interface ] = "nothing") do={ - $LogPrintExit2 error $0 ("This script is supposed to run from ppp on-up script hook.") true; -} + :local Interface $interface; -:local IntName [ /interface/get $Interface name ]; -$LogPrintExit2 info $0 ("PPP interface " . $IntName . " is up.") false; - -/ipv6/dhcp-client/release [ find where interface=$IntName !disabled ]; - -:foreach Script in=[ /system/script/find where source~("\n# provides: ppp-on-up\n") ] do={ - :local ScriptName [ /system/script/get $Script name ]; - :do { - $LogPrintExit2 debug $0 ("Running script: " . $ScriptName) false; - /system/script/run $Script; - } on-error={ - $LogPrintExit2 warning $0 ("Running script '" . $ScriptName . "' failed!") false; + :if ([ :typeof $Interface ] = "nothing") do={ + $LogPrint error $ScriptName ("This script is supposed to run from ppp on-up script hook."); + :set ExitOK true; + :error false; } + + :local IntName [ /interface/get $Interface name ]; + $LogPrint info $ScriptName ("PPP interface " . $IntName . " is up."); + + /ipv6/dhcp-client/release [ find where interface=$IntName !disabled bound ]; + + :foreach Script in=[ /system/script/find where source~("\n# provides: ppp-on-up\r?\n") ] do={ + :local ScriptName [ /system/script/get $Script name ]; + :do { + $LogPrint debug $ScriptName ("Running script: " . $ScriptName); + /system/script/run $Script; + } on-error={ + $LogPrint warning $ScriptName ("Running script '" . $ScriptName . "' failed!"); + } + } +} on-error={ + :global ExitError; $ExitError $ExitOK [ :jobname ]; } diff --git a/sms-action.rsc b/sms-action.rsc index f5de11f..3c8307a 100644 --- a/sms-action.rsc +++ b/sms-action.rsc @@ -1,31 +1,41 @@ #!rsc by RouterOS # RouterOS script: sms-action -# Copyright (c) 2018-2023 Christian Hesse -# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md +# Copyright (c) 2018-2025 Christian Hesse +# https://rsc.eworm.de/COPYING.md +# +# requires RouterOS, version=7.15 # # run action on received SMS -# https://git.eworm.de/cgit/routeros-scripts/about/doc/sms-action.md +# https://rsc.eworm.de/doc/sms-action.md -:local 0 "sms-action"; :global GlobalFunctionsReady; :while ($GlobalFunctionsReady != true) do={ :delay 500ms; } -:global SmsAction; +:local ExitOK false; +:do { + :local ScriptName [ :jobname ]; -:global LogPrintExit2; -:global ValidateSyntax; + :global SmsAction; -:local Action $action; + :global LogPrint; + :global ValidateSyntax; -:if ([ :typeof $Action ] = "nothing") do={ - $LogPrintExit2 error $0 ("This script is supposed to run from SMS hook with action=...") true; -} - -:local Code ($SmsAction->$Action); -:if ([ $ValidateSyntax $Code ] = true) do={ - :log info ("Acting on SMS action '" . $Action . "': " . $Code); - :delay 1s; - [ :parse $Code ]; -} else={ - $LogPrintExit2 warning $0 ("The code for action '" . $Action . "' failed syntax validation!") false; + :local Action $action; + + :if ([ :typeof $Action ] = "nothing") do={ + $LogPrint error $ScriptName ("This script is supposed to run from SMS hook with action=..."); + :set ExitOK true; + :error false; + } + + :local Code ($SmsAction->$Action); + :if ([ $ValidateSyntax $Code ] = true) do={ + :log info ("Acting on SMS action '" . $Action . "': " . $Code); + :delay 1s; + [ :parse $Code ]; + } else={ + $LogPrint warning $ScriptName ("The code for action '" . $Action . "' failed syntax validation!"); + } +} on-error={ + :global ExitError; $ExitError $ExitOK [ :jobname ]; } diff --git a/sms-forward.rsc b/sms-forward.rsc index 4f2d2e6..8169022 100644 --- a/sms-forward.rsc +++ b/sms-forward.rsc @@ -1,89 +1,101 @@ #!rsc by RouterOS # RouterOS script: sms-forward -# Copyright (c) 2013-2023 Christian Hesse +# Copyright (c) 2013-2025 Christian Hesse # Anatoly Bubenkov -# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md +# https://rsc.eworm.de/COPYING.md +# +# requires RouterOS, version=7.15 # # forward SMS to e-mail -# https://git.eworm.de/cgit/routeros-scripts/about/doc/sms-forward.md +# https://rsc.eworm.de/doc/sms-forward.md -:local 0 "sms-forward"; :global GlobalFunctionsReady; :while ($GlobalFunctionsReady != true) do={ :delay 500ms; } -:global Identity; -:global SmsForwardHooks; +:local ExitOK false; +:do { + :local ScriptName [ :jobname ]; -:global IfThenElse; -:global LogPrintExit2; -:global ScriptLock; -:global SendNotification2; -:global SymbolForNotification; -:global ValidateSyntax; -:global WaitFullyConnected; + :global Identity; + :global SmsForwardHooks; -$ScriptLock $0; + :global IfThenElse; + :global LogPrint; + :global LogPrintOnce; + :global ScriptLock; + :global SendNotification2; + :global SymbolForNotification; + :global ValidateSyntax; + :global WaitFullyConnected; -:if ([ /tool/sms/get receive-enabled ] = false) do={ - $LogPrintExit2 warning $0 ("Receiving of SMS is not enabled.") true; -} + :if ([ $ScriptLock $ScriptName ] = false) do={ + :set ExitOK true; + :error false; + } -$WaitFullyConnected; + :if ([ /tool/sms/get receive-enabled ] = false) do={ + $LogPrintOnce warning $ScriptName ("Receiving of SMS is not enabled."); + :set ExitOK true; + :error false; + } -:local Settings [ /tool/sms/get ]; + $WaitFullyConnected; -:if ([ /interface/lte/get ($Settings->"port") running ] != true) do={ - $LogPrintExit2 info $0 ("The LTE interface is not in running state, skipping.") true; -} + :local Settings [ /tool/sms/get ]; -# forward SMS in a loop -:while ([ :len [ /tool/sms/inbox/find ] ] > 0) do={ - :local Phone [ /tool/sms/inbox/get ([ find ]->0) phone ]; - :local Messages ""; - :local Delete ({}); + :if ([ /interface/lte/get ($Settings->"port") running ] != true) do={ + $LogPrint info $ScriptName ("The LTE interface is not in running state, skipping."); + :set ExitOK true; + :error true; + } - :foreach Sms in=[ /tool/sms/inbox/find where phone=$Phone ] do={ - :local SmsVal [ /tool/sms/inbox/get $Sms ]; + # forward SMS in a loop + :while ([ :len [ /tool/sms/inbox/find ] ] > 0) do={ + :local Phone [ /tool/sms/inbox/get ([ find ]->0) phone ]; + :local Messages ""; + :local Delete ({}); - :if ($Phone = $Settings->"allowed-number" && \ - ($SmsVal->"message")~("^:cmd " . $Settings->"secret" . " script ")) do={ - $LogPrintExit2 debug $0 ("Removing SMS, which started a script.") false; - /tool/sms/inbox/remove $Sms; - } else={ - :set Messages ($Messages . "\n\nOn " . $SmsVal->"timestamp" . \ - " type " . $SmsVal->"type" . ":\n" . $SmsVal->"message"); - :foreach Hook in=$SmsForwardHooks do={ - :if ($Phone~($Hook->"allowed-number") && ($SmsVal->"message")~($Hook->"match")) do={ - :if ([ $ValidateSyntax ($Hook->"command") ] = true) do={ - $LogPrintExit2 info $0 ("Running hook '" . $Hook->"match" . "': " . \ - $Hook->"command") false; - :do { - :local Command [ :parse ($Hook->"command") ]; - $Command Phone=$Phone Message=($SmsVal->"message"); - :set Messages ($Messages . "\n\nRan hook '" . $Hook->"match" . "':\n" . \ - $Hook->"command"); - } on-error={ - $LogPrintExit2 warning $0 ("The code for hook '" . $Hook->"match" . \ - "' failed to run!") false; + :foreach Sms in=[ /tool/sms/inbox/find where phone=$Phone ] do={ + :local SmsVal [ /tool/sms/inbox/get $Sms ]; + + :if ($Phone = $Settings->"allowed-number" && \ + ($SmsVal->"message")~("^:cmd " . $Settings->"secret" . " script ")) do={ + $LogPrint debug $ScriptName ("Removing SMS, which started a script."); + /tool/sms/inbox/remove $Sms; + } else={ + :set Messages ($Messages . "\n\nOn " . $SmsVal->"timestamp" . \ + " type " . $SmsVal->"type" . ":\n" . $SmsVal->"message"); + :foreach Hook in=$SmsForwardHooks do={ + :if ($Phone~($Hook->"allowed-number") && ($SmsVal->"message")~($Hook->"match")) do={ + :if ([ $ValidateSyntax ($Hook->"command") ] = true) do={ + $LogPrint info $ScriptName ("Running hook '" . $Hook->"match" . "': " . $Hook->"command"); + :do { + :local Command [ :parse ($Hook->"command") ]; + $Command Phone=$Phone Message=($SmsVal->"message"); + :set Messages ($Messages . "\n\nRan hook '" . $Hook->"match" . "':\n" . $Hook->"command"); + } on-error={ + $LogPrint warning $ScriptName ("The code for hook '" . $Hook->"match" . "' failed to run!"); + } + } else={ + $LogPrint warning $ScriptName ("The code for hook '" . $Hook->"match" . "' failed syntax validation!"); } - } else={ - $LogPrintExit2 warning $0 ("The code for hook '" . $Hook->"match" . \ - "' failed syntax validation!") false; } } + :set Delete ($Delete, $Sms); } - :set Delete ($Delete, $Sms); } - } - :if ([ :len $Messages ] > 0) do={ - :local Count [ :len $Delete ]; - $SendNotification2 ({ origin=$0; \ - subject=([ $SymbolForNotification "incoming-envelope" ] . "SMS Forwarding from " . $Phone); \ - message=("Received " . [ $IfThenElse ($Count = 1) "this message" ("these " . $Count . " messages") ] . \ - " by " . $Identity . " from " . $Phone . ":" . $Messages) }); - :foreach Sms in=$Delete do={ - /tool/sms/inbox/remove $Sms; + :if ([ :len $Messages ] > 0) do={ + :local Count [ :len $Delete ]; + $SendNotification2 ({ origin=$ScriptName; \ + subject=([ $SymbolForNotification "incoming-envelope" ] . "SMS Forwarding from " . $Phone); \ + message=("Received " . [ $IfThenElse ($Count = 1) "this message" ("these " . $Count . " messages") ] . \ + " by " . $Identity . " from " . $Phone . ":" . $Messages) }); + :foreach Sms in=$Delete do={ + /tool/sms/inbox/remove $Sms; + } } } +} on-error={ + :global ExitError; $ExitError $ExitOK [ :jobname ]; } diff --git a/super-mario-theme.rsc b/super-mario-theme.rsc index 7787a12..726c526 100644 --- a/super-mario-theme.rsc +++ b/super-mario-theme.rsc @@ -1,10 +1,10 @@ #!rsc by RouterOS # RouterOS script: super-mario-theme -# Copyright (c) 2013-2023 Christian Hesse -# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md +# Copyright (c) 2013-2025 Christian Hesse +# https://rsc.eworm.de/COPYING.md # # play Super Mario theme -# https://git.eworm.de/cgit/routeros-scripts/about/doc/super-mario-theme.md +# https://rsc.eworm.de/doc/super-mario-theme.md :local Beeps { { 660; 100 }; 150; { 660; 100 }; 300; { 660; 100 }; 300; diff --git a/telegram-chat.rsc b/telegram-chat.rsc index 686becc..5db4860 100644 --- a/telegram-chat.rsc +++ b/telegram-chat.rsc @@ -1,154 +1,195 @@ #!rsc by RouterOS # RouterOS script: telegram-chat -# Copyright (c) 2023 Christian Hesse -# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md +# Copyright (c) 2023-2025 Christian Hesse +# https://rsc.eworm.de/COPYING.md +# +# requires RouterOS, version=7.15 +# requires device-mode, fetch # # use Telegram to chat with your Router and send commands -# https://git.eworm.de/cgit/routeros-scripts/about/doc/telegram-chat.md +# https://rsc.eworm.de/doc/telegram-chat.md -:local 0 "telegram-chat"; :global GlobalFunctionsReady; :while ($GlobalFunctionsReady != true) do={ :delay 500ms; } -:global Identity; -:global TelegramChatActive; -:global TelegramChatGroups; -:global TelegramChatId; -:global TelegramChatIdsTrusted; -:global TelegramChatOffset; -:global TelegramChatRunTime; -:global TelegramMessageIDs; -:global TelegramTokenId; +:local ExitOK false; +:do { + :local ScriptName [ :jobname ]; -:global CertificateAvailable; -:global EitherOr; -:global EscapeForRegEx; -:global GetRandom20CharAlNum; -:global IfThenElse; -:global LogPrintExit2; -:global MkDir; -:global ParseJson; -:global ScriptLock; -:global SendTelegram2; -:global SymbolForNotification; -:global ValidateSyntax; -:global WaitForFile; -:global WaitFullyConnected; + :global Identity; + :global TelegramChatActive; + :global TelegramChatGroups; + :global TelegramChatId; + :global TelegramChatIdsTrusted; + :global TelegramChatOffset; + :global TelegramChatRunTime; + :global TelegramMessageIDs; + :global TelegramRandomDelay; + :global TelegramTokenId; -$ScriptLock $0; + :global CertificateAvailable; + :global EitherOr; + :global EscapeForRegEx; + :global GetRandom20CharAlNum; + :global IfThenElse; + :global LogPrint; + :global MAX; + :global MIN; + :global MkDir; + :global RandomDelay; + :global RmDir; + :global ScriptLock; + :global SendTelegram2; + :global SymbolForNotification; + :global ValidateSyntax; + :global WaitForFile; + :global WaitFullyConnected; -$WaitFullyConnected; + :if ([ $ScriptLock $ScriptName ] = false) do={ + :set ExitOK true; + :error false; + } -:if ([ :typeof $TelegramChatOffset ] != "array") do={ - :set TelegramChatOffset { 0; 0; 0 }; -} + $WaitFullyConnected; -:if ([ $CertificateAvailable "Go Daddy Secure Certificate Authority - G2" ] = false) do={ - $LogPrintExit2 warning $0 ("Downloading required certificate failed.") true; -} + :if ([ :typeof $TelegramChatOffset ] != "array") do={ + :set TelegramChatOffset { 0; 0; 0 }; + } + :if ([ :typeof $TelegramRandomDelay ] != "num") do={ + :set TelegramRandomDelay 0; + } -:local Data false; -:for I from=1 to=4 do={ - :if ($Data = false) do={ - :do { - :set Data ([ /tool/fetch check-certificate=yes-without-crl output=user \ - ("https://api.telegram.org/bot" . $TelegramTokenId . "/getUpdates?offset=" . \ - $TelegramChatOffset->0 . "&allowed_updates=%5B%22message%22%5D") as-value ]->"data"); - } on-error={ - :if ($I < 4) do={ - $LogPrintExit2 debug $0 ("Fetch failed, " . $I . ". try.") false; - :delay (($I * $I) "s"); + :if ([ $CertificateAvailable "Go Daddy Root Certificate Authority - G2" ] = false) do={ + $LogPrint warning $ScriptName ("Downloading required certificate failed."); + :set ExitOK true; + :error false; + } + + $RandomDelay $TelegramRandomDelay; + + :local Data false; + :for I from=1 to=4 do={ + :if ($Data = false) do={ + :do { + :set Data ([ /tool/fetch check-certificate=yes-without-crl output=user \ + ("https://api.telegram.org/bot" . $TelegramTokenId . "/getUpdates?offset=" . \ + $TelegramChatOffset->0 . "&allowed_updates=%5B%22message%22%5D") as-value ]->"data"); + :set TelegramRandomDelay [ $MAX 0 ($TelegramRandomDelay - 1) ]; + } on-error={ + :if ($I < 4) do={ + $LogPrint debug $ScriptName ("Fetch failed, " . $I . ". try."); + :set TelegramRandomDelay [ $MIN 15 ($TelegramRandomDelay + 5) ]; + :delay (($I * $I) . "s"); + } } } } -} -:if ($Data = false) do={ - $LogPrintExit2 warning $0 ("Failed getting updates from Telegram.") true; -} + :if ($Data = false) do={ + $LogPrint warning $ScriptName ("Failed getting updates."); + :set ExitOK true; + :error false; + } -:local UpdateID 0; -:local Uptime [ /system/resource/get uptime ]; -:foreach UpdateArray in=([ $ParseJson $Data ]->"result") do={ - :local Update [ $ParseJson $UpdateArray ]; - :set UpdateID ($Update->"update_id"); - :local Message [ $ParseJson ($Update->"message") ]; - :local IsReply [ :len ($Message->"reply_to_message") ]; - :local IsMyReply ($TelegramMessageIDs->([ $ParseJson ($Message->"reply_to_message") ]->"message_id")); - :if (($IsMyReply = 1 || $TelegramChatOffset->0 > 0 || $Uptime > 5m) && $UpdateID >= $TelegramChatOffset->2) do={ - :local Trusted false; - :local Chat [ $ParseJson ($Message->"chat") ]; - :local From [ $ParseJson ($Message->"from") ]; + :local JSON [ :deserialize from=json value=$Data ]; + :local UpdateID 0; + :local Uptime [ /system/resource/get uptime ]; + :foreach Update in=($JSON->"result") do={ + :set UpdateID ($Update->"update_id"); + :local Message ($Update->"message"); + :local IsReply ([ :typeof ($Message->"reply_to_message") ] = "string"); + :local IsMyReply ($TelegramMessageIDs->[ :tostr ($Message->"reply_to_message"->"message_id") ]); + :if (($IsMyReply = 1 || $TelegramChatOffset->0 > 0 || $Uptime > 5m) && $UpdateID >= $TelegramChatOffset->2) do={ + :local Trusted false; + :local Chat ($Message->"chat"); + :local From ($Message->"from"); + :local Command ($Message->"text"); + :local ThreadId [ $IfThenElse ($Message->"is_topic_message") ($Message->"message_thread_id") "" ]; - :foreach IdsTrusted in=($TelegramChatId, $TelegramChatIdsTrusted) do={ - :if ($From->"id" = $IdsTrusted || $From->"username" = $IdsTrusted) do={ - :set Trusted true; - } - } - - :if ($Trusted = true) do={ - :local Done false; - :if ($Message->"text" = "?") do={ - $LogPrintExit2 info $0 ("Sending notice for update " . $UpdateID . ".") false; - $SendTelegram2 ({ origin=$0; chatid=($Chat->"id"); silent=true; replyto=($Message->"message_id"); \ - subject=([ $SymbolForNotification "speech-balloon" ] . "Telegram Chat"); \ - message=("Online, awaiting your commands!") }); - :set Done true; - } - :if ($Done = false && [ :pick ($Message->"text") 0 1 ] = "!") do={ - :if ($Message->"text" ~ ("^! *(" . [ $EscapeForRegEx $Identity ] . "|@" . $TelegramChatGroups . ")\$")) do={ - :set TelegramChatActive true; - } else={ - :set TelegramChatActive false; + :foreach IdsTrusted in=($TelegramChatId, $TelegramChatIdsTrusted) do={ + :if ($From->"id" = $IdsTrusted || $From->"username" = $IdsTrusted) do={ + :set Trusted true; } - $LogPrintExit2 info $0 ("Now " . [ $IfThenElse $TelegramChatActive "active" "passive" ] . \ - " from update " . $UpdateID . "!") false; - :set Done true; } - :if ($Done = false && ($IsMyReply = 1 || ($IsReply = 0 && $TelegramChatActive = true)) && [ :len ($Message->"text") ] > 0) do={ - :if ([ $ValidateSyntax ($Message->"text") ] = true) do={ - :local State ""; - :local File ("tmpfs/telegram-chat/" . [ $GetRandom20CharAlNum 6 ]); - $MkDir "tmpfs/telegram-chat"; - $LogPrintExit2 info $0 ("Running command from update " . $UpdateID . ": " . $Message->"text") false; - :execute script=(":do {\n" . $Message->"text" . "\n} on-error={ /file/add name=\"" . $File . ".failed\" };" . \ - "/file/add name=\"" . $File . ".done\"") file=$File; - :if ([ $WaitForFile ($File . ".done") [ $EitherOr $TelegramChatRunTime 20s ] ] = false) do={ - :set State "The command did not finish, still running in background.\n\n"; - } - :if ([ :len [ /file/find where name=($File . ".failed") ] ] > 0) do={ - :set State "The command failed with an error!\n\n"; - } - :local Content [ /file/get ($File . ".txt") contents ]; - $SendTelegram2 ({ origin=$0; chatid=($Chat->"id"); silent=true; replyto=($Message->"message_id"); \ + + :if ($Trusted = true) do={ + :local Done false; + :if ($Command = "?") do={ + $LogPrint info $ScriptName ("Sending notice for update " . $UpdateID . "."); + $SendTelegram2 ({ origin=$ScriptName; chatid=($Chat->"id"); silent=true; \ + replyto=($Message->"message_id"); threadid=$ThreadId; \ subject=([ $SymbolForNotification "speech-balloon" ] . "Telegram Chat"); \ - message=("Command:\n" . $Message->"text" . "\n\n" . $State . [ $IfThenElse ([ :len $Content ] > 0) \ - ("Output:\n" . $Content) [ $IfThenElse ([ /file/get ($File . ".txt") size ] > 0) \ - ("Output exceeds file read size.") ("No output.") ] ]) }); - /file/remove "tmpfs/telegram-chat"; + message=([ $IfThenElse ([ :len ($From->"first_name") ] > 0) ("Hello " . ($From->"first_name") . "!\n\n") ] . \ + "Online" . [ $IfThenElse $TelegramChatActive " (and active!)" ] . ", awaiting your commands!") }); + :set Done true; + } + :if ($Done = false && [ :pick $Command 0 1 ] = "!") do={ + :if ($Command ~ ("^! *(" . [ $EscapeForRegEx $Identity ] . "|@" . $TelegramChatGroups . ")\$")) do={ + :set TelegramChatActive true; + } else={ + :set TelegramChatActive false; + } + $LogPrint info $ScriptName ("Now " . [ $IfThenElse $TelegramChatActive "active" "passive" ] . \ + " from update " . $UpdateID . "!"); + :set Done true; + } + :if ($Done = false && ($IsMyReply = 1 || ($IsReply = false && \ + $TelegramChatActive = true)) && [ :len $Command ] > 0) do={ + :if ([ $ValidateSyntax $Command ] = true) do={ + :local State ""; + :local File ("tmpfs/telegram-chat/" . [ $GetRandom20CharAlNum 6 ]); + :if ([ $MkDir "tmpfs/telegram-chat" ] = false) do={ + $LogPrint error $ScriptName ("Failed creating directory!"); + :set ExitOK true; + :error false; + } + $LogPrint info $ScriptName ("Running command from update " . $UpdateID . ": " . $Command); + :execute script=(":do {\n" . $Command . "\n} on-error={ /file/add name=\"" . $File . ".failed\" };" . \ + "/file/add name=\"" . $File . ".done\"") file=($File . "\00"); + :if ([ $WaitForFile ($File . ".done") [ $EitherOr $TelegramChatRunTime 20s ] ] = false) do={ + :set State ([ $SymbolForNotification "warning-sign" ] . "The command did not finish, still running in background.\n\n"); + } + :if ([ :len [ /file/find where name=($File . ".failed") ] ] > 0) do={ + :set State ([ $SymbolForNotification "cross-mark" ] . "The command failed with an error!\n\n"); + } + :local Content ([ /file/read chunk-size=32768 file=$File as-value ]->"data"); + $SendTelegram2 ({ origin=$ScriptName; chatid=($Chat->"id"); silent=true; \ + replyto=($Message->"message_id"); threadid=$ThreadId; \ + subject=([ $SymbolForNotification "speech-balloon" ] . "Telegram Chat"); \ + message=([ $SymbolForNotification "gear" ] . "Command:\n" . $Command . "\n\n" . \ + $State . [ $IfThenElse ([ :len $Content ] > 0) \ + ([ $SymbolForNotification "memo" ] . "Output:\n" . $Content) \ + ([ $SymbolForNotification "memo" ] . "No output.") ]) }); + $RmDir "tmpfs/telegram-chat"; + } else={ + $LogPrint info $ScriptName ("The command from update " . $UpdateID . " failed syntax validation!"); + $SendTelegram2 ({ origin=$ScriptName; chatid=($Chat->"id"); silent=false; \ + replyto=($Message->"message_id"); threadid=$ThreadId; \ + subject=([ $SymbolForNotification "speech-balloon" ] . "Telegram Chat"); \ + message=([ $SymbolForNotification "gear" ] . "Command:\n" . $Command . "\n\n" . \ + [ $SymbolForNotification "cross-mark" ] . "The command failed syntax validation!") }); + } + } + } else={ + :local MessageText ("Received a message from untrusted contact " . \ + [ $IfThenElse ([ :len ($From->"username") ] = 0) "without username" ("'" . $From->"username" . "'") ] . \ + " (ID " . $From->"id" . ") in update " . $UpdateID . "!"); + :if ($Command ~ ("^! *" . [ $EscapeForRegEx $Identity ] . "\$")) do={ + $LogPrint warning $ScriptName $MessageText; + $SendTelegram2 ({ origin=$ScriptName; chatid=($Chat->"id"); silent=false; \ + replyto=($Message->"message_id"); threadid=$ThreadId; \ + subject=([ $SymbolForNotification "speech-balloon" ] . "Telegram Chat"); \ + message=("You are not trusted.") }); } else={ - $LogPrintExit2 info $0 ("The command from update " . $UpdateID . " failed syntax validation!") false; - $SendTelegram2 ({ origin=$0; chatid=($Chat->"id"); silent=false; replyto=($Message->"message_id"); \ - subject=([ $SymbolForNotification "speech-balloon" ] . "Telegram Chat"); \ - message=("Command:\n" . $Message->"text" . "\n\nThe command failed syntax validation!") }); + $LogPrint info $ScriptName $MessageText; } } } else={ - :local MessageText ("Received a message from untrusted contact " . \ - [ $IfThenElse ([ :len ($From->"username") ] = 0) "without username" ("'" . $From->"username" . "'") ] . \ - " (ID " . $From->"id" . ") in update " . $UpdateID . "!"); - :if ($Message->"text" ~ ("^! *" . [ $EscapeForRegEx $Identity ] . "\$")) do={ - $LogPrintExit2 warning $0 $MessageText false; - $SendTelegram2 ({ origin=$0; chatid=($Chat->"id"); silent=false; replyto=($Message->"message_id"); \ - subject=([ $SymbolForNotification "speech-balloon" ] . "Telegram Chat"); \ - message=("You are not trusted.") }); - } else={ - $LogPrintExit2 info $0 $MessageText false; - } + $LogPrint debug $ScriptName ("Already handled update " . $UpdateID . "."); } - } else={ - $LogPrintExit2 debug $0 ("Already handled update " . $UpdateID . ".") false; } + :set TelegramChatOffset ([ :pick $TelegramChatOffset 1 3 ], \ + [ $IfThenElse ($UpdateID >= $TelegramChatOffset->2) ($UpdateID + 1) ($TelegramChatOffset->2) ]); +} on-error={ + :global ExitError; $ExitError $ExitOK [ :jobname ]; } -:set TelegramChatOffset ([ :pick $TelegramChatOffset 1 3 ], \ - [ $IfThenElse ($UpdateID >= $TelegramChatOffset->2) ($UpdateID + 1) ($TelegramChatOffset->2) ]); diff --git a/unattended-lte-firmware-upgrade.rsc b/unattended-lte-firmware-upgrade.rsc index eac65c3..83925fd 100644 --- a/unattended-lte-firmware-upgrade.rsc +++ b/unattended-lte-firmware-upgrade.rsc @@ -1,16 +1,19 @@ #!rsc by RouterOS # RouterOS script: unattended-lte-firmware-upgrade -# Copyright (c) 2018-2023 Christian Hesse -# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md +# Copyright (c) 2018-2025 Christian Hesse +# https://rsc.eworm.de/COPYING.md +# +# requires RouterOS, version=7.15 +# requires device-mode, scheduler # # schedule unattended lte firmware upgrade -# https://git.eworm.de/cgit/routeros-scripts/about/doc/unattended-lte-firmware-upgrade.md +# https://rsc.eworm.de/doc/unattended-lte-firmware-upgrade.md :foreach Interface in=[ /interface/lte/find where running ] do={ :local Firmware; :local IntName [ /interface/lte/get $Interface name ]; :do { - :set Firmware [ /interface/lte/firmware-upgrade $Interface once as-value ]; + :set Firmware [ /interface/lte/firmware-upgrade $Interface as-value ]; } on-error={ :log debug ("Could not get latest LTE firmware version for interface " . $IntName . "."); } @@ -24,13 +27,17 @@ :set LTEFirmwareUpgrade; /system/scheduler/remove ($1 . "-firmware-upgrade"); - /interface/lte/firmware-upgrade $1 upgrade=yes; - :log info ("LTE firmware upgrade on '" . $1 . "' finished, waiting for reset."); - :delay 240s; - :local Firmware [ /interface/lte/firmware-upgrade $1 once as-value ]; - :if (($Firmware->"installed") != ($Firmware->"latest")) do={ - :log warning ("LTE firmware versions still differ. Resetting again..."); - /interface/lte/at-chat $1 input="AT+RESET"; + :do { + /interface/lte/firmware-upgrade $1 upgrade=yes; + :log info ("LTE firmware upgrade on '" . $1 . "' finished, waiting for reset."); + :delay 240s; + :local Firmware [ /interface/lte/firmware-upgrade $1 as-value ]; + :if ([ :len ($Firmware->"latest") ] > 0 && \ + ($Firmware->"installed") != ($Firmware->"latest")) do={ + :log warning ("LTE firmware versions still differ. Upgrade failed anyway?"); + } + } on-error={ + :log error ("LTE firmware upgrade on '" . $1 . "' failed."); } } diff --git a/update-gre-address.rsc b/update-gre-address.rsc index e38839c..cddfa92 100644 --- a/update-gre-address.rsc +++ b/update-gre-address.rsc @@ -1,35 +1,46 @@ #!rsc by RouterOS # RouterOS script: update-gre-address -# Copyright (c) 2013-2023 Christian Hesse -# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md +# Copyright (c) 2013-2025 Christian Hesse +# https://rsc.eworm.de/COPYING.md +# +# requires RouterOS, version=7.15 # # update gre interface remote address with dynamic address from # ipsec remote peer -# https://git.eworm.de/cgit/routeros-scripts/about/doc/update-gre-address.md +# https://rsc.eworm.de/doc/update-gre-address.md -:local 0 "update-gre-address"; :global GlobalFunctionsReady; :while ($GlobalFunctionsReady != true) do={ :delay 500ms; } -:global CharacterReplace; -:global LogPrintExit2; -:global ScriptLock; +:local ExitOK false; +:do { + :local ScriptName [ :jobname ]; -$ScriptLock $0; + :global CharacterReplace; + :global LogPrint; + :global ScriptLock; -/interface/gre/set remote-address=0.0.0.0 disabled=yes [ find where !running !disabled ]; + :if ([ $ScriptLock $ScriptName ] = false) do={ + :set ExitOK true; + :error false; + } -:foreach Peer in=[ /ip/ipsec/active-peers/find ] do={ - :local PeerVal [ /ip/ipsec/active-peers/get $Peer ]; - :local GreInt [ /interface/gre/find where comment=($PeerVal->"id") or comment=[ $CharacterReplace ($PeerVal->"id") "CN=" "" ] ]; - :if ([ :len $GreInt ] > 0) do={ - :local GreIntVal [ /interface/gre/get $GreInt ]; - :if ([ :typeof ($PeerVal->"dynamic-address") ] = "str" && \ - ($PeerVal->"dynamic-address" != $GreIntVal->"remote-address" || \ - $GreIntVal->"disabled" = true)) do={ - $LogPrintExit2 info $0 ("Updating remote address for interface " . $GreIntVal->"name" . " to " . $PeerVal->"dynamic-address") false; - /interface/gre/set remote-address=0.0.0.0 disabled=yes [ find where remote-address=$PeerVal->"dynamic-address" name!=$GreIntVal->"name" ]; - /interface/gre/set $GreInt remote-address=($PeerVal->"dynamic-address") disabled=no; + /interface/gre/set remote-address=0.0.0.0 disabled=yes [ find where !running !disabled ]; + + :foreach Peer in=[ /ip/ipsec/active-peers/find ] do={ + :local PeerVal [ /ip/ipsec/active-peers/get $Peer ]; + :local GreInt [ /interface/gre/find where comment=($PeerVal->"id") or comment=[ $CharacterReplace ($PeerVal->"id") "CN=" "" ] ]; + :if ([ :len $GreInt ] > 0) do={ + :local GreIntVal [ /interface/gre/get $GreInt ]; + :if ([ :typeof ($PeerVal->"dynamic-address") ] = "str" && \ + ($PeerVal->"dynamic-address" != $GreIntVal->"remote-address" || \ + $GreIntVal->"disabled" = true)) do={ + $LogPrint info $ScriptName ("Updating remote address for interface " . $GreIntVal->"name" . " to " . $PeerVal->"dynamic-address"); + /interface/gre/set remote-address=0.0.0.0 disabled=yes [ find where remote-address=$PeerVal->"dynamic-address" name!=$GreIntVal->"name" ]; + /interface/gre/set $GreInt remote-address=($PeerVal->"dynamic-address") disabled=no; + } } } +} on-error={ + :global ExitError; $ExitError $ExitOK [ :jobname ]; } diff --git a/update-tunnelbroker.rsc b/update-tunnelbroker.rsc index 60cb003..45afa6f 100644 --- a/update-tunnelbroker.rsc +++ b/update-tunnelbroker.rsc @@ -1,59 +1,74 @@ #!rsc by RouterOS # RouterOS script: update-tunnelbroker -# Copyright (c) 2013-2023 Christian Hesse +# Copyright (c) 2013-2025 Christian Hesse # Michael Gisbers -# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md +# https://rsc.eworm.de/COPYING.md # # provides: ppp-on-up +# requires RouterOS, version=7.15 +# requires device-mode, fetch # # update local address of tunnelbroker interface -# https://git.eworm.de/cgit/routeros-scripts/about/doc/update-tunnelbroker.md +# https://rsc.eworm.de/doc/update-tunnelbroker.md -:local 0 "update-tunnelbroker"; :global GlobalFunctionsReady; :while ($GlobalFunctionsReady != true) do={ :delay 500ms; } -:global CertificateAvailable; -:global LogPrintExit2; -:global ParseKeyValueStore; -:global ScriptLock; +:local ExitOK false; +:do { + :local ScriptName [ :jobname ]; -$ScriptLock $0; + :global CertificateAvailable; + :global LogPrint; + :global ParseKeyValueStore; + :global ScriptLock; -:if ([ $CertificateAvailable "Starfield Secure Certificate Authority - G2" ] = false) do={ - $LogPrintExit2 error $0 ("Downloading required certificate failed.") true; -} + :if ([ $ScriptLock $ScriptName ] = false) do={ + :set ExitOK true; + :error false; + } -:foreach Interface in=[ /interface/6to4/find where comment~"^tunnelbroker" !disabled ] do={ - :local Data false; - :local InterfaceVal [ /interface/6to4/get $Interface ]; - :local Comment [ $ParseKeyValueStore ($InterfaceVal->"comment") ]; + :if ([ $CertificateAvailable "Starfield Root Certificate Authority - G2" ] = false) do={ + $LogPrint error $ScriptName ("Downloading required certificate failed."); + :set ExitOK true; + :error false; + } - :for I from=2 to=0 do={ - :if ($Data = false) do={ - :do { - :set Data ([ /tool/fetch check-certificate=yes-without-crl \ - ("https://ipv4.tunnelbroker.net/nic/update?hostname=" . $Comment->"id") \ - user=($Comment->"user") password=($Comment->"pass") output=user as-value ]->"data"); - } on-error={ - $LogPrintExit2 debug $0 ("Failed downloading, " . $I . " retries pending.") false; - :delay 2s; + :foreach Interface in=[ /interface/6to4/find where comment~"^tunnelbroker" !disabled ] do={ + :local Data false; + :local InterfaceVal [ /interface/6to4/get $Interface ]; + :local Comment [ $ParseKeyValueStore ($InterfaceVal->"comment") ]; + + :for I from=2 to=0 do={ + :if ($Data = false) do={ + :do { + :set Data ([ /tool/fetch check-certificate=yes-without-crl \ + ("https://ipv4.tunnelbroker.net/nic/update?hostname=" . $Comment->"id") \ + user=($Comment->"user") password=($Comment->"pass") output=user as-value ]->"data"); + } on-error={ + $LogPrint debug $ScriptName ("Failed downloading, " . $I . " retries pending."); + :delay 2s; + } } } - } - :if (!($Data ~ "^(good|nochg) ")) do={ - $LogPrintExit2 error $0 ("Failed sending the local address to tunnelbroker or unexpected response!") true; - } - - :local PublicAddress [ :pick $Data ([ :find $Data " " ] + 1) [ :find $Data "\n" ] ]; - - :if ($PublicAddress != $InterfaceVal->"local-address") do={ - :if ([ :len [ /ip/address find where address~("^" . $PublicAddress . "/") ] ] < 1) do={ - $LogPrintExit2 warning $0 ("The address " . $PublicAddress . " is not configured on your device. NAT by ISP?") false; + :if (!($Data ~ "^(good|nochg) ")) do={ + $LogPrint error $ScriptName ("Failed sending the local address to tunnelbroker or unexpected response!"); + :set ExitOK true; + :error false; } - $LogPrintExit2 info $0 ("Local address changed, updating tunnel configuration with address: " . $PublicAddress) false; - /interface/6to4/set $Interface local-address=$PublicAddress; + :local PublicAddress [ :pick $Data ([ :find $Data " " ] + 1) [ :find $Data "\n" ] ]; + + :if ($PublicAddress != $InterfaceVal->"local-address") do={ + :if ([ :len [ /ip/address find where address~("^" . $PublicAddress . "/") ] ] < 1) do={ + $LogPrint warning $ScriptName ("The address " . $PublicAddress . " is not configured on your device. NAT by ISP?"); + } + + $LogPrint info $ScriptName ("Local address changed, updating tunnel configuration with address: " . $PublicAddress); + /interface/6to4/set $Interface local-address=$PublicAddress; + } } +} on-error={ + :global ExitError; $ExitError $ExitOK [ :jobname ]; }