diff --git a/.gitignore b/.gitignore
index f29d4e8..8abdc28 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,16 @@
+# backup and temporary files
*~
+
+# patches and related files
+*.orig
*.patch
+*.rej
+
+# html files (as generated from markdown)
*.html
+
+# checksums file as used by $ScriptInstallUpdate
+checksums.json
+
+# Mac OS X folder settings file
+.DS_Store
diff --git a/BRANCHES.md b/BRANCHES.md
new file mode 100644
index 0000000..dc4f4ac
--- /dev/null
+++ b/BRANCHES.md
@@ -0,0 +1,50 @@
+Installing from branches
+========================
+
+[](https://github.com/eworm-de/routeros-scripts/stargazers)
+[](https://github.com/eworm-de/routeros-scripts/network)
+[](https://github.com/eworm-de/routeros-scripts/watchers)
+[](https://mikrotik.com/download/changelogs/)
+[](https://t.me/routeros_scripts)
+[](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..0e0a867
--- /dev/null
+++ b/CERTIFICATES.md
@@ -0,0 +1,83 @@
+Certificate name from browser
+=============================
+
+[](https://github.com/eworm-de/routeros-scripts/stargazers)
+[](https://github.com/eworm-de/routeros-scripts/network)
+[](https://github.com/eworm-de/routeros-scripts/watchers)
+[](https://mikrotik.com/download/changelogs/)
+[](https://t.me/routeros_scripts)
+[](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*".
+
+
+
+The dialog will change, click "*More information*".
+
+
+
+A new window opens, click the button "*View Certificate*". (That window
+can be closed now.)
+
+
+
+A new tab opens, showing information on the server certificate and its
+chain of trust. The leftmost certificate is what we are interested in.
+
+
+
+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" "fetch";
+
+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 Gotify](doc/mod/notification-gotify.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 54b3228..00861c1 100644
--- a/CONTRIBUTIONS.md
+++ b/CONTRIBUTIONS.md
@@ -1,7 +1,14 @@
Past Contributions
==================
-[◀ Go back to main README](README.md)
+[](https://github.com/eworm-de/routeros-scripts/stargazers)
+[](https://github.com/eworm-de/routeros-scripts/network)
+[](https://github.com/eworm-de/routeros-scripts/watchers)
+[](https://mikrotik.com/download/changelogs/)
+[](https://t.me/routeros_scripts)
+[](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! ❤️
@@ -10,35 +17,50 @@ Thanks a lot for your contributions! ❤️
These persons contributed code or documentation. See the git history
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)
+* [Ilya Kulakov](mailto:kulakov.ilya@gmail.com) (@Kentzo)
+* [Leonardo David Monteiro](mailto:leo@cub3.xyz) (@leosfsm)
* [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)
## Donations
Add yourself to the list,
-[donate with PayPal](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J)!
+[donate with PayPal ↗️](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J)!
* Abdul Mannan Abbasi
+* Alex Maier
* 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
* Sunny Chu (@sunnychuchu)
+* Ulrich Wessendorf
* Zac Kornilakis
---
-[◀ Go back to main README](README.md)
-[▲ Go back to top](#top)
+[⬅️ Go back to main README](README.md)
+[⬆️ Go back to top](#top)
diff --git a/DEBUG.md b/DEBUG.md
new file mode 100644
index 0000000..66bf728
--- /dev/null
+++ b/DEBUG.md
@@ -0,0 +1,63 @@
+Debug output and logs
+=====================
+
+[](https://github.com/eworm-de/routeros-scripts/stargazers)
+[](https://github.com/eworm-de/routeros-scripts/network)
+[](https://github.com/eworm-de/routeros-scripts/watchers)
+[](https://mikrotik.com/download/changelogs/)
+[](https://t.me/routeros_scripts)
+[](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 remove 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 e8be3e1..e580bc5 100644
--- a/INITIAL-COMMANDS.md
+++ b/INITIAL-COMMANDS.md
@@ -1,37 +1,69 @@
Initial commands
================
-[◀ Go back to main README](README.md)
+[](https://github.com/eworm-de/routeros-scripts/stargazers)
+[](https://github.com/eworm-de/routeros-scripts/network)
+[](https://github.com/eworm-de/routeros-scripts/watchers)
+[](https://mikrotik.com/download/changelogs/)
+[](https://t.me/routeros_scripts)
+[](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J)
-> ⚠️ **Warning**: These command are inteneded for initial setup. If you are
+[⬅️ Go back to main README](README.md)
+
+> ⚠️ **Warning**: These commands are intended for initial setup. If you are
> not aware of the procedure please follow
> [the long way in detail](README.md#the-long-way-in-detail).
Run the complete base installation:
{
- /tool/fetch "https://git.eworm.de/cgit/routeros-scripts/plain/certs/R3.pem" dst-path="letsencrypt-R3.pem" as-value;
- :delay 1s;
- /certificate/import file-name=letsencrypt-R3.pem passphrase="";
- :if ([ :len [ /certificate/find where fingerprint="67add1166b020ae61b8f5fc96813c04c2aa589960796865572a3c7e737613dfd" or fingerprint="96bcec06264976f37460779acf28c5a7cfe8a3c0aae11a8ffcee05c0bddf08c6" ] ] != 2) do={
- :error "Something is wrong with your certificates!";
+ :local BaseUrl "https://rsc.eworm.de/main/";
+ :local CertCommonName "ISRG Root X2";
+ :local CertFileName "ISRG-Root-X2.pem";
+ :local CertFingerprint "69729b8e15a86efc177a57afb7171dfc64add28c2fca8cf1507e34453ccb1470";
+
+ :local CertSettings [ /certificate/settings/get ];
+ :if (!((($CertSettings->"builtin-trust-anchors") = "trusted" || \
+ ($CertSettings->"builtin-trust-store") ~ "fetch" || \
+ ($CertSettings->"builtin-trust-store") = "all") && \
+ [[ :parse (":return [ :len [ /certificate/builtin/find where common-name=\"" . $CertCommonName . "\" ] ]") ]] > 0)) do={
+ :put "Importing certificate...";
+ /tool/fetch ($BaseUrl . "certs/" . $CertFileName) dst-path=$CertFileName as-value;
+ :delay 1s;
+ /certificate/import file-name=$CertFileName passphrase="";
+ :if ([ :len [ /certificate/find where fingerprint=$CertFingerprint ] ] != 1) do={
+ :error "Something is wrong with your certificates!";
+ };
+ :delay 1s;
};
- /file/remove "letsencrypt-R3.pem";
- :delay 1s;
+ :put "Renaming global-config-overlay, if exists...";
+ /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) output=user as-value]->"data");
+ :put "Installing $Script...";
+ /system/script/remove [ find where name=$Script ];
+ /system/script/add name=$Script owner=$Script source=([ /tool/fetch check-certificate=yes-without-crl ($BaseUrl . $Script . ".rsc") output=user as-value ]->"data");
};
+ :put "Loading configuration and functions...";
/system/script { run global-config; run global-functions; };
- /system/scheduler/add name="global-scripts" start-time=startup on-event="/system/script { run global-config; run global-functions; }";
- :global CertificateNameByCN;
- $CertificateNameByCN "R3";
- $CertificateNameByCN "ISRG Root X1";
+ :if ([ :len [ /certificate/find where fingerprint=$CertFingerprint ] ] > 0) do={
+ :put "Renaming certificate by its common-name...";
+ :global CertificateNameByCN;
+ $CertificateNameByCN $CertFingerprint;
+ };
};
-Optional to update the scripts automatically:
+Then continue setup with
+[scheduled automatic updates](README.md#scheduled-automatic-updates) or
+[editing configuration](README.md#editing-configuration).
- /system/scheduler/add name="ScriptInstallUpdate" start-time=startup interval=1d on-event=":global ScriptInstallUpdate; \$ScriptInstallUpdate;";
+## 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)
+[⬅️ Go back to main README](README.md)
+[⬆️ Go back to top](#top)
diff --git a/Makefile b/Makefile
index b0737ab..5db0a30 100644
--- a/Makefile
+++ b/Makefile
@@ -2,27 +2,45 @@
# template scripts -> final scripts
# markdown files -> html files
-TEMPLATE = $(wildcard *.template)
-CAPSMAN = $(TEMPLATE:.template=.capsman)
-LOCAL = $(TEMPLATE:.template=.local)
+ALL_RSC := $(wildcard *.rsc */*.rsc)
+GEN_RSC := $(wildcard *.capsman.rsc *.local.rsc *.wifi.rsc)
-MARKDOWN = $(wildcard *.md doc/*.md doc/mod/*.md)
-HTML = $(MARKDOWN:.md=.html)
+MARKDOWN := $(wildcard *.md doc/*.md doc/mod/*.md)
+HTML := $(MARKDOWN:.md=.html)
-all: $(CAPSMAN) $(LOCAL) $(HTML)
+DATE ?= $(shell date --rfc-email)
+VERSION ?= $(shell git symbolic-ref --short HEAD 2>/dev/null)/$(shell git rev-list --count HEAD 2>/dev/null)/$(shell git rev-parse --short=8 HEAD 2>/dev/null)
+export DATE VERSION
-%.html: %.md Makefile
- markdown $< | sed 's/href="\([-_\./[:alnum:]]*\)\.md"/href="\1.html"/g' > $@
+.PHONY: all checksums commitinfo docs rsc clean
-%.local: %.template Makefile
- sed -e '/\/caps-man/d' -e 's|%PATH%|interface\/wireless|' -e 's|%TEMPL%|$(suffix $@)|' \
- -e '/^# !!/,/^# !!/c # !! Do not edit this file, it is generated from template!' \
- < $< > $@
+all: checksums docs rsc
-%.capsman: %.template Makefile
- sed -e '/\/interface\/wireless/d' -e 's/%PATH%/caps-man/' -e 's/%TEMPL%/$(suffix $@)/' \
- -e '/^# !!/,/^# !!/c # !! Do not edit this file, it is generated from template!' \
- < $< > $@
+checksums: checksums.json
+
+checksums.json: contrib/checksums.sh $(ALL_RSC)
+ contrib/checksums.sh > $@
+
+commitinfo: global-functions.rsc
+ contrib/commitinfo.sh $< > $<~
+ mv $<~ $<
+
+docs: $(HTML)
+
+%.html: %.md general/style.css contrib/html.sh contrib/html.sh.d/head.html contrib/html.sh.d/foot.html
+ contrib/html.sh $< > $@
+
+rsc: $(GEN_RSC)
+
+%.capsman.rsc: %.template.rsc contrib/template-capsman.sh
+ contrib/template-capsman.sh $< > $@
+
+%.local.rsc: %.template.rsc contrib/template-local.sh
+ contrib/template-local.sh $< > $@
+
+%.wifi.rsc: %.template.rsc contrib/template-wifi.sh
+ contrib/template-wifi.sh $< > $@
clean:
- rm -f $(HTML)
+ rm -f $(HTML) checksums.json
+ make -C contrib/ clean
diff --git a/README.d/01-download-certs.avif b/README.d/01-download-certs.avif
index 4da73fa..f2afeb5 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 308be5b..b31343c 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 adf1161..1f03ad2 100644
Binary files a/README.d/03-check-certs.avif and b/README.d/03-check-certs.avif differ
diff --git a/README.d/04-import-scripts.avif b/README.d/04-import-scripts.avif
index 53439e4..c09949a 100644
Binary files a/README.d/04-import-scripts.avif and b/README.d/04-import-scripts.avif differ
diff --git a/README.d/05-edit-global-config-overlay.avif b/README.d/05-edit-global-config-overlay.avif
deleted file mode 100644
index f2f0f2d..0000000
Binary files a/README.d/05-edit-global-config-overlay.avif and /dev/null differ
diff --git a/README.d/05-run-scripts.avif b/README.d/05-run-scripts.avif
new file mode 100644
index 0000000..12d812c
Binary files /dev/null and b/README.d/05-run-scripts.avif differ
diff --git a/README.d/06-run-and-schedule-scripts.avif b/README.d/06-run-and-schedule-scripts.avif
deleted file mode 100644
index 37e1173..0000000
Binary files a/README.d/06-run-and-schedule-scripts.avif and /dev/null differ
diff --git a/README.d/06-schedule-update.avif b/README.d/06-schedule-update.avif
new file mode 100644
index 0000000..158e13f
Binary files /dev/null and b/README.d/06-schedule-update.avif differ
diff --git a/README.d/07-edit-global-config-overlay.avif b/README.d/07-edit-global-config-overlay.avif
new file mode 100644
index 0000000..9a5b903
Binary files /dev/null and b/README.d/07-edit-global-config-overlay.avif differ
diff --git a/README.d/07-schedule-update.avif b/README.d/07-schedule-update.avif
deleted file mode 100644
index 7c96f3a..0000000
Binary files a/README.d/07-schedule-update.avif and /dev/null differ
diff --git a/README.d/08-apply-configuration.avif b/README.d/08-apply-configuration.avif
new file mode 100644
index 0000000..ab22cae
Binary files /dev/null and b/README.d/08-apply-configuration.avif differ
diff --git a/README.d/08-update-scripts.avif b/README.d/08-update-scripts.avif
deleted file mode 100644
index f549fef..0000000
Binary files a/README.d/08-update-scripts.avif and /dev/null differ
diff --git a/README.d/09-install-scripts.avif b/README.d/09-install-scripts.avif
deleted file mode 100644
index 00225b1..0000000
Binary files a/README.d/09-install-scripts.avif and /dev/null differ
diff --git a/README.d/09-update-scripts.avif b/README.d/09-update-scripts.avif
new file mode 100644
index 0000000..e713ac2
Binary files /dev/null and b/README.d/09-update-scripts.avif differ
diff --git a/README.d/10-install-scripts.avif b/README.d/10-install-scripts.avif
new file mode 100644
index 0000000..cf26b16
Binary files /dev/null and b/README.d/10-install-scripts.avif differ
diff --git a/README.d/10-schedule-script.avif b/README.d/10-schedule-script.avif
deleted file mode 100644
index 27541b7..0000000
Binary files a/README.d/10-schedule-script.avif and /dev/null differ
diff --git a/README.d/11-schedule-script.avif b/README.d/11-schedule-script.avif
new file mode 100644
index 0000000..558614f
Binary files /dev/null and b/README.d/11-schedule-script.avif differ
diff --git a/README.d/11-setup-lease-script.avif b/README.d/11-setup-lease-script.avif
deleted file mode 100644
index 365e0e8..0000000
Binary files a/README.d/11-setup-lease-script.avif and /dev/null differ
diff --git a/README.d/12-install-custom-script.avif b/README.d/12-install-custom-script.avif
deleted file mode 100644
index c27408f..0000000
Binary files a/README.d/12-install-custom-script.avif and /dev/null differ
diff --git a/README.d/12-setup-lease-script.avif b/README.d/12-setup-lease-script.avif
new file mode 100644
index 0000000..2a8bcb2
Binary files /dev/null and b/README.d/12-setup-lease-script.avif differ
diff --git a/README.d/13-install-custom-script.avif b/README.d/13-install-custom-script.avif
new file mode 100644
index 0000000..221b84e
Binary files /dev/null and b/README.d/13-install-custom-script.avif differ
diff --git a/README.d/13-remove-script.avif b/README.d/13-remove-script.avif
deleted file mode 100644
index a5c7daf..0000000
Binary files a/README.d/13-remove-script.avif and /dev/null differ
diff --git a/README.d/14-remove-script.avif b/README.d/14-remove-script.avif
new file mode 100644
index 0000000..3e4c105
Binary files /dev/null and b/README.d/14-remove-script.avif differ
diff --git a/README.d/news-and-changes-notification.svg b/README.d/news-and-changes-notification.svg
deleted file mode 100644
index ab8d611..0000000
--- a/README.d/news-and-changes-notification.svg
+++ /dev/null
@@ -1,40 +0,0 @@
-
-
diff --git a/README.d/notification-news-and-changes.avif b/README.d/notification-news-and-changes.avif
new file mode 100644
index 0000000..d2e8aa7
Binary files /dev/null and b/README.d/notification-news-and-changes.avif differ
diff --git a/README.md b/README.md
index c670fd0..746390b 100644
--- a/README.md
+++ b/README.md
@@ -1,47 +1,75 @@
RouterOS Scripts
================
-[](https://github.com/eworm-de/routeros-scripts/stargazers)
-[](https://github.com/eworm-de/routeros-scripts/network)
-[](https://github.com/eworm-de/routeros-scripts/watchers)
+[](https://github.com/eworm-de/routeros-scripts/stargazers)
+[](https://github.com/eworm-de/routeros-scripts/network)
+[](https://github.com/eworm-de/routeros-scripts/watchers)
+[](https://mikrotik.com/download/changelogs/)
+[](https://t.me/routeros_scripts)
+[](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J)
+
+**a collection of scripts for MikroTik RouterOS**

-[RouterOS](https://mikrotik.com/software) is the operating system developed
-by [MikroTik](https://mikrotik.com/aboutus) for networking tasks. This
-repository holds a number of [scripts](https://wiki.mikrotik.com/wiki/Manual:Scripting)
+[RouterOS ↗️](https://mikrotik.com/software) is the operating system developed
+by [MikroTik ↗️](https://mikrotik.com/aboutus) for networking tasks. This
+repository holds a number of [scripts ↗️](https://wiki.mikrotik.com/wiki/Manual:Scripting)
to manage RouterOS devices or extend their functionality.
*Use at your own risk*, pay attention to
-[license and warranty](#license-and-warranty)!
+[license and warranty](#license-and-warranty), and
+[disclaimer on external links](#disclaimer-on-external-links)!
Requirements
------------
+### Software (RouterOS)
+
Latest version of the scripts require recent RouterOS to function properly.
-Make sure to install latest updates before you begin.
+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
+change some time after `7.(n+1)` was released. At any time you should have
+at least two minor and their bugfix releases to choose from.
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
-------------
### Get me ready!
If you know how things work just copy and paste the
-[initial commands](INITIAL-COMMANDS.md). Remember to edit and rerun
+[initial commands](INITIAL-COMMANDS.md). These also support fixing an
+existing but broken installation. Remember to edit and rerun
`global-config-overlay`!
-First time users should take the long way below.
+
+> 💡️ **Hint**: First time users should take
+> [the long way in detail](#the-long-way-in-detail) below.
### Live presentation
Want to see it in action? I've had a presentation [Repository based
-RouterOS script distribution](https://www.youtube.com/watch?v=B9neG3oAhcY)
-including demonstation recorded live at [MUM Europe
-2019](https://mum.mikrotik.com/2019/EU/) in Vienna.
+RouterOS script distribution ↗️](https://www.youtube.com/watch?v=B9neG3oAhcY)
+including demonstration recorded live at [MUM Europe
+2019 ↗️](https://mum.mikrotik.com/2019/EU/) in Vienna.
> ⚠️ **Warning**: Some details changed. So see the presentation, then follow
> the steps below for up-to-date commands.
@@ -49,70 +77,112 @@ including demonstation recorded live at [MUM Europe
### The long way in detail
The update script does server certificate verification, so first step is to
-download the certificates. If you intend to download the scripts from a
+download the certificates.
+
+> 💡️ **Hint**: RouterOS 7.19 comes with a builtin certificate store. You
+> can skip the steps regarding certificate download and import and jump
+> to [installation of scripts](#installation-of-scripts) if you set the
+> trust for these builtin trust anchors:
+> `/certificate/settings/set builtin-trust-anchors=trusted;`
+> With RouterOS 7.21 the functionality was changed. Set this at minimum,
+> but make sure not to drop other targets:
+> `/certificate/settings/set builtin-trust-store=fetch;`
+
+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/R3.pem" dst-path="letsencrypt-R3.pem";
+ /tool/fetch "https://rsc.eworm.de/main/certs/ISRG-Root-X2.pem" dst-path="isrg-root-x2.pem";

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 X1](https://letsencrypt.org/certs/isrgrootx1.pem)
-* Let's Encrypt [R3](https://letsencrypt.org/certs/lets-encrypt-r3.pem)
+* [ISRG Root X2 ↗️](https://letsencrypt.org/certs/isrg-root-x2.pem)
-Then we import the certificates.
+Then we import the certificate.
- /certificate/import file-name=letsencrypt-R3.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.

-For basic verification we rename the certificates and print their count. Make
-sure the certificate count is **two**.
+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="R3" [ find where fingerprint="67add1166b020ae61b8f5fc96813c04c2aa589960796865572a3c7e737613dfd" ];
- /certificate/set name="ISRG-Root-X1" [ find where fingerprint="96bcec06264976f37460779acf28c5a7cfe8a3c0aae11a8ffcee05c0bddf08c6" ];
- /certificate/print count-only where fingerprint="67add1166b020ae61b8f5fc96813c04c2aa589960796865572a3c7e737613dfd" or fingerprint="96bcec06264976f37460779acf28c5a7cfe8a3c0aae11a8ffcee05c0bddf08c6";
+ /certificate/set name="ISRG-Root-X2" [ find where common-name="ISRG Root X2" ];
+ /certificate/print proplist=name,fingerprint where fingerprint="69729b8e15a86efc177a57afb7171dfc64add28c2fca8cf1507e34453ccb1470";

Always make sure there are no certificates installed you do not know or want!
+#### Installation of scripts
+
All following commands will verify the server certificate. For validity the
certificate's lifetime is checked with local time, so make sure the device's
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) 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://rsc.eworm.de/main/" . $Script . ".rsc") output=user as-value ]->"data"); };

-The configuration needs to be tweaked for your needs. Edit
-`global-config-overlay`, copy configuration from
-[`global-config`](global-config) (the one without `-overlay`).
-Save changes and exit with `Ctrl-o`.
-
- /system/script edit global-config-overlay source;
-
-
-
-And finally load configuration and functions and add the scheduler.
+And finally run configuration and functions. This will also add the
+scheduler for loading at system startup automatically.
/system/script { run global-config; run global-functions; };
- /system/scheduler/add name="global-scripts" start-time=startup on-event="/system/script { run global-config; run global-functions; }";
-
+
-The last step is optional: Add this scheduler **only** if you want the scripts
-to be updated automatically!
+> 💡️ **Hint**: You see complaints regarding syntax errors? Most likely the
+> RouterOS on your device is too old. Check for updates!
+
+### Scheduled automatic updates
+
+The last step is optional: Add this scheduler **only** if you want the
+scripts to be updated automatically!
/system/scheduler/add name="ScriptInstallUpdate" start-time=startup interval=1d on-event=":global ScriptInstallUpdate; \$ScriptInstallUpdate;";
-
+
+
+Editing configuration
+---------------------
+
+The configuration needs to be tweaked for your needs. Edit
+`global-config-overlay`, copy relevant configuration from
+[`global-config`](global-config.rsc) (the one without `-overlay`).
+Save changes and exit with `Ctrl-o`.
+
+ /system/script/edit global-config-overlay source;
+
+
+
+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:
+
+ /system/script/run global-config;
+
+
+
+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=[ :tocrlf [ get global-config-overlay source ] ] global-config-overlay;`
Updating scripts
----------------
@@ -122,12 +192,12 @@ everything is up-to-date it will not produce any output.
$ScriptInstallUpdate;
-
+
If the update includes news or requires configuration changes a notification
is sent - in addition to terminal output and log messages.
-
+
Adding a script
---------------
@@ -137,19 +207,19 @@ a comma separated list of script names.
$ScriptInstallUpdate check-certificates,check-routeros-update;
-
+
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
+[scheduler ↗️](https://wiki.mikrotik.com/wiki/Manual:System/Scheduler). We just
+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;";
-
+
Some events can run a script. If you want your DHCP hostnames to be available
in DNS use `dhcp-to-dns` with the events from dhcp server. For a regular
@@ -157,70 +227,71 @@ cleanup add a scheduler entry.
$ScriptInstallUpdate dhcp-to-dns,lease-script;
/ip/dhcp-server/set lease-script=lease-script [ find ];
- /system/scheduler/add name="dhcp-to-dns" interval=5m on-event="/system/script/run dhcp-to-dns;";
+ /system/scheduler/add name="dhcp-to-dns" interval=5m start-time=startup on-event="/system/script/run dhcp-to-dns;";
-
+
There's much more to explore... Have fun!
Available scripts
-----------------
-* [Find and remove access list duplicates](doc/accesslist-duplicates.md)
-* [Upload backup to Mikrotik cloud](doc/backup-cloud.md)
-* [Send backup via e-mail](doc/backup-email.md)
-* [Save configuration to fallback partition](doc/backup-partition.md)
-* [Upload backup to server](doc/backup-upload.md)
-* [Download packages for CAP upgrade from CAPsMAN](doc/capsman-download-packages.md)
-* [Run rolling CAP upgrades from CAPsMAN](doc/capsman-rolling-upgrade.md)
-* [Renew locally issued certificates](doc/certificate-renew-issued.md)
-* [Renew certificates and notify on expiration](doc/check-certificates.md)
-* [Notify about health state](doc/check-health.md)
-* [Notify on LTE firmware upgrade](doc/check-lte-firmware-upgrade.md)
-* [Notify on RouterOS update](doc/check-routeros-update.md)
-* [Collect MAC addresses in wireless access list](doc/collect-wireless-mac.md)
-* [Use wireless network with daily psk](doc/daily-psk.md)
-* [Comment DHCP leases with info from access list](doc/dhcp-lease-comment.md)
-* [Create DNS records for DHCP leases](doc/dhcp-to-dns.md)
-* [Automatically upgrade firmware and reboot](doc/firmware-upgrade-reboot.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)
-* [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)
-* [Run other scripts on DHCP lease](doc/lease-script.md)
-* [Manage LEDs dark mode](doc/leds-mode.md)
-* [Forward log messages via notification](doc/log-forward.md)
-* [Mode button with multiple presses](doc/mode-button.md)
-* [Manage DNS and DoH servers from netwatch](doc/netwatch-dns.md)
-* [Notify on host up and down](doc/netwatch-notify.md)
-* [Visualize OSPF state via LEDs](doc/ospf-to-leds.md)
-* [Manage system update](doc/packages-update.md)
-* [Run scripts on ppp connection](doc/ppp-on-up.md)
-* [Act on received SMS](doc/sms-action.md)
-* [Forward received SMS](doc/sms-forward.md)
-* [Import SSH keys](doc/ssh-keys-import.md)
-* [Play Super Mario theme](doc/super-mario-theme.md)
-* [Install LTE firmware upgrade](doc/unattended-lte-firmware-upgrade.md)
-* [Update GRE configuration with dynamic addresses](doc/update-gre-address.md)
-* [Update tunnelbroker configuration](doc/update-tunnelbroker.md)
-
-[comment]: # (TODO: currently undocumented)
-[comment]: # (* learn-mac-based-vlan)
-[comment]: # (* manage-umts)
+* [Find and remove access list duplicates](doc/accesslist-duplicates.md) (`accesslist-duplicates`)
+* [Upload backup to Mikrotik cloud](doc/backup-cloud.md) (`backup-cloud`)
+* [Send backup via e-mail](doc/backup-email.md) (`backup-email`)
+* [Save configuration to fallback partition](doc/backup-partition.md) (`backup-partition`)
+* [Upload backup to server](doc/backup-upload.md) (`backup-upload`)
+* [Download packages for CAP upgrade from CAPsMAN](doc/capsman-download-packages.md) (`capsman-download-packages`)
+* [Run rolling CAP upgrades from CAPsMAN](doc/capsman-rolling-upgrade.md) (`capsman-rolling-upgrade`)
+* [Renew locally issued certificates](doc/certificate-renew-issued.md) (`certificate-renew-issued`)
+* [Renew certificates and notify on expiration](doc/check-certificates.md) (`check-certificates`)
+* [Notify about health state](doc/check-health.md) (`check-health`)
+* [Notify on LTE firmware upgrade](doc/check-lte-firmware-upgrade.md) (`check-lte-firmware-upgrade`)
+* [Check perpetual license on CHR](doc/check-perpetual-license.md) (`check-perpetual-license`)
+* [Notify on RouterOS update](doc/check-routeros-update.md) (`check-routeros-update`)
+* [Collect MAC addresses in wireless access list](doc/collect-wireless-mac.md) (`collect-wireless-mac`)
+* [Use wireless network with daily psk](doc/daily-psk.md) (`daily-psk`)
+* [Comment DHCP leases with info from access list](doc/dhcp-lease-comment.md) (`dhcp-lease-comment`)
+* [Create DNS records for DHCP leases](doc/dhcp-to-dns.md) (`dhcp-to-dns`)
+* [Automatically upgrade firmware and reboot](doc/firmware-upgrade-reboot.md) (`firmware-upgrade-reboot`)
+* [Download, import and update firewall address-lists](doc/fw-addr-lists.md) (`fw-addr-lists`)
+* [Wait for global functions und modules](doc/global-wait.md) (`global-wait`)
+* [Send GPS position to server](doc/gps-track.md) (`gps-track`)
+* [Use WPA network with hotspot credentials](doc/hotspot-to-wpa.md) (`hotspot-to-wpa` & `hotspot-to-wpa-cleanup`)
+* [Create DNS records for IPSec peers](doc/ipsec-to-dns.md) (`ipsec-to-dns`)
+* [Update configuration on IPv6 prefix change](doc/ipv6-update.md) (`ipv6-update`)
+* [Manage IP addresses with bridge status](doc/ip-addr-bridge.md) (`ip-addr-bridge`)
+* [Run other scripts on DHCP lease](doc/lease-script.md) (`lease-script`)
+* [Manage LEDs dark mode](doc/leds-mode.md) (`leds-day-mode`, `leds-night-mode` & `leds-toggle-mode`)
+* [Forward log messages via notification](doc/log-forward.md) (`log-forward`)
+* [Mode button with multiple presses](doc/mode-button.md) (`mode-button`)
+* [Manage DNS and DoH servers from netwatch](doc/netwatch-dns.md) (`netwatch-dns`)
+* [Notify on host up and down](doc/netwatch-notify.md) (`netwatch-notify`)
+* [Visualize OSPF state via LEDs](doc/ospf-to-leds.md) (`ospf-to-leds`)
+* [Manage system update](doc/packages-update.md) (`packages-update`)
+* [Run scripts on ppp connection](doc/ppp-on-up.md) (`ppp-on-up`)
+* [Act on received SMS](doc/sms-action.md) (`sms-action`)
+* [Forward received SMS](doc/sms-forward.md) (`sms-forward`)
+* [Play Super Mario theme](doc/super-mario-theme.md) (`super-mario-theme`)
+* [Chat with your router and send commands via Telegram bot](doc/telegram-chat.md) (`telegram-chat`)
+* [Install LTE firmware upgrade](doc/unattended-lte-firmware-upgrade.md) (`unattended-lte-firmware-upgrade`)
+* [Update GRE configuration with dynamic addresses](doc/update-gre-address.md) (`update-gre-address`)
+* [Update tunnelbroker configuration](doc/update-tunnelbroker.md) (`update-tunnelbroker`)
Available modules
-----------------
-* [Manage ports in bridge](doc/mod/bridge-port-to.md)
-* [Manage VLANs on bridge ports](doc/mod/bridge-port-vlan.md)
-* [Inspect variables](doc/mod/inspectvar.md)
-* [IP address calculation](doc/mod/ipcalc.md)
-* [Send notifications via e-mail](doc/mod/notification-email.md)
-* [Send notifications via Matrix](doc/mod/notification-matrix.md)
-* [Send notifications via Telegram](doc/mod/notification-telegram.md)
-* [Download script and run it once](doc/mod/scriptrunonce.md)
+* [Manage ports in bridge](doc/mod/bridge-port-to.md) (`mod/bridge-port-to`)
+* [Manage VLANs on bridge ports](doc/mod/bridge-port-vlan.md) (`mod/bridge-port-vlan`)
+* [Inspect variables](doc/mod/inspectvar.md) (`mod/inspectvar`)
+* [IP address calculation](doc/mod/ipcalc.md) (`mod/ipcalc`)
+* [Send notifications via e-mail](doc/mod/notification-email.md) (`mod/notification-email`)
+* [Send notifications via Gotify](doc/mod/notification-gotify.md) (`mod/notification-gotify`)
+* [Send notifications via Matrix](doc/mod/notification-matrix.md) (`mod/notification-matrix`)
+* [Send notifications via Ntfy](doc/mod/notification-ntfy.md) (`mod/notification-ntfy`)
+* [Send notifications via Telegram](doc/mod/notification-telegram.md) (`mod/notification-telegram`)
+* [Download script and run it once](doc/mod/scriptrunonce.md) (`mod/scriptrunonce`)
+* [Import ssh keys for public key authentication](doc/mod/ssh-keys-import.md) (`mod/ssh-keys-import`)
Installing custom scripts & modules
-----------------------------------
@@ -231,12 +302,9 @@ still use my scripts to manage and deploy yours, by specifying `base-url`
This will fetch and install a script `hello-world.rsc` from the given url:
- $ScriptInstallUpdate hello-world.rsc "base-url=https://git.eworm.de/cgit/routeros-scripts/plain/README.d/";
+ $ScriptInstallUpdate hello-world "base-url=https://git.eworm.de/cgit/routeros-scripts-custom/plain/";
-
-
-(Yes, the example url still belongs to the repository for easy
-handling - but the url can be what ever you use.)
+
For a script to be considered valid it has to begin with a *magic token*.
Have a look at [any script](README.d/hello-world.rsc) and copy the first line
@@ -245,6 +313,26 @@ without modification.
Starting a script's name with `mod/` makes it a module and it is run
automatically by `global-functions`.
+### Linked custom scripts & modules
+
+> ⚠️ **Warning**: These links are being provided for your convenience only;
+> they do not constitute an endorsement or an approval by me. I bear no
+> responsibility for the accuracy, legality or content of the external site
+> or for that of subsequent links. Contact the external site for answers to
+> questions regarding its content.
+
+* [Hello World](https://git.eworm.de/cgit/routeros-scripts-custom/about/doc/hello-world.md)
+ (This is a demo script to show how the linking to external documentation
+ will be done.)
+
+> ℹ️ **Info**: You have your own set of scripts and/or modules and want these
+> to be listed here? There should be a general info page that links here,
+> and documentation for each script. You can start by cloning my
+> [Custom RouterOS-Scripts](https://git.eworm.de/cgit/routeros-scripts-custom/)
+> (or fork on [GitHub](https://github.com/eworm-de/routeros-scripts-custom)
+> or [GitLab](https://gitlab.com/eworm-de/routeros-scripts-custom)) and make
+> your changes. Then please [get in contact](#patches-issues-and-whishlist)...
+
Removing a script
-----------------
@@ -253,16 +341,16 @@ configuration...
/system/script/remove to-be-removed;
-
+
Possibly a scheduler and other configuration has to be removed as well.
Contact
-------
-We have a Telegram Group [RouterOS-Scripts](https://t.me/routeros_scripts)!
+We have a Telegram Group [RouterOS-Scripts ↗️](https://t.me/routeros_scripts)!
-
+[](https://t.me/routeros_scripts)
Get help, give feedback or just chat - but do not expect free professional
support!
@@ -275,16 +363,18 @@ Thanks a lot for [past contributions](CONTRIBUTIONS.md)! ❤️
### Patches, issues and whishlist
Feel free to contact me via e-mail or open an
-[issue at github](https://github.com/eworm-de/routeros-scripts/issues).
+[issue](https://github.com/eworm-de/routeros-scripts/issues) or
+[pull request](https://github.com/eworm-de/routeros-scripts/pulls)
+at github.
### Donate
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 to
-[donate with PayPal](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J).
+[donate with PayPal ↗️](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J).
-[](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J)
+[](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J)
Thanks a lot for your support!
@@ -301,15 +391,33 @@ but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
[GNU General Public License](COPYING.md) for more details.
+Disclaimer on external links
+----------------------------
+
+Our website contains links to the websites of third parties ("external
+links"). As the content of these websites is not under our control, we
+cannot assume any liability for such external content. In all cases, the
+provider of information of the linked websites is liable for the content
+and accuracy of the information provided. At the point in time when the
+links were placed, no infringements of the law were recognisable to us.
+As soon as an infringement of the law becomes known to us, we will
+immediately remove the link in question.
+
+> 💡️ **Hint**: All external links are marked with an arrow pointing
+> diagonally in an up-right (or north-east) direction (↗️).
+
Upstream
--------
-URL:
-[GitHub.com](https://github.com/eworm-de/routeros-scripts#routeros-scripts)
+[rsc.eworm.de](https://rsc.eworm.de/)
-Mirror:
-[eworm.de](https://git.eworm.de/cgit/routeros-scripts/about/)
-[GitLab.com](https://gitlab.com/eworm-de/routeros-scripts#routeros-scripts)
+[](https://rsc.eworm.de/)
+
+### Code hosting
+
+* [git.eworm.de](https://git.eworm.de/cgit/routeros-scripts/about/)
+* [GitHub.com](https://github.com/eworm-de/routeros-scripts#routeros-scripts)
+* [GitLab.com](https://gitlab.com/eworm-de/routeros-scripts#routeros-scripts)
---
-[▲ Go back to top](#top)
+[⬆️ Go back to top](#top)
diff --git a/accesslist-duplicates.capsman b/accesslist-duplicates.capsman
deleted file mode 100644
index 848ca52..0000000
--- a/accesslist-duplicates.capsman
+++ /dev/null
@@ -1,42 +0,0 @@
-#!rsc by RouterOS
-# RouterOS script: accesslist-duplicates.capsman
-# Copyright (c) 2018-2022 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.capsman";
-:global GlobalFunctionsReady;
-:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
-
-:global Read;
-
-:local Seen ({});
-:local Shown ({});
-
-: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 ];
- :foreach SeenMac in=$Seen do={
- :if ($SeenMac = $Mac) do={
- :local Skip 0;
- :foreach ShownMac in=$Shown do={
- :if ($ShownMac = $Mac) do={ :set Skip 1; }
- }
- :if ($Skip = 0) do={
- /caps-man/access-list/print where mac-address=$Mac;
- :set Shown ($Shown, $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;
- }
- }
- }
- }
- :set Seen ($Seen, $Mac);
-}
diff --git a/accesslist-duplicates.capsman.rsc b/accesslist-duplicates.capsman.rsc
new file mode 100644
index 0000000..0d7a438
--- /dev/null
+++ b/accesslist-duplicates.capsman.rsc
@@ -0,0 +1,37 @@
+#!rsc by RouterOS
+# RouterOS script: accesslist-duplicates.capsman
+# Copyright (c) 2018-2026 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!
+
+:local ExitOK false;
+:onerror Err {
+ :global GlobalConfigReady; :global GlobalFunctionsReady;
+ :retry { :if ($GlobalConfigReady != true || $GlobalFunctionsReady != true) \
+ do={ :error ("Global config and/or functions not ready."); }; } delay=500ms max=50;
+ :local ScriptName [ :jobname ];
+
+ :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 without-paging 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;
+ }
+} do={
+ :global ExitError; $ExitError $ExitOK [ :jobname ] $Err;
+}
diff --git a/accesslist-duplicates.local b/accesslist-duplicates.local
deleted file mode 100644
index 67f16f3..0000000
--- a/accesslist-duplicates.local
+++ /dev/null
@@ -1,42 +0,0 @@
-#!rsc by RouterOS
-# RouterOS script: accesslist-duplicates.local
-# Copyright (c) 2018-2022 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.local";
-:global GlobalFunctionsReady;
-:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
-
-:global Read;
-
-:local Seen ({});
-:local Shown ({});
-
-: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 ];
- :foreach SeenMac in=$Seen do={
- :if ($SeenMac = $Mac) do={
- :local Skip 0;
- :foreach ShownMac in=$Shown do={
- :if ($ShownMac = $Mac) do={ :set Skip 1; }
- }
- :if ($Skip = 0) do={
- /interface/wireless/access-list/print where mac-address=$Mac;
- :set Shown ($Shown, $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;
- }
- }
- }
- }
- :set Seen ($Seen, $Mac);
-}
diff --git a/accesslist-duplicates.local.rsc b/accesslist-duplicates.local.rsc
new file mode 100644
index 0000000..080ce72
--- /dev/null
+++ b/accesslist-duplicates.local.rsc
@@ -0,0 +1,37 @@
+#!rsc by RouterOS
+# RouterOS script: accesslist-duplicates.local
+# Copyright (c) 2018-2026 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!
+
+:local ExitOK false;
+:onerror Err {
+ :global GlobalConfigReady; :global GlobalFunctionsReady;
+ :retry { :if ($GlobalConfigReady != true || $GlobalFunctionsReady != true) \
+ do={ :error ("Global config and/or functions not ready."); }; } delay=500ms max=50;
+ :local ScriptName [ :jobname ];
+
+ :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 without-paging 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;
+ }
+} do={
+ :global ExitError; $ExitError $ExitOK [ :jobname ] $Err;
+}
diff --git a/accesslist-duplicates.template b/accesslist-duplicates.template
deleted file mode 100644
index 8676551..0000000
--- a/accesslist-duplicates.template
+++ /dev/null
@@ -1,43 +0,0 @@
-#!rsc by RouterOS
-# RouterOS script: accesslist-duplicates%TEMPL%
-# Copyright (c) 2018-2022 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
-#
-# !! This is just a template! Replace '%PATH%' with 'caps-man'
-# !! or 'interface wireless'!
-
-:local 0 "accesslist-duplicates%TEMPL%";
-:global GlobalFunctionsReady;
-:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
-
-:global Read;
-
-:local Seen ({});
-:local Shown ({});
-
-:foreach AccList in=[ /%PATH%/access-list/find where mac-address!="00:00:00:00:00:00" ] do={
- :local Mac [ /%PATH%/access-list/get $AccList mac-address ];
- :foreach SeenMac in=$Seen do={
- :if ($SeenMac = $Mac) do={
- :local Skip 0;
- :foreach ShownMac in=$Shown do={
- :if ($ShownMac = $Mac) do={ :set Skip 1; }
- }
- :if ($Skip = 0) do={
- /%PATH%/access-list/print where mac-address=$Mac;
- :set Shown ($Shown, $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");
- /%PATH%/access-list/remove $Remove;
- }
- }
- }
- }
- :set Seen ($Seen, $Mac);
-}
diff --git a/accesslist-duplicates.template.rsc b/accesslist-duplicates.template.rsc
new file mode 100644
index 0000000..15e96a9
--- /dev/null
+++ b/accesslist-duplicates.template.rsc
@@ -0,0 +1,46 @@
+#!rsc by RouterOS
+# RouterOS script: accesslist-duplicates%TEMPL%
+# Copyright (c) 2018-2026 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
+#
+# !! This is just a template to generate the real script!
+# !! Pattern '%TEMPL%' is replaced, paths are filtered.
+
+:local ExitOK false;
+:onerror Err {
+ :global GlobalConfigReady; :global GlobalFunctionsReady;
+ :retry { :if ($GlobalConfigReady != true || $GlobalFunctionsReady != true) \
+ do={ :error ("Global config and/or functions not ready."); }; } delay=500ms max=50;
+ :local ScriptName [ :jobname ];
+
+ :local Seen ({});
+
+ :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 without-paging where mac-address=$Mac;
+ /interface/wifi/access-list/print without-paging where mac-address=$Mac;
+ /interface/wireless/access-list/print without-paging 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;
+ }
+} do={
+ :global ExitError; $ExitError $ExitOK [ :jobname ] $Err;
+}
diff --git a/accesslist-duplicates.wifi.rsc b/accesslist-duplicates.wifi.rsc
new file mode 100644
index 0000000..7696f6c
--- /dev/null
+++ b/accesslist-duplicates.wifi.rsc
@@ -0,0 +1,37 @@
+#!rsc by RouterOS
+# RouterOS script: accesslist-duplicates.wifi
+# Copyright (c) 2018-2026 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!
+
+:local ExitOK false;
+:onerror Err {
+ :global GlobalConfigReady; :global GlobalFunctionsReady;
+ :retry { :if ($GlobalConfigReady != true || $GlobalFunctionsReady != true) \
+ do={ :error ("Global config and/or functions not ready."); }; } delay=500ms max=50;
+ :local ScriptName [ :jobname ];
+
+ :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 without-paging 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;
+ }
+} do={
+ :global ExitError; $ExitError $ExitOK [ :jobname ] $Err;
+}
diff --git a/backup-cloud b/backup-cloud
deleted file mode 100644
index 38aed1f..0000000
--- a/backup-cloud
+++ /dev/null
@@ -1,58 +0,0 @@
-#!rsc by RouterOS
-# RouterOS script: backup-cloud
-# Copyright (c) 2013-2022 Christian Hesse
-# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
-#
-# provides: backup-script
-#
-# upload backup to MikroTik cloud
-# https://git.eworm.de/cgit/routeros-scripts/about/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 LogPrintExit2;
-:global RandomDelay;
-:global ScriptFromTerminal;
-:global SendNotification2;
-:global SymbolForNotification;
-:global WaitFullyConnected;
-
-$WaitFullyConnected;
-
-:if ([ $ScriptFromTerminal $0 ] = false && $BackupRandomDelay > 0) do={
- $RandomDelay $BackupRandomDelay;
-}
-
-: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) ];
-
- $SendNotification2 ({ origin=$0; \
- subject=([ $SymbolForNotification "floppy-disk,cloud" ] . "Cloud backup"); \
- message=("Uploaded backup for " . $Identity . " to cloud.\n\n" . \
- [ $DeviceInfo ] . "\n\n" . \
- "Name: " . $Cloud->"name" . "\n" . \
- "Size: " . $Cloud->"size" . " B (" . ($Cloud->"size" / 1024) . " KiB)\n" . \
- "Download key: " . $Cloud->"secret-download-key"); silent=true });
-} on-error={
- $SendNotification2 ({ origin=$0; \
- subject=([ $SymbolForNotification "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;
-}
diff --git a/backup-cloud.rsc b/backup-cloud.rsc
new file mode 100644
index 0000000..4d8830b
--- /dev/null
+++ b/backup-cloud.rsc
@@ -0,0 +1,104 @@
+#!rsc by RouterOS
+# RouterOS script: backup-cloud
+# Copyright (c) 2013-2026 Christian Hesse
+# https://rsc.eworm.de/COPYING.md
+#
+# provides: backup-script, order=40
+# requires RouterOS, version=7.15
+#
+# upload backup to MikroTik cloud
+# https://rsc.eworm.de/doc/backup-cloud.md
+
+:local ExitOK false;
+:onerror Err {
+ :global GlobalConfigReady; :global GlobalFunctionsReady;
+ :retry { :if ($GlobalConfigReady != true || $GlobalFunctionsReady != true) \
+ do={ :error ("Global config and/or functions not ready."); }; } delay=500ms max=50;
+ :local ScriptName [ :jobname ];
+
+ :global 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";
+} do={
+ :global ExitError; $ExitError $ExitOK [ :jobname ] $Err;
+}
diff --git a/backup-email b/backup-email
deleted file mode 100644
index 7cdf55e..0000000
--- a/backup-email
+++ /dev/null
@@ -1,80 +0,0 @@
-#!rsc by RouterOS
-# RouterOS script: backup-email
-# Copyright (c) 2013-2022 Christian Hesse
-# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
-#
-# provides: backup-script
-#
-# create and email backup and config file
-# https://git.eworm.de/cgit/routeros-scripts/about/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 Domain;
-:global Identity;
-
-:global CharacterReplace;
-:global DeviceInfo;
-:global LogPrintExit2;
-:global MkDir;
-:global RandomDelay;
-:global ScriptFromTerminal;
-: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;
-}
-
-$WaitFullyConnected;
-
-:if ([ $ScriptFromTerminal $0 ] = false && $BackupRandomDelay > 0) do={
- $RandomDelay $BackupRandomDelay;
-}
-
-:if ([ $MkDir $0 ] = false) do={
- $LogPrintExit2 error $0 ("Failed creating directory!") true;
-}
-
-# filename based on identity
-:local FileName [ $CharacterReplace ($Identity . "." . $Domain) "." "_" ];
-:local FilePath ($0 . "/" . $FileName);
-:local BackupFile "none";
-:local ConfigFile "none";
-:local Attach ({});
-
-# 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 ConfigFile ($FileName . ".rsc");
- :set Attach ($Attach, ($FilePath . ".rsc"));
-}
-
-# 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" . \
- "Backup file: " . $BackupFile . "\n" . \
- "Config file: " . $ConfigFile); \
- attach=$Attach; remove-attach=true });
diff --git a/backup-email.rsc b/backup-email.rsc
new file mode 100644
index 0000000..317242b
--- /dev/null
+++ b/backup-email.rsc
@@ -0,0 +1,143 @@
+#!rsc by RouterOS
+# RouterOS script: backup-email
+# Copyright (c) 2013-2026 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://rsc.eworm.de/doc/backup-email.md
+
+:local ExitOK false;
+:onerror Err {
+ :global GlobalConfigReady; :global GlobalFunctionsReady;
+ :retry { :if ($GlobalConfigReady != true || $GlobalFunctionsReady != true) \
+ do={ :error ("Global config and/or functions not ready."); }; } delay=500ms max=50;
+ :local ScriptName [ :jobname ];
+
+ :global BackupPassword;
+ :global BackupRandomDelay;
+ :global BackupSendBinary;
+ :global BackupSendExport;
+ :global BackupSendGlobalConfig;
+ :global Domain;
+ :global Identity;
+ :global PackagesUpdateBackupFailure;
+
+ :global CleanName;
+ :global DeviceInfo;
+ :global FileExists;
+ :global FormatLine;
+ :global LogPrint;
+ :global MkDir;
+ :global RandomDelay;
+ :global ScriptFromTerminal;
+ :global ScriptLock;
+ :global SendEMail2;
+ :global SymbolForNotification;
+ :global WaitForFile;
+ :global WaitFullyConnected;
+
+ :if ([ :typeof $SendEMail2 ] = "nothing") do={
+ $LogPrint error $ScriptName ("The module for sending notifications via e-mail is not installed.");
+ :set ExitOK true;
+ :error false;
+ }
+
+ :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
+ :do {
+ :retry {
+ :if ([ $FileExists ($FilePath . ".conf") ".conf file" ] = true || \
+ [ $FileExists ($FilePath . ".backup") "backup" ] = true || \
+ [ $FileExists ($FilePath . ".rsc") "script" ] = true) do={
+ :error "Files are still available.";
+ }
+ } delay=1s max=120;
+ } on-error={
+ $LogPrint warning $ScriptName ("Files are still available, sending e-mail failed.");
+ :set PackagesUpdateBackupFailure true;
+ }
+ # do not remove the files here, as the mail is still queued!
+} do={
+ :global ExitError; $ExitError $ExitOK [ :jobname ] $Err;
+}
diff --git a/backup-partition b/backup-partition
deleted file mode 100644
index a8aecac..0000000
--- a/backup-partition
+++ /dev/null
@@ -1,36 +0,0 @@
-#!rsc by RouterOS
-# RouterOS script: backup-partition
-# Copyright (c) 2022 Christian Hesse
-# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
-#
-# provides: backup-script
-#
-# save configuration to fallback partition
-# https://git.eworm.de/cgit/routeros-scripts/about/doc/backup-partition.md
-
-:local 0 "backup-partition";
-:global GlobalFunctionsReady;
-:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
-
-:global LogPrintExit2;
-
-: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 ];
-
-:do {
- /partitions/save-config-to ($ActiveRunningVar->"fallback-to");
- $LogPrintExit2 info $0 ("Saved configuration to partition '" . \
- ($ActiveRunningVar->"fallback-to") . "'.") false;
-} on-error={
- $LogPrintExit2 error $0 ("Failed saving configuration to partition '" . \
- ($ActiveRunningVar->"fallback-to") . "'!") true;
-}
diff --git a/backup-partition.rsc b/backup-partition.rsc
new file mode 100644
index 0000000..5f8a635
--- /dev/null
+++ b/backup-partition.rsc
@@ -0,0 +1,128 @@
+#!rsc by RouterOS
+# RouterOS script: backup-partition
+# Copyright (c) 2022-2026 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://rsc.eworm.de/doc/backup-partition.md
+
+:local ExitOK false;
+:onerror Err {
+ :global GlobalConfigReady; :global GlobalFunctionsReady;
+ :retry { :if ($GlobalConfigReady != true || $GlobalFunctionsReady != true) \
+ do={ :error ("Global config and/or functions not ready."); }; } delay=500ms max=50;
+ :local ScriptName [ :jobname ];
+
+ :global 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;
+
+ :onerror Err {
+ /partitions/copy-to $FallbackTo;
+ $LogPrint info $ScriptName ("Copied RouterOS to partition '" . $FallbackToName . "'.");
+ } do={
+ $LogPrint error $ScriptName ("Failed copying RouterOS to partition '" . \
+ $FallbackToName . "': " . $Err);
+ :return false;
+ }
+ :return true;
+ }
+
+ :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;
+ }
+ }
+ }
+ }
+
+ :onerror Err {
+ /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 . "'.");
+ } do={
+ /system/scheduler/remove [ find where name="running-from-backup-partition" ];
+ $LogPrint error $ScriptName ("Failed saving configuration to partition '" . \
+ $FallbackToName . "': " . $Err);
+ :set PackagesUpdateBackupFailure true;
+ :set ExitOK true;
+ :error false;
+ }
+} do={
+ :global ExitError; $ExitError $ExitOK [ :jobname ] $Err;
+}
diff --git a/backup-upload b/backup-upload
deleted file mode 100644
index 8ed4149..0000000
--- a/backup-upload
+++ /dev/null
@@ -1,106 +0,0 @@
-#!rsc by RouterOS
-# RouterOS script: backup-upload
-# Copyright (c) 2013-2022 Christian Hesse
-# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
-#
-# provides: backup-script
-#
-# create and upload backup and config file
-# https://git.eworm.de/cgit/routeros-scripts/about/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 BackupUploadPass;
-:global BackupUploadUrl;
-:global BackupUploadUser;
-:global Domain;
-:global Identity;
-
-:global CharacterReplace;
-:global DeviceInfo;
-:global IfThenElse;
-:global LogPrintExit2;
-:global MkDir;
-:global RandomDelay;
-:global ScriptFromTerminal;
-:global SendNotification2;
-:global SymbolForNotification;
-:global WaitForFile;
-:global WaitFullyConnected;
-
-:if ($BackupSendBinary != true && \
- $BackupSendExport != true) do={
- $LogPrintExit2 error $0 ("Configured to send neither backup nor config export.") true;
-}
-
-$WaitFullyConnected;
-
-:if ([ $ScriptFromTerminal $0 ] = false && $BackupRandomDelay > 0) do={
- $RandomDelay $BackupRandomDelay;
-}
-
-:if ([ $MkDir $0 ] = false) do={
- $LogPrintExit2 error $0 ("Failed creating directory!") true;
-}
-
-# filename based on identity
-:local FileName [ $CharacterReplace ($Identity . "." . $Domain) "." "_" ];
-:local FilePath ($0 . "/" . $FileName);
-:local BackupFile "none";
-:local ConfigFile "none";
-:local Failed 0;
-
-# 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;
- }
-
- /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 ConfigFile ($FileName . ".rsc");
- } on-error={
- $LogPrintExit2 error $0 ("Uploading configuration export failed!") false;
- :set ConfigFile "failed";
- :set Failed 1;
- }
-
- /file/remove ($FilePath . ".rsc");
-}
-
-$SendNotification2 ({ origin=$0; \
- subject=[ $IfThenElse ($Failed > 0) \
- ([ $SymbolForNotification "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" . \
- "Backup file: " . $BackupFile . "\n" . \
- "Config file: " . $ConfigFile); silent=true });
-
-:if ($Failed = 1) do={
- :error "An error occured!";
-}
diff --git a/backup-upload.rsc b/backup-upload.rsc
new file mode 100644
index 0000000..f27032c
--- /dev/null
+++ b/backup-upload.rsc
@@ -0,0 +1,178 @@
+#!rsc by RouterOS
+# RouterOS script: backup-upload
+# Copyright (c) 2013-2026 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://rsc.eworm.de/doc/backup-upload.md
+
+:local ExitOK false;
+:onerror Err {
+ :global GlobalConfigReady; :global GlobalFunctionsReady;
+ :retry { :if ($GlobalConfigReady != true || $GlobalFunctionsReady != true) \
+ do={ :error ("Global config and/or functions not ready."); }; } delay=500ms max=50;
+ :local ScriptName [ :jobname ];
+
+ :global BackupPassword;
+ :global BackupRandomDelay;
+ :global BackupSendBinary;
+ :global BackupSendExport;
+ :global BackupSendGlobalConfig;
+ :global BackupUploadPass;
+ :global BackupUploadUrl;
+ :global BackupUploadUser;
+ :global Domain;
+ :global Identity;
+ :global PackagesUpdateBackupFailure;
+
+ :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;
+
+ :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 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");
+
+ :onerror Err {
+ /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");
+ } do={
+ $LogPrint error $ScriptName ("Uploading backup file failed: " . $Err);
+ :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");
+
+ :onerror Err {
+ /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");
+ } do={
+ $LogPrint error $ScriptName ("Uploading configuration export failed: " . $Err);
+ :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");
+
+ :onerror Err {
+ /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");
+ } do={
+ $LogPrint error $ScriptName ("Uploading global-config-overlay failed: " . $Err);
+ :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;
+} do={
+ :global ExitError; $ExitError $ExitOK [ :jobname ] $Err;
+}
diff --git a/capsman-download-packages b/capsman-download-packages
deleted file mode 100644
index b745f14..0000000
--- a/capsman-download-packages
+++ /dev/null
@@ -1,86 +0,0 @@
-#!rsc by RouterOS
-# RouterOS script: capsman-download-packages
-# Copyright (c) 2018-2022 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
-
-:local 0 "capsman-download-packages";
-: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 [ /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;
- }
- $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 [ /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;
- }
-}
-
-: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 ($Updated = true) do={
- :if ([ :len [ /system/script/find where name="capsman-rolling-upgrade" ] ] > 0) do={
- /system/script/run capsman-rolling-upgrade;
- } else={
- /caps-man/remote-cap/upgrade [ find where version!=$InstalledVersion ];
- }
-}
diff --git a/capsman-download-packages.capsman.rsc b/capsman-download-packages.capsman.rsc
new file mode 100644
index 0000000..ff91e7c
--- /dev/null
+++ b/capsman-download-packages.capsman.rsc
@@ -0,0 +1,93 @@
+#!rsc by RouterOS
+# RouterOS script: capsman-download-packages.capsman
+# Copyright (c) 2018-2026 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!
+
+:local ExitOK false;
+:onerror Err {
+ :global GlobalConfigReady; :global GlobalFunctionsReady;
+ :retry { :if ($GlobalConfigReady != true || $GlobalFunctionsReady != true) \
+ do={ :error ("Global config and/or functions not ready."); }; } delay=500ms max=50;
+ :local ScriptName [ :jobname ];
+
+ :global CleanFilePath;
+ :global DownloadPackage;
+ :global FileGet;
+ :global LogPrint;
+ :global MkDir;
+ :global RmFile;
+ :global ScriptLock;
+ :global WaitFullyConnected;
+
+ :if ([ $ScriptLock $ScriptName ] = false) do={
+ :set ExitOK true;
+ :error false;
+ }
+ $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={
+ $LogPrint warning $ScriptName ("The CAPsMAN package path is not defined, can not download packages.");
+ :set ExitOK true;
+ :error false;
+ }
+
+ :if ([ $FileGet $PackagePath ] = false) 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"; "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 ];
+ }
+ }
+} do={
+ :global ExitError; $ExitError $ExitOK [ :jobname ] $Err;
+}
diff --git a/capsman-download-packages.template.rsc b/capsman-download-packages.template.rsc
new file mode 100644
index 0000000..7f0a4b4
--- /dev/null
+++ b/capsman-download-packages.template.rsc
@@ -0,0 +1,104 @@
+#!rsc by RouterOS
+# RouterOS script: capsman-download-packages%TEMPL%
+# Copyright (c) 2018-2026 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
+#
+# !! This is just a template to generate the real script!
+# !! Pattern '%TEMPL%' is replaced, paths are filtered.
+
+:local ExitOK false;
+:onerror Err {
+ :global GlobalConfigReady; :global GlobalFunctionsReady;
+ :retry { :if ($GlobalConfigReady != true || $GlobalFunctionsReady != true) \
+ do={ :error ("Global config and/or functions not ready."); }; } delay=500ms max=50;
+ :local ScriptName [ :jobname ];
+
+ :global CleanFilePath;
+ :global DownloadPackage;
+ :global FileGet;
+ :global LogPrint;
+ :global MkDir;
+ :global RmFile;
+ :global ScriptLock;
+ :global WaitFullyConnected;
+
+ :if ([ $ScriptLock $ScriptName ] = false) do={
+ :set ExitOK true;
+ :error false;
+ }
+ $WaitFullyConnected;
+
+ :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;
+
+ :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 ([ $FileGet $PackagePath ] = false) 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.");
+# 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;
+ }
+ }
+ }
+ }
+
+ :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 ];
+ }
+ }
+} do={
+ :global ExitError; $ExitError $ExitOK [ :jobname ] $Err;
+}
diff --git a/capsman-download-packages.wifi.rsc b/capsman-download-packages.wifi.rsc
new file mode 100644
index 0000000..d65a4ae
--- /dev/null
+++ b/capsman-download-packages.wifi.rsc
@@ -0,0 +1,95 @@
+#!rsc by RouterOS
+# RouterOS script: capsman-download-packages.wifi
+# Copyright (c) 2018-2026 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!
+
+:local ExitOK false;
+:onerror Err {
+ :global GlobalConfigReady; :global GlobalFunctionsReady;
+ :retry { :if ($GlobalConfigReady != true || $GlobalFunctionsReady != true) \
+ do={ :error ("Global config and/or functions not ready."); }; } delay=500ms max=50;
+ :local ScriptName [ :jobname ];
+
+ :global CleanFilePath;
+ :global DownloadPackage;
+ :global FileGet;
+ :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 ([ $FileGet $PackagePath ] = false) 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 ];
+ }
+ }
+} do={
+ :global ExitError; $ExitError $ExitOK [ :jobname ] $Err;
+}
diff --git a/capsman-rolling-upgrade b/capsman-rolling-upgrade
deleted file mode 100644
index e6b1c51..0000000
--- a/capsman-rolling-upgrade
+++ /dev/null
@@ -1,36 +0,0 @@
-#!rsc by RouterOS
-# RouterOS script: capsman-rolling-upgrade
-# Copyright (c) 2018-2022 Christian Hesse
-# Michael Gisbers
-# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
-#
-# upgrade CAPs one after another
-# https://git.eworm.de/cgit/routeros-scripts/about/doc/capsman-rolling-upgrade.md
-
-:local 0 "capsman-rolling-upgrade";
-: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 [ /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");
- }
-}
diff --git a/capsman-rolling-upgrade.capsman.rsc b/capsman-rolling-upgrade.capsman.rsc
new file mode 100644
index 0000000..81ce015
--- /dev/null
+++ b/capsman-rolling-upgrade.capsman.rsc
@@ -0,0 +1,50 @@
+#!rsc by RouterOS
+# RouterOS script: capsman-rolling-upgrade.capsman
+# Copyright (c) 2018-2026 Christian Hesse
+# Michael Gisbers
+# https://rsc.eworm.de/COPYING.md
+#
+# provides: capsman-rolling-upgrade.capsman
+# 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!
+
+:local ExitOK false;
+:onerror Err {
+ :global GlobalConfigReady; :global GlobalFunctionsReady;
+ :retry { :if ($GlobalConfigReady != true || $GlobalFunctionsReady != true) \
+ do={ :error ("Global config and/or functions not ready."); }; } delay=500ms max=50;
+ :local ScriptName [ :jobname ];
+
+ :global LogPrint;
+ :global ScriptLock;
+
+ :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");
+ }
+ }
+} do={
+ :global ExitError; $ExitError $ExitOK [ :jobname ] $Err;
+}
diff --git a/capsman-rolling-upgrade.template.rsc b/capsman-rolling-upgrade.template.rsc
new file mode 100644
index 0000000..9789d2f
--- /dev/null
+++ b/capsman-rolling-upgrade.template.rsc
@@ -0,0 +1,58 @@
+#!rsc by RouterOS
+# RouterOS script: capsman-rolling-upgrade%TEMPL%
+# Copyright (c) 2018-2026 Christian Hesse
+# Michael Gisbers
+# https://rsc.eworm.de/COPYING.md
+#
+# provides: capsman-rolling-upgrade%TEMPL%
+# requires RouterOS, version=7.15
+#
+# upgrade CAPs one after another
+# https://rsc.eworm.de/doc/capsman-rolling-upgrade.md
+#
+# !! This is just a template to generate the real script!
+# !! Pattern '%TEMPL%' is replaced, paths are filtered.
+
+:local ExitOK false;
+:onerror Err {
+ :global GlobalConfigReady; :global GlobalFunctionsReady;
+ :retry { :if ($GlobalConfigReady != true || $GlobalFunctionsReady != true) \
+ do={ :error ("Global config and/or functions not ready."); }; } delay=500ms max=50;
+ :local ScriptName [ :jobname ];
+
+ :global LogPrint;
+ :global ScriptLock;
+
+ :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");
+ }
+ }
+} do={
+ :global ExitError; $ExitError $ExitOK [ :jobname ] $Err;
+}
diff --git a/capsman-rolling-upgrade.wifi.rsc b/capsman-rolling-upgrade.wifi.rsc
new file mode 100644
index 0000000..bc0d4d3
--- /dev/null
+++ b/capsman-rolling-upgrade.wifi.rsc
@@ -0,0 +1,51 @@
+#!rsc by RouterOS
+# RouterOS script: capsman-rolling-upgrade.wifi
+# Copyright (c) 2018-2026 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!
+
+:local ExitOK false;
+:onerror Err {
+ :global GlobalConfigReady; :global GlobalFunctionsReady;
+ :retry { :if ($GlobalConfigReady != true || $GlobalFunctionsReady != true) \
+ do={ :error ("Global config and/or functions not ready."); }; } delay=500ms max=50;
+ :local ScriptName [ :jobname ];
+
+ :global 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");
+ }
+ }
+} do={
+ :global ExitError; $ExitError $ExitOK [ :jobname ] $Err;
+}
diff --git a/certificate-renew-issued b/certificate-renew-issued
deleted file mode 100644
index 29aaa93..0000000
--- a/certificate-renew-issued
+++ /dev/null
@@ -1,38 +0,0 @@
-#!rsc by RouterOS
-# RouterOS script: certificate-renew-issued
-# Copyright (c) 2019-2022 Christian Hesse
-# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
-#
-# renew locally issued certificates
-# https://git.eworm.de/cgit/routeros-scripts/about/doc/certificate-renew-issued.md
-
-:local 0 "certificate-renew-issued";
-:global GlobalFunctionsReady;
-:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
-
-:global CertIssuedExportPass;
-
-:global LogPrintExit2;
-:global MkDir;
-
-: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;
- }
-}
diff --git a/certificate-renew-issued.rsc b/certificate-renew-issued.rsc
new file mode 100644
index 0000000..807f060
--- /dev/null
+++ b/certificate-renew-issued.rsc
@@ -0,0 +1,52 @@
+#!rsc by RouterOS
+# RouterOS script: certificate-renew-issued
+# Copyright (c) 2019-2026 Christian Hesse
+# https://rsc.eworm.de/COPYING.md
+#
+# requires RouterOS, version=7.15
+#
+# renew locally issued certificates
+# https://rsc.eworm.de/doc/certificate-renew-issued.md
+
+:local ExitOK false;
+:onerror Err {
+ :global GlobalConfigReady; :global GlobalFunctionsReady;
+ :retry { :if ($GlobalConfigReady != true || $GlobalFunctionsReady != true) \
+ do={ :error ("Global config and/or functions not ready."); }; } delay=500ms max=50;
+ :local ScriptName [ :jobname ];
+
+ :global CertIssuedExportPass;
+
+ :global LogPrint;
+ :global MkDir;
+ :global ScriptLock;
+
+ :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" . "'.");
+ }
+ }
+} do={
+ :global ExitError; $ExitError $ExitOK [ :jobname ] $Err;
+}
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/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 4c3c212..0000000
--- a/certs/E1.pem
+++ /dev/null
@@ -1,243 +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-----
-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/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/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 b/check-certificates
deleted file mode 100644
index 99da7d5..0000000
--- a/check-certificates
+++ /dev/null
@@ -1,127 +0,0 @@
-#!rsc by RouterOS
-# RouterOS script: check-certificates
-# Copyright (c) 2013-2022 Christian Hesse
-# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
-#
-# check for certificate validity
-# https://git.eworm.de/cgit/routeros-scripts/about/doc/check-certificates.md
-
-:local 0 "check-certificates";
-:global GlobalFunctionsReady;
-:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
-
-:global CertRenewPass;
-:global CertRenewTime;
-:global CertRenewUrl;
-:global Identity;
-
-:global CertificateAvailable
-:global CertificateNameByCN;
-:global IfThenElse;
-:global LogPrintExit2;
-:global ParseKeyValueStore;
-:global SendNotification2;
-:global SymbolForNotification;
-:global UrlEncode;
-:global WaitForFile;
-:global WaitFullyConnected;
-
-:local FormatExpire do={
- :global CharacterReplace;
- :return [ $CharacterReplace [ $CharacterReplace [ :tostr $1 ] "w" "w " ] "d" "d " ];
-}
-
-$WaitFullyConnected;
-
-:foreach Cert in=[ /certificate/find where !revoked !ca !scep-url expires-after<$CertRenewTime ] do={
- :local CertVal [ /certificate/get $Cert ];
-
- :do {
- :if ([ :len $CertRenewUrl ] = 0) do={
- $LogPrintExit2 info $0 ("No CertRenewUrl given.") true;
- }
- $LogPrintExit2 info $0 ("Attempting to renew certificate " . ($CertVal->"name") . ".") false;
-
- :foreach Type in={ ".pem"; ".p12" } do={
- :local CertFileName ([ $UrlEncode ($CertVal->"common-name") ] . $Type);
- :do {
- /tool/fetch check-certificate=yes-without-crl \
- ($CertRenewUrl . $CertFileName) dst-path=$CertFileName as-value;
- $WaitForFile $CertFileName;
- :foreach PassPhrase in=$CertRenewPass do={
- /certificate/import file-name=$CertFileName passphrase=$PassPhrase as-value;
- }
- /file/remove [ find where name=$CertFileName ];
-
- :foreach CertInChain in=[ /certificate/find where name~("^" . $CertFileName . "_[0-9]+\$") common-name!=($CertVal->"common-name") ] do={
- $CertificateNameByCN [ /certificate/get $CertInChain common-name ];
- }
- } on-error={
- $LogPrintExit2 debug $0 ("Could not download certificate file " . $CertFileName) false;
- }
- }
-
- :local CertNew [ /certificate/find where common-name=($CertVal->"common-name") 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 ($Cert != $CertNew) do={
- $LogPrintExit2 debug $0 ("Certificate '" . $CertVal->"name" . "' was not updated, but replaced.") 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");
- }
-
- $SendNotification2 ({ origin=$0; \
- subject=([ $SymbolForNotification "lock-with-ink-pen" ] . "Certificate renewed"); \
- message=("A certificate on " . $Identity . " has been renewed.\n\n" . \
- "Name: " . ($CertVal->"name") . "\n" . \
- "CommonName: " . ($CertNewVal->"common-name") . "\n" . \
- "Private key: " . [ $IfThenElse (($CertNewVal->"private-key") = true) "available" "missing" ] . "\n" . \
- "Fingerprint: " . ($CertNewVal->"fingerprint") . "\n" . \
- "Issuer: " . ([ $ParseKeyValueStore ($CertNewVal->"issuer") ]->"CN") . "\n" . \
- "Validity: " . ($CertNewVal->"invalid-before") . " to " . ($CertNewVal->"invalid-after") . "\n" . \
- "Expires in: " . [ $FormatExpire ($CertNewVal->"expires-after") ]); silent=true });
- $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<2w !(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!"); \
- message=("A certificate on " . $Identity . " " . $State . ".\n\n" . \
- "Name: " . ($CertVal->"name") . "\n" . \
- "CommonName: " . ($CertVal->"common-name") . "\n" . \
- "Private key: " . [ $IfThenElse (($CertVal->"private-key") = true) "available" "missing" ] . "\n" . \
- "Fingerprint: " . ($CertVal->"fingerprint") . "\n" . \
- "Issuer: " . ($CertVal->"ca") . ([ $ParseKeyValueStore ($CertVal->"issuer") ]->"CN") . "\n" . \
- "Validity: " . ($CertVal->"invalid-before") . " to " . ($CertVal->"invalid-after") . "\n" . \
- "Expires in: " . [ $IfThenElse (($CertVal->"expired") = true) "expired" [ $FormatExpire ($CertVal->"expires-after") ] ]) });
- $LogPrintExit2 info $0 ("The certificate " . ($CertVal->"name") . " " . $State . \
- ", it is invalid after " . ($CertVal->"invalid-after") . ".") false;
- }
-}
diff --git a/check-certificates.rsc b/check-certificates.rsc
new file mode 100644
index 0000000..db1e2d4
--- /dev/null
+++ b/check-certificates.rsc
@@ -0,0 +1,242 @@
+#!rsc by RouterOS
+# RouterOS script: check-certificates
+# Copyright (c) 2013-2026 Christian Hesse
+# https://rsc.eworm.de/COPYING.md
+#
+# requires RouterOS, version=7.15
+# requires device-mode, fetch
+#
+# check for certificate validity
+# https://rsc.eworm.de/doc/check-certificates.md
+
+:local ExitOK false;
+:onerror Err {
+ :global GlobalConfigReady; :global GlobalFunctionsReady;
+ :retry { :if ($GlobalConfigReady != true || $GlobalFunctionsReady != true) \
+ do={ :error ("Global config and/or functions not ready."); }; } delay=500ms max=50;
+ :local ScriptName [ :jobname ];
+
+ :global CertRenewTime;
+ :global CertRenewUrl;
+ :global CertWarnTime;
+ :global Identity;
+
+ :global CertificateAvailable;
+ :global EscapeForRegEx;
+ :global IfThenElse;
+ :global LogPrint;
+ :global ParseKeyValueStore;
+ :global ScriptLock;
+ :global SendNotification2;
+ :global SymbolForNotification;
+ :global UrlEncode;
+ :global WaitFullyConnected;
+
+ :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;
+
+ :do {
+ :if ([ :len $CertRenewUrl ] = 0) do={
+ $LogPrint info $ScriptName ("No CertRenewUrl given.");
+ :error false;
+ }
+ $LogPrint info $ScriptName ("Attempting to renew certificate '" . ($CertVal->"name") . "'.");
+
+ :local ImportSuccess false;
+ :set LastName ($CertVal->"common-name");
+ :set FetchName $LastName;
+ :set ImportSuccess [ $CheckCertificatesDownloadImport $ScriptName $LastName $FetchName ];
+ :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 ];
+ }
+ }
+ }
+ :if ($ImportSuccess = false) do={ :error 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") "fetch" ] = 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 ];
+ }
+
+ $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={
+ $LogPrint debug $ScriptName ("Could not renew certificate '" . ($CertVal->"name") . "'.");
+ }
+ }
+
+ :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={
+ $LogPrint debug $ScriptName ("Certificate '" . ($CertVal->"name") . "' is handled by SCEP, skipping.");
+ } else={
+ :local State [ $IfThenElse (($CertVal->"expired") = true) "expired" "is about to expire" ];
+
+ $SendNotification2 ({ origin=$ScriptName; \
+ subject=([ $SymbolForNotification "lock-with-ink-pen,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") . ".");
+ }
+ }
+} do={
+ :global ExitError; $ExitError $ExitOK [ :jobname ] $Err;
+}
diff --git a/check-health b/check-health
deleted file mode 100644
index 5da7d2a..0000000
--- a/check-health
+++ /dev/null
@@ -1,131 +0,0 @@
-#!rsc by RouterOS
-# RouterOS script: check-health
-# Copyright (c) 2019-2022 Christian Hesse
-# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
-#
-# check for RouterOS health state
-# https://git.eworm.de/cgit/routeros-scripts/about/doc/check-health.md
-
-:local 0 "check-health";
-:global GlobalFunctionsReady;
-:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
-
-:global CheckHealthLast;
-:global CheckHealthTemperature;
-:global CheckHealthTemperatureDeviation;
-:global CheckHealthTemperatureNotified;
-:global CheckHealthVoltageLow;
-:global CheckHealthVoltagePercent;
-:global Identity;
-
-:global IfThenElse;
-:global LogPrintExit2;
-:global ScriptLock;
-:global SendNotification2;
-:global SymbolForNotification;
-
-:local TempToNum do={
- :global CharacterReplace;
- :local T [ :toarray [ $CharacterReplace $1 "." "," ] ];
- :return ($T->0 * 10 + $T->1);
-}
-
-:if ([ :len [ /system/health/find ] ] = 0) do={
- $LogPrintExit2 error $0 ("Your device does not provide any health values.") true;
-}
-
-:if ([ :typeof $CheckHealthLast ] != "array") do={
- :set CheckHealthLast ({});
-}
-:if ([ :typeof $CheckHealthTemperatureNotified ] != "array") do={
- :set CheckHealthTemperatureNotified ({});
-}
-
-$ScriptLock $0;
-
-: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" . \
- "old value: " . ($CheckHealthLast->$Name) . " V\n" . \
- "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.") });
- }
- }
- }
- :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!") });
- }
- }
- :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;
-}
diff --git a/check-health.d/state.rsc b/check-health.d/state.rsc
new file mode 100644
index 0000000..ab32e41
--- /dev/null
+++ b/check-health.d/state.rsc
@@ -0,0 +1,49 @@
+#!rsc by RouterOS
+# RouterOS script: check-health.d/state
+# Copyright (c) 2019-2026 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 ];
+ :local ScriptName [ :tostr $1 ];
+
+ :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=$ScriptName; \
+ subject=([ $SymbolForNotification "cross-mark" ] . "Health warning: " . $Name); \
+ message=("The device '" . $Name . "' on " . $Identity . " failed!") });
+ }
+ :if ($CheckHealthLast->$Name != "ok" && \
+ $Value = "ok") do={
+ $SendNotification2 ({ origin=$ScriptName; \
+ 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..6ce3e95
--- /dev/null
+++ b/check-health.d/temperature.rsc
@@ -0,0 +1,75 @@
+#!rsc by RouterOS
+# RouterOS script: check-health.d/temperature
+# Copyright (c) 2019-2026 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 ];
+ :local ScriptName [ :tostr $1 ];
+
+ :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=$ScriptName; \
+ 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=$ScriptName; \
+ 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..59dd23c
--- /dev/null
+++ b/check-health.d/voltage.rsc
@@ -0,0 +1,64 @@
+#!rsc by RouterOS
+# RouterOS script: check-health.d/voltage
+# Copyright (c) 2019-2026 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 ];
+ :local ScriptName [ :tostr $1 ];
+
+ :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=$ScriptName; \
+ 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=$ScriptName; \
+ 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=$ScriptName; \
+ 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
new file mode 100644
index 0000000..4cb5baa
--- /dev/null
+++ b/check-health.rsc
@@ -0,0 +1,110 @@
+#!rsc by RouterOS
+# RouterOS script: check-health
+# Copyright (c) 2019-2026 Christian Hesse
+# https://rsc.eworm.de/COPYING.md
+#
+# requires RouterOS, version=7.15
+#
+# check for RouterOS health state
+# https://rsc.eworm.de/doc/check-health.md
+
+:local ExitOK false;
+:onerror Err {
+ :global GlobalConfigReady; :global GlobalFunctionsReady;
+ :retry { :if ($GlobalConfigReady != true || $GlobalFunctionsReady != true) \
+ do={ :error ("Global config and/or functions not ready."); }; } delay=500ms max=50;
+ :local ScriptName [ :jobname ];
+
+ :global CheckHealthCPUUtilization;
+ :global CheckHealthCPUUtilizationNotified;
+ :global CheckHealthLast;
+ :global CheckHealthRAMUtilizationNotified;
+ :global Identity;
+
+ :global FormatLine;
+ :global HumanReadableNum;
+ :global IfThenElse;
+ :global LogPrint;
+ :global ScriptLock;
+ :global SendNotification2;
+ :global SymbolForNotification;
+ :global ValidateSyntax;
+
+ :local TempToNum do={
+ :global CharacterReplace;
+ :local T [ :toarray [ $CharacterReplace $1 "." "," ] ];
+ :return ($T->0 * 10 + $T->1);
+ }
+
+ :if ([ $ScriptLock $ScriptName ] = false) do={
+ :set ExitOK true;
+ :error false;
+ }
+
+ :local Resource [ /system/resource/get ];
+
+ :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;
+ }
+
+ :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;
+ }
+
+ :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 Plugin in=$Plugins do={
+ :local PluginVal [ /system/script/get $Plugin ];
+ :if ([ $ValidateSyntax ($PluginVal->"source") ] = true) do={
+ :onerror Err {
+ /system/script/run $Plugin;
+ } do={
+ $LogPrint error $ScriptName ("Plugin '" . $PluginVal->"name" . "' failed to run: " . $Err);
+ }
+ } else={
+ $LogPrint error $ScriptName ("Plugin '" . $PluginVal->"name" . "' failed syntax validation, skipping.");
+ }
+ }
+
+ :foreach PluginName,Discard in=$CheckHealthPlugins do={
+ ($CheckHealthPlugins->$PluginName) \
+ ("\$CheckHealthPlugins->\"" . $PluginName . "\"") $ScriptName;
+ }
+
+ :set CheckHealthPlugins;
+} do={
+ :global ExitError; $ExitError $ExitOK [ :jobname ] $Err;
+}
diff --git a/check-lte-firmware-upgrade b/check-lte-firmware-upgrade
deleted file mode 100644
index 62943ee..0000000
--- a/check-lte-firmware-upgrade
+++ /dev/null
@@ -1,82 +0,0 @@
-#!rsc by RouterOS
-# RouterOS script: check-lte-firmware-upgrade
-# Copyright (c) 2018-2022 Christian Hesse
-# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
-#
-# check for LTE firmware upgrade, send notification
-# https://git.eworm.de/cgit/routeros-scripts/about/doc/check-lte-firmware-upgrade.md
-
-:local 0 "check-lte-firmware-upgrade";
-:global GlobalFunctionsReady;
-:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
-
-:global SentLteFirmwareUpgradeNotification;
-
-:if ([ :typeof $SentLteFirmwareUpgradeNotification ] != "array") do={
- :global SentLteFirmwareUpgradeNotification ({});
-}
-
-:local CheckInterface do={
- :local Interface $1;
-
- :global Identity;
- :global SentLteFirmwareUpgradeNotification;
-
- :global CharacterReplace;
- :global LogPrintExit2;
- :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 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 (($Firmware->"installed") = ($Firmware->"latest")) do={
- :if ([ $ScriptFromTerminal $0 ] = true) do={
- $LogPrintExit2 info $0 ("No firmware upgrade available for LTE interface " . $IntName . ".") 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;
- :return true;
- } else={
- :put "Canceled...";
- }
- }
-
- :if (($SentLteFirmwareUpgradeNotification->$IntName) = ($Firmware->"latest")) do={
- $LogPrintExit2 debug $0 ("Already sent the LTE firmware upgrade notification for version " . \
- ($Firmware->"latest") . ".") false;
- :return false;
- }
-
- $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" . \
- "Interface: " . [ $CharacterReplace ($Info->"manufacturer" . " " . $Info->"model") ("\"") "" ] . "\n" . \
- "Installed: " . ($Firmware->"installed") . "\n" . \
- "Available: " . ($Firmware->"latest")); silent=true });
- :set ($SentLteFirmwareUpgradeNotification->$IntName) ($Firmware->"latest");
-}
-
-:foreach Interface in=[ /interface/lte/find ] do={
- $CheckInterface $Interface;
-}
diff --git a/check-lte-firmware-upgrade.rsc b/check-lte-firmware-upgrade.rsc
new file mode 100644
index 0000000..ced827e
--- /dev/null
+++ b/check-lte-firmware-upgrade.rsc
@@ -0,0 +1,107 @@
+#!rsc by RouterOS
+# RouterOS script: check-lte-firmware-upgrade
+# Copyright (c) 2018-2026 Christian Hesse
+# https://rsc.eworm.de/COPYING.md
+#
+# requires RouterOS, version=7.15
+#
+# check for LTE firmware upgrade, send notification
+# https://rsc.eworm.de/doc/check-lte-firmware-upgrade.md
+
+:local ExitOK false;
+:onerror Err {
+ :global GlobalConfigReady; :global GlobalFunctionsReady;
+ :retry { :if ($GlobalConfigReady != true || $GlobalFunctionsReady != true) \
+ do={ :error ("Global config and/or functions not ready."); }; } delay=500ms max=50;
+ :local ScriptName [ :jobname ];
+
+ :global SentLteFirmwareUpgradeNotification;
+
+ :global ScriptLock;
+
+ :if ([ $ScriptLock $ScriptName ] = false) do={
+ :set ExitOK true;
+ :error 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;
+ :onerror Err {
+ :set Firmware [ /interface/lte/firmware-upgrade $Interface as-value ];
+ :set Info [ /interface/lte/monitor $Interface once as-value ];
+ } do={
+ $LogPrint debug $ScriptName ("Could not get latest LTE firmware version for interface " . \
+ $IntName . ": " . $Err);
+ :return 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;
+ }
+
+ :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");
+ }
+
+ :foreach Interface in=[ /interface/lte/find ] do={
+ $CheckInterface $ScriptName $Interface;
+ }
+} do={
+ :global ExitError; $ExitError $ExitOK [ :jobname ] $Err;
+}
diff --git a/check-perpetual-license.rsc b/check-perpetual-license.rsc
new file mode 100644
index 0000000..0e66bcc
--- /dev/null
+++ b/check-perpetual-license.rsc
@@ -0,0 +1,78 @@
+#!rsc by RouterOS
+# RouterOS script: check-perpetual-license
+# Copyright (c) 2025-2026 Christian Hesse
+# https://rsc.eworm.de/COPYING.md
+#
+# requires RouterOS, version=7.15
+#
+# check perpetual license on CHR
+# https://rsc.eworm.de/doc/check-perpetual-license.md
+
+:local ExitOK false;
+:onerror Err {
+ :global GlobalConfigReady; :global GlobalFunctionsReady;
+ :retry { :if ($GlobalConfigReady != true || $GlobalFunctionsReady != true) \
+ do={ :error ("Global config and/or functions not ready."); }; } delay=500ms max=50;
+ :local ScriptName [ :jobname ];
+
+ :global Identity;
+ :global SentCertificateNotification;
+
+ :global LogPrint;
+ :global ScriptLock;
+ :global SendNotification2;
+ :global SymbolForNotification;
+ :global WaitFullyConnected;
+
+ :if ([ $ScriptLock $ScriptName ] = false) do={
+ :set ExitOK true;
+ :error false;
+ }
+
+ $WaitFullyConnected;
+
+ :local License [ /system/license/get ];
+ :if ([ :typeof ($License->"deadline-at") ] != "str") do={
+ $LogPrint info $ScriptName ("This device does not have a perpetual license.");
+ :set ExitOK true;
+ :error true;
+ }
+
+ :if ([ :len ($License->"next-renewal-at") ] = 0 && ($License->"limited-upgrades") = true) do={
+ $LogPrint warning $ScriptName ("Your license expired on " . ($License->"deadline-at") . "!");
+ :if ($SentCertificateNotification != "expired") do={
+ $SendNotification2 ({ origin=$ScriptName; \
+ subject=([ $SymbolForNotification "scroll,cross-mark" ] . "License expired!"); \
+ message=("Your license expired on " . ($License->"deadline-at") . \
+ ", can no longer update RouterOS on " . $Identity . "...") });
+ :set SentCertificateNotification "expired";
+ }
+ :set ExitOK true;
+ :error true;
+ }
+
+ :if ([ :totime ($License->"deadline-at") ] - 3w < [ :timestamp ]) do={
+ $LogPrint warning $ScriptName ("Your license will expire on " . ($License->"deadline-at") . "!");
+ :if ($SentCertificateNotification != "warning") do={
+ $SendNotification2 ({ origin=$ScriptName; \
+ subject=([ $SymbolForNotification "scroll,warning-sign" ] . "License about to expire!"); \
+ message=("Your license failed to renew and is about to expire on " . \
+ ($License->"deadline-at") . " on " . $Identity . "...") });
+ :set SentCertificateNotification "warning";
+ }
+ :set ExitOK true;
+ :error true;
+ }
+
+ :if ([ :typeof $SentCertificateNotification ] = "str" && \
+ [ :totime ($License->"deadline-at") ] - 4w > [ :timestamp ]) do={
+ $LogPrint info $ScriptName ("Your license was successfully renewed.");
+ $SendNotification2 ({ origin=$ScriptName; \
+ subject=([ $SymbolForNotification "scroll,white-heavy-check-mark" ] . "License renewed"); \
+ message=("Your license was successfully renewed on " . $Identity . \
+ ". It is now valid until " . ($License->"deadline-at") . ".") });
+ :set SentCertificateNotification;
+ }
+} do={
+ :global ExitError; $ExitError $ExitOK [ :jobname ] $Err;
+}
diff --git a/check-routeros-update b/check-routeros-update
deleted file mode 100644
index 9e1de8e..0000000
--- a/check-routeros-update
+++ /dev/null
@@ -1,143 +0,0 @@
-#!rsc by RouterOS
-# RouterOS script: check-routeros-update
-# Copyright (c) 2013-2022 Christian Hesse
-# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
-#
-# check for RouterOS update, send notification and/or install
-# https://git.eworm.de/cgit/routeros-scripts/about/doc/check-routeros-update.md
-
-:local 0 "check-routeros-update";
-:global GlobalFunctionsReady;
-:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
-
-:global Identity;
-:global SafeUpdateNeighbor;
-:global SafeUpdateOnCap;
-:global SafeUpdatePatch;
-:global SafeUpdateUrl;
-:global SentRouterosUpdateNotification;
-
-:global DeviceInfo;
-:global LogPrintExit2;
-:global ScriptFromTerminal;
-:global ScriptLock;
-:global SendNotification2;
-:global SymbolForNotification;
-:global VersionToNum;
-:global WaitFullyConnected;
-
-: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.";
-}
-
-$ScriptLock $0;
-
-$WaitFullyConnected;
-
-:if ([ /interface/wireless/cap/get enabled ] = true && \
- [ /caps-man/manager/get enabled ] = false && \
- $SafeUpdateOnCap != true) do={
- $LogPrintExit2 error $0 ("System is managed by CAPsMAN, not checking for RouterOS version.") true;
-}
-
-:if ([ :len [ /system/scheduler/find where name="reboot-for-update" ] ] > 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 ($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"); \
- message=("Version " . $Update->"latest-version" . " is a patch update for " . $Update->"channel" . \
- ", updating on " . $Identity . "..."); link=$Link; silent=true });
- $DoUpdate;
- }
-
- :if ($SafeUpdateNeighbor = true && [ :len [ /ip/neighbor/find where \
- version=($Update->"latest-version" . " (" . $Update->"channel" . ")") ] ] > 0) do={
- $LogPrintExit2 info $0 ("Seen a neighbor running version " . $Update->"latest-version" . ", updating...") false;
- $SendNotification2 ({ origin=$0; \
- subject=([ $SymbolForNotification "sparkles" ] . "RouterOS update"); \
- message=("Seen a 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"); \
- 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;
- } else={
- :put "Canceled...";
- }
- }
-
- :if ($SentRouterosUpdateNotification = $Update->"latest-version") do={
- $LogPrintExit2 info $0 ("Already sent the RouterOS update notification for version " . \
- $Update->"latest-version" . ".") true;
- }
-
- $SendNotification2 ({ origin=$0; \
- subject=([ $SymbolForNotification "sparkles" ] . "RouterOS update"); \
- 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;
- }
-
- $SendNotification2 ({ origin=$0; \
- subject=([ $SymbolForNotification "warning-sign" ] . "RouterOS 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");
-}
diff --git a/check-routeros-update.rsc b/check-routeros-update.rsc
new file mode 100644
index 0000000..4a6925d
--- /dev/null
+++ b/check-routeros-update.rsc
@@ -0,0 +1,222 @@
+#!rsc by RouterOS
+# RouterOS script: check-routeros-update
+# Copyright (c) 2013-2026 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://rsc.eworm.de/doc/check-routeros-update.md
+
+:local ExitOK false;
+:onerror Err {
+ :global GlobalConfigReady; :global GlobalFunctionsReady;
+ :retry { :if ($GlobalConfigReady != true || $GlobalFunctionsReady != true) \
+ do={ :error ("Global config and/or functions not ready."); }; } delay=500ms max=50;
+ :local ScriptName [ :jobname ];
+
+ :global Identity;
+ :global SafeUpdateAll;
+ :global SafeUpdateNeighbor;
+ :global SafeUpdateNeighborIdentity;
+ :global SafeUpdatePatch;
+ :global SafeUpdateUrl;
+ :global SentRouterosUpdateNotification;
+
+ :global DeviceInfo;
+ :global EscapeForRegEx;
+ :global FetchUserAgentStr;
+ :global LogPrint;
+ :global RebootForUpdate;
+ :global ScriptFromTerminal;
+ :global ScriptLock;
+ :global SendNotification2;
+ :global SymbolForNotification;
+ :global VersionToNum;
+ :global WaitFullyConnected;
+
+ :local DoUpdate do={
+ :local ScriptName [ :tostr $1 ];
+
+ :if ([ :len [ /system/script/find where name="packages-update" ] ] > 0) do={
+ /system/script/run packages-update;
+ } else={
+ /system/package/update/install without-paging;
+ }
+ }
+
+ :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={
+ :if ([ :typeof $RebootForUpdate ] = "nothing") do={
+ $LogPrint info $ScriptName ("Found a stale scheduler for reboot, removing.");
+ /system/scheduler/remove "_RebootForUpdate";
+ } else={
+ $LogPrint info $ScriptName ("A reboot for update is already scheduled.");
+ :set ExitOK true;
+ :error false;
+ }
+ }
+
+ $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;
+ }
+
+ :if ([ :len ($Update->"latest-version") ] = 0) do={
+ $LogPrint info $ScriptName ("Received an empty version string from server.");
+ :set ExitOK true;
+ :error false;
+ }
+
+ :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;
+ :onerror Err {
+ :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 ];
+ } do={
+ $LogPrint warning $ScriptName ("Failed receiving safe version for " . $Update->"channel" . ": " . $Err);
+ }
+ :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");
+ }
+} do={
+ :global ExitError; $ExitError $ExitOK [ :jobname ] $Err;
+}
diff --git a/collect-wireless-mac.capsman b/collect-wireless-mac.capsman
deleted file mode 100644
index 9e502e3..0000000
--- a/collect-wireless-mac.capsman
+++ /dev/null
@@ -1,85 +0,0 @@
-#!rsc by RouterOS
-# RouterOS script: collect-wireless-mac.capsman
-# Copyright (c) 2013-2022 Christian Hesse
-# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
-#
-# collect wireless mac adresses in access list
-# https://git.eworm.de/cgit/routeros-scripts/about/doc/collect-wireless-mac.md
-#
-# provides: lease-script, order=40
-#
-# !! 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;
-
-:global EitherOr;
-:global GetMacVendor;
-:global LogPrintExit2;
-:global ScriptLock;
-:global SendNotification2;
-:global SymbolForNotification;
-
-$ScriptLock $0 false 10;
-
-: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 ([ :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 $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 mac-address=($RegVal->"mac-address") dynamic=yes status=bound ]->0);
- :if ([ :len $Lease ] > 0) do={
- :set Address [ /ip/dhcp-server/lease/get $Lease 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 ];
- }
- }
- :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" . \
- "Controller: " . $Identity . "\n" . \
- "Interface: " . $RegVal->"interface" . "\n" . \
- "SSID: " . $RegVal->"ssid" . "\n" . \
- "MAC: " . $RegVal->"mac-address" . "\n" . \
- "Vendor: " . $Vendor . "\n" . \
- "Hostname: " . $HostName . "\n" . \
- "Address: " . $Address . "\n" . \
- "DNS name: " . $DnsName . "\n" . \
- "Date: " . $DateTime) });
- }
- } else={
- $LogPrintExit2 debug $0 ("No mac address available... Ignoring.") false;
- }
-}
diff --git a/collect-wireless-mac.capsman.rsc b/collect-wireless-mac.capsman.rsc
new file mode 100644
index 0000000..2572acc
--- /dev/null
+++ b/collect-wireless-mac.capsman.rsc
@@ -0,0 +1,100 @@
+#!rsc by RouterOS
+# RouterOS script: collect-wireless-mac.capsman
+# Copyright (c) 2013-2026 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!
+
+:local ExitOK false;
+:onerror Err {
+ :global GlobalConfigReady; :global GlobalFunctionsReady;
+ :retry { :if ($GlobalConfigReady != true || $GlobalFunctionsReady != true) \
+ do={ :error ("Global config and/or functions not ready."); }; } delay=500ms max=50;
+ :local ScriptName [ :jobname ];
+
+ :global 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 [ /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 ($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 ]) });
+ }
+ } else={
+ $LogPrint debug $ScriptName ("No mac address available... Ignoring.");
+ }
+ }
+} do={
+ :global ExitError; $ExitError $ExitOK [ :jobname ] $Err;
+}
diff --git a/collect-wireless-mac.local b/collect-wireless-mac.local
deleted file mode 100644
index a6dbd24..0000000
--- a/collect-wireless-mac.local
+++ /dev/null
@@ -1,86 +0,0 @@
-#!rsc by RouterOS
-# RouterOS script: collect-wireless-mac.local
-# Copyright (c) 2013-2022 Christian Hesse
-# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
-#
-# collect wireless mac adresses in access list
-# https://git.eworm.de/cgit/routeros-scripts/about/doc/collect-wireless-mac.md
-#
-# provides: lease-script, order=40
-#
-# !! 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;
-
-:global EitherOr;
-:global GetMacVendor;
-:global LogPrintExit2;
-:global ScriptLock;
-:global SendNotification2;
-:global SymbolForNotification;
-
-$ScriptLock $0 false 10;
-
-: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 ] do={
- :local RegVal;
- :do {
- :set RegVal [ /interface/wireless/registration-table/get $Reg ];
- } on-error={
- $LogPrintExit2 debug $0 ("Device already gone... Ignoring.") 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 $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 mac-address=($RegVal->"mac-address") dynamic=yes status=bound ]->0);
- :if ([ :len $Lease ] > 0) do={
- :set Address [ /ip/dhcp-server/lease/get $Lease 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 ];
- }
- }
- :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" . \
- "Controller: " . $Identity . "\n" . \
- "Interface: " . $RegVal->"interface" . "\n" . \
- "SSID: " . $RegVal->"ssid" . "\n" . \
- "MAC: " . $RegVal->"mac-address" . "\n" . \
- "Vendor: " . $Vendor . "\n" . \
- "Hostname: " . $HostName . "\n" . \
- "Address: " . $Address . "\n" . \
- "DNS name: " . $DnsName . "\n" . \
- "Date: " . $DateTime) });
- }
- } else={
- $LogPrintExit2 debug $0 ("No mac address available... Ignoring.") false;
- }
-}
diff --git a/collect-wireless-mac.local.rsc b/collect-wireless-mac.local.rsc
new file mode 100644
index 0000000..ae9a339
--- /dev/null
+++ b/collect-wireless-mac.local.rsc
@@ -0,0 +1,101 @@
+#!rsc by RouterOS
+# RouterOS script: collect-wireless-mac.local
+# Copyright (c) 2013-2026 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!
+
+:local ExitOK false;
+:onerror Err {
+ :global GlobalConfigReady; :global GlobalFunctionsReady;
+ :retry { :if ($GlobalConfigReady != true || $GlobalFunctionsReady != true) \
+ do={ :error ("Global config and/or functions not ready."); }; } delay=500ms max=50;
+ :local ScriptName [ :jobname ];
+
+ :global 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/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 ($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 ]) });
+ }
+ } else={
+ $LogPrint debug $ScriptName ("No mac address available... Ignoring.");
+ }
+ }
+} do={
+ :global ExitError; $ExitError $ExitOK [ :jobname ] $Err;
+}
diff --git a/collect-wireless-mac.template b/collect-wireless-mac.template
deleted file mode 100644
index 42a3d0a..0000000
--- a/collect-wireless-mac.template
+++ /dev/null
@@ -1,87 +0,0 @@
-#!rsc by RouterOS
-# RouterOS script: collect-wireless-mac%TEMPL%
-# Copyright (c) 2013-2022 Christian Hesse
-# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
-#
-# collect wireless mac adresses in access list
-# https://git.eworm.de/cgit/routeros-scripts/about/doc/collect-wireless-mac.md
-#
-# provides: lease-script, order=40
-#
-# !! This is just a template! Replace '%PATH%' with 'caps-man'
-# !! or 'interface wireless'!
-
-:local 0 "collect-wireless-mac%TEMPL%";
-:global GlobalFunctionsReady;
-:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
-
-:global Identity;
-
-:global EitherOr;
-:global GetMacVendor;
-:global LogPrintExit2;
-:global ScriptLock;
-:global SendNotification2;
-:global SymbolForNotification;
-
-$ScriptLock $0 false 10;
-
-:if ([ :len [ /%PATH%/access-list/find where comment="--- collected above ---" disabled ] ] = 0) do={
- /%PATH%/access-list/add comment="--- collected above ---" disabled=yes;
- $LogPrintExit2 warning $0 ("Added disabled access-list entry with comment '--- collected above ---'.") false;
-}
-:local PlaceBefore ([ /%PATH%/access-list/find where comment="--- collected above ---" disabled ]->0);
-
-:foreach Reg in=[ /%PATH%/registration-table/find ] do={
- :local RegVal;
- :do {
- :set RegVal [ /%PATH%/registration-table/get $Reg ];
- } on-error={
- $LogPrintExit2 debug $0 ("Device already gone... Ignoring.") false;
- }
-
- :if ([ :len ($RegVal->"mac-address") ] > 0) do={
- :local AccessList ([ /%PATH%/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: " . \
- [ /%PATH%/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 mac-address=($RegVal->"mac-address") dynamic=yes status=bound ]->0);
- :if ([ :len $Lease ] > 0) do={
- :set Address [ /ip/dhcp-server/lease/get $Lease 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 ];
- }
- }
- :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;
- /%PATH%/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" . \
- "Controller: " . $Identity . "\n" . \
- "Interface: " . $RegVal->"interface" . "\n" . \
- "SSID: " . $RegVal->"ssid" . "\n" . \
- "MAC: " . $RegVal->"mac-address" . "\n" . \
- "Vendor: " . $Vendor . "\n" . \
- "Hostname: " . $HostName . "\n" . \
- "Address: " . $Address . "\n" . \
- "DNS name: " . $DnsName . "\n" . \
- "Date: " . $DateTime) });
- }
- } else={
- $LogPrintExit2 debug $0 ("No mac address available... Ignoring.") false;
- }
-}
diff --git a/collect-wireless-mac.template.rsc b/collect-wireless-mac.template.rsc
new file mode 100644
index 0000000..54b113e
--- /dev/null
+++ b/collect-wireless-mac.template.rsc
@@ -0,0 +1,118 @@
+#!rsc by RouterOS
+# RouterOS script: collect-wireless-mac%TEMPL%
+# Copyright (c) 2013-2026 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
+#
+# !! This is just a template to generate the real script!
+# !! Pattern '%TEMPL%' is replaced, paths are filtered.
+
+:local ExitOK false;
+:onerror Err {
+ :global GlobalConfigReady; :global GlobalFunctionsReady;
+ :retry { :if ($GlobalConfigReady != true || $GlobalFunctionsReady != true) \
+ do={ :error ("Global config and/or functions not ready."); }; } delay=500ms max=50;
+ :local ScriptName [ :jobname ];
+
+ :global 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 [ /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 ($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 ]) });
+ }
+ } else={
+ $LogPrint debug $ScriptName ("No mac address available... Ignoring.");
+ }
+ }
+} do={
+ :global ExitError; $ExitError $ExitOK [ :jobname ] $Err;
+}
diff --git a/collect-wireless-mac.wifi.rsc b/collect-wireless-mac.wifi.rsc
new file mode 100644
index 0000000..20bbb10
--- /dev/null
+++ b/collect-wireless-mac.wifi.rsc
@@ -0,0 +1,100 @@
+#!rsc by RouterOS
+# RouterOS script: collect-wireless-mac.wifi
+# Copyright (c) 2013-2026 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!
+
+:local ExitOK false;
+:onerror Err {
+ :global GlobalConfigReady; :global GlobalFunctionsReady;
+ :retry { :if ($GlobalConfigReady != true || $GlobalFunctionsReady != true) \
+ do={ :error ("Global config and/or functions not ready."); }; } delay=500ms max=50;
+ :local ScriptName [ :jobname ];
+
+ :global 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.");
+ }
+ }
+} do={
+ :global ExitError; $ExitError $ExitOK [ :jobname ] $Err;
+}
diff --git a/contrib/Makefile b/contrib/Makefile
new file mode 100644
index 0000000..e755a1d
--- /dev/null
+++ b/contrib/Makefile
@@ -0,0 +1,17 @@
+# Makefile
+
+HTML := $(shell grep -xl '' *.html)
+
+.PHONY: all docs clean
+
+all: docs
+
+badges.html: badges.md
+ markdown $< > $@
+
+docs: static-html.sh $(HTML) badges.html
+ ./static-html.sh $(HTML)
+
+clean:
+ rm -f badges.html
+ git checkout HEAD -- $(HTML)
diff --git a/contrib/badges.md b/contrib/badges.md
new file mode 100644
index 0000000..24bd205
--- /dev/null
+++ b/contrib/badges.md
@@ -0,0 +1,6 @@
+[](https://github.com/eworm-de/routeros-scripts/stargazers)
+[](https://github.com/eworm-de/routeros-scripts/network)
+[](https://github.com/eworm-de/routeros-scripts/watchers)
+[](https://mikrotik.com/download/changelogs/)
+[](https://t.me/routeros_scripts)
+[](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J)
diff --git a/contrib/checksums.sh b/contrib/checksums.sh
new file mode 100755
index 0000000..ab4e973
--- /dev/null
+++ b/contrib/checksums.sh
@@ -0,0 +1,9 @@
+#!/bin/sh
+
+# generate a checksums file as used by $ScriptInstallUpdate
+
+set -e
+
+md5sum $(find -name '*.rsc' | sort) | \
+ sed -e "s| \./||" -e 's|.rsc$||' | \
+ jq --raw-input --null-input '[ inputs | split (" ") | { (.[1]): (.[0]) }] | add'
diff --git a/contrib/commitinfo.sh b/contrib/commitinfo.sh
new file mode 100755
index 0000000..21faf9f
--- /dev/null
+++ b/contrib/commitinfo.sh
@@ -0,0 +1,6 @@
+#!/bin/sh
+
+sed \
+ -e "/^:global CommitId/c :global CommitId \"${COMMITID:-unknown}\";" \
+ -e "/^:global CommitInfo/c :global CommitInfo \"${COMMITINFO:-unknown}\";" \
+ < "${1}"
diff --git a/contrib/html.sh b/contrib/html.sh
new file mode 100755
index 0000000..03eba23
--- /dev/null
+++ b/contrib/html.sh
@@ -0,0 +1,23 @@
+#!/bin/sh
+
+set -e
+
+RELTO="$(dirname "${1}")"
+
+sed \
+ -e "s|__TITLE__|$(head -n1 "${1}")|" \
+ -e "s|__GENERAL__|$(realpath --relative-to="${RELTO}" general/)|" \
+ -e "s|__ROOT__|$(realpath --relative-to="${RELTO}" ./)|" \
+ < "${0}.d/head.html"
+
+markdown -f toc,idanchor "${1}" | sed \
+ -e 's/href="\([-_\./[:alnum:]]*\)\.md\(#[-[:alnum:]]*\)\?"/href="\1.html\2"/g' \
+ -e '/| id="\L\1">|' \
+ -e '//s|pre|pre class="code" onclick="CopyToClipboard(this)"|g' \
+ -e '/The above link may be broken on code hosting sites/s|blockquote|blockquote style="display: none;"|'
+
+sed \
+ -e "s|__DATE__|${DATE:-$(date --rfc-email)}|" \
+ -e "s|__VERSION__|${VERSION:-unknown}|" \
+ < "${0}.d/foot.html"
diff --git a/contrib/html.sh.d/foot.html b/contrib/html.sh.d/foot.html
new file mode 100644
index 0000000..9e28e11
--- /dev/null
+++ b/contrib/html.sh.d/foot.html
@@ -0,0 +1,5 @@
+
+
+
+