From c9d468dcf00d994c9dfb72d3c5bde8b0c162783c Mon Sep 17 00:00:00 2001 From: vados-dev Date: Wed, 15 Jul 2026 14:00:03 +0300 Subject: [PATCH] Backup for MikroNUC created successfully at Jul 15 14:00:03. --- .../20260715/20260715140001-Binary.backup | Bin 0 -> 388625 bytes MikroNUC/20260715/20260715140001-Config.rsc | 5522 +++++++++++++++++ .../20260715/20260715140001-Diff-Config.rsc | 6 + 3 files changed, 5528 insertions(+) create mode 100644 MikroNUC/20260715/20260715140001-Binary.backup create mode 100644 MikroNUC/20260715/20260715140001-Config.rsc create mode 100644 MikroNUC/history/20260715/20260715140001-Diff-Config.rsc diff --git a/MikroNUC/20260715/20260715140001-Binary.backup b/MikroNUC/20260715/20260715140001-Binary.backup new file mode 100644 index 0000000000000000000000000000000000000000..db20438b92708ce3ea36c6a3b019a741c9ccfeab GIT binary patch literal 388625 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

hU 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

PlvCsQh8{Nxt-6bxX{h9hz65!#64 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�S#eX&WCqu%}P`W#bIVo2uo+@&=yhwyAKMl$Mf< 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 A{r~^~ literal 0 HcmV?d00001 diff --git a/MikroNUC/20260715/20260715140001-Config.rsc b/MikroNUC/20260715/20260715140001-Config.rsc new file mode 100644 index 0000000..8194cb7 --- /dev/null +++ b/MikroNUC/20260715/20260715140001-Config.rsc @@ -0,0 +1,5522 @@ +# 2026-07-15 14:00:17 by RouterOS 7.23.2 +# system id = NmJcEM+8XAP +# +/caps-man channel +add band=2ghz-g/n extension-channel=disabled frequency=2422 name=2G-3 \ + reselect-interval=20m save-selected=yes +add band=5ghz-a/n/ac extension-channel=XXXX frequency=5660 name=5G-5660 \ + save-selected=yes +add band=5ghz-a/n/ac extension-channel=XXXXXXXX frequency=5160,5200,5280 \ + name=5G-5160-5280 reselect-interval=30m save-selected=yes +add band=5ghz-n/ac extension-channel=XXXX frequency=5180 name=5G-36 \ + reselect-interval=30m save-selected=yes +add band=5ghz-n/ac extension-channel=XXXX frequency=5200 name=5G-40 \ + reselect-interval=30m save-selected=yes +add band=5ghz-n/ac extension-channel=XXXX frequency=5220 name=5G-44 \ + reselect-interval=30m save-selected=yes +add band=5ghz-n/ac extension-channel=XXXX frequency=5240 name=5G-48 \ + reselect-interval=30m save-selected=yes +add band=2ghz-g/n extension-channel=disabled frequency=2442 name=2G-7 \ + reselect-interval=20m save-selected=yes +add band=2ghz-g/n extension-channel=disabled frequency=2462 name=2G-11 \ + reselect-interval=20m save-selected=yes +/interface bridge +add name=Bridge-LAN +add name=bridge1 +/interface ethernet +set [ find default-name=ether1 ] disable-running-check=no +/interface wireguard +add comment="Interface for Master Connection" disabled=yes listen-port=22022 \ + mtu=1360 name=MASTER +add listen-port=21641 mtu=1420 name=wg-VDSina +/caps-man security +add authentication-types=wpa2-psk encryption=aes-ccm group-encryption=aes-ccm \ + group-key-update=30m name=xs-Sec +add authentication-types=wpa2-psk encryption=aes-ccm group-encryption=aes-ccm \ + group-key-update=30m name=WPA2 +/disk +add slot=ram tmpfs-max-size=100000000 type=tmpfs +/interface list +add include=dynamic,static name=LAN +add exclude=LAN name=WAN +add exclude=LAN include=WAN name=Internet +/interface wireless security-profiles +set [ find default=yes ] supplicant-identity=MikroMIC +/iot wiliot servers +set *1 address=mqtt.us-east-2.prod.wiliot.cloud name="Wiliot US East" +/ip dhcp-server option +add code=12 name=HostName +add code=119 force=yes name=domain-search-list value="0x03'WORKGROUP'0x00" +/ip dhcp-server option sets +add name=P1505n options=HostName +add name=StartLife1 options=HostName +add name=amsterDev options=HostName +add name=MiTV options=HostName +add name=miller options=HostName +add name=amster options=HostName +add name=garage options=HostName +add name=domain-search-set options=domain-search-list +/ip pool +add name=dhcp ranges=10.30.30.150-10.30.30.200 +/system logging action +add email-start-tls=yes email-to=vados@vados.ru name=email target=email +/system script +add dont-require-permissions=no name=test owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + \r\ + \n#:local ScriptName [ :jobname ];\r\ + \n:local expSrc;\r\ + \n:local FileName;\r\ + \n:local FilePath;\r\ + \n:local DirPath \"tmpfs/\";\r\ + \n:global WaitForFile;\r\ + \n:global IfThenElse;\r\ + \n\r\ + \n:foreach Script in=([ /system/script find where name~\"Amster-GlobalConf\ + *\" ]) do={\r\ + \n:local ScriptVal [ /system/script/get \$Script ];\r\ + \n\r\ + \n:local expName [ (\$ScriptVal->\"name\") ];\r\ + \n#[/system/script get \$expScript name];\r\ + \n:delay 2s;\r\ + \n\r\ + \n:set expSrc [/system/script print where name=\$expName];\r\ + \n/system/script print where name=\$expName;\r\ + \n#[:put [/system/script get \$expScript source]];\r\ + \n:put [\$expSrc];\r\ + \n:delay 2s;\r\ + \n:set FileName \$expName;\r\ + \n:set FilePath (\$DirPath . \$FileName);\r\ + \n\r\ + \n#:log warning \"\\n----------------------\\n\\\$expSrc = \$expSrc\\n____\ + ______________________________\";\r\ + \n:log info \"\\n\\\$FileName = '\$FileName'\";\r\ + \n:log info \"\\n\\\$FilePath = \$FilePath\";\r\ + \n#:log warning {/system/script/get source=[/system/script/get \$expScript\ + \_source];};\r\ + \n\r\ + \n#\$[/system script get [/system script find name=\"\$FileName\"] source]\ + \r\ + \n\r\ + \n#:local gettingScript \"\$FileName\";\r\ + \n#:execute script=\"[[:parse \\\"[:parse { :put (\"\$[/system script get \ + [/system script find name=\$FileName] source]\"); } \\\"]]\"; \\\r\ + \n\r\ + \n#:execute script={:put \"[:parse \\\"[:parse [/system/script get [/syste\ + m/script find name=\$gettingScript] source];\\\"]]\";} \\\r\ + \n# file=(\$FilePath . \".rsc\\00\");\r\ + \n:do {\r\ + \n:local fName (\$FilePath . \".rsc\");\r\ + \n#:delay 3s;\r\ + \n#/file add name=\$fName;\r\ + \n:put [:tostr [\$expSrc]];\r\ + \n/file set \$fName contents=[:put [\$expSrc]];\r\ + \n:delay 3s;\r\ + \n\$WaitForFile \$fName;\r\ + \n(\$FilePath . \".rsc\");\r\ + \n#:delay 2s;\r\ + \n\r\ + \n} on-error={\r\ + \n:log error \"\\nERROR \\\$FilePath = \$FilePath\";\r\ + \n}\r\ + \n#\$WaitForFile (\$FilePath . \".rsc\");\r\ + \n#:execute script={ :put [/system/script get [:put \$FileName] source]; }\ + ;\r\ + \n#/system/script export \$Sscript file=\$FilePath;\r\ + \n#/system/script/export [:put [/system/script/get \$expScript source]] fi\ + le=\$FilePath;\r\ + \n#:execute script={ :put [/system/script/get \$expScript source]; } \\\r\ + \n\r\ + \n#:log warning \"\\n\\\$expScript = \$expScript\";\r\ + \n# \\\r\ + \n# file=(\$FilePath . \".rsc\\00\");\r\ + \n#script={:put [/system/script/get \$FileName source];} file=(\$FilePath\ + \_. \".rsc\\00\");\r\ + \n#/system script export \$src file=(\$FilePath . \$FileName);\r\ + \n}\r\ + \n\r\ + \n:log warning \"\\nComplete!\";\r\ + \n\r\ + \n # :execute script={ :put [ /system/script/get Amster-GlobalConfig sou\ + rce ]; } \\\r\ + \n # file=(\$FilePath . \".conf\\00\");\r\ + \n # \$WaitForFile (\$FilePath . \".conf\");\r\ + \n # :onerror Err {\r\ + \n # /tool/fetch upload=yes url=(\$BackupUploadUrl . \"/\" . \$FileName\ + \_. \".conf\") \\\r\ + \n # user=\$BackupUploadUser password=\$BackupUploadPass src-path=(\ + \$FilePath . \".conf\");\r\ + \n # :set ConfigFile [ /file/get (\$FilePath . \".conf\") ];\r\ + \n # :set (\$ConfigFile->\"name\") (\$FileName . \".conf\");\r\ + \n " +add dont-require-permissions=no name=test1 owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="\ + \r\ + \n#:local runningVer [:find [/system/resource/get version] \"(\"];\r\ + \n\r\ + \n:local runningChannel;\r\ + \n:local runningVersion;\r\ + \n:local ParseCustom;\r\ + \n\r\ + \n:set ParseCustom do={\r\ + \n :local Str [ :tostr \$1 ];\r\ + \n :local Start [:find [ \$Str ] [:tostr \$2]];\r\ + \n :local End [ :tostr \$3 ];\r\ + \n :local Num [ :tonum \$4 ];\r\ + \n :local Return;\r\ + \n\r\ + \n :if ([:len \$Num] = 0) do={:set Num \"0\"}\r\ + \n :local rest [:pick \$Str (\$Start+\$Num) [:len \$Str]]; \r\ + \n :if ([:len \$End ] = 0) do={:set Return [:pick \$Str \$Num \$Start]} e\ + lse={\r\ + \n :set End [:find \$rest \$End];\r\ + \n :set Return [:pick \$rest 0 \$End];\r\ + \n }\r\ + \n:return \$Return;\r\ + \n}\r\ + \n \r\ + \n:global runningChannel [\$ParseCustom [/system/resource/get version] \"(\ + \" \")\" 1];\r\ + \n:log warning [\$runningChannel];\r\ + \n:set runningVersion [\$ParseCustom [/system/resource/get version] \" \" \ + \")\" 2];\r\ + \n:log warning [\$runningVersion];\r\ + \n\r\ + \n\r\ + \n\r\ + \n# :if ([:len \$open] = 0) do={\r\ + \n# :log error \"\$SMP Could not extract installed OS channel from vers\ + ion string: `\$runningOsAndChannel`.\";\r\ + \n# :error \"\$SMP error, check logs\";\r\ + \n# }\r\ + \n# :local rest [:pick \$runningOsAndChannel (\$open+1) [:len \$runningOs\ + AndChannel]];\r\ + \n# :local close [:find \$rest \")\"];\r\ + \n# :local channel [:pick \$rest 0 \$close];\r\ + \n# :return \$channel;\r\ + \n#}" +add dont-require-permissions=no name=01-Amster-Init-Script owner=vados \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + source="# Amster Initial Script\r\ + \n#\r\ + \n#:global GlobalEnvRemove true; /system/script {run Amster-GlobalEnvRemov\ + e; };\r\ + \n#:delay 1s;\r\ + \n:#global ReloadGlobal true; \r\ + \n\r\ + \n:global AmScriptsUrl \"https://ros.vados.ru/\";\r\ + \n:global defOwner \"vados\";\r\ + \n:global AmFirstRun;\r\ + \n\r\ + \n:set AmFirstRun do={\r\ + \n :global AmScriptsUrl;\r\ + \n :global defOwner;\r\ + \n #:global ParseKeyValueStore;\r\ + \n #:local AmUpdCRLF true;\r\ + \n #:local IfThenElse;\r\ + \n #:set IfThenElse do={:if ([ :tostr \$1 ] = \"true\" || [ :tobool \$1\ + \_] = true) do={:return \$2}; :return \$3}\r\ + \n #/certificate/settings/set builtin-trust-store=fetch;\r\ + \n #/tool/fetch \"\$scriptsUrl/certs/Root-YE.pem\" dst-path=\"root-ye.p\ + em\";\r\ + \n #/certificate/import file-name=\"root-ye.pem\" passphrase=\"\";\r\ + \n # For basic verification we rename the certificate and print it by f\ + ingerprint. Make sure exactly this one certificate (\"Root-YE\") is shown.\ + \r\ + \n #/certificate/set name=\"Root-YE\" [ find where common-name=\"Root Y\ + E\" ];\r\ + \n #/certificate/print proplist=name,fingerprint where fingerprint=\"e1\ + 4ffcad5b0025731006caa43a121a22d8e9700f4fb9cf852f02a708aa5d5666\";\r\ + \n #:log info \"\\nAmScriptsUrl = \$AmScriptsUrl\";\r\ + \n :local Scripts {\r\ + \n \"Amster-GlobalConfig\"\r\ + \n \"Amster-GlobalFunc\";\r\ + \n# \"Amster-SFTP-backup\"\r\ + \n# \"Amster-BkpEmail-and-upd\";\r\ + \n };\r\ + \n# /system/script/set owner=(\$ScriptVal->\"name\") \\\r\ + \n# source=[ \$IfThenElse (\$ScriptUpdatesCRLF = true) \$SourceCRLF \$S\ + ourceNew ] \$Script;\r\ + \n :foreach Script in=\$Scripts do={\r\ + \n :put \"Installing \$Script...\";\r\ + \n /system/script/remove [ find where name=\$Script ];\r\ + \n /system/script/add name=\$Script owner=\$defOwner source=([ /too\ + l/fetch (\"\$AmScriptsUrl\" . \$Script . \".rsc\") output=user as-value ]-\ + >\"data\");\r\ + \n /system/script/set source=[ :tocrlf [ get \$Script source ] ] \$\ + Script;\r\ + \n };\r\ + \n /system/script { run Amster-GlobalConfig; run Amster-GlobalFunc; }\r\ + \n}\r\ + \n:global AmUpdRun;\r\ + \n:set AmUpdRun do={\r\ + \n #:local UpdScript [ :tostr \$1 ];\r\ + \n #:global ParseKeyValueStore;\r\ + \n :global AmScriptsUrl;\r\ + \n :global defOwner;\r\ + \n :local Scripts {\r\ + \n# \"Amster-GlobalConfig\"\r\ + \n# \"Amster-GlobalFunc\"\r\ + \n# \"Amster-GlobalRemove\"\r\ + \n \"Amster-backup-SFTP\"\r\ + \n \"Amster-SFTP-backup\"\r\ + \n \"Amster-BkpEmail-and-upd\"\r\ + \n \"Amster-TG-Bot\"\r\ + \n \"Amster-TG-Notification\";\r\ + \n };\r\ + \n :foreach Script in=\$Scripts do={\r\ + \n :put \"updating \$Script...\";\r\ + \n /system/script/remove [ find where name=\$Script ];\r\ + \n /system/script/add name=\$Script owner=\$defOwner source=([ /too\ + l/fetch (\"\$AmScriptsUrl\" . \$Script . \".rsc\") output=user as-value ]-\ + >\"data\");\r\ + \n /system/script/set source=[ :tocrlf [ get \$Script source ] ] \$\ + Script;\r\ + \n };\r\ + \n# /system/script/add name=\$Script owner=\$AmOwner source=([ /tool/fe\ + tch check-certificate=yes-without-crl (\"\$AmScriptsUrl\" . \$Script . \".\ + rsc\") output=user as-value ]->\"data\"); };\r\ + \n}\r\ + \n\r\ + \n\$AmFirstRun;\r\ + \n#\$AmUpdRun;\r\ + \n\r\ + \n#\$setUpdExist;\r\ + \n#:global setUpdExist;\r\ + \n#:global ParseKeyValueStore;\r\ + \n#:set setUpdExist do={\r\ + \n#:do {\r\ + \n#:global ParseKeyValueStore;\r\ + \n# :foreach Script in=[ /system/script/find where source~\"^#!rsc by Vad\ + os\\r\?\\n#comment=*\" ] do={\r\ + \n# :local ScriptVal [ /system/script/get \$Script ];\r\ + \n# :local ScriptInfo [ \$ParseKeyValueStore (\$ScriptVal->\"#comment\"\ + ) ];\r\ + \n# :local scriptName (\$ScriptVal->\"name\");\r\ + \n#:log warning (\"\\n\\\$scriptName = \". \$scriptName);\r\ + \n#: log warning (\"\\n\\\$ScriptInfo = \". \$ScriptInfo);\r\ + \n# }\r\ + \n#} on-error={}\r\ + \n#}\r\ + \n#\$name;\$owner;\$policy;\$dont-require-permissions;\$last-started;\$run\ + -count;\$source;\$invalid;\$comment;\$.id;\$.nextid;\$.dead;\$.about;\r\ + \n#;(evl / (evl /docommand=;(evl / (evl /foreachcounter=\$Script;do=;(evl \ + / (evl /localname=\$ScriptVal;value=(evl (evl /system/script/getnumber=\$S\ + cript))) (evl /localname=\$ScriptInfo;value=(evl (<%% \$ParseKeyValueStore\ + \_(> \$ParseKeyValueStore);(-> \$ScriptVal comment)))) (evl /localname=\$s\ + criptName) (evl /setname=\$scriptName;value=(-> #\$ScriptVal name)) (evl /\ + log/errormessage=(. \r\ + \n# \$ ScriptInfo = \$ScriptInfo)));in=(evl (evl /system/script/findwhere\ + =\$name;\$owner;\$policy;\$dont-require-permissions;\$last-started;\$run-c\ + ount;\$source;\$invalid;\$comment;\$.id;\$.nextid;\$.dead;\$.about;(~ \$so\ + urce (. ^#!rsc by Vados \r\ + \n# \? \r\ + \n#));5))));on-error=;(evl /)))\r\ + \n" +add dont-require-permissions=no name="Download cAP's Packages" owner=vados \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + source="# +------------------------------+\r\ + \n# | |\r\ + \n# | Mikrotik RouterOS Scripts |\r\ + \n# | _Download cAP's Packages_ |\r\ + \n# | |\r\ + \n# +------------------------------+\r\ + \n# https://github.com/sebastian13/mikrotik-routeros-scripts\r\ + \n#\r\ + \n\r\ + \n# Set a directory path providing the required space\r\ + \n# Add a microSD card https://wiki.mikrotik.com/wiki/Manual:System/Disks\ + \r\ + \n# or setup a RAM-Disk via '/disk/add type=tmpfs tmpfs-max-size=20M slot=\ + ram'\r\ + \n:local dir \"/tmpfs/capsman-packages\";\r\ + \n\r\ + \n\r\ + \n# Set Package Path if required, set policy to none if using 'upgrade-cap\ + s' script\r\ + \n/interface/wifi/capsman/set package-path=\$dir upgrade-policy=none;\r\ + \n/caps-man/manager/set package-path=\$dir upgrade-policy=none;\r\ + \n#require-same-version;\r\ + \n\r\ + \n:local installed [/system package get value-name=version [find where nam\ + e=\"routeros\"]]\r\ + \n\r\ + \n# Delete old downloads\r\ + \n# /file/remove [ find type=package name~\"capsman*\" ]\r\ + \n\r\ + \n# Starting from RouterOS version 7.13, drivers for older wireless and 60\ + GHz interfaces are\r\ + \n# part of the separate \"wireless\" package. The existing \"wifiwave2\" \ + package has been divided\r\ + \n# into distinct packages: \"wifi-qcom\" and \"wifi-qcom-ac\".\r\ + \n# RouterOS >=7.12 is required to upgrade to 7.13.\r\ + \n# https://forum.mikrotik.com/viewtopic.php\?t=202423\r\ + \n\r\ + \n:foreach architecture in={ \"arm\";\r\ + \n# \"arm64\";\r\ + \n \"x86_64\";\r\ + \n \"mipsbe\"} do={\r\ + \n :foreach package in={ \"routeros\";\r\ + \n# \"wifi-qcom-ac\";\r\ + \n# \"wifi-qcom\";\r\ + \n \"container\";\r\ + \n \"wireless\"} do={ :do {\r\ + \n :put \"\"\r\ + \n :put \"Download \$package \$installed for \$architecture\"\r\ + \n /tool fetch mode=https \\\r\ + \n url=\"https://download.mikrotik.com/routeros/\$installed/\$packa\ + ge-\$installed-\$architecture.npk\" \\\r\ + \n dst-path=\"\$dir/\$package-\$installed-\$architecture.npk\"\r\ + \n } on-error={ :put \"Error downloading the file. On a 404, note t\ + hat the package may not exist for the specified architecture!\" }\r\ + \n }\r\ + \n}\r\ + \n" +add comment="Script for setup routeros script system" \ + dont-require-permissions=no name=AM-setup owner=AM-setup policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + !rsc by Vados\r\ + \n# RouterOS script: AM-setup\r\ + \n# Script comment: Script for setup routeros script system\r\ + \n# Copyright (c) 2005-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# requires device-mode, fetch\r\ + \n#\r\ + \n# :local ScriptName [ :jobname ];\r\ + \n# Additional commands:\r\ + \n#\r\ + \n# For scripts:\r\ + \n#:global NewScript \"script_name\"; \r\ + \n#:global GlobalScriptsUrl \"https://ros.vados.ru/\";\r\ + \n#/system/script/add name=\$NewScript owner=\$NewScript source=([/tool/fe\ + tch check-certificate=yes-without-crl (\$GlobalScriptsUrl . \$ScriptAdd . \ + \".rsc\") output=user as-value ]->\"data\");\r\ + \n#:global NewScript (a);\r\ + \n#:global ScriptInstallUpdate; \$ScriptInstallUpdate;\r\ + \n#\r\ + \n# For run from terminal:\r\ + \n#/system/script/add name=AM-setup owner=AM-Setup source=([/tool/fetch ch\ + eck-certificate=yes-without-crl \"https://ros.vados.ru/AM-setup.rsc\" outp\ + ut=user as-value ]->\"data\");\r\ + \n#\r\ + \n\r\ + \n:global ScriptUpdatesCRLF \"LF\";\r\ + \n#:if (\$ScriptUpdatesCRLF != false) do={\r\ + \n# :foreach Script in=[ /system/script/find where source~\"^#!rsc by Vad\ + os\" ] do={\r\ + \n# if (\$ScriptUpdatesCRLF = true) do={\r\ + \n# /system/script/set source=[ :tocrlf [ get \$Script source ] ] \$S\ + cript;\r\ + \n# } else={\r\ + \n# /system/script/set source=[ :tolf [ get \$Script source ] ] \$Scr\ + ipt;\r\ + \n# }\r\ + \n# }\r\ + \n#}\r\ + \n\r\ + \n# /import verbose=no \$ScriptName\r\ + \n:global Identity [/system/identity/get name];\r\ + \n:global SetupScript [ :jobname ];\r\ + \n:global RemoveMe true;\r\ + \n:global RunOnce (a);\r\ + \n:global InitSetup true;\r\ + \n:global tmpfs false;\r\ + \n\r\ + \n{\r\ + \n :local RmMe [:tobool \$1];\r\ + \n\r\ + \n :global GlobalScriptsUrl;\r\ + \n :global SetupScript;\r\ + \n :global RemoveMe;\r\ + \n :global InitSetup;\r\ + \n :global tmpfs;\r\ + \n\r\ + \n :local UrlSuffix \$Identity;\r\ + \n :local UrlDir \"certs/\";\r\ + \n :local CertUrl (\$GlobalScriptsUrl . \$UrlDir . \$UrlSuffix .\"/\"); \ + \r\ + \n :local CertCommonName \"Root YE\";\r\ + \n :local CertFileName \"Root-YE.pem\";\r\ + \n :local CertFingerprint \"e14ffcad5b0025731006caa43a121a22d8e9700f4fb9c\ + f852f02a708aa5d5666\";\r\ + \n\r\ + \n :if (\$SetupScript = false) do={\r\ + \n :global SetupScript \"AM-setup\";\r\ + \n }\r\ + \n :if (\$RemoveMe != false) do={\r\ + \n :set RemoveMe true;\r\ + \n }\r\ + \n\r\ + \n :log warning (\"\\n\\\$GlobalScriptsUrl = \" . \$GlobalScriptsUrl);\r\ + \n \r\ + \n\r\ + \n :if (!(((\$CertSettings->\"builtin-trust-anchors\") = \"trusted\" ||\ + \_\\\r\ + \n (\$CertSettings->\"builtin-trust-store\") ~ \"fetch\" || \\\r\ + \n (\$CertSettings->\"builtin-trust-store\") = \"all\") && \\\r\ + \n [ :len [ /certificate/builtin/find where common-name=\$CertComm\ + onName ] ] > 0)) do={\r\ + \n :put \"Importing certificate...\";\r\ + \n /tool/fetch (\$CertUrl . \$CertFileName) dst-path=\$CertFileName\ + \_as-value;\r\ + \n :delay 1s;\r\ + \n /certificate/import file-name=\$CertFileName passphrase=\"\";\r\ + \n /certificate/set name=\"Root-YE\" [ find where common-name=\"Roo\ + t YE\" ];\r\ + \n :delay 1s;\r\ + \n };\r\ + \n :local CertSettings [ /certificate/settings/get ];\r\ + \n :if ([ :len [ /certificate/find where fingerprint=\$CertFingerprint ] \ + ] != 1) do={\r\ + \n :error \"Something is wrong with your certificates!\";\r\ + \n };\r\ + \n\r\ + \n:local MkTmpfs do={\r\ + \n :local TmpFs [ /disk/find where slot=tmpfs type=tmpfs ];\r\ + \n :if ([ :len \$TmpFs ] = 1) do={\r\ + \n :if ([ /disk/get \$TmpFs disabled ] = true) do={\r\ + \n :log info (\"The tmpfs is disabled, enabling.\");\r\ + \n /disk/enable \$TmpFs;\r\ + \n }\r\ + \n :return true;\r\ + \n }\r\ + \n :log info (\"Try to removing directory 'tmpfs'.\");\r\ + \n :do {\r\ + \n :retry {\r\ + \n /file/remove tmpfs;\r\ + \n :return true;\r\ + \n } delay=100ms max=10;\r\ + \n } on-error={ }\r\ + \n# \$RmDir \"tmpfs\";\r\ + \n :onerror Err {\r\ + \n /disk/add slot=tmpfs type=tmpfs tmpfs-max-size=([ /system/resource/g\ + et total-memory ] / 3);\r\ + \n :do {\r\ + \n :retry {\r\ + \n /file/get tmpfs;\r\ + \n :return true;\r\ + \n } delay=222ms max=10;\r\ + \n } on-error={ }\r\ + \n } do={\r\ + \n :log warning (\"Creating disk of type tmpfs failed: \" . \$Err);\r\ + \n :return false;\r\ + \n }\r\ + \n# :return true;\r\ + \n}\r\ + \n:if (\$tmpfs = true) do={\r\ + \n :put \"Tmpfs variable is true. Checking for tmpfs/scripts directory...\ + \";\r\ + \n :if ([/file find name=\"tmpfs/scripts\"] != \"\") do={\r\ + \n :log info (\$0 . \"Directory tmpfs/scripts exsist.\");\r\ + \n :return \$true;\r\ + \n } else={\r\ + \n :log warning (\$0 . \"Directory tmpfs/scripts does't exsist.\");\r\ + \n :if ([ \$MkTmpfs ] = false) do={\r\ + \n :log error (\$0 . \"Directory tmpfs is not created!\");\r\ + \n :return false;\r\ + \n } else={\r\ + \n :onerror Err {\r\ + \n /file/add type=\"directory\" name=/tmpfs/scripts;\r\ + \n :do {\r\ + \n :retry {\r\ + \n /file/get /tmpfs/scripts;\r\ + \n :return true;\r\ + \n } delay=100ms max=10;\r\ + \n } on-error={ }\r\ + \n } do={\r\ + \n :log warning (\$0 . \"Making directory 'scripts' failed: \" . \$\ + Err);\r\ + \n :return false;\r\ + \n }\r\ + \n# :return true;\r\ + \n# [ /file/get \$FileName ]\r\ + \n }\r\ + \n# :log error (\$0 . \"Directory tmpfs/scripts does not exsist. Remove\ + \_global variable tmpfs!\");\r\ + \n# :global tmpfs (a);\r\ + \n# return false;\r\ + \n }\r\ + \n}\r\ + \n :put \"Renaming AM-GlobalConfig, if exists...\";\r\ + \n :local ConfOldName (\"AM-GlobalConfig-\" . [ /system/clock/get date ] \ + . [ /system/clock/get time ] . \".rsc\");\r\ + \n /system/script/set name=\$ConfOldName [ find where name=\"AM-GlobalCon\ + fig\" ];\r\ + \n :foreach Script in={ \"AM-GlobalConfig\"; \"AM-GlobalFunc\" } do={\r\ + \n :put \"Installing \$Script...\";\r\ + \n /system/script/remove [ find where name=\$Script ];\r\ + \n /system/script/add name=\$Script owner=\$Script source=([ /tool/fetc\ + h check-certificate=yes-without-crl url=(\$GlobalScriptsUrl . \$Script . \ + \".rsc\") output=user as-value ]->\"data\");\r\ + \n \r\ + \n /system/script run \$Script;\r\ + \n :delay 2s;\r\ + \n };\r\ + \n\r\ + \n :local Script \"mod/AM-Certs-By-Ident\";\r\ + \n \r\ + \n :put \"Installing \$Script...\";\r\ + \n /system/script/remove [ find where name=\$Script ];\r\ + \n /system/script/add name=\$Script owner=\$Script source=([ /tool/fetch \ + check-certificate=yes-without-crl url=(\$GlobalScriptsUrl . \$Script . \".\ + rsc\") output=user as-value ]->\"data\");\r\ + \n /system/script run \$Script;\r\ + \n :global tmpAddCerts;\r\ + \n :while ([ \$tmpAddCerts ] = true) do={\r\ + \n :delay 1s;\r\ + \n }\r\ + \n :delay 1s;\r\ + \n :put \"Removing \$Script...\";\r\ + \n /system/script/remove [ find where name=\$Script ];\r\ + \n\r\ + \n :if ([ :len [ /certificate/find where fingerprint=\$CertFingerprint ] \ + ] > 0) do={\r\ + \n :put \"Renaming certificate by its common-name...\";\r\ + \n :global CertificateNameByCN;\r\ + \n \$CertificateNameByCN \$CertFingerprint;\r\ + \n };\r\ + \n\r\ + \n :if (\$RemoveMe = true) do={\r\ + \n :put \"Add Scheduler for remove this script...\";\r\ + \n :local OnEvent (\":delay 30s;\\n\" . \"/system/script/remove [ find \ + where name=\\\"\$SetupScript\\\" ];\\n\" . \\\r\ + \n \":delay 2s;\\n\" . \"/system/scheduler/remove [ find where name=\\\ + \"_RemoveSetup\\\" ];\\n\" . \\\r\ + \n \":delay 2s;\\n\" . \":global RemoveMe (a);\\n\");\r\ + \n :local Comment \"_RemoveSetup tmp scheduler for remove setup script.\ + \";\r\ + \n /system/scheduler/add name=\"_RemoveSetup\" comment=\$Comment start-\ + time=startup interval=30s on-event=\$OnEvent;\r\ + \n :log warning (\$0 . \"\\nI've done everything, and now I'm tired..\ + . I'm leaving.\\nThere will be an error below, this is normal.\");\r\ + \n }\r\ + \n :delay 1;\r\ + \n :put \"Loading configuration and functions...\";\r\ + \n :do { \r\ + \n :global ScriptInstallUpdate; \$ScriptInstallUpdate;\r\ + \n :put \"Normal removing temporary global environment variables.\";\ + \r\ + \n :global SetupScript (a);\r\ + \n :global InitSetup (a);\r\ + \n :global RemoveMe (a);\r\ + \n } on-error={\r\ + \n :local ErrorMsg \"Loading configuration and functions error!\";\r\ + \n :log error (\$0 . \$ErrorMsg . :error);\r\ + \n :error \$ErrorMsg;\r\ + \n }\r\ + \n}\r\ + \n" +add comment="Global Config Script" dont-require-permissions=no name=\ + AM-GlobalConfig owner=AM-GlobalConfig policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + !rsc by Vados\r\ + \n# RouterOS script: AM-GlobalConfig\r\ + \n# Script comment: Global Config Script\r\ + \n# Copyright (c) 2007-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# Warning: Dont touch this line!\r\ + \n:global Identity [/system/identity/get name];\r\ + \n:global GlobalConfReady false;\r\ + \n#\r\ + \n#\r\ + \n# Global Url For Amster scripts to fetch\r\ + \n# most be defined in AM-Init-Script or AM-setup scripts\r\ + \n:global GlobalScriptsUrl \"https://ros.vados.ru/\";\r\ + \n:global ScriptsUrlSuffix (a);\r\ + \n:global CheckSumsVerify true;\r\ + \n\r\ + \n:global ScriptUpdatesCRLF true;\r\ + \n:global CommentsInScripts true;\r\ + \n\r\ + \n# Debug output:\r\ + \n:global PrintDebug true; #false;\r\ + \n\r\ + \n# Debug output for specific script:\r\ + \n#:set (\$PrintDebugOverride->\"AM-GlobalFunc\") true;\r\ + \n\r\ + \n# Debug logs\r\ + \n# Other actions (disk, email, remote or support) can be used as well.\r\ + \n# I do not recommend using echo - use debug output instead (https://rsc.\ + eworm.de/main/DEBUG.html#debug-output).\r\ + \n#/system/logging/add topics=script,debug action=memory;\r\ + \n\r\ + \n:global MikrotikUpgradeUrl \"https://upgrade.mikrotik.com/routeros\";\r\ + \n:global Domain \"WORKGROUP\";\r\ + \n\r\ + \n:global EmailGeneralTo \"vados@vados.ru\";\r\ + \n#:global EmailGeneralCc \"\";\r\ + \n\r\ + \n# \$ScriptInstallUpdate mod/notification-telegram\r\ + \n:global TelegramTokenId \"8390868324:AAG3_d1hfVR2M6pGAgDK6ZvhOe4J97nyopw\ + \";\r\ + \n:global TelegramChatId \"-1003631309063\"; #\"454403445\";\r\ + \n# Use this to send notifications to a specific topic in group.\r\ + \n#:global TelegramThreadId \"30\";\r\ + \n# Using telegram-chat you have to define trusted chat ids (not group ids\ + !)\r\ + \n# or user names. Groups allow to chat with devices simultaneously.\r\ + \n:global TelegramChatIdsTrusted {\r\ + \n \"454403445\"\r\ + \n \"-1003631309063\"\r\ + \n \"-5124482807\"\r\ + \n \"1087968824\";\r\ + \n};\r\ + \n# \"-5294757340\";\r\ + \n\r\ + \n:global TelegramChatGroups \"(all)\";\r\ + \n#:global TelegramChatGroups \"(all|home|office)\";\r\ + \n\r\ + \n:global NotificationFunctions {\r\ + \n \"email\"\r\ + \n \"telegram\";\r\ + \n};\r\ + \n# Toggle this to disable symbols in notifications.\r\ + \n:global NotificationsWithSymbols true;\r\ + \n:global TerminalColorOutput true;\r\ + \n\r\ + \n:global DetectIpAddrDefault \"https://ipv4.mikrotik.ovh/\";\r\ + \n:global DetectIpAddrFallback \"https://api.ipify.org/\";\r\ + \n\r\ + \n## Update channel. Possible values: stable, long-term, testing, developm\ + ent\r\ + \n:global updateChannel \"stable\";\r\ + \n\r\ + \n:global CertRenewUrl (\$GlobalScriptsUrl . \"certs/\" . \$Identity . \"/\ + \");\r\ + \n:global CertRenewTime 2w;\r\ + \n:global CertRenewPass (a); #{\r\ + \n# \"\";\r\ + \n# \"v3ry-s3cr3t\";\r\ + \n# \"4n0th3r-s3cr3t\";\r\ + \n#};\r\ + \n:global CertWarnTime 1w;\r\ + \n:global CertIssuedExportPass (a); # {\r\ + \n# \"\";\r\ + \n# \"cert1-cn\"=\"v3ry-s3cr3t\";\r\ + \n# \"cert2-cn\"=\"4n0th3r-s3cr3t\";\r\ + \n#};\r\ + \n\r\ + \n# Script BackupAndUpdate mode, possible values: backup,\r\ + \n# updateOnce - Update once as \"osnotify\" and back \"osnotify\" mode (i\ + n terminal run :set OSUpdateMode updateOnce; \$OSUpdateMode;). \r\ + \n# osupdate - Update if available and creates backups before/after update\ + \_(ignores `forceBackup`)\r\ + \n# Set `forceBackup` to true to always create backups, even\ + \_without updates\r\ + \n# osnotify - Set `forceBackup` to always create backups on every run\r\ + \n:global DevInfoIncludeIP true;\r\ + \n:global BackupAndUpdateMode \"osnotify\"\r\ + \n\r\ + \n:global forceBackup true;\r\ + \n:global BackupPassword \"Peskar55\";\r\ + \n\r\ + \n# SFTP Backup Variables\r\ + \n:global BackupRandomDelay 0;\r\ + \n:global BackupUploadUrl \"sftp://amster.vados.ru/upload/\";\r\ + \n:global BackupUploadUser \$Identity;\r\ + \n:global BackupUploadPass \"root.mnuc.backuper\";\r\ + \n\r\ + \n:global BackupSendBinary true;\r\ + \n:global BackupSendExport true;\r\ + \n:global BackupSendGlobalConfig true;\r\ + \n:global BackupSendScripts true;\r\ + \n\r\ + \n# Remove local file after uploading if no errors\r\ + \n:global BackupRmLocal true;\r\ + \n\r\ + \n# Local Backups Root Directory \r\ + \n:global BackupLocalRoot \"tmpfs\";\r\ + \n\r\ + \n# Encrypt Backup\r\ + \n:global BackupEncrypt true;\r\ + \n\r\ + \n# Sensitive information in Backups\r\ + \n:global BackupSens true;\r\ + \n\r\ + \n# User Export\r\ + \n:global BackupUser true;\r\ + \n\r\ + \n# License Export (not for CHR, will silently skip)\r\ + \n:global BackupLicense false;\r\ + \n\r\ + \n# SSH Keys\r\ + \n:global BackupSshKeys true;\r\ + \n\r\ + \n# Certificate Export\r\ + \n:global BackupCerts true;\r\ + \n\r\ + \n# Certificate Password\r\ + \n:global BackupCertsPasswd \"\";\r\ + \n\r\ + \n# User-Manager Export\r\ + \n:global BackupUserMan false;\r\ + \n\r\ + \n# The Dude Export\r\ + \n:global BackupDude false;\r\ + \n\r\ + \n# User Files to export, comma-separated string or array of strings\r\ + \n# User Files are not removed on backup\r\ + \n# Any directory paths will be removed (/ -> _) on remote file\r\ + \n# Nonpresent files are silently skipped\r\ + \n:global BackupUserList \"autosupout.rif,autosupout.old.rif\";\r\ + \n\r\ + \n# load functions and any custom scripts\r\ + \n# [ /system/script/find where name=\"global-config-ov\ + erlay\" ], \\\r\ + \n# [ /system/script/find where name~\"^global-config-o\ + verlay\\\\.d/.\" ]\r\ + \n#\r\ + \n#:foreach Script in=([ /system/script/find where name=\"Amster-TGglobalV\ + ars\" ], [ /system/script/find where name=\"Amster-TGglobalFunc\" ]) do={\ + \r\ + \n# :onerror Err {\r\ + \n# /system/script/run \$Script;\r\ + \n# } do={\r\ + \n# :log error (\"Loading configuration from configs and functions\"\ + \_. [ /system/script/get \$Script name ] . \" failed: \" . \$Err);\r\ + \n# }\r\ + \n#}\r\ + \n#:log info \"\\ndevMode = \$devMode\";\r\ + \n#:log info \"\\ndevRunning = \$devRunning\";\r\ + \n:foreach Script in=[ /system/script/find where source~\"^#!rsc by Vados \ + GlobalRUN\\r\?\\n\" ] do={\r\ + \n :onerror Err {\r\ + \n /system/script/run \$Script;\r\ + \n } do={\r\ + \n :log error (\"Run script \" . [ /system/script/get \$Script name \ + ] . \" width marker \\\" GlobalRUN\\\" failed: \" . \$Err);\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n# signal we are ready\r\ + \n:set GlobalConfReady true;\r\ + \n:while (\$GlobalConfReady != true) do={:delay 500ms};\r\ + \n" +add comment="Global functions" dont-require-permissions=no name=AM-GlobalFunc \ + owner=AM-GlobalFunc policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + !rsc by Vados\r\ + \n# RouterOS script: AM-GglobalFunc\r\ + \n# Script comment: Global functions\r\ + \n# Copyright (c) 2005-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# requires device-mode, fetch, scheduler\r\ + \n:local ExitOK false;\r\ + \n:local ScriptName [ :jobname ];\r\ + \n#:global checkRunJob \$SriptName;\r\ + \n#:global fixCRLF;\r\ + \n#:if (\$fixCRLF != false) do={\r\ + \n#:foreach fixScript in=([ /system/script/find where name~\"*\" ]) do={/s\ + ystem/script/set source=[ :tocrlf [ get \$fixScript source ] ] \$fixScript\ + }\r\ + \n#:if (\$fixCRLF != \"LF\") do={ /system/script/set source=[ :tocrlf [ g\ + et \$fixScript source ] ] \$fixScript};};\r\ + \n#}\r\ + \n:global GlobalFuncReady false;\r\ + \n\r\ + \n# Git commit id & info, expected configuration version\r\ + \n:global CommitId \"main/729/f42d43ae\";\r\ + \n:global CommitTAG \"unknown\";\r\ + \n:global CommitInfo \"Autocommit in repo: ros.vados.ru, generated by owne\ + r: root, in 00:05:03 01.07.2026.\";\r\ + \n\r\ + \n# The variables are received right here.\r\ + \n:global GlobalConfVersion 140;\r\ + \n\r\ + \n:global SumsDirAndFileName \"tmp/checksums.json\";\r\ + \n\r\ + \n:global Identity [/system/identity/get name];\r\ + \n:global IdentityShort [:pick \$Identity 0 18];\r\ + \n\r\ + \n:global devBoardName [/system/resource/get board-name];\r\ + \n:global devMode [/system/device-mode/get mode];\r\ + \n:global devCurArch [/system/resource/get architecture-name];\r\ + \n:global devCurVersion [/system/resource/get version];\r\ + \n\r\ + \n# Variables defined in AM-GlobalConfig.rsc\r\ + \n:global TerminalColorOutput;\r\ + \n:global CommentsInScripts;\r\ + \n:global DetectIpAddrDefault;\r\ + \n:global DetectIpAddrFallback;\r\ + \n:global updateChannel;\r\ + \n:global Domain;\r\ + \n:global EmailGeneralTo;\r\ + \n:global BackupAndUpdateMode;\r\ + \n\r\ + \n# Single functions, without nested functions.\r\ + \n:global MiniDateTimeStamp;\r\ + \n:global MicroDateTimeStamp;\r\ + \n\r\ + \n# Include globals \$PrintDebug and \$PrintDebugOverride\r\ + \n:global LogPrint;\r\ + \n\r\ + \n# DeviceInfo and Updates\r\ + \n:global IsCheckUpdates;\r\ + \n:global WaitCheckUpdates;\r\ + \n:global GetPubIp;\r\ + \n\r\ + \n:global GetArrDeviceInfo;\r\ + \n:global WaitArrDeviceInfo;\r\ + \n\r\ + \n:global devModel;\r\ + \n:global devRunningMode;\r\ + \n\r\ + \n:global devRbRevision;\r\ + \n:global devRbSerialNumber;\r\ + \n:global devRbVerAvail;\r\ + \n\r\ + \n:global runningChannelVersion;\r\ + \n:global runningChannel;\r\ + \n:global runningVersion;\r\ + \n\r\ + \n:global devBuildTime;\r\ + \n:global ROsVerAvail;\r\ + \n:global PkgUpdStatus;\r\ + \n:global currentUpdateChannel;\r\ + \n\r\ + \n:global ScriptInstallUpdate;\r\ + \n\r\ + \n:global DeviceInfo;\r\ + \n\r\ + \n:global OldMode;\r\ + \n\r\ + \n:global ParseKeyValueStore;\r\ + \n:global RandomDelay;\r\ + \n:global GetRandomNumber;\r\ + \n:global EscapeForRegEx;\r\ + \n:global CharacterMultiply;\r\ + \n:global CharacterReplace;\r\ + \n:global CleanFilePath;\r\ + \n:global CleanName;\r\ + \n\r\ + \n:global CommitBrief;\r\ + \n\r\ + \n:global EitherOr;\r\ + \n:global FormatLine;\r\ + \n:global GetRandom20CharAlNum;\r\ + \n:global GetRandom20CharHex;\r\ + \n:global GetRandomNumber;\r\ + \n:global Grep;\r\ + \n:global IfThenElse;\r\ + \n:global MAX;\r\ + \n:global MIN;\r\ + \n:global HumanReadableNum;\r\ + \n\r\ + \n:global IsFullyConnected;\r\ + \n:global IsDNSResolving;\r\ + \n:global IsTimeSync;\r\ + \n:global IsTimeSyncCached;\r\ + \n:global IsTimeSyncResetNtp;\r\ + \n\r\ + \n:global MkDir;\r\ + \n:global RmDir;\r\ + \n:global RmFile;\r\ + \n:global FileGet;\r\ + \n:global Unix2Dos;\r\ + \n:global UrlEncode;\r\ + \n:global WaitForFile;\r\ + \n:global ScriptLock;\r\ + \n:global SendTelegram;\r\ + \n:global NotificationFunctions;\r\ + \n:global SendNotification;\r\ + \n:global SendNotification2;\r\ + \n:global SymbolForNotification;\r\ + \n:global SymbolByUnicodeName;\r\ + \n:global ValidateSyntax;\r\ + \n:global CheckSumsVerify;\r\ + \n:global GlobalConfChanges;\r\ + \n:global GlobalConfMigration;\r\ + \n:global VersionToNum;\r\ + \n:global RequiredRouterOS;\r\ + \n:global ExitError;\r\ + \n:global ExitOnError;\r\ + \n\r\ + \n:global FetchHugeFiles;\r\ + \n:global FetchUserAgentStr;\r\ + \n:global FileExists;\r\ + \n:global CertificateAvailable;\r\ + \n:global CertificateDownload;\r\ + \n:global CertificateNameByCN;\r\ + \n:global CharacterMultiply;\r\ + \n\r\ + \n:global InspectVar;\r\ + \n:global InspectVarRet;\r\ + \n:global ParseCustom;\r\ + \n\r\ + \n:set IsCheckUpdates do={\r\ + \n :local isInfo [:tobool \$1];\r\ + \n :global updateChannel;\r\ + \n :global PkgUpdStatus;\r\ + \n :global currentUpdateChannel;\r\ + \n :global ROsVerAvail;\r\ + \n :global arrDeviceInfo;\r\ + \n :log info (\$0 . \" \\\$isInfo = \" . \$isInfo); \r\ + \n :local CurrentChannel [ /system/package/update/get channel ];\r\ + \n :if ([ :len \$updateChannel ] > 0 && \$CurrentChannel != \$updateChann\ + el) do={\r\ + \n :retry command={/system/package/update set channel=\$updateChannel} \ + \_delay=300ms max=5 on-error={};\r\ + \n :log info (\$0 . \" Set updates channel to: \$updateChannel\");\r\ + \n }\r\ + \n :local Update [ /system/package/update/get ];\r\ + \n :local Channel [ :tostr (\$Update->\"channel\") ];\r\ + \n :set currentUpdateChannel \$Channel;\r\ + \n :log info (\$0 . \" Update Channel: \" . \$Channel . \".\");\r\ + \n :local StatusPkg \"undefined\";\r\ + \n :log info (\$0 . \" Checking for updates...\");\r\ + \n :retry command={:set \$StatusPkg ([/system/package/update/check-for-up\ + dates without-paging as-value]->\"status\")} \\\r\ + \n delay=300ms max=5 on-error={};\r\ + \n :log info (\$0 . \" \\\$StatusPkg = \" . \$StatusPkg);\r\ + \n :if (\$StatusPkg = \"New version is available\") do={ :set PkgUpdStatu\ + s true; } else={ :set PkgUpdStatus false; }\r\ + \n :set ROsVerAvail [ :tostr [ /system/package/update/get latest-version \ + ] ];\r\ + \n :log info (\$0 . \" \\\$PkgUpdStatus = \" . \$PkgUpdStatus);\r\ + \n :if (\$isInfo != false || \$PkgUpdStatus = true) do={\r\ + \n :log info (\$0 . \" \\\$PkgUpdStatus = \" . \$PkgUpdStatus);\r\ + \n :if ([ :typeof \$arrDeviceInfo ] != \"array\") do={\r\ + \n :set arrDeviceInfo [:toarray \$arrDeviceInfo];\r\ + \n }\r\ + \n :retry command={:set (\$arrDeviceInfo->\"devUpdates\") [ /system/pac\ + kage/update/get ]} delay=500ms max=5 on-error={:log error (\"Error\")};\r\ + \n :return true;\r\ + \n } else={\r\ + \n :return \$PkgUpdStatus;\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set WaitCheckUpdates do={\r\ + \n :global IsCheckUpdates;\r\ + \n :global PkgUpdStatus;\r\ + \n :global currentUpdateChannel;\r\ + \n :global ROsVerAvail;\r\ + \n :global arrDeviceInfo;\r\ + \n :local Status \"\";\r\ + \n :local Update;\r\ + \n :local WaitCount 0;\r\ + \n :if ([ \$IsCheckUpdates true ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n :set Status [ :tostr [ /system/package/update/get status ] ];\r\ + \n :while ((\$Status = \"\" || \$Status = \"finding out latest version...\ + \") && \$WaitCount < 60) do={\r\ + \n :delay 1s;\r\ + \n :set WaitCount (\$WaitCount + 1);\r\ + \n :set Status [ :tostr [ /system/package/update/get status ] ];\r\ + \n }\r\ + \n :set Update [ /system/package/update/get ];\r\ + \n :set currentUpdateChannel [ :tostr (\$Update->\"channel\") ];\r\ + \n :set ROsVerAvail [ :tostr (\$Update->\"latest-version\") ];\r\ + \n :if ((\$Update->\"status\") = \"New version is available\") do={\r\ + \n :set PkgUpdStatus true;\r\ + \n } else={\r\ + \n :set PkgUpdStatus false;\r\ + \n }\r\ + \n :if ([ :typeof \$arrDeviceInfo ] != \"array\") do={\r\ + \n :set arrDeviceInfo [:toarray \$arrDeviceInfo];\r\ + \n }\r\ + \n :set (\$arrDeviceInfo->\"devUpdates\") \$Update;\r\ + \n :if (\$Status = \"\" || \$Status = \"finding out latest version...\") \ + do={\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set WaitArrDeviceInfo do={\r\ + \n :global GetArrDeviceInfo;\r\ + \n :while ([ \$GetArrDeviceInfo ] = false) do={\r\ + \n :delay 1s;\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set GetArrDeviceInfo do={\r\ + \n :global LogPrint;\r\ + \n :global arrDeviceInfo;\r\ + \n :global devBoardName;\r\ + \n :global devModel;\r\ + \n :global devRunning;\r\ + \n :global devCurArch;\r\ + \n :global devRbRevision;\r\ + \n :global devRbSerialNumber;\r\ + \n :global devBuildTime;\r\ + \n :global runningChannelVersion;\r\ + \n :global runningChannel;\r\ + \n :global runningVersion;\r\ + \n :global currentUpdateChannel;\r\ + \n :global ROsVerAvail;\r\ + \n\r\ + \n :local DeviceInfo;\r\ + \n :local Resource [/system/resource/get];\r\ + \n :local Update [ /system/package/update/get ];\r\ + \n :local VersionString [ :tostr (\$Resource->\"version\") ];\r\ + \n :local ChannelStart [ :find \$VersionString \" (\" ];\r\ + \n :local RouterBoard;\r\ + \n :local IsRouterBoard false;\r\ + \n :local RunningMode \"\";\r\ + \n\r\ + \n :set devCurArch [ :tostr (\$Resource->\"architecture-name\") ];\r\ + \n :set devBuildTime [ :tostr (\$Resource->\"build-time\") ];\r\ + \n :set runningChannelVersion \$VersionString;\r\ + \n :set runningVersion \$VersionString;\r\ + \n :set runningChannel \"\";\r\ + \n\r\ + \n :if ([ :typeof \$ChannelStart ] = \"num\" && \$ChannelStart >= 0) do=\ + {\r\ + \n :set runningVersion [ :pick \$VersionString 0 \$ChannelStart ];\r\ + \n :local ChannelRest [ :pick \$VersionString (\$ChannelStart + 2) [ :\ + len \$VersionString ] ];\r\ + \n :local ChannelEnd [ :find \$ChannelRest \")\" ];\r\ + \n :if ([ :typeof \$ChannelEnd ] = \"num\" && \$ChannelEnd >= 0) do={\ + \r\ + \n :set runningChannel [ :pick \$ChannelRest 0 \$ChannelEnd ];\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :if ([:pick \$devBoardName 0 3] != \"CHR\" or [:pick \$devBoardName 0\ + \_3] != \"x86\") do={\r\ + \n :do {\r\ + \n :set RouterBoard [[ :parse \"/system/routerboard/get\" ]];\r\ + \n# :set RouterBoard [ /system/routerboard/get ];\r\ + \n :if ((\$RouterBoard->\"routerboard\") = true) do={\r\ + \n :set IsRouterBoard true;\r\ + \n }\r\ + \n } on-error={}\r\ + \n }\r\ + \n\r\ + \n :do {\r\ + \n :set RunningMode [ /system/device-mode/get mode ];\r\ + \n } on-error={\r\ + \n :do {\r\ + \n :set RunningMode [ /system/device-mode/get running ];\r\ + \n } on-error={\r\ + \n :set RunningMode \"\";\r\ + \n }\r\ + \n }\r\ + \n :set devRunning \$RunningMode;\r\ + \n\r\ + \n :if (\$IsRouterBoard = true) do={\r\ + \n :set devModel [ :tostr (\$RouterBoard->\"model\") ];\r\ + \n :set devRbRevision [ :tostr (\$RouterBoard->\"revision\") ];\r\ + \n :set devRbSerialNumber [ :tostr (\$RouterBoard->\"serial-number\") \ + ];\r\ + \n } else={\r\ + \n :set devModel \$devBoardName;\r\ + \n :set devRbRevision \"\";\r\ + \n :set devRbSerialNumber \"\";\r\ + \n }\r\ + \n\r\ + \n :set currentUpdateChannel [ :tostr (\$Update->\"channel\") ];\r\ + \n :set ROsVerAvail [ :tostr (\$Update->\"latest-version\") ];\r\ + \n\r\ + \n :set (\$DeviceInfo->\"board-name\") \$devBoardName;\r\ + \n :set (\$DeviceInfo->\"architecture-name\") \$devCurArch;\r\ + \n :set (\$DeviceInfo->\"model\") \$devModel;\r\ + \n :set (\$DeviceInfo->\"revision\") \$devRbRevision;\r\ + \n :set (\$DeviceInfo->\"serial-number\") \$devRbSerialNumber;\r\ + \n :set (\$DeviceInfo->\"running-mode\") \$devRunning;\r\ + \n :set (\$DeviceInfo->\"build-time\") \$devBuildTime;\r\ + \n :set (\$DeviceInfo->\"running-channel-version\") \$runningChannelVers\ + ion;\r\ + \n :set (\$DeviceInfo->\"running-version\") \$runningVersion;\r\ + \n :set (\$DeviceInfo->\"running-channel\") \$runningChannel;\r\ + \n :set (\$DeviceInfo->\"routerboard\") \$IsRouterBoard;\r\ + \n :set (\$DeviceInfo->\"current-firmware\") [ :tostr (\$RouterBoard->\"\ + current-firmware\") ];\r\ + \n :set (\$DeviceInfo->\"upgrade-firmware\") [ :tostr (\$RouterBoard->\"\ + upgrade-firmware\") ];\r\ + \n :set (\$DeviceInfo->\"package-channel\") \$currentUpdateChannel;\r\ + \n :set (\$DeviceInfo->\"installed-version\") [ :tostr (\$Update->\"inst\ + alled-version\") ];\r\ + \n :set (\$DeviceInfo->\"latest-version\") \$ROsVerAvail;\r\ + \n :set (\$DeviceInfo->\"update-status\") [ :tostr (\$Update->\"status\"\ + ) ];\r\ + \n\r\ + \n :set arrDeviceInfo \$DeviceInfo;\r\ + \n :return \$DeviceInfo;\r\ + \n}\r\ + \n\r\ + \n:set DeviceInfo do={\r\ + \n :global GetArrDeviceInfo;\r\ + \n :global GlobalConfVersion;\r\ + \n :global Identity;\r\ + \n :global IfThenElse;\r\ + \n :global FormatLine;\r\ + \n :global CommitBrief;\r\ + \n\r\ + \n :local License [/system/license/get];\r\ + \n :local Snmp [ /snmp/get ];\r\ + \n :local Info [ \$GetArrDeviceInfo ];\r\ + \n\r\ + \n :if ([ :typeof \$Info ] != \"array\") do={\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :return ( \\\r\ + \n [ \$FormatLine \"Hostname\" \$Identity ] . \"\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$Snmp->\"location\") ] > 0) \\\r\ + \n ([ \$FormatLine \"Location\" (\$Snmp->\"location\") ] . \"\\n\") ]\ + \_. \\\r\ + \n [ \$IfThenElse ([ :len (\$Snmp->\"contact\") ] > 0) \\\r\ + \n ([ \$FormatLine \"Contact\" (\$Snmp->\"contact\") ] . \"\\n\") ] .\ + \_\\\r\ + \n \"Hardware:\\n\" . \\\r\ + \n [ \$FormatLine \" Board\" (\$Info->\"board-name\") ] . \"\\n\" . \ + \\\r\ + \n [ \$FormatLine \" Arch\" (\$Info->\"architecture-name\") ] . \"\\\ + n\" . \\\r\ + \n [ \$FormatLine \" Model\" (\$Info->\"model\") ] . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"revision\") ] > 0) \\\r\ + \n (\" \" . (\$Info->\"revision\")) ] . \"\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"serial-number\") ] > 0) \\\r\ + \n ([ \$FormatLine \" Serial\" (\$Info->\"serial-number\") ] . \"\ + \\n\") ] . \\\r\ + \n [ \$IfThenElse ([ :len (\$License->\"nlevel\") ] > 0) \\\r\ + \n ([ \$FormatLine \" License\" (\"level \" . (\$License->\"nlevel\ + \")) ] . \"\\n\") ] . \\\r\ + \n \"RouterOS:\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$License->\"level\") ] > 0) \\\r\ + \n ([ \$FormatLine \" License\" (\"level \" . (\$License->\"level\ + \")) ] . \"\\n\") ] . \\\r\ + \n [ \$FormatLine \" Channel\" (\$Info->\"package-channel\") ] . \"\ + \\n\" . \\\r\ + \n [ \$FormatLine \" Installed\" (\$Info->\"installed-version\") ] .\ + \_\"\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"running-channel\") ] > 0) \\\r\ + \n ([ \$FormatLine \" Running\" ((\$Info->\"running-version\") . \ + \" (\" . (\$Info->\"running-channel\") . \")\") ] . \"\\n\") \\\r\ + \n ([ \$FormatLine \" Running\" (\$Info->\"running-version\") ] . \ + \"\\n\") ] . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"latest-version\") ] > 0 && \\\r\ + \n (\$Info->\"installed-version\") != (\$Info->\"latest-version\"))\ + \_\\\r\ + \n ([ \$FormatLine \" Available\" (\$Info->\"latest-version\") ] .\ + \_\"\\n\") ] . \\\r\ + \n [ \$IfThenElse ((\$Info->\"routerboard\") = true && \\\r\ + \n (\$Info->\"current-firmware\") != (\$Info->\"upgrade-firmware\")\ + ) \\\r\ + \n ([ \$FormatLine \" Firmware\" ((\$Info->\"current-firmware\") .\ + \_\" -> \" . (\$Info->\"upgrade-firmware\")) ] . \"\\n\") ] . \\\r\ + \n \"RouterOS-Scripts:\\n\" . \\\r\ + \n [ \$FormatLine \" Commit\" [ \$CommitBrief ] ] . \"\\n\" . \\\r\ + \n [ \$FormatLine \" Version\" \$GlobalConfVersion ]);\r\ + \n}\r\ + \n\r\ + \n:if ([ :typeof \$NotificationFunctions ] != \"array\") do={:set Notifica\ + tionFunctions ({})}\r\ + \n\r\ + \n:set GetRandom20CharAlNum do={\r\ + \n :global EitherOr;\r\ + \n :return [ :rndstr length=[ \$EitherOr [ :tonum \$1 ] 20 ] from=\"ABCDE\ + FGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\" ];\r\ + \n}\r\ + \n\r\ + \n:set GetRandom20CharHex do={:global EitherOr; :return [ :rndstr length=[\ + \_\$EitherOr [ :tonum \$1 ] 20 ] from=\"0123456789abcdef\" ]; }\r\ + \n:set GetRandomNumber do={:global EitherOr; :return [ :rndnum from=0 to=[\ + \_\$EitherOr [ :tonum \$1 ] 4294967295 ] ]; }\r\ + \n\r\ + \n:global checkRunJob;\r\ + \n:set checkRunJob do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :global ScriptLock; \r\ + \n :if ([ \$ScriptLock \$Script ] = false) do={:return false}\r\ + \n :foreach Job in=[ /system/script/job/find where script=\$Script ] do={\ + :set Job [ /system/script/job/get \$Job ];\r\ + \n :while ([ :typeof (\$Job->\"parent\") ] = \"id\") do={:set Job [ /sy\ + stem/script/job/get [ find where .id=(\$Job->\"parent\") ] ]}\r\ + \n :if ((\$Job->\"type\") = \"login\") do={:return true};}; :return fa\ + lse; \r\ + \n}\r\ + \n\r\ + \n:set InspectVar do={\r\ + \n :global InspectVarRet;\r\ + \n :put [ :tocrlf [ \$InspectVarRet \$1 ] ];\r\ + \n}\r\ + \n\r\ + \n:set CommitBrief do={\r\ + \n :global CommitId;\r\ + \n :global CommitInfo;\r\ + \n :if (\$CommitId = \"unknown\") do={\r\ + \n :return \"unknown\";\r\ + \n }\r\ + \n :return (\$CommitInfo . \"/\" . [ :pick \$CommitId 0 8 ]);\r\ + \n}\r\ + \n\r\ + \n:set InspectVarRet do={\r\ + \n :local Input \$1;\r\ + \n :local Level (0 + [ :tonum \$2 ]);\r\ + \n :global CharacterReplace;\r\ + \n :global IfThenElse;\r\ + \n :global InspectVarRet;\r\ + \n :local IndentReturn do={\r\ + \n :local Prefix [ :tostr \$1 ];\r\ + \n :local Value [ :tostr \$2 ];\r\ + \n :local Level [ :tonum \$3 ];\r\ + \n :global CharacterMultiply;\r\ + \n :return ([ \$CharacterMultiply \" \" (\$Level * 2) ] . \"-\" . \$Pre\ + fix . \"-> \" . \$Value);\r\ + \n }\r\ + \n :local TypeOf [ :typeof \$Input ];\r\ + \n :local Len [ :len \$Input ];\r\ + \n :local Return [ \$IndentReturn \"type\" \$TypeOf \$Level ];\r\ + \n :if (\$TypeOf = \"array\") do={\r\ + \n :foreach Key,Value in=\$Input do={\r\ + \n :set \$Return (\$Return . \"\\n\" . \\\r\ + \n [ \$IndentReturn \"key\" \$Key (\$Level + 1) ] . \"\\n\" . \\\r\ + \n [ \$InspectVarRet \$Value (\$Level + 2) ]);\r\ + \n }\r\ + \n } else={\r\ + \n :if (\$TypeOf = \"str\") do={\r\ + \n :set \$Return (\$Return . \"\\n\" . \\\r\ + \n [ \$IndentReturn \"len\" \$Len \$Level ]);\r\ + \n :if ([ :typeof [ :find \$Input (\"\\r\") ] ] = \"num\") do={\r\ + \n :set Input [ \$CharacterReplace \$Input (\"\\r\") \"\" ];\r\ + \n }\r\ + \n :if ([ :typeof [ :find \$Input (\"\\n\") ] ] = \"num\") do={\r\ + \n :set Input [ \$CharacterReplace \$Input (\"\\n\") \" \" ];\r\ + \n }\r\ + \n }\r\ + \n :if (\$TypeOf != \"nothing\") do={\r\ + \n :set \$Return (\$Return . \"\\n\" . \\\r\ + \n [ \$IndentReturn \"value\" [ \$IfThenElse ([ :len \$Input ] > 80)\ + \_\\\r\ + \n ([ :pick \$Input 0 77 ] . \"...\") \$Input ] \$Level ]);\r\ + \n }\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set GetPubIp do={\r\ + \n :global DetectIpAddrDefault;\r\ + \n :global DetectIpAddrFallback;\r\ + \n\r\ + \n :local PubIpAddr false;\r\ + \n :local Url \$DetectIpAddrDefault;\r\ + \n :local JobName [ :jobname ];\r\ + \n\r\ + \n :do {\r\ + \n :log debug (\$0 . \" Trying to detect public IP using default serv\ + ice: \" . \$Url . \"...\");\r\ + \n :retry command={:set PubIpAddr ([ /tool/fetch http-method=\"get\"\ + \_check-certificate=yes-without-crl \$Url output=user as-value ]->\"data\"\ + )} \\\r\ + \n delay=50ms max=5 on-error={:log error (\$JobName . \" Failed to \ + detect public IP using default service: \" . \$Url . \".\")}\r\ + \n } on-error={\r\ + \n :log error (\$0 . \" Failed to detect public IP using default serv\ + ice: \" . \$Url . \".\");\r\ + \n :set Url \$DetectIpAddrFallBack;\r\ + \n :log debug (\$0 . \" Trying to detect public IP using fallback ser\ + vice: \" . \$Url . \"...\");\r\ + \n :retry command={:set PubIpAddr ([ /tool/fetch http-method=\"get\"\ + \_check-certificate=yes-without-crl \$Url output=user as-value ]->\"data\"\ + )} \\\r\ + \n delay=200ms max=5 on-error={:log error (\$JobName . \" Failed to\ + \_detect public IP using fallback service: \" . \$Url . \".\")}\r\ + \n };\r\ + \n :local IpLen ([ :len \$PubIpAddr ] -1);\r\ + \n :if (\$PubIpAddr = false) do={:set PubIpAddr \"not-detected\"} else=\ + {:set PubIpAddr [:pick \$PubIpAddr 0 \$IpLen]}\r\ + \n :return \$PubIpAddr;\r\ + \n}\r\ + \n\r\ + \n:set FormatLine do={\r\ + \n :local Key [ :tostr \$1 ];\r\ + \n :local Value [ :tostr \$2 ];\r\ + \n :local Indent [ :tonum \$3 ];\r\ + \n :local Spaces;\r\ + \n :local Return \"\";\r\ + \n :global CharacterMultiply;\r\ + \n :global EitherOr;\r\ + \n :set Indent [ \$EitherOr \$Indent 16 ];\r\ + \n :local Spaces [ \$CharacterMultiply \" \" \$Indent ];\r\ + \n :if ([ :len \$Key ] > 0) do={ :set Return (\$Key . \":\")}\r\ + \n :if ([ :len \$Key ] > (\$Indent - 2)) do={:set Return (\$Return . \"\\\ + n\" . [ :pick \$Spaces 0 \$Indent ] . \$Value);\r\ + \n } else={:set Return (\$Return . [ :pick \$Spaces 0 (\$Indent - [ :len \ + \$Return ]) ] . \$Value)}\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set ParseCustom do={\r\ + \n :local Str [ :tostr \$1 ];\r\ + \n :local Start [:find [ \$Str ] [:tostr \$2]];\r\ + \n :local End [ :tostr \$3 ];\r\ + \n :local Num [ :tonum \$4 ];\r\ + \n :local Return;\r\ + \n :if ([:len \$Num] = 0) do={:set Num \"0\"}\r\ + \n :local rest [:pick \$Str (\$Start+\$Num) [:len \$Str]]; \r\ + \n :if ([:len \$End ] = 0) do={:set Return [:pick \$Str \$Num \$Start]} e\ + lse={\r\ + \n :set End [:find \$rest \$End];\r\ + \n :set Return [:pick \$rest 0 \$End];\r\ + \n }\r\ + \n:return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set LogPrint do={\r\ + \n :local Severity [ :tostr \$1 ];\r\ + \n :local Name [ :tostr \$2 ];\r\ + \n :local Message [ :tostr \$3 ];\r\ + \n :global PrintDebug;\r\ + \n :global PrintDebugOverride;\r\ + \n :global EitherOr;\r\ + \n :local Debug [ \$EitherOr (\$PrintDebugOverride->\$Name) \$PrintDebug \ + ];\r\ + \n :local PrintSeverity do={\r\ + \n :global TerminalColorOutput;\r\ + \n :if (\$TerminalColorOutput != true) do={:return \$1}\r\ + \n :local Color { debug=96; info=97; warning=93; error=91 };\r\ + \n :return (\"\\1B[\" . \$Color->\$1 . \"m\" . \$1 . \"\\1B[0m\");\r\ + \n}\r\ + \n :local Log ([ \$EitherOr \$Name \"\" ] . \": \" . \$Message);\ + \r\ + \n :if (\$Severity ~ (\"^(custom|debug|error|info)\\\$\")) do={\r\ + \n :if (\$Severity = \"debug\") do={:log debug \$Log}\r\ + \n :if (\$Severity = \"error\") do={:log error \$Log}\r\ + \n :if (\$Severity = \"info\" ) do={:log info \$Log}\r\ + \n } else={:log warning \$Log; :set Severity \"warning\"}\r\ + \n :if (\$Severity != \"debug\" || \$Debug = true) do={:put ([ \$PrintSev\ + erity \$Severity ] . \": \" . \$Message)}\r\ + \n}\r\ + \n\r\ + \n:set CertificateAvailable do={\r\ + \n :local CommonName [ :tostr \$1 ];\r\ + \n :local UseFor [ :tostr \$2 ];\r\ + \n\r\ + \n :global CertificateDownload;\r\ + \n :global ParseKeyValueStore;\r\ + \n\r\ + \n :if ([ :len \$UseFor ] = 0) do={\r\ + \n :log warning (\$0 . \"The intended use is undefined!\");\r\ + \n :set UseFor \"undefined\";\r\ + \n }\r\ + \n :if ([ /system/resource/get free-hdd-space ] < 8388608 && \\\r\ + \n [ /certificate/settings/get crl-download ] = true && \\\r\ + \n [ /certificate/settings/get crl-store ] = \"system\") do={\r\ + \n :log warning (\$0 . \"This system has low free flash space but \" . \ + \\\r\ + \n \"is configured to download certificate CRLs to system!\");\r\ + \n }\r\ + \n :if ([ :len \$CommonName ] = 0) do={\r\ + \n :log warning (\$0 . \"No CommonName given!\");\r\ + \n :return false;\r\ + \n }\r\ + \n :local CertSettings [ /certificate/settings/get ];\r\ + \n :if (((\$CertSettings->\"builtin-trust-anchors\") = \"trusted\" || \\\ + \r\ + \n (\$CertSettings->\"builtin-trust-store\") ~ \$UseFor || \\\r\ + \n (\$CertSettings->\"builtin-trust-store\") = \"all\") && \\\r\ + \n [ :len [ /certificate/builtin/find where common-name=\$CommonName\ + \_] ] > 0) do={\r\ + \n :return true;\r\ + \n }\r\ + \n :if ([ :len [ /certificate/find where common-name=\$CommonName ] ] = 0\ + ) do={\r\ + \n :log info (\$0 . \"Certificate with CommonName '\" . \$CommonName . \ + \"' not available.\");\r\ + \n :if ([ \$CertificateDownload \$CommonName ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n :if ([ :len [ /certificate/find where common-name=\$CommonName ] ] > 1\ + ) do={\r\ + \n :log info (\$0 . \"There are \" . \$CertCount . \" Certificates with\ + \_CommonName '\" . \$CommonName . \"'. Should be ok.\");\r\ + \n :return true;\r\ + \n }\r\ + \n :local CertVal [ /certificate/get [ find where common-name=\$CommonNam\ + e ] ];\r\ + \n :while ((\$CertVal->\"akid\") != \"\" && (\$CertVal->\"akid\") != (\$C\ + ertVal->\"skid\")) do={\r\ + \n :if ([ :len [ /certificate/find where skid=(\$CertVal->\"akid\") ] ]\ + \_= 0) do={\r\ + \n :local IssuerCN ([ \$ParseKeyValueStore (\$CertVal->\"issuer\") ]-\ + >\"CN\");\r\ + \n :log info (\$0 . \"Certificate chain for '\" . \$CommonName . \"' \ + is incomplete, missing '\" . \$IssuerCN . \"'.\");\r\ + \n :if ([ \$CertificateDownload \$IssuerCN ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n :set CertVal [ /certificate/get [ find where skid=(\$CertVal->\"akid\ + \") ] ];\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set CertificateDownload do={\r\ + \n :local CommonName [ :tostr \$1 ];\r\ + \n :global ScriptUpdatesBaseUrl;\r\ + \n :global ScriptUpdatesUrlSuffix;\r\ + \n :global CertificateNameByCN;\r\ + \n :global CleanName;\r\ + \n :global FetchUserAgentStr;\r\ + \n :global RmFile;\r\ + \n :global WaitForFile;\r\ + \n\r\ + \n :log info (\$0 . \"Downloading and importing certificate with \" . \\\ + \r\ + \n \"CommonName '\" . \$CommonName . \"'.\");\r\ + \n :local FileName ([ \$CleanName \$CommonName ] . \".pem\");\r\ + \n :do {\r\ + \n /tool/fetch check-certificate=yes-without-crl mode=https http-header\ + -field=({ [ \$FetchUserAgentStr \$0 ] }) \\\r\ + \n (\$ScriptUpdatesBaseUrl . \"certs/\" . \$FileName . \$ScriptUpdate\ + sUrlSuffix) \\\r\ + \n dst-path=\$FileName as-value;\r\ + \n \$WaitForFile \$FileName;\r\ + \n } on-error={\r\ + \n :log warning (\$0 . \" Failed downloading certificate with CommonNam\ + e '\" . \$CommonName . \\\r\ + \n \"' from repository! Trying fallback to mkcert.org...\");\r\ + \n :do {\r\ + \n :local CertSettings [ /certificate/settings/get ];\r\ + \n :if ([ :len [ /certificate/find where common-name=\"ISRG Root X1\"\ + \_] ] = 0 && \\\r\ + \n !(((\$CertSettings->\"builtin-trust-anchors\") = \"trusted\" \ + || \\\r\ + \n (\$CertSettings->\"builtin-trust-store\") ~ \"fetch\" || \ + \\\r\ + \n (\$CertSettings->\"builtin-trust-store\") = \"all\") && \\\ + \r\ + \n [ :len [ /certificate/builtin/find where common-name=\"ISRG\ + \_Root X1\" ] ] > 0)) do={\r\ + \n :log error (\$0 . \" Required certificate is not available.\");\ + \r\ + \n :return false;\r\ + \n }\r\ + \n /tool/fetch check-certificate=yes-without-crl mode=https http-head\ + er-field=({ [ \$FetchUserAgentStr \$0 ] }) \\\r\ + \n url=\"https://mkcert.org/generate/\" http-data=[ :serialize to=jso\ + n ({ \$CommonName }) ] \\\r\ + \n dst-path=\$FileName as-value;\r\ + \n \$WaitForFile \$FileName;\r\ + \n :if ([ /file/get \$FileName size ] = 0) do={\r\ + \n \$RmFile \$FileName;\r\ + \n :error false;\r\ + \n }\r\ + \n } on-error={\r\ + \n :log error (\$0 . \" Failed downloading certificate with CommonNam\ + e '\" . \$CommonName . \"'!\");\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n /certificate/import file-name=\$FileName passphrase=\"\" as-value;\r\ + \n :delay 1s;\r\ + \n \$RmFile \$FileName;\r\ + \n :if ([ :len [ /certificate/find where common-name=\$CommonName ] ] = 0\ + ) do={\r\ + \n /certificate/remove [ find where name~(\"^\" . \$FileName . \"_[0-9]\ + +\\\$\") ];\r\ + \n :log warning (\$0 . \" Certificate with CommonName '\" . \$CommonNam\ + e . \"' still unavailable!\");\r\ + \n :return false;\r\ + \n }\r\ + \n :foreach Cert in=[ /certificate/find where name~(\"^\" . \$FileName . \ + \"_[0-9]+\\\$\") ] do={\r\ + \n \$CertificateNameByCN [ /certificate/get \$Cert common-name ];\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set CertificateNameByCN do={\r\ + \n :local Match [ :tostr \$1 ];\r\ + \n :global CleanName;\r\ + \n :local Cert ([ /certificate/find where (common-name=\$Match or fingerp\ + rint=\$Match or name=\$Match) ]->0);\r\ + \n :if ([ :len \$Cert ] = 0) do={\r\ + \n :log warning (\$0 .\" No matching certificate found.\");\r\ + \n :return false;\r\ + \n }\r\ + \n :local CommonName [ /certificate/get \$Cert common-name ];\r\ + \n /certificate/set \$Cert name=[ \$CleanName \$CommonName ];\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set CharacterMultiply do={\r\ + \n :local Str [ :tostr \$1 ];\r\ + \n :local Num [ :tonum \$2 ];\r\ + \n :if (\$Num = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :for I from=1 to=\$Num do={:set Return (\$Return . \$Str)}\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set FileExists do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :local Type [ :tostr \$2 ];\r\ + \n :global FileGet;\r\ + \n :local FileVal [ \$FileGet \$FileName ];\r\ + \n :if (\$FileVal = false) do={ :return false; }\r\ + \n :if ([ :len (\$FileVal->\"size\") ] = 0) do={ :return false; }\r\ + \n :if ([ :len \$Type ] = 0 || \$FileVal->\"type\" = \$Type) do={ :return\ + \_true; }\r\ + \n :return false;\r\ + \n}\r\ + \n\r\ + \n:set Grep do={\r\ + \n :local Input ([ :tostr \$1 ] . \"\\n\");\r\ + \n :local Pattern [ :tostr \$2 ];\r\ + \n :if ([ :typeof [ :find \$Input \$Pattern ] ] = \"nil\") do={:return []\ + }\r\ + \n :do {\r\ + \n :local Line [ :pick \$Input 0 [ :find \$Input \"\\n\" ] ];\r\ + \n :if ([ :typeof [ :find \$Line \$Pattern ] ] = \"num\") do={:return \ + \$Line}\r\ + \n :set Input [ :pick \$Input ([ :find \$Input \"\\n\" ] + 1) [ :len \$\ + Input ] ];\r\ + \n } while=([ :len \$Input ] > 0);\r\ + \n :return [];\r\ + \n}\r\ + \n\r\ + \n:set HumanReadableNum do={\r\ + \n :local Input [ :tonum \$1 ];\r\ + \n :local Base [ :tonum \$2 ];\r\ + \n :global EitherOr;\r\ + \n :global IfThenElse;\r\ + \n :local Prefix \"kMGTPE\";\r\ + \n :local Pow 1;\r\ + \n :set Base [ \$EitherOr \$Base 1024 ];\r\ + \n :local Bin [ \$IfThenElse (\$Base = 1024) \"i\" \"\" ];\r\ + \n :if (\$Input < \$Base) do={:return \$Input}\r\ + \n :for I from=0 to=[ :len \$Prefix ] do={\r\ + \n :set Pow (\$Pow * \$Base);\r\ + \n :if (\$Input / \$Base < \$Pow) do={\r\ + \n :set Prefix [ :pick \$Prefix \$I ];\r\ + \n :local Tmp1 (\$Input * 100 / \$Pow);\r\ + \n :local Tmp2 (\$Tmp1 / 100);\r\ + \n :if (\$Tmp2 >= 100) do={:return (\$Tmp2 . \$Prefix . \$Bin)}\r\ + \n :return (\$Tmp2 . \".\" . \\\r\ + \n [ :pick \$Tmp1 [ :len \$Tmp2 ] ([ :len \$Tmp1 ] - [ :len \$Tmp\ + 2 ] + 1) ] . \\\r\ + \n \$Prefix . \$Bin);\r\ + \n }\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set CleanFilePath do={\r\ + \n :local Path [ :tostr \$1 ];\r\ + \n :global CharacterReplace;\r\ + \n :while (\$Path ~ \"//\") do={ :set \$Path [ \$CharacterReplace \$Path \ + \"//\" \"/\" ]; }\r\ + \n :if ([ :pick \$Path 0 ] = \"/\") do={ :set Path [ :pick \$Path 1 [ :le\ + n \$Path ] ]; }\r\ + \n :if ([ :pick \$Path ([ :len \$Path ] - 1) ] = \"/\") do={ :set Path [ \ + :pick \$Path 0 ([ :len \$Path ] - 1) ]; }\r\ + \n :return \$Path;\r\ + \n}\r\ + \n\r\ + \n:set CleanName do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :local Return \"\";\r\ + \n :for I from=0 to=([ :len \$Input ] - 1) do={\r\ + \n :local Char [ :pick \$Input \$I ];\r\ + \n :if ([ :typeof [ find \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqr\ + stuvwxyz0123456789\" \$Char ] ] = \"nil\") do={\r\ + \n :do {:if ([ :len \$Return ] = 0) do={:error true};\r\ + \n :if ([ :pick \$Return ([ :len \$Return ] - 1) ] = \"-\") do={:er\ + ror true}; :set Char \"-\"} on-error={:set Char \"\"}; \r\ + \n };\r\ + \n :set Return (\$Return . \$Char)};\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set RandomDelay do={\r\ + \n :local Time [ :tonum \$1 ];\r\ + \n :local Unit [ :tostr \$2 ];\r\ + \n :global EitherOr;\r\ + \n :global GetRandomNumber;\r\ + \n :global MAX;\r\ + \n :if (\$Time = 0) do={:return false}\r\ + \n :delay ([ \$MAX 10 [ \$GetRandomNumber ([ :tonsec [ :totime (\$Time . \ + [ \$EitherOr \$Unit \"s\" ]) ] ] / 1000000) ] ] . \"ms\");\r\ + \n}\r\ + \n\r\ + \n:set EscapeForRegEx do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :if ([ :len \$Input ] = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :local Chars (\"^.[]\\\$()|*+\?{}\\\\\");\r\ + \n :for I from=0 to=([ :len \$Input ] - 1) do={\r\ + \n :local Char [ :pick \$Input \$I ];\r\ + \n :if ([ :find \$Chars \$Char ]) do={:set Char (\"\\\\\" . \$Char)}\r\ + \n :set Return (\$Return . \$Char);\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set ExitError do={\r\ + \n :local ExitOK [ :tostr \$1 ];\r\ + \n :local Name [ :tostr \$2 ];\r\ + \n :local Error [ :tostr \$3 ];\r\ + \n :global IfThenElse;\r\ + \n :if (\$ExitOK = \"false\") do={\r\ + \n :log error (\$Name [ \$IfThenElse ([ :pick \$Name 0 1 ] = \"\\\$\") \ + \\\r\ + \n \"Function\" \"Script\" ] . \" '\" . \$Name . \"' exited with er\ + ror\" . \\\r\ + \n [ \$IfThenElse (!(\$Error ~ \"^(|true|false)\\\$\")) (\": \" . \ + \$Error) \".\" ]);\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set ExitOnError do={\r\ + \n :local Name [ :tostr \$1 ];\r\ + \n :local Error [ :tostr \$2 ];\r\ + \n :global IfThenElse;\r\ + \n :log error (\$Name . \" \" . [ \$IfThenElse ([ :pick \$Name 0 1 ] = \"\ + \\\$\") \\\r\ + \n \"Function\" \"Script\" ] . \" '\" . \$Name . \"' exited with erro\ + r\" . \\\r\ + \n [ \$IfThenElse (!(\$Error ~ \"^(|true|false)\\\$\")) (\": \" . \$E\ + rror) \".\" ]);\r\ + \n}\r\ + \n\r\ + \n:set MiniDateTimeStamp do={\r\ + \n :local mikro [ :tobool \$1 ];\r\ + \n /system/clock;\r\ + \n :local vdate [get date];\r\ + \n :local vtime [get time];\r\ + \n :local vdoff [:toarray \"0,4,5,7,8,10\"];\r\ + \n :local MM [:pick \$vdate (\$vdoff->2) (\$vdoff->3)];\r\ + \n :local M [:tonum \$MM];\r\ + \n :if (\$vdate ~ \".../../....\") do={\r\ + \n :set vdoff [:toarray \"7,11,1,3,4,6\"];\r\ + \n :set M ([:find \"xxanebarprayunulugepctovecANEBARPRAYUNULUGE\ + PCTOVEC\" [:pick \$vdate (\$vdoff->2) (\$vdoff->3)] -1] / 2);\r\ + \n :if (\$M>12) do={:set M (\$M - 12)}\r\ + \n :set MM [:pick (100 + \$M) 1 3];\r\ + \n }\r\ + \n :local yyyy [:pick \$vdate (\$vdoff->0) (\$vdoff->1)];\r\ + \n :local dd [:pick \$vdate (\$vdoff->4) (\$vdoff->5)];\r\ + \n :local HH [:pick \$vtime 0 2];\r\ + \n :local mm [:pick \$vtime 3 5];\r\ + \n :local ss [:pick \$vtime 6 8];\r\ + \n :local Return;\r\ + \n :if (\$mikro = true) do={\r\ + \n :set Return ([:pick \$yyyy 2 4] . [:pick \$dd 0 1] . [:pick \$HH 0\ + \_1] . [:pick \$mm 3 4] . [:pick \$ss 6 7]);\r\ + \n } else={\r\ + \n :set Return ([\$yyyy] . [\$MM] . [\$dd] . [\$HH] . [\$mm] . [\$ss]);\ + \r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set WaitForFile do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :local WaitTime [ :totime \$2 ];\r\ + \n :global CleanFilePath;\r\ + \n :global EitherOr;\r\ + \n :global MAX;\r\ + \n :set FileName [ \$CleanFilePath \$FileName ];\r\ + \n :local Delay ([ \$MAX [ \$EitherOr \$WaitTime 2s ] 100ms ] / 9);\r\ + \n :do {\r\ + \n :retry {\r\ + \n /file/get \$FileName;\r\ + \n :return true;\r\ + \n } delay=\$Delay max=10;\r\ + \n } on-error={ }\r\ + \n :while ([ :len [ /file/find where name=\$FileName ] ] > 0) do={\r\ + \n :do {\r\ + \n /file/get \$FileName;\r\ + \n :return true;\r\ + \n } on-error={ }\r\ + \n :delay \$Delay;\r\ + \n :set Delay (\$Delay * 3 / 2);\r\ + \n }\r\ + \n :return false;\r\ + \n}\r\ + \n\r\ + \n:set FileGet do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :global WaitForFile;\r\ + \n :if ([ \$WaitForFile \$FileName 0s ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n :local FileVal false;\r\ + \n :do {\r\ + \n :set FileVal [ /file/get \$FileName ];\r\ + \n } on-error={ }\r\ + \n :return \$FileVal;\r\ + \n}\r\ + \n\r\ + \n:set RmDir do={\r\ + \n :local DirName [ :tostr \$1 ];\r\ + \n :global FileGet;\r\ + \n :log debug (\$0 . \" Removing directory: \". \$DirName);\r\ + \n :local DirVal [ \$FileGet \$DirName ];\r\ + \n :if (\$DirVal = false) do={\r\ + \n :log debug (\$0 . \" ... which does not exist.\");\r\ + \n :return true;\r\ + \n };\r\ + \n :if (\$DirVal->\"type\" != \"directory\") do={\r\ + \n :log error (\$0 . \" Directory '\" . \$DirName . \"' is not a direct\ + ory.\");\r\ + \n :return false;\r\ + \n };\r\ + \n :onerror Err {\r\ + \n /file/remove \$DirName;\r\ + \n } do={\r\ + \n :log error (\$0 . \" Removing directory '\" . \$DirName . \"' failed\ + : \" . \$Err);\r\ + \n :return false;\r\ + \n };\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set RmFile do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :global FileGet;\r\ + \n :log debug (\$0 . \" Removing file: \". \$FileName);\r\ + \n :local FileVal [ \$FileGet \$FileName ];\r\ + \n :if (\$FileVal = false) do={\r\ + \n :log debug (\$0 . \" ... which does not exist.\");\r\ + \n :return true;\r\ + \n }\r\ + \n :if (\$FileVal->\"type\" = \"directory\" || \$FileVal->\"type\" = \"di\ + sk\") do={\r\ + \n :log error (\$0 . \" File '\" . \$FileName . \"' is not a file.\");\ + \r\ + \n :return false;\r\ + \n }\r\ + \n :onerror Err {\r\ + \n /file/remove \$FileName;\r\ + \n } do={\r\ + \n :log error (\$0 . \" Removing file '\" . \$FileName . \"' failed: \"\ + \_. \$Err); :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set MkDir do={\r\ + \n :local Path [ :tostr \$1 ];\r\ + \n :global CleanFilePath;\r\ + \n :global FileGet;\r\ + \n :global RmDir;\r\ + \n :global WaitForFile;\r\ + \n\r\ + \n :local MkTmpfs do={\r\ + \n :global WaitForFile;\r\ + \n :local TmpFs [ /disk/find where slot=tmpfs type=tmpfs ];\r\ + \n :if ([ :len \$TmpFs ] = 1) do={\r\ + \n :if ([ /disk/get \$TmpFs disabled ] = true) do={\r\ + \n :log info (\$0 . \" The tmpfs is disabled, enabling.\");\r\ + \n /disk/enable \$TmpFs;\r\ + \n }\r\ + \n :return true;\r\ + \n }\r\ + \n :log info (\$0 . \" Creating disk of type tmpfs.\");\r\ + \n \$RmDir \"tmpfs\";\r\ + \n :onerror Err {\r\ + \n /disk/add slot=tmpfs type=tmpfs tmpfs-max-size=([ /system/resource\ + /get total-memory ] / 3);\r\ + \n \$WaitForFile \"tmpfs\";\r\ + \n } do={\r\ + \n :log warning (\$0 . \" Creating disk of type tmpfs failed: \" . \$\ + Err);\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n }\r\ + \n :set Path [ \$CleanFilePath \$Path ];\r\ + \n :if (\$Path = \"\") do={\r\ + \n :return true;\r\ + \n }\r\ + \n :log debug (\$0 . \" Making directory: \" . \$Path);\r\ + \n :local PathVal [ \$FileGet \$Path ];\r\ + \n :if (\$PathVal->\"type\" = \"directory\") do={\r\ + \n :log debug (\$0 . \" ... which already exists.\");\r\ + \n :return true;\r\ + \n }\r\ + \n :if ([ :pick \$Path 0 5 ] = \"tmpfs\") do={\r\ + \n :if ([ \$MkTmpfs ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n :onerror Err {\r\ + \n /file/add type=\"directory\" name=\$Path;\r\ + \n \$WaitForFile \$Path;\r\ + \n } do={\r\ + \n :log warning (\$0 . \" Making directory '\" . \$Path . \"' failed: \ + \" . \$Err);\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set MAX do={:if (\$1 > \$2) do={:return \$1}\r\ + \n :return \$2;\r\ + \n}\r\ + \n:set MIN do={:if (\$1 < \$2) do={:return \$1}\r\ + \n :return \$2;\r\ + \n}\r\ + \n\r\ + \n:set CharacterMultiply do={\r\ + \n :local Str [ :tostr \$1 ];\r\ + \n :local Num [ :tonum \$2 ];\r\ + \n :if (\$Num = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :for I from=1 to=\$Num do={:set Return (\$Return . \$Str)}\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set CharacterReplace do={\r\ + \n :local String [ :tostr \$1 ];\r\ + \n :local ReplaceFrom [ :tostr \$2 ];\r\ + \n :local ReplaceWith [ :tostr \$3 ];\r\ + \n :local Return \"\";\r\ + \n :if (\$ReplaceFrom = \"\") do={:return \$String}\r\ + \n :while ([ :typeof [ :find \$String \$ReplaceFrom ] ] != \"nil\") do={\ + \r\ + \n :local Pos [ :find \$String \$ReplaceFrom ];\r\ + \n :set Return (\$Return . [ :pick \$String 0 \$Pos ] . \$ReplaceWith);\ + \r\ + \n :set String [ :pick \$String (\$Pos + [ :len \$ReplaceFrom ]) [ :len\ + \_\$String ] ];\r\ + \n }\r\ + \n :return (\$Return . \$String);\r\ + \n}\r\ + \n\r\ + \n:set IfThenElse do={\r\ + \n :if ([ :tostr \$1 ] = \"true\" || [ :tobool \$1 ] = true) do={:return \ + \$2};\r\ + \n :return \$3;\r\ + \n}\r\ + \n\r\ + \n:set EitherOr do={\r\ + \n :global IfThenElse;\r\ + \n :if ([ :typeof \$1 ] = \"num\") do={:return [ \$IfThenElse (\$1 != 0) \ + \$1 \$2 ]}\r\ + \n :if ([ :typeof \$1 ] = \"time\") do={:return [ \$IfThenElse (\$1 > 0s)\ + \_\$1 \$2 ]}\r\ + \n# this works for boolean values, literal ones with parentheses\r\ + \n :return [ \$IfThenElse ([ :len [ :tostr \$1 ] ] > 0) \$1 \$2 ];\r\ + \n}\r\ + \n\r\ + \n:set ParseKeyValueStore do={\r\ + \n :local Source \$1;\r\ + \n :if ([ :pick \$Source 0 1 ] = \"{\") do={\r\ + \n :do {\r\ + \n :return [ :deserialize from=json \$Source ];\r\ + \n } on-error={ }\r\ + \n } \r\ + \n :if ([ :typeof \$Source ] != \"array\") do={:set Source [ :tostr \$1 ]\ + }\r\ + \n :local Result ({});\r\ + \n :foreach KeyValue in=[ :toarray \$Source ] do={\r\ + \n :if ([ :find \$KeyValue \"=\" ]) do={\r\ + \n :local Key [ :pick \$KeyValue 0 [ :find \$KeyValue \"=\" ] ];\r\ + \n :local Value [ :pick \$KeyValue ([ :find \$KeyValue \"=\" ] + 1) [\ + \_:len \$KeyValue ] ];\r\ + \n :if (\$Value=\"true\") do={:set Value true}\r\ + \n :if (\$Value=\"false\") do={:set Value false}\r\ + \n :set (\$Result->\$Key) \$Value;\r\ + \n } else={:set (\$Result->\$KeyValue) true}\r\ + \n }\r\ + \n :return \$Result;\r\ + \n}\r\ + \n:set RequiredRouterOS do={\r\ + \n :local Caller [ :tostr \$1 ];\r\ + \n :local Required [ :tostr \$2 ];\r\ + \n :local Warn [ :tostr \$3 ];\r\ + \n :global IfThenElse;\r\ + \n :global VersionToNum;\r\ + \n :if (!(\$Required ~ \"^\\\\d+\\\\.\\\\d+((alpha|beta|rc|\\\\.)\\\\d+|)\ + \\\$\")) do={\r\ + \n :log error (\$0 . \" No valid RouterOS version: \" . \$Required);\r\ + \n :return false;\r\ + \n }\r\ + \n :if ([ \$VersionToNum \$Required ] > [ \$VersionToNum [ /system/packag\ + e/update/get installed-version ] ]) do={\r\ + \n :if (\$Warn = \"true\") do={\r\ + \n :log warning (\$0 . \" This \" . [ \$IfThenElse ([ :pick \$Caller \ + 0 ] = (\"\\\$\")) \"function\" \"script\" ] . \\\r\ + \n \" '\" . \$Caller . \"' (at least specific functionality) requir\ + es RouterOS \" . \$Required . \". Please update!\");\r\ + \n }\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set ValidateSyntax do={\r\ + \n :local Code [ :tostr \$1 ];\r\ + \n :global LogPrint;\r\ + \n :onerror Err {\r\ + \n [ :parse (\":local Validate do={\\n\" . \$Code . \"\\n}\") ];\r\ + \n } do={\r\ + \n \$LogPrint debug \$0 (\"Valdation failed: \" . \$Err);\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set VersionToNum do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :local Multi 0x1000000;\r\ + \n :local Return 0;\r\ + \n :global CharacterReplace;\r\ + \n :set Input [ \$CharacterReplace \$Input \".\" \",\" ];\r\ + \n :foreach I in={ \"zero\"; \"alpha\"; \"beta\"; \"rc\" } do={\r\ + \n :set Input [ \$CharacterReplace \$Input \$I (\",\" . \$I . \",\") ];\ + \r\ + \n }\r\ + \n :foreach Value in=([ :toarray \$Input ], 0) do={\r\ + \n :local Num [ :tonum \$Value ];\r\ + \n :if (\$Multi = 0x100) do={\r\ + \n :if ([ :typeof \$Num ] = \"num\") do={\r\ + \n :set Return (\$Return + 0xff00);\r\ + \n :set Multi (\$Multi / 0x100);\r\ + \n } else={\r\ + \n :if (\$Value = \"zero\") do={ }\r\ + \n :if (\$Value = \"alpha\") do={ :set Return (\$Return + 0x3f00); \ + }\r\ + \n :if (\$Value = \"beta\") do={ :set Return (\$Return + 0x5f00); }\ + \r\ + \n :if (\$Value = \"rc\") do={ :set Return (\$Return + 0x7f00); }\r\ + \n }\r\ + \n }\r\ + \n :if ([ :typeof \$Num ] = \"num\") do={ :set Return (\$Return + (\$Va\ + lue * \$Multi)); }\r\ + \n :set Multi (\$Multi / 0x100);\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set ScriptLock do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local WaitMax [ :totime \$2 ];\r\ + \n :global GetRandom20CharAlNum;\r\ + \n :global IfThenElse;\r\ + \n :global ScriptLockOrder;\r\ + \n :if ([ :typeof \$ScriptLockOrder ] = \"nothing\") do={\r\ + \n :set ScriptLockOrder ({});\r\ + \n }\r\ + \n :if ([ :typeof (\$ScriptLockOrder->\$Script) ] = \"nothing\") do={\r\ + \n :set (\$ScriptLockOrder->\$Script) ({});\r\ + \n }\r\ + \n :local JobCount do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :return [ :len [ /system/script/job/find where script=\$Script ] ];\ + \r\ + \n }\r\ + \n :local TicketCount do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :local Count 0;\r\ + \n :foreach Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :if ([ :typeof \$Ticket ] != \"nothing\") do={:set Count (\$Count \ + + 1)}\r\ + \n }\r\ + \n :return \$Count;\r\ + \n }\r\ + \n :local IsFirstTicket do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local Check [ :tostr \$2 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :foreach Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :if (\$Ticket = \$Check) do={:return true}\r\ + \n :if ([ :typeof \$Ticket ] != \"nothing\" && \$Ticket != \$Check) d\ + o={:return false}\r\ + \n }\r\ + \n :return false;\r\ + \n }\r\ + \n :local AddTicket do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local Add [ :tostr \$2 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :while (true) do={\r\ + \n :local Pos [ :len (\$ScriptLockOrder->\$Script) ];\r\ + \n :set (\$ScriptLockOrder->\$Script->\$Pos) \$Add;\r\ + \n :delay 10ms;\r\ + \n :if ((\$ScriptLockOrder->\$Script->\$Pos) = \$Add) do={:return tru\ + e}\r\ + \n }\r\ + \n }\r\ + \n :local RemoveTicket do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local Remove [ :tostr \$2 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :foreach Id,Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :while ((\$ScriptLockOrder->\$Script->\$Id) = \$Remove) do={\r\ + \n :set (\$ScriptLockOrder->\$Script->\$Id);\r\ + \n :delay 10ms;\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n :local CleanupTickets do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :foreach Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :if ([ :typeof \$Ticket ] != \"nothing\") do={:return false}\r\ + \n }\r\ + \n :set (\$ScriptLockOrder->\$Script) ({});\r\ + \n }\r\ + \n :if ([ :typeof \$WaitMax ] = \"nil\" ) do={:set WaitMax 0s}\r\ + \n :if ([ :len [ /system/script/find where name=\$Script ] ] = 0) do={\r\ + \n :log error (\$0 . \" A script named '\" . \$Script . \"' does not ex\ + ist!\");\r\ + \n :error false;\r\ + \n }\r\ + \n :if ([ \$JobCount \$Script ] = 0) do={\r\ + \n \$LogPrint error \$0 (\"No script '\" . \$Script . \"' is running!\"\ + );\r\ + \n :error false;\r\ + \n }\r\ + \n :if ([ \$TicketCount \$Script ] >= [ \$JobCount \$Script ]) do={\r\ + \n :log error (\$0 . \" More tickets than running scripts '\" . \$Scrip\ + t . \"', resetting!\");\r\ + \n :set (\$ScriptLockOrder->\$Script) ({});\r\ + \n /system/script/job/remove [ find where script=\$Script ];\r\ + \n }\r\ + \n :local MyTicket [ \$GetRandom20CharAlNum 6 ];\r\ + \n \$AddTicket \$Script \$MyTicket;\r\ + \n :local WaitInterval (\$WaitMax / 20);\r\ + \n :local WaitTime \$WaitMax;\r\ + \n :while (\$WaitTime > 0 && \\\r\ + \n ([ \$IsFirstTicket \$Script \$MyTicket ] = false || \\\r\ + \n [ \$TicketCount \$Script ] < [ \$JobCount \$Script ])) do={\r\ + \n :set WaitTime (\$WaitTime - \$WaitInterval);\r\ + \n :delay \$WaitInterval;\r\ + \n }\r\ + \n :if ([ \$IsFirstTicket \$Script \$MyTicket ] = true && \\\r\ + \n [ \$TicketCount \$Script ] = [ \$JobCount \$Script ]) do={\r\ + \n \$RemoveTicket \$Script \$MyTicket;\r\ + \n \$CleanupTickets \$Script;\r\ + \n :return true;\r\ + \n }\r\ + \n \$RemoveTicket \$Script \$MyTicket;\r\ + \n \$LogPrint debug \$0 (\"Script '\" . \$Script . \"' started more than \ + once\" . \\\r\ + \n [ \$IfThenElse (\$WaitTime < \$WaitMax) \" and timed out waiting for\ + \_lock\" \"\" ] . \"...\");\r\ + \n :return false;\r\ + \n}\r\ + \n\r\ + \n:set SendNotification do={ :onerror Err {\r\ + \n :global SendNotification2;\r\ + \n \$SendNotification2 ({ origin=\$0; subject=\$1; message=\$2; link=\$3;\ + \_silent=\$4 });\r\ + \n} do={:global ExitOnError; \$ExitOnError \$0 \$Err}\r\ + \n}\r\ + \n\r\ + \n:set SendNotification2 do={\r\ + \n :local Notification \$1;\r\ + \n :global NotificationFunctions;\r\ + \n :foreach FunctionName,Discard in=\$NotificationFunctions do={\r\ + \n (\$NotificationFunctions->\$FunctionName) (\"\\\$NotificationFunctio\ + ns->\\\"\" . \$FunctionName . \"\\\"\") \$Notification;\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set SymbolByUnicodeName do={\r\ + \n :local Name [ :tostr \$1 ];\r\ + \n :global EitherOr;\r\ + \n :global SymbolsExtra;\r\ + \n :local Symbols ({\r\ + \n \"abacus\"=\"\\F0\\9F\\A7\\AE\";\r\ + \n \"alarm-clock\"=\"\\E2\\8F\\B0\";\r\ + \n \"arrow-down\"=\"\\E2\\AC\\87\";\r\ + \n \"arrow-up\"=\"\\E2\\AC\\86\";\r\ + \n \"calendar\"=\"\\F0\\9F\\93\\85\";\r\ + \n \"card-file-box\"=\"\\F0\\9F\\97\\83\";\r\ + \n \"chart-decreasing\"=\"\\F0\\9F\\93\\89\";\r\ + \n \"chart-increasing\"=\"\\F0\\9F\\93\\88\";\r\ + \n \"cloud\"=\"\\E2\\98\\81\";\r\ + \n \"cross-mark\"=\"\\E2\\9D\\8C\";\r\ + \n \"earth\"=\"\\F0\\9F\\8C\\8D\";\r\ + \n \"fire\"=\"\\F0\\9F\\94\\A5\";\r\ + \n \"floppy-disk\"=\"\\F0\\9F\\92\\BE\";\r\ + \n \"gear\"=\"\\E2\\9A\\99\";\r\ + \n \"heart\"=\"\\E2\\99\\A5\";\r\ + \n \"high-voltage-sign\"=\"\\E2\\9A\\A1\";\r\ + \n \"incoming-envelope\"=\"\\F0\\9F\\93\\A8\";\r\ + \n \"information\"=\"\\E2\\84\\B9\";\r\ + \n \"large-orange-circle\"=\"\\F0\\9F\\9F\\A0\";\r\ + \n \"large-red-circle\"=\"\\F0\\9F\\94\\B4\";\r\ + \n \"link\"=\"\\F0\\9F\\94\\97\";\r\ + \n \"lock-with-ink-pen\"=\"\\F0\\9F\\94\\8F\";\r\ + \n \"memo\"=\"\\F0\\9F\\93\\9D\";\r\ + \n \"mobile-phone\"=\"\\F0\\9F\\93\\B1\";\r\ + \n \"pushpin\"=\"\\F0\\9F\\93\\8C\";\r\ + \n \"scissors\"=\"\\E2\\9C\\82\";\r\ + \n \"scroll\"=\"\\F0\\9F\\93\\9C\";\r\ + \n \"smiley-partying-face\"=\"\\F0\\9F\\A5\\B3\";\r\ + \n \"smiley-smiling-face\"=\"\\E2\\98\\BA\";\r\ + \n \"smiley-winking-face-with-tongue\"=\"\\F0\\9F\\98\\9C\";\r\ + \n \"sparkles\"=\"\\E2\\9C\\A8\";\r\ + \n \"speech-balloon\"=\"\\F0\\9F\\92\\AC\";\r\ + \n \"star\"=\"\\E2\\AD\\90\";\r\ + \n \"warning-sign\"=\"\\E2\\9A\\A0\";\r\ + \n \"white-heavy-check-mark\"=\"\\E2\\9C\\85\"\r\ + \n }, \$SymbolsExtra);\r\ + \n :local Magic [ :pick [ /system/clock/get date ] 4 10 ];\r\ + \n :local Special {\r\ + \n \"information-04-01\"=\"\\F0\\9F\\9A\\BB\";\r\ + \n \"large-orange-circle-04-01\"=\"\\F0\\9F\\8D\\8A\";\r\ + \n \"large-orange-circle-10-31\"=\"\\F0\\9F\\8E\\83\";\r\ + \n \"large-red-circle-04-01\"=\"\\F0\\9F\\8D\\92\" };\r\ + \n :if ([ :len (\$Symbols->\$Name) ] = 0) do={\r\ + \n :log warning (\$0 . \"No symbol available for name '\" . \$Name . \"\ + '!\");\r\ + \n :return \"\";\r\ + \n }\r\ + \n :return ([ \$EitherOr (\$Special->(\$Name . \$Magic)) (\$Symbols->\$Na\ + me) ] . \"\\EF\\B8\\8F\");\r\ + \n}\r\ + \n\r\ + \n:set SymbolForNotification do={\r\ + \n :global NotificationsWithSymbols;\r\ + \n :global SymbolByUnicodeName;\r\ + \n :global IfThenElse;\r\ + \n :if (\$NotificationsWithSymbols != true) do={:return [ \$IfThenElse ([\ + \_:len \$2 ] > 0) ([ :tostr \$2 ] . \" \") \"\" ]}\r\ + \n :local Return \"\";\r\ + \n :foreach Symbol in=[ :toarray \$1 ] do={:set Return (\$Return . [ \$Sy\ + mbolByUnicodeName \$Symbol ])}\r\ + \n :return (\$Return . \" \");\r\ + \n}\r\ + \n\r\ + \n:set Unix2Dos do={:return [ :tocrlf [ :tostr \$1 ] ]}\r\ + \n:set UrlEncode do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :if ([ :len \$Input ] = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :local Chars (\"\\n\\r !\\\"#\\\$%&'()*+,:;<=>\?@[\\\\]^`{|}~\");\r\ + \n :local Subs { \"%0A\"; \"%0D\"; \"%20\"; \"%21\"; \"%22\"; \"%23\"; \"\ + %24\"; \"%25\"; \"%26\"; \"%27\";\r\ + \n \"%28\"; \"%29\"; \"%2A\"; \"%2B\"; \"%2C\"; \"%3A\"; \"%3B\"; \ + \"%3C\"; \"%3D\"; \"%3E\"; \"%3F\";\r\ + \n \"%40\"; \"%5B\"; \"%5C\"; \"%5D\"; \"%5E\"; \"%60\"; \"%7B\"; \ + \"%7C\"; \"%7D\"; \"%7E\" };\r\ + \n :for I from=0 to=([ :len \$Input ] - 1) do={\r\ + \n :local Char [ :pick \$Input \$I ];\r\ + \n :local Replace [ :find \$Chars \$Char ];\r\ + \n :if ([ :typeof \$Replace ] = \"num\") do={:set Char (\$Subs->\$Repla\ + ce)}\r\ + \n :set Return (\$Return . \$Char);\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set IsDNSResolving do={\r\ + \n :do {:resolve \"api.telegram.org\"} on-error={:return false}\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set IsFullyConnected do={\r\ + \n :global IsDNSResolving;\r\ + \n :global IsTimeSync;\r\ + \n :if ([ \$IsDNSResolving ] = false) do={:return false};\r\ + \n :if ([ \$IsTimeSync ] = false) do={:return false};\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set IsTimeSync do={\r\ + \n :global IsTimeSyncCached;\r\ + \n :global IsTimeSyncResetNtp;\r\ + \n :if (\$IsTimeSyncCached = true) do={:return true}\r\ + \n :if ([ /system/ntp/client/get enabled ] = true) do={\r\ + \n :if ([ /system/ntp/client/get status ] = \"synchronized\") do={\r\ + \n :set IsTimeSyncCached true;\r\ + \n :return true;\r\ + \n }\r\ + \n :local Uptime [ /system/resource/get uptime ];\r\ + \n :if ([ :typeof \$IsTimeSyncResetNtp ] = \"nothing\") do={\r\ + \n :set IsTimeSyncResetNtp \$Uptime;\r\ + \n }\r\ + \n :if (\$Uptime - \$IsTimeSyncResetNtp < 3m) do={:return false}\r\ + \n :log warning \"\$0 The ntp client is configured, but did not sync.\"\ + ;\r\ + \n :set IsTimeSyncResetNtp \$Uptime;\r\ + \n /system/ntp/client/set enabled=no;\r\ + \n :delay 20ms;\r\ + \n /system/ntp/client/set enabled=yes;\r\ + \n :return false;\r\ + \n }\r\ + \n :if ([ /system/license/get ]->\"level\" = \"free\" || \\\r\ + \n [ /system/resource/get ]->\"board-name\" = \"x86\") do={\r\ + \n :log debug \"\$0 No ntp client configured, relying on RTC for CHR fr\ + ee license and x86.\";\r\ + \n :return true;\r\ + \n }\r\ + \n :if ([ /ip/cloud/get update-time ] = true) do={\r\ + \n :if ([ :typeof [ /ip/cloud/get public-address ] ] = \"ip\") do={\r\ + \n :set IsTimeSyncCached true;\r\ + \n :return true;\r\ + \n }\r\ + \n :return false;\r\ + \n }\r\ + \n :log debug \"\$0 No time source configured! Returning gracefully...\";\ + \r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set FetchHugeFiles do={\r\ + \n :local ScriptName [ :tostr \$1 ];\r\ + \n :local Url [ :tostr \$2 ];\r\ + \n :local CheckCert [ :tostr \$3 ];\r\ + \n\r\ + \n :global CleanName;\r\ + \n :global FetchUserAgentStr;\r\ + \n :global GetRandom20CharAlNum;\r\ + \n :global IfThenElse;\r\ + \n :global LogPrint;\r\ + \n :global MkDir;\r\ + \n :global RmDir;\r\ + \n :global RmFile;\r\ + \n :global WaitForFile;\r\ + \n\r\ + \n :set CheckCert [ \$IfThenElse (\$CheckCert = \"false\") \"no\" \"yes-w\ + ithout-crl\" ];\r\ + \n\r\ + \n :local DirName (\"tmpfs/\" . [ \$CleanName \$ScriptName ]);\r\ + \n :if ([ \$MkDir \$DirName ] = false) do={\r\ + \n \$LogPrint error \$0 (\"Failed creating directory!\");\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :local FileName (\$DirName . \"/\" . [ \$CleanName \$0 ] . \"-\" . [ \ + \$GetRandom20CharAlNum ]);\r\ + \n :onerror Err {\r\ + \n /tool/fetch check-certificate=\$CheckCert \$Url dst-path=\$FileName \ + \\\r\ + \n http-header-field=({ [ \$FetchUserAgentStr \$ScriptName ] }) as-va\ + lue;\r\ + \n } do={\r\ + \n :if ([ \$WaitForFile \$FileName 500ms ] = true) do={\r\ + \n \$RmFile \$FileName;\r\ + \n }\r\ + \n \$LogPrint debug \$0 (\"Failed downloading from \" . \$Url . \" - \"\ + \_. \$Err);\r\ + \n \$RmDir \$DirName;\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :if ([ \$WaitForFile \$FileName 5s ] = false) do={\r\ + \n \$LogPrint debug \$0 (\"The file downloaded from \" . \$Url . \" did\ + \_not show up.\");\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :local FileSize [ /file/get \$FileName size ];\r\ + \n :local Return \"\";\r\ + \n :local VarSize 0;\r\ + \n :while (\$VarSize != \$FileSize) do={\r\ + \n :set Return (\$Return . ([ /file/read offset=\$VarSize chunk-size=32\ + 768 file=\$FileName as-value ]->\"data\"));\r\ + \n :set FileSize [ /file/get \$FileName size ];\r\ + \n :set VarSize [ :len \$Return ];\r\ + \n :if (\$VarSize > \$FileSize) do={\r\ + \n :delay 100ms;\r\ + \n }\r\ + \n }\r\ + \n \$RmDir \$DirName;\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set FetchUserAgentStr do={\r\ + \n :local Caller [ :tostr \$1 ];\r\ + \n :global GlobalScriptsUrl;\r\ + \n :global CommitBrief;\r\ + \n :global IfThenElse;\r\ + \n :local Resource [ /system/resource/get ];\r\ + \n :return (\"User-Agent: Mikrotik/\" . \$Resource->\"version\" . \" \" .\ + \_\$Resource->\"architecture-name\" . \\\r\ + \n \" \" . \$Caller . \"/Fetch (\" . \$GlobalScriptsUrl . \"; \" . [ \$\ + CommitBrief ] . \")\");\r\ + \n}\r\ + \n\r\ + \n:set ScriptInstallUpdate do={ :onerror Err {\r\ + \n :local Scripts [ :toarray \$1 ];\r\ + \n :local NewComment [ :tostr \$2 ];\r\ + \n :global InitSetup;\r\ + \n\r\ + \n :global CommitId;\r\ + \n :global ScriptUpdatesCRLF;\r\ + \n :global GlobalConfVersion;\r\ + \n :global Identity;\r\ + \n :global CertificateAvailable;\r\ + \n :global CheckSumsVerify;\r\ + \n :global GlobalScriptsUrl;\r\ + \n :global SumsDirAndFileName;\r\ + \n :global GlobalScriptsUrlSuffix;\r\ + \n :global EitherOr;\r\ + \n :global FetchUserAgentStr;\r\ + \n :global Grep;\r\ + \n :global IfThenElse;\r\ + \n :global LogPrint;\r\ + \n :global ParseKeyValueStore;\r\ + \n :global RequiredRouterOS;\r\ + \n :global SendNotification2;\r\ + \n :global SymbolForNotification;\r\ + \n :global ValidateSyntax;\r\ + \n :global CommentsInScripts;\r\ + \n\r\ + \n :if ([ \$CertificateAvailable \"Root YE\" \"fetch\" ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Downloading certificate failed, trying wit\ + hout.\");\r\ + \n }\r\ + \n\r\ + \n :foreach Script in=\$Scripts do={\r\ + \n :if ([ :len [ /system/script/find where name=\$Script ] ] = 0) do={\ + \r\ + \n \$LogPrint info \$0 (\"Adding new script: \" . \$Script);\r\ + \n /system/script/add name=\$Script owner=\$Script source=\"#!rsc by \ + Vados\\n\" comment=\$NewComment;\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :local CommitIdBefore \$CommitId;\r\ + \n :local GlobalConfVersionBefore \$GlobalConfVersion;\r\ + \n :local ReloadGlobal false;\r\ + \n :local DeviceMode [ /system/device-mode/get ];\r\ + \n\r\ + \n :if (\$CheckSumsVerify = true) do={\r\ + \n :local CheckSums ({});\r\ + \n :do {\r\ + \n :local Url (\$GlobalScriptsUrl . \$SumsDirAndFileName . \$GlobalScr\ + iptsUrlSuffix);\r\ + \n \$LogPrint debug \$0 (\"Fetching checksums from url: \" . \$Url);\ + \r\ + \n :set CheckSums [ :deserialize from=json ([ /tool/fetch check-certi\ + ficate=yes-without-crl \\\r\ + \n http-header-field=({ [ \$FetchUserAgentStr \$0 ] }) \$Url output\ + =user as-value ]->\"data\") ];\r\ + \n } on-error={ \$LogPrint warning \$0 (\"Err = \" . \$Err); }\r\ + \n }\r\ + \n\r\ + \n :foreach Script in=[ /system/script/find where source~\"^#!rsc by Vado\ + s\" ] do={\r\ + \n :local ScriptVal [ /system/script/get \$Script ];\r\ + \n :local ScriptInfo [ \$ParseKeyValueStore (\$ScriptVal->\"comment\") \ + ];\r\ + \n :local SourceNew;\r\ + \n\r\ + \n :foreach Scheduler in=[ /system/scheduler/find where on-event~(\"\\\ + \\b\" . \$ScriptVal->\"name\" . \"\\\\b\") ] do={\r\ + \n :local SchedulerVal [ /system/scheduler/get \$Scheduler ];\r\ + \n :if (\$ScriptVal->\"policy\" != \$SchedulerVal->\"policy\") do={\r\ + \n \$LogPrint warning \$0 (\"Policies differ for script '\" . \$Scr\ + iptVal->\"name\" . \"' and its scheduler '\" . \$SchedulerVal->\"name\" . \ + \"'!\");\r\ + \n }\r\ + \n }\r\ + \n :do {\r\ + \n :if (\$ScriptInfo->\"ignore\" = true) do={\r\ + \n \$LogPrint debug \$0 (\"Ignoring script '\" . \$ScriptVal->\"nam\ + e\" . \"', as requested.\");\r\ + \n :error true;\r\ + \n }\r\ + \n :if (\$CheckSumsVerify = true) do={\r\ + \n :local CheckSum (\$CheckSums->(\$ScriptVal->\"name\"));\r\ + \n :if ([ :len (\$ScriptInfo->\"base-url\") ] = 0 && [ :len (\$Scri\ + ptInfo->\"url-suffix\") ] = 0 && \\\r\ + \n [ :convert transform=md5 to=hex [ :tolf (\$ScriptVal->\"sou\ + rce\") ] ] = \$CheckSum) do={\r\ + \n \$LogPrint debug \$0 (\"Checksum for script '\" . \$ScriptVal-\ + >\"name\" . \"' matches, ignoring.\");\r\ + \n :error true;\r\ + \n }\r\ + \n }\r\ + \n :if ([ :len (\$ScriptInfo->\"certificate\") ] > 0) do={\r\ + \n :if ([ \$CertificateAvailable (\$ScriptInfo->\"certificate\") \"\ + fetch\" ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Downloading certificate failed, tryi\ + ng without.\");\r\ + \n }\r\ + \n }\r\ + \n :onerror Err {\r\ + \n :local BaseUrl [ \$EitherOr (\$ScriptInfo->\"base-url\") \$Globa\ + lScriptsUrl ];\r\ + \n :local UrlSuffix [ \$EitherOr (\$ScriptInfo->\"url-suffix\") \$G\ + lobalScriptsUrlSuffix ];\r\ + \n :local Url (\$BaseUrl . \$ScriptVal->\"name\" . \".rsc\" . \$Url\ + Suffix);\r\ + \n \$LogPrint warning \$0 (\"Fetching script '\" . (\$ScriptVal->\"\ + name\") . \"' from url: \" . \$Url);\r\ + \n :local Result [ /tool/fetch check-certificate=yes-without-crl \\\ + \r\ + \n http-header-field=({ [ \$FetchUserAgentStr \$0 ] }) \$Url outp\ + ut=user as-value ];\r\ + \n :if (\$Result->\"status\" = \"finished\") do={\r\ + \n :set SourceNew [ :tolf (\$Result->\"data\") ];\r\ + \n } \r\ + \n } do={\r\ + \n \$LogPrint warning \$0 (\"Failed fetching script '\" . \$ScriptV\ + al->\"name\" . \"': \" . \$Err);\r\ + \n :if (\$Err != \"Fetch failed with status 404\") do={\r\ + \n :error false;\r\ + \n }\r\ + \n :if (\$ScriptVal->\"source\" = \"#!rsc by Vados\") do={\r\ + \n \$LogPrint warning \$0 (\"Removing dummy. Typo on installation\ + \?\");\r\ + \n /system/script/remove \$Script;\r\ + \n :error false;\r\ + \n }\r\ + \n :if (\$CheckSumsVerify = true) do={\r\ + \n :if ([ :len (\$ScriptInfo->\"base-url\") ] = 0 && [ :len (\$Sc\ + riptInfo->\"url-suffix\") ] = 0 && \\\r\ + \n [ :len \$CheckSum ] = 0) do={\r\ + \n \$LogPrint warning \$0 (\"Added the script manually\? Skip u\ + pdates with 'ignore=true' in comment.\");\r\ + \n }\r\ + \n }\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :if ([ :len \$SourceNew ] = 0) do={\r\ + \n \$LogPrint debug \$0 (\"No update for script '\" . (\$ScriptVal-\ + >\"name\") . \"'.\");\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :local SourceCRLF [ :tocrlf \$SourceNew ];\r\ + \n :if (\$InitSetup != true && ((\$ScriptVal->\"name\") != \"AM-Globa\ + lConfig\" || (\$ScriptVal->\"name\") != \"AM-GlobalFunc\")) do={\r\ + \n :if (\$SourceNew = (\$ScriptVal->\"source\") || \$SourceCRLF = (\ + \$ScriptVal->\"source\")) do={\r\ + \n \$LogPrint debug \$0 (\"Script '\" . (\$ScriptVal->\"name\") \ + . \"' source did not change. End of lines set to CRLF.\");\r\ + \n if (\$ScriptUpdatesCRLF = false) do={\r\ + \n :put [ :tolf \$SourceNew ];\r\ + \n \$LogPrint debug \$0 (\"Script '\" . (\$ScriptVal->\"name\"\ + ) . \"' source update end of lines to LF.\");\r\ + \n }\r\ + \n :error false;\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :if ([ :pick \$SourceNew 0 14 ] != \"#!rsc by Vados\") do={\r\ + \n \$LogPrint warning \$0 (\"Looks like new script '\" . \$ScriptVa\ + l->\"name\" . \"' is not valid (missing shebang = '\$shebang') Ignoring!\"\ + );\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :if (\$CommentsInScripts = true) do={\r\ + \n :local CommentLine [ \$Grep \$SourceNew (\"\\23 Script comment:\ + \_\") ];\r\ + \n :if ([ :len \$CommentLine ] = 0) do={\r\ + \n \$LogPrint debug \$0 (\"The search in new script '\" . (\$Scr\ + iptVal->\"name\") . \"' for comment in script body, is not found result Ig\ + noring!\");\r\ + \n :error false;\r\ + \n } else={\r\ + \n :if ([ :len (\$ScriptVal->\"comment\")] = 0 || [ :len (\$Scri\ + ptVal->\"comment\")] != [ :pick \$CommentLine ([ :find \$CommentLine \":\"\ + ] + 2) [ :len \$CommentLine] ]) do={\r\ + \n \$LogPrint debug \$0 (\"Exsiting comment for script '\" . (\ + \$ScriptVal->\"name\") . \"' is '\" . (\$ScriptVal->\"comment\") . \"'.\")\ + ;\r\ + \n :set (\$ScriptVal->\"comment\") [ :pick \$CommentLine ([ :f\ + ind \$CommentLine \":\"] + 2) [ :len \$CommentLine] ];\r\ + \n \$LogPrint debug \$0 (\"New comment (\" . (\$ScriptVal->\"c\ + omment\") . \") for script '\" . (\$ScriptVal->\"name\") . \"' has been ad\ + ded!\");\r\ + \n } else={\r\ + \n \$LogPrint debug \$0 (\"Comment for script '\" . (\$ScriptV\ + al->\"name\") . \"' did not change.\");\r\ + \n :error false;\r\ + \n }\r\ + \n } \r\ + \n }\r\ + \n\r\ + \n :local RequiredROS ([ \$ParseKeyValueStore [ \$Grep \$SourceNew (\ + \"\\23 requires RouterOS, \") ] ]->version);\r\ + \n :if ([ \$RequiredRouterOS \$0 [ \$EitherOr \$RequiredROS \"0.0\" ]\ + \_false ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"The script '\" . (\$ScriptVal->\"name\ + \") . \"' requires RouterOS \" . \\\r\ + \n \$RequiredROS . \", which is not met by your installation. I\ + gnoring!\");\r\ + \n :error false;\r\ + \n }\r\ + \n :local RequiredDM [ \$ParseKeyValueStore [ \$Grep \$SourceNew (\"\ + \\23 requires device-mode, \") ] ];\r\ + \n :local MissingDM ({});\r\ + \n :foreach Feature,Value in=\$RequiredDM do={\r\ + \n :if ([ :typeof (\$DeviceMode->\$Feature) ] = \"bool\" && (\$Devi\ + ceMode->\$Feature) = false) do={\r\ + \n :set MissingDM (\$MissingDM, \$Feature);\r\ + \n }\r\ + \n }\r\ + \n :if ([ :len \$MissingDM ] > 0) do={\r\ + \n \$LogPrint warning \$0 (\"The script '\" . (\$ScriptVal->\"name\ + \") . \"' requires disabled \" . \\\r\ + \n \"device-mode features (\" . [ :tostr \$MissingDM ] . \"). I\ + gnoring!\");\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :if ([ \$ValidateSyntax \$SourceNew ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Syntax validation for script '\" . (\$\ + ScriptVal->\"name\") . \"' failed! Ignoring!\");\r\ + \n :error false;\r\ + \n }\r\ + \n \r\ + \n :log info (\"Updating script: \" . (\$ScriptVal->\"name\") . \" Sc\ + ript comment: \" . (\$ScriptVal->\"comment\"));\r\ + \n\r\ + \n /system/script/set owner=(\$ScriptVal->\"name\") comment=(\$Script\ + Val->\"comment\") \\\r\ + \n source=[ \$IfThenElse (\$ScriptUpdatesCRLF = true) \$SourceCRL\ + F \$SourceNew ] \$Script;\r\ + \n :if (\$ScriptVal->\"name\" = \"AM-GlobalConfig\" || \\\r\ + \n \$ScriptVal->\"name\" = \"AM-GlobalFunc\" || \\\r\ + \n \$ScriptVal->\"name\" ~ (\"^(AM-GlobalFunc\\\\.d|mod)/.\")) d\ + o={\r\ + \n :set ReloadGlobal true;\r\ + \n }\r\ + \n } on-error={ }\r\ + \n }\r\ + \n :if (\$ReloadGlobal = true) do={\r\ + \n \$LogPrint info \$0 (\"Reloading global configuration and functions.\ + \");\r\ + \n :onerror Err {\r\ + \n :global InitSetup (a);\r\ + \n /system/script {run AM-GlobalConfig; run AM-GlobalFunc;};\r\ + \n } do={\$LogPrint error \$0 (\"Reloading global configuration and fun\ + ctions failed! \" . \$Err)}\r\ + \n }\r\ + \n :if (\$GlobalConfVersionBefore > \$GlobalConfVersion) do={\r\ + \n \$LogPrint warning \$0 (\"The configuration version decreased from \ + \" . \\\r\ + \n \$GlobalConfVersionBefore . \" to \" . \$GlobalConfVersion . \\\r\ + \n \". Installed an older version\?\");\r\ + \n }\r\ + \n :if (\$GlobalConfVersionBefore < \$GlobalConfVersion) do={\r\ + \n :global GlobalConfChanges;\r\ + \n :global GlobalConfMigration;\r\ + \n :local ChangeLogCode;\r\ + \n :if ([ :len \$ChangeLogCode ] > 0) do={\r\ + \n :if ([ \$ValidateSyntax \$ChangeLogCode ] = true) do={\r\ + \n :onerror Err {\r\ + \n [ :parse \$ChangeLogCode ];\r\ + \n } do={\$LogPrint warning \$0 (\"The changelog failed to run: \" \ + . \$Err)}\r\ + \n } else={\$LogPrint warning \$0 (\"The changelog failed syntax vali\ + dation!\")}\r\ + \n }\r\ + \n :if ([ :len \$GlobalConfMigration ] > 0) do={\r\ + \n :for I from=(\$GlobalConfVersionBefore + 1) to=\$GlobalConfVersion\ + \_do={\r\ + \n :local Migration (\$GlobalConfMigration->[ :tostr \$I ]);\r\ + \n :do {\r\ + \n :if ([ :typeof \$Migration ] != \"str\") do={\r\ + \n \$LogPrint debug \$0 (\"Migration code for change \" . \$I .\ + \_\" is not available.\");\r\ + \n :error false;\r\ + \n }\r\ + \n :if ([ \$ValidateSyntax \$Migration ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Migration code for change \" . \$I\ + \_. \" failed syntax validation!\");\r\ + \n :error false}\r\ + \n \$LogPrint info \$0 (\"Applying migration for change \" . \$I \ + . \": \" . \$Migration);\r\ + \n :onerror Err {\r\ + \n [ :parse \$Migration ];\r\ + \n } do={\$LogPrint warning \$0 (\"Migration code for change \" .\ + \_\$I . \" failed to run: \" . \$Err)}\r\ + \n } on-error={ }\r\ + \n }\r\ + \n }\r\ + \n :local NotificationMessage (\"The configuration version on \" . \$Id\ + entity . \" increased \" . \\\r\ + \n \"to \" . \$GlobalConfVersion . \", current configuration may nee\ + d modification. \" . \\\r\ + \n \"Please review and update global-config-overlay, then re-run glo\ + bal-config.\");\r\ + \n \$LogPrint info \$0 (\$NotificationMessage);\r\ + \n :if ([ :len \$GlobalConfChanges ] > 0) do={\r\ + \n :set NotificationMessage (\$NotificationMessage . \"\\n\\nChanges:\ + \");\r\ + \n :for I from=(\$GlobalConfVersionBefore + 1) to=\$GlobalConfVersion\ + \_do={\r\ + \n :local Change (\$GlobalConfChanges->[ :tostr \$I ]);\r\ + \n :set NotificationMessage (\$NotificationMessage . \"\\n \" . \\\ + \r\ + \n [ \$SymbolForNotification \"pushpin\" \"*\" ] . \$Change);\r\ + \n \$LogPrint info \$0 (\"Change \" . \$I . \": \" . \$Change);\r\ + \n }\r\ + \n } else={\r\ + \n :set NotificationMessage (\$NotificationMessage . \"\\n\\nNews and\ + \_changes are not available.\");\r\ + \n\r\ + \n }\r\ + \n\r\ + \n :if ([ :len \$GlobalConfChanges ] > 0) do={ :set GlobalConfChanges; \ + } else={ :global GlobalConfChanges (a); }\r\ + \n :if ([ :len \$GlobalConfMigration ] > 0) do={ :set GlobalConfMigrati\ + on; } else={ :global GlobalConfMigration (a); }\r\ + \n }\r\ + \n} do={\r\ + \n :global ExitOnError; \$ExitOnError \$0 \$Err;\r\ + \n} }\r\ + \n\r\ + \n# load modules\r\ + \n:foreach Script in=[ /system/script/find where name ~ \"^(global-functio\ + ns\\\\.d|mod)/.\" ] do={\r\ + \n :local ScriptVal [ /system/script/get \$Script ];\r\ + \n :if ([ \$ValidateSyntax (\$ScriptVal->\"source\") ] = true) do={\r\ + \n :onerror Err {\r\ + \n /system/script/run \$Script;\r\ + \n } do={\r\ + \n \$LogPrint error \$0 (\"Module '\" . \$ScriptVal->\"name\" . \"' f\ + ailed to run: \" . \$Err);\r\ + \n }\r\ + \n } else={\r\ + \n \$LogPrint error \$0 (\"Module '\" . \$ScriptVal->\"name\" . \"' fai\ + led syntax validation, skipping.\");\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n# Add and fix global scheduler\r\ + \n:put \"Add or/and fix Global scheduler...\";\r\ + \n:local OnEvent \"/system/script { run AM-GlobalConfig; run AM-GlobalFunc\ + ; }\";\r\ + \n:local Comment \"Every Day Set Global Environment Variables\";\r\ + \n:if ([ :len [ /system/scheduler/find where name=\"AM-SetGlobalEnv\" ] ] \ + = 0) do={\r\ + \n /system/scheduler/add name=\"AM-SetGlobalEnv\" comment=\$Comment on-ev\ + ent=\$OnEvent start-time=startup;\r\ + \n}\r\ + \n:if ([ /system/scheduler/get \"AM-SetGlobalEnv\" comment ] != \$Comment)\ + \_do={\r\ + \n /system/scheduler/set \"AM-SetGlobalEnv\" comment=\$Comment;\r\ + \n}\r\ + \n:if ([ /system/scheduler/get \"AM-SetGlobalEnv\" on-event ] != \$OnEvent\ + ) do={\r\ + \n /system/scheduler/set \"AM-SetGlobalEnv\" on-event=\$OnEvent;\r\ + \n}\r\ + \n# Add Update Script scheduler\r\ + \n:put \"Add or/and fix Update Script scheduler\";\r\ + \n:set OnEvent \":global ScriptInstallUpdate; \\\$ScriptInstallUpdate;\";\ + \r\ + \n:set Comment \"Every Day Check For Update Scripts\";\r\ + \n:if ([ :len [ /system/scheduler/find where name=\"AM-ScriptInstallUpdate\ + \" ] ] = 0) do={\r\ + \n/system/scheduler/add name=\"AM-ScriptInstallUpdate\" comment=\$Comment \ + start-time=startup interval=1d on-event=\$OnEvent;\r\ + \n}\r\ + \n\r\ + \n:onerror Err {\r\ + \n :global GlobalConfReady;\r\ + \n :retry { :if (\$GlobalConfReady != true) do={ :error (\"Global configs\ + \_not ready.\"); }; } delay=100ms max=5;\r\ + \n\r\ + \n:log warning (\"This script \$ScriptName Loaded \" . \\\r\ + \n [ \$IfThenElse (\$CommitId != \"unknown\") ([ \$CommitBrief ] . \" \ + \") ] . \\\r\ + \n \"on \" . \$devBoardName . \"\\nRouterOS: \" . \$devCurVersion . \"\ + \\nArchitecture: \" . \$devCurArch . \"\\nIdentity: \" . \$Identity);\r\ + \n # signal we are ready\r\ + \n:set GlobalFuncReady true;\r\ + \n:set ExitOK true;\r\ + \n\r\ + \n:log info (\$0 . \"Global configs ready!\");\r\ + \n} do={\r\ + \n :global ExitOnError; \$ExitOnError \$0 \$Err;\r\ + \n}\r\ + \n" +add dont-require-permissions=no name=fetchCustom owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + \n#/tool/fetch mode=https url=(\$GlobalScriptsUrl . \".bak/mikro_bak_rsa@1\ + 0.30.30.10.pub\") dst-path=\"/mikro_bak_rsa@10.30.30.10.pub\";\ + \n#\ + \n#root.mnuc.backuper\ + \n\ + \n#/tool/fetch mode=https url=(\$GlobalScriptsUrl . \".bak/mikro_bak_rsa@1\ + 0.30.30.81.pub\") dst-path=\"/mikro_bak_rsa@10.30.30.81.pub\";\ + \n#:local Script AM-GlobalConfig;\ + \n#/system/script/add name=\$Script owner=\$Script source=([ /tool/fetch c\ + heck-certificate=yes-without-crl url=(\$GlobalScriptsUrl . \$Script . \".r\ + sc\") output=user as-value ]->\"data\");\ + \n#/usb1/mikro_bak_rsa.pub\ + \n#:local FetchFile AM-DOHCert.rsc\ + \n\ + \n:global GlobalScriptsUrl \"https://ros.vados.ru/tmp/\";\ + \n:global MkDir;\ + \n\ + \n:local FetchFile Manager@id_rsa.pub\ + \n:local localDir \"/\";\ + \n\ + \n\$MkDir \$localDir;\ + \n\ + \n/tool/fetch mode=https url=(\$GlobalScriptsUrl . \$FetchFile) dst-path=(\ + \$localDir . \$FetchFile);\ + \n" +add dont-require-permissions=yes name=MkTmpfs owner=vados policy=\ + ftp,read,write,policy,test,sniff,sensitive source=\ + ":global MkDir;\r\ + \n\r\ + \n\$MkDir /usb1/tmpfs;" +add dont-require-permissions=no name=CreateContainer owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source=":\ + local contIF \"veth1\";\r\ + \n:local contIP \"10.10.10.10/24\";\r\ + \n:local contGW \"10.10.10.1\";\r\ + \n#/interface/veth/add name=\$contIF address=\$contIP gateway=\$contGW;\r\ + \n:local contBridgeIF \"Bridge-DOC\";\r\ + \n:local contBridgeIP \"10.10.10.1/24\";\r\ + \n/interface/bridge/add name=\$contBridgeIF;\r\ + \n/ip/address/add address=\$contBridgeIP interface=\$contBridgeIF;\r\ + \n/interface/bridge/port/add bridge=\$contBridgeIF interface=\$contIF;\r\ + \n/container/config/set ram-high=768M registry-url=https://registry-1.dock\ + er.io tmpdir=sata1/tmp\r\ + \n" +add dont-require-permissions=no name=carkeeperbot owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="/\ + container/mounts/add list=carkeeper_configs/.profile src=sata1/carkeeper/c\ + onfigs/.profile dst=/root/.ashrc;\r\ + \n/container/mounts/add list=carkeeper_configs/php.ini src=sata1/carkeeper\ + /configs/php.ini dst=/etc/php82/php.ini;\r\ + \n#/container/mounts/add list=carkeeper_configs src=sata1/carkeeper/config\ + s dst=/configs;\r\ + \n/container/mounts/add list=carkeeper_app src=sata1/carkeeper/app dst=/ap\ + p;\r\ + \n/container/mounts/add list=carkeeper_scripts src=sata1/carkeeper/scripts\ + \_dst=/scripts;\r\ + \n/container/mounts/add list=carkeeper_configs/sshd_config src=sata1/carke\ + eper/configs/sshd_config dst=/etc/ssh/sshd_config;\r\ + \n\r\ + \n/container/mounts/add list=carkeeper_tmp src=sata1/carkeeper/tmp dst=/va\ + r/tmp;\r\ + \n/container/mounts/add list=carkeeper_sync src=sata1/carkeeper/sync dst=/\ + var/sync;\r\ + \n" +add comment="Enable or disable ip addresses based on bridge port state" \ + dont-require-permissions=no name=mod/AM-IPbridge owner=mod/AM-IPbridge \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + source="#!rsc by Vados\r\ + \n# RouterOS script: mod/AM-IPbridge\r\ + \n# Script comment: Enable or disable ip addresses based on bridge port st\ + ate\r\ + \n# Copyright (c) 2005-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# requires device-mode, fetch\r\ + \n#\r\ + \n:local ScriptName [ :jobname ];\r\ + \n\r\ + \n# Add this Script scheduler\r\ + \n:if ([ :len [ /system/scheduler/find where name=\$ScriptName ] ] = 0) do\ + ={\r\ + \n:log warning (\"SystemScheduler for script: \" . \$ScriptName . \" is NO\ + T SET! Fix It Now!\");\r\ + \n:local OnEvent \"/system/script/run \$ScriptName;\";\r\ + \n:local Comment \"Every Day Check For Fake Bridges\";\r\ + \n/system/scheduler/add name=\$ScriptName comment=\$Comment start-time=sta\ + rtup interval=1d on-event=\$OnEvent;\r\ + \n}\r\ + \n\r\ + \n:foreach Bridge in=[ /interface/bridge/find ] do={\r\ + \n :local BrName [ /interface/bridge/get \$Bridge name ];\r\ + \n :if ([ :len [ /interface/bridge/port/find where bridge=\$BrName ] ] > \ + 0) do={\r\ + \n :if ([ :len [ /interface/bridge/port/find where bridge=\$BrName and \ + inactive=no ] ] = 0) do={\r\ + \n /ip/address/disable [ find where !dynamic interface=\$BrName ];\r\ + \n } else={\r\ + \n /ip/address/enable [ find where !dynamic interface=\$BrName ];\r\ + \n }\r\ + \n }\r\ + \n}\r\ + \n" +add comment="Use Telegram to chat with your Router and send commands" \ + dont-require-permissions=no name=mod/AM-TG-Bot owner=mod/AM-TG-Bot \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + source="#!rsc by Vados\r\ + \n# RouterOS script: mod/AM-TG-Bot\r\ + \n# Script comment: Use Telegram to chat with your Router and send command\ + s\r\ + \n# Copyright (c) 2007-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# requires device-mode, fetch\r\ + \n:local ExitOK false;\r\ + \n:onerror Err {\r\ + \n :global GlobalConfReady; :global GlobalFuncReady;\r\ + \n :retry { :if (\$GlobalConfReady != true || \$GlobalFuncReady != true) \ + \\\r\ + \n do={ :error (\"Global config and/or functions not ready.\"); }; } \ + delay=500ms max=50;\r\ + \n :local ScriptName [ :jobname ];\r\ + \n\r\ + \n :global Identity;\r\ + \n :global TelegramChatActive;\r\ + \n :global TelegramChatGroups;\r\ + \n :global TelegramChatId;\r\ + \n :global TelegramChatIdsTrusted;\r\ + \n :global TelegramChatOffset;\r\ + \n :global TelegramChatRunTime;\r\ + \n :global TelegramMessageIDs;\r\ + \n :global TelegramRandomDelay;\r\ + \n :global TelegramTokenId;\r\ + \n\r\ + \n :global CertificateAvailable;\r\ + \n :global EitherOr;\r\ + \n :global EscapeForRegEx;\r\ + \n :global FileExists;\r\ + \n :global GetRandom20CharAlNum;\r\ + \n :global IfThenElse;\r\ + \n :global LogPrint;\r\ + \n :global LogPrintVerbose;\r\ + \n :global MAX;\r\ + \n :global MIN;\r\ + \n :global MkDir;\r\ + \n :global RandomDelay;\r\ + \n :global RmDir;\r\ + \n :global ScriptLock;\r\ + \n :global SendTelegram2;\r\ + \n :global SymbolForNotification;\r\ + \n :global ValidateSyntax;\r\ + \n :global WaitForFile;\r\ + \n :global WaitFullyConnected;\r\ + \n\r\ + \n :if ([ \$ScriptLock \$ScriptName ] = false) do={\r\ + \n :set ExitOK true;\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n \$WaitFullyConnected;\r\ + \n\r\ + \n :if ([ :len [ /system/scheduler/find where name=\$ScriptName ] ] = 0) \ + do={\r\ + \n /system/scheduler/add start-time=startup interval=30s name=\$ScriptN\ + ame on-event=\"/system/script/run \$ScriptName;\" comment=\"Telegram Chat \ + Bot\";\r\ + \n }\r\ + \n :if ([ :typeof \$TelegramChatOffset ] != \"array\") do={\r\ + \n :set TelegramChatOffset { 0; 0; 0 };\r\ + \n }\r\ + \n :if ([ :typeof \$TelegramRandomDelay ] != \"num\") do={\r\ + \n :set TelegramRandomDelay 0;\r\ + \n }\r\ + \n\r\ + \n :if ([ \$CertificateAvailable \"Go Daddy Root Certificate Authority - \ + G2\" \"fetch\" ] = false) do={\r\ + \n \$LogPrint warning \$ScriptName (\"Downloading required certificate \ + failed.\");\r\ + \n :set ExitOK true;\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n \$RandomDelay \$TelegramRandomDelay;\r\ + \n\r\ + \n :local Data false;\r\ + \n :for I from=1 to=4 do={\r\ + \n :if (\$Data = false) do={\r\ + \n :onerror Err {\r\ + \n :set Data ([ /tool/fetch check-certificate=yes-without-crl outpu\ + t=user \\\r\ + \n (\"https://api.telegram.org/bot\" . \$TelegramTokenId . \"/get\ + Updates\?offset=\" . \\\r\ + \n \$TelegramChatOffset->0 . \"&allowed_updates=%5B%22message%22%\ + 5D\") as-value ]->\"data\");\r\ + \n :set TelegramRandomDelay [ \$MAX 0 (\$TelegramRandomDelay - 1) ]\ + ;\r\ + \n } do={\r\ + \n :if (\$I < 4) do={\r\ + \n \$LogPrint debug \$ScriptName (\"Fetch failed, \" . \$I . \". \ + try: \" . \$Err);\r\ + \n :set TelegramRandomDelay [ \$MIN 15 (\$TelegramRandomDelay + 5\ + ) ];\r\ + \n :delay ((\$I * \$I) . \"s\");\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :if (\$Data = false) do={\r\ + \n \$LogPrint warning \$ScriptName (\"Failed getting updates.\");\r\ + \n :set ExitOK true;\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :local JSON [ :deserialize from=json value=\$Data ];\r\ + \n :local UpdateID 0;\r\ + \n :local Uptime [ /system/resource/get uptime ];\r\ + \n :foreach Update in=(\$JSON->\"result\") do={\r\ + \n :set UpdateID (\$Update->\"update_id\");\r\ + \n \$LogPrintVerbose debug \$ScriptName (\"Update \" . \$UpdateID . \":\ + \_\" . [ :serialize to=json \$Update ]);\r\ + \n\r\ + \n :local Message (\$Update->\"message\");\r\ + \n :local IsAnyReply ([ :typeof (\$Message->\"reply_to_message\") ] = \ + \"array\");\r\ + \n :local IsMyReply (\$TelegramMessageIDs->[ :tostr (\$Message->\"reply\ + _to_message\"->\"message_id\") ]);\r\ + \n :if ((\$IsMyReply = 1 || \$TelegramChatOffset->0 > 0 || \$Uptime > 5\ + m) && \$UpdateID >= \$TelegramChatOffset->2) do={\r\ + \n :local Trusted false;\r\ + \n :local Chat (\$Message->\"chat\");\r\ + \n :local From (\$Message->\"from\");\r\ + \n :local Command (\$Message->\"text\");\r\ + \n :local ThreadId [ \$IfThenElse (\$Message->\"is_topic_message\") (\ + \$Message->\"message_thread_id\") \"\" ];\r\ + \n\r\ + \n :foreach IdsTrusted in=(\$TelegramChatId, \$TelegramChatIdsTrusted\ + ) do={\r\ + \n :if (\$From->\"id\" = \$IdsTrusted || \\\r\ + \n \$From->\"username\" = \$IdsTrusted || \\\r\ + \n \$Chat->\"id\" = \$IdsTrusted) do={\r\ + \n :set Trusted true;\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :if (\$Trusted = true) do={\r\ + \n :local Done false;\r\ + \n :if (\$Command = \"\?\") do={\r\ + \n \$LogPrint info \$ScriptName (\"Sending notice for update \" .\ + \_\$UpdateID . \".\");\r\ + \n \$SendTelegram2 ({ origin=\$ScriptName; chatid=(\$Chat->\"id\"\ + ); silent=true; \\\r\ + \n replyto=(\$Message->\"message_id\"); threadid=\$ThreadId; \\\ + \r\ + \n subject=([ \$SymbolForNotification \"speech-balloon\" ] . \"\ + Telegram Chat\"); \\\r\ + \n message=([ \$IfThenElse ([ :len (\$From->\"first_name\") ] >\ + \_0) (\"Hello \" . (\$From->\"first_name\") . \"!\\n\\n\") ] . \\\r\ + \n \"Online\" . [ \$IfThenElse \$TelegramChatActive \" (and a\ + ctive!)\" ] . \", awaiting your commands!\") });\r\ + \n :set Done true;\r\ + \n }\r\ + \n :if (\$Done = false && [ :pick \$Command 0 1 ] = \"!\") do={\r\ + \n :if (\$Command ~ (\"^! *(\" . [ \$EscapeForRegEx \$Identity ] \ + . \"|@\" . \$TelegramChatGroups . \")\\\$\")) do={\r\ + \n :set TelegramChatActive true;\r\ + \n } else={\r\ + \n :set TelegramChatActive false;\r\ + \n }\r\ + \n \$LogPrint info \$ScriptName (\"Now \" . [ \$IfThenElse \$Tele\ + gramChatActive \"active\" \"passive\" ] . \\\r\ + \n \" from update \" . \$UpdateID . \"!\");\r\ + \n :set Done true;\r\ + \n }\r\ + \n :if (\$Done = false && (\$IsMyReply = 1 || (\$IsAnyReply = false\ + \_&& \\\r\ + \n \$TelegramChatActive = true)) && [ :len \$Command ] > 0) do\ + ={\r\ + \n :if ([ \$ValidateSyntax \$Command ] = true) do={\r\ + \n :local State \"\";\r\ + \n :local File (\"tmpfs/telegram-chat/\" . [ \$GetRandom20CharA\ + lNum 6 ]);\r\ + \n :if ([ \$MkDir \"tmpfs/telegram-chat\" ] = false) do={\r\ + \n \$LogPrint error \$ScriptName (\"Failed creating directory\ + !\");\r\ + \n :set ExitOK true;\r\ + \n :error false;\r\ + \n }\r\ + \n \$LogPrint info \$ScriptName (\"Running command from update \ + \" . \$UpdateID . \": \" . \$Command);\r\ + \n :execute script=(\":do {\\n\" . \$Command . \"\\n} on-error=\ + { /file/add name=\\\"\" . \$File . \".failed\\\" };\" . \\\r\ + \n \"/file/add name=\\\"\" . \$File . \".done\\\"\") file=(\$\ + File . \"\\00\");\r\ + \n :if ([ \$WaitForFile (\$File . \".done\") [ \$EitherOr \$Tel\ + egramChatRunTime 20s ] ] = false) do={\r\ + \n :set State ([ \$SymbolForNotification \"warning-sign\" ] .\ + \_\"The command did not finish, still running in background.\\n\\n\");\r\ + \n }\r\ + \n :if ([ \$FileExists (\$File . \".failed\") ] = true) do={\r\ + \n :set State ([ \$SymbolForNotification \"cross-mark\" ] . \ + \"The command failed with an error!\\n\\n\");\r\ + \n }\r\ + \n :local Content ([ /file/read chunk-size=32768 file=\$File as\ + -value ]->\"data\");\r\ + \n \$SendTelegram2 ({ origin=\$ScriptName; chatid=(\$Chat->\"id\ + \"); silent=true; \\\r\ + \n replyto=(\$Message->\"message_id\"); threadid=\$ThreadId; \ + \\\r\ + \n subject=([ \$SymbolForNotification \"speech-balloon\" ] . \ + \"Telegram Chat\"); \\\r\ + \n message=([ \$SymbolForNotification \"gear\" ] . \"Command:\ + \\n\" . \$Command . \"\\n\\n\" . \\\r\ + \n \$State . [ \$IfThenElse ([ :len \$Content ] > 0) \\\r\ + \n ([ \$SymbolForNotification \"memo\" ] . \"Output:\\n\" .\ + \_\$Content) \\\r\ + \n ([ \$SymbolForNotification \"memo\" ] . \"No output.\") \ + ]) });\r\ + \n \$RmDir \"tmpfs/telegram-chat\";\r\ + \n } else={\r\ + \n \$LogPrint info \$ScriptName (\"The command from update \" .\ + \_\$UpdateID . \" failed syntax validation!\");\r\ + \n \$SendTelegram2 ({ origin=\$ScriptName; chatid=(\$Chat->\"id\ + \"); silent=false; \\\r\ + \n replyto=(\$Message->\"message_id\"); threadid=\$ThreadId; \ + \\\r\ + \n subject=([ \$SymbolForNotification \"speech-balloon\" ] . \ + \"Telegram Chat\"); \\\r\ + \n message=([ \$SymbolForNotification \"gear\" ] . \"Command:\ + \\n\" . \$Command . \"\\n\\n\" . \\\r\ + \n [ \$SymbolForNotification \"cross-mark\" ] . \"The comma\ + nd failed syntax validation!\") });\r\ + \n }\r\ + \n }\r\ + \n } else={\r\ + \n :local MessageText (\"Received a message from untrusted contact \ + \" . \\\r\ + \n [ \$IfThenElse ([ :len (\$From->\"username\") ] = 0) \"without\ + \_username\" (\"'\" . \$From->\"username\" . \"'\") ] . \\\r\ + \n \" (ID \" . \$From->\"id\" . \") in update \" . \$UpdateID . \ + \"!\");\r\ + \n :if (\$Command ~ (\"^! *\" . [ \$EscapeForRegEx \$Identity ] . \ + \"\\\$\")) do={\r\ + \n \$LogPrint warning \$ScriptName \$MessageText;\r\ + \n \$SendTelegram2 ({ origin=\$ScriptName; chatid=(\$Chat->\"id\"\ + ); silent=false; \\\r\ + \n replyto=(\$Message->\"message_id\"); threadid=\$ThreadId; \\\ + \r\ + \n subject=([ \$SymbolForNotification \"speech-balloon\" ] . \"\ + Telegram Chat\"); \\\r\ + \n message=(\"You are not trusted.\") });\r\ + \n } else={\r\ + \n \$LogPrint info \$ScriptName \$MessageText;\r\ + \n }\r\ + \n }\r\ + \n } else={\r\ + \n \$LogPrint debug \$ScriptName (\"Already handled update \" . \$Upd\ + ateID . \".\");\r\ + \n }\r\ + \n }\r\ + \n :set TelegramChatOffset ([ :pick \$TelegramChatOffset 1 3 ], \\\r\ + \n [ \$IfThenElse (\$UpdateID >= \$TelegramChatOffset->2) (\$UpdateID +\ + \_1) (\$TelegramChatOffset->2) ]);\r\ + \n} do={\r\ + \n :global ExitError; \$ExitError \$ExitOK [ :jobname ] \$Err;\r\ + \n}\r\ + \n" +add comment="Script send Notifications in Telegram" dont-require-permissions=\ + no name=mod/AM-TG-Notifications owner=mod/AM-TG-Notifications policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + !rsc by Vados\r\ + \n# RouterOS script: mod/AM-TG-Notifications\r\ + \n# Script comment: Script send Notifications in Telegram\r\ + \n# Copyright (c) 2007-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# requires device-mode, fetch, scheduler\r\ + \n:global FlushTelegramQueue;\r\ + \n:global GetTelegramChatId;\r\ + \n:global NotificationFunctions;\r\ + \n:global PurgeTelegramQueue;\r\ + \n:global SendTelegram;\r\ + \n:global SendTelegram2;\r\ + \n# flush telegram queue\r\ + \n:set FlushTelegramQueue do={ :onerror Err {\r\ + \n :global TelegramQueue;\r\ + \n :global TelegramMessageIDs;\r\ + \n :global CertificateAvailable;\r\ + \n :global IsFullyConnected;\r\ + \n :global LogPrint;\r\ + \n :if ([ \$IsFullyConnected ] = false) do={\r\ + \n \$LogPrint debug \$0 (\"System is not fully connected, not flushing.\ + \");\r\ + \n :return false;\r\ + \n }\r\ + \n :if ([ \$CertificateAvailable \"Go Daddy Root Certificate Authority - \ + G2\" \"fetch\" ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Downloading required certificate failed.\"\ + );\r\ + \n :return false;\r\ + \n }\r\ + \n :local AllDone true;\r\ + \n :local QueueLen [ :len \$TelegramQueue ];\r\ + \n :if ([ :len [ /system/scheduler/find where name=\"_FlushTelegramQueue\ + \" ] ] > 0 && \$QueueLen = 0) do={\r\ + \n \$LogPrint warning \$0 (\"Flushing Telegram messages from scheduler,\ + \_but queue is empty.\");\r\ + \n }\r\ + \n :foreach Id,Message in=\$TelegramQueue do={\r\ + \n :if ([ :typeof \$Message ] = \"array\" ) do={\r\ + \n :onerror Err {\r\ + \n :local Data ([ /tool/fetch check-certificate=yes-without-crl out\ + put=user http-method=post \\\r\ + \n (\"https://api.telegram.org/bot\" . (\$Message->\"tokenid\") .\ + \_\"/sendMessage\") \\\r\ + \n http-data=(\$Message->\"http-data\") as-value ]->\"data\");\r\ + \n :set (\$TelegramQueue->\$Id);\r\ + \n :set (\$TelegramMessageIDs->[ :tostr ([ :deserialize from=json v\ + alue=\$Data ]->\"result\"->\"message_id\") ]) 1;\r\ + \n } do={\r\ + \n \$LogPrint debug \$0 (\"Sending queued Telegram message failed: \ + \" . \$Err);\r\ + \n :set AllDone false;\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n :if (\$AllDone = true && \$QueueLen = [ :len \$TelegramQueue ]) do={\r\ + \n /system/scheduler/remove [ find where name=\"_FlushTelegramQueue\" ]\ + ;\r\ + \n :set TelegramQueue;\r\ + \n }\r\ + \n} do={\r\ + \n :global ExitOnError; \$ExitOnError \$0 \$Err;\r\ + \n} }\r\ + \n# get the chat id\r\ + \n:set GetTelegramChatId do={ :onerror Err {\r\ + \n :global TelegramTokenId;\r\ + \n :global CertificateAvailable;\r\ + \n :global LogPrint;\r\ + \n :if ([ \$CertificateAvailable \"Go Daddy Root Certificate Authority - \ + G2\" \"fetch\" ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Downloading required certificate failed.\"\ + );\r\ + \n :return false;\r\ + \n }\r\ + \n :local Data;\r\ + \n :onerror Err {\r\ + \n :set Data ([ /tool/fetch check-certificate=yes-without-crl output=us\ + er \\\r\ + \n (\"https://api.telegram.org/bot\" . \$TelegramTokenId . \"/getUpd\ + ates\?offset=0\" . \\\r\ + \n \"&allowed_updates=%5B%22message%22%5D\") as-value ]->\"data\");\ + \r\ + \n } do={\r\ + \n \$LogPrint warning \$0 (\"Fetching data failed: \" . \$Err);\r\ + \n :return false;\r\ + \n }\r\ + \n :local JSON [ :deserialize from=json value=\$Data ];\r\ + \n :local Count [ :len (\$JSON->\"result\") ];\r\ + \n :if (\$Count = 0) do={\r\ + \n \$LogPrint info \$0 (\"No message received.\");\r\ + \n :return false;\r\ + \n }\r\ + \n :local Message (\$JSON->\"result\"->(\$Count - 1)->\"message\");\r\ + \n \$LogPrint info \$0 (\"The chat id is: \" . (\$Message->\"chat\"->\"id\ + \"));\r\ + \n :if ((\$Message->\"is_topic_message\") = true) do={\r\ + \n \$LogPrint info \$0 (\"The thread id is: \" . (\$Message->\"message_\ + thread_id\"));\r\ + \n }\r\ + \n} do={\r\ + \n :global ExitOnError; \$ExitOnError \$0 \$Err;\r\ + \n} }\r\ + \n# send notification via telegram - expects one array argument\r\ + \n:set (\$NotificationFunctions->\"telegram\") do={\r\ + \n :local Notification \$1;\r\ + \n :global Identity;\r\ + \n :global IdentityExtra;\r\ + \n :global TelegramChatId;\r\ + \n :global TelegramChatIdOverride;\r\ + \n :global TelegramMessageIDs;\r\ + \n :global TelegramQueue;\r\ + \n :global TelegramThreadId;\r\ + \n :global TelegramThreadIdOverride;\r\ + \n :global TelegramTokenId;\r\ + \n :global TelegramTokenIdOverride;\r\ + \n :global CertificateAvailable;\r\ + \n :global CharacterReplace;\r\ + \n :global EitherOr;\r\ + \n :global IfThenElse;\r\ + \n :global LogPrint;\r\ + \n :global ProtocolStrip;\r\ + \n :global SymbolForNotification;\r\ + \n :global UrlEncode;\r\ + \n :local EscapeMD do={\r\ + \n :local Text [ :tostr \$1 ];\r\ + \n :local Mode [ :tostr \$2 ];\r\ + \n :local Excl [ :tostr \$3 ];\r\ + \n :global CharacterReplace;\r\ + \n :global IfThenElse;\r\ + \n :local Chars {\r\ + \n \"body\"={ \"\\\\\"; \"`\" };\r\ + \n \"plain\"={ \"_\"; \"*\"; \"[\"; \"]\"; \"(\"; \")\"; \"~\"; \"`\"\ + ; \">\";\r\ + \n \"#\"; \"+\"; \"-\"; \"=\"; \"|\"; \"{\"; \"}\"; \".\"; \ + \"!\" };\r\ + \n }\r\ + \n :foreach Char in=(\$Chars->\$Mode) do={\r\ + \n :if ([ :typeof [ :find \$Excl \$Char ] ] = \"nil\") do={\r\ + \n :set Text [ \$CharacterReplace \$Text \$Char (\"\\\\\" . \$Char)\ + \_];\r\ + \n }\r\ + \n }\r\ + \n :if (\$Mode = \"body\") do={:return (\"```\\n\" . \$Text . \"\\n```\ + \")}\r\ + \n :return \$Text;\r\ + \n }\r\ + \n :local ChatId [ \$EitherOr (\$Notification->\"chatid\") \\\r\ + \n [ \$EitherOr (\$TelegramChatIdOverride->(\$Notification->\"origin\")\ + ) \$TelegramChatId ] ];\r\ + \n :local ThreadId [ \$EitherOr (\$Notification->\"threadid\") \\\r\ + \n [ \$EitherOr (\$TelegramThreadIdOverride->(\$Notification->\"origin\ + \")) \\\r\ + \n [ \$IfThenElse ([ :len (\$TelegramChatIdOverride->(\$Notification->\ + \"origin\")) ] = 0) \$TelegramThreadId ] ] ];\r\ + \n :local TokenId [ \$EitherOr (\$TelegramTokenIdOverride->(\$Notificatio\ + n->\"origin\")) \$TelegramTokenId ];\r\ + \n :if ([ :len \$TokenId ] = 0 || [ :len \$ChatId ] = 0) do={:return fals\ + e}\r\ + \n :if ([ :typeof \$TelegramMessageIDs ] = \"nothing\") do={:set Telegram\ + MessageIDs ({})}\r\ + \n :local Truncated false;\r\ + \n :local Text (\"*__\" . [ \$EscapeMD (\"[\" . \$IdentityExtra . \$Ident\ + ity . \"] \" . \\\r\ + \n (\$Notification->\"subject\")) \"plain\" ] . \"__*\\n\\n\");\r\ + \n :local LenSubject [ :len \$Text ];\r\ + \n :local LenMessage [ :len (\$Notification->\"message\") ];\r\ + \n :local LenLink ([ :len (\$Notification->\"link\") ] * 2);\r\ + \n :local LenSum (\$LenSubject + \$LenMessage + \$LenLink);\r\ + \n :if (\$LenSum > 3968) do={\r\ + \n :set Text (\$Text . [ \$EscapeMD ([ :pick (\$Notification->\"message\ + \") 0 (3840 - \$LenSubject - \$LenLink) ] . \"...\") \"body\" ]);\r\ + \n :set Truncated true;\r\ + \n } else={:set Text (\$Text . [ \$EscapeMD (\$Notification->\"message\")\ + \_\"body\" ])}\r\ + \n :if (\$LenLink > 0) do={\r\ + \n :set Text (\$Text . \"\\n\" . [ \$SymbolForNotification \"link\" ] .\ + \_\\\r\ + \n \"[\" . [ \$EscapeMD [ \$ProtocolStrip (\$Notification->\"link\") \ + ] \"plain\" ] . \"]\" . \\\r\ + \n \"(\" . [ \$EscapeMD (\$Notification->\"link\") \"plain\" ] . \")\ + \");\r\ + \n }\r\ + \n :if (\$Truncated = true) do={\r\ + \n :set Text (\$Text . \"\\n\" . [ \$SymbolForNotification \"scissors\"\ + \_] . \\\r\ + \n [ \$EscapeMD (\"The message was too long and has been truncated, c\ + ut off _\" . \\\r\ + \n ((\$LenSum - [ :len \$Text ]) * 100 / \$LenSum) . \"%_!\") \"plain\ + \" \"_\" ]);\r\ + \n }\r\ + \n :local HTTPData (\"chat_id=\" . \$ChatId . \"&disable_notification=\" \ + . (\$Notification->\"silent\") . \\\r\ + \n \"&reply_to_message_id=\" . (\$Notification->\"replyto\") . \"&mes\ + sage_thread_id=\" . \$ThreadId . \\\r\ + \n \"&disable_web_page_preview=true&parse_mode=MarkdownV2\");\r\ + \n :onerror Err {\r\ + \n :if ([ \$CertificateAvailable \"Go Daddy Root Certificate Authority \ + - G2\" \"fetch\" ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Downloading required certificate failed.\ + \");\r\ + \n :error false;\r\ + \n }\r\ + \n :local Data ([ /tool/fetch check-certificate=yes-without-crl output=\ + user http-method=post \\\r\ + \n (\"https://api.telegram.org/bot\" . \$TokenId . \"/sendMessage\") \ + \\\r\ + \n http-data=(\$HTTPData . \"&text=\" . [ \$UrlEncode \$Text ]) as-va\ + lue ]->\"data\");\r\ + \n :set (\$TelegramMessageIDs->[ :tostr ([ :deserialize from=json value\ + =\$Data ]->\"result\"->\"message_id\") ]) 1;\r\ + \n } do={\r\ + \n \$LogPrint info \$0 (\"Failed sending Telegram notification: \" . \$\ + Err . \" - Queuing...\");\r\ + \n :if ([ :typeof \$TelegramQueue ] = \"nothing\") do={:set TelegramQue\ + ue ({})}\r\ + \n :set Text (\$Text . \"\\n\" . [ \$SymbolForNotification \"alarm-cloc\ + k\" ] . \\\r\ + \n [ \$EscapeMD (\"This message was queued since _\" . [ /system/cloc\ + k/get date ] . \\\r\ + \n \" \" . [ /system/clock/get time ] . \"_ and may be obsolete.\") \ + \"plain\" \"_\" ]);\r\ + \n :set (\$TelegramQueue->[ :len \$TelegramQueue ]) { tokenid=\$TokenId\ + ;\r\ + \n http-data=(\$HTTPData . \"&text=\" . [ \$UrlEncode \$Text ]) };\r\ + \n :if ([ :len [ /system/scheduler/find where name=\"_FlushTelegramQueu\ + e\" ] ] = 0) do={\r\ + \n /system/scheduler/add name=\"_FlushTelegramQueue\" interval=1m sta\ + rt-time=startup \\\r\ + \n on-event=(\":global FlushTelegramQueue; \\\$FlushTelegramQueue;\ + \");\r\ + \n }\r\ + \n }\r\ + \n}\r\ + \n# purge the Telegram queue\r\ + \n:set PurgeTelegramQueue do={\r\ + \n :global TelegramQueue;\r\ + \n /system/scheduler/remove [ find where name=\"_FlushTelegramQueue\" ];\ + \r\ + \n :set TelegramQueue;\r\ + \n}\r\ + \n# send notification via telegram - expects at least two string arguments\ + \r\ + \n:set SendTelegram do={ :onerror Err {\r\ + \n :global SendTelegram2;\r\ + \n \$SendTelegram2 ({ origin=\$0; subject=\$1; message=\$2; link=\$3; sil\ + ent=\$4 });\r\ + \n} do={\r\ + \n :global ExitOnError; \$ExitOnError \$0 \$Err;\r\ + \n} }\r\ + \n# send notification via telegram - expects one array argument\r\ + \n:set SendTelegram2 do={\r\ + \n :local Notification \$1;\r\ + \n :global NotificationFunctions;\r\ + \n (\$NotificationFunctions->\"telegram\") (\"\\\$NotificationFunctions->\ + \\\"telegram\\\"\") \$Notification;\r\ + \n}\r\ + \n" +add dont-require-permissions=no name=testIfThenElseAndEitherOr owner=vados \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + source=":local ExitOK false;\r\ + \n:local ScriptName [ :jobname ];\r\ + \n\r\ + \n:local ExitError;\r\ + \n#:local IfThenElse;\r\ + \n#:local EitherOr;\r\ + \n\r\ + \n:local one 11;\r\ + \n:local two 8;\r\ + \n#:log warning ([ \$IfThenElse (\$one != 0) \$one \$two ]);\r\ + \n:local Test; # [ :totime \"22s\" ];\r\ + \n\r\ + \n:local IfThenElse do={:if ([ :tostr \$1 ] = \"true\" || [ :tobool \$1 ] \ + = true) do={:return \$2}; :return \$3;}\r\ + \n:local EitherOr do={\r\ + \n :if ([ :typeof \$1 ] = \"num\") do={:if ((\$1 != 0) = true) do={:retur\ + n \$1} else={:return \$2};}\r\ + \n :if ([ :typeof \$1 ] = \"time\") do={:if ((\$1 > 0s) = true) do={:retu\ + rn \$1} else={:return \$2};}\r\ + \n :if ([ :len [ :tostr \$1 ] ] > 0) do={:return \$1} else={:return \$2};\ + \r\ + \n}\r\ + \n\r\ + \n:log warning ([ \$IfThenElse (\$one != 0) \$one \$two ]);\r\ + \n\r\ + \n:set Test [ \$EitherOr \$Test \"\" ];\r\ + \n:log warning (\"\\\$ Test = \" . \$Test);\r\ + \n\r\ + \n:set ExitError do={\r\ + \n :local ExitOK [ :tostr \$1 ];\r\ + \n :local Name [ :tostr \$2 ];\r\ + \n :local Error [ :tostr \$3 ];\r\ + \n :local IfThenElse;\r\ + \n :if (\$ExitOK = \"false\") do={\r\ + \n :log error (\$Name [ \$IfThenElse ([ :pick \$Name 0 1 ] = \"\\\$\") \ + \\\r\ + \n \"Function\" \"Script\" ] . \" '\" . \$Name . \"' exited with er\ + ror\" . \\\r\ + \n [ \$IfThenElse (!(\$Error ~ \"^(|true|false)\\\$\")) (\": \" . \ + \$Error) \".\" ]);\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n\r\ + \n#:log warning ([ \$IfThenElse (\$one != 0) \$one \$two ]);\r\ + \n\r\ + \n:set ExitOK true;\r\ + \n\$ExitError \$ExitOK \$ScriptName \"Err test custom\";" +add comment="Update and Backup send notification to EMail" \ + dont-require-permissions=no name=AM-Backup-UpdateEmail owner=\ + AM-Backup-UpdateEmail policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + !rsc by Vados\r\ + \n# RouterOS script: AM-Backup-UpdateEmail\r\ + \n# Script comment: Update and Backup send notification to EMail\r\ + \n# Copyright (c) 2007-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version>=6.43.7\r\ + \n# requires device-mode, fetch\r\ + \n#\r\ + \n# Updated: 15/04/2025\r\ + \n# Website: https://github.com/beeyev\r\ + \n# Notification e-mail (Make sure you have configured Email settings in T\ + ools -> Email)\r\ + \n:local ExitOK false;\r\ + \n:onerror Err {\r\ + \n :global GlobalConfReady; :global GlobalFuncReady;\r\ + \n :retry { :if (\$GlobalConfReady != true || \$GlobalFuncReady != true) \ + \\\r\ + \n do={ :error (\"Global config and/or functions not ready.\"); }; } \ + delay=500ms max=50;\r\ + \n :local ScriptName [ :jobname ];\r\ + \n\r\ + \n:local IfThenElse do={:if ([ :tostr \$1 ] = \"true\" || [ :tobool \$1 ] \ + = true) do={:return \$2}; :return \$3;}\r\ + \n:local EitherOr do={\r\ + \n :if ([ :typeof \$1 ] = \"num\") do={:if ((\$1 != 0) = true) do={:retur\ + n \$1} else={:return \$2};}\r\ + \n :if ([ :typeof \$1 ] = \"time\") do={:if ((\$1 > 0s) = true) do={:retu\ + rn \$1} else={:return \$2};}\r\ + \n :if ([ :len [ :tostr \$1 ] ] > 0) do={:return \$1} else={:return \$2};\ + \r\ + \n}\r\ + \n:local ExitError do={\r\ + \n :local ExitOK [ :tostr \$1 ];\r\ + \n :local Name [ :tostr \$2 ];\r\ + \n :local Error [ :tostr \$3 ];\r\ + \n :if (\$ExitOK = \"false\") do={:log error (\$Name [ \$IfThenElse ([ :p\ + ick \$Name 0 1 ] = \"\\\$\") \\\r\ + \n \"Function\" \"Script\" ] . \" '\" . \$Name . \"' exited with er\ + ror\" . \\\r\ + \n [ \$IfThenElse (!(\$Error ~ \"^(|true|false)\\\$\")) (\": \" . \ + \$Error) \".\" ]);}\r\ + \n}\r\ + \n\r\ + \n:global LogPrint;\r\ + \n:global EditSchedulers;\r\ + \n:global DevInfoIncludeIP;\r\ + \n\r\ + \n:local SchedInterval \"7d\";\r\ + \n:local SchedStartTime \"03:41:27\";\r\ + \n:local SchedComment \"Every 7 Day Check For Update and Backup send notif\ + ication to EMail.\";\r\ + \n\r\ + \n# Function to add or edit schedulers for scripts, to ensure they are set\ + \_up correctly\r\ + \n# How to use:\r\ + \n# Call this function with the following parameters:\r\ + \n# 1. \$1 - Scheduler name (usually same as script name) [*]\r\ + \n# 2. \$2 - Interval (e.g. \"1d\" (default is \"1d\")) [**]\r\ + \n# 3. \$3 - Start Time (e.g. \"00:00:00\", default is current time) [**]\ + \r\ + \n# 4. \$4 - Comment (optional, default: \"Scheduler for script: \")\ + \_[**]\r\ + \n# 5. \$5 - On Event command (e.g. \"/system/script { run ; }\" (de\ + fault is \"/system/script { run \$SchedName; }\")) [**]\r\ + \n# 6. \$6 - Policy (e.g. \"ftp,reboot,read,write,policy,test,password,sni\ + ff,sensitive,romon\" (default is the same)) [**]\r\ + \n# 7. \$7 - Start Date (e.g. \"jan/01/1970\", default is current date) [*\ + *]\r\ + \n# Notes:\r\ + \n# * - Scheduler name is required, and is used to identify the scheduler\ + \_to create or edit. It is usually the same as the script name (\$ScriptNa\ + me).\r\ + \n# ** - If optional parameters are not provided, default values will be u\ + sed.\r\ + \n# This allows for quick setup with minimal parameters, while still \ + providing flexibility for customization.\r\ + \n# Example usage:\r\ + \n# :if ([ \$EditSchedulers \"my-script\" ] = false) do={:set ExitOK false\ + ; :error true;}\r\ + \n# :if ([ \$EditSchedulers \"my-script\" \"1d\" \"03:41:27\" \"My custom \ + scheduler comment\" false false \"jan/01/1970\" ] = false) do={:set ExitOK\ + \_false; :error true;}\r\ + \n\r\ + \n:set EditSchedulers do={\r\ + \n :local SchedName [ :tostr \$1 ];\r\ + \n :if ([ :len \$SchedName ] = 0) do={ :return false; }\r\ + \n :local Interval [ :tostr \$2 ];\r\ + \n :if ([ :len \$Interval ] = 0) do={ :set Interval \"1d\"; }\r\ + \n :local StartTime [ :tostr \$3 ];\r\ + \n :if ([ :len \$StartTime ] = 0) do={ :set StartTime [/system/clock/get \ + time]; }\r\ + \n :local Comment [ :tostr \$4 ];\r\ + \n :if ([ :len \$Comment ] = 0) do={ :set Comment \"Scheduler for script:\ + \_\$SchedName\"; }\r\ + \n :local OnEvent [ :tostr \$5 ];\r\ + \n :if ([ :len \$OnEvent ] = 0) do={ :set OnEvent \"/system/script { run \ + \$SchedName; }\"; }\r\ + \n :local Policy [ :tostr \$6 ];\r\ + \n :if ([ :len \$Policy ] = 0) do={ :set Policy \"ftp,reboot,read,write,p\ + olicy,test,password,sniff,sensitive,romon\"; }\r\ + \n :local StartDate [ :tostr \$7 ];\r\ + \n :if ([ :len \$StartDate ] = 0) do={ :set StartDate [/system/clock/get \ + date]; }\r\ + \n\r\ + \n :onerror Err {\r\ + \n # Check if scheduler exists, if not - create it\r\ + \n :if ([ :len [ /system/scheduler/find where name=\$SchedName ] ] = 0)\ + \_do={\r\ + \n :log warning (\$SchedName . \"System Scheduler for script: \" . \$\ + SchedName . \" is NOT SET! Fix It Now!\");\r\ + \n /system/scheduler/add comment=\$Comment interval=\$Interval name=\ + \$SchedName on-event=\$OnEvent policy=\$Policy start-date=\$StartDate star\ + t-time=\$StartTime;\r\ + \n }\r\ + \n # Check if scheduler parameters are correct, if not - fix them\r\ + \n :if ([ /system/scheduler/get \$SchedName comment ] != \$Comment) do=\ + {\r\ + \n /system/scheduler/set \$SchedName comment=\$Comment;\r\ + \n }\r\ + \n :if ([ /system/scheduler/get \$SchedName interval ] != \$Interval) d\ + o={\r\ + \n /system/scheduler/set \$SchedName interval=\$Interval;\r\ + \n }\r\ + \n :if ([ /system/scheduler/get \$SchedName on-event ] != \$OnEvent) do\ + ={\r\ + \n /system/scheduler/set \$SchedName on-event=\$OnEvent;\r\ + \n }\r\ + \n :if ([ /system/scheduler/get \$SchedName policy ] != \$Policy) do={\ + \r\ + \n /system/scheduler/set \$SchedName policy=\$Policy;\r\ + \n }\r\ + \n :if ([ /system/scheduler/get \$SchedName start-date ] != \$StartDate\ + ) do={\r\ + \n /system/scheduler/set \$SchedName start-date=\$StartDate;\r\ + \n }\r\ + \n :if ([ /system/scheduler/get \$SchedName start-time ] != \$StartTime\ + ) do={\r\ + \n /system/scheduler/set \$SchedName start-time=\$StartTime;\r\ + \n }\r\ + \n } do={:log error (\$SchedName . \"Failed to add/modify scheduler for '\ + \" . \$SchedName . \"' failed: \" . \$Err); :return false};\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:if ([ \$EditSchedulers \$ScriptName \$SchedInterval \$SchedStartTime \$\ + SchedComment ] = false) do={\r\ + \n :log error (\$ScriptName . \"Failed to add/modify scheduler for \" . \ + \$ScriptName . \"!\");\r\ + \n :set ExitOK true;\r\ + \n :error true;\r\ + \n}\r\ + \n\r\ + \n# Local Function CheckOSUpdateMode\r\ + \n:local CheckOSUpdateMode;\r\ + \n:set CheckOSUpdateMode do={\r\ + \n :local NewMode [ :tostr \$1 ];\r\ + \n :if ([ :len \$NewMode ] = 0) do={\r\ + \n :log error (\$0 . \"/nFunction /\$CheckOSUpdateMode received empty v\ + alue!\");\r\ + \n :return false;\r\ + \n :error true;\r\ + \n }\r\ + \n\r\ + \n :global BackupAndUpdateMode;\r\ + \n :global OldMode [ :tostr \$BackupAndUpdateMode ];\r\ + \n :if (\$NewMode != \$BackupAndUpdateMode && \$NewMode = \"updateOnce\")\ + \_do={\r\ + \n :set BackupAndUpdateMode [ :tostr \$NewMode ];\r\ + \n :log info (\$0 . \"/nOk. /\$BackupAndUpdateMode set to: '\" . \$NewM\ + ode . \"'.\\nYou're a good boy! ))\\nI'm launching the update in 5 seconds\ + .\");\r\ + \n :delay 5;\r\ + \n /system/script { run \$ScriptName; };\r\ + \n } else={\r\ + \n :log warning (\$0 . \"/nFunction /\$CheckOSUpdateMode received value: \ + '\" . \$NewMode . \"', but is may be only \\\"updateOnce\\\"!\");\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n :error false;\r\ + \n}\r\ + \n\r\ + \n# :if ([ :len [ /system/scheduler/find where name=\"running-from-backup\ + -partition\" ] ] > 0) do={\r\ + \n# \$LogPrint warning \$ScriptName (\"Running from backup partition, r\ + efusing to act.\");\r\ + \n# :set ExitOK true;\r\ + \n# :error false;\r\ + \n# }\r\ + \n\r\ + \n:global EmailGeneralTo;\r\ + \n:global BackupAndUpdateMode;\r\ + \n:global forceBackup;\r\ + \n:global BackupPassword;\r\ + \n:global BackupSens;\r\ + \n:global updateChannel;\r\ + \n\r\ + \n:global DevInfoIncludeIP;\r\ + \n\r\ + \n# Installs patch updates only (BackupAndUpdateMode = \"osupdate\").\r\ + \n# Works for `stable` and `long-term` channels.\r\ + \n# Updates only if MAJOR.MINOR match (e.g. 6.43.2 > 6.43.6 allowed, 6.44.\ + 1 skipped).\r\ + \n# Sends info if a newer (non-patch) version is found.\r\ + \n:local installOnlyPatchUpdates false;\r\ + \n:local scriptVersion \"26.04.30\";\r\ + \n:local copyrightUrl \"https://www.github.com/vados-dev/\";\r\ + \n#Script messages prefix\r\ + \n:local SMP \"BkpAndUpdate:\";\r\ + \n\r\ + \n:local exitErrorMessage \"\$SMP script stopped due to an error. Please c\ + heck logs for more details.\";\r\ + \n:log info \"\\n\\n\$SMP Script \\\"Mikrotik RouterOS automatic backup & \ + update\\\" v.\$scriptVersion started.\";\r\ + \n:log info \"\$SMP Script Mode: `\$BackupAndUpdateMode`, Update channel: \ + `\$updateChannel`, Force backup: `\$forceBackup`, Install only patch updat\ + es: `\$installOnlyPatchUpdates`\";\r\ + \n\r\ + \n## vv FUNCTIONS vv ##\r\ + \n:global WaitCheckUpdates; \$WaitCheckUpdates;\r\ + \n:global MiniDateTimeStamp;\r\ + \n:local MDTSFix [\$MiniDateTimeStamp];\r\ + \n:global Identity;\r\ + \n:global IdentityShort;\r\ + \n:global devBoardName;\r\ + \n:global runningVersion;\r\ + \n:global runningChannel;\r\ + \n:global devModel;\r\ + \n:global devRbSerialNumber;\r\ + \n:global ROsVerAvail;\r\ + \n:global PkgUpdStatus;\r\ + \n\r\ + \n# Checks if two RouterOS version strings differ only by the patch versio\ + n\r\ + \n# :put [\$FuncIsPatchUpdateOnly \"6.2.1\" \"6.2.4\"] # Output: true\r\ + \n# :put [\$FuncIsPatchUpdateOnly \"6.2.1\" \"6.3.1\"] # Output: false\r\ + \n:local FuncIsPatchUpdateOnly do={\r\ + \n :local ver1 \$1;\r\ + \n :local ver2 \$2;\r\ + \n # Extract the major and minor components from a version\r\ + \n :local extractMajorMinor do={\r\ + \n :local ver \$1;\r\ + \n :local dot1 [:find \$ver \".\"];\r\ + \n :if (\$dot1 = -1) do={:return \$ver}\r\ + \n :local major [:pick \$ver 0 \$dot1];\r\ + \n :local rest [:pick \$ver (\$dot1 + 1) [:len \$ver]];\r\ + \n :local dot2 [:find \$rest \".\"];\r\ + \n :local minor \$rest;\r\ + \n :if (\$dot2 >= 0) do={:set minor [:pick \$rest 0 \$dot2]}\r\ + \n :return (\$major . \".\" . \$minor);\r\ + \n }\r\ + \n\r\ + \n# Compare the major and minor components of both version strings\r\ + \n:if ([\$extractMajorMinor \$ver1] = [\$extractMajorMinor \$ver2]) do={:r\ + eturn true}\r\ + \n :return false;\r\ + \n}\r\ + \n# Creates backups and returns array of names\r\ + \n# Possible arguments:\r\ + \n# \$1 - file name, without extension\r\ + \n# \$2 - password (optional)\r\ + \n# \$3 - sensitive data in config (optional, default: false)\r\ + \n# Example:\r\ + \n#:put [\$FuncCreateBackups \$backupName]\r\ + \n:local FuncCreateBackups do={\r\ + \n :local backupName [ :tostr \$1 ];\r\ + \n :local BackPass \$2;\r\ + \n :local BackSens \$3;\r\ + \n\r\ + \n #Script messages prefix\r\ + \n :local SMP \"BkpAndUpdate:\";\r\ + \n :local exitErrorMessage \"\$SMP script stopped due to an error. Please\ + \_check logs for more details.\";\r\ + \n :log info (\"\$SMP global function `FuncCreateBackups` started, input:\ + \_`\$backupName`\");\r\ + \n\r\ + \n # validate required parameter: backupName\r\ + \n :if ([:typeof \$backupName] != \"str\" or [:len \$backupName] = 0) do=\ + {\r\ + \n :log error \"\$SMP parameter 'backupName' is required and must be a \ + non-empty string\";\r\ + \n# :log warning \"\$SMP parameter '\\\$backupName' is required and mus\ + t be a non-empty string.\\nI set '\\\$backupName' to 'default-backup'\";\r\ + \n# :set \$backupName \"default-backup\";\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n\r\ + \n :local backupFileSys \"\$backupName.backup\";\r\ + \n :local backupFileConfig \"\$backupName.rsc\";\r\ + \n :local backupNames {\$backupFileSys;\$backupFileConfig};\r\ + \n\r\ + \n ## Perform system backup\r\ + \n :if ([:len \$BackPass] = 0) do={\r\ + \n :log info (\"\$SMP starting backup without password, backup name: `\ + \$backupName`\");\r\ + \n /system backup save dont-encrypt=yes name=\$backupName;\r\ + \n } else={\r\ + \n :log info (\"\$SMP starting backup with password, backup name: `\$ba\ + ckupName`\");\r\ + \n /system backup save password=\$BackPass name=\$backupName;\r\ + \n }\r\ + \n :log info (\"\$SMP system backup created: `\$backupFileSys`\");\r\ + \n ## Export config file\r\ + \n :if (\$BackSens = true) do={\r\ + \n :log info (\"\$SMP starting export config with sensitive data, backu\ + p name: `\$backupName`\");\r\ + \n # Since RouterOS v7 it needs to be explicitly set that we want to ex\ + port sensitive data\r\ + \n :if ([:pick [/system resource get version] 0 1] < 7) do={\r\ + \n :execute \"/export compact terse file=\$backupName\";\r\ + \n } else={\r\ + \n :execute \"/export compact show-sensitive terse file=\$backupName\ + \";\r\ + \n }\r\ + \n } else={\r\ + \n :log info (\"\$SMP starting export config without sensitive data, ba\ + ckup name: `\$backupName`\");\r\ + \n /export compact hide-sensitive terse file=\$backupName;\r\ + \n }\r\ + \n :log info (\"\$SMP Config export complete: `\$backupFileConfig`\");\r\ + \n :log info (\"\$SMP Waiting a little to ensure backup files are written\ + \");\r\ + \n :delay 20;\r\ + \n :if ([:len [/file find name=\$backupFileSys]] > 0) do={\r\ + \n :log info (\"\$SMP system backup file successfully saved to the file\ + \_system: `\$backupFileSys`\");\r\ + \n } else={\r\ + \n :log error (\"\$SMP system backup was not created, file does not exi\ + st: `\$backupFileSys`\");\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n :if ([:len [/file find name=\$backupFileConfig]] > 0) do={\r\ + \n :log info (\"\$SMP config backup file successfully saved to the file\ + \_system: `\$backupFileConfig`\");\r\ + \n } else={\r\ + \n :log error (\"\$SMP config backup was not created, file does not exi\ + st: `\$backupFileConfig`\");\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n :log info (\"\$SMP global function `FuncCreateBackups` finished. Creat\ + ed backups, system: `\$backupFileSys`, config: `\$backupFileConfig`\")\r\ + \n :return \$backupNames;\r\ + \n}\r\ + \n# Sends an email\r\ + \n# Parameters:\r\ + \n# \$1 - to (email address)\r\ + \n# \$2 - subject\r\ + \n# \$3 - body\r\ + \n# \$4 - file attachments (optional; pass \"\" if not needed)\r\ + \n#\r\ + \n# Example:\r\ + \n# \$FuncSendEmailSafe \"admin@domain.com\" \"Backup Done\" \"Backup comp\ + lete.\" \"backup1.backup\"\r\ + \n:local FuncSendEmailSafe do={\r\ + \n :global EmailGeneralTo;\r\ + \n :local emailSubject \$2;\r\ + \n :local emailBody \$3;\r\ + \n :local emailAttachments \$4;\r\ + \n :local SMP \"Bkp&Upd:\";\r\ + \n :local exitErrorMessage \"\$SMP script stopped due to an error. Please\ + \_check logs for more details.\";\r\ + \n :log info \"\$SMP Attempting to send email to `\$EmailGeneralTo`\";\r\ + \n # SAFETY: wait for any previously queued email to finish\r\ + \n :local waitTimeoutPre 60;\r\ + \n :local waitCounterPre 0;\r\ + \n :while (([/tool e-mail get last-status] = \"resolving-dns\" or [/tool \ + e-mail get last-status] = \"in-progress\")) do={\r\ + \n :if (\$waitCounterPre >= \$waitTimeoutPre) do={\r\ + \n :log error \"\$SMP Email send aborted: previous send did not compl\ + ete after \$waitTimeoutPre seconds\";\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n :log info \"\$SMP Waiting for previous email to finish (status: \$[/\ + tool e-mail get last-status])...\";\r\ + \n :delay 1; \r\ + \n :set waitCounterPre (\$waitCounterPre + 1);\r\ + \n }\r\ + \n # Send the email\r\ + \n :do {\r\ + \n /tool e-mail send to=\$EmailGeneralTo subject=\$emailSubject body=\$\ + emailBody file=\$emailAttachments;\r\ + \n } on-error={\r\ + \n :log error \"\$SMP Email send command failed to execute. Check logs \ + and verify email settings.\";\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n # Wait for send status to change from \"in-progress\" / \"resolving-dn\ + s\"\r\ + \n :local waitTimeout 60;\r\ + \n :local waitCounter 0;\r\ + \n :local emailStatus \"\";\r\ + \n :log info \"\$SMP Waiting for email to be sent, timeout in `\$waitTime\ + out` seconds...\";\r\ + \n :while (\$waitCounter < \$waitTimeout) do={\r\ + \n :set emailStatus [/tool e-mail get last-status];\r\ + \n :if (\$emailStatus != \"in-progress\" and \$emailStatus != \"resolvi\ + ng-dns\") do={\r\ + \n :log info \"\$SMP Email send status received: \$emailStatus\";\r\ + \n # exit loop\r\ + \n :set waitCounter \$waitTimeout;\r\ + \n } else={:delay 1; :set waitCounter (\$waitCounter + 1)}\r\ + \n }\r\ + \n # Final decision based on last status\r\ + \n :if (\$emailStatus = \"succeeded\") do={\r\ + \n :log info \"\$SMP Email successfully sent to `\$EmailGeneralTo`\";\ + \r\ + \n } else={\r\ + \n :log error \"\$SMP Email failed to send. Status: `\$emailStatus`. Ch\ + eck logs for more details and verify email settings.\";\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n}\r\ + \n# Global variable to track current update step\r\ + \n# They need to be initialized here first to be available in the script\r\ + \n:global buGlobalVarTargetOsVersion;\r\ + \n:global buGlobalVarScriptStep;\r\ + \n:local scriptStep \$buGlobalVarScriptStep;\r\ + \n:do {\r\ + \n /system/script/environment remove buGlobalVarScriptStep;\r\ + \n} on-error={}\r\ + \n:if ([:len \$scriptStep] = 0) do={\r\ + \n :set scriptStep 1;\r\ + \n}\r\ + \n## ^^ FUNCTIONS ^^ ##\r\ + \n#\r\ + \n# Initial validation\r\ + \n## Check email settings\r\ + \n:if ([:len \$EmailGeneralTo] < 3) do={\r\ + \n :log error (\"\$SMP Parameter `\\\$EmailGeneralTo` is not set, or cont\ + ains invalid value. Script stopped.\");\r\ + \n :error \$exitErrorMessage;\r\ + \n}\r\ + \n# Values will be defined later in the script\r\ + \n:local emailServer \"\";\r\ + \n:local emailFromAddress [/tool e-mail get from];\r\ + \n:log info \"\$SMP Validating email settings...\";\r\ + \n:do {\r\ + \n :set emailServer [/tool e-mail get server];\r\ + \n} on-error={\r\ + \n # This is a workaround for the RouterOS v7.12 and older versions\r\ + \n :set emailServer [/tool e-mail get address];\r\ + \n}\r\ + \n:if (\$emailServer = \"0.0.0.0\") do={\r\ + \n :log error (\"\$SMP Email server address is not correct: `\$emailServe\ + r`, check `Tools -> Email`. Script stopped.\");\r\ + \n :error \$exitErrorMessage;\r\ + \n}\r\ + \n:if ([:len \$emailFromAddress] < 3) do={\r\ + \n :log error (\"\$SMP Email configuration FROM address is not correct: `\ + \$emailFromAddress`, check `Tools -> Email`. Script stopped.\");\r\ + \n :error \$exitErrorMessage;\r\ + \n}\r\ + \n# Script mode validation\r\ + \n:if (\$BackupAndUpdateMode != \"backup\" and \$BackupAndUpdateMode != \"\ + osupdate\" and \$BackupAndUpdateMode != \"updateOnce\" and \$BackupAndUpda\ + teMode != \"osnotify\") do={\r\ + \n :log error (\"\$SMP Script parameter `\\\$BackupAndUpdateMode` is not \ + set, or contains invalid value: `\$BackupAndUpdateMode`. Script stopped.\"\ + );\r\ + \n :error \$exitErrorMessage;\r\ + \n}\r\ + \n# Update channel validation\r\ + \n:if (\$updateChannel != \"stable\" and \$updateChannel != \"long-term\" \ + and \$updateChannel != \"testing\" and \$updateChannel != \"development\")\ + \_do={\r\ + \n :log error (\"\$SMP Script parameter `\\\$updateChannel` is not set, o\ + r contains invalid value: `\$updateChannel`. Script stopped.\");\r\ + \n :error \$exitErrorMessage;\r\ + \n}\r\ + \n# Verify if script is set to install patch updates and if the update cha\ + nnel is valid\r\ + \n:if ((\$BackupAndUpdateMode = \"osupdate\" or \$BackupAndUpdateMode = \"\ + updateOnce\") and \$installOnlyPatchUpdates = true) do={\r\ + \n :if (\$updateChannel != \"stable\" and \$updateChannel != \"long-term\ + \") do={\r\ + \n :log error (\"\$SMP Patch-only updates enabled, but update channel `\ + \$updateChannel` is invalid. Only `stable` and `long-term` are supported. \ + Script stopped\");\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n \r\ + \n :if (\$runningChannel != \"stable\" and \$runningChannel != \"long-ter\ + m\") do={\r\ + \n :log error (\"\$SMP Script is set to install only patch updates, but\ + \_the installed RouterOS version is not from `stable` or `long-term` chann\ + el: `\$runningChannel`. Script stopped\");\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n}\r\ + \n#\r\ + \n\r\ + \n:local rawTime [/system clock get time];\r\ + \n:local rawDate [/system clock get date];\r\ + \n\r\ + \n:local deviceOsVerAndChannelRunning [/system/resource/get version];\r\ + \n\r\ + \n:local backupNameTemplate (\"backup_v\" . \$runningVersion . \"_\" .\ + \_\$runningChannel . \"_\" . \$MDTSFix);\r\ + \n:local backupNameBeforeUpdate (\$backupNameTemplate . \"_before_update\"\ + );\r\ + \n:local backupNameAfterUpdate (\$backupNameTemplate . \"_after_update\")\ + ;\r\ + \n\r\ + \n## Email body template\r\ + \n:local mailSubjectPrefix \"\$SMP Device - `\$IdentityShort`\";\r\ + \n:local mailBodyCopyright \"Mikrotik RouterOS automatic backup & update \ + (ver. \$scriptVersion) \\n\$copyrightUrl\";\r\ + \n:local changelogUrl \"Check RouterOS changelog: https://mikrotik.com\ + /download/changelogs/\";\r\ + \n:local mailBodyDeviceInfo \"\";\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"Device information:\")\ + ;\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\n-------------------\ + --\");\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nName: \$Identity\")\ + ;\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nModel: \$devModel\"\ + );\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nBoard: \$devBoardNa\ + me\");\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nSerial number: \$de\ + vRbSerialNumber\");\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nRouterOS version: v\ + \$deviceOsVerAndChannelRunning\");\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nBuild time: \$[/sys\ + tem/resource/get build-time]\");\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nRouterboard FW Avai\ + lable: \$ROsVerAvail\");\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nDevice date-time: \ + \$rawDate \$rawTime (\$[/system/clock/get time-zone-name ])\");\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nUptime: \$[/system/\ + resource/get uptime]\");\r\ + \n## IP address detection\r\ + \n:if ([ \$DevInfoIncludeIP ] = true) do={\r\ + \n :global GetPubIp;\r\ + \n :local PublicIpAddress [ \$GetPubIp ];\r\ + \n :set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nPublic IP address\ + : \" . \$PublicIpAddress . \".\");\r\ + \n :log info \"\$SMP Public IP address detected: \$PublicIpAddress\";\r\ + \n} \r\ + \n\r\ + \n:local mailAttachments [:toarray \"\"];\r\ + \n:if (\$scriptStep = 1 or \$scriptStep = 3) do={\r\ + \n :if (\$scriptStep = 3) do={\r\ + \n :log info (\"\$SMP Waiting for one minute before continuing to the f\ + inal step.\");\r\ + \n :delay 1m;\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n## STEP 1: Create backups, check for new RouterOS, and send email\r\ + \n## Steps 2\C3\A23 run only if auto-update is enabled and a new version i\ + s available\r\ + \n:if (\$scriptStep = 1) do={\r\ + \n :global BackupPassword;\r\ + \n :global BackupSens;\r\ + \n :local isNewOsUpdateAvailable false;\r\ + \n :local isLatestOsAlreadyInstalled true;\r\ + \n :local isOsNeedsToBeUpdated false;\r\ + \n :local isUpdateCheckSucceeded false;\r\ + \n :local isEmailNeedsToBeSent false;\r\ + \n :local mailSubjectPartAction \"\";\r\ + \n :local mailPtBodyAction \"\";\r\ + \n :local mailPtSubjectBackup \"\";\r\ + \n :local mailPtBodyBackup \"\";\r\ + \n # Checking for new version\r\ + \n :if (\$BackupAndUpdateMode = \"osupdate\" or \$BackupAndUpdateMode = \ + \"osnotify\" or \$BackupAndUpdateMode = \"updateOnce\") do={\r\ + \n :log info (\"\$SMP Setting update channel to `\$updateChannel`\");\r\ + \n /system/package/update/set channel=\$updateChannel;\r\ + \n :log info (\"\$SMP Checking for new RouterOS version. Current instal\ + led version is: `\$runningVersion`\");\r\ + \n \$LogPrint debug \$ScriptName (\"Checking for updates...\");\r\ + \n \r\ + \n# /system/package/update/check-for-updates without-paging as-value;\ + \r\ + \n# /system/package/update check-for-updates;\r\ + \n :delay 5s;\r\ + \n :set PkgUpdStatus [ /system/package/update/get status ];\r\ + \n :if (\$PkgUpdStatus = \"New version is available\") do={\r\ + \n :set ROsVerAvail [/system/package/update/get latest-version];\r\ + \n }\r\ + \n\r\ + \n :if (\$PkgUpdStatus = \"New version is available\") do={\r\ + \n :log info (\"\$SMP New RouterOS version is available: `\$ROsVerAva\ + il`\");\r\ + \n :set isNewOsUpdateAvailable true;\r\ + \n :set isLatestOsAlreadyInstalled false;\r\ + \n :set isUpdateCheckSucceeded true;\r\ + \n :set isEmailNeedsToBeSent true;\r\ + \n :set mailSubjectPartAction \"New RouterOS available\";\r\ + \n :set mailPtBodyAction \"New RouterOS version is available, curren\ + t version: v\$runningVersion, new version: v\$ROsVerAvail. \\n\$changelogU\ + rl\";\r\ + \n } else={\r\ + \n :if (\$PkgUpdStatus = \"System is already up to date\") do={\r\ + \n :log info (\"\$SMP No new RouterOS version is available, the lat\ + est version is already installed: `v\$runningVersion`\");\r\ + \n :set isUpdateCheckSucceeded true;\r\ + \n :set mailSubjectPartAction \"No os update available\";\r\ + \n :set mailPtBodyAction \"No new RouterOS version is available, t\ + he latest version is already installed: `v\$runningVersion`\";\r\ + \n } else={\r\ + \n :log error (\"\$SMP Failed to check for new RouterOS version. Pa\ + ckage check status: `\$PkgUpdStatus`\");\r\ + \n :set isEmailNeedsToBeSent true;\r\ + \n :set mailSubjectPartAction \"Error unable to check new os versio\ + n\";\r\ + \n :set mailPtBodyAction \"An error occurred while checking for a \ + new RouterOS version.\\nStatus returned: `\$PkgUpdStatus`\\n\\nPlease revi\ + ew the logs on the device for more details and verify internet connectivit\ + y.\";\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n # Checking if the script needs to install new os version\r\ + \n :if ((\$BackupAndUpdateMode = \"osupdate\" or \$BackupAndUpdateMode = \ + \"updateOnce\") and \$isNewOsUpdateAvailable = true) do={\r\ + \n :if (\$installOnlyPatchUpdates = true) do={\r\ + \n :if ([\$FuncIsPatchUpdateOnly \$runningVersion \$ROsVerAvail] = tr\ + ue) do={\r\ + \n :log info \"\$SMP New RouterOS version is available, and it is a\ + \_patch update. Current version: v\$runningVersion, new version: v\$ROsVer\ + Avail\";\r\ + \n :set isOsNeedsToBeUpdated true;\r\ + \n } else={\r\ + \n :log info \"\$SMP The script will not install this update, becau\ + se it is not a patch update. Current version: v\$runningVersion, new versi\ + on: v\$ROsVerAvail\";\r\ + \n :set mailPtBodyAction (\$mailPtBodyAction . \"\\nThis update wil\ + l not be installed, because the script is set to install only patch update\ + s.\");\r\ + \n }\r\ + \n } else={\r\ + \n :set isOsNeedsToBeUpdated true;\r\ + \n }\r\ + \n }\r\ + \n # Checking If the script needs to create a backup\r\ + \n :if (\$forceBackup = true or \$BackupAndUpdateMode = \"backup\" or \$i\ + sOsNeedsToBeUpdated = true) do={\r\ + \n :log info (\"\$SMP Starting backup process.\");\r\ + \n :set isEmailNeedsToBeSent true;\r\ + \n :local backupName \$backupNameTemplate;\r\ + \n # This means it's the first step where we create a backup before the\ + \_update process\r\ + \n :if (\$isOsNeedsToBeUpdated = true) do={\r\ + \n :set backupName \$backupNameBeforeUpdate;\r\ + \n #Email body if the purpose of the script is to update the device\r\ + \n :set mailSubjectPartAction \"Update preparation\";\r\ + \n :set mailPtBodyAction (\$mailPtBodyAction . \"\\nThe update proces\ + s for device '\$Identity' is scheduled to upgrade RouterOS from version v.\ + \$runningVersion to version v.\$ROsVerAvail (Update channel: \$updateChann\ + el)\");\r\ + \n :set mailPtBodyAction (\$mailPtBodyAction . \"\\nPlease note: The \ + update will proceed only after a successful backup.\");\r\ + \n :set mailPtBodyAction (\$mailPtBodyAction . \"\\nA final report wi\ + th detailed information will be sent once the update process is completed.\ + \");\r\ + \n :set mailPtBodyAction (\$mailPtBodyAction . \"\\nIf you do not rec\ + eive a second email within the next 10 minutes, there may be an issue. Ple\ + ase check your device logs for further information.\");\r\ + \n }\r\ + \n :do {\r\ + \n :set mailAttachments [\$FuncCreateBackups \$backupName \$BackupPas\ + sword \$BackupSens];\r\ + \n :set mailPtSubjectBackup \"Backup created\";\r\ + \n :set mailPtBodyBackup \"System backups have been successfully crea\ + ted and attached to this email.\";\r\ + \n } on-error={\r\ + \n :set isOsNeedsToBeUpdated false;\r\ + \n :set mailPtSubjectBackup \"Backup failed\";\r\ + \n :set mailPtBodyBackup \"The script failed to create backups. Pleas\ + e check device logs for more details.\";\r\ + \n :log warning \"\$SMP Backup creation failed. Update process will b\ + e canceled if automatic update is enabled\";\r\ + \n }\r\ + \n }\r\ + \n :if (\$isEmailNeedsToBeSent = true) do={\r\ + \n :log info \"\$SMP Preparing to send email...\";\r\ + \n :local mailStep1Subject \$mailSubjectPrefix;\r\ + \n :local mailStep1Body \"\";\r\ + \n # subject\r\ + \n :if (\$mailSubjectPartAction != \"\") do={:set mailStep1Subject (\$\ + mailStep1Subject . \" - \" . \$mailSubjectPartAction)}\r\ + \n :if (\$mailPtSubjectBackup != \"\") do={:set mailStep1Subject (\$ma\ + ilStep1Subject . \" - \" . \$mailPtSubjectBackup)}\r\ + \n # body\r\ + \n :if (\$mailPtBodyAction != \"\") do={:set mailStep1Body (\$mailStep1\ + Body . \$mailPtBodyAction . \"\\n\\n\")}\r\ + \n :if (\$mailPtBodyBackup != \"\") do={:set mailStep1Body (\$mailStep1\ + Body . \$mailPtBodyBackup . \"\\n\\n\")}\r\ + \n :set mailStep1Body (\$mailStep1Body . \$mailBodyDeviceInfo . \"\\n__\ + ________\\n\" . \$mailBodyCopyright);\r\ + \n :set mailStep1Body (\$mailStep1Body . \"\\n[*** TEST START ***]\\n\\\ + \$DeviceInfo: \" . [ \$DeviceInfo ] . \"\\n[*** TEST END ***]\\n\");\r\ + \n # Send email with backups\r\ + \n :do {\$FuncSendEmailSafe \$EmailGeneralTo \$mailStep1Subject \$mailS\ + tep1Body \$mailAttachments} on-error={\r\ + \n :set isOsNeedsToBeUpdated false;\r\ + \n :log error \"\$SMP The script will not proceed with the update pro\ + cess, because the email was not sent.\";\r\ + \n }\r\ + \n }\r\ + \n :if ([:len \$mailAttachments] > 0) do={\r\ + \n :log info \"\$SMP Cleaning up backup files from the file system...\"\ + ;\r\ + \n /file remove \$mailAttachments;\r\ + \n :delay 2s;\r\ + \n }\r\ + \n :if (\$isOsNeedsToBeUpdated = true) do={\r\ + \n :global OldMode;\r\ + \n :log info \"\$SMP everything is ready to install new RouterOS, going\ + \_to start the update process and reboot the device.\";\r\ + \n :do {\r\ + \n :local nextStep 2;\r\ + \n :if (\$isCloudHostedRouter = true) do={\r\ + \n :log info \"\$SMP The device is a cloud hosted router, the secon\ + d step updating the Routerboard firmware will be skipped.\";\r\ + \n :set nextStep 3;\r\ + \n }\r\ + \n :local scheduledCommand (\":delay 5s; /system/scheduler remove BKP\ + UPD-NEXT-BOOT-TASK; \\\r\ + \n :global buGlobalVarScriptStep \$nextStep; :global buGlobalVarTarg\ + etOsVersion \\\"\$ROsVerAvail\\\"; \\\r\ + \n :delay 10s; /system/script { run \$ScriptName; }\");\r\ + \n /system/scheduler add name=BKPUPD-NEXT-BOOT-TASK on-event=\$schedu\ + ledCommand start-time=startup interval=0;\r\ + \n /system/package/update install;\r\ + \n } on-error={\r\ + \n # Failed to install new os version, remove the task and variables\ + \r\ + \n :do {\r\ + \n /system/scheduler remove BKPUPD-NEXT-BOOT-TASK;\r\ + \n :set BackupAndUpdateMode \$OldMode;\r\ + \n :delay 2;\r\ + \n :do {\r\ + \n :global buGlobalVarTargetOsVersion (a);\r\ + \n } on-error={\r\ + \n :set \$mailSubjectPrefix (\$mailSubjectPrefix . \"ERROR Remove\ + \_global variable \\\$buGlobalVarTargetOsVersion failed!\");\r\ + \n }\r\ + \n# :if ([ :len \$OldMode ] > 0) do={\r\ + \n# :do {\r\ + \n# :global OldMode (a);\r\ + \n# } on-error={\r\ + \n# :set \$mailSubjectPrefix (\$mailSubjectPrefix . \" ERROR Re\ + move global variable \\\$OldMode failed!\");\r\ + \n# }\r\ + \n# }\r\ + \n } on-error={\r\ + \n :log error \"\$SMP Failed to install new RouterOS version. Pleas\ + e check device logs for more details. \\\r\ + \n \\nAnd Failed to remove task and variables! Check it!\";\r\ + \n :set \$mailSubjectPrefix (\$mailSubjectPrefix . \" ERROR Remove \ + task and global variables!\");\r\ + \n }\r\ + \n :log error \"\$SMP Failed to install new RouterOS version. Please \ + check device logs for more details.\";\r\ + \n :local mailUpdateErrorSubject (\$mailSubjectPrefix . \" - Update f\ + ailed\");\r\ + \n :local mailUpdateErrorBody \"The script was unable to install new \ + RouterOS version. Please check device logs for more details.\";\r\ + \n # Send email with error\r\ + \n \$FuncSendEmailSafe \$EmailGeneralTo \$mailUpdateErrorSubject \$ma\ + ilUpdateErrorBody \"\";\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n }\r\ + \n}\r\ + \n## STEP 2: (Post-reboot) Upgrade RouterBOARD firmware\r\ + \n## Runs only if auto-update is enabled and a new RouterOS version was fo\ + und\r\ + \n:if (\$scriptStep = 2) do={\r\ + \n :log info \"\$SMP The script is in the second step, updating Routerboa\ + rd firmware.\";\r\ + \n :log info \"\$SMP Upgrading routerboard firmware from v.\$deviceRbCurr\ + entFw to v.\$deviceRbUpgradeFw\";\r\ + \n /system routerboard upgrade;\r\ + \n :delay 2;\r\ + \n :log info \"\$SMP routerboard upgrade process was completed, going to \ + reboot in a moment!\";\r\ + \n ## Set task to send final report on the next boot\r\ + \n /system scheduler add name=BKPUPD-NEXT-BOOT-TASK on-event=\":delay 2; \ + :global buGlobalVarScriptStep 3; \\\r\ + \n :global buGlobalVarTargetOsVersion \\\"\$buGlobalVarTargetOsVersion\\\ + \"; :delay 10s; /system/script { run \$ScriptName; }; \\ \r\ + \n :delay 5s; /system/scheduler remove BKPUPD-NEXT-BOOT-TASK;\" start-ti\ + me=startup interval=0;\r\ + \n :delay 2;\r\ + \n /system reboot;\r\ + \n}\r\ + \n\r\ + \n## STEP 3: Final report (after second reboot, with delay).\r\ + \n## Runs only if auto-update is enabled and a new RouterOS version was fo\ + und.\r\ + \n:if (\$scriptStep = 3) do={\r\ + \n :log info (\"\$SMP The script is in the third step, sending final repo\ + rt.\");\r\ + \n :local targetOsVersion \$buGlobalVarTargetOsVersion;\r\ + \n :do {\r\ + \n# /system/script/environment remove [find name=\"buGlobalVarTargetOsV\ + ersion\"];\r\ + \n :global buGlobalVarTargetOsVersion (a);\r\ + \n } on-error={\r\ + \n :set \$mailSubjectPrefix (\$mailSubjectPrefix . \" ERROR Remove glob\ + al variable \\\$buGlobalVarTargetOsVersion failed!\");\r\ + \n }\r\ + \n\r\ + \n :if ([ :len \$OldMode ] != 0) do={\r\ + \n :do {\r\ + \n# /system/script/environment remove [find name=\"OldMode\"];\r\ + \n :global OldMode (a);\r\ + \n } on-error={\r\ + \n :set \$mailSubjectPrefix (\$mailSubjectPrefix . \" ERROR! Remove\ + \_global variable \\\$OldMode failed!\");\r\ + \n }\r\ + \n }\r\ + \n :if ([:len \$targetOsVersion] = 0) do={\r\ + \n :log warning \"\$SMP Something is wrong, the script was unable to ge\ + t the target updated OS version from the global variable.\";\r\ + \n }\r\ + \n :local mailStep3Subject \$mailSubjectPrefix;\r\ + \n :local mailStep3Body \"\";\r\ + \n :if (\$targetOsVersion = \$runningVersion) do={\r\ + \n :log info \"\$SMP Successfully verified new RouterOS version: target\ + : `\$targetOsVersion`, current: `\$runningVersion`\";\r\ + \n :set mailStep3Subject (\$mailStep3Subject . \" - Update completed - \ + Backup created\");\r\ + \n :set mailStep3Body (\$mailStep3Body . \"RouterOS and routerboard upg\ + rade process was completed\");\r\ + \n :set mailStep3Body (\$mailStep3Body . \"\\nNew RouterOS version: v.\ + \$targetOsVersion, routerboard firmware: v.\$deviceRbCurrentFw\");\r\ + \n :set mailStep3Body (\$mailStep3Body . \"\\n\$changelogUrl\\nBackups \ + of the upgraded system are in the attachment of this email.\\n\\n\$mailBod\ + yDeviceInfo\\n\\n\$mailBodyCopyright\");\r\ + \n :set mailAttachments [\$FuncCreateBackups \$backupNameAfterUpdate \$\ + BackupPassword \$BackupSens];\r\ + \n } else={\r\ + \n :log error \"\$SMP Failed to verify new RouterOS version: target: `\ + \$targetOsVersion`, current: `\$runningVersion`\";\r\ + \n :set mailStep3Subject (\$mailStep3Subject . \" - Update failed\");\r\ + \n :set mailStep3Body (\$mailStep3Body . \"The script was unable to ver\ + ify that the new RouterOS version was installed, target version: `\$target\ + OsVersion`, current version: `\$runningVersion`\\nCheck device logs for mo\ + re details.\\n\\n\$mailBodyDeviceInfo\\n\\n\$mailBodyCopyright\");\r\ + \n }\r\ + \n \$FuncSendEmailSafe \$EmailGeneralTo \$mailStep3Subject \$mailStep3Bod\ + y \$mailAttachments;\r\ + \n :if ([:len \$mailAttachments] > 0) do={\r\ + \n :log info \"\$SMP Cleaning up backup files from the file system...\"\ + ;\r\ + \n /file remove \$mailAttachments;\r\ + \n :delay 2;\r\ + \n }\r\ + \n :log info (\"\$SMP Final report email sent successfully, and the scrip\ + t has finished.\");\r\ + \n}\r\ + \n:log info (\"\$SMP the script has finished, script step: `\$scriptStep` \ + \\n\\n\");\r\ + \n\r\ + \n} do={:global ExitError; \$ExitError \$ExitOK [ :jobname ] \$Err}\r\ + \n" +add dont-require-permissions=no name=testGetPubIP owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source=":\ + global DevInfoIncludeIP;\r\ + \n:global CharacterReplace;\r\ + \n:global EscapeForRegEx;\r\ + \n:global UrlEncode;\r\ + \n\r\ + \n:if ([ \$DevInfoIncludeIP ] = true) do={\r\ + \n :global GetPubIp;\r\ + \n :local Char (\"\\n\\r\");\r\ + \n :local PublicIpAddress [\$GetPubIp];\r\ + \n# :if ([ :pick \$PublicIpAddress ([ :len \$PublicIpAddress ] - 2) ] = \ + \"\\n\") do={:error true}; :set error falseon-error={:set PublicIpAddress \ + (\$PublicIpAddress . Char)};\r\ + \n#:if ([ :pick \$PublicIpAddress ([ :len \$PublicIpAddress ] - 1) ] = \"/\ + n\") do={\r\ + \n#:log warning \"N is = //n\"; \r\ + \n# :set PublicIpAddress [ \$CharacterReplace \$PublicIpAddress \$Char \"\ + \" ];\r\ + \n#}\r\ + \n# :set PublicIpAddress [ \$UrlEncode \$PublicIpAddress ];\r\ + \n#:local IpLen ([ :len \$PublicIpAddress ] -1);\r\ + \n#:set PublicIpAddress [:pick \$PublicIpAddress 0 \$IpLen];\r\ + \n :log warning \"Public IP address detected: \$PublicIpAddress , len = \ + \$IpLen\";\r\ + \n}\r\ + \n" +add comment="download, import and update firewall address-lists" \ + dont-require-permissions=no name=AM-FwAddrLists owner=AM-FwAddrLists \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + source="#!rsc by Vados\r\ + \n# RouterOS script: AM-FwAddrLists\r\ + \n# Script comment: download, import and update firewall address-lists\r\ + \n# Copyright (c) 2007-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# https://ros.vados.ru/doc/fw-addr-lists.md\r\ + \n:local ExitOK false;\r\ + \n:onerror Err {\r\ + \n :global GlobalConfReady; :global GlobalFuncReady;\r\ + \n :retry { :if (\$GlobalConfReady != true || \$GlobalFuncReady != true) \ + \\\r\ + \n do={ :error (\"Global config and/or functions not ready.\"); }; } \ + delay=500ms max=50;\r\ + \n :local ScriptName [ :jobname ];\r\ + \n\r\ + \n :global GlobalScriptsUrl;\r\ + \n :local ListsDirName (\"fw-addr-lists.d/\" . [ /system identity get nam\ + e ] . \"/\");\r\ + \n :local ListsUrl (\$GlobalScriptsUrl . \$ListsDirName);\r\ + \n :local ListsIndex \"index.lst\";\r\ + \n :local AddrListsTimeOut 1d;\r\ + \n :local CheckCerts false;\r\ + \n\r\ + \n :global CertificateAvailable;\r\ + \n :global EitherOr;\r\ + \n :global FetchHugeFiles;\r\ + \n :global HumanReadableNum;\r\ + \n :global IfThenElse;\r\ + \n :global LogPrint;\r\ + \n :global NetMask4;\r\ + \n :global NetMask6;\r\ + \n :global ScriptLock;\r\ + \n \r\ + \n :local FindDelim do={\r\ + \n :local ValidChars \"0123456789.:/ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghi\ + jklmnopqrstuvwxyz-\";\r\ + \n :for I from=0 to=[ :len \$1 ] do={\r\ + \n :if ([ :typeof [ :find \$ValidChars [ :pick (\$1 . \" \") \$I ] ] \ + ] != \"num\") do={\r\ + \n :return \$I;\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :local GetBranch do={\r\ + \n :global EitherOr;\r\ + \n :return [ :pick [ :convert transform=md5 to=hex [ :pick \$1 0 [ \$Ei\ + therOr [ :find \$1 \"/\" ] [ :len \$1 ] ] ] ] 0 2 ];\r\ + \n }\r\ + \n \r\ + \n :if ([ \$ScriptLock \$ScriptName ] = false) do={\r\ + \n :set ExitOK true;\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :local ListComment (\"managed by \" . \$ScriptName);\r\ + \n :local Url (\$ListsUrl . \$ListsIndex);\r\ + \n :local tmpIndex false;\r\ + \n\r\ + \n :for I from=1 to=5 do={\r\ + \n :if (\$tmpIndex = false) do={\r\ + \n :set tmpIndex [ :tolf [ \$FetchHugeFiles \$ScriptName \$Url \$Chec\ + kCerts ] ];\r\ + \n :if (\$tmpIndex = false && \$I < 5) do={\r\ + \n :log warning (\$ScriptName . \" Failed downloading address-list \ + index, \" . \$I . \". try from: \" . \$Url);\r\ + \n :delay ((\$I * \$I) . \"s\");\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n :if (\$tmpIndex = false) do={\r\ + \n :log warning (\$ScriptName . \" Failed downloading address-list inde\ + x from: \" . \$Url);\r\ + \n :set tmpIndex \"\";\r\ + \n }\r\ + \n\r\ + \n :foreach List in=[ :deserialize \$tmpIndex delimiter=\"\\n\" from=dsv \ + options=dsv.plain ] do={\r\ + \n# :set List (\$List->0);\r\ + \n :local ListName;\r\ + \n :local Delim [ \$FindDelim \$List ];\r\ + \n :if (\$Delim = 0) do={ :set List \"\"; };\r\ + \n :log warning (\$ScriptName . \" Parsing address-list index entry: \"\ + \_. \$List);\r\ + \n :if ([ :len \$List ] = 0) do={\r\ + \n :log error (\$ScriptName . \" Failed parsing address-list index en\ + try: \" . \$List);\r\ + \n } else={\r\ + \n :set ListName [ :pick \$List 0 \$Delim ];\r\ + \n }\r\ + \n :log warning (\$ScriptName . \" Parsing address-list index entry: \"\ + \_. \$ListName . \" - Ok.\");\r\ + \n :local ListUrl (\$ListsUrl . \$ListName);\r\ + \n :local CntAdd 0;\r\ + \n :local CntRenew 0;\r\ + \n :local CntRemove 0;\r\ + \n :local IPv4Addresses ({});\r\ + \n :local IPv6Addresses ({});\r\ + \n :local Failure false;\r\ + \n :local Data false;\r\ + \n :local TimeOut \$AddrListTimeOut;\r\ + \n\r\ + \n :for I from=1 to=5 do={\r\ + \n :if (\$Data = false) do={\r\ + \n :set Data [ :tolf [ \$FetchHugeFiles \$ScriptName \$ListUrl \$Ch\ + eckCerts ] ];\r\ + \n :if (\$Data = false && \$I < 5) do={\r\ + \n :log warning (\$ScriptName . \" Failed downloading for list '\ + \" . \$ListName . \"', \" . \$I . \". try from: \" . \$ListUrl);\r\ + \n :delay ((\$I * \$I) . \"s\");\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :if (\$Data = false) do={\r\ + \n :set Data \"\";\r\ + \n :set Failure true;\r\ + \n :log warning (\$ScriptName . \" Failed downloading for list '\" . \ + \$ListName . \"' from: \" . \$ListUrl);\r\ + \n } else={\r\ + \n :log warning (\$ScriptName . \" Downloaded \" . [ \$HumanReadableN\ + um [ :len \$Data ] 1024 ] . \"B for list '\" . \$ListName . \"' from: \" .\ + \_\$ListUrl);\r\ + \n }\r\ + \n\r\ + \n :foreach Line in=[ :deserialize \$Data delimiter=\"\\n\" from=dsv op\ + tions=dsv.plain ] do={\r\ + \n :set Line (\$Line->0);\r\ + \n :local Address;\r\ + \n :if ([ :pick \$Line 0 1 ] = \"{\" && [ :pick \$Line ([ :len \$Line\ + \_] - 1) ] = \"}\") do={\r\ + \n :do {\r\ + \n :set Address [ :tostr ([ :deserialize from=json \$Line ]->\"ci\ + dr\") ];\r\ + \n } on-error={ }\r\ + \n } else={\r\ + \n :set Address ([ :pick \$Line 0 [ \$FindDelim \$Line ] ] . (\$L\ + ist->\"cidr\"));\r\ + \n }\r\ + \n :do {\r\ + \n :local Branch;\r\ + \n :if (\$Address ~ \"^[0-9]{1,3}\\\\.[0-9]{1,3}\\\\.[0-9]{1,3}\\\\\ + .[0-9]{1,3}(/[0-9]{1,2})\?\\\$\") do={\r\ + \n :local Net \$Address;\r\ + \n :local CIDR 32;\r\ + \n :local Slash [ :find \$Address \"/\" ];\r\ + \n :if ([ :typeof \$Slash ] = \"num\") do={\r\ + \n :set Net [ :toip [ :pick \$Address 0 \$Slash ] ]\r\ + \n :set CIDR [ :pick \$Address (\$Slash + 1) [ :len \$Address ]\ + \_];\r\ + \n :set Address [ :tostr (([ :toip \$Net ] & [ \$NetMask4 \$CID\ + R ]) . [ \$IfThenElse (\$CIDR < 32) (\"/\" . \$CIDR) ]) ];\r\ + \n }\r\ + \n :set Branch [ \$GetBranch \$Address ];\r\ + \n :set (\$IPv4Addresses->\$Branch->\$Address) \$TimeOut;\r\ + \n }\r\ + \n :if (\$Address ~ \"^[0-9a-zA-Z]*:[0-9a-zA-Z:\\\\.]+(/[0-9]{1,3})\ + \?\\\$\") do={\r\ + \n :local Net \$Address;\r\ + \n :local CIDR 128;\r\ + \n :local Slash [ :find \$Address \"/\" ];\r\ + \n :if ([ :typeof \$Slash ] = \"num\") do={\r\ + \n :set Net [ :toip6 [ :pick \$Address 0 \$Slash ] ]\r\ + \n :set CIDR [ :pick \$Address (\$Slash + 1) [ :len \$Address ]\ + \_];\r\ + \n }\r\ + \n :set Address (([ :toip6 \$Net ] & [ \$NetMask6 \$CIDR ]) . \"/\ + \" . \$CIDR);\r\ + \n :set Branch [ \$GetBranch \$Address ];\r\ + \n :set (\$IPv6Addresses->\$Branch->\$Address) \$TimeOut;\r\ + \n }\r\ + \n :if (\$Address ~ \"^[\\\\.a-zA-Z0-9-]+\\\\.[a-zA-Z]{2,}\\\$\") d\ + o={\r\ + \n :set Branch [ \$GetBranch \$Address ];\r\ + \n :set (\$IPv4Addresses->\$Branch->\$Address) \$TimeOut;\r\ + \n :set (\$IPv6Addresses->\$Branch->\$Address) \$TimeOut;\r\ + \n }\r\ + \n } on-error={ }\r\ + \n }\r\ + \n \r\ + \n\r\ + \n :foreach Entry in=[ /ip/firewall/address-list/find where \\\r\ + \n list=\$ListName comment=\$ListComment ] do={\r\ + \n :local Address [ /ip/firewall/address-list/get \$Entry address ];\ + \r\ + \n :local Branch [ \$GetBranch \$Address ];\r\ + \n :local TimeOut (\$IPv4Addresses->\$Branch->\$Address);\r\ + \n :if ([ :typeof \$TimeOut ] = \"time\") do={\r\ + \n :log warning (\$ScriptName . \" Renewing IPv4 address \" . \$Add\ + ress . \\\r\ + \n \" in list '\" . \$ListName . \"' with \" . \$TimeOut . \".\ + \");\r\ + \n /ip/firewall/address-list/set \$Entry timeout=\$TimeOut;\r\ + \n :set (\$IPv4Addresses->\$Branch->\$Address);\r\ + \n :set CntRenew (\$CntRenew + 1);\r\ + \n } else={\r\ + \n :if (\$Failure = false) do={\r\ + \n :log warning (\$ScriptName . \" Removing IPv4 address \" . \$A\ + ddress . \\\r\ + \n \" from list '\" . \$ListName . \".\");\r\ + \n /ip/firewall/address-list/remove \$Entry;\r\ + \n :set CntRemove (\$CntRemove + 1);\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :foreach Entry in=[ /ipv6/firewall/address-list/find where \\\r\ + \n list=\$ListName comment=\$ListComment ] do={\r\ + \n :local Address [ /ipv6/firewall/address-list/get \$Entry address ]\ + ;\r\ + \n :local Branch [ \$GetBranch \$Address ];\r\ + \n :local TimeOut (\$IPv6Addresses->\$Branch->\$Address);\r\ + \n :if ([ :typeof \$TimeOut ] = \"time\") do={\r\ + \n :log warning (\$ScriptName . \" Renewing IPv6 address \" . \$Add\ + ress . \\\r\ + \n \" in list '\" . \$ListName . \"' with \" . \$TimeOut . \".\ + \");\r\ + \n /ipv6/firewall/address-list/set \$Entry timeout=\$TimeOut;\r\ + \n :set (\$IPv6Addresses->\$Branch->\$Address);\r\ + \n :set CntRenew (\$CntRenew + 1);\r\ + \n } else={\r\ + \n :if (\$Failure = false) do={\r\ + \n :log warning (\$ScriptName . \" Removing IPv6 address \" . \$A\ + ddress . \\\r\ + \n \" from list '\" . \$ListName . \".\");\r\ + \n /ipv6/firewall/address-list/remove \$Entry;\r\ + \n :set CntRemove (\$CntRemove + 1);\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :foreach BranchName,Branch in=\$IPv4Addresses do={\r\ + \n :log warning (\$ScriptName . \" Handling branch: \" . \$BranchName\ + );\r\ + \n :foreach Address,Timeout in=\$Branch do={\r\ + \n :log warning (\$ScriptName . \" Adding IPv4 address \" . \$Addre\ + ss . \\\r\ + \n \" to list '\" . \$ListName . \"' with \" . \$Timeout . \".\ + \");\r\ + \n :onerror Err {\r\ + \n /ip/firewall/address-list/add list=\$ListName comment=\$ListCo\ + mment \\\r\ + \n address=\$Address timeout=\$Timeout;\r\ + \n :set CntAdd (\$CntAdd + 1);\r\ + \n } do={\r\ + \n :log warning (\$ScriptName . \" Failed to add IPv4 address \" \ + . \$Address . \\\r\ + \n \" to list '\" . \$ListName . \"': \" . \$Err);\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :foreach BranchName,Branch in=\$IPv6Addresses do={\r\ + \n :log warning (\$ScriptName . \" Handling branch: \" . \$BranchName\ + );\r\ + \n :foreach Address,Timeout in=\$Branch do={\r\ + \n :log warning (\$ScriptName . \" Adding IPv6 address \" . \$Addre\ + ss . \\\r\ + \n \" to list '\" . \$ListName . \"' with \" . \$Timeout . \".\ + \");\r\ + \n :onerror Err {\r\ + \n /ipv6/firewall/address-list/add list=\$ListName comment=\$List\ + Comment \\\r\ + \n address=\$Address timeout=\$Timeout;\r\ + \n :set CntAdd (\$CntAdd + 1);\r\ + \n } do={\r\ + \n :log warning (\$ScriptName . \" Failed to add IPv6 address \" \ + . \$Address . \\\r\ + \n \" to list '\" . \$ListName . \"': \" . \$Err);\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :log info (\$ScriptName . \" list: \" . \$ListName . \\\r\ + \n \" (\" . [ \$HumanReadableNum (\$CntAdd + \$CntRenew) 1000 ] . \ + \")\" . \\\r\ + \n \" -- added: \" . [ \$HumanReadableNum \$CntAdd 1000 ] . \\\r\ + \n \" - renewed: \" . [ \$HumanReadableNum \$CntRenew 1000 ] . \\\r\ + \n \" - removed: \" . [ \$HumanReadableNum \$CntRemove 1000 ]);\r\ + \n }\r\ + \n} do={\r\ + \n :global ExitError; \$ExitError \$ExitOK [ :jobname ] \$Err;\r\ + \n}\r\ + \n" +add dont-require-permissions=no name=awg-env owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="/\ + container/envs/add list=awg-proxy-env key=AWG_LISTEN value=\":55055\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_REMOTE value=\"109.107.17\ + 7.79:55055\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_JC value=\"9\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_JMIN value=\"105\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_JMAX value=\"920\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_S1 value=\"90\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_S2 value=\"136\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_S3 value=\"45\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_S4 value=\"27\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_H1 value=\"472437748-4724\ + 38637\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_H2 value=\"707808381-7078\ + 09250\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_H3 value=\"1609078552-160\ + 9079495\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_H4 value=\"1012329484-101\ + 2329741\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_I1 value=\"\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_I2 value=\"\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_I3 value=\"\ + \"\ + \n/container/envs/add list=awg-proxy-env key=AWG_I4 value=\"\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_I5 value=\"\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_SERVER_PUB value=\"o5IgCl\ + 1/G6xbl6j7sPaP4ZNYREeYOmqZjKbm+v0b9jU=\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_CLIENT_PUB value=[/interf\ + ace/wireguard/get [find name=wg-awg-proxy] public-key]" +add dont-require-permissions=no name=awg-container-add owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="/\ + container/add file=sata1/tmp/awg-proxy-amd64.tar.gz interface=veth-awg-pro\ + xy envlist=awg-proxy-env \\\ + \n hostname=awg-proxy root-dir=sata1/awg-proxy logging=yes shm-size=4M \ + start-on-boot=yes\ + \n/container/start [find where tag~\"awg-proxy\"]" +add dont-require-permissions=yes name=awg2-webui owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source=":\ + put \"Download image, and save him to usb1/tmp\"\ + \n/tool/fetch url=\"https://ros.vados.ru/docker/awg2-webui-chr.tar\" dst-p\ + ath=sata1/tmp/awg2-webui.tar\ + \n\ + \n:put \"1. Create veth interface\"\ + \n/interface veth add name=veth-awg address=10.9.9.2/24 gateway=10.9.9.1\ + \n\ + \n:put \"2. Create bridge and add veth\"\ + \n/interface bridge add name=br-awg\ + \n/interface bridge port add bridge=br-awg interface=veth-awg\ + \n\ + \n:put \"3. Assign IP to bridge\"\ + \n/ip address add address=10.9.9.1/24 interface=br-awg\ + \n\ + \n:put \"4. NAT: masquerade container traffic\"\ + \n/ip firewall nat add chain=srcnat src-address=10.9.9.0/24 action=masquer\ + ade\ + \n\ + \n:put \"5. Port forward: UDP 443 \? container (AmneziaWG QUIC mimicry)\"\ + \n/ip firewall nat add chain=dstnat protocol=udp dst-port=443 action=dst-n\ + at to-addresses=10.9.9.2 to-ports=55055 in-interface=ether1\ + \n\ + \n:put \"6. Port forward: HTTPS 9443 \? container Web UI\"\ + \n/ip firewall nat add chain=dstnat protocol=tcp dst-port=9443 action=dst-\ + nat to-addresses=10.9.9.2 to-ports=443 in-interface=ether1\ + \n\ + \n:put \"Create environment list..\"\ + \n/container envs add list=awgui-env key=WEB_USER value=vados\ + \n/container envs add list=awgui-env key=WEB_PASS value=zoomzoomrulezz\ + \n/container envs add list=awgui-env key=SECRET_KEY value=RanDomSecret32Sy\ + mboLs2325272931q\ + \n/container envs add list=awgui-env key=AWG_ENDPOINT value=10.30.30.10:44\ + 3\ + \n\ + \n:put \"Load and start container...\"\ + \n/container add file=sata1/tmp/awg2-webui.tar interface=veth-awg hostname\ + =awg2-webui \\\ + \nroot-dir=sata1/awg2-webui layer-dir=sata1/docker/awg2-webu/ envlist=awgu\ + i-env start-on-boot=yes logging=yes shm-size=4M\ + \n\ + \n:put \"Start...\"\ + \n/container start 0\ + \n\ + \n:put \"Verify...\"\ + \n/container print\ + \n\ + \n:put \"Should show: status=running\"\ + \n\ + \n/log print where topics~\"container\"\ + \n\ + \n:put \"Should show: AWG started, nginx running\"\ + \n" +add dont-require-permissions=no name=awg-proxy-go-Client owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="\ + \n:put \"[::::::::::::::: START :::::::::::::::]\"\ + \n:put \"\"\ + \n:put \"Add ram disk tmpfs\"\ + \n#/disk add type=tmpfs tmpfs-max-size=100M slot=ram\ + \n\ + \n:put \"veth and network:\"\ + \n /interface/bridge add name=docker port-cost-mode=short\ + \n /ip/address/add address=192.168.100.1/24 interface=docker\ + \n\ + \n /interface/veth add address=192.168.100.3/24 gateway=192.168.100.1 name\ + =awg-proxy-go-client\ + \n /interface/bridge/port add bridge=docker interface=awg-proxy-go-client\ + \n\ + \n:put \"add config-client.json\"\ + \n/file/add name=sata1/mounts/awg-proxy-go/config-client.json\ + \n/container/mounts/add src=sata1/mounts/awg-proxy-go/config-client.json d\ + st=/app/config.json list=awgcfg-client\ + \n\ + \n#:put \"Download image, and save him to sata1/tmp/awgproxy-0.0.1-0034-am\ + d64.tar.gz\"\ + \n#/tool/fetch url=\"https://ros.vados.ru/amneziawg/img/awgproxy-0.0.1-003\ + 4-amd64.tar.gz\" dst-#path=\"tmp/awgproxy-0.0.1-0034-amd64.tar.gz\"\ + \n\ + \n:put \"Add container\"\ + \n/container/add file=\"usb1/dst/awgproxy-0.0.1-0036-amd64.tar\" interface\ + =awg-proxy-go-client hostname=awg-proxy-go-client root-dir=sata1/awg-proxy\ + -client logging=yes start-on-boot=yes workdir=sata1/docker/ name=\"awg-pro\ + xy-go-client\" mountlists=awgcfg-client\ + \n\ + \n:put \"Start container\"\ + \n#/container/start awg-proxy-go-client\ + \n\ + \n:put \"Add user:\"\ + \n /user/add address=192.168.100.2 group=write name=awg-proxy-go password=\ + peskar55\ + \n\ + \n:put \"Add forwarding port web-ui\"\ + \n /ip firewall nat add action=dst-nat chain=dstnat dst-port=8089 in-inter\ + face=ether1 protocol=tcp to-addresses=192.168.100.3 to-port=8088\ + \n\ + \n:put \"Add native WireGuard-server wg-awg, add address.\"\ + \n /interface/wireguard/add name=wg-awg-client\ + \n /ip/address/add address=172.17.0.1/24 interface=wg-awg-client\ + \n\ + \n:put \"\"\ + \n:put \"[::::::::::::::: END :::::::::::::::]\"\ + \n" +add comment=awg2wg dont-require-permissions=no name=awg2wg-uninstall owner=\ + Manager policy=ftp,read,write,policy source="\r\ + \n :put \"Uninstalling AmneziaWG...\"\r\ + \n :log info \"Uninstalling AmneziaWG...\"\r\ + \n /ip/route/remove [find where comment=awg2wg-tunnel]\r\ + \n :do { /tool/netwatch/remove [find where comment=Monitor-AWG2WG] } on-e\ + rror={}\r\ + \n :do { /ip/route/remove [find where comment=Monitor-AWG2WG] } on-error=\ + {}\r\ + \n /container/stop [find where interface=veth-awg2wg]\r\ + \n :delay 7s\r\ + \n /interface/list/member/remove [find where interface=wg-awg2wg]\r\ + \n /container/remove [find where interface=veth-awg2wg]\r\ + \n /container/envs/remove [find where list=\"awg2wg-env\"]\r\ + \n /ip/firewall/nat/remove [find where out-interface=\"wg-awg2wg\"]\r\ + \n /ip/address/remove [find where address=\"172.20.0.1/30\"]\r\ + \n /interface/veth/remove [find where name=\"veth-awg2wg\"]\r\ + \n /interface/wireguard/peers/remove [find where interface=\"wg-awg2wg\"]\ + \r\ + \n /ip/address/remove [find where interface=\"wg-awg2wg\"]\r\ + \n /interface/wireguard/remove [find where name=\"wg-awg2wg\"]\r\ + \n :do { /file/remove [find where name~\"awg2wg.+tar\"] } on-error={}\r\ + \n :do { /file/remove [find where name=\"disk1/awg2wg\"] } on-error={}\r\ + \n :put \"Uninstall AmneziaWG Proxy complete!\"\r\ + \n :log info \"Uninstall AmneziaWG Proxy complete!\"\r\ + \n /system/script/remove [find where name=awg2wg-uninstall]\r\ + \n" +add dont-require-permissions=no name=amneziawg-mikrotik-c owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="\ + \n:global GlobalScriptsUrl \"https://ros.vados.ru/\";\ + \n:global imagesPath \"amneziawg/img/\";\ + \n:local Image \"awg-proxy-amd64-7.20-Docker.tar.gz\";\ + \n\ + \n:global DstDir \"sata1/dst/\";\ + \n\ + \n:local Url=(\$GlobalScriptsUrl . \$ImagesPath . \$Image);\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n" +add comment=Generator dont-require-permissions=no name=awg-VDSina owner=vados \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + source="# ============================================================\ + \n# AWG Proxy -- MikroTik RouterOS (protocol: v2)\ + \n# 2026-06-30 22:08:12\ + \n# ============================================================\ + \n\ + \n# 0. Check prerequisites\ + \n:if ([:len [/system/package/find where name=\"container\" disabled=no]] \ + = 0) do={\ + \n :put \"Container package is not installed or disabled. Install it and \ + reboot.\"\ + \n :error \"Usage: /system/device-mode/update container=yes\"\ + \n}\ + \n\ + \n# Auto-detect storage\ + \n:global awgDisk \"disk1\"\ + \n{\ + \n :local usbFree 0\ + \n :do { :set usbFree [/disk/get [find where mount-point=\"usb1\"] free] \ + } on-error={}\ + \n :if (\$usbFree > 0) do={\ + \n :set awgDisk \"usb1\"\ + \n :put (\"Using USB: usb1 (\" . (\$usbFree / 1048576) . \" MB free)\")\ + \n } else={\ + \n :if ([:len [/disk/find where slot=\"usb1\"]] > 0) do={\ + \n :put \"USB found but not mounted. Format as ext4\?\"\ + \n :put \"Press 'y' to confirm:\"\ + \n :local key [/terminal/inkey timeout=30]\ + \n :if (\$key = 121 || \$key = 89) do={\ + \n /disk/format-drive usb1 file-system=ext4 label=usb1\ + \n :delay 3s\ + \n :do { :set usbFree [/disk/get [find where mount-point=\"usb1\"] \ + free] } on-error={}\ + \n :if (\$usbFree > 0) do={\ + \n :set awgDisk \"usb1\"\ + \n } else={ :put \"Format failed, using disk1\" }\ + \n } else={ :put \"Using disk1\" }\ + \n } else={ :put \"No USB, using disk1\" }\ + \n }\ + \n}\ + \n\ + \n# 1. Uninstall script\ + \n/system/script/add name=awg-VDSina-uninstall comment=awg-VDSina source={\ + \n :put \"Uninstallimg AmneziaWG...\"\ + \n :log info \"Uninstallimg AmneziaWG...\"\ + \n :local prevDNS \"\"\ + \n :do { :set prevDNS [/container/envs/get [find where list=\"awg-VDSina-\ + env\" key=\"AWG_PREV_DNS\"] value] } on-error={}\ + \n :local storageDisk \"disk1\"\ + \n :do { :set storageDisk [/container/envs/get [find where list=\"awg-VDS\ + ina-env\" key=\"AWG_STORAGE\"] value] } on-error={}\ + \n :local prevTmpdir \"\"\ + \n :if (\$storageDisk != \"disk1\") do={\ + \n :do { :set prevTmpdir [/container/envs/get [find where list=\"awg-VD\ + Sina-env\" key=\"AWG_PREV_TMPDIR\"] value] } on-error={}\ + \n }\ + \n /ip/route/remove [find where comment=awg-VDSina-tunnel]\ + \n /ip/route/remove [find where comment=awg-VDSina-dns]\ + \n /container/stop [find where interface=veth-awg-VDSina]\ + \n :delay 7s\ + \n /container/remove [find where interface=veth-awg-VDSina]\ + \n /container/envs/remove [find where list=\"awg-VDSina-env\"]\ + \n :if (\$prevDNS != \"\") do={\ + \n :do { /ip/dns/set servers=\"\$prevDNS\" } on-error={}\ + \n :put (\"DNS restored to: \$prevDNS\")\ + \n }\ + \n :if (\$storageDisk != \"disk1\") do={\ + \n /container/config set tmpdir=\$prevTmpdir\ + \n :put (\"Container tmpdir restored to: \$prevTmpdir\")\ + \n }\ + \n /ip/firewall/nat/remove [find where out-interface=\"wg-awg-VDSina\"]\ + \n /ip/firewall/nat/remove [find where src-address=\"172.19.75.208/30\"]\ + \n /ip/address/remove [find where address=\"172.19.75.209/30\"]\ + \n /interface/veth/remove [find where name=\"veth-awg-VDSina\"]\ + \n /interface/wireguard/peers/remove [find where interface=\"wg-awg-VDSin\ + a\"]\ + \n /ip/address/remove [find where interface=\"wg-awg-VDSina\"]\ + \n /interface/wireguard/remove [find where name=\"wg-awg-VDSina\"]\ + \n :do { /file/remove [find where name~\"awg-VDSina.+tar\"] } on-error={}\ + \n :do { /file/remove [find where name=(\$storageDisk . \"/awg-VDSina\")]\ + \_} on-error={}\ + \n :do { /file/remove [find where name=(\$storageDisk . \"/ru_ranges_time\ + out.rsc\")] } on-error={}\ + \n :if (\$storageDisk != \"disk1\") do={\ + \n :do { /file/remove [find where name~(\$storageDisk . \"/pull\")] } o\ + n-error={}\ + \n }\ + \n :put \"Uninstall AmneziaWG Proxy complete!\"\ + \n :log info \"Uninstall AmneziaWG Proxy complete!\"\ + \n /system/script/remove [find where name=awg-VDSina-uninstall]\ + \n}\ + \n\ + \n# 2. Network infrastructure\ + \n/interface/veth/add name=veth-awg-VDSina address=172.19.75.210/30 gatewa\ + y=172.19.75.209\ + \n/ip/address/add address=172.19.75.209/30 interface=veth-awg-VDSina\ + \n/ip/firewall/nat/add chain=srcnat action=masquerade src-address=172.19.7\ + 5.208/30\ + \n\ + \n# 3. WireGuard interface (MikroTik derives the public key automatically)\ + \n/interface/wireguard/add name=wg-awg-VDSina private-key=\"4CHyNhbOs923/E\ + yNDguMXJXdghL2I3UyzXPwrANVgHs=\" listen-port=14710 disabled=yes\ + \n/interface/wireguard/peers/add interface=wg-awg-VDSina public-key=\"o5Ig\ + Cl1/G6xbl6j7sPaP4ZNYREeYOmqZjKbm+v0b9jU=\" preshared-key=\"RNvF7T1cg5tyXcY\ + QnD9N5BEp6e20vVJQTv0GMjyvHU0=\" endpoint-address=172.19.75.210 endpoint-po\ + rt=51820 allowed-address=0.0.0.0/0 persistent-keepalive=10\ + \n/ip/address/add address=10.9.0.3/32 interface=wg-awg-VDSina\ + \n\ + \n# 4. Container environment variables\ + \n/container/envs/add list=awg-VDSina-env key=AWG_LISTEN value=\":51820\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_REMOTE value=\"109.107.1\ + 77.79:55055\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_JC value=\"9\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_JMIN value=\"105\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_JMAX value=\"920\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_S1 value=\"90\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_S2 value=\"136\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_H1 value=\"472437748-472\ + 438637\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_H2 value=\"707808381-707\ + 809250\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_H3 value=\"1609078552-16\ + 09079495\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_H4 value=\"1012329484-10\ + 12329741\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_SERVER_PUB value=\"o5IgC\ + l1/G6xbl6j7sPaP4ZNYREeYOmqZjKbm+v0b9jU=\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_CLIENT_PUB value=[/inter\ + face/wireguard/get [find name=wg-awg-VDSina] public-key]\ + \n/container/envs/add list=awg-VDSina-env key=AWG_S3 value=\"45\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_S4 value=\"27\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_I1 value=\"\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_I2 value=\"\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_I3 value=\"\ + \"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_I4 value=\"\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_I5 value=\"\"\ + \n\ + \n# 5. DNS\ + \n{ :local rawDNS [/ip/dns/get servers]; :local prevDNS \"\"; :foreach s i\ + n=[:toarray \$rawDNS] do={ :if ([:len \$prevDNS] > 0) do={ :set prevDNS \"\ + \$prevDNS,\$s\" } else={ :set prevDNS \$s } }; /container/envs/add list=aw\ + g-VDSina-env key=AWG_PREV_DNS value=\"\$prevDNS\" }\ + \n# NOTE: replaces existing DNS configuration\ + \n/ip/dns/set servers=109.107.177.79\ + \n/ip/route/add dst-address=109.107.177.79/32 gateway=wg-awg-VDSina distan\ + ce=1 comment=awg-VDSina-dns\ + \n\ + \n# 6. Download, create and start container\ + \n/container/envs/add list=awg-VDSina-env key=AWG_STORAGE value=\$awgDisk\ + \n:if (\$awgDisk != \"disk1\") do={\ + \n :local tmpPath (\$awgDisk . \"/pull\")\ + \n /container/envs/add list=awg-VDSina-env key=AWG_PREV_TMPDIR value=[/co\ + ntainer/config/get tmpdir]\ + \n /container/config set tmpdir=\$tmpPath memory-high=200M\ + \n}\ + \n{\ + \n :local arch [/system/resource/get architecture-name]\ + \n :local ver [/system/resource/get version]\ + \n :local dotPos [:find \$ver \".\"]\ + \n :local rest [:pick \$ver (\$dotPos + 1) [:len \$ver]]\ + \n :local endPos [:find \$rest \".\"]\ + \n :if ([:typeof \$endPos] = \"nil\") do={ :set endPos [:find \$rest \" \ + \"] }\ + \n :local minor [:tonum [:pick \$rest 0 \$endPos]]\ + \n :local suffix \"\"\ + \n :if (\$minor <= 20) do={ :set suffix \"-7.20-Docker\" }\ + \n :local file \"\"\ + \n :if (\$arch = \"arm64\") do={ :set file (\"awg-proxy-arm64\" . \$suffi\ + x . \".tar.gz\") }\ + \n :if (\$arch = \"arm\") do={ :set file (\"awg-proxy-arm\" . \$suffix . \ + \".tar.gz\") }\ + \n :if (\$arch ~ \"x86\") do={ :set file (\"awg-proxy-amd64\" . \$suffix \ + . \".tar.gz\") }\ + \n :if (\$file = \"\") do={ :error \"Unsupported architecture: \$arch\" }\ + \n :local url \"https://github.com/timbrs/amneziawg-mikrotik-c/releases/l\ + atest/download/\$file\"\ + \n :local filePath \$file\ + \n :if (\$awgDisk != \"disk1\") do={\ + \n :set filePath (\$awgDisk . \"/pull/\" . \$file)\ + \n :do { /file/make-directory (\$awgDisk . \"/pull\") } on-error={}\ + \n }\ + \n :if ([:len [/file/find where name=\$filePath]] = 0) do={\ + \n :local freeStorage 0\ + \n :if (\$awgDisk = \"disk1\") do={\ + \n :set freeStorage [/system/resource/get free-hdd-space]\ + \n } else={\ + \n :do { :set freeStorage [/disk/get [find where mount-point=\$awgDis\ + k] free] } on-error={}\ + \n }\ + \n :if (\$freeStorage < 262144) do={\ + \n :put (\"WARNING: Low disk space (\" . (\$freeStorage / 1024) . \"K\ + B free). Need at least 256KB.\")\ + \n :put \"See: https://github.com/timbrs/amneziawg-mikrotik-c#insuffi\ + cient-disk-space\"\ + \n :error \"Insufficient disk space\"\ + \n }\ + \n :put (\"Fetching: \$url -> \" . \$filePath)\ + \n /tool/fetch url=\$url dst-path=\$filePath http-max-redirect-count=10\ + \n } else={\ + \n :put (\"File already exists: \" . \$filePath)\ + \n }\ + \n :local rootDir (\$awgDisk . \"/awg-VDSina\")\ + \n /container/add file=\$filePath interface=veth-awg-VDSina envlist=awg-V\ + DSina-env hostname=awg-VDSina root-dir=\$rootDir logging=no start-on-boot=\ + yes comment=awg-VDSina\ + \n :if (\$minor > 20) do={ [:parse \"/container/set [find where interface\ + =veth-awg-VDSina] shm-size=4M\"] }\ + \n /file/remove \$filePath\ + \n :local freeMem [/system/resource/get free-memory]\ + \n :if (\$freeMem < 16777216) do={\ + \n :put (\"WARNING: Low memory (\" . (\$freeMem / 1048576) . \"MB free)\ + . 16MB+ recommended.\")\ + \n }\ + \n /container/start [find where interface=veth-awg-VDSina]\ + \n :do { /file/remove [find where name=\"console-dump.txt\"] } on-error={\ + }\ + \n :put \"Waiting for container to start...\"\ + \n :delay 5s\ + \n /interface/wireguard/enable wg-awg-VDSina\ + \n :put \"WireGuard interface enabled\"\ + \n :put \"Installation complete!\"\ + \n :put \"Ping test: 109.107.177.79 via AmneziaWG tunnel. repeat 10 times\ + \"\ + \n /ping 109.107.177.79 count=10 interval=1\ + \n :put \"\"\ + \n :put \"======= NEXT STEPS =======\"\ + \n :put \"\"\ + \n :put \"Route specific traffic through the AmneziaWG tunnel:\"\ + \n :put \"\"\ + \n :put \" Route a single host:\"\ + \n :put \" /ip/route/add dst-address=8.8.8.8/32 gateway=wg-awg-VDSina \ + comment=awg-VDSina-tunnel\"\ + \n :put \"\"\ + \n :put \" Route a subnet:\"\ + \n :put \" /ip/route/add dst-address=100.0.0.0/8 gateway=wg-awg-VDSina\ + \_comment=awg-VDSina-tunnel\"\ + \n :put \"\"\ + \n :put \" Remove a route:\"\ + \n :put \" /ip/route/remove [find where comment=awg-VDSina-tunnel dst-\ + address=8.8.8.8/32]\"\ + \n :put \"\"\ + \n :put \" Show all tunnel routes:\"\ + \n :put \" /ip/route/print where comment=awg-VDSina-tunnel\"\ + \n :put \"\"\ + \n :put \" Uninstall:\"\ + \n :put \" /system/script/run awg-VDSina-uninstall\"\ + \n :put \"=========================\"\ + \n}\ + \n\ + \n# 7. Verification\ + \n# /container/print\ + \n# /interface/wireguard/print\ + \n# /interface/wireguard/peers/print\ + \n" +add comment="wg2awg GH repo in client mode" dont-require-permissions=no name=\ + "wg2awg client" owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="\ + \n:local Arch \"amd64\";\ + \n:local Version \"v0.0.1\";\ + \n:local Name wg2awg;\ + \n:local WorkDisk \"sata1\";\ + \n:local WorkDir (\$WorkDisk . \"/\" . \$Name);\ + \n:local Bridge (\"Bridge-\" . \$Name);\ + \n:local VetchMTU \"1280\";\ + \n:local Vetch (\"vetch-\" . \$Name);\ + \n:local VetchNet \"10.9.0.0\";\ + \n:local VetchGate \"10.9.0.1\";\ + \n:local VetchAddr \"10.9.0.3/32\";\ + \n:local Env (\$Name . \"-env\");\ + \n:local ConfDir (\"/etc/\" . \$Name . \"/\");\ + \n:local ConfFile (\$Name . \".conf\");\ + \n:local Mounts (\$WorkDisk . \"/mounts/\" . \$Name . \"/\");\ + \n:local RootDir (\$WorkDisk . \"/\" . \$Name);\ + \n:local Registry \"https://registry-1.docker.io\";\ + \n:local FetchFile (\$Name . \"-\" . \$Arch . \"-oci.tar\");\ + \n:local FetchUrl (\"https://github.com/vados-dev/wg2awg/releases/download\ + /\" . \$Version . \"/\" . \$FetchFile);\ + \n:local FetchDir (\$WorkDisk . \"/dst/\");\ + \n:local AwgListen \":44044\";\ + \n:local AwgRemote \"109.107.177.79\";\ + \n\ + \n#:put \"\"\ + \n#put \"Add ram disk tmpfs\"\ + \n#/disk add type=tmpfs tmpfs-max-size=100M slot=ram\ + \n\ + \n\ + \n:log warning \"[::::::::::::::: START :::::::::::::::]\"\ + \n:log warning \"Add veth and nat...\";\ + \n\ + \n/interface/veth/add name=\$Vetch address=\$VetchAddr gateway=\$VetchGate\ + ;\ + \n\ + \n:do {\ + \n:log warning (\"Remove interface: name=\" . \$Vetch . \", address=\" . \ + \$VetchAddr . \", gateway=\" . \$VetchGate);\ + \n/interface/veth/remove [find name=\$Vetch];\ + \n:log warning (\"Add interface: name=\" . \$Vetch . \", address=\" . \$Ve\ + tchAddr . \", gateway=\" . \$VetchGate);\ + \n/interface/veth/add name=\$Vetch address=\$VetchAddr gateway=\$VetchGate\ + ;\ + \n:log warning (\"Remove ip address: address=\" . \$VetchGate . \", interf\ + ace=\" . \$Vetch);\ + \n/ip/address/remove [find address=\$VetchGate interface=\$Vetch];\ + \n:log warning (\"Add ip address: address=\" . \$VetchGate . \", interface\ + =\" . \$Vetch);\ + \n/ip/address/add address=\$VetchGate interface=\$Vetch;\ + \n:log warning (\"Remove firewall nat rule: chain=srcnat action=masquerade\ + \_src-address=\" . \$VetchNet . \"/24\");\ + \n/ip/firewall/nat/remove [find chain=srcnat action=masquerade src-address\ + =(\$VetchNet . \"/24\")];\ + \n:log warning (\"Add firewall nat rule: chain=srcnat action=masquerade sr\ + c-address=\" . \$VetchNet . \"/24\");\ + \n/ip/firewall/nat/add chain=srcnat action=masquerade src-address=(\$VethN\ + et . \"/24\");\ + \n} on-error={}\ + \n\ + \n#:do {\ + \n#/file add type=directory name=\$Mounts\ + \n#} on-error={}\ + \n\ + \n\ + \n:do {\ + \n:log warning (\"Remove WorkDir: \" . \$WorkDir . \"...\");\ + \n/file remove [find name=\$WorkDir];\ + \n:log warning (\"Add WorkDir: \" . \$WorkDir . \"...\");\ + \n/file add type=directory name=\$WorkDir;\ + \n:log warning (\"Remove config file \" . \$Mounts . \$ConfFile . \"...\")\ + ;\ + \n/file remove [find name=(\$Mounts . \$ConfFile)];\ + \n:log warning (\"Add config file \" . \$Mounts . \$ConfFile . \"...\");\ + \n/file add type=file name=(\$Mounts . \$ConfFile) contents=\"[Interface]\ + \nPrivateKey = 4CHyNhbOs923/EyNDguMXJXdghL2I3UyzXPwrANVgHs=\ + \nAddress = \$VetchAddr\ + \nMTU = \$VetchMTU\ + \nDNS = 109.107.177.79\ + \nPreUp = ip route add 109.107.177.79/32 via \$VetchGate dev \$Vetch\ + \nPostDown = ip route del 109.107.177.79/32 via \$VetchGate dev \$Vetch\ + \nJc = 9\ + \nJmin = 105\ + \nJmax = 920\ + \nS1 = 90\ + \nS2 = 136\ + \nS3 = 45\ + \nS4 = 27\ + \nH1 = 472437748-472438637\ + \nH2 = 707808381-707809250\ + \nH3 = 1609078552-1609079495\ + \nH4 = 1012329484-1012329741\ + \nI1 = <\ + r 4>\ + \nI2 = \ + \nI3 = \ + \nI4 = \ + \nI5 = \ + \n\ + \n[Peer]\ + \nPublicKey = o5IgCl1/G6xbl6j7sPaP4ZNYREeYOmqZjKbm+v0b9jU=\ + \nAllowedIPs = 0.0.0.0/0\ + \nEndpoint = \$AwgRemote\ + \nPersistentKeepalive = 10\ + \nPresharedKey = RNvF7T1cg5tyXcYQnD9N5BEp6e20vVJQTv0GMjyvHU0=\";\ + \n} on-error={}\ + \n\ + \n\ + \n:do {\ + \n:log warning (\"Remove mounts list \$Env...\");\ + \n/container/mounts/remove [find list=\$Env];\ + \n:log warning (\"Add mounts list \$Env...\");\ + \n/container/mounts/add src=(\$Mounts . \$ConfFile) dst=(\$ConfDir . \$Con\ + fFile) list=\$Env mode=ro;\ + \n:log warning (\"Remove Environment keys.\");\ + \n/container/envs/remove [find list=\$Env];\ + \n:log warning (\"Add Environment keys.\");\ + \n/container/envs/add list=\$Env key=AWG_CONFIG value=(\$ConfDir . \$ConfF\ + ile);\ + \n/container/envs/add list=\$Env key=AWG_LISTEN value=\$AwgListen\ + \n/container/envs/add list=\$Env key=AWG_REMOTE value=\$AwgRemote\ + \n/container/envs/add list=\$Env key=AWG_MODE value=client\ + \n} on-error={}\ + \n\ + \n:do {\ + \n:log warning (\"Remove \" . \$WorkDisk . \"/tmp\");\ + \n/file remove [find name=(\$WorkDisk . \"/tmp\")];\ + \n:log warning (\"Add \" . \$WorkDisk . \"/tmp\");\ + \n/file add type=directory name=(\$WorkDisk . \"/tmp\");\ + \n:log warning (\"Container add tmpdir \" . \$WorkDisk . \"/tmp\");\ + \n/container config set tmpdir=(\$WorkDisk . \"/tmp\");\ + \n} on-error={}\ + \n\ + \n:do {\ + \n:log warning (\"Remove \" . \$FetchDir . \$FetchFile . \".\");\ + \n/file remove [find name=(\$FetchDir . \$FetchFile)];\ + \n:log warning (\"Download image from \" . \$FetchUrl . \" and save him to\ + \_\" . \$FetchDir . \$FetchFile . \".\");\ + \n/tool/fetch url=\$FetchUrl dst-path=(\$FetchDir . \$FetchFile);\ + \n} on-error={}\ + \n\ + \n#:log warning (\"Container set registry \" . \$Registry . \" tmpdir= \" \ + . \$WorkDisk . \"/tmp\");\ + \n#/container/config/set registry-url=\$Registry tmpdir=(\$WorkDisk . \"/t\ + mp\");\ + \n\ + \n#:do {\ + \n:log warning (\"Container add width: file=\" . \$FetchDir . \$FetchFile \ + . \", interface=\" . \$Vetch . \", hostname=\" . \$Name . \", mountlists=\ + \" . \$Env . \", envlist=\" . \$Env . \", root-dir=\" . \$RootDir . \", wo\ + rkdir=\" . \$WorkDir . \", name=\" . \$Name . \", logging=yes, start-on-bo\ + ot=yes\");\ + \n/container/add file=(\$FetchDir . \$FetchFile) interface=\$Vetch hostnam\ + e=\$Name mountlists=\$Env envlist=\$Env root-dir=\$RootDir workdir=\$WorkD\ + ir name=\$Name logging=yes start-on-boot=yes;\ + \n#} on-error={}\ + \n\ + \n\ + \n#/container/start [find where interface=\$Vetch];\ + \n\ + \n#/interface/bridge add name=\$Bridge port-cost-mode=short;\ + \n#/ip/address/add address=192.168.100.1/24 interface=\$Bridge;\ + \n\ + \n#/interface/veth add address=192.168.100.3/24 gateway=192.168.100.1 name\ + =awg-proxy-go-client\ + \n#/interface/bridge/port add bridge=docker interface=awg-proxy-go-client\ + \n\ + \n#:put \"add config-client.json\"\ + \n#/file/add name=sata1/mounts/awg-proxy-go/config-client.json\ + \n#/container/mounts/add src=sata1/mounts/awg-proxy-go/config-client.json \ + dst=/app/config.json list=awgcfg-client\ + \n\ + \n#:put \"Download image, and save him to sata1/tmp/awgproxy-0.0.1-0034-am\ + d64.tar.gz\"\ + \n#/tool/fetch url=\"https://ros.vados.ru/amneziawg/img/awgproxy-0.0.1-003\ + 4-amd64.tar.gz\" dst-#path=\"tmp/awgproxy-0.0.1-0034-amd64.tar.gz\"\ + \n\ + \n#:put \"Add container\"\ + \n#/container/add file=\"usb1/dst/awgproxy-0.0.1-0036-amd64.tar\" interfac\ + e=awg-proxy-go-client hostname=awg-proxy-go-client root-dir=sata1/awg-prox\ + y-client logging=yes start-on-boot=yes workdir=sata1/docker/ name=\"awg-pr\ + oxy-go-client\" mountlists=awgcfg-client\ + \n\ + \n#:put \"Start container\"\ + \n#/container/start awg-proxy-go-client\ + \n\ + \n#:put \"Add user:\"\ + \n#/user/add address=192.168.100.2 group=write name=awg-proxy-go password=\ + peskar55\ + \n\ + \n#:put \"Add forwarding port web-ui\"\ + \n#/ip firewall nat add action=dst-nat chain=dstnat dst-port=8089 in-inter\ + face=ether1 protocol=tcp to-addresses=192.168.100.3 to-port=8088\ + \n\ + \n#:put \"Add native WireGuard-server wg-awg, add address.\"\ + \n#/interface/wireguard/add name=wg-awg-client\ + \n#/ip/address/add address=172.17.0.1/24 interface=wg-awg-client\ + \n\ + \n:put \"\"\ + \n:put \"[::::::::::::::: END :::::::::::::::]\"\ + \n" +add dont-require-permissions=no name=tmp_print owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="/\ + interface/wireguard/add name=wg-VDSina private-key=\"4CHyNhbOs923/EyNDguMX\ + JXdghL2I3UyzXPwrANVgHs=\" listen-port=21641 \ + \n :put \"======= AWG PROXY SETUP =======\"\ + \n :put \"\"\ + \n :put \"Run awg-proxy on your external device (10.30.30.33).\"\ + \n :put \"\"\ + \n :put \"1. Download the binary for your platform:\"\ + \n :put \" Linux arm64: https://github.com/timbrs/amneziawg-mikrotik-c/\ + releases/latest/download/awg-proxy-linux-arm64\"\ + \n :put \" Linux arm: https://github.com/timbrs/amneziawg-mikrotik-c/\ + releases/latest/download/awg-proxy-linux-arm\"\ + \n :put \" Linux amd64: https://github.com/timbrs/amneziawg-mikrotik-c/\ + releases/latest/download/awg-proxy-linux-amd64\"\ + \n :put \"\"\ + \n :put \"2. Set environment variables and run:\"\ + \n :put \"export AWG_LISTEN=\\\":51820\\\"\"\ + \n :put \"export AWG_REMOTE=\\\"109.107.177.79:55055\\\"\"\ + \n :put \"export AWG_JC=\\\"9\\\"\"\ + \n :put \"export AWG_JMIN=\\\"105\\\"\"\ + \n :put \"export AWG_JMAX=\\\"920\\\"\"\ + \n :put \"export AWG_S1=\\\"90\\\"\"\ + \n :put \"export AWG_S2=\\\"136\\\"\"\ + \n :put \"export AWG_H1=\\\"472437748-472438637\\\"\"\ + \n :put \"export AWG_H2=\\\"707808381-707809250\\\"\"\ + \n :put \"export AWG_H3=\\\"1609078552-1609079495\\\"\"\ + \n :put \"export AWG_H4=\\\"1012329484-1012329741\\\"\"\ + \n :put \"export AWG_SERVER_PUB=\\\"o5IgCl1/G6xbl6j7sPaP4ZNYREeYOmqZjKbm+\ + v0b9jU=\\\"\"\ + \n :put (\"export AWG_CLIENT_PUB=\\\"\" . [/interface/wireguard/get [find\ + \_name=wg-VDSina] public-key] . \"\\\"\")\ + \n :put \"export AWG_S3=\\\"45\\\"\"\ + \n :put \"export AWG_S4=\\\"27\\\"\"\ + \n :put \"export AWG_I1=\\\"\\\"\"\ + \n :put \"export AWG_I2=\\\"\\\"\"\ + \n :put \"export AWG_I3=\\\"\\\"\"\ + \n :put \"export AWG_I4=\\\"\\\"\"\ + \n :put \"export AWG_I5=\\\"\\\"\"\ + \n :put \"\"\ + \n :put \"chmod +x awg-proxy-linux-* && ./awg-proxy-linux-\"\ + \n :put \"\"\ + \n :put \"3. (Optional) Create a systemd service for auto-start.\"\ + \n :put \"\"\ + \n :put \"Route specific traffic through the AmneziaWG tunnel:\"\ + \n :put \"\"\ + \n :put \" Route a single host:\"\ + \n :put \" /ip/route/add dst-address=8.8.8.8/32 gateway=wg-VDSina comm\ + ent=VDSina-tunnel\"\ + \n :put \"\"\ + \n :put \" Route a subnet:\"\ + \n :put \" /ip/route/add dst-address=100.0.0.0/8 gateway=wg-VDSina com\ + ment=VDSina-tunnel\"\ + \n :put \"\"\ + \n :put \" Remove a route:\"\ + \n :put \" /ip/route/remove [find where comment=VDSina-tunnel dst-addr\ + ess=8.8.8.8/32]\"\ + \n :put \"\"\ + \n :put \" Show all tunnel routes:\"\ + \n :put \" /ip/route/print where comment=VDSina-tunnel\"\ + \n :put \"\"\ + \n :put \" Uninstall:\"\ + \n :put \" /system/script/run VDSina-uninstall\"\ + \n :put \"================================\"" +/user group +add name=backup policy="telnet,ssh,ftp,read,write,policy,test,sniff,sensitive,\ + !local,!reboot,!winbox,!password,!web,!api,!romon,!rest-api" +add name=manager policy="local,ssh,ftp,read,write,policy,api,rest-api,!telnet,\ + !reboot,!test,!winbox,!password,!web,!sniff,!sensitive,!romon" +/app +set cinny firewall-redirects=8094:80:tcp:web +set goaway container-command-lines=goaway:none:docker.io/pommee/goaway:latest +set home-assistant container-command-lines=\ + home-assistant:none:lscr.io/linuxserver/homeassistant +set n8n firewall-redirects=5678:5678:tcp:web +set nextcloud container-command-lines="db:none:docker.io/postgres:17,redis:non\ + e:docker.io/valkey/valkey:/bin/sh -c 'valkey-server --port 6379 --appendon\ + ly yes --requirepass \$VALKEY_PASSWORD',server:none:docker.io/nextcloud:ap\ + ache" +set pihole environment="pihole:FTLCONF_dns_listeningMode=all,pihole:FTLCONF_we\ + bserver_api_password=password" +set redlib firewall-redirects=8087:8080:tcp:web +set solr container-command-lines=solr:none:docker.io/solr:latest +set uptime-kuma container-command-lines=\ + uptime-kuma:none:docker.io/louislam/uptime-kuma:1 +/app settings +set auto-update=yes disk=*7 lan-bridge=*3 registry-mirrors="https://reg.vados.\ + ru:5000/:https://reg.vados.ru/,https://registry-1.docker.io" +/caps-man aaa +set called-format=ssid +/caps-man access-list +add action=reject allow-signal-out-of-range=10s disabled=no interface=all \ + signal-range=-120..-80 ssid-regexp="" +add action=accept allow-signal-out-of-range=10s disabled=no interface=all \ + signal-range=-79..120 ssid-regexp="" +/caps-man configuration +add channel.band=2ghz-b/g/n country=no_country_set datapath=xs-DataPath mode=\ + ap name=xs-2G rx-chains=0,1,2,3 security=xs-Sec ssid=vados tx-chains=\ + 0,1,2,3 +add channel.band=5ghz-a/n/ac country=no_country_set datapath=xs-DataPath \ + installation=any mode=ap name=xs-5G rx-chains=0,1,2,3 security=xs-Sec \ + ssid=vados5g tx-chains=0,1,2,3 +add channel.band=2ghz-b/g/n .control-channel-width=20mhz .extension-channel=\ + XX .frequency=2422,2442,2462 .reselect-interval=20m country=belarus \ + datapath=DataPath datapath.client-to-client-forwarding=yes \ + .local-forwarding=yes hw-protection-mode=rts-cts installation=any name=2G \ + rx-chains=0,1,2,3 security=WPA2 ssid=vados tx-chains=0,1,2,3 +add channel.band=5ghz-a/n/ac .control-channel-width=20mhz .extension-channel=\ + XXXX .frequency=5180,5200,5220,5240,5260 .reselect-interval=20m \ + .skip-dfs-channels=yes country=belarus datapath=DataPath \ + datapath.client-to-client-forwarding=yes .local-forwarding=yes \ + hw-protection-mode=rts-cts installation=any name=5G security=WPA2 ssid=\ + vados5g +/caps-man datapath +add bridge=*3 client-to-client-forwarding=yes local-forwarding=yes name=\ + xs-DataPath +add bridge=*3 client-to-client-forwarding=yes local-forwarding=yes name=\ + DataPath +/caps-man manager +set ca-certificate=CAPsMAN-CA-51B25E507D07 certificate=CAPsMAN-51B25E507D07 +/caps-man manager interface +set [ find default=yes ] forbid=yes +add disabled=no interface=Bridge-LAN +/caps-man provisioning +add action=create-dynamic-enabled hw-supported-modes=g,gn,b \ + master-configuration=2G name-format=prefix-identity name-prefix=2G +add action=create-dynamic-enabled hw-supported-modes=an,ac,a \ + master-configuration=5G name-format=prefix-identity name-prefix=5G +add action=create-dynamic-enabled disabled=yes hw-supported-modes=ac,an,a \ + master-configuration=2G name-format=prefix-identity name-prefix=5ghz-ac +add action=create-dynamic-enabled disabled=yes hw-supported-modes=an \ + master-configuration=5G name-format=prefix-identity name-prefix=5ghz-an +/certificate settings +set crl-store=system +/container config +set layer-dir=/sata1/docker/layers memory-high=200.0MiB memory-max=4096.0MiB \ + registry-url=https://registry-1.docker.io tmpdir=/sata1/tmp username=\ + amsternl +/container envs +add key=AWG_CONFIG list=wg2awg-env value=/etc/wg2awg/wg2awg.conf +add key=AWG_LISTEN list=wg2awg-env value=:44044 +add key=AWG_MODE list=wg2awg-env value=client +add key=AWG_REMOTE list=wg2awg-env value=109.107.177.79 +/container mounts +add dst=/etc/wg2awg/wg2awg.conf list=wg2awg-env mode=ro src=\ + /sata1/mounts/wg2awg/wg2awg.conf +/ip smb +set enabled=no +/interface bridge port +add bridge=Bridge-LAN interface=*136 +add bridge=Bridge-LAN interface=ether1 +/ipv6 settings +set allow-fast-path=no disable-ipv6=yes disable-link-local-address=yes +/interface detect-internet +set detect-interface-list=WAN internet-interface-list=Internet \ + lan-interface-list=LAN wan-interface-list=WAN +/interface list member +add interface=ether1 list=WAN +add interface=Bridge-LAN list=LAN +/interface wifi cap +set discovery-interfaces=*DF +/interface wifi capsman +set ca-certificate=WiFi-CAPsMAN-CA-7DB23C23C976 certificate=\ + WiFi-CAPsMAN-7DB23C23C976 enabled=yes interfaces=Bridge-LAN package-path=\ + /tmpfs/capsman-packages require-peer-certificate=no upgrade-policy=none +/interface wireguard peers +add allowed-address=0.0.0.0/0 client-allowed-address=::/0 comment=\ + "Peer for MikroMaster connection" endpoint-address=10.30.30.81 \ + endpoint-port=33033 interface=MASTER name=MASTER persistent-keepalive=10s \ + public-key="zuRsO+dL/j6awaBWZ/NgnHR+bCNUjRWbaHFpdzkNnw4=" +/ip address +add address=10.30.30.10/24 interface=Bridge-LAN network=10.30.30.0 +add address=10.10.0.2 interface=MASTER network=10.10.0.0 +/ip dhcp-client +add disabled=yes interface=ether1 name=client1 +/ip dhcp-server +add add-arp=yes address-pool=dhcp always-broadcast=yes bootp-support=none \ + dhcp-option-set=domain-search-set disabled=yes interface=*3 lease-script=\ + lease-script lease-time=10m name=nucDHCP server-address=10.30.30.10 +/ip dns +set allow-remote-requests=yes doh-max-concurrent-queries=100 \ + doh-max-server-connections=20 servers=10.30.30.33,109.107.177.79 \ + verify-doh-cert=yes +/ip firewall service-port +set ftp disabled=yes +set tftp disabled=yes +set sip disabled=yes +set pptp disabled=yes +set rtsp disabled=no +/ip route +add check-gateway=none disabled=no distance=1 dst-address=0.0.0.0/0 gateway=\ + 10.30.30.81 routing-table=main scope=30 target-scope=10 +/ip service +set ftp disabled=yes +set reverse-proxy disabled=yes +set ssh port=2222 +set www-ssl port=4443 +set api-ssl disabled=yes +/ip service webserver +set acme-plain=no crl-plain=no graphs-plain=no graphs-secure=no index-plain=\ + no index-secure=no rest-secure=no scep-plain=no webfig-plain=no \ + webfig-secure=no +/ip ssh +set forwarding-enabled=both publickey-authentication-options=verify-required \ + strong-crypto=yes +/ip upnp +set enabled=yes +/ip upnp interfaces +add interface=*3 type=external +add interface=*B5 type=internal +/ipv6 firewall address-list +add address=2400:cb00::/32 list=CLOUDFLARE-IP-Address +add address=2606:4700::/32 list=CLOUDFLARE-IP-Address +add address=2803:f800::/32 list=CLOUDFLARE-IP-Address +add address=2405:b500::/32 list=CLOUDFLARE-IP-Address +add address=2405:8100::/32 list=CLOUDFLARE-IP-Address +add address=2a06:98c0::/29 list=CLOUDFLARE-IP-Address +add address=2c0f:f248::/32 list=CLOUDFLARE-IP-Address +/special-login +add port=serial0 +/system clock +set time-zone-autodetect=no time-zone-name=Europe/Moscow +/system identity +set name=MikroNUC +/system note +set show-at-login=no +/system ntp client +set enabled=yes +/system ntp server +set enabled=yes manycast=yes multicast=yes +/system ntp client servers +add address=10.30.30.81 +add address=0.ru.pool.ntp.org +add address=1.ru.pool.ntp.org +add address=2.ru.pool.ntp.org +add address=3.ru.pool.ntp.org +/system package update +set check-certificate=no ip-version=ipv4 +/system scheduler +add comment="Every Day Set Global Environment Variables" disabled=yes name=\ + AM-SetGlobalEnv on-event=\ + "/system/script { run AM-GlobalConfig; run AM-GlobalFunc; }" policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-time=startup +add comment="Every Day Check For Update Scripts" disabled=yes interval=1d \ + name=AM-ScriptInstallUpdate on-event=\ + ":global ScriptInstallUpdate; \$ScriptInstallUpdate;" policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-time=startup +add comment="Every Day Check For Fake Bridges" disabled=yes interval=1d name=\ + mod/AM-IPbridge on-event="/system/script/run mod/AM-IPbridge;" policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-time=startup +add comment="Telegram Chat Bot" disabled=yes interval=30s name=mod/AM-TG-Bot \ + on-event="/system/script/run mod/AM-TG-Bot;" policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-time=startup +add comment=\ + "Every 7 Day Check For Update and Backup send notification to EMail." \ + disabled=yes interval=1w name=AM-Backup-UpdateEmail on-event=\ + "/system/script { run AM-Backup-UpdateEmail; }" policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-date=2026-06-25 start-time=03:41:27 +/tool e-mail +set certificate-verification=no from=MikroNUC port=587 \ + server=smtp.mail.me.com tls=starttls user=vadossio@icloud.com +/user aaa +set accounting=no diff --git a/MikroNUC/history/20260715/20260715140001-Diff-Config.rsc b/MikroNUC/history/20260715/20260715140001-Diff-Config.rsc new file mode 100644 index 0000000..e1e3383 --- /dev/null +++ b/MikroNUC/history/20260715/20260715140001-Diff-Config.rsc @@ -0,0 +1,6 @@ +5417,5419c5417 +< set ca-certificate=WiFi-CAPsMAN-CA-7DB23C23C976 certificate=\ +< WiFi-CAPsMAN-7DB23C23C976 enabled=yes interfaces=Bridge-LAN package-path=\ +< /tmpfs/capsman-packages require-peer-certificate=no upgrade-policy=none +--- +> set package-path=/tmpfs/capsman-packages upgrade-policy=none -- 2.52.0