diff options
-rw-r--r-- | CONTRIBUTIONS.md | 1 | ||||
-rw-r--r-- | INITIAL-COMMANDS.md | 1 | ||||
-rw-r--r-- | README.md | 4 | ||||
-rw-r--r-- | check-certificates | 3 | ||||
-rw-r--r-- | global-config | 8 | ||||
-rw-r--r-- | global-config-overlay | 5 | ||||
-rw-r--r-- | global-config.changes | 2 | ||||
-rw-r--r-- | global-functions | 198 | ||||
-rw-r--r-- | log-forward | 28 |
9 files changed, 170 insertions, 80 deletions
diff --git a/CONTRIBUTIONS.md b/CONTRIBUTIONS.md index a78b0df..92cf2e7 100644 --- a/CONTRIBUTIONS.md +++ b/CONTRIBUTIONS.md @@ -21,6 +21,7 @@ Add yourself to the list, * Christoph Boss (@Kampfwurst) * Klaus Michael Rübsam * Linux-Schmie.de Michael Gisbers +* Manuel Kuhn * Marek Čábák * Reiner Vehrenkamp diff --git a/INITIAL-COMMANDS.md b/INITIAL-COMMANDS.md index 9a5d6c8..fa32654 100644 --- a/INITIAL-COMMANDS.md +++ b/INITIAL-COMMANDS.md @@ -17,7 +17,6 @@ procedure please follow [the long way in detail](README.md#the-long-way-in-detai :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"); } - / system script set comment="ignore" global-config-overlay; / system script { run global-config; run global-config-overlay; run global-functions; } / system scheduler add name="global-scripts" start-time=startup on-event="/ system script { run global-config; run global-config-overlay; run global-functions; }"; :global CertificateNameByCN; @@ -92,10 +92,6 @@ Now let's download the main scripts and add them in configuration on the fly. [admin@MikroTik] > :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"); } -Mark `global-config-overlay` not to be overwritten by future updates. - - [admin@MikroTik] > / system script set comment="ignore" global-config-overlay - The configuration needs to be tweaked for your needs. Edit `global-config-overlay`, copy configuration from [`global-config`](global-config) (the one without `-overlay`). diff --git a/check-certificates b/check-certificates index 68be7ee..f99b20a 100644 --- a/check-certificates +++ b/check-certificates @@ -40,6 +40,7 @@ $WaitFullyConnected; :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); @@ -48,7 +49,7 @@ $WaitFullyConnected; ($CertRenewUrl . $CertFileName) dst-path=$CertFileName as-value; $WaitForFile $CertFileName; :foreach PassPhrase in=$CertRenewPass do={ - / certificate import file-name=$CertFileName passphrase=$PassPhrase; + / certificate import file-name=$CertFileName passphrase=$PassPhrase as-value; } / file remove [ find where name=$CertFileName ]; diff --git a/global-config b/global-config index 9c8dbea..107ef19 100644 --- a/global-config +++ b/global-config @@ -55,9 +55,11 @@ # This defines a filter on log topics not to be forwarded. :global LogForwardFilter "(debug|info)"; -# ... and the same for log message text. -:global LogForwardFilterMessage "(^\$|^Error sending e-mail <.* Log Forwarding>:)"; -#:global LogForwardFilterMessage "(^\$|message text|...)"; +# ... and the same for log message text. Regular expressions are supported. +# Do *NOT* set an empty string - that will filter everything! +:global LogForwardFilterMessage []; +#:global LogForwardFilterMessage "message text"; +#:global LogForwardFilterMessage "(message text|another text|...)"; # Specify an address to enable auto update to version assumed safe. # The configured channel (bugfix, current, release-candidate) is appended. diff --git a/global-config-overlay b/global-config-overlay index 10376c7..850e6d8 100644 --- a/global-config-overlay +++ b/global-config-overlay @@ -1,5 +1,4 @@ -#!rsc by RouterOS -# RouterOS script: global-config-overlay +# Overlay for global configuration by RouterOS Scripts # Copyright (c) 2013-2021 Christian Hesse <mail@eworm.de> # https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md # @@ -8,7 +7,7 @@ # Make sure all configuration properties are up to date and this # value is in sync with value in script 'global-functions'! -# Comment or remove to disable change notifications. +# Comment or remove to disable news and change notifications. :global GlobalConfigVersion 47; # Copy configuration from global-config here and modify it. diff --git a/global-config.changes b/global-config.changes index 2bc18c0..2dc68f7 100644 --- a/global-config.changes +++ b/global-config.changes @@ -1,4 +1,4 @@ -# RouterOS global-config changes +# News, changes and migration by RouterOS Scripts # Copyright (c) 2019-2021 Christian Hesse <mail@eworm.de> # https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md diff --git a/global-functions b/global-functions index 7de3d72..387e4c0 100644 --- a/global-functions +++ b/global-functions @@ -24,6 +24,7 @@ :global DeviceInfo; :global DNSIsResolving; :global DownloadPackage; +:global EscapeForRegEx; :global FlushEmailQueue; :global FlushTelegramQueue; :global GetMacVendor; @@ -35,6 +36,7 @@ :global LogPrintExit2; :global MkDir; :global ParseKeyValueStore; +:global QuotedPrintable; :global RandomDelay; :global RequiredRouterOS; :global ScriptFromTerminal; @@ -83,7 +85,7 @@ } :local CertVal [ / certificate get [ find where common-name=$CommonName ] ]; - :do { + :while (($CertVal->"akid") != "" && ($CertVal->"akid") != ($CertVal->"skid")) do={ :if ([ :len [ / certificate find where skid=($CertVal->"akid") ] ] = 0) do={ $LogPrintExit2 info $0 ("Certificate chain for \"" . $CommonName . \ "\" is incomplete, missing \"" . ([ $ParseKeyValueStore ($CertVal->"issuer") ]->"CN") . "\".") false; @@ -92,7 +94,7 @@ } } :set CertVal [ / certificate get [ find where skid=($CertVal->"akid") ] ]; - } while=(($CertVal->"akid") != "" && ($CertVal->"akid") != ($CertVal->"skid")); + } :return true; } @@ -118,7 +120,7 @@ $UrlFileName . $ScriptUpdatesUrlSuffix) \ dst-path=$LocalFileName as-value; $WaitForFile $LocalFileName; - / certificate import file-name=$LocalFileName passphrase=""; + / certificate import file-name=$LocalFileName passphrase="" as-value; / file remove $LocalFileName; :foreach Cert in=[ / certificate find where name~("^" . $LocalFileName . "_[0-9]+\$") ] do={ @@ -229,10 +231,10 @@ :do { :resolve "low-ttl.eworm.de"; - :return true; } on-error={ :return false; } + :return true; } # download package from upgrade server @@ -288,6 +290,28 @@ :return false; } +# escape for regular expression +:set EscapeForRegEx do={ + :local Input [ :tostr $1 ]; + + :if ([ :len $Input ] = 0) do={ + :return ""; + } + + :local Return ""; + :local Chars "^.[]\$()|*+\?{}\\"; + + :for I from=0 to=([ :len $Input ] - 1) do={ + :local Char [ :pick $Input $I ]; + :if ([ :find $Chars $Char ]) do={ + :set Char ("\\" . $Char); + } + :set Return ($Return . $Char); + } + + :return $Return; +} + # flush e-mail queue :set FlushEmailQueue do={ :global EmailQueue; @@ -550,6 +574,42 @@ :return $Result; } +# convert string to quoted-printable +:global QuotedPrintable do={ + :local Input [ :tostr $1 ]; + + :if ([ :len $Input ] = 0) do={ + :return $Input; + } + + :local Return ""; + :local Chars ("\80\81\82\83\84\85\86\87\88\89\8A\8B\8C\8D\8E\8F\90\91\92\93\94\95\96\97" . \ + "\98\99\9A\9B\9C\9D\9E\9F\A0\A1\A2\A3\A4\A5\A6\A7\A8\A9\AA\AB\AC\AD\AE\AF\B0\B1\B2\B3" . \ + "\B4\B5\B6\B7\B8\B9\BA\BB\BC\BD\BE\BF\C0\C1\C2\C3\C4\C5\C6\C7\C8\C9\CA\CB\CC\CD\CE\CF" . \ + "\D0\D1\D2\D3\D4\D5\D6\D7\D8\D9\DA\DB\DC\DD\DE\DF\E0\E1\E2\E3\E4\E5\E6\E7\E8\E9\EA\EB" . \ + "\EC\ED\EE\EF\F0\F1\F2\F3\F4\F5\F6\F7\F8\F9\FA\FB\FC\FD\FE\FF"); + :local Hex { "0"; "1"; "2"; "3"; "4"; "5"; "6"; "7"; "8"; "9"; "A"; "B"; "C"; "D"; "E"; "F" }; + + :for I from=0 to=([ :len $Input ] - 1) do={ + :local Char [ :pick $Input $I ]; + :local Replace [ :find $Chars $Char ]; + + :if ($Char = "=") do={ + :set Char "=3D"; + } + :if ([ :typeof $Replace ] = "num") do={ + :set Char ("=" . ($Hex->($Replace / 16 + 8)) . ($Hex->($Replace % 16))); + } + :set Return ($Return . $Char); + } + + :if ($Input = $Return) do={ + :return $Input; + } + + :return ("=\?utf-8\?Q\?" . $Return . "\?="); +} + # delay a random amount of seconds :set RandomDelay do={ :global GetRandomNumber; @@ -629,6 +689,7 @@ } } + :local ExpectedConfigVersionBefore $ExpectedConfigVersion; :local ScriptInstallUpdateBefore [ :tostr $ScriptInstallUpdate ]; :foreach Script in=[ / system script find where source~"^#!rsc( by RouterOS)\?\n" ] do={ @@ -711,18 +772,12 @@ } } - :if ($SentConfigChangesNotification!=$ExpectedConfigVersion && \ - $GlobalConfigVersion < $ExpectedConfigVersion) do={ + :if ($ExpectedConfigVersionBefore != $ExpectedConfigVersion) do={ :global GlobalConfigChanges; :global GlobalConfigMigration; :local ChangeLogCode; - :local NotificationMessage ("Current configuration on " . $Identity . \ - " is out of date. Please update global-config-overlay, then increase " . \ - "\$GlobalConfigVersion (currently " . $GlobalConfigVersion . \ - ") to " . $ExpectedConfigVersion . " and re-run global-config-overlay."); - $LogPrintExit2 info $0 ($NotificationMessage) false; - $LogPrintExit2 debug $0 ("Fetching changelog.") false; + $LogPrintExit2 debug $0 ("Fetching news, changes and migration.") false; :do { :local Result [ / tool fetch check-certificate=yes-without-crl \ ($ScriptUpdatesBaseUrl . "global-config.changes" . $ScriptUpdatesUrlSuffix) \ @@ -731,52 +786,68 @@ :set ChangeLogCode ($Result->"data"); } } on-error={ - $LogPrintExit2 warning $0 ("Failed fetching changes!") false; - :set NotificationMessage ($NotificationMessage . \ - "\n\nChanges are not available."); + $LogPrintExit2 warning $0 ("Failed fetching news, changes and migration!") false; } :if ([ :len $ChangeLogCode ] > 0) do={ :if ([ $ValidateSyntax $ChangeLogCode ] = true) do={ - :set NotificationMessage ($NotificationMessage . "\n\nChanges:"); [ :parse $ChangeLogCode ]; - :for I from=($GlobalConfigVersion + 1) to=$ExpectedConfigVersion do={ - :local Migration ($GlobalConfigMigration->[ :tostr $I ]); - :if ([ :typeof $Migration ] = "str") do={ - :if ([ $ValidateSyntax $Migration ] = true) do={ - $LogPrintExit2 info $0 ("Applying migration: " . $Migration) false; - [ :parse $Migration ]; - } else={ - $LogPrintExit2 warning $0 ("Migration code for change " . $I . " failed syntax validation!") false; - } + } else={ + $LogPrintExit2 warning $0 ("The changelog failed syntax validation!") false; + } + } + + :if ([ :len $GlobalConfigMigration ] > 0) do={ + :for I from=($ExpectedConfigVersionBefore + 1) to=$ExpectedConfigVersion do={ + :local Migration ($GlobalConfigMigration->[ :tostr $I ]); + :if ([ :typeof $Migration ] = "str") do={ + :if ([ $ValidateSyntax $Migration ] = true) do={ + $LogPrintExit2 info $0 ("Applying migration for change " . $I . ": " . $Migration) false; + [ :parse $Migration ]; + } else={ + $LogPrintExit2 warning $0 ("Migration code for change " . $I . " failed syntax validation!") false; } - :set NotificationMessage ($NotificationMessage . \ - "\n " . [ $IfThenElse ($NotificationsWithSymbols = true) ("\E2\97\8F") "*" ] . " " . \ - $GlobalConfigChanges->[ :tostr $I ]); - $LogPrintExit2 info $0 ("Change: " . $GlobalConfigChanges->[ :tostr $I ]) false; } - :set GlobalConfigChanges; - :set GlobalConfigMigration; + } + } + + :if ($SentConfigChangesNotification != $ExpectedConfigVersion && \ + $GlobalConfigVersion < $ExpectedConfigVersion) do={ + :local NotificationMessage ("Current configuration on " . $Identity . \ + " is out of date. Please update global-config-overlay, then increase " . \ + "\$GlobalConfigVersion (currently " . $GlobalConfigVersion . \ + ") to " . $ExpectedConfigVersion . " and re-run global-config-overlay."); + $LogPrintExit2 info $0 ($NotificationMessage) false; + + :if ([ :len $GlobalConfigChanges ] > 0) do={ + :set NotificationMessage ($NotificationMessage . "\n\nChanges:"); + :for I from=($GlobalConfigVersion + 1) to=$ExpectedConfigVersion do={ + :local Change ($GlobalConfigChanges->[ :tostr $I ]); + :set NotificationMessage ($NotificationMessage . "\n " . \ + [ $IfThenElse ($NotificationsWithSymbols = true) ("\E2\97\8F") "*" ] . " " . $Change); + $LogPrintExit2 info $0 ("Change " . $I . ": " . $Change) false; + } } else={ - $LogPrintExit2 warning $0 ("The changelog failed syntax validation!") false; + :set NotificationMessage ($NotificationMessage . "\n\nNews and changes are not available."); + } + + :local Link; + :if ($IDonate != true) do={ :set NotificationMessage ($NotificationMessage . \ - "\n\nChanges are not available."); + "\n\n==== donation hint ====\n" . \ + "This project is developed in private spare time and usage is " . \ + "free of charge for you. If you like the scripts and think this is " . \ + "of value for you or your business please consider a donation."); + :set Link "https://git.eworm.de/cgit/routeros-scripts/about/#donate"; } - } - :local Link; - :if ($IDonate != true) do={ - :set NotificationMessage ($NotificationMessage . \ - "\n\n==== donation hint ====\n" . \ - "This project is developed in private spare time and usage is " . \ - "free of charge for you. If you like the scripts and think this is " . \ - "of value for you or your business please consider a donation."); - :set Link "https://git.eworm.de/cgit/routeros-scripts/about/#donate"; + $SendNotification ([ $SymbolForNotification "pushpin" ] . "News and configuration changes") \ + $NotificationMessage $Link; + :set SentConfigChangesNotification $ExpectedConfigVersion; } - $SendNotification ([ $SymbolForNotification "pushpin" ] . "News and configuration changes") \ - $NotificationMessage $Link; - :set SentConfigChangesNotification $ExpectedConfigVersion; + :set GlobalConfigChanges; + :set GlobalConfigMigration; } :if ($ScriptInstallUpdateBefore != [ :tostr $ScriptInstallUpdate ]) do={ @@ -806,8 +877,9 @@ :global EmailGeneralCc; :global EmailQueue; - :global LogPrintExit2; :global IfThenElse; + :global LogPrintExit2; + :global QuotedPrintable; :if ([ :len $EmailGeneralTo ] = 0) do={ :return false; @@ -818,7 +890,8 @@ } :local Signature [ / system note get note ]; :set ($EmailQueue->[ :len $EmailQueue ]) { - to=$EmailGeneralTo; cc=$EmailGeneralCc; subject=("[" . $Identity . "] " . $Subject); + to=$EmailGeneralTo; cc=$EmailGeneralCc; + subject=[ $QuotedPrintable ("[" . $Identity . "] " . $Subject) ]; body=($Message . \ [ $IfThenElse ([ :len $Link ] > 0) ("\n\n" . $Link) "" ] . \ [ $IfThenElse ([ :len $Signature ] > 0) ("\n-- \n" . $Signature) "" ]) }; @@ -1016,24 +1089,25 @@ # url encoding :set UrlEncode do={ :local Input [ :tostr $1 ]; - :local Return ""; - :if ([ :len $Input ] > 0) do={ - :local Chars "\n\r !\"#\$%&'()*+,:;<=>\?@[\\]^`{|}~"; - :local Subs { "%0A"; "%0D"; "%20"; "%21"; "%22"; "%23"; "%24"; "%25"; "%26"; "%27"; - "%28"; "%29"; "%2A"; "%2B"; "%2C"; "%3A"; "%3B"; "%3C"; "%3D"; "%3E"; - "%3F"; "%40"; "%5B"; "%5C"; "%5D"; "%5E"; "%60"; "%7B"; "%7C"; "%7D"; - "%7E" }; + :if ([ :len $Input ] = 0) do={ + :return ""; + } - :for I from=0 to=([ :len $Input ] - 1) do={ - :local Char [ :pick $Input $I ]; - :local Replace [ :find $Chars $Char ]; + :local Return ""; + :local Chars "\n\r !\"#\$%&'()*+,:;<=>\?@[\\]^`{|}~"; + :local Subs { "%0A"; "%0D"; "%20"; "%21"; "%22"; "%23"; "%24"; "%25"; "%26"; "%27"; + "%28"; "%29"; "%2A"; "%2B"; "%2C"; "%3A"; "%3B"; "%3C"; "%3D"; "%3E"; "%3F"; + "%40"; "%5B"; "%5C"; "%5D"; "%5E"; "%60"; "%7B"; "%7C"; "%7D"; "%7E" }; - :if ([ :len $Replace ] > 0) do={ - :set Char ($Subs->$Replace); - } - :set Return ($Return . $Char); + :for I from=0 to=([ :len $Input ] - 1) do={ + :local Char [ :pick $Input $I ]; + :local Replace [ :find $Chars $Char ]; + + :if ([ :typeof $Replace ] = "num") do={ + :set Char ($Subs->$Replace); } + :set Return ($Return . $Char); } :return $Return; @@ -1044,7 +1118,7 @@ :local Code [ :tostr $1 ]; :do { - [ :parse (":local Validate do={ " . $Code . " }") ]; + [ :parse (":local Validate do={\n" . $Code . "\n}") ]; } on-error={ :return false; } diff --git a/log-forward b/log-forward index def04e6..8842c7c 100644 --- a/log-forward +++ b/log-forward @@ -15,9 +15,12 @@ :global LogForwardFilterMessage; :global LogForwardLast; :global LogForwardRateLimit; +:global NotificationsWithSymbols; +:global EscapeForRegEx; :global IfThenElse; :global LogPrintExit2; +:global QuotedPrintable; :global ScriptLock; :global SendNotification; :global SymbolForNotification; @@ -37,18 +40,32 @@ $ScriptLock $0; $WaitFullyConnected; :local Count 0; +:local Duplicates false; :local Messages ""; :local MessageVal; +:local MessageDups [ :toarray "" ]; -:foreach Message in=[ / log find where !(topics~$LogForwardFilter) !(message~$LogForwardFilterMessage) ] do={ +:local LogForwardFilterLogForwarding ("^" . [ $EscapeForRegEx ("Error sending e-mail <" . \ + [ $QuotedPrintable ("[" . $Identity . "] " . [ $SymbolForNotification "warning-sign" ] . \ + "Log Forwarding") ] . ">:") ]); +:foreach Message in=[ / log find where !(topics~$LogForwardFilter) !(message="") \ + !(message~$LogForwardFilterLogForwarding) !(message~$LogForwardFilterMessage) ] do={ :set MessageVal [ / log get $Message ]; :if ($LogForwardLast = ($MessageVal->".id")) do={ - :set Messages ""; :set Count 0; + :set Duplicates false; + :set Messages ""; + :set MessageDups [ :toarray "" ]; } else={ - :set Messages ($Messages . "\n" . $MessageVal->"time" . " " . \ - [ :tostr ($MessageVal->"topics") ] . " " . $MessageVal->"message"); + :local DupCount ($MessageDups->($MessageVal->"message")); + :if ($DupCount < 3) do={ + :set Messages ($Messages . "\n" . [ $IfThenElse ($NotificationsWithSymbols = true) (" \E2\97\8F ") ] . \ + $MessageVal->"time" . " " . [ :tostr ($MessageVal->"topics") ] . " " . $MessageVal->"message"); + } else={ + :set Duplicates true; + } + :set ($MessageDups->($MessageVal->"message")) ($DupCount + 1); :set Count ($Count + 1); } } @@ -57,7 +74,8 @@ $WaitFullyConnected; $SendNotification ([ $SymbolForNotification "warning-sign" ] . "Log Forwarding") \ ("The log on " . $Identity . " contains " . [ $IfThenElse ($Count = 1) \ "this message" ("these " . $Count . " messages") ] . " after " . \ - [ / system resource get uptime ] . " uptime.\n" . $Messages); + [ / system resource get uptime ] . " uptime." . [ $IfThenElse ($Duplicates = true) \ + (" Multi-repeated messages have been skipped.") ] . "\n" . $Messages); :set LogForwardRateLimit ($LogForwardRateLimit + 10); :set LogForwardLast ($MessageVal->".id"); |