diff --git a/.gitignore b/.gitignore index cf89f87..f29d4e8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,13 +1,3 @@ -# backup and temporary files *~ - -# patches and related files -*.orig *.patch -*.rej - -# html files (as generated from markdown) *.html - -# Mac OS X folder settings file -.DS_Store diff --git a/BRANCHES.md b/BRANCHES.md deleted file mode 100644 index 8a0bdad..0000000 --- a/BRANCHES.md +++ /dev/null @@ -1,50 +0,0 @@ -Installing from branches -======================== - -[](https://github.com/eworm-de/routeros-scripts/stargazers) -[](https://github.com/eworm-de/routeros-scripts/network) -[](https://github.com/eworm-de/routeros-scripts/watchers) -[](https://mikrotik.com/download/changelogs/) -[](https://t.me/routeros_scripts) -[](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J) - -[⬅️ Go back to main README](README.md) - -> ⚠️ **Warning**: Living on the edge? Great, read on! -> If not: Please use the `main` branch and leave this page! - -These scripts are developed in a [git](https://git-scm.com/) repository. -Development and experimental branches are used to provide early access -for specific changes. You can install scripts from these branches -for testing. - -## Install single script - -To install a single script from `next` branch: - - $ScriptInstallUpdate script-name "base-url=https://rsc.eworm.de/next/"; - -## Switch existing script - -Alternatively switch an existing script to update from `next` branch: - - /system/script/set comment="base-url=https://rsc.eworm.de/next/" script-name; - $ScriptInstallUpdate; - -## Switch installation - -Last but not least - to switch the complete installation to the `next` -branch edit `global-config-overlay` and add: - - :global ScriptUpdatesBaseUrl "https://rsc.eworm.de/next/"; - -... then reload the configuration and update: - - /system/script/run global-config; - $ScriptInstallUpdate; - -> ℹ️ **Info**: Replace `next` with *whatever* to use another specific branch. - ---- -[⬅️ Go back to main README](README.md) -[⬆️ Go back to top](#top) diff --git a/CERTIFICATES.d/01-dialog-A.avif b/CERTIFICATES.d/01-dialog-A.avif deleted file mode 100644 index 2fc3c9b..0000000 Binary files a/CERTIFICATES.d/01-dialog-A.avif and /dev/null differ diff --git a/CERTIFICATES.d/02-dialog-B.avif b/CERTIFICATES.d/02-dialog-B.avif deleted file mode 100644 index 5e408ab..0000000 Binary files a/CERTIFICATES.d/02-dialog-B.avif and /dev/null differ diff --git a/CERTIFICATES.d/03-window.avif b/CERTIFICATES.d/03-window.avif deleted file mode 100644 index 96039a3..0000000 Binary files a/CERTIFICATES.d/03-window.avif and /dev/null differ diff --git a/CERTIFICATES.d/04-certificate.avif b/CERTIFICATES.d/04-certificate.avif deleted file mode 100644 index e666314..0000000 Binary files a/CERTIFICATES.d/04-certificate.avif and /dev/null differ diff --git a/CERTIFICATES.md b/CERTIFICATES.md deleted file mode 100644 index 5432d78..0000000 --- a/CERTIFICATES.md +++ /dev/null @@ -1,82 +0,0 @@ -Certificate name from browser -============================= - -[](https://github.com/eworm-de/routeros-scripts/stargazers) -[](https://github.com/eworm-de/routeros-scripts/network) -[](https://github.com/eworm-de/routeros-scripts/watchers) -[](https://mikrotik.com/download/changelogs/) -[](https://t.me/routeros_scripts) -[](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J) - -[⬅️ Go back to main README](README.md) - -All well known desktop, mobile and server operating systems come with a -certificate store that is populated with a set of well known and trusted -certificates, acting as *trust anchors*. - -However RouterOS does not, still sometimes a specific certificate is -required to properly verify a chain of trust. One example is downloading -the scripts from this repository with `fetch` command, thus the very -first step of [installation](README.md#the-long-way-in-detail) is importing -the certificate. - -The scripts can install additional certificates when required. This happens -from this repository if available, or from [mkcert.org](https://mkcert.org) -as a fallback. - -Get the certificate's CommonName --------------------------------- - -But how to determine what certificate may be required? Often easiest way -is to use a desktop browser to get that information. This demonstration uses -[Mozilla Firefox](https://www.mozilla.org/firefox/). - -Let's assume we want to make sure the certificate for -[git.eworm.de](https://git.eworm.de/) is available. Open that page in the -browser, then click the *lock* icon in addressbar, followed by "*Connection -secure*". - - - -The dialog will change, click "*More information*". - - - -A new window opens, click the button "*View Certificate*". (That window -can be closed now.) - - - -A new tab opens, showing information on the server certificate and its -chain of trust. The leftmost certificate is what we are interested in. - - - -Now we know that "`ISRG Root X2`" is required, some scripts need just -that information. - -Import a certificate by CommonName ----------------------------------- - -Running the function `$CertificateAvailable` with that name as parameter -makes sure the certificate is available in the device's store: - - $CertificateAvailable "ISRG Root X2"; - -If the certificate is actually available already nothing happens, and there -is no output. Otherwise the certificate is downloaded and imported. - -If importing a certificate with that exact name fails a warning is given -and nothing is actually imported. - -See also --------- - -* [Download, import and update firewall address-lists](doc/fw-addr-lists.md) -* [Manage DNS and DoH servers from netwatch](doc/netwatch-dns.md) -* [Send notifications via Matrix](doc/mod/notification-matrix.md) -* [Send notifications via Ntfy](doc/mod/notification-ntfy.md) - ---- -[⬅️ Go back to main README](README.md) -[⬆️ Go back to top](#top) diff --git a/CONTRIBUTIONS.md b/CONTRIBUTIONS.md deleted file mode 100644 index 0b35c40..0000000 --- a/CONTRIBUTIONS.md +++ /dev/null @@ -1,63 +0,0 @@ -Past Contributions -================== - -[](https://github.com/eworm-de/routeros-scripts/stargazers) -[](https://github.com/eworm-de/routeros-scripts/network) -[](https://github.com/eworm-de/routeros-scripts/watchers) -[](https://mikrotik.com/download/changelogs/) -[](https://t.me/routeros_scripts) -[](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J) - -[⬅️ Go back to main README](README.md) - -Thanks a lot for your contributions! ❤️ - -## Patches - -These persons contributed code or documentation. See the git history -for details! - -* [Anatoly Bubenkov](mailto:bubenkoff@gmail.com) (@bubenkoff) -* [Ben Harris](mailto:mail@bharr.is) (@bharrisau) -* [Daniel Ziegenberg](mailto:daniel@ziegenberg.at) (@ziegenberg) -* [Ignacio Serrano](mailto:ignic@ignic.com) (@ignic) -* [Michael Gisbers](mailto:michael@gisbers.de) (@mgisbers) -* [Miquel Bonastre](mailto:mbonastre@yahoo.com) (@mbonastre) -* @netravnen -* [netztrip](mailto:dave-tvg@netztrip.de) (@netztrip) -* [Stefan Müller](mailto:stefan.mueller.83@gmail.com) (@PackElend) - -## Donations - -Add yourself to the list, -[donate with PayPal](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J)! - -* Abdul Mannan Abbasi -* Andrea Ruffini Perico -* Andrew Cox -* Christoph Boss (@Kampfwurst) -* Daniel Ziegenberg (@ziegenberg) -* Devin Dean (@dd2594gh) -* Evaldo Gardenal -* Florian Estraviz -* Giorgio Bikos -* Harold Schoemaker -* Hugo BV -* Klaus Michael Rübsam -* Leonardo Valeri Manera -* Linux-Schmie.de Michael Gisbers -* Manuel Kuhn -* Marek Čábák -* Oleksandr Yukhymchuk -* Peter Holtkamp -* Peter Ponzel -* Reiner Vehrenkamp -* Richard Österreicher -* Simon Hitzemann -* Sunny Chu (@sunnychuchu) -* Ulrich Wessendorf -* Zac Kornilakis - ---- -[⬅️ Go back to main README](README.md) -[⬆️ Go back to top](#top) diff --git a/COPYING.md b/COPYING.md deleted file mode 100644 index 2fb2e74..0000000 --- a/COPYING.md +++ /dev/null @@ -1,675 +0,0 @@ -### GNU GENERAL PUBLIC LICENSE - -Version 3, 29 June 2007 - -Copyright (C) 2007 Free Software Foundation, Inc. - - -Everyone is permitted to copy and distribute verbatim copies of this -license document, but changing it is not allowed. - -### Preamble - -The GNU General Public License is a free, copyleft license for -software and other kinds of works. - -The licenses for most software and other practical works are designed -to take away your freedom to share and change the works. By contrast, -the GNU General Public License is intended to guarantee your freedom -to share and change all versions of a program--to make sure it remains -free software for all its users. We, the Free Software Foundation, use -the GNU General Public License for most of our software; it applies -also to any other work released this way by its authors. You can apply -it to your programs, too. - -When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -them if you wish), that you receive source code or can get it if you -want it, that you can change the software or use pieces of it in new -free programs, and that you know you can do these things. - -To protect your rights, we need to prevent others from denying you -these rights or asking you to surrender the rights. Therefore, you -have certain responsibilities if you distribute copies of the -software, or if you modify it: responsibilities to respect the freedom -of others. - -For example, if you distribute copies of such a program, whether -gratis or for a fee, you must pass on to the recipients the same -freedoms that you received. You must make sure that they, too, receive -or can get the source code. And you must show them these terms so they -know their rights. - -Developers that use the GNU GPL protect your rights with two steps: -(1) assert copyright on the software, and (2) offer you this License -giving you legal permission to copy, distribute and/or modify it. - -For the developers' and authors' protection, the GPL clearly explains -that there is no warranty for this free software. For both users' and -authors' sake, the GPL requires that modified versions be marked as -changed, so that their problems will not be attributed erroneously to -authors of previous versions. - -Some devices are designed to deny users access to install or run -modified versions of the software inside them, although the -manufacturer can do so. This is fundamentally incompatible with the -aim of protecting users' freedom to change the software. The -systematic pattern of such abuse occurs in the area of products for -individuals to use, which is precisely where it is most unacceptable. -Therefore, we have designed this version of the GPL to prohibit the -practice for those products. If such problems arise substantially in -other domains, we stand ready to extend this provision to those -domains in future versions of the GPL, as needed to protect the -freedom of users. - -Finally, every program is threatened constantly by software patents. -States should not allow patents to restrict development and use of -software on general-purpose computers, but in those that do, we wish -to avoid the special danger that patents applied to a free program -could make it effectively proprietary. To prevent this, the GPL -assures that patents cannot be used to render the program non-free. - -The precise terms and conditions for copying, distribution and -modification follow. - -### TERMS AND CONDITIONS - -#### 0. Definitions. - -"This License" refers to version 3 of the GNU General Public License. - -"Copyright" also means copyright-like laws that apply to other kinds -of works, such as semiconductor masks. - -"The Program" refers to any copyrightable work licensed under this -License. Each licensee is addressed as "you". "Licensees" and -"recipients" may be individuals or organizations. - -To "modify" a work means to copy from or adapt all or part of the work -in a fashion requiring copyright permission, other than the making of -an exact copy. The resulting work is called a "modified version" of -the earlier work or a work "based on" the earlier work. - -A "covered work" means either the unmodified Program or a work based -on the Program. - -To "propagate" a work means to do anything with it that, without -permission, would make you directly or secondarily liable for -infringement under applicable copyright law, except executing it on a -computer or modifying a private copy. Propagation includes copying, -distribution (with or without modification), making available to the -public, and in some countries other activities as well. - -To "convey" a work means any kind of propagation that enables other -parties to make or receive copies. Mere interaction with a user -through a computer network, with no transfer of a copy, is not -conveying. - -An interactive user interface displays "Appropriate Legal Notices" to -the extent that it includes a convenient and prominently visible -feature that (1) displays an appropriate copyright notice, and (2) -tells the user that there is no warranty for the work (except to the -extent that warranties are provided), that licensees may convey the -work under this License, and how to view a copy of this License. If -the interface presents a list of user commands or options, such as a -menu, a prominent item in the list meets this criterion. - -#### 1. Source Code. - -The "source code" for a work means the preferred form of the work for -making modifications to it. "Object code" means any non-source form of -a work. - -A "Standard Interface" means an interface that either is an official -standard defined by a recognized standards body, or, in the case of -interfaces specified for a particular programming language, one that -is widely used among developers working in that language. - -The "System Libraries" of an executable work include anything, other -than the work as a whole, that (a) is included in the normal form of -packaging a Major Component, but which is not part of that Major -Component, and (b) serves only to enable use of the work with that -Major Component, or to implement a Standard Interface for which an -implementation is available to the public in source code form. A -"Major Component", in this context, means a major essential component -(kernel, window system, and so on) of the specific operating system -(if any) on which the executable work runs, or a compiler used to -produce the work, or an object code interpreter used to run it. - -The "Corresponding Source" for a work in object code form means all -the source code needed to generate, install, and (for an executable -work) run the object code and to modify the work, including scripts to -control those activities. However, it does not include the work's -System Libraries, or general-purpose tools or generally available free -programs which are used unmodified in performing those activities but -which are not part of the work. For example, Corresponding Source -includes interface definition files associated with source files for -the work, and the source code for shared libraries and dynamically -linked subprograms that the work is specifically designed to require, -such as by intimate data communication or control flow between those -subprograms and other parts of the work. - -The Corresponding Source need not include anything that users can -regenerate automatically from other parts of the Corresponding Source. - -The Corresponding Source for a work in source code form is that same -work. - -#### 2. Basic Permissions. - -All rights granted under this License are granted for the term of -copyright on the Program, and are irrevocable provided the stated -conditions are met. This License explicitly affirms your unlimited -permission to run the unmodified Program. The output from running a -covered work is covered by this License only if the output, given its -content, constitutes a covered work. This License acknowledges your -rights of fair use or other equivalent, as provided by copyright law. - -You may make, run and propagate covered works that you do not convey, -without conditions so long as your license otherwise remains in force. -You may convey covered works to others for the sole purpose of having -them make modifications exclusively for you, or provide you with -facilities for running those works, provided that you comply with the -terms of this License in conveying all material for which you do not -control copyright. Those thus making or running the covered works for -you must do so exclusively on your behalf, under your direction and -control, on terms that prohibit them from making any copies of your -copyrighted material outside their relationship with you. - -Conveying under any other circumstances is permitted solely under the -conditions stated below. Sublicensing is not allowed; section 10 makes -it unnecessary. - -#### 3. Protecting Users' Legal Rights From Anti-Circumvention Law. - -No covered work shall be deemed part of an effective technological -measure under any applicable law fulfilling obligations under article -11 of the WIPO copyright treaty adopted on 20 December 1996, or -similar laws prohibiting or restricting circumvention of such -measures. - -When you convey a covered work, you waive any legal power to forbid -circumvention of technological measures to the extent such -circumvention is effected by exercising rights under this License with -respect to the covered work, and you disclaim any intention to limit -operation or modification of the work as a means of enforcing, against -the work's users, your or third parties' legal rights to forbid -circumvention of technological measures. - -#### 4. Conveying Verbatim Copies. - -You may convey verbatim copies of the Program's source code as you -receive it, in any medium, provided that you conspicuously and -appropriately publish on each copy an appropriate copyright notice; -keep intact all notices stating that this License and any -non-permissive terms added in accord with section 7 apply to the code; -keep intact all notices of the absence of any warranty; and give all -recipients a copy of this License along with the Program. - -You may charge any price or no price for each copy that you convey, -and you may offer support or warranty protection for a fee. - -#### 5. Conveying Modified Source Versions. - -You may convey a work based on the Program, or the modifications to -produce it from the Program, in the form of source code under the -terms of section 4, provided that you also meet all of these -conditions: - -- a) The work must carry prominent notices stating that you modified - it, and giving a relevant date. -- b) The work must carry prominent notices stating that it is - released under this License and any conditions added under - section 7. This requirement modifies the requirement in section 4 - to "keep intact all notices". -- c) You must license the entire work, as a whole, under this - License to anyone who comes into possession of a copy. This - License will therefore apply, along with any applicable section 7 - additional terms, to the whole of the work, and all its parts, - regardless of how they are packaged. This License gives no - permission to license the work in any other way, but it does not - invalidate such permission if you have separately received it. -- d) If the work has interactive user interfaces, each must display - Appropriate Legal Notices; however, if the Program has interactive - interfaces that do not display Appropriate Legal Notices, your - work need not make them do so. - -A compilation of a covered work with other separate and independent -works, which are not by their nature extensions of the covered work, -and which are not combined with it such as to form a larger program, -in or on a volume of a storage or distribution medium, is called an -"aggregate" if the compilation and its resulting copyright are not -used to limit the access or legal rights of the compilation's users -beyond what the individual works permit. Inclusion of a covered work -in an aggregate does not cause this License to apply to the other -parts of the aggregate. - -#### 6. Conveying Non-Source Forms. - -You may convey a covered work in object code form under the terms of -sections 4 and 5, provided that you also convey the machine-readable -Corresponding Source under the terms of this License, in one of these -ways: - -- a) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by the - Corresponding Source fixed on a durable physical medium - customarily used for software interchange. -- b) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by a - written offer, valid for at least three years and valid for as - long as you offer spare parts or customer support for that product - model, to give anyone who possesses the object code either (1) a - copy of the Corresponding Source for all the software in the - product that is covered by this License, on a durable physical - medium customarily used for software interchange, for a price no - more than your reasonable cost of physically performing this - conveying of source, or (2) access to copy the Corresponding - Source from a network server at no charge. -- c) Convey individual copies of the object code with a copy of the - written offer to provide the Corresponding Source. This - alternative is allowed only occasionally and noncommercially, and - only if you received the object code with such an offer, in accord - with subsection 6b. -- d) Convey the object code by offering access from a designated - place (gratis or for a charge), and offer equivalent access to the - Corresponding Source in the same way through the same place at no - further charge. You need not require recipients to copy the - Corresponding Source along with the object code. If the place to - copy the object code is a network server, the Corresponding Source - may be on a different server (operated by you or a third party) - that supports equivalent copying facilities, provided you maintain - clear directions next to the object code saying where to find the - Corresponding Source. Regardless of what server hosts the - Corresponding Source, you remain obligated to ensure that it is - available for as long as needed to satisfy these requirements. -- e) Convey the object code using peer-to-peer transmission, - provided you inform other peers where the object code and - Corresponding Source of the work are being offered to the general - public at no charge under subsection 6d. - -A separable portion of the object code, whose source code is excluded -from the Corresponding Source as a System Library, need not be -included in conveying the object code work. - -A "User Product" is either (1) a "consumer product", which means any -tangible personal property which is normally used for personal, -family, or household purposes, or (2) anything designed or sold for -incorporation into a dwelling. In determining whether a product is a -consumer product, doubtful cases shall be resolved in favor of -coverage. For a particular product received by a particular user, -"normally used" refers to a typical or common use of that class of -product, regardless of the status of the particular user or of the way -in which the particular user actually uses, or expects or is expected -to use, the product. A product is a consumer product regardless of -whether the product has substantial commercial, industrial or -non-consumer uses, unless such uses represent the only significant -mode of use of the product. - -"Installation Information" for a User Product means any methods, -procedures, authorization keys, or other information required to -install and execute modified versions of a covered work in that User -Product from a modified version of its Corresponding Source. The -information must suffice to ensure that the continued functioning of -the modified object code is in no case prevented or interfered with -solely because modification has been made. - -If you convey an object code work under this section in, or with, or -specifically for use in, a User Product, and the conveying occurs as -part of a transaction in which the right of possession and use of the -User Product is transferred to the recipient in perpetuity or for a -fixed term (regardless of how the transaction is characterized), the -Corresponding Source conveyed under this section must be accompanied -by the Installation Information. But this requirement does not apply -if neither you nor any third party retains the ability to install -modified object code on the User Product (for example, the work has -been installed in ROM). - -The requirement to provide Installation Information does not include a -requirement to continue to provide support service, warranty, or -updates for a work that has been modified or installed by the -recipient, or for the User Product in which it has been modified or -installed. Access to a network may be denied when the modification -itself materially and adversely affects the operation of the network -or violates the rules and protocols for communication across the -network. - -Corresponding Source conveyed, and Installation Information provided, -in accord with this section must be in a format that is publicly -documented (and with an implementation available to the public in -source code form), and must require no special password or key for -unpacking, reading or copying. - -#### 7. Additional Terms. - -"Additional permissions" are terms that supplement the terms of this -License by making exceptions from one or more of its conditions. -Additional permissions that are applicable to the entire Program shall -be treated as though they were included in this License, to the extent -that they are valid under applicable law. If additional permissions -apply only to part of the Program, that part may be used separately -under those permissions, but the entire Program remains governed by -this License without regard to the additional permissions. - -When you convey a copy of a covered work, you may at your option -remove any additional permissions from that copy, or from any part of -it. (Additional permissions may be written to require their own -removal in certain cases when you modify the work.) You may place -additional permissions on material, added by you to a covered work, -for which you have or can give appropriate copyright permission. - -Notwithstanding any other provision of this License, for material you -add to a covered work, you may (if authorized by the copyright holders -of that material) supplement the terms of this License with terms: - -- a) Disclaiming warranty or limiting liability differently from the - terms of sections 15 and 16 of this License; or -- b) Requiring preservation of specified reasonable legal notices or - author attributions in that material or in the Appropriate Legal - Notices displayed by works containing it; or -- c) Prohibiting misrepresentation of the origin of that material, - or requiring that modified versions of such material be marked in - reasonable ways as different from the original version; or -- d) Limiting the use for publicity purposes of names of licensors - or authors of the material; or -- e) Declining to grant rights under trademark law for use of some - trade names, trademarks, or service marks; or -- f) Requiring indemnification of licensors and authors of that - material by anyone who conveys the material (or modified versions - of it) with contractual assumptions of liability to the recipient, - for any liability that these contractual assumptions directly - impose on those licensors and authors. - -All other non-permissive additional terms are considered "further -restrictions" within the meaning of section 10. If the Program as you -received it, or any part of it, contains a notice stating that it is -governed by this License along with a term that is a further -restriction, you may remove that term. If a license document contains -a further restriction but permits relicensing or conveying under this -License, you may add to a covered work material governed by the terms -of that license document, provided that the further restriction does -not survive such relicensing or conveying. - -If you add terms to a covered work in accord with this section, you -must place, in the relevant source files, a statement of the -additional terms that apply to those files, or a notice indicating -where to find the applicable terms. - -Additional terms, permissive or non-permissive, may be stated in the -form of a separately written license, or stated as exceptions; the -above requirements apply either way. - -#### 8. Termination. - -You may not propagate or modify a covered work except as expressly -provided under this License. Any attempt otherwise to propagate or -modify it is void, and will automatically terminate your rights under -this License (including any patent licenses granted under the third -paragraph of section 11). - -However, if you cease all violation of this License, then your license -from a particular copyright holder is reinstated (a) provisionally, -unless and until the copyright holder explicitly and finally -terminates your license, and (b) permanently, if the copyright holder -fails to notify you of the violation by some reasonable means prior to -60 days after the cessation. - -Moreover, your license from a particular copyright holder is -reinstated permanently if the copyright holder notifies you of the -violation by some reasonable means, this is the first time you have -received notice of violation of this License (for any work) from that -copyright holder, and you cure the violation prior to 30 days after -your receipt of the notice. - -Termination of your rights under this section does not terminate the -licenses of parties who have received copies or rights from you under -this License. If your rights have been terminated and not permanently -reinstated, you do not qualify to receive new licenses for the same -material under section 10. - -#### 9. Acceptance Not Required for Having Copies. - -You are not required to accept this License in order to receive or run -a copy of the Program. Ancillary propagation of a covered work -occurring solely as a consequence of using peer-to-peer transmission -to receive a copy likewise does not require acceptance. However, -nothing other than this License grants you permission to propagate or -modify any covered work. These actions infringe copyright if you do -not accept this License. Therefore, by modifying or propagating a -covered work, you indicate your acceptance of this License to do so. - -#### 10. Automatic Licensing of Downstream Recipients. - -Each time you convey a covered work, the recipient automatically -receives a license from the original licensors, to run, modify and -propagate that work, subject to this License. You are not responsible -for enforcing compliance by third parties with this License. - -An "entity transaction" is a transaction transferring control of an -organization, or substantially all assets of one, or subdividing an -organization, or merging organizations. If propagation of a covered -work results from an entity transaction, each party to that -transaction who receives a copy of the work also receives whatever -licenses to the work the party's predecessor in interest had or could -give under the previous paragraph, plus a right to possession of the -Corresponding Source of the work from the predecessor in interest, if -the predecessor has it or can get it with reasonable efforts. - -You may not impose any further restrictions on the exercise of the -rights granted or affirmed under this License. For example, you may -not impose a license fee, royalty, or other charge for exercise of -rights granted under this License, and you may not initiate litigation -(including a cross-claim or counterclaim in a lawsuit) alleging that -any patent claim is infringed by making, using, selling, offering for -sale, or importing the Program or any portion of it. - -#### 11. Patents. - -A "contributor" is a copyright holder who authorizes use under this -License of the Program or a work on which the Program is based. The -work thus licensed is called the contributor's "contributor version". - -A contributor's "essential patent claims" are all patent claims owned -or controlled by the contributor, whether already acquired or -hereafter acquired, that would be infringed by some manner, permitted -by this License, of making, using, or selling its contributor version, -but do not include claims that would be infringed only as a -consequence of further modification of the contributor version. For -purposes of this definition, "control" includes the right to grant -patent sublicenses in a manner consistent with the requirements of -this License. - -Each contributor grants you a non-exclusive, worldwide, royalty-free -patent license under the contributor's essential patent claims, to -make, use, sell, offer for sale, import and otherwise run, modify and -propagate the contents of its contributor version. - -In the following three paragraphs, a "patent license" is any express -agreement or commitment, however denominated, not to enforce a patent -(such as an express permission to practice a patent or covenant not to -sue for patent infringement). To "grant" such a patent license to a -party means to make such an agreement or commitment not to enforce a -patent against the party. - -If you convey a covered work, knowingly relying on a patent license, -and the Corresponding Source of the work is not available for anyone -to copy, free of charge and under the terms of this License, through a -publicly available network server or other readily accessible means, -then you must either (1) cause the Corresponding Source to be so -available, or (2) arrange to deprive yourself of the benefit of the -patent license for this particular work, or (3) arrange, in a manner -consistent with the requirements of this License, to extend the patent -license to downstream recipients. "Knowingly relying" means you have -actual knowledge that, but for the patent license, your conveying the -covered work in a country, or your recipient's use of the covered work -in a country, would infringe one or more identifiable patents in that -country that you have reason to believe are valid. - -If, pursuant to or in connection with a single transaction or -arrangement, you convey, or propagate by procuring conveyance of, a -covered work, and grant a patent license to some of the parties -receiving the covered work authorizing them to use, propagate, modify -or convey a specific copy of the covered work, then the patent license -you grant is automatically extended to all recipients of the covered -work and works based on it. - -A patent license is "discriminatory" if it does not include within the -scope of its coverage, prohibits the exercise of, or is conditioned on -the non-exercise of one or more of the rights that are specifically -granted under this License. You may not convey a covered work if you -are a party to an arrangement with a third party that is in the -business of distributing software, under which you make payment to the -third party based on the extent of your activity of conveying the -work, and under which the third party grants, to any of the parties -who would receive the covered work from you, a discriminatory patent -license (a) in connection with copies of the covered work conveyed by -you (or copies made from those copies), or (b) primarily for and in -connection with specific products or compilations that contain the -covered work, unless you entered into that arrangement, or that patent -license was granted, prior to 28 March 2007. - -Nothing in this License shall be construed as excluding or limiting -any implied license or other defenses to infringement that may -otherwise be available to you under applicable patent law. - -#### 12. No Surrender of Others' Freedom. - -If conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot convey a -covered work so as to satisfy simultaneously your obligations under -this License and any other pertinent obligations, then as a -consequence you may not convey it at all. For example, if you agree to -terms that obligate you to collect a royalty for further conveying -from those to whom you convey the Program, the only way you could -satisfy both those terms and this License would be to refrain entirely -from conveying the Program. - -#### 13. Use with the GNU Affero General Public License. - -Notwithstanding any other provision of this License, you have -permission to link or combine any covered work with a work licensed -under version 3 of the GNU Affero General Public License into a single -combined work, and to convey the resulting work. The terms of this -License will continue to apply to the part which is the covered work, -but the special requirements of the GNU Affero General Public License, -section 13, concerning interaction through a network will apply to the -combination as such. - -#### 14. Revised Versions of this License. - -The Free Software Foundation may publish revised and/or new versions -of the GNU General Public License from time to time. Such new versions -will be similar in spirit to the present version, but may differ in -detail to address new problems or concerns. - -Each version is given a distinguishing version number. If the Program -specifies that a certain numbered version of the GNU General Public -License "or any later version" applies to it, you have the option of -following the terms and conditions either of that numbered version or -of any later version published by the Free Software Foundation. If the -Program does not specify a version number of the GNU General Public -License, you may choose any version ever published by the Free -Software Foundation. - -If the Program specifies that a proxy can decide which future versions -of the GNU General Public License can be used, that proxy's public -statement of acceptance of a version permanently authorizes you to -choose that version for the Program. - -Later license versions may give you additional or different -permissions. However, no additional obligations are imposed on any -author or copyright holder as a result of your choosing to follow a -later version. - -#### 15. Disclaimer of Warranty. - -THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY -APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT -HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT -WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND -PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE -DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR -CORRECTION. - -#### 16. Limitation of Liability. - -IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR -CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, -INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES -ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT -NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR -LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM -TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER -PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - -#### 17. Interpretation of Sections 15 and 16. - -If the disclaimer of warranty and limitation of liability provided -above cannot be given local legal effect according to their terms, -reviewing courts shall apply local law that most closely approximates -an absolute waiver of all civil liability in connection with the -Program, unless a warranty or assumption of liability accompanies a -copy of the Program in return for a fee. - -END OF TERMS AND CONDITIONS - -### How to Apply These Terms to Your New Programs - -If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these -terms. - -To do so, attach the following notices to the program. It is safest to -attach them to the start of each source file to most effectively state -the exclusion of warranty; and each file should have at least the -"copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - -Also add information on how to contact you by electronic and paper -mail. - -If the program does terminal interaction, make it output a short -notice like this when it starts in an interactive mode: - - Copyright (C) - This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands \`show w' and \`show c' should show the -appropriate parts of the General Public License. Of course, your -program's commands might be different; for a GUI interface, you would -use an "about box". - -You should also get your employer (if you work as a programmer) or -school, if any, to sign a "copyright disclaimer" for the program, if -necessary. For more information on this, and how to apply and follow -the GNU GPL, see . - -The GNU General Public License does not permit incorporating your -program into proprietary programs. If your program is a subroutine -library, you may consider it more useful to permit linking proprietary -applications with the library. If this is what you want to do, use the -GNU Lesser General Public License instead of this License. But first, -please read . diff --git a/DEBUG.md b/DEBUG.md deleted file mode 100644 index d5e9beb..0000000 --- a/DEBUG.md +++ /dev/null @@ -1,63 +0,0 @@ -Debug output and logs -===================== - -[](https://github.com/eworm-de/routeros-scripts/stargazers) -[](https://github.com/eworm-de/routeros-scripts/network) -[](https://github.com/eworm-de/routeros-scripts/watchers) -[](https://mikrotik.com/download/changelogs/) -[](https://t.me/routeros_scripts) -[](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J) - -[⬅️ Go back to main README](README.md) - -Sometimes scripts do not behave as expected. In these cases debug output -or logs can help. - -## Debug output - -Run this command in a terminal: - - :set PrintDebug true; - -You will then see debug output when running the script from terminal. - -To revert to default output run: - - :set PrintDebug false; - -### Debug output for specific script - -Even having debug output for a specific script or function only (or a -set of) is possible. To enable debug output for `telegram-chat` run: - - :set ($PrintDebugOverride->"telegram-chat") true; - -## Debug logs - -The debug info can go to system log. To make it show up in `memory` run: - - /system/logging/add topics=script,debug action=memory; - -Other actions (`disk`, `email`, `remote` or `support`) can be used as -well. I do not recommend using `echo` - use [debug output](#debug-output) -instead. - -Disable or remote that setting to restore regular logging. - -## Verbose output - -Specific scripts can generate huge amount of output. These do use a function -`$LogPrintVerbose`, which is declared, but has no code, intentionally. - -If you *really* want that output set the function to be the same as -`$LogPrint`: - - :set LogPrintVerbose $LogPrint; - -To revert that change just run: - - :set LogPrintVerbose; - ---- -[⬅️ Go back to main README](README.md) -[⬆️ Go back to top](#top) diff --git a/INITIAL-COMMANDS.md b/INITIAL-COMMANDS.md deleted file mode 100644 index 8b64d28..0000000 --- a/INITIAL-COMMANDS.md +++ /dev/null @@ -1,53 +0,0 @@ -Initial commands -================ - -[](https://github.com/eworm-de/routeros-scripts/stargazers) -[](https://github.com/eworm-de/routeros-scripts/network) -[](https://github.com/eworm-de/routeros-scripts/watchers) -[](https://mikrotik.com/download/changelogs/) -[](https://t.me/routeros_scripts) -[](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J) - -[⬅️ Go back to main README](README.md) - -> ⚠️ **Warning**: These command are inteneded for initial setup. If you are -> not aware of the procedure please follow -> [the long way in detail](README.md#the-long-way-in-detail). - -Run the complete base installation: - - { - /tool/fetch "https://git.eworm.de/cgit/routeros-scripts/plain/certs/ISRG-Root-X2.pem" dst-path="isrg-root-x2.pem" as-value; - :delay 1s; - /certificate/import file-name="isrg-root-x2.pem" passphrase=""; - :if ([ :len [ /certificate/find where fingerprint="69729b8e15a86efc177a57afb7171dfc64add28c2fca8cf1507e34453ccb1470" ] ] != 1) do={ - :error "Something is wrong with your certificates!"; - }; - :delay 1s; - /system/script/set name=("global-config-overlay-" . [ /system/clock/get date ] . "-" . [ /system/clock/get time ]) [ find where name="global-config-overlay" ]; - :foreach Script in={ "global-config"; "global-config-overlay"; "global-functions" } do={ - /system/script/remove [ find where name=$Script ]; - /system/script/add name=$Script owner=$Script source=([ /tool/fetch check-certificate=yes-without-crl ("https://git.eworm.de/cgit/routeros-scripts/plain/" . $Script . ".rsc") output=user as-value]->"data"); - }; - /system/script { run global-config; run global-functions; }; - /system/scheduler/remove [ find where name="global-scripts" ]; - /system/scheduler/add name="global-scripts" start-time=startup on-event="/system/script { run global-config; run global-functions; }"; - :global CertificateNameByCN; - $CertificateNameByCN "ISRG Root X2"; - }; - -Then continue setup with -[scheduled automatic updates](README.md#scheduled-automatic-updates) or -[editing configuration](README.md#editing-configuration). - -## Fix existing installation - -The [initial commands](#initial-commands) above allow to fix an existing -installation in case it ever breaks. If `global-config-overlay` did exist -before it is renamed with a date and time suffix (like -`global-config-overlay-2024-01-25-09:33:12`). Make sure to restore the -configuration overlay if required. - ---- -[⬅️ Go back to main README](README.md) -[⬆️ Go back to top](#top) diff --git a/Makefile b/Makefile index d21713c..f96e73e 100644 --- a/Makefile +++ b/Makefile @@ -2,35 +2,24 @@ # template scripts -> final scripts # markdown files -> html files -CAPSMAN = $(wildcard *.capsman.rsc) -LOCAL = $(wildcard *.local.rsc) -WIFI = $(wildcard *.wifi.rsc) +TEMPLATE = $(wildcard *.template) +CAPSMAN = $(TEMPLATE:.template=.capsman) +LOCAL = $(TEMPLATE:.template=.local) -MARKDOWN = $(wildcard *.md doc/*.md doc/mod/*.md) -HTML = $(MARKDOWN:.md=.html) +MARKDOWN = $(wildcard *.md) +HTML = $(MARKDOWN:.md=.html) -all: $(CAPSMAN) $(LOCAL) $(WIFI) $(HTML) +all: $(CAPSMAN) $(LOCAL) $(HTML) %.html: %.md Makefile - markdown $< | sed 's/href="\([-_\./[:alnum:]]*\)\.md"/href="\1.html"/g' > $@ + markdown $< | sed 's/href="\([-[:alnum:]]*\)\.md"/href="\1.html"/g' > $@ -%.capsman.rsc: %.template.rsc Makefile - sed -e '/\/interface\/wifi\//d' -e '/\/interface\/wireless\//d' -e 's|%TEMPL%|.capsman|' \ - -e '/^# NOT \/caps-man\/ #$$/,/^# NOT \/caps-man\/ #$$/d' \ +%.local: %.template Makefile + sed -e '/\/ caps-man/d' -e 's|%PATH%|interface wireless|' -e 's|%TEMPL%|$(suffix $@)|' \ -e '/^# !!/,/^# !!/c # !! Do not edit this file, it is generated from template!' \ < $< > $@ -%.local.rsc: %.template.rsc Makefile - sed -e '/\/caps-man\//d' -e '/\/interface\/wifi\//d' -e 's|%TEMPL%|.local|' \ - -e '/^# NOT \/interface\/wireless\/ #$$/,/^# NOT \/interface\/wireless\/ #$$/d' \ +%.capsman: %.template Makefile + sed -e '/\/ interface wireless/d' -e 's/%PATH%/caps-man/' -e 's/%TEMPL%/$(suffix $@)/' \ -e '/^# !!/,/^# !!/c # !! Do not edit this file, it is generated from template!' \ < $< > $@ - -%.wifi.rsc: %.template.rsc Makefile - sed -e '/\/caps-man\//d' -e '/\/interface\/wireless\//d' -e 's|%TEMPL%|.wifi|' \ - -e '/^# NOT \/interface\/wifi\/ #$$/,/^# NOT \/interface\/wifi\/ #$$/d' \ - -e '/^# !!/,/^# !!/c # !! Do not edit this file, it is generated from template!' \ - < $< > $@ - -clean: - rm -f $(HTML) diff --git a/README.d/01-download-certs.avif b/README.d/01-download-certs.avif deleted file mode 100644 index d41ca05..0000000 Binary files a/README.d/01-download-certs.avif and /dev/null differ diff --git a/README.d/02-import-certs.avif b/README.d/02-import-certs.avif deleted file mode 100644 index bf7d577..0000000 Binary files a/README.d/02-import-certs.avif and /dev/null differ diff --git a/README.d/03-check-certs.avif b/README.d/03-check-certs.avif deleted file mode 100644 index 4717b3e..0000000 Binary files a/README.d/03-check-certs.avif and /dev/null differ diff --git a/README.d/04-import-scripts.avif b/README.d/04-import-scripts.avif deleted file mode 100644 index 53439e4..0000000 Binary files a/README.d/04-import-scripts.avif and /dev/null differ diff --git a/README.d/05-run-and-schedule-scripts.avif b/README.d/05-run-and-schedule-scripts.avif deleted file mode 100644 index 37e1173..0000000 Binary files a/README.d/05-run-and-schedule-scripts.avif and /dev/null differ diff --git a/README.d/06-schedule-update.avif b/README.d/06-schedule-update.avif deleted file mode 100644 index 7c96f3a..0000000 Binary files a/README.d/06-schedule-update.avif and /dev/null differ diff --git a/README.d/07-edit-global-config-overlay.avif b/README.d/07-edit-global-config-overlay.avif deleted file mode 100644 index f87fda8..0000000 Binary files a/README.d/07-edit-global-config-overlay.avif and /dev/null differ diff --git a/README.d/08-apply-configuration.avif b/README.d/08-apply-configuration.avif deleted file mode 100644 index b66af1a..0000000 Binary files a/README.d/08-apply-configuration.avif and /dev/null differ diff --git a/README.d/09-update-scripts.avif b/README.d/09-update-scripts.avif deleted file mode 100644 index f549fef..0000000 Binary files a/README.d/09-update-scripts.avif and /dev/null differ diff --git a/README.d/10-install-scripts.avif b/README.d/10-install-scripts.avif deleted file mode 100644 index 00225b1..0000000 Binary files a/README.d/10-install-scripts.avif and /dev/null differ diff --git a/README.d/11-schedule-script.avif b/README.d/11-schedule-script.avif deleted file mode 100644 index d6eb0f8..0000000 Binary files a/README.d/11-schedule-script.avif and /dev/null differ diff --git a/README.d/12-setup-lease-script.avif b/README.d/12-setup-lease-script.avif deleted file mode 100644 index fb4024e..0000000 Binary files a/README.d/12-setup-lease-script.avif and /dev/null differ diff --git a/README.d/13-install-custom-script.avif b/README.d/13-install-custom-script.avif deleted file mode 100644 index 2f01c43..0000000 Binary files a/README.d/13-install-custom-script.avif and /dev/null differ diff --git a/README.d/14-remove-script.avif b/README.d/14-remove-script.avif deleted file mode 100644 index a5c7daf..0000000 Binary files a/README.d/14-remove-script.avif and /dev/null differ diff --git a/README.d/hello-world.rsc b/README.d/hello-world.rsc deleted file mode 100644 index 6404781..0000000 --- a/README.d/hello-world.rsc +++ /dev/null @@ -1,3 +0,0 @@ -#!rsc by RouterOS - -:put ("Hello World from " . [ /system/identity/get name ] . "!"); diff --git a/README.d/notification-news-and-changes.avif b/README.d/notification-news-and-changes.avif deleted file mode 100644 index d91b8a0..0000000 Binary files a/README.d/notification-news-and-changes.avif and /dev/null differ diff --git a/README.d/telegram-group.avif b/README.d/telegram-group.avif deleted file mode 100644 index eb75d13..0000000 Binary files a/README.d/telegram-group.avif and /dev/null differ diff --git a/README.d/upstream.png b/README.d/upstream.png deleted file mode 100644 index fd5e877..0000000 Binary files a/README.d/upstream.png and /dev/null differ diff --git a/README.md b/README.md index fae6986..2b7a009 100644 --- a/README.md +++ b/README.md @@ -1,52 +1,23 @@ RouterOS Scripts ================ -[](https://github.com/eworm-de/routeros-scripts/stargazers) -[](https://github.com/eworm-de/routeros-scripts/network) -[](https://github.com/eworm-de/routeros-scripts/watchers) -[](https://mikrotik.com/download/changelogs/) -[](https://t.me/routeros_scripts) -[](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J) - - - [RouterOS](https://mikrotik.com/software) is the operating system developed by [MikroTik](https://mikrotik.com/aboutus) for networking tasks. This repository holds a number of [scripts](https://wiki.mikrotik.com/wiki/Manual:Scripting) to manage RouterOS devices or extend their functionality. -*Use at your own risk*, pay attention to -[license and warranty](#license-and-warranty)! +*Use at your own risk!* Requirements ------------ -### Software (RouterOS) +Latest version of the scripts require at least **RouterOS 6.43** to function +properly. The changelog lists the corresponding change as follows: -Latest version of the scripts require recent RouterOS to function properly. -Make sure to install latest updates before you begin. If new functionality -or a breaking change in RouterOS `7.n` is used in my scripts I push my -change some time after `7.(n+1)` was released. At any time you should have -at least two minor and their bugfix releases to choose from. +> *) fetch - added "as-value" output format; -Specific scripts may require even newer RouterOS version. - -> ℹ️ **Info**: The `main` branch is now RouterOS v7 only. If you are still -> running RouterOS v6 switch to `routeros-v6` branch! - -Starting with RouterOS 7.17 the -[device-mode](https://help.mikrotik.com/docs/spaces/ROS/pages/93749258/Device-mode) -has been extended to give more fine-grained control over what features are -available. You need to enable `scheduler` and `fetch` at least, specific -scripts may require additional features. - -### Hardware - -RouterOS packages increase in size with each release. This becomes a -problem for devices with 16MB storage and below, those with an ARM CPU -are specifically affected. - -Huge configuration and lots of scripts give an extra risk. **Take care!** +Specific scripts may require even newer RouterOS version, for example cloud +backup was added in 6.44. Initial setup ------------- @@ -54,9 +25,9 @@ Initial setup ### Get me ready! If you know how things work just copy and paste the -[initial commands](INITIAL-COMMANDS.md). Remember to edit and rerun -`global-config-overlay`! -First time users should take the long way below. +[initial commands](initial-commands). Remember to edit and rerun +`global-config`! +First time useres should take the long way below. ### Live presentation @@ -65,9 +36,6 @@ RouterOS script distribution](https://www.youtube.com/watch?v=B9neG3oAhcY) including demonstation recorded live at [MUM Europe 2019](https://mum.mikrotik.com/2019/EU/) in Vienna. -> ⚠️ **Warning**: Some details changed. So see the presentation, then follow -> the steps below for up-to-date commands. - ### The long way in detail The update script does server certificate verification, so first step is to @@ -75,309 +43,107 @@ download the certificates. If you intend to download the scripts from a different location (for example from github.com) install the corresponding certificate chain. - /tool/fetch "https://git.eworm.de/cgit/routeros-scripts/plain/certs/ISRG-Root-X2.pem" dst-path="isrg-root-x2.pem"; - - + [admin@MikroTik] > / tool fetch "https://git.eworm.de/cgit.cgi/routeros-scripts/plain/certs/letsencrypt.pem" dst-path="letsencrypt.pem" + status: finished + downloaded: 3KiBC-z pause] + total: 3KiB + duration: 1s Note that the commands above do *not* verify server certificate, so if you want to be safe download with your workstations's browser and transfer the -file to your MikroTik device. +files to your MikroTik device. -* [ISRG Root X2](https://letsencrypt.org/certs/isrg-root-x2.pem) +* [ISRG Root X1](https://letsencrypt.org/certs/isrgrootx1.pem.txt) +* [Let's Encrypt Authority X3](https://letsencrypt.org/certs/letsencryptauthorityx3.pem.txt) -Then we import the certificate. +Then we import the certificates. - /certificate/import file-name="isrg-root-x2.pem" passphrase=""; + [admin@MikroTik] > / certificate import file-name=letsencrypt.pem passphrase="" + certificates-imported: 3 + private-keys-imported: 0 + files-imported: 1 + decryption-failures: 0 + keys-with-no-certificate: 0 -Do not worry that the command is not shown - that happens because it contains -a sensitive property, the passphrase. +For basic verification we rename the certifiactes and print their count. Make +sure the certificate count is **three**. - - -For basic verification we rename the certificate and print it by -fingerprint. Make sure exactly this one certificate ("*ISRG-Root-X2*") -is shown. - - /certificate/set name="ISRG-Root-X2" [ find where common-name="ISRG Root X2" ]; - /certificate/print proplist=name,fingerprint where fingerprint="69729b8e15a86efc177a57afb7171dfc64add28c2fca8cf1507e34453ccb1470"; - - + [admin@MikroTik] > / certificate set name="ISRG-Root-X1" [ find where fingerprint="96bcec06264976f37460779acf28c5a7cfe8a3c0aae11a8ffcee05c0bddf08c6" ] + [admin@MikroTik] > / certificate set name="Let-s-Encrypt-Authority-X3" [ find where fingerprint="731d3d9cfaa061487a1d71445a42f67df0afca2a6c2d2f98ff7b3ce112b1f568" ] + [admin@MikroTik] > / certificate set name="DST-Root-CA-X3" [ find where fingerprint="0687260331a72403d909f105e69bcf0d32e1bd2493ffc6d9206d11bcd6770739" ] + [admin@MikroTik] > / certificate print count-only where fingerprint="96bcec06264976f37460779acf28c5a7cfe8a3c0aae11a8ffcee05c0bddf08c6" or fingerprint="731d3d9cfaa061487a1d71445a42f67df0afca2a6c2d2f98ff7b3ce112b1f568" or fingerprint="0687260331a72403d909f105e69bcf0d32e1bd2493ffc6d9206d11bcd6770739" + 3 Always make sure there are no certificates installed you do not know or want! -All following commands will verify the server certificate. For validity the -certificate's lifetime is checked with local time, so make sure the device's -date and time is set correctly! +Actually we do not require the certificate named `DST Root CA X3`, but as it +is used by `Let's Encrypt` to cross-sign we install it anyway - this makes +sure things do not go wrong if the intermediate certificate is replaced. +The IdenTrust certificate *should* be available from their +[download page](https://www.identrust.com/support/downloads). The site is +crap and a good example how to *not* do it. Now let's download the main scripts and add them in configuration on the fly. - :foreach Script in={ "global-config"; "global-config-overlay"; "global-functions" } do={ /system/script/add name=$Script owner=$Script source=([ /tool/fetch check-certificate=yes-without-crl ("https://git.eworm.de/cgit/routeros-scripts/plain/" . $Script . ".rsc") output=user as-value]->"data"); }; + [admin@MikroTik] > :foreach Script in={ "global-config"; "global-functions"; "script-updates" } do={ / system script add name=$Script source=([ / tool fetch check-certificate=yes-without-crl ("https://git.eworm.de/cgit.cgi/routeros-scripts/plain/" . $Script) output=user as-value]->"data"); } - +The configuration needs to be tweaked for your needs. Make sure not to send +your mails to `mail@example.com`! -And finally load configuration and functions and add the scheduler. + [admin@MikroTik] > / system script edit global-config source - /system/script { run global-config; run global-functions; }; - /system/scheduler/add name="global-scripts" start-time=startup on-event="/system/script { run global-config; run global-functions; }"; +And finally load configuration and functions and add the schedulers. - - -### Scheduled automatic updates - -The last step is optional: Add this scheduler **only** if you want the -scripts to be updated automatically! - - /system/scheduler/add name="ScriptInstallUpdate" start-time=startup interval=1d on-event=":global ScriptInstallUpdate; \$ScriptInstallUpdate;"; - - - -Editing configuration ---------------------- - -The configuration needs to be tweaked for your needs. Edit -`global-config-overlay`, copy relevant configuration from -[`global-config`](global-config.rsc) (the one without `-overlay`). -Save changes and exit with `Ctrl-o`. - - /system/script/edit global-config-overlay source; - - - -Additionally creating configuration snippets is supported. The script name -of these snippets has to start with `global-config-overlay.d/` to make them -being loaded automatically. This allows to split off parts of the -configuration. - -To apply your changes run `global-config`, which will automatically load -the overlay as well: - - /system/script/run global-config; - - - -This last step is required when ever you make changes to your configuration. - -> ℹ️ **Info**: It is recommended to edit the configuration using the command -> line interface. If using Winbox on Windows OS, the line endings may be -> missing. To fix this run: -> `/system/script/set source=[ :tocrlf [ get global-config-overlay source ] ] global-config-overlay;` + [admin@MikroTik] > / system script run global-config + [admin@MikroTik] > / system script run global-functions + [admin@MikroTik] > / system scheduler add name=global-config start-time=startup on-event=global-config + [admin@MikroTik] > / system scheduler add name=global-functions start-time=startup on-event=global-functions Updating scripts ---------------- -To update existing scripts just run function `$ScriptInstallUpdate`. If -everything is up-to-date it will not produce any output. +To update existing scripts just run `script-updates`. - $ScriptInstallUpdate; - - - -If the update includes news or requires configuration changes a notification -is sent - in addition to terminal output and log messages. - - + [admin@MikroTik] > / system script run script-updates Adding a script --------------- -To add a script from the repository run function `$ScriptInstallUpdate` with -a comma separated list of script names. +To add a script from the repository create a configuration item first, then +update scripts to fetch the source. - $ScriptInstallUpdate check-certificates,check-routeros-update; - - + [admin@MikroTik] > / system script add name=check-routeros-update + [admin@MikroTik] > / system script run script-updates Scheduler and events -------------------- Most scripts are designed to run regularly from [scheduler](https://wiki.mikrotik.com/wiki/Manual:System/Scheduler). We just -added `check-routeros-update`, so let's run it daily to make sure not to +added `check-routeros-update`, so let's run it every hour to make sure not to miss an update. - /system/scheduler/add name="check-routeros-update" interval=1d start-time=startup on-event="/system/script/run check-routeros-update;"; - - + [admin@MikroTik] > / system scheduler add name=check-routeros-update interval=1h on-event=check-routeros-update Some events can run a script. If you want your DHCP hostnames to be available in DNS use `dhcp-to-dns` with the events from dhcp server. For a regular cleanup add a scheduler entry. - $ScriptInstallUpdate dhcp-to-dns,lease-script; - /ip/dhcp-server/set lease-script=lease-script [ find ]; - /system/scheduler/add name="dhcp-to-dns" interval=5m on-event="/system/script/run dhcp-to-dns;"; - - + [admin@MikroTik] > / system script add name=dhcp-to-dns + [admin@MikroTik] > / system script run script-updates + [admin@MikroTik] > / ip dhcp-server set lease-script=dhcp-to-dns [ find ] + [admin@MikroTik] > / system scheduler add name=dhcp-to-dns interval=5m on-event=dhcp-to-dns There's much more to explore... Have fun! -Available scripts ------------------ - -* [Find and remove access list duplicates](doc/accesslist-duplicates.md) -* [Upload backup to Mikrotik cloud](doc/backup-cloud.md) -* [Send backup via e-mail](doc/backup-email.md) -* [Save configuration to fallback partition](doc/backup-partition.md) -* [Upload backup to server](doc/backup-upload.md) -* [Download packages for CAP upgrade from CAPsMAN](doc/capsman-download-packages.md) -* [Run rolling CAP upgrades from CAPsMAN](doc/capsman-rolling-upgrade.md) -* [Renew locally issued certificates](doc/certificate-renew-issued.md) -* [Renew certificates and notify on expiration](doc/check-certificates.md) -* [Notify about health state](doc/check-health.md) -* [Notify on LTE firmware upgrade](doc/check-lte-firmware-upgrade.md) -* [Notify on RouterOS update](doc/check-routeros-update.md) -* [Collect MAC addresses in wireless access list](doc/collect-wireless-mac.md) -* [Use wireless network with daily psk](doc/daily-psk.md) -* [Comment DHCP leases with info from access list](doc/dhcp-lease-comment.md) -* [Create DNS records for DHCP leases](doc/dhcp-to-dns.md) -* [Automatically upgrade firmware and reboot](doc/firmware-upgrade-reboot.md) -* [Download, import and update firewall address-lists](doc/fw-addr-lists.md) -* [Wait for global functions und modules](doc/global-wait.md) -* [Send GPS position to server](doc/gps-track.md) -* [Use WPA network with hotspot credentials](doc/hotspot-to-wpa.md) -* [Create DNS records for IPSec peers](doc/ipsec-to-dns.md) -* [Update configuration on IPv6 prefix change](doc/ipv6-update.md) -* [Manage IP addresses with bridge status](doc/ip-addr-bridge.md) -* [Run other scripts on DHCP lease](doc/lease-script.md) -* [Manage LEDs dark mode](doc/leds-mode.md) -* [Forward log messages via notification](doc/log-forward.md) -* [Mode button with multiple presses](doc/mode-button.md) -* [Manage DNS and DoH servers from netwatch](doc/netwatch-dns.md) -* [Notify on host up and down](doc/netwatch-notify.md) -* [Visualize OSPF state via LEDs](doc/ospf-to-leds.md) -* [Manage system update](doc/packages-update.md) -* [Run scripts on ppp connection](doc/ppp-on-up.md) -* [Act on received SMS](doc/sms-action.md) -* [Forward received SMS](doc/sms-forward.md) -* [Play Super Mario theme](doc/super-mario-theme.md) -* [Chat with your router and send commands via Telegram bot](doc/telegram-chat.md) -* [Install LTE firmware upgrade](doc/unattended-lte-firmware-upgrade.md) -* [Update GRE configuration with dynamic addresses](doc/update-gre-address.md) -* [Update tunnelbroker configuration](doc/update-tunnelbroker.md) - -Available modules ------------------ - -* [Manage ports in bridge](doc/mod/bridge-port-to.md) -* [Manage VLANs on bridge ports](doc/mod/bridge-port-vlan.md) -* [Inspect variables](doc/mod/inspectvar.md) -* [IP address calculation](doc/mod/ipcalc.md) -* [Send notifications via e-mail](doc/mod/notification-email.md) -* [Send notifications via Matrix](doc/mod/notification-matrix.md) -* [Send notifications via Ntfy](doc/mod/notification-ntfy.md) -* [Send notifications via Telegram](doc/mod/notification-telegram.md) -* [Download script and run it once](doc/mod/scriptrunonce.md) -* [Import ssh keys for public key authentication](doc/mod/ssh-keys-import.md) - -Installing custom scripts & modules ------------------------------------ - -My scripts cover a lot of use cases, but you may have your own ones. You can -still use my scripts to manage and deploy yours, by specifying `base-url` -(and `url-suffix`) for each script. - -This will fetch and install a script `hello-world.rsc` from the given url: - - $ScriptInstallUpdate hello-world "base-url=https://git.eworm.de/cgit/routeros-scripts-custom/plain/"; - - - -For a script to be considered valid it has to begin with a *magic token*. -Have a look at [any script](README.d/hello-world.rsc) and copy the first line -without modification. - -Starting a script's name with `mod/` makes it a module and it is run -automatically by `global-functions`. - -### Linked custom scripts & modules - -> ⚠️ **Warning**: These links are being provided for your convenience only; -> they do not constitute an endorsement or an approval by me. I bear no -> responsibility for the accuracy, legality or content of the external site -> or for that of subsequent links. Contact the external site for answers to -> questions regarding its content. - -* [Hello World](https://git.eworm.de/cgit/routeros-scripts-custom/about/doc/hello-world.md) - (This is a demo script to show how the linking to external documentation - will be done.) - -> ℹ️ **Info**: You have your own set of scripts and/or modules and want these -> to be listed here? There should be a general info page that links here, -> and documentation for each script. You can start by cloning my -> [Custom RouterOS-Scripts](https://git.eworm.de/cgit/routeros-scripts-custom/) -> (or fork on [GitHub](https://github.com/eworm-de/routeros-scripts-custom) -> or [GitLab](https://gitlab.com/eworm-de/routeros-scripts-custom)) and make -> your changes. Then please [get in contact](#patches-issues-and-whishlist)... - -Removing a script ------------------ - -There is no specific function for script removal. Just remove it from -configuration... - - /system/script/remove to-be-removed; - - - -Possibly a scheduler and other configuration has to be removed as well. - -Contact -------- - -We have a Telegram Group [RouterOS-Scripts](https://t.me/routeros_scripts)! - -[](https://t.me/routeros_scripts) - -Get help, give feedback or just chat - but do not expect free professional -support! - -Contribute ----------- - -Thanks a lot for [past contributions](CONTRIBUTIONS.md)! ❤️ - -### Patches, issues and whishlist - -Feel free to contact me via e-mail or open an -[issue](https://github.com/eworm-de/routeros-scripts/issues) or -[pull request](https://github.com/eworm-de/routeros-scripts/pulls) -at github. - -### Donate - -This project is developed in private spare time and usage is free of charge -for you. If you like the scripts and think this is of value for you or your -business please consider to -[donate with PayPal](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J). - -[](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J) - -Thanks a lot for your support! - -License and warranty --------------------- - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -[GNU General Public License](COPYING.md) for more details. - -Upstream --------- - -[](https://rsc.eworm.de/) +### Upstream URL: [GitHub.com](https://github.com/eworm-de/routeros-scripts#routeros-scripts) Mirror: -[eworm.de](https://git.eworm.de/cgit/routeros-scripts/about/) +[eworm.de](https://git.eworm.de/cgit.cgi/routeros-scripts/about/) [GitLab.com](https://gitlab.com/eworm-de/routeros-scripts#routeros-scripts) --- -[⬆️ Go back to top](#top) +[▲ Go back to top](#top) diff --git a/accesslist-duplicates.capsman b/accesslist-duplicates.capsman new file mode 100644 index 0000000..a7ae099 --- /dev/null +++ b/accesslist-duplicates.capsman @@ -0,0 +1,34 @@ +#!rsc +# RouterOS script: accesslist-duplicates.capsman +# Copyright (c) 2018-2019 Christian Hesse +# +# print duplicate antries in wireless access list +# +# !! Do not edit this file, it is generated from template! + +:local Seen [ :toarray "" ]; +:local Shown [ :toarray "" ]; + +:foreach AccList in=[ / caps-man access-list find where mac-address!="00:00:00:00:00:00" ] do={ + :local Mac [ / caps-man access-list get $AccList mac-address ]; + :foreach SeenMac in=$Seen do={ + :if ($SeenMac = $Mac) do={ + :local Skip 0; + :foreach ShownMac in=$Shown do={ + :if ($ShownMac = $Mac) do={ :set Skip 1; } + } + :if ($Skip = 0) do={ + / caps-man access-list print where mac-address=$Mac; + :set Shown ($Shown, $Mac); + + :put "\nNumeric id to remove, any key to skip!"; + :local Remove ([ :terminal inkey ] - 48); + :if ($Remove >= 0 && $Remove <= 9) do={ + :put ("Removing numeric id " . $Remove . "...\n"); + / caps-man access-list remove $Remove; + } + } + } + } + :set Seen ($Seen, $Mac); +} diff --git a/accesslist-duplicates.capsman.rsc b/accesslist-duplicates.capsman.rsc deleted file mode 100644 index 27546c8..0000000 --- a/accesslist-duplicates.capsman.rsc +++ /dev/null @@ -1,37 +0,0 @@ -#!rsc by RouterOS -# RouterOS script: accesslist-duplicates.capsman -# Copyright (c) 2018-2025 Christian Hesse -# https://rsc.eworm.de/COPYING.md -# -# requires RouterOS, version=7.15 -# -# print duplicate antries in wireless access list -# https://rsc.eworm.de/doc/accesslist-duplicates.md -# -# !! Do not edit this file, it is generated from template! - -:global GlobalFunctionsReady; -:while ($GlobalFunctionsReady != true) do={ :delay 500ms; } - -:local ExitOK false; -:do { - :local ScriptName [ :jobname ]; - - :local Seen ({}); - - :foreach AccList in=[ /caps-man/access-list/find where mac-address!="00:00:00:00:00:00" ] do={ - :local Mac [ /caps-man/access-list/get $AccList mac-address ]; - :if ($Seen->$Mac = 1) do={ - /caps-man/access-list/print where mac-address=$Mac; - :local Remove [ :tonum [ /terminal/ask prompt="\nNumeric id to remove, any key to skip!" ] ]; - - :if ([ :typeof $Remove ] = "num") do={ - :put ("Removing numeric id " . $Remove . "...\n"); - /caps-man/access-list/remove $Remove; - } - } - :set ($Seen->$Mac) 1; - } -} on-error={ - :global ExitError; $ExitError $ExitOK [ :jobname ]; -} diff --git a/accesslist-duplicates.local b/accesslist-duplicates.local new file mode 100644 index 0000000..94b6b18 --- /dev/null +++ b/accesslist-duplicates.local @@ -0,0 +1,34 @@ +#!rsc +# RouterOS script: accesslist-duplicates.local +# Copyright (c) 2018-2019 Christian Hesse +# +# print duplicate antries in wireless access list +# +# !! Do not edit this file, it is generated from template! + +:local Seen [ :toarray "" ]; +:local Shown [ :toarray "" ]; + +:foreach AccList in=[ / interface wireless access-list find where mac-address!="00:00:00:00:00:00" ] do={ + :local Mac [ / interface wireless access-list get $AccList mac-address ]; + :foreach SeenMac in=$Seen do={ + :if ($SeenMac = $Mac) do={ + :local Skip 0; + :foreach ShownMac in=$Shown do={ + :if ($ShownMac = $Mac) do={ :set Skip 1; } + } + :if ($Skip = 0) do={ + / interface wireless access-list print where mac-address=$Mac; + :set Shown ($Shown, $Mac); + + :put "\nNumeric id to remove, any key to skip!"; + :local Remove ([ :terminal inkey ] - 48); + :if ($Remove >= 0 && $Remove <= 9) do={ + :put ("Removing numeric id " . $Remove . "...\n"); + / interface wireless access-list remove $Remove; + } + } + } + } + :set Seen ($Seen, $Mac); +} diff --git a/accesslist-duplicates.local.rsc b/accesslist-duplicates.local.rsc deleted file mode 100644 index 589815d..0000000 --- a/accesslist-duplicates.local.rsc +++ /dev/null @@ -1,37 +0,0 @@ -#!rsc by RouterOS -# RouterOS script: accesslist-duplicates.local -# Copyright (c) 2018-2025 Christian Hesse -# https://rsc.eworm.de/COPYING.md -# -# requires RouterOS, version=7.15 -# -# print duplicate antries in wireless access list -# https://rsc.eworm.de/doc/accesslist-duplicates.md -# -# !! Do not edit this file, it is generated from template! - -:global GlobalFunctionsReady; -:while ($GlobalFunctionsReady != true) do={ :delay 500ms; } - -:local ExitOK false; -:do { - :local ScriptName [ :jobname ]; - - :local Seen ({}); - - :foreach AccList in=[ /interface/wireless/access-list/find where mac-address!="00:00:00:00:00:00" ] do={ - :local Mac [ /interface/wireless/access-list/get $AccList mac-address ]; - :if ($Seen->$Mac = 1) do={ - /interface/wireless/access-list/print where mac-address=$Mac; - :local Remove [ :tonum [ /terminal/ask prompt="\nNumeric id to remove, any key to skip!" ] ]; - - :if ([ :typeof $Remove ] = "num") do={ - :put ("Removing numeric id " . $Remove . "...\n"); - /interface/wireless/access-list/remove $Remove; - } - } - :set ($Seen->$Mac) 1; - } -} on-error={ - :global ExitError; $ExitError $ExitOK [ :jobname ]; -} diff --git a/accesslist-duplicates.template b/accesslist-duplicates.template new file mode 100644 index 0000000..7345ad5 --- /dev/null +++ b/accesslist-duplicates.template @@ -0,0 +1,35 @@ +#!rsc +# RouterOS script: accesslist-duplicates%TEMPL% +# Copyright (c) 2018-2019 Christian Hesse +# +# print duplicate antries in wireless access list +# +# !! This is just a template! Replace '%PATH%' with 'caps-man' +# !! or 'interface wireless'! + +:local Seen [ :toarray "" ]; +:local Shown [ :toarray "" ]; + +:foreach AccList in=[ / %PATH% access-list find where mac-address!="00:00:00:00:00:00" ] do={ + :local Mac [ / %PATH% access-list get $AccList mac-address ]; + :foreach SeenMac in=$Seen do={ + :if ($SeenMac = $Mac) do={ + :local Skip 0; + :foreach ShownMac in=$Shown do={ + :if ($ShownMac = $Mac) do={ :set Skip 1; } + } + :if ($Skip = 0) do={ + / %PATH% access-list print where mac-address=$Mac; + :set Shown ($Shown, $Mac); + + :put "\nNumeric id to remove, any key to skip!"; + :local Remove ([ :terminal inkey ] - 48); + :if ($Remove >= 0 && $Remove <= 9) do={ + :put ("Removing numeric id " . $Remove . "...\n"); + / %PATH% access-list remove $Remove; + } + } + } + } + :set Seen ($Seen, $Mac); +} diff --git a/accesslist-duplicates.template.rsc b/accesslist-duplicates.template.rsc deleted file mode 100644 index ccbca3d..0000000 --- a/accesslist-duplicates.template.rsc +++ /dev/null @@ -1,46 +0,0 @@ -#!rsc by RouterOS -# RouterOS script: accesslist-duplicates%TEMPL% -# Copyright (c) 2018-2025 Christian Hesse -# https://rsc.eworm.de/COPYING.md -# -# requires RouterOS, version=7.15 -# -# print duplicate antries in wireless access list -# https://rsc.eworm.de/doc/accesslist-duplicates.md -# -# !! This is just a template to generate the real script! -# !! Pattern '%TEMPL%' is replaced, paths are filtered. - -:global GlobalFunctionsReady; -:while ($GlobalFunctionsReady != true) do={ :delay 500ms; } - -:local ExitOK false; -:do { - :local ScriptName [ :jobname ]; - - :local Seen ({}); - - :foreach AccList in=[ /caps-man/access-list/find where mac-address!="00:00:00:00:00:00" ] do={ - :foreach AccList in=[ /interface/wifi/access-list/find where mac-address!="00:00:00:00:00:00" ] do={ - :foreach AccList in=[ /interface/wireless/access-list/find where mac-address!="00:00:00:00:00:00" ] do={ - :local Mac [ /caps-man/access-list/get $AccList mac-address ]; - :local Mac [ /interface/wifi/access-list/get $AccList mac-address ]; - :local Mac [ /interface/wireless/access-list/get $AccList mac-address ]; - :if ($Seen->$Mac = 1) do={ - /caps-man/access-list/print where mac-address=$Mac; - /interface/wifi/access-list/print where mac-address=$Mac; - /interface/wireless/access-list/print where mac-address=$Mac; - :local Remove [ :tonum [ /terminal/ask prompt="\nNumeric id to remove, any key to skip!" ] ]; - - :if ([ :typeof $Remove ] = "num") do={ - :put ("Removing numeric id " . $Remove . "...\n"); - /caps-man/access-list/remove $Remove; - /interface/wifi/access-list/remove $Remove; - /interface/wireless/access-list/remove $Remove; - } - } - :set ($Seen->$Mac) 1; - } -} on-error={ - :global ExitError; $ExitError $ExitOK [ :jobname ]; -} diff --git a/accesslist-duplicates.wifi.rsc b/accesslist-duplicates.wifi.rsc deleted file mode 100644 index 527ebb4..0000000 --- a/accesslist-duplicates.wifi.rsc +++ /dev/null @@ -1,37 +0,0 @@ -#!rsc by RouterOS -# RouterOS script: accesslist-duplicates.wifi -# Copyright (c) 2018-2025 Christian Hesse -# https://rsc.eworm.de/COPYING.md -# -# requires RouterOS, version=7.15 -# -# print duplicate antries in wireless access list -# https://rsc.eworm.de/doc/accesslist-duplicates.md -# -# !! Do not edit this file, it is generated from template! - -:global GlobalFunctionsReady; -:while ($GlobalFunctionsReady != true) do={ :delay 500ms; } - -:local ExitOK false; -:do { - :local ScriptName [ :jobname ]; - - :local Seen ({}); - - :foreach AccList in=[ /interface/wifi/access-list/find where mac-address!="00:00:00:00:00:00" ] do={ - :local Mac [ /interface/wifi/access-list/get $AccList mac-address ]; - :if ($Seen->$Mac = 1) do={ - /interface/wifi/access-list/print where mac-address=$Mac; - :local Remove [ :tonum [ /terminal/ask prompt="\nNumeric id to remove, any key to skip!" ] ]; - - :if ([ :typeof $Remove ] = "num") do={ - :put ("Removing numeric id " . $Remove . "...\n"); - /interface/wifi/access-list/remove $Remove; - } - } - :set ($Seen->$Mac) 1; - } -} on-error={ - :global ExitError; $ExitError $ExitOK [ :jobname ]; -} diff --git a/backup-cloud.rsc b/backup-cloud.rsc deleted file mode 100644 index c4e23b2..0000000 --- a/backup-cloud.rsc +++ /dev/null @@ -1,104 +0,0 @@ -#!rsc by RouterOS -# RouterOS script: backup-cloud -# Copyright (c) 2013-2025 Christian Hesse -# https://rsc.eworm.de/COPYING.md -# -# provides: backup-script, order=40 -# requires RouterOS, version=7.15 -# -# upload backup to MikroTik cloud -# https://rsc.eworm.de/doc/backup-cloud.md - -:global GlobalFunctionsReady; -:while ($GlobalFunctionsReady != true) do={ :delay 500ms; } - -:local ExitOK false; -:do { - :local ScriptName [ :jobname ]; - - :global BackupRandomDelay; - :global Identity; - :global PackagesUpdateBackupFailure; - - :global DeviceInfo; - :global FormatLine; - :global HumanReadableNum; - :global LogPrint; - :global MkDir; - :global RandomDelay; - :global RmDir; - :global ScriptFromTerminal; - :global ScriptLock; - :global SendNotification2; - :global SymbolForNotification; - :global WaitForFile; - :global WaitFullyConnected; - - :if ([ $ScriptLock $ScriptName ] = false) do={ - :set PackagesUpdateBackupFailure true; - :set ExitOK true; - :error false; - } - - :if ([ :len [ /system/scheduler/find where name="running-from-backup-partition" ] ] > 0) do={ - $LogPrint warning $ScriptName ("Running from backup partition, refusing to act."); - :set PackagesUpdateBackupFailure true; - :set ExitOK true; - :error false; - } - - $WaitFullyConnected; - - :if ([ $ScriptFromTerminal $ScriptName ] = false && $BackupRandomDelay > 0) do={ - $RandomDelay $BackupRandomDelay; - } - - :if ([ $MkDir ("tmpfs/backup-cloud") ] = false) do={ - $LogPrint error $ScriptName ("Failed creating directory!"); - :set ExitOK true; - :error false; - } - - :local I 5; - :do { - :execute { - :global BackupPassword; - - :local Backup ([ /system/backup/cloud/find ]->0); - :if ([ :typeof $Backup ] = "id") do={ - /system/backup/cloud/upload-file action=create-and-upload \ - password=$BackupPassword replace=$Backup; - } else={ - /system/backup/cloud/upload-file action=create-and-upload \ - password=$BackupPassword; - } - /file/add name="tmpfs/backup-cloud/done"; - } as-string; - :set I ($I - 1); - } while=([ $WaitForFile "tmpfs/backup-cloud/done" 200ms ] = false && $I > 0); - - :if ([ $WaitForFile "tmpfs/backup-cloud/done" ] = true) do={ - :if ($I < 4) do={ - :log warning ($ScriptName . ": Retry successful, please discard previous connection errors."); - } - - :local Cloud [ /system/backup/cloud/get ([ find ]->0) ]; - - $SendNotification2 ({ origin=$ScriptName; \ - subject=([ $SymbolForNotification "floppy-disk,cloud" ] . "Cloud backup"); \ - message=("Uploaded backup for " . $Identity . " to cloud.\n\n" . \ - [ $DeviceInfo ] . "\n\n" . \ - [ $FormatLine "Name" ($Cloud->"name") ] . "\n" . \ - [ $FormatLine "Size" ([ $HumanReadableNum ($Cloud->"size") 1024 ] . "B") ] . "\n" . \ - [ $FormatLine "Download key" ($Cloud->"secret-download-key") ]); silent=true }); - } else={ - $SendNotification2 ({ origin=$ScriptName; \ - subject=([ $SymbolForNotification "floppy-disk,warning-sign" ] . "Cloud backup failed"); \ - message=("Failed uploading backup for " . $Identity . " to cloud!\n\n" . [ $DeviceInfo ]) }); - $LogPrint error $ScriptName ("Failed uploading backup for " . $Identity . " to cloud!"); - :set PackagesUpdateBackupFailure true; - } - $RmDir "tmpfs/backup-cloud"; -} on-error={ - :global ExitError; $ExitError $ExitOK [ :jobname ]; -} diff --git a/backup-email.rsc b/backup-email.rsc deleted file mode 100644 index d097301..0000000 --- a/backup-email.rsc +++ /dev/null @@ -1,140 +0,0 @@ -#!rsc by RouterOS -# RouterOS script: backup-email -# Copyright (c) 2013-2025 Christian Hesse -# https://rsc.eworm.de/COPYING.md -# -# provides: backup-script, order=20 -# requires RouterOS, version=7.15 -# -# create and email backup and config file -# https://rsc.eworm.de/doc/backup-email.md - -:global GlobalFunctionsReady; -:while ($GlobalFunctionsReady != true) do={ :delay 500ms; } - -:local ExitOK false; -:do { - :local ScriptName [ :jobname ]; - - :global BackupPassword; - :global BackupRandomDelay; - :global BackupSendBinary; - :global BackupSendExport; - :global BackupSendGlobalConfig; - :global Domain; - :global Identity; - :global PackagesUpdateBackupFailure; - - :global CleanName; - :global DeviceInfo; - :global FormatLine; - :global LogPrint; - :global MkDir; - :global RandomDelay; - :global ScriptFromTerminal; - :global ScriptLock; - :global SendEMail2; - :global SymbolForNotification; - :global WaitForFile; - :global WaitFullyConnected; - - :if ([ :typeof $SendEMail2 ] = "nothing") do={ - $LogPrint error $ScriptName ("The module for sending notifications via e-mail is not installed."); - :set ExitOK true; - :error false; - } - - :if ($BackupSendBinary != true && \ - $BackupSendExport != true) do={ - $LogPrint error $ScriptName ("Configured to send neither backup nor config export."); - :set ExitOK true; - :error false; - } - - :if ([ $ScriptLock $ScriptName ] = false) do={ - :set PackagesUpdateBackupFailure true; - :set ExitOK true; - :error false; - } - - :if ([ :len [ /system/scheduler/find where name="running-from-backup-partition" ] ] > 0) do={ - $LogPrint warning $ScriptName ("Running from backup partition, refusing to act."); - :set PackagesUpdateBackupFailure true; - :set ExitOK true; - :error false; - } - - $WaitFullyConnected; - - :if ([ $ScriptFromTerminal $ScriptName ] = false && $BackupRandomDelay > 0) do={ - $RandomDelay $BackupRandomDelay; - } - - # filename based on identity - :local DirName ("tmpfs/" . $ScriptName); - :local FileName [ $CleanName ($Identity . "." . $Domain) ]; - :local FilePath ($DirName . "/" . $FileName); - :local BackupFile "none"; - :local ExportFile "none"; - :local ConfigFile "none"; - :local Attach ({}); - - :if ([ $MkDir $DirName ] = false) do={ - $LogPrint error $ScriptName ("Failed creating directory!"); - :set ExitOK true; - :error false; - } - - # binary backup - :if ($BackupSendBinary = true) do={ - /system/backup/save encryption=aes-sha256 name=$FilePath password=$BackupPassword; - $WaitForFile ($FilePath . ".backup"); - :set BackupFile ($FileName . ".backup"); - :set Attach ($Attach, ($FilePath . ".backup")); - } - - # create configuration export - :if ($BackupSendExport = true) do={ - /export terse show-sensitive file=$FilePath; - $WaitForFile ($FilePath . ".rsc"); - :set ExportFile ($FileName . ".rsc"); - :set Attach ($Attach, ($FilePath . ".rsc")); - } - - # global-config-overlay - :if ($BackupSendGlobalConfig = true) do={ - # Do *NOT* use '/file/add ...' here, as it is limited to 4095 bytes! - :execute script={ :put [ /system/script/get global-config-overlay source ]; } \ - file=($FilePath . ".conf\00"); - $WaitForFile ($FilePath . ".conf"); - :set ConfigFile ($FileName . ".conf"); - :set Attach ($Attach, ($FilePath . ".conf")); - } - - # send email with status and files - $SendEMail2 ({ origin=$ScriptName; \ - subject=([ $SymbolForNotification "floppy-disk,incoming-envelope" ] . \ - "Backup & Config"); \ - message=("See attached files for backup and config export for " . \ - $Identity . ".\n\n" . \ - [ $DeviceInfo ] . "\n\n" . \ - [ $FormatLine "Backup file" $BackupFile ] . "\n" . \ - [ $FormatLine "Export file" $ExportFile ] . "\n" . \ - [ $FormatLine "Config file" $ConfigFile ]); \ - attach=$Attach; remove-attach=true }); - - # wait for the mail to be sent - :local I 0; - :while ([ :len [ /file/find where name ~ ($FilePath . "\\.(backup|rsc)\$") ] ] > 0) do={ - :if ($I >= 120) do={ - $LogPrint warning $ScriptName ("Files are still available, sending e-mail failed."); - :set PackagesUpdateBackupFailure true; - :set ExitOK true; - :error false; - } - :delay 1s; - :set I ($I + 1); - } -} on-error={ - :global ExitError; $ExitError $ExitOK [ :jobname ]; -} diff --git a/backup-partition.rsc b/backup-partition.rsc deleted file mode 100644 index 1f0cf2e..0000000 --- a/backup-partition.rsc +++ /dev/null @@ -1,126 +0,0 @@ -#!rsc by RouterOS -# RouterOS script: backup-partition -# Copyright (c) 2022-2025 Christian Hesse -# https://rsc.eworm.de/COPYING.md -# -# provides: backup-script, order=70 -# requires RouterOS, version=7.15 -# requires device-mode, scheduler -# -# save configuration to fallback partition -# https://rsc.eworm.de/doc/backup-partition.md - -:global GlobalFunctionsReady; -:while ($GlobalFunctionsReady != true) do={ :delay 500ms; } - -:local ExitOK false; -:do { - :local ScriptName [ :jobname ]; - - :global BackupPartitionCopyBeforeFeatureUpdate; - :global PackagesUpdateBackupFailure; - - :global LogPrint; - :global ScriptFromTerminal; - :global ScriptLock; - :global VersionToNum; - - :local CopyTo do={ - :local ScriptName [ :tostr $1 ]; - :local FallbackTo [ :toid $2 ]; - :local FallbackToName [ :tostr $3 ]; - - :global LogPrint; - - :do { - /partitions/copy-to $FallbackTo; - $LogPrint info $ScriptName ("Copied RouterOS to partition '" . $FallbackToName . "'."); - :return true; - } on-error={ - $LogPrint error $ScriptName ("Failed copying RouterOS to partition '" . $FallbackToName . "'!"); - :return false; - } - } - - :if ([ $ScriptLock $ScriptName ] = false) do={ - :set PackagesUpdateBackupFailure true; - :set ExitOK true; - :error false; - } - - :if ([ :len [ /system/scheduler/find where name="running-from-backup-partition" ] ] > 0) do={ - $LogPrint warning $ScriptName ("Running from backup partition, refusing to act."); - :set PackagesUpdateBackupFailure true; - :set ExitOK true; - :error false; - } - - :if ([ :len [ /partitions/find ] ] < 2) do={ - $LogPrint error $ScriptName ("Device does not have a fallback partition."); - :set PackagesUpdateBackupFailure true; - :set ExitOK true; - :error false; - } - - :local ActiveRunning [ /partitions/find where active running ]; - - :if ([ :len $ActiveRunning ] < 1) do={ - $LogPrint error $ScriptName ("Device is not running from active partition."); - :set PackagesUpdateBackupFailure true; - :set ExitOK true; - :error false; - } - - :local FallbackToName [ /partitions/get $ActiveRunning fallback-to ]; - :local FallbackTo [ /partition/find where name=$FallbackToName !active ]; - - :if ([ :len $FallbackTo ] < 1) do={ - $LogPrint error $ScriptName ("There is no inactive partition named '" . $FallbackToName . "'."); - :set PackagesUpdateBackupFailure true; - :set ExitOK true; - :error false; - } - - :if ([ /partitions/get $ActiveRunning version ] != [ /partitions/get $FallbackTo version]) do={ - :if ([ $ScriptFromTerminal $ScriptName ] = true) do={ - :put ("The partitions have different RouterOS versions. Copy over to '" . $FallbackToName . "'? [y/N]"); - :if (([ /terminal/inkey timeout=60 ] % 32) = 25) do={ - :if ([ $CopyTo $ScriptName $FallbackTo $FallbackToName ] = false) do={ - :set PackagesUpdateBackupFailure true; - :set ExitOK true; - :error false; - } - } - } else={ - :local Update [ /system/package/update/get ]; - :local NumInstalled [ $VersionToNum ($Update->"installed-version") ]; - :local NumLatest [ $VersionToNum ($Update->"latest-version") ]; - :local BitMask [ $VersionToNum "255.255zero0" ]; - :if ($BackupPartitionCopyBeforeFeatureUpdate = true && $NumLatest > 0 && \ - ($NumInstalled & $BitMask) != ($NumLatest & $BitMask)) do={ - :if ([ $CopyTo $ScriptName $FallbackTo $FallbackToName ] = false) do={ - :set PackagesUpdateBackupFailure true; - :set ExitOK true; - :error false; - } - } - } - } - - :do { - /system/scheduler/add start-time=startup name="running-from-backup-partition" \ - on-event=(":log warning (\"Running from partition '\" . " . \ - "[ /partitions/get [ find where running ] name ] . \"'!\")"); - /partitions/save-config-to $FallbackTo; - /system/scheduler/remove "running-from-backup-partition"; - $LogPrint info $ScriptName ("Saved configuration to partition '" . $FallbackToName . "'."); - } on-error={ - /system/scheduler/remove [ find where name="running-from-backup-partition" ]; - $LogPrint error $ScriptName ("Failed saving configuration to partition '" . $FallbackToName . "'!"); - :set PackagesUpdateBackupFailure true; - :set ExitOK true; - :error false; - } -} on-error={ - :global ExitError; $ExitError $ExitOK [ :jobname ]; -} diff --git a/backup-upload.rsc b/backup-upload.rsc deleted file mode 100644 index 14c3914..0000000 --- a/backup-upload.rsc +++ /dev/null @@ -1,178 +0,0 @@ -#!rsc by RouterOS -# RouterOS script: backup-upload -# Copyright (c) 2013-2025 Christian Hesse -# https://rsc.eworm.de/COPYING.md -# -# provides: backup-script, order=50 -# requires RouterOS, version=7.15 -# requires device-mode, fetch -# -# create and upload backup and config file -# https://rsc.eworm.de/doc/backup-upload.md - -:global GlobalFunctionsReady; -:while ($GlobalFunctionsReady != true) do={ :delay 500ms; } - -:local ExitOK false; -:do { - :local ScriptName [ :jobname ]; - - :global BackupPassword; - :global BackupRandomDelay; - :global BackupSendBinary; - :global BackupSendExport; - :global BackupSendGlobalConfig; - :global BackupUploadPass; - :global BackupUploadUrl; - :global BackupUploadUser; - :global Domain; - :global Identity; - :global PackagesUpdateBackupFailure; - - :global CleanName; - :global DeviceInfo; - :global IfThenElse; - :global LogPrint; - :global MkDir; - :global RandomDelay; - :global RmDir; - :global RmFile; - :global ScriptFromTerminal; - :global ScriptLock; - :global SendNotification2; - :global SymbolForNotification; - :global WaitForFile; - :global WaitFullyConnected; - - :if ($BackupSendBinary != true && \ - $BackupSendExport != true) do={ - $LogPrint error $ScriptName ("Configured to send neither backup nor config export."); - :set ExitOK true; - :error false; - } - - :if ([ $ScriptLock $ScriptName ] = false) do={ - :set PackagesUpdateBackupFailure true; - :set ExitOK true; - :error false; - } - - :if ([ :len [ /system/scheduler/find where name="running-from-backup-partition" ] ] > 0) do={ - $LogPrint warning $ScriptName ("Running from backup partition, refusing to act."); - :set PackagesUpdateBackupFailure true; - :set ExitOK true; - :error false; - } - - $WaitFullyConnected; - - :if ([ $ScriptFromTerminal $ScriptName ] = false && $BackupRandomDelay > 0) do={ - $RandomDelay $BackupRandomDelay; - } - - # filename based on identity - :local DirName ("tmpfs/" . $ScriptName); - :local FileName [ $CleanName ($Identity . "." . $Domain) ]; - :local FilePath ($DirName . "/" . $FileName); - :local BackupFile "none"; - :local ExportFile "none"; - :local ConfigFile "none"; - :local Failed 0; - - :if ([ $MkDir $DirName ] = false) do={ - $LogPrint error $ScriptName ("Failed creating directory!"); - :set ExitOK true; - :error false; - } - - # binary backup - :if ($BackupSendBinary = true) do={ - /system/backup/save encryption=aes-sha256 name=$FilePath password=$BackupPassword; - $WaitForFile ($FilePath . ".backup"); - - :do { - /tool/fetch upload=yes url=($BackupUploadUrl . "/" . $FileName . ".backup") \ - user=$BackupUploadUser password=$BackupUploadPass src-path=($FilePath . ".backup"); - :set BackupFile [ /file/get ($FilePath . ".backup") ]; - :set ($BackupFile->"name") ($FileName . ".backup"); - } on-error={ - $LogPrint error $ScriptName ("Uploading backup file failed!"); - :set BackupFile "failed"; - :set Failed 1; - } - - $RmFile ($FilePath . ".backup"); - } - - # create configuration export - :if ($BackupSendExport = true) do={ - /export terse show-sensitive file=$FilePath; - $WaitForFile ($FilePath . ".rsc"); - - :do { - /tool/fetch upload=yes url=($BackupUploadUrl . "/" . $FileName . ".rsc") \ - user=$BackupUploadUser password=$BackupUploadPass src-path=($FilePath . ".rsc"); - :set ExportFile [ /file/get ($FilePath . ".rsc") ]; - :set ($ExportFile->"name") ($FileName . ".rsc"); - } on-error={ - $LogPrint error $ScriptName ("Uploading configuration export failed!"); - :set ExportFile "failed"; - :set Failed 1; - } - - $RmFile ($FilePath . ".rsc"); - } - - # global-config-overlay - :if ($BackupSendGlobalConfig = true) do={ - # Do *NOT* use '/file/add ...' here, as it is limited to 4095 bytes! - :execute script={ :put [ /system/script/get global-config-overlay source ]; } \ - file=($FilePath . ".conf\00"); - $WaitForFile ($FilePath . ".conf"); - - :do { - /tool/fetch upload=yes url=($BackupUploadUrl . "/" . $FileName . ".conf") \ - user=$BackupUploadUser password=$BackupUploadPass src-path=($FilePath . ".conf"); - :set ConfigFile [ /file/get ($FilePath . ".conf") ]; - :set ($ConfigFile->"name") ($FileName . ".conf"); - } on-error={ - $LogPrint error $ScriptName ("Uploading global-config-overlay failed!"); - :set ConfigFile "failed"; - :set Failed 1; - } - - $RmFile ($FilePath . ".conf"); - } - - :local FileInfo do={ - :local Name $1; - :local File $2; - - :global FormatLine; - :global HumanReadableNum; - :global IfThenElse; - - :return \ - [ $IfThenElse ([ :typeof $File ] = "array") \ - ($Name . ":\n" . [ $FormatLine " name" ($File->"name") ] . "\n" . \ - [ $FormatLine " size" ([ $HumanReadableNum ($File->"size") 1024 ] . "B") ]) \ - [ $FormatLine $Name $File ] ]; - } - - $SendNotification2 ({ origin=$ScriptName; \ - subject=[ $IfThenElse ($Failed > 0) \ - ([ $SymbolForNotification "floppy-disk,warning-sign" ] . "Backup & Config upload with failure") \ - ([ $SymbolForNotification "floppy-disk,arrow-up" ] . "Backup & Config upload") ]; \ - message=("Backup and config export upload for " . $Identity . ".\n\n" . \ - [ $DeviceInfo ] . "\n\n" . \ - [ $FileInfo "Backup file" $BackupFile ] . "\n" . \ - [ $FileInfo "Export file" $ExportFile ] . "\n" . \ - [ $FileInfo "Config file" $ConfigFile ]); silent=true }); - - :if ($Failed = 1) do={ - :set PackagesUpdateBackupFailure true; - } - $RmDir $DirName; -} on-error={ - :global ExitError; $ExitError $ExitOK [ :jobname ]; -} diff --git a/bridge-port-to-default b/bridge-port-to-default new file mode 100644 index 0000000..1117c1d --- /dev/null +++ b/bridge-port-to-default @@ -0,0 +1,31 @@ +#!rsc +# RouterOS script: bridge-port-to-default +# Copyright (c) 2013-2019 Christian Hesse +# +# reset bridge ports to default bridge + +:global BridgePortTo; + +:local Len ([ :len $BridgePortTo ] + 1); + +:if ($Len = 1) do={ + :delay 1s; + :set Len ([ :len $BridgePortTo ] + 1); +} + +:foreach Interface in=[ / interface bridge port find where comment!="" ] do={ + :foreach Comment in=[ :toarray [ / interface bridge port get $Interface comment ] ] do={ + :if ([ :pick $Comment 0 $Len ] = ($BridgePortTo . ":")) do={ + :local InterfaceName [ / interface bridge port get $Interface interface ]; + :local BridgeDefault [ :pick $Comment $Len [ :len $Comment ] ]; + :local BridgeCurrent [ / interface bridge port get $Interface bridge ]; + :if ($BridgeDefault != $BridgeCurrent) do={ + :log info ("Changing interface " . $InterfaceName . " to " . $BridgePortTo . " bridge " . $BridgeDefault); + / interface bridge port set bridge=$BridgeDefault $Interface; + / ip dhcp-client renew [ find where interface=$BridgeDefault ]; + } else={ + :log debug ("Interface " . $InterfaceName . " already connected to " . $BridgePortTo . " bridge " . $BridgeDefault); + } + } + } +} diff --git a/bridge-port-toggle b/bridge-port-toggle new file mode 100644 index 0000000..fc122f6 --- /dev/null +++ b/bridge-port-toggle @@ -0,0 +1,15 @@ +#!rsc +# RouterOS script: bridge-port-toggle +# Copyright (c) 2013-2019 Christian Hesse +# +# toggle bridge ports between default and alt bridge + +:global BridgePortTo; + +:if ($BridgePortTo != "default") do={ + :set BridgePortTo "default"; +} else={ + :set BridgePortTo "alt"; +} + +/ system script run bridge-port-to-default; diff --git a/capsman-download-packages b/capsman-download-packages new file mode 100644 index 0000000..7462699 --- /dev/null +++ b/capsman-download-packages @@ -0,0 +1,39 @@ +#!rsc +# RouterOS script: capsman-download-packages +# Copyright (c) 2018-2019 Christian Hesse +# Michael Gisbers +# +# requires: dont-require-permissions=yes +# +# download and cleanup packages for CAP installation from CAPsMAN + +:global DownloadPackage; +:global CleanFilePath; + +:local PackagePath [ $CleanFilePath [ / caps-man manager get package-path ] ]; +:local InstalledVersion [ / system package update get installed-version ]; +:local Updated false; + +:foreach Package in=[ / file find where type=package \ + package-version!=$InstalledVersion name~("^" . $PackagePath) ] do={ + :local PackageName [ / file get $Package package-name ]; + :local PackageArchitecture [ / file get $Package package-architecture ]; + :if ($PackageArchitecture = "mips") do={ + :set PackageArchitecture "mipsbe"; + } + :if ($PackageName = "wireless@") do={ + :set PackageName "wireless"; + } + :if ([ $DownloadPackage $PackageName $InstalledVersion $PackageArchitecture $PackagePath ] = true) do={ + :set Updated true; + / file remove $Package; + } +} + +:if ($Updated = true) do={ + :if ([ / system script print count-only where name="capsman-rolling-upgrade" ] > 0) do={ + / system script run capsman-rolling-upgrade; + } else={ + / caps-man remote-cap upgrade [ find where version!=$InstalledVersion ]; + } +} diff --git a/capsman-download-packages.capsman.rsc b/capsman-download-packages.capsman.rsc deleted file mode 100644 index 25c43f5..0000000 --- a/capsman-download-packages.capsman.rsc +++ /dev/null @@ -1,92 +0,0 @@ -#!rsc by RouterOS -# RouterOS script: capsman-download-packages.capsman -# Copyright (c) 2018-2025 Christian Hesse -# Michael Gisbers -# https://rsc.eworm.de/COPYING.md -# -# requires RouterOS, version=7.15 -# -# download and cleanup packages for CAP installation from CAPsMAN -# https://rsc.eworm.de/doc/capsman-download-packages.md -# -# !! Do not edit this file, it is generated from template! - -:global GlobalFunctionsReady; -:while ($GlobalFunctionsReady != true) do={ :delay 500ms; } - -:local ExitOK false; -:do { - :local ScriptName [ :jobname ]; - - :global CleanFilePath; - :global DownloadPackage; - :global LogPrint; - :global MkDir; - :global RmFile; - :global ScriptLock; - :global WaitFullyConnected; - - :if ([ $ScriptLock $ScriptName ] = false) do={ - :set ExitOK true; - :error false; - } - $WaitFullyConnected; - - :local PackagePath [ $CleanFilePath [ /caps-man/manager/get package-path ] ]; - :local InstalledVersion [ /system/package/update/get installed-version ]; - :local Updated false; - - :if ([ :len $PackagePath ] = 0) do={ - $LogPrint warning $ScriptName ("The CAPsMAN package path is not defined, can not download packages."); - :set ExitOK true; - :error false; - } - - :if ([ :len [ /file/find where name=$PackagePath type="directory" ] ] = 0) do={ - :if ([ $MkDir $PackagePath ] = false) do={ - $LogPrint warning $ScriptName ("Creating directory at CAPsMAN package path (" . \ - $PackagePath . ") failed!"); - :set ExitOK true; - :error false; - } - $LogPrint info $ScriptName ("Created directory at CAPsMAN package path (" . $PackagePath . \ - "). Please place your packages!"); - } - - :foreach Package in=[ /file/find where type=package \ - package-version!=$InstalledVersion name~("^" . $PackagePath) ] do={ - :local File [ /file/get $Package ]; - :if ($File->"package-architecture" = "mips") do={ - :set ($File->"package-architecture") "mipsbe"; - } - :if ([ $DownloadPackage ($File->"package-name") $InstalledVersion \ - ($File->"package-architecture") $PackagePath ] = true) do={ - :set Updated true; - $RmFile ($File->"name"); - } - } - - :if ([ :len [ /file/find where type=package name~("^" . $PackagePath) ] ] = 0) do={ - $LogPrint info $ScriptName ("No packages available, downloading default set."); - :foreach Arch in={ "arm"; "mipsbe" } do={ - :foreach Package in={ "routeros"; "wireless" } do={ - :if ([ $DownloadPackage $Package $InstalledVersion $Arch $PackagePath ] = true) do={ - :set Updated true; - } - } - } - } - - :if ($Updated = true) do={ - :local Scripts [ /system/script/find where source~"\n# provides: capsman-rolling-upgrade.capsman\r?\n" ]; - :if ([ :len $Scripts ] > 0) do={ - :foreach Script in=$Scripts do={ - /system/script/run $Script; - } - } else={ - /caps-man/remote-cap/upgrade [ find where version!=$InstalledVersion ]; - } - } -} on-error={ - :global ExitError; $ExitError $ExitOK [ :jobname ]; -} diff --git a/capsman-download-packages.template.rsc b/capsman-download-packages.template.rsc deleted file mode 100644 index b269838..0000000 --- a/capsman-download-packages.template.rsc +++ /dev/null @@ -1,103 +0,0 @@ -#!rsc by RouterOS -# RouterOS script: capsman-download-packages%TEMPL% -# Copyright (c) 2018-2025 Christian Hesse -# Michael Gisbers -# https://rsc.eworm.de/COPYING.md -# -# requires RouterOS, version=7.15 -# -# download and cleanup packages for CAP installation from CAPsMAN -# https://rsc.eworm.de/doc/capsman-download-packages.md -# -# !! This is just a template to generate the real script! -# !! Pattern '%TEMPL%' is replaced, paths are filtered. - -:global GlobalFunctionsReady; -:while ($GlobalFunctionsReady != true) do={ :delay 500ms; } - -:local ExitOK false; -:do { - :local ScriptName [ :jobname ]; - - :global CleanFilePath; - :global DownloadPackage; - :global LogPrint; - :global MkDir; - :global RmFile; - :global ScriptLock; - :global WaitFullyConnected; - - :if ([ $ScriptLock $ScriptName ] = false) do={ - :set ExitOK true; - :error false; - } - $WaitFullyConnected; - - :local PackagePath [ $CleanFilePath [ /caps-man/manager/get package-path ] ]; - :local PackagePath [ $CleanFilePath [ /interface/wifi/capsman/get package-path ] ]; - :local InstalledVersion [ /system/package/update/get installed-version ]; - :local Updated false; - - :if ([ :len $PackagePath ] = 0) do={ - $LogPrint warning $ScriptName ("The CAPsMAN package path is not defined, can not download packages."); - :set ExitOK true; - :error false; - } - - :if ([ :len [ /file/find where name=$PackagePath type="directory" ] ] = 0) do={ - :if ([ $MkDir $PackagePath ] = false) do={ - $LogPrint warning $ScriptName ("Creating directory at CAPsMAN package path (" . \ - $PackagePath . ") failed!"); - :set ExitOK true; - :error false; - } - $LogPrint info $ScriptName ("Created directory at CAPsMAN package path (" . $PackagePath . \ - "). Please place your packages!"); - } - - :foreach Package in=[ /file/find where type=package \ - package-version!=$InstalledVersion name~("^" . $PackagePath) ] do={ - :local File [ /file/get $Package ]; - :if ($File->"package-architecture" = "mips") do={ - :set ($File->"package-architecture") "mipsbe"; - } - :if ([ $DownloadPackage ($File->"package-name") $InstalledVersion \ - ($File->"package-architecture") $PackagePath ] = true) do={ - :set Updated true; - $RmFile ($File->"name"); - } - } - - :if ([ :len [ /file/find where type=package name~("^" . $PackagePath) ] ] = 0) do={ - $LogPrint info $ScriptName ("No packages available, downloading default set."); -# NOT /interface/wifi/ # - :foreach Arch in={ "arm"; "mipsbe" } do={ - :foreach Package in={ "routeros"; "wireless" } do={ -# NOT /interface/wifi/ # -# NOT /caps-man/ # - :foreach Arch in={ "arm"; "arm64" } do={ - :local Packages { "arm"={ "routeros"; "wifi-qcom"; "wifi-qcom-ac" }; - "arm64"={ "routeros"; "wifi-qcom" } }; - :foreach Package in=($Packages->$Arch) do={ -# NOT /caps-man/ # - :if ([ $DownloadPackage $Package $InstalledVersion $Arch $PackagePath ] = true) do={ - :set Updated true; - } - } - } - } - - :if ($Updated = true) do={ - :local Scripts [ /system/script/find where source~"\n# provides: capsman-rolling-upgrade%TEMPL%\r?\n" ]; - :if ([ :len $Scripts ] > 0) do={ - :foreach Script in=$Scripts do={ - /system/script/run $Script; - } - } else={ - /caps-man/remote-cap/upgrade [ find where version!=$InstalledVersion ]; - /interface/wifi/capsman/remote-cap/upgrade [ find where version!=$InstalledVersion ]; - } - } -} on-error={ - :global ExitError; $ExitError $ExitOK [ :jobname ]; -} diff --git a/capsman-download-packages.wifi.rsc b/capsman-download-packages.wifi.rsc deleted file mode 100644 index 901bb0a..0000000 --- a/capsman-download-packages.wifi.rsc +++ /dev/null @@ -1,94 +0,0 @@ -#!rsc by RouterOS -# RouterOS script: capsman-download-packages.wifi -# Copyright (c) 2018-2025 Christian Hesse -# Michael Gisbers -# https://rsc.eworm.de/COPYING.md -# -# requires RouterOS, version=7.15 -# -# download and cleanup packages for CAP installation from CAPsMAN -# https://rsc.eworm.de/doc/capsman-download-packages.md -# -# !! Do not edit this file, it is generated from template! - -:global GlobalFunctionsReady; -:while ($GlobalFunctionsReady != true) do={ :delay 500ms; } - -:local ExitOK false; -:do { - :local ScriptName [ :jobname ]; - - :global CleanFilePath; - :global DownloadPackage; - :global LogPrint; - :global MkDir; - :global RmFile; - :global ScriptLock; - :global WaitFullyConnected; - - :if ([ $ScriptLock $ScriptName ] = false) do={ - :set ExitOK true; - :error false; - } - $WaitFullyConnected; - - :local PackagePath [ $CleanFilePath [ /interface/wifi/capsman/get package-path ] ]; - :local InstalledVersion [ /system/package/update/get installed-version ]; - :local Updated false; - - :if ([ :len $PackagePath ] = 0) do={ - $LogPrint warning $ScriptName ("The CAPsMAN package path is not defined, can not download packages."); - :set ExitOK true; - :error false; - } - - :if ([ :len [ /file/find where name=$PackagePath type="directory" ] ] = 0) do={ - :if ([ $MkDir $PackagePath ] = false) do={ - $LogPrint warning $ScriptName ("Creating directory at CAPsMAN package path (" . \ - $PackagePath . ") failed!"); - :set ExitOK true; - :error false; - } - $LogPrint info $ScriptName ("Created directory at CAPsMAN package path (" . $PackagePath . \ - "). Please place your packages!"); - } - - :foreach Package in=[ /file/find where type=package \ - package-version!=$InstalledVersion name~("^" . $PackagePath) ] do={ - :local File [ /file/get $Package ]; - :if ($File->"package-architecture" = "mips") do={ - :set ($File->"package-architecture") "mipsbe"; - } - :if ([ $DownloadPackage ($File->"package-name") $InstalledVersion \ - ($File->"package-architecture") $PackagePath ] = true) do={ - :set Updated true; - $RmFile ($File->"name"); - } - } - - :if ([ :len [ /file/find where type=package name~("^" . $PackagePath) ] ] = 0) do={ - $LogPrint info $ScriptName ("No packages available, downloading default set."); - :foreach Arch in={ "arm"; "arm64" } do={ - :local Packages { "arm"={ "routeros"; "wifi-qcom"; "wifi-qcom-ac" }; - "arm64"={ "routeros"; "wifi-qcom" } }; - :foreach Package in=($Packages->$Arch) do={ - :if ([ $DownloadPackage $Package $InstalledVersion $Arch $PackagePath ] = true) do={ - :set Updated true; - } - } - } - } - - :if ($Updated = true) do={ - :local Scripts [ /system/script/find where source~"\n# provides: capsman-rolling-upgrade.wifi\r?\n" ]; - :if ([ :len $Scripts ] > 0) do={ - :foreach Script in=$Scripts do={ - /system/script/run $Script; - } - } else={ - /interface/wifi/capsman/remote-cap/upgrade [ find where version!=$InstalledVersion ]; - } - } -} on-error={ - :global ExitError; $ExitError $ExitOK [ :jobname ]; -} diff --git a/capsman-rolling-upgrade b/capsman-rolling-upgrade new file mode 100644 index 0000000..431ac02 --- /dev/null +++ b/capsman-rolling-upgrade @@ -0,0 +1,20 @@ +#!rsc +# RouterOS script: capsman-rolling-upgrade +# Copyright (c) 2018-2019 Christian Hesse +# Michael Gisbers +# +# upgrade CAPs one after another + +:local InstalledVersion [ / system package update get installed-version ]; + +:local RemoteCapCount [ /caps-man remote-cap print count-only ]; +:if ($RemoteCapCount > 0) do={ + :local Delay (600 / $RemoteCapCount); + :if ($Delay > 120) do={ :set Delay 120; } + :foreach RemoteCap in=[ / caps-man remote-cap find where version!=$InstalledVersion ] do={ + :local RemoteCapName [ / caps-man remote-cap get $RemoteCap name ]; + :log debug ("Starting upgrade for CAP " . $RemoteCapName . "..."); + / caps-man remote-cap upgrade $RemoteCap; + :delay ($Delay . "s"); + } +} diff --git a/capsman-rolling-upgrade.capsman.rsc b/capsman-rolling-upgrade.capsman.rsc deleted file mode 100644 index 791b3db..0000000 --- a/capsman-rolling-upgrade.capsman.rsc +++ /dev/null @@ -1,50 +0,0 @@ -#!rsc by RouterOS -# RouterOS script: capsman-rolling-upgrade.capsman -# Copyright (c) 2018-2025 Christian Hesse -# Michael Gisbers -# https://rsc.eworm.de/COPYING.md -# -# provides: capsman-rolling-upgrade.capsman -# requires RouterOS, version=7.15 -# -# upgrade CAPs one after another -# https://rsc.eworm.de/doc/capsman-rolling-upgrade.md -# -# !! Do not edit this file, it is generated from template! - -:global GlobalFunctionsReady; -:while ($GlobalFunctionsReady != true) do={ :delay 500ms; } - -:local ExitOK false; -:do { - :local ScriptName [ :jobname ]; - - :global LogPrint; - :global ScriptLock; - - :if ([ $ScriptLock $ScriptName ] = false) do={ - :set ExitOK true; - :error false; - } - - :local InstalledVersion [ /system/package/update/get installed-version ]; - - :local RemoteCapCount [ :len [ /caps-man/remote-cap/find ] ]; - :if ($RemoteCapCount > 0) do={ - :local Delay (600 / $RemoteCapCount); - :if ($Delay > 120) do={ :set Delay 120; } - :foreach RemoteCap in=[ /caps-man/remote-cap/find where version!=$InstalledVersion ] do={ - :local RemoteCapVal [ /caps-man/remote-cap/get $RemoteCap ]; - :if ([ :len $RemoteCapVal ] > 1) do={ - $LogPrint info $ScriptName ("Starting upgrade for " . $RemoteCapVal->"name" . \ - " (" . $RemoteCapVal->"identity" . ")..."); - /caps-man/remote-cap/upgrade $RemoteCap; - } else={ - $LogPrint warning $ScriptName ("Remote CAP vanished, skipping upgrade."); - } - :delay ($Delay . "s"); - } - } -} on-error={ - :global ExitError; $ExitError $ExitOK [ :jobname ]; -} diff --git a/capsman-rolling-upgrade.template.rsc b/capsman-rolling-upgrade.template.rsc deleted file mode 100644 index 0b1cc2b..0000000 --- a/capsman-rolling-upgrade.template.rsc +++ /dev/null @@ -1,58 +0,0 @@ -#!rsc by RouterOS -# RouterOS script: capsman-rolling-upgrade%TEMPL% -# Copyright (c) 2018-2025 Christian Hesse -# Michael Gisbers -# https://rsc.eworm.de/COPYING.md -# -# provides: capsman-rolling-upgrade%TEMPL% -# requires RouterOS, version=7.15 -# -# upgrade CAPs one after another -# https://rsc.eworm.de/doc/capsman-rolling-upgrade.md -# -# !! This is just a template to generate the real script! -# !! Pattern '%TEMPL%' is replaced, paths are filtered. - -:global GlobalFunctionsReady; -:while ($GlobalFunctionsReady != true) do={ :delay 500ms; } - -:local ExitOK false; -:do { - :local ScriptName [ :jobname ]; - - :global LogPrint; - :global ScriptLock; - - :if ([ $ScriptLock $ScriptName ] = false) do={ - :set ExitOK true; - :error false; - } - - :local InstalledVersion [ /system/package/update/get installed-version ]; - - :local RemoteCapCount [ :len [ /caps-man/remote-cap/find ] ]; - :local RemoteCapCount [ :len [ /interface/wifi/capsman/remote-cap/find ] ]; - :if ($RemoteCapCount > 0) do={ - :local Delay (600 / $RemoteCapCount); - :if ($Delay > 120) do={ :set Delay 120; } - :foreach RemoteCap in=[ /caps-man/remote-cap/find where version!=$InstalledVersion ] do={ - :foreach RemoteCap in=[ /interface/wifi/capsman/remote-cap/find where version!=$InstalledVersion ] do={ - :local RemoteCapVal [ /caps-man/remote-cap/get $RemoteCap ]; - :local RemoteCapVal [ /interface/wifi/capsman/remote-cap/get $RemoteCap ]; - :if ([ :len $RemoteCapVal ] > 1) do={ -# NOT /caps-man/ # - :set ($RemoteCapVal->"name") ($RemoteCapVal->"common-name"); -# NOT /caps-man/ # - $LogPrint info $ScriptName ("Starting upgrade for " . $RemoteCapVal->"name" . \ - " (" . $RemoteCapVal->"identity" . ")..."); - /caps-man/remote-cap/upgrade $RemoteCap; - /interface/wifi/capsman/remote-cap/upgrade $RemoteCap; - } else={ - $LogPrint warning $ScriptName ("Remote CAP vanished, skipping upgrade."); - } - :delay ($Delay . "s"); - } - } -} on-error={ - :global ExitError; $ExitError $ExitOK [ :jobname ]; -} diff --git a/capsman-rolling-upgrade.wifi.rsc b/capsman-rolling-upgrade.wifi.rsc deleted file mode 100644 index 4afdee2..0000000 --- a/capsman-rolling-upgrade.wifi.rsc +++ /dev/null @@ -1,51 +0,0 @@ -#!rsc by RouterOS -# RouterOS script: capsman-rolling-upgrade.wifi -# Copyright (c) 2018-2025 Christian Hesse -# Michael Gisbers -# https://rsc.eworm.de/COPYING.md -# -# provides: capsman-rolling-upgrade.wifi -# requires RouterOS, version=7.15 -# -# upgrade CAPs one after another -# https://rsc.eworm.de/doc/capsman-rolling-upgrade.md -# -# !! Do not edit this file, it is generated from template! - -:global GlobalFunctionsReady; -:while ($GlobalFunctionsReady != true) do={ :delay 500ms; } - -:local ExitOK false; -:do { - :local ScriptName [ :jobname ]; - - :global LogPrint; - :global ScriptLock; - - :if ([ $ScriptLock $ScriptName ] = false) do={ - :set ExitOK true; - :error false; - } - - :local InstalledVersion [ /system/package/update/get installed-version ]; - - :local RemoteCapCount [ :len [ /interface/wifi/capsman/remote-cap/find ] ]; - :if ($RemoteCapCount > 0) do={ - :local Delay (600 / $RemoteCapCount); - :if ($Delay > 120) do={ :set Delay 120; } - :foreach RemoteCap in=[ /interface/wifi/capsman/remote-cap/find where version!=$InstalledVersion ] do={ - :local RemoteCapVal [ /interface/wifi/capsman/remote-cap/get $RemoteCap ]; - :if ([ :len $RemoteCapVal ] > 1) do={ - :set ($RemoteCapVal->"name") ($RemoteCapVal->"common-name"); - $LogPrint info $ScriptName ("Starting upgrade for " . $RemoteCapVal->"name" . \ - " (" . $RemoteCapVal->"identity" . ")..."); - /interface/wifi/capsman/remote-cap/upgrade $RemoteCap; - } else={ - $LogPrint warning $ScriptName ("Remote CAP vanished, skipping upgrade."); - } - :delay ($Delay . "s"); - } - } -} on-error={ - :global ExitError; $ExitError $ExitOK [ :jobname ]; -} diff --git a/certificate-renew-issued.rsc b/certificate-renew-issued.rsc deleted file mode 100644 index 91a48de..0000000 --- a/certificate-renew-issued.rsc +++ /dev/null @@ -1,52 +0,0 @@ -#!rsc by RouterOS -# RouterOS script: certificate-renew-issued -# Copyright (c) 2019-2025 Christian Hesse -# https://rsc.eworm.de/COPYING.md -# -# requires RouterOS, version=7.15 -# -# renew locally issued certificates -# https://rsc.eworm.de/doc/certificate-renew-issued.md - -:global GlobalFunctionsReady; -:while ($GlobalFunctionsReady != true) do={ :delay 500ms; } - -:local ExitOK false; -:do { - :local ScriptName [ :jobname ]; - - :global CertIssuedExportPass; - - :global LogPrint; - :global MkDir; - :global ScriptLock; - - :if ([ $ScriptLock $ScriptName ] = false) do={ - :set ExitOK true; - :error false; - } - - :foreach Cert in=[ /certificate/find where issued expires-after<3w ] do={ - :local CertVal [ /certificate/get $Cert ]; - /certificate/issued-revoke $Cert; - /certificate/set name=($CertVal->"name" . "-revoked-" . [ /system/clock/get date ]) $Cert; - /certificate/add name=($CertVal->"name") common-name=($CertVal->"common-name") \ - key-usage=($CertVal->"key-usage") subject-alt-name=($CertVal->"subject-alt-name"); - /certificate/sign ($CertVal->"name") ca=($CertVal->"ca"); - :if ([ :typeof ($CertIssuedExportPass->($CertVal->"common-name")) ] = "str") do={ - :if ([ $MkDir "cert-issued" ] = true) do={ - /certificate/export-certificate ($CertVal->"name") type=pkcs12 \ - file-name=("cert-issued/" . $CertVal->"common-name") \ - export-passphrase=($CertIssuedExportPass->($CertVal->"common-name")); - $LogPrint info $ScriptName ("Issued a new certificate for '" . $CertVal->"common-name" . \ - "', exported to 'cert-issued/" . $CertVal->"common-name" . ".p12'."); - } else={ - $LogPrint warning $ScriptName ("Failed creating directory, not exporting certificate."); - } - } else={ - $LogPrint info $ScriptName ("Issued a new certificate for '" . $CertVal->"common-name" . "'."); - } - } -} on-error={ - :global ExitError; $ExitError $ExitOK [ :jobname ]; -} diff --git a/certs/Certum-Trusted-Network-CA.pem b/certs/Certum-Trusted-Network-CA.pem deleted file mode 100644 index a48e706..0000000 --- a/certs/Certum-Trusted-Network-CA.pem +++ /dev/null @@ -1,29 +0,0 @@ -# Issuer: CN=Certum Trusted Network CA O=Unizeto Technologies S.A. OU=Certum Certification Authority -# Subject: CN=Certum Trusted Network CA O=Unizeto Technologies S.A. OU=Certum Certification Authority -# Label: "Certum Trusted Network CA" -# Serial: 279744 -# MD5 Fingerprint: d5:e9:81:40:c5:18:69:fc:46:2c:89:75:62:0f:aa:78 -# SHA1 Fingerprint: 07:e0:32:e0:20:b7:2c:3f:19:2f:06:28:a2:59:3a:19:a7:0f:06:9e -# SHA256 Fingerprint: 5c:58:46:8d:55:f5:8e:49:7e:74:39:82:d2:b5:00:10:b6:d1:65:37:4a:cf:83:a7:d4:a3:2d:b7:68:c4:40:8e ------BEGIN CERTIFICATE----- -MIIDuzCCAqOgAwIBAgIDBETAMA0GCSqGSIb3DQEBBQUAMH4xCzAJBgNVBAYTAlBM -MSIwIAYDVQQKExlVbml6ZXRvIFRlY2hub2xvZ2llcyBTLkEuMScwJQYDVQQLEx5D -ZXJ0dW0gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxIjAgBgNVBAMTGUNlcnR1bSBU -cnVzdGVkIE5ldHdvcmsgQ0EwHhcNMDgxMDIyMTIwNzM3WhcNMjkxMjMxMTIwNzM3 -WjB+MQswCQYDVQQGEwJQTDEiMCAGA1UEChMZVW5pemV0byBUZWNobm9sb2dpZXMg -Uy5BLjEnMCUGA1UECxMeQ2VydHVtIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MSIw -IAYDVQQDExlDZXJ0dW0gVHJ1c3RlZCBOZXR3b3JrIENBMIIBIjANBgkqhkiG9w0B -AQEFAAOCAQ8AMIIBCgKCAQEA4/t9o3K6wvDJFIf1awFO4W5AB7ptJ11/91sts1rH -UV+rpDKmYYe2bg+G0jACl/jXaVehGDldamR5xgFZrDwxSjh80gTSSyjoIF87B6LM -TXPb865Px1bVWqeWifrzq2jUI4ZZJ88JJ7ysbnKDHDBy3+Ci6dLhdHUZvSqeexVU -BBvXQzmtVSjF4hq79MDkrjhJM8x2hZ85RdKknvISjFH4fOQtf/WsX+sWn7Et0brM -kUJ3TCXJkDhv2/DM+44el1k+1WBO5gUo7Ul5E0u6SNsv+XLTOcr+H9g0cvW0QM8x -AcPs3hEtF10fuFDRXhmnad4HMyjKUJX5p1TLVIZQRan5SQIDAQABo0IwQDAPBgNV -HRMBAf8EBTADAQH/MB0GA1UdDgQWBBQIds3LB/8k9sXN7buQvOKEN0Z19zAOBgNV -HQ8BAf8EBAMCAQYwDQYJKoZIhvcNAQEFBQADggEBAKaorSLOAT2mo/9i0Eidi15y -sHhE49wcrwn9I0j6vSrEuVUEtRCjjSfeC4Jj0O7eDDd5QVsisrCaQVymcODU0HfL -I9MA4GxWL+FpDQ3Zqr8hgVDZBqWo/5U30Kr+4rP1mS1FhIrlQgnXdAIv94nYmem8 -J9RHjboNRhx3zxSkHLmkMcScKHQDNP8zGSal6Q10tz6XxnboJ5ajZt3hrvJBW8qY -VoNzcOSGGtIxQbovvi0TWnZvTuhOgQ4/WwMioBK+ZlgRSssDxLQqKi2WF+A5VLxI -03YnnZotBqbJ7DnSq9ufmgsnAjUpsUCV5/nonFWIGUbWtzT1fs45mtk48VH3Tyw= ------END CERTIFICATE----- diff --git a/certs/DigiCert-Global-Root-G2.pem b/certs/DigiCert-Global-Root-G2.pem deleted file mode 100644 index 8af6c7a..0000000 --- a/certs/DigiCert-Global-Root-G2.pem +++ /dev/null @@ -1,29 +0,0 @@ -# Issuer: CN=DigiCert Global Root G2 O=DigiCert Inc OU=www.digicert.com -# Subject: CN=DigiCert Global Root G2 O=DigiCert Inc OU=www.digicert.com -# Label: "DigiCert Global Root G2" -# Serial: 4293743540046975378534879503202253541 -# MD5 Fingerprint: e4:a6:8a:c8:54:ac:52:42:46:0a:fd:72:48:1b:2a:44 -# SHA1 Fingerprint: df:3c:24:f9:bf:d6:66:76:1b:26:80:73:fe:06:d1:cc:8d:4f:82:a4 -# SHA256 Fingerprint: cb:3c:cb:b7:60:31:e5:e0:13:8f:8d:d3:9a:23:f9:de:47:ff:c3:5e:43:c1:14:4c:ea:27:d4:6a:5a:b1:cb:5f ------BEGIN CERTIFICATE----- -MIIDjjCCAnagAwIBAgIQAzrx5qcRqaC7KGSxHQn65TANBgkqhkiG9w0BAQsFADBh -MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 -d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBH -MjAeFw0xMzA4MDExMjAwMDBaFw0zODAxMTUxMjAwMDBaMGExCzAJBgNVBAYTAlVT -MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j -b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IEcyMIIBIjANBgkqhkiG -9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuzfNNNx7a8myaJCtSnX/RrohCgiN9RlUyfuI -2/Ou8jqJkTx65qsGGmvPrC3oXgkkRLpimn7Wo6h+4FR1IAWsULecYxpsMNzaHxmx -1x7e/dfgy5SDN67sH0NO3Xss0r0upS/kqbitOtSZpLYl6ZtrAGCSYP9PIUkY92eQ -q2EGnI/yuum06ZIya7XzV+hdG82MHauVBJVJ8zUtluNJbd134/tJS7SsVQepj5Wz -tCO7TG1F8PapspUwtP1MVYwnSlcUfIKdzXOS0xZKBgyMUNGPHgm+F6HmIcr9g+UQ -vIOlCsRnKPZzFBQ9RnbDhxSJITRNrw9FDKZJobq7nMWxM4MphQIDAQABo0IwQDAP -BgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUTiJUIBiV -5uNu5g/6+rkS7QYXjzkwDQYJKoZIhvcNAQELBQADggEBAGBnKJRvDkhj6zHd6mcY -1Yl9PMWLSn/pvtsrF9+wX3N3KjITOYFnQoQj8kVnNeyIv/iPsGEMNKSuIEyExtv4 -NeF22d+mQrvHRAiGfzZ0JFrabA0UWTW98kndth/Jsw1HKj2ZL7tcu7XUIOGZX1NG -Fdtom/DzMNU+MeKNhJ7jitralj41E6Vf8PlwUHBHQRFXGU7Aj64GxJUTFy8bJZ91 -8rGOmaFvE7FBcf6IKshPECBV1/MUReXgRPTqh5Uykw7+U0b6LJ3/iyK5S9kJRaTe -pLiaWN0bfVKfjllDiIGknibVb63dDcY3fe0Dkhvld1927jyNxF1WW6LZZm6zNTfl -MrY= ------END CERTIFICATE----- diff --git a/certs/DigiCert-Global-Root-G3.pem b/certs/DigiCert-Global-Root-G3.pem deleted file mode 100644 index 12324dc..0000000 --- a/certs/DigiCert-Global-Root-G3.pem +++ /dev/null @@ -1,22 +0,0 @@ -# Issuer: CN=DigiCert Global Root G3 O=DigiCert Inc OU=www.digicert.com -# Subject: CN=DigiCert Global Root G3 O=DigiCert Inc OU=www.digicert.com -# Label: "DigiCert Global Root G3" -# Serial: 7089244469030293291760083333884364146 -# MD5 Fingerprint: f5:5d:a4:50:a5:fb:28:7e:1e:0f:0d:cc:96:57:56:ca -# SHA1 Fingerprint: 7e:04:de:89:6a:3e:66:6d:00:e6:87:d3:3f:fa:d9:3b:e8:3d:34:9e -# SHA256 Fingerprint: 31:ad:66:48:f8:10:41:38:c7:38:f3:9e:a4:32:01:33:39:3e:3a:18:cc:02:29:6e:f9:7c:2a:c9:ef:67:31:d0 ------BEGIN CERTIFICATE----- -MIICPzCCAcWgAwIBAgIQBVVWvPJepDU1w6QP1atFcjAKBggqhkjOPQQDAzBhMQsw -CQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cu -ZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBHMzAe -Fw0xMzA4MDExMjAwMDBaFw0zODAxMTUxMjAwMDBaMGExCzAJBgNVBAYTAlVTMRUw -EwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20x -IDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IEczMHYwEAYHKoZIzj0CAQYF -K4EEACIDYgAE3afZu4q4C/sLfyHS8L6+c/MzXRq8NOrexpu80JX28MzQC7phW1FG -fp4tn+6OYwwX7Adw9c+ELkCDnOg/QW07rdOkFFk2eJ0DQ+4QE2xy3q6Ip6FrtUPO -Z9wj/wMco+I+o0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAd -BgNVHQ4EFgQUs9tIpPmhxdiuNkHMEWNpYim8S8YwCgYIKoZIzj0EAwMDaAAwZQIx -AK288mw/EkrRLTnDCgmXc/SINoyIJ7vmiI1Qhadj+Z4y3maTD/HMsQmP3Wyr+mt/ -oAIwOWZbwmSNuJ5Q3KjVSaLtx9zRSX8XAbjIho9OjIgrqJqpisXRAL34VOKa5Vt8 -sycX ------END CERTIFICATE----- diff --git a/certs/GTS-Root-R1.pem b/certs/GTS-Root-R1.pem deleted file mode 100644 index a6095d2..0000000 --- a/certs/GTS-Root-R1.pem +++ /dev/null @@ -1,38 +0,0 @@ -# Issuer: CN=GTS Root R1 O=Google Trust Services LLC -# Subject: CN=GTS Root R1 O=Google Trust Services LLC -# Label: "GTS Root R1" -# Serial: 159662320309726417404178440727 -# MD5 Fingerprint: 05:fe:d0:bf:71:a8:a3:76:63:da:01:e0:d8:52:dc:40 -# SHA1 Fingerprint: e5:8c:1c:c4:91:3b:38:63:4b:e9:10:6e:e3:ad:8e:6b:9d:d9:81:4a -# SHA256 Fingerprint: d9:47:43:2a:bd:e7:b7:fa:90:fc:2e:6b:59:10:1b:12:80:e0:e1:c7:e4:e4:0f:a3:c6:88:7f:ff:57:a7:f4:cf ------BEGIN CERTIFICATE----- -MIIFVzCCAz+gAwIBAgINAgPlk28xsBNJiGuiFzANBgkqhkiG9w0BAQwFADBHMQsw -CQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEU -MBIGA1UEAxMLR1RTIFJvb3QgUjEwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAw -MDAwWjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZp -Y2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjEwggIiMA0GCSqGSIb3DQEBAQUA -A4ICDwAwggIKAoICAQC2EQKLHuOhd5s73L+UPreVp0A8of2C+X0yBoJx9vaMf/vo -27xqLpeXo4xL+Sv2sfnOhB2x+cWX3u+58qPpvBKJXqeqUqv4IyfLpLGcY9vXmX7w -Cl7raKb0xlpHDU0QM+NOsROjyBhsS+z8CZDfnWQpJSMHobTSPS5g4M/SCYe7zUjw -TcLCeoiKu7rPWRnWr4+wB7CeMfGCwcDfLqZtbBkOtdh+JhpFAz2weaSUKK0Pfybl -qAj+lug8aJRT7oM6iCsVlgmy4HqMLnXWnOunVmSPlk9orj2XwoSPwLxAwAtcvfaH -szVsrBhQf4TgTM2S0yDpM7xSma8ytSmzJSq0SPly4cpk9+aCEI3oncKKiPo4Zor8 -Y/kB+Xj9e1x3+naH+uzfsQ55lVe0vSbv1gHR6xYKu44LtcXFilWr06zqkUspzBmk -MiVOKvFlRNACzqrOSbTqn3yDsEB750Orp2yjj32JgfpMpf/VjsPOS+C12LOORc92 -wO1AK/1TD7Cn1TsNsYqiA94xrcx36m97PtbfkSIS5r762DL8EGMUUXLeXdYWk70p -aDPvOmbsB4om3xPXV2V4J95eSRQAogB/mqghtqmxlbCluQ0WEdrHbEg8QOB+DVrN -VjzRlwW5y0vtOUucxD/SVRNuJLDWcfr0wbrM7Rv1/oFB2ACYPTrIrnqYNxgFlQID -AQABo0IwQDAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4E -FgQU5K8rJnEaK0gnhS9SZizv8IkTcT4wDQYJKoZIhvcNAQEMBQADggIBAJ+qQibb -C5u+/x6Wki4+omVKapi6Ist9wTrYggoGxval3sBOh2Z5ofmmWJyq+bXmYOfg6LEe -QkEzCzc9zolwFcq1JKjPa7XSQCGYzyI0zzvFIoTgxQ6KfF2I5DUkzps+GlQebtuy -h6f88/qBVRRiClmpIgUxPoLW7ttXNLwzldMXG+gnoot7TiYaelpkttGsN/H9oPM4 -7HLwEXWdyzRSjeZ2axfG34arJ45JK3VmgRAhpuo+9K4l/3wV3s6MJT/KYnAK9y8J -ZgfIPxz88NtFMN9iiMG1D53Dn0reWVlHxYciNuaCp+0KueIHoI17eko8cdLiA6Ef -MgfdG+RCzgwARWGAtQsgWSl4vflVy2PFPEz0tv/bal8xa5meLMFrUKTX5hgUvYU/ -Z6tGn6D/Qqc6f1zLXbBwHSs09dR2CQzreExZBfMzQsNhFRAbd03OIozUhfJFfbdT -6u9AWpQKXCBfTkBdYiJ23//OYb2MI3jSNwLgjt7RETeJ9r/tSQdirpLsQBqvFAnZ -0E6yove+7u7Y/9waLd64NnHi/Hm3lCXRSHNboTXns5lndcEZOitHTtNCjv0xyBZm -2tIMPNuzjsmhDYAPexZ3FL//2wmUspO8IFgV6dtxQ/PeEMMA3KgqlbbC1j+Qa3bb -bP6MvPJwNQzcmRk13NfIRmPVNnGuV/u3gm3c ------END CERTIFICATE----- diff --git a/certs/GTS-Root-R4.pem b/certs/GTS-Root-R4.pem deleted file mode 100644 index 16a1c36..0000000 --- a/certs/GTS-Root-R4.pem +++ /dev/null @@ -1,20 +0,0 @@ -# Issuer: CN=GTS Root R4 O=Google Trust Services LLC -# Subject: CN=GTS Root R4 O=Google Trust Services LLC -# Label: "GTS Root R4" -# Serial: 159662532700760215368942768210 -# MD5 Fingerprint: 43:96:83:77:19:4d:76:b3:9d:65:52:e4:1d:22:a5:e8 -# SHA1 Fingerprint: 77:d3:03:67:b5:e0:0c:15:f6:0c:38:61:df:7c:e1:3b:92:46:4d:47 -# SHA256 Fingerprint: 34:9d:fa:40:58:c5:e2:63:12:3b:39:8a:e7:95:57:3c:4e:13:13:c8:3f:e6:8f:93:55:6c:d5:e8:03:1b:3c:7d ------BEGIN CERTIFICATE----- -MIICCTCCAY6gAwIBAgINAgPlwGjvYxqccpBQUjAKBggqhkjOPQQDAzBHMQswCQYD -VQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIG -A1UEAxMLR1RTIFJvb3QgUjQwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAwMDAw -WjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2Vz -IExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjQwdjAQBgcqhkjOPQIBBgUrgQQAIgNi -AATzdHOnaItgrkO4NcWBMHtLSZ37wWHO5t5GvWvVYRg1rkDdc/eJkTBa6zzuhXyi -QHY7qca4R9gq55KRanPpsXI5nymfopjTX15YhmUPoYRlBtHci8nHc8iMai/lxKvR -HYqjQjBAMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQW -BBSATNbrdP9JNqPV2Py1PsVq8JQdjDAKBggqhkjOPQQDAwNpADBmAjEA6ED/g94D -9J+uHXqnLrmvT/aDHQ4thQEd0dlq7A/Cr8deVl5c1RxYIigL9zC2L7F8AjEA8GE8 -p/SgguMh1YQdc4acLa/KNJvxn7kjNuK8YAOdgLOaVsjh4rsUecrNIdSUtUlD ------END CERTIFICATE----- diff --git a/certs/Go-Daddy-Root-Certificate-Authority-G2.pem b/certs/Go-Daddy-Root-Certificate-Authority-G2.pem deleted file mode 100644 index c61f300..0000000 --- a/certs/Go-Daddy-Root-Certificate-Authority-G2.pem +++ /dev/null @@ -1,30 +0,0 @@ -# Issuer: CN=Go Daddy Root Certificate Authority - G2 O=GoDaddy.com, Inc. -# Subject: CN=Go Daddy Root Certificate Authority - G2 O=GoDaddy.com, Inc. -# Label: "Go Daddy Root Certificate Authority - G2" -# Serial: 0 -# MD5 Fingerprint: 80:3a:bc:22:c1:e6:fb:8d:9b:3b:27:4a:32:1b:9a:01 -# SHA1 Fingerprint: 47:be:ab:c9:22:ea:e8:0e:78:78:34:62:a7:9f:45:c2:54:fd:e6:8b -# SHA256 Fingerprint: 45:14:0b:32:47:eb:9c:c8:c5:b4:f0:d7:b5:30:91:f7:32:92:08:9e:6e:5a:63:e2:74:9d:d3:ac:a9:19:8e:da ------BEGIN CERTIFICATE----- -MIIDxTCCAq2gAwIBAgIBADANBgkqhkiG9w0BAQsFADCBgzELMAkGA1UEBhMCVVMx -EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxGjAYBgNVBAoT -EUdvRGFkZHkuY29tLCBJbmMuMTEwLwYDVQQDEyhHbyBEYWRkeSBSb290IENlcnRp -ZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5MDkwMTAwMDAwMFoXDTM3MTIzMTIz -NTk1OVowgYMxCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6b25hMRMwEQYDVQQH -EwpTY290dHNkYWxlMRowGAYDVQQKExFHb0RhZGR5LmNvbSwgSW5jLjExMC8GA1UE -AxMoR28gRGFkZHkgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgLSBHMjCCASIw -DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL9xYgjx+lk09xvJGKP3gElY6SKD -E6bFIEMBO4Tx5oVJnyfq9oQbTqC023CYxzIBsQU+B07u9PpPL1kwIuerGVZr4oAH -/PMWdYA5UXvl+TW2dE6pjYIT5LY/qQOD+qK+ihVqf94Lw7YZFAXK6sOoBJQ7Rnwy -DfMAZiLIjWltNowRGLfTshxgtDj6AozO091GB94KPutdfMh8+7ArU6SSYmlRJQVh -GkSBjCypQ5Yj36w6gZoOKcUcqeldHraenjAKOc7xiID7S13MMuyFYkMlNAJWJwGR -tDtwKj9useiciAF9n9T521NtYJ2/LOdYq7hfRvzOxBsDPAnrSTFcaUaz4EcCAwEA -AaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYE -FDqahQcQZyi27/a9BUFuIMGU2g/eMA0GCSqGSIb3DQEBCwUAA4IBAQCZ21151fmX -WWcDYfF+OwYxdS2hII5PZYe096acvNjpL9DbWu7PdIxztDhC2gV7+AJ1uP2lsdeu -9tfeE8tTEH6KRtGX+rcuKxGrkLAngPnon1rpN5+r5N9ss4UXnT3ZJE95kTXWXwTr -gIOrmgIttRD02JDHBHNA7XIloKmf7J6raBKZV8aPEjoJpL1E/QYVN8Gb5DKj7Tjo -2GTzLH4U/ALqn83/B2gX2yKQOC16jdFU8WnjXzPKej17CuPKf1855eJ1usV2GDPO -LPAvTK33sefOT6jEm0pUBsV/fdUID+Ic/n4XuKxe9tQWskMJDE32p2u0mYRlynqI -4uJEvlz36hz1 ------END CERTIFICATE----- diff --git a/certs/ISRG-Root-X1.pem b/certs/ISRG-Root-X1.pem deleted file mode 100644 index 995c95d..0000000 --- a/certs/ISRG-Root-X1.pem +++ /dev/null @@ -1,38 +0,0 @@ -# Issuer: CN=ISRG Root X1 O=Internet Security Research Group -# Subject: CN=ISRG Root X1 O=Internet Security Research Group -# Label: "ISRG Root X1" -# Serial: 172886928669790476064670243504169061120 -# MD5 Fingerprint: 0c:d2:f9:e0:da:17:73:e9:ed:86:4d:a5:e3:70:e7:4e -# SHA1 Fingerprint: ca:bd:2a:79:a1:07:6a:31:f2:1d:25:36:35:cb:03:9d:43:29:a5:e8 -# SHA256 Fingerprint: 96:bc:ec:06:26:49:76:f3:74:60:77:9a:cf:28:c5:a7:cf:e8:a3:c0:aa:e1:1a:8f:fc:ee:05:c0:bd:df:08:c6 ------BEGIN CERTIFICATE----- -MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw -TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh -cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4 -WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu -ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY -MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc -h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+ -0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U -A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW -T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH -B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC -B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv -KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn -OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn -jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw -qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI -rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV -HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq -hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL -ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ -3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK -NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5 -ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur -TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC -jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc -oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq -4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA -mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d -emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc= ------END CERTIFICATE----- diff --git a/certs/ISRG-Root-X2.pem b/certs/ISRG-Root-X2.pem deleted file mode 100644 index 9cca880..0000000 --- a/certs/ISRG-Root-X2.pem +++ /dev/null @@ -1,21 +0,0 @@ -# Issuer: CN=ISRG Root X2 O=Internet Security Research Group -# Subject: CN=ISRG Root X2 O=Internet Security Research Group -# Label: "ISRG Root X2" -# Serial: 87493402998870891108772069816698636114 -# MD5 Fingerprint: d3:9e:c4:1e:23:3c:a6:df:cf:a3:7e:6d:e0:14:e6:e5 -# SHA1 Fingerprint: bd:b1:b9:3c:d5:97:8d:45:c6:26:14:55:f8:db:95:c7:5a:d1:53:af -# SHA256 Fingerprint: 69:72:9b:8e:15:a8:6e:fc:17:7a:57:af:b7:17:1d:fc:64:ad:d2:8c:2f:ca:8c:f1:50:7e:34:45:3c:cb:14:70 ------BEGIN CERTIFICATE----- -MIICGzCCAaGgAwIBAgIQQdKd0XLq7qeAwSxs6S+HUjAKBggqhkjOPQQDAzBPMQsw -CQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJuZXQgU2VjdXJpdHkgUmVzZWFyY2gg -R3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBYMjAeFw0yMDA5MDQwMDAwMDBaFw00 -MDA5MTcxNjAwMDBaME8xCzAJBgNVBAYTAlVTMSkwJwYDVQQKEyBJbnRlcm5ldCBT -ZWN1cml0eSBSZXNlYXJjaCBHcm91cDEVMBMGA1UEAxMMSVNSRyBSb290IFgyMHYw -EAYHKoZIzj0CAQYFK4EEACIDYgAEzZvVn4CDCuwJSvMWSj5cz3es3mcFDR0HttwW -+1qLFNvicWDEukWVEYmO6gbf9yoWHKS5xcUy4APgHoIYOIvXRdgKam7mAHf7AlF9 -ItgKbppbd9/w+kHsOdx1ymgHDB/qo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0T -AQH/BAUwAwEB/zAdBgNVHQ4EFgQUfEKWrt5LSDv6kviejM9ti6lyN5UwCgYIKoZI -zj0EAwMDaAAwZQIwe3lORlCEwkSHRhtFcP9Ymd70/aTSVaYgLXTWNLxBo1BfASdW -tL4ndQavEi51mI38AjEAi/V3bNTIZargCyzuFJ0nN6T5U6VR5CmD1/iQMVtCnwr1 -/q4AaOeMSQ+2b1tbFfLn ------END CERTIFICATE----- diff --git a/certs/Makefile b/certs/Makefile deleted file mode 100644 index 3ccad6e..0000000 --- a/certs/Makefile +++ /dev/null @@ -1,58 +0,0 @@ -# Makefile to check certificates - -CURL = curl \ - --capath /dev/null \ - --connect-timeout 5 \ - --output /dev/null \ - --silent - -DOMAINS_DUAL = \ - api.macvendors.com/GTS-Root-R4 \ - api.telegram.org/Go-Daddy-Root-Certificate-Authority-G2 \ - cloudflare-dns.com/DigiCert-Global-Root-G2 \ - dns.google/GTS-Root-R4 \ - dns.quad9.net/DigiCert-Global-Root-G3 \ - git.eworm.de/ISRG-Root-X2 \ - lists.blocklist.de/Certum-Trusted-Network-CA \ - matrix.org/GTS-Root-R4 \ - raw.githubusercontent.com/USERTrust-RSA-Certification-Authority \ - rsc.eworm.de/ISRG-Root-X2 \ - upgrade.mikrotik.com/ISRG-Root-X1 -DOMAINS_IPV4 = \ - 1.1.1.1/DigiCert-Global-Root-G2 \ - 8.8.8.8/GTS-Root-R1 \ - 9.9.9.9/DigiCert-Global-Root-G3 \ - api.mullvad.net/ISRG-Root-X1 \ - ipv4.showipv6.de/ISRG-Root-X1 \ - ipv4.tunnelbroker.net/Starfield-Root-Certificate-Authority-G2 \ - mkcert.org/ISRG-Root-X1 \ - ntfy.sh/ISRG-Root-X1 \ - www.dshield.org/ISRG-Root-X1 \ - www.spamhaus.org/GTS-Root-R4 -DOMAINS_IPV6 = \ - [2606\:4700\:4700\:\:1111]/DigiCert-Global-Root-G2 \ - [2001\:4860\:4860\:\:8888]/GTS-Root-R1 \ - [2620\:fe\:\:9]/DigiCert-Global-Root-G3 \ - ipv6.showipv6.de/ISRG-Root-X1 - -.PHONY: $(DOMAINS_DUAL) $(DOMAINS_IPV4) $(DOMAINS_IPV6) - -all: $(DOMAINS_DUAL) $(DOMAINS_IPV4) $(DOMAINS_IPV6) - -$(DOMAINS_DUAL): -ifndef NOIPV4 - $(CURL) -4 --cacert $(notdir $@).pem https://$(dir $@) -endif -ifndef NOIPV6 - $(CURL) -6 --cacert $(notdir $@).pem https://$(dir $@) -endif - -$(DOMAINS_IPV4): -ifndef NOIPV4 - $(CURL) -4 --cacert $(notdir $@).pem https://$(dir $@) -endif - -$(DOMAINS_IPV6): -ifndef NOIPV6 - $(CURL) -6 --cacert $(notdir $@).pem https://$(dir $@) -endif diff --git a/certs/Starfield-Root-Certificate-Authority-G2.pem b/certs/Starfield-Root-Certificate-Authority-G2.pem deleted file mode 100644 index 4e6774d..0000000 --- a/certs/Starfield-Root-Certificate-Authority-G2.pem +++ /dev/null @@ -1,30 +0,0 @@ -# Issuer: CN=Starfield Root Certificate Authority - G2 O=Starfield Technologies, Inc. -# Subject: CN=Starfield Root Certificate Authority - G2 O=Starfield Technologies, Inc. -# Label: "Starfield Root Certificate Authority - G2" -# Serial: 0 -# MD5 Fingerprint: d6:39:81:c6:52:7e:96:69:fc:fc:ca:66:ed:05:f2:96 -# SHA1 Fingerprint: b5:1c:06:7c:ee:2b:0c:3d:f8:55:ab:2d:92:f4:fe:39:d4:e7:0f:0e -# SHA256 Fingerprint: 2c:e1:cb:0b:f9:d2:f9:e1:02:99:3f:be:21:51:52:c3:b2:dd:0c:ab:de:1c:68:e5:31:9b:83:91:54:db:b7:f5 ------BEGIN CERTIFICATE----- -MIID3TCCAsWgAwIBAgIBADANBgkqhkiG9w0BAQsFADCBjzELMAkGA1UEBhMCVVMx -EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoT -HFN0YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xMjAwBgNVBAMTKVN0YXJmaWVs -ZCBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5MDkwMTAwMDAw -MFoXDTM3MTIzMTIzNTk1OVowgY8xCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6 -b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxTdGFyZmllbGQgVGVj -aG5vbG9naWVzLCBJbmMuMTIwMAYDVQQDEylTdGFyZmllbGQgUm9vdCBDZXJ0aWZp -Y2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC -ggEBAL3twQP89o/8ArFvW59I2Z154qK3A2FWGMNHttfKPTUuiUP3oWmb3ooa/RMg -nLRJdzIpVv257IzdIvpy3Cdhl+72WoTsbhm5iSzchFvVdPtrX8WJpRBSiUZV9Lh1 -HOZ/5FSuS/hVclcCGfgXcVnrHigHdMWdSL5stPSksPNkN3mSwOxGXn/hbVNMYq/N -Hwtjuzqd+/x5AJhhdM8mgkBj87JyahkNmcrUDnXMN/uLicFZ8WJ/X7NfZTD4p7dN -dloedl40wOiWVpmKs/B/pM293DIxfJHP4F8R+GuqSVzRmZTRouNjWwl2tVZi4Ut0 -HZbUJtQIBFnQmA4O5t78w+wfkPECAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAO -BgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFHwMMh+n2TB/xH1oo2Kooc6rB1snMA0G -CSqGSIb3DQEBCwUAA4IBAQARWfolTwNvlJk7mh+ChTnUdgWUXuEok21iXQnCoKjU -sHU48TRqneSfioYmUeYs0cYtbpUgSpIB7LiKZ3sx4mcujJUDJi5DnUox9g61DLu3 -4jd/IroAow57UvtruzvE03lRTs2Q9GcHGcg8RnoNAX3FWOdt5oUwF5okxBDgBPfg -8n/Uqgr/Qh037ZTlZFkSIHc40zI+OIF1lnP6aI+xy84fxez6nH7PfrHxBy22/L/K -pL/QlwVKvOoYKAKQvVR4CSFx09F9HdkWsKlhPdAKACL8x3vLCWRFCztAgfd9fDL1 -mMpYjn0q7pBZc2T5NnReJaH1ZgUufzkVqSr7UIuOhWn0 ------END CERTIFICATE----- diff --git a/certs/USERTrust-RSA-Certification-Authority.pem b/certs/USERTrust-RSA-Certification-Authority.pem deleted file mode 100644 index 0fbeef6..0000000 --- a/certs/USERTrust-RSA-Certification-Authority.pem +++ /dev/null @@ -1,41 +0,0 @@ -# Issuer: CN=USERTrust RSA Certification Authority O=The USERTRUST Network -# Subject: CN=USERTrust RSA Certification Authority O=The USERTRUST Network -# Label: "USERTrust RSA Certification Authority" -# Serial: 2645093764781058787591871645665788717 -# MD5 Fingerprint: 1b:fe:69:d1:91:b7:19:33:a3:72:a8:0f:e1:55:e5:b5 -# SHA1 Fingerprint: 2b:8f:1b:57:33:0d:bb:a2:d0:7a:6c:51:f7:0e:e9:0d:da:b9:ad:8e -# SHA256 Fingerprint: e7:93:c9:b0:2f:d8:aa:13:e2:1c:31:22:8a:cc:b0:81:19:64:3b:74:9c:89:89:64:b1:74:6d:46:c3:d4:cb:d2 ------BEGIN CERTIFICATE----- -MIIF3jCCA8agAwIBAgIQAf1tMPyjylGoG7xkDjUDLTANBgkqhkiG9w0BAQwFADCB -iDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0pl -cnNleSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNV -BAMTJVVTRVJUcnVzdCBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAw -MjAxMDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBiDELMAkGA1UEBhMCVVMxEzARBgNV -BAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQKExVU -aGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBSU0EgQ2Vy -dGlmaWNhdGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK -AoICAQCAEmUXNg7D2wiz0KxXDXbtzSfTTK1Qg2HiqiBNCS1kCdzOiZ/MPans9s/B -3PHTsdZ7NygRK0faOca8Ohm0X6a9fZ2jY0K2dvKpOyuR+OJv0OwWIJAJPuLodMkY -tJHUYmTbf6MG8YgYapAiPLz+E/CHFHv25B+O1ORRxhFnRghRy4YUVD+8M/5+bJz/ -Fp0YvVGONaanZshyZ9shZrHUm3gDwFA66Mzw3LyeTP6vBZY1H1dat//O+T23LLb2 -VN3I5xI6Ta5MirdcmrS3ID3KfyI0rn47aGYBROcBTkZTmzNg95S+UzeQc0PzMsNT -79uq/nROacdrjGCT3sTHDN/hMq7MkztReJVni+49Vv4M0GkPGw/zJSZrM233bkf6 -c0Plfg6lZrEpfDKEY1WJxA3Bk1QwGROs0303p+tdOmw1XNtB1xLaqUkL39iAigmT -Yo61Zs8liM2EuLE/pDkP2QKe6xJMlXzzawWpXhaDzLhn4ugTncxbgtNMs+1b/97l -c6wjOy0AvzVVdAlJ2ElYGn+SNuZRkg7zJn0cTRe8yexDJtC/QV9AqURE9JnnV4ee -UB9XVKg+/XRjL7FQZQnmWEIuQxpMtPAlR1n6BB6T1CZGSlCBst6+eLf8ZxXhyVeE -Hg9j1uliutZfVS7qXMYoCAQlObgOK6nyTJccBz8NUvXt7y+CDwIDAQABo0IwQDAd -BgNVHQ4EFgQUU3m/WqorSs9UgOHYm8Cd8rIDZsswDgYDVR0PAQH/BAQDAgEGMA8G -A1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEMBQADggIBAFzUfA3P9wF9QZllDHPF -Up/L+M+ZBn8b2kMVn54CVVeWFPFSPCeHlCjtHzoBN6J2/FNQwISbxmtOuowhT6KO -VWKR82kV2LyI48SqC/3vqOlLVSoGIG1VeCkZ7l8wXEskEVX/JJpuXior7gtNn3/3 -ATiUFJVDBwn7YKnuHKsSjKCaXqeYalltiz8I+8jRRa8YFWSQEg9zKC7F4iRO/Fjs -8PRF/iKz6y+O0tlFYQXBl2+odnKPi4w2r78NBc5xjeambx9spnFixdjQg3IM8WcR -iQycE0xyNN+81XHfqnHd4blsjDwSXWXavVcStkNr/+XeTWYRUc+ZruwXtuhxkYze -Sf7dNXGiFSeUHM9h4ya7b6NnJSFd5t0dCy5oGzuCr+yDZ4XUmFF0sbmZgIn/f3gZ -XHlKYC6SQK5MNyosycdiyA5d9zZbyuAlJQG03RoHnHcAP9Dc1ew91Pq7P8yF1m9/ -qS3fuQL39ZeatTXaw2ewh0qpKJ4jjv9cJ2vhsE/zB+4ALtRZh8tSQZXq9EfX7mRB -VXyNWQKV3WKdwrnuWih0hKWbt5DHDAff9Yk2dDLWKMGwsAvgnEzDHNb842m1R0aB -L6KCq9NjRHDEjf8tM7qtj3u1cIiuPhnPQCjY/MiQu12ZIvVS5ljFH4gxQ+6IHdfG -jjxDah2nGN59PRbxYvnKkKj9 ------END CERTIFICATE----- diff --git a/certs/godaddy.pem b/certs/godaddy.pem new file mode 100644 index 0000000..72e5054 --- /dev/null +++ b/certs/godaddy.pem @@ -0,0 +1,51 @@ +-----BEGIN CERTIFICATE----- +MIIDxTCCAq2gAwIBAgIBADANBgkqhkiG9w0BAQsFADCBgzELMAkGA1UEBhMCVVMx +EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxGjAYBgNVBAoT +EUdvRGFkZHkuY29tLCBJbmMuMTEwLwYDVQQDEyhHbyBEYWRkeSBSb290IENlcnRp +ZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5MDkwMTAwMDAwMFoXDTM3MTIzMTIz +NTk1OVowgYMxCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6b25hMRMwEQYDVQQH +EwpTY290dHNkYWxlMRowGAYDVQQKExFHb0RhZGR5LmNvbSwgSW5jLjExMC8GA1UE +AxMoR28gRGFkZHkgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgLSBHMjCCASIw +DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL9xYgjx+lk09xvJGKP3gElY6SKD +E6bFIEMBO4Tx5oVJnyfq9oQbTqC023CYxzIBsQU+B07u9PpPL1kwIuerGVZr4oAH +/PMWdYA5UXvl+TW2dE6pjYIT5LY/qQOD+qK+ihVqf94Lw7YZFAXK6sOoBJQ7Rnwy +DfMAZiLIjWltNowRGLfTshxgtDj6AozO091GB94KPutdfMh8+7ArU6SSYmlRJQVh +GkSBjCypQ5Yj36w6gZoOKcUcqeldHraenjAKOc7xiID7S13MMuyFYkMlNAJWJwGR +tDtwKj9useiciAF9n9T521NtYJ2/LOdYq7hfRvzOxBsDPAnrSTFcaUaz4EcCAwEA +AaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYE +FDqahQcQZyi27/a9BUFuIMGU2g/eMA0GCSqGSIb3DQEBCwUAA4IBAQCZ21151fmX +WWcDYfF+OwYxdS2hII5PZYe096acvNjpL9DbWu7PdIxztDhC2gV7+AJ1uP2lsdeu +9tfeE8tTEH6KRtGX+rcuKxGrkLAngPnon1rpN5+r5N9ss4UXnT3ZJE95kTXWXwTr +gIOrmgIttRD02JDHBHNA7XIloKmf7J6raBKZV8aPEjoJpL1E/QYVN8Gb5DKj7Tjo +2GTzLH4U/ALqn83/B2gX2yKQOC16jdFU8WnjXzPKej17CuPKf1855eJ1usV2GDPO +LPAvTK33sefOT6jEm0pUBsV/fdUID+Ic/n4XuKxe9tQWskMJDE32p2u0mYRlynqI +4uJEvlz36hz1 +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIE0DCCA7igAwIBAgIBBzANBgkqhkiG9w0BAQsFADCBgzELMAkGA1UEBhMCVVMx +EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxGjAYBgNVBAoT +EUdvRGFkZHkuY29tLCBJbmMuMTEwLwYDVQQDEyhHbyBEYWRkeSBSb290IENlcnRp +ZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTExMDUwMzA3MDAwMFoXDTMxMDUwMzA3 +MDAwMFowgbQxCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6b25hMRMwEQYDVQQH +EwpTY290dHNkYWxlMRowGAYDVQQKExFHb0RhZGR5LmNvbSwgSW5jLjEtMCsGA1UE +CxMkaHR0cDovL2NlcnRzLmdvZGFkZHkuY29tL3JlcG9zaXRvcnkvMTMwMQYDVQQD +EypHbyBEYWRkeSBTZWN1cmUgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IC0gRzIwggEi +MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC54MsQ1K92vdSTYuswZLiBCGzD +BNliF44v/z5lz4/OYuY8UhzaFkVLVat4a2ODYpDOD2lsmcgaFItMzEUz6ojcnqOv +K/6AYZ15V8TPLvQ/MDxdR/yaFrzDN5ZBUY4RS1T4KL7QjL7wMDge87Am+GZHY23e +cSZHjzhHU9FGHbTj3ADqRay9vHHZqm8A29vNMDp5T19MR/gd71vCxJ1gO7GyQ5HY +pDNO6rPWJ0+tJYqlxvTV0KaudAVkV4i1RFXULSo6Pvi4vekyCgKUZMQWOlDxSq7n +eTOvDCAHf+jfBDnCaQJsY1L6d8EbyHSHyLmTGFBUNUtpTrw700kuH9zB0lL7AgMB +AAGjggEaMIIBFjAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNV +HQ4EFgQUQMK9J47MNIMwojPX+2yz8LQsgM4wHwYDVR0jBBgwFoAUOpqFBxBnKLbv +9r0FQW4gwZTaD94wNAYIKwYBBQUHAQEEKDAmMCQGCCsGAQUFBzABhhhodHRwOi8v +b2NzcC5nb2RhZGR5LmNvbS8wNQYDVR0fBC4wLDAqoCigJoYkaHR0cDovL2NybC5n +b2RhZGR5LmNvbS9nZHJvb3QtZzIuY3JsMEYGA1UdIAQ/MD0wOwYEVR0gADAzMDEG +CCsGAQUFBwIBFiVodHRwczovL2NlcnRzLmdvZGFkZHkuY29tL3JlcG9zaXRvcnkv +MA0GCSqGSIb3DQEBCwUAA4IBAQAIfmyTEMg4uJapkEv/oV9PBO9sPpyIBslQj6Zz +91cxG7685C/b+LrTW+C05+Z5Yg4MotdqY3MxtfWoSKQ7CC2iXZDXtHwlTxFWMMS2 +RJ17LJ3lXubvDGGqv+QqG+6EnriDfcFDzkSnE3ANkR/0yBOtg2DZ2HKocyQetawi +DsoXiWJYRBuriSUBAA/NxBti21G00w9RKpv0vHP8ds42pM3Z2Czqrpv1KrKQ0U11 +GIo/ikGQI31bS/6kA1ibRrLDYGCD+H1QQc7CoZDDu+8CL9IVVO5EFdkKrqeKM+2x +LXY2JtwE65/3YR8V3Idv7kaWKK2hJn0KCacuBKONvPi8BDAB +-----END CERTIFICATE----- diff --git a/certs/letsencrypt.pem b/certs/letsencrypt.pem new file mode 100644 index 0000000..7df773f --- /dev/null +++ b/certs/letsencrypt.pem @@ -0,0 +1,83 @@ +-----BEGIN CERTIFICATE----- +MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw +TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh +cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4 +WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu +ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY +MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc +h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+ +0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U +A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW +T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH +B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC +B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv +KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn +OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn +jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw +qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI +rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV +HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq +hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL +ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ +3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK +NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5 +ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur +TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC +jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc +oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq +4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA +mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d +emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIFjTCCA3WgAwIBAgIRANOxciY0IzLc9AUoUSrsnGowDQYJKoZIhvcNAQELBQAw +TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh +cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTYxMDA2MTU0MzU1 +WhcNMjExMDA2MTU0MzU1WjBKMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNTGV0J3Mg +RW5jcnlwdDEjMCEGA1UEAxMaTGV0J3MgRW5jcnlwdCBBdXRob3JpdHkgWDMwggEi +MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCc0wzwWuUuR7dyXTeDs2hjMOrX +NSYZJeG9vjXxcJIvt7hLQQWrqZ41CFjssSrEaIcLo+N15Obzp2JxunmBYB/XkZqf +89B4Z3HIaQ6Vkc/+5pnpYDxIzH7KTXcSJJ1HG1rrueweNwAcnKx7pwXqzkrrvUHl +Npi5y/1tPJZo3yMqQpAMhnRnyH+lmrhSYRQTP2XpgofL2/oOVvaGifOFP5eGr7Dc +Gu9rDZUWfcQroGWymQQ2dYBrrErzG5BJeC+ilk8qICUpBMZ0wNAxzY8xOJUWuqgz +uEPxsR/DMH+ieTETPS02+OP88jNquTkxxa/EjQ0dZBYzqvqEKbbUC8DYfcOTAgMB +AAGjggFnMIIBYzAOBgNVHQ8BAf8EBAMCAYYwEgYDVR0TAQH/BAgwBgEB/wIBADBU +BgNVHSAETTBLMAgGBmeBDAECATA/BgsrBgEEAYLfEwEBATAwMC4GCCsGAQUFBwIB +FiJodHRwOi8vY3BzLnJvb3QteDEubGV0c2VuY3J5cHQub3JnMB0GA1UdDgQWBBSo +SmpjBH3duubRObemRWXv86jsoTAzBgNVHR8ELDAqMCigJqAkhiJodHRwOi8vY3Js +LnJvb3QteDEubGV0c2VuY3J5cHQub3JnMHIGCCsGAQUFBwEBBGYwZDAwBggrBgEF +BQcwAYYkaHR0cDovL29jc3Aucm9vdC14MS5sZXRzZW5jcnlwdC5vcmcvMDAGCCsG +AQUFBzAChiRodHRwOi8vY2VydC5yb290LXgxLmxldHNlbmNyeXB0Lm9yZy8wHwYD +VR0jBBgwFoAUebRZ5nu25eQBc4AIiMgaWPbpm24wDQYJKoZIhvcNAQELBQADggIB +ABnPdSA0LTqmRf/Q1eaM2jLonG4bQdEnqOJQ8nCqxOeTRrToEKtwT++36gTSlBGx +A/5dut82jJQ2jxN8RI8L9QFXrWi4xXnA2EqA10yjHiR6H9cj6MFiOnb5In1eWsRM +UM2v3e9tNsCAgBukPHAg1lQh07rvFKm/Bz9BCjaxorALINUfZ9DD64j2igLIxle2 +DPxW8dI/F2loHMjXZjqG8RkqZUdoxtID5+90FgsGIfkMpqgRS05f4zPbCEHqCXl1 +eO5HyELTgcVlLXXQDgAWnRzut1hFJeczY1tjQQno6f6s+nMydLN26WuU4s3UYvOu +OsUxRlJu7TSRHqDC3lSE5XggVkzdaPkuKGQbGpny+01/47hfXXNB7HntWNZ6N2Vw +p7G6OfY+YQrZwIaQmhrIqJZuigsrbe3W+gdn5ykE9+Ky0VgVUsfxo52mwFYs1JKY +2PGDuWx8M6DlS6qQkvHaRUo0FMd8TsSlbF0/v965qGFKhSDeQoMpYnwcmQilRh/0 +ayLThlHLN81gSkJjVrPI0Y8xCVPB4twb1PFUd2fPM3sA1tJ83sZ5v8vgFv2yofKR +PB0t6JzUA81mSqM3kxl5e+IZwhYAyO0OTg3/fs8HqGTNKd9BqoUwSRBzp06JMg5b +rUCGwbCUDI0mxadJ3Bz4WxR6fyNpBK2yAinWEsikxqEt +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDSjCCAjKgAwIBAgIQRK+wgNajJ7qJMDmGLvhAazANBgkqhkiG9w0BAQUFADA/ +MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT +DkRTVCBSb290IENBIFgzMB4XDTAwMDkzMDIxMTIxOVoXDTIxMDkzMDE0MDExNVow +PzEkMCIGA1UEChMbRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3QgQ28uMRcwFQYDVQQD +Ew5EU1QgUm9vdCBDQSBYMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB +AN+v6ZdQCINXtMxiZfaQguzH0yxrMMpb7NnDfcdAwRgUi+DoM3ZJKuM/IUmTrE4O +rz5Iy2Xu/NMhD2XSKtkyj4zl93ewEnu1lcCJo6m67XMuegwGMoOifooUMM0RoOEq +OLl5CjH9UL2AZd+3UWODyOKIYepLYYHsUmu5ouJLGiifSKOeDNoJjj4XLh7dIN9b +xiqKqy69cK3FCxolkHRyxXtqqzTWMIn/5WgTe1QLyNau7Fqckh49ZLOMxt+/yUFw +7BZy1SbsOFU5Q9D8/RhcQPGX69Wam40dutolucbY38EVAjqr2m7xPi71XAicPNaD +aeQQmxkqtilX4+U9m5/wAl0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNV +HQ8BAf8EBAMCAQYwHQYDVR0OBBYEFMSnsaR7LHH62+FLkHX/xBVghYkQMA0GCSqG +SIb3DQEBBQUAA4IBAQCjGiybFwBcqR7uKGY3Or+Dxz9LwwmglSBd49lZRNI+DT69 +ikugdB/OEIKcdBodfpga3csTS7MgROSR6cz8faXbauX+5v3gTt23ADq1cEmv8uXr +AvHRAosZy5Q6XkjEGB5YGV8eAlrwDPGxrancWYaLbumR9YbK+rlmM6pZW87ipxZz +R8srzJmwN0jP41ZL9c8PDHIyh8bwRLtTcm1D9SZImlJnt1ir/md2cXjbDaJWFBM5 +JDGFoqgCWjBH4d1QB7wCCZAA62RjYJsWvIjJEubSfZGL+T0yjWW06XyxV3bqxbYo +Ob8VZRzI9neWagqNdwvYkQsEjgfbKbYK7p2CNTUQ +-----END CERTIFICATE----- diff --git a/certs/starfield.pem b/certs/starfield.pem new file mode 100644 index 0000000..9c17e74 --- /dev/null +++ b/certs/starfield.pem @@ -0,0 +1,52 @@ +-----BEGIN CERTIFICATE----- +MIID3TCCAsWgAwIBAgIBADANBgkqhkiG9w0BAQsFADCBjzELMAkGA1UEBhMCVVMx +EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoT +HFN0YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xMjAwBgNVBAMTKVN0YXJmaWVs +ZCBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5MDkwMTAwMDAw +MFoXDTM3MTIzMTIzNTk1OVowgY8xCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6 +b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxTdGFyZmllbGQgVGVj +aG5vbG9naWVzLCBJbmMuMTIwMAYDVQQDEylTdGFyZmllbGQgUm9vdCBDZXJ0aWZp +Y2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC +ggEBAL3twQP89o/8ArFvW59I2Z154qK3A2FWGMNHttfKPTUuiUP3oWmb3ooa/RMg +nLRJdzIpVv257IzdIvpy3Cdhl+72WoTsbhm5iSzchFvVdPtrX8WJpRBSiUZV9Lh1 +HOZ/5FSuS/hVclcCGfgXcVnrHigHdMWdSL5stPSksPNkN3mSwOxGXn/hbVNMYq/N +Hwtjuzqd+/x5AJhhdM8mgkBj87JyahkNmcrUDnXMN/uLicFZ8WJ/X7NfZTD4p7dN +dloedl40wOiWVpmKs/B/pM293DIxfJHP4F8R+GuqSVzRmZTRouNjWwl2tVZi4Ut0 +HZbUJtQIBFnQmA4O5t78w+wfkPECAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAO +BgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFHwMMh+n2TB/xH1oo2Kooc6rB1snMA0G +CSqGSIb3DQEBCwUAA4IBAQARWfolTwNvlJk7mh+ChTnUdgWUXuEok21iXQnCoKjU +sHU48TRqneSfioYmUeYs0cYtbpUgSpIB7LiKZ3sx4mcujJUDJi5DnUox9g61DLu3 +4jd/IroAow57UvtruzvE03lRTs2Q9GcHGcg8RnoNAX3FWOdt5oUwF5okxBDgBPfg +8n/Uqgr/Qh037ZTlZFkSIHc40zI+OIF1lnP6aI+xy84fxez6nH7PfrHxBy22/L/K +pL/QlwVKvOoYKAKQvVR4CSFx09F9HdkWsKlhPdAKACL8x3vLCWRFCztAgfd9fDL1 +mMpYjn0q7pBZc2T5NnReJaH1ZgUufzkVqSr7UIuOhWn0 +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIFADCCA+igAwIBAgIBBzANBgkqhkiG9w0BAQsFADCBjzELMAkGA1UEBhMCVVMx +EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoT +HFN0YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xMjAwBgNVBAMTKVN0YXJmaWVs +ZCBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTExMDUwMzA3MDAw +MFoXDTMxMDUwMzA3MDAwMFowgcYxCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6 +b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxTdGFyZmllbGQgVGVj +aG5vbG9naWVzLCBJbmMuMTMwMQYDVQQLEypodHRwOi8vY2VydHMuc3RhcmZpZWxk +dGVjaC5jb20vcmVwb3NpdG9yeS8xNDAyBgNVBAMTK1N0YXJmaWVsZCBTZWN1cmUg +Q2VydGlmaWNhdGUgQXV0aG9yaXR5IC0gRzIwggEiMA0GCSqGSIb3DQEBAQUAA4IB +DwAwggEKAoIBAQDlkGZL7PlGcakgg77pbL9KyUhpgXVObST2yxcT+LBxWYR6ayuF +pDS1FuXLzOlBcCykLtb6Mn3hqN6UEKwxwcDYav9ZJ6t21vwLdGu4p64/xFT0tDFE +3ZNWjKRMXpuJyySDm+JXfbfYEh/JhW300YDxUJuHrtQLEAX7J7oobRfpDtZNuTlV +Bv8KJAV+L8YdcmzUiymMV33a2etmGtNPp99/UsQwxaXJDgLFU793OGgGJMNmyDd+ +MB5FcSM1/5DYKp2N57CSTTx/KgqT3M0WRmX3YISLdkuRJ3MUkuDq7o8W6o0OPnYX +v32JgIBEQ+ct4EMJddo26K3biTr1XRKOIwSDAgMBAAGjggEsMIIBKDAPBgNVHRMB +Af8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUJUWBaFAmOD07LSy+ +zWrZtj2zZmMwHwYDVR0jBBgwFoAUfAwyH6fZMH/EfWijYqihzqsHWycwOgYIKwYB +BQUHAQEELjAsMCoGCCsGAQUFBzABhh5odHRwOi8vb2NzcC5zdGFyZmllbGR0ZWNo +LmNvbS8wOwYDVR0fBDQwMjAwoC6gLIYqaHR0cDovL2NybC5zdGFyZmllbGR0ZWNo +LmNvbS9zZnJvb3QtZzIuY3JsMEwGA1UdIARFMEMwQQYEVR0gADA5MDcGCCsGAQUF +BwIBFitodHRwczovL2NlcnRzLnN0YXJmaWVsZHRlY2guY29tL3JlcG9zaXRvcnkv +MA0GCSqGSIb3DQEBCwUAA4IBAQBWZcr+8z8KqJOLGMfeQ2kTNCC+Tl94qGuc22pN +QdvBE+zcMQAiXvcAngzgNGU0+bE6TkjIEoGIXFs+CFN69xpk37hQYcxTUUApS8L0 +rjpf5MqtJsxOYUPl/VemN3DOQyuwlMOS6eFfqhBJt2nk4NAfZKQrzR9voPiEJBjO +eT2pkb9UGBOJmVQRDVXFJgt5T1ocbvlj2xSApAer+rKluYjdkf5lO6Sjeb6JTeHQ +sPTIFwwKlhR8Cbds4cLYVdQYoKpBaXAko7nv6VrcPuuUSvC33l8Odvr7+2kDRUBQ +7nIMpBKGgc0T0U7EPMpODdIm8QC3tKai4W56gf0wrHofx1l7 +-----END CERTIFICATE----- diff --git a/check-certificates b/check-certificates new file mode 100644 index 0000000..4c10a52 --- /dev/null +++ b/check-certificates @@ -0,0 +1,117 @@ +#!rsc +# RouterOS script: check-certificates +# Copyright (c) 2013-2019 Christian Hesse +# +# check for certificate validity + +:global Identity; +:global CertRenewUrl; +:global CertRenewPass; + +:global SendNotification; + +:local GetIssuerCN do={ + :foreach IssuerI in=$1 do={ + :if ([ :pick $IssuerI 0 3 ] = "CN=") do={ + :return [ :pick $IssuerI 3 99 ]; + } + } +} + +:local FormatExpire do={ + :global CharacterReplace; + :return [ $CharacterReplace [ $CharacterReplace [ :tostr $1 ] "w" "w " ] "d" "d " ]; +} + +:foreach Cert in=[ / certificate find where !revoked expires-after<3w ] do={ + :local CertName [ / certificate get $Cert name ]; + :local CommonName [ / certificate get $Cert common-name ]; + :local FingerPrint [ / certificate get $Cert fingerprint ]; + + :do { + :if ([ :len $CertRenewUrl ] = 0) do={ + :error "No CertRenewUrl given."; + } + + / tool fetch mode=https check-certificate=yes-without-crl url=($CertRenewUrl . $CommonName . ".pem"); + :foreach PassPhrase in=$CertRenewPass do={ + / certificate import file-name=($CommonName . ".pem") passphrase=$PassPhrase; + } + / file remove [ find where name=($CommonName . ".pem") ]; + + :local CertNew [ / certificate find where common-name=$CommonName fingerprint!=$FingerPrint expires-after>3w ]; + :local CertNameNew [ / certificate get $CertNew name ]; + + :foreach IpService in=[ / ip service find where certificate=$CertName ] do={ + / ip service set $IpService certificate=$CertNameNew; + } + + :do { + :foreach Identity in=[ / ip ipsec identity find where certificate=$CertName ] do={ + / ip ipsec identity set $Identity certificate=$CertNameNew; + } + :foreach Identity in=[ / ip ipsec identity find where remote-certificate=$CertName ] do={ + / ip ipsec identity set $Identity remote-certificate=$CertNameNew; + } + } on-error={ + :log debug ("Setting IPSEC certificates failed. Package 'security' not installed?"); + } + + :do { + :foreach Hotspot in=[ / ip hotspot profile find where ssl-certificate=$CertName ] do={ + / ip hotspot profile set $Hotspot ssl-certificate=$CertNameNew; + } + } on-error={ + :log debug ("Setting hotspot certificates failed. Package 'hotspot' not installed?"); + } + + / certificate remove $Cert; + / certificate set $CertNew name=$CertName; + + :set CommonName [ / certificate get $CertNew common-name ]; + :set FingerPrint [ / certificate get $CertNew fingerprint ]; + :local Issuer [ $GetIssuerCN [ / certificate get $CertNew issuer ] ]; + :local InvalidBefore [ / certificate get $CertNew invalid-before ]; + :local InvalidAfter [ / certificate get $CertNew invalid-after ]; + :local ExpiresAfter [ $FormatExpire [ / certificate get $CertNew expires-after ] ]; + + $SendNotification ("Certificate renewed") \ + ("A certificate on " . $Identity . " has been renewed.\n\n" . \ + "Name: " . $CertName . "\n" . \ + "CommonName: " . $CommonName . "\n" . \ + "Fingerprint: " . $FingerPrint . "\n" . \ + "Issuer: " . $Issuer . "\n" . \ + "Validity: " . $InvalidBefore . " to " . $InvalidAfter . "\n" . \ + "Expires in: " . $ExpiresAfter); + :log info ("The certificate " . $CertName . " has been renewed."); + } on-error={ + :log debug ("Could not renew certificate " . $CertName "."); + } +} + +:foreach Cert in=[ / certificate find where !revoked expires-after<2w ] do={ + :local CertName [ / certificate get $Cert name ]; + :local CommonName [ / certificate get $Cert common-name ]; + :local FingerPrint [ / certificate get $Cert fingerprint ]; + :local Issuer [ $GetIssuerCN [ / certificate get $Cert issuer ] ]; + :local InvalidBefore [ / certificate get $Cert invalid-before ]; + :local InvalidAfter [ / certificate get $Cert invalid-after ]; + + :local ExpiresAfter [ $FormatExpire [ / certificate get $Cert expires-after ] ]; + :local State "is about to expire"; + :if ([ / certificate get $Cert expired ] = true) do={ + :set ExpiresAfter "expired"; + :set State "expired"; + } + + $SendNotification ("Certificate warning!") \ + ("A certificate on " . $Identity . " " . $State . ".\n\n" . \ + "Name: " . $CertName . "\n" . \ + "CommonName: " . $CommonName . "\n" . \ + "Fingerprint: " . $FingerPrint . "\n" . \ + "Issuer: " . $Issuer . "\n" . \ + "Validity: " . $InvalidBefore . " to " . $InvalidAfter . "\n" . \ + "Expires in: " . $ExpiresAfter); + :log warning ("The certificate " . $CertName . " " . $State . \ + ", it is invalid after " . $InvalidAfter . "."); +} diff --git a/check-certificates.rsc b/check-certificates.rsc deleted file mode 100644 index be8e4df..0000000 --- a/check-certificates.rsc +++ /dev/null @@ -1,242 +0,0 @@ -#!rsc by RouterOS -# RouterOS script: check-certificates -# Copyright (c) 2013-2025 Christian Hesse -# https://rsc.eworm.de/COPYING.md -# -# requires RouterOS, version=7.15 -# requires device-mode, fetch -# -# check for certificate validity -# https://rsc.eworm.de/doc/check-certificates.md - -:global GlobalFunctionsReady; -:while ($GlobalFunctionsReady != true) do={ :delay 500ms; } - -:local ExitOK false; -:do { - :local ScriptName [ :jobname ]; - - :global CertRenewTime; - :global CertRenewUrl; - :global CertWarnTime; - :global Identity; - - :global CertificateAvailable - :global EscapeForRegEx; - :global IfThenElse; - :global LogPrint; - :global ParseKeyValueStore; - :global ScriptLock; - :global SendNotification2; - :global SymbolForNotification; - :global UrlEncode; - :global WaitFullyConnected; - - :local CheckCertificatesDownloadImport do={ - :local ScriptName [ :tostr $1 ]; - :local CertName [ :tostr $2 ]; - :local FetchName [ :tostr $3 ]; - - :global CertRenewUrl; - :global CertRenewPass; - - :global CertificateNameByCN; - :global EscapeForRegEx; - :global FetchUserAgentStr; - :global LogPrint; - :global RmFile; - :global UrlEncode; - :global WaitForFile; - - :foreach Type in={ "p12"; "pem" } do={ - :local CertFileName ([ $UrlEncode $FetchName ] . "." . $Type); - $LogPrint debug $ScriptName ("Trying type '" . $Type . "' for '" . $CertName . \ - "' (file '" . $CertFileName . "')..."); - - :do { - /tool/fetch check-certificate=yes-without-crl http-header-field=({ [ $FetchUserAgentStr $ScriptName ] }) \ - ($CertRenewUrl . $CertFileName) dst-path=$CertFileName as-value; - $WaitForFile $CertFileName; - - :local DecryptionFailed true; - :foreach I,PassPhrase in=$CertRenewPass do={ - :do { - $LogPrint debug $ScriptName ("Trying " . $I . ". passphrase... "); - :local Result [ /certificate/import file-name=$CertFileName passphrase=$PassPhrase as-value ]; - :if ($Result->"decryption-failures" = 0) do={ - $LogPrint debug $ScriptName ("Success!"); - :set DecryptionFailed false; - } - } on-error={ } - } - $RmFile $CertFileName; - - :if ($DecryptionFailed = true) do={ - $LogPrint warning $ScriptName ("Decryption failed for certificate file '" . $CertFileName . "'."); - } - - :foreach CertInChain in=[ /certificate/find where common-name!=$CertName !private-key \ - name~("^" . [ $EscapeForRegEx $CertFileName ] . "_[0-9]+\$") \ - !(subject-alt-name~("(^|\\W)(DNS|IP):" . [ $EscapeForRegEx $CertName ] . "(\\W|\$)")) \ - !(common-name=[]) ] do={ - $CertificateNameByCN [ /certificate/get $CertInChain common-name ]; - } - - :return true; - } on-error={ - $LogPrint debug $ScriptName ("Could not download certificate file '" . $CertFileName . "'."); - } - } - - :return false; - } - - :local FormatInfo do={ - :local Cert $1; - - :global FormatLine; - :global FormatMultiLines; - :global IfThenElse; - - :local FormatExpire do={ - :global CharacterReplace; - :return [ $CharacterReplace [ $CharacterReplace [ :tostr $1 ] "w" "w " ] "d" "d " ]; - } - - :local FormatCertChain do={ - :local Cert $1; - - :global EitherOr; - :global ParseKeyValueStore; - - :local CertVal [ /certificate/get $Cert ]; - - :if ([ :typeof ($CertVal->"issuer") ] = "nothing") do={ - :return "self-signed"; - } - - :local Return ""; - :for I from=0 to=5 do={ - :set Return ($Return . [ $EitherOr ([ $ParseKeyValueStore ($CertVal->"issuer") ]->"CN") \ - ([ $ParseKeyValueStore (($CertVal->"issuer")->0) ]->"CN") ]); - :set CertVal [ /certificate/get [ find where skid=($CertVal->"akid") ] ]; - :if (($CertVal->"akid") = "" || ($CertVal->"akid") = ($CertVal->"skid")) do={ - :return $Return; - } - :set Return ($Return . " -> "); - } - :return ($Return . "..."); - } - - :local CertVal [ /certificate/get $Cert ]; - - :return ( \ - [ $FormatLine "Name" ($CertVal->"name") ] . "\n" . \ - [ $IfThenElse ([ :len ($CertVal->"common-name") ] > 0) ([ $FormatLine "CommonName" ($CertVal->"common-name") ] . "\n") ] . \ - [ $IfThenElse ([ :len ($CertVal->"subject-alt-name") ] > 0) ([ $FormatMultiLines "SubjectAltNames" ($CertVal->"subject-alt-name") ] . "\n") ] . \ - [ $FormatLine "Private key" [ $IfThenElse (($CertVal->"private-key") = true) "available" "missing" ] ] . "\n" . \ - [ $FormatLine "Fingerprint" ($CertVal->"fingerprint") ] . "\n" . \ - [ $IfThenElse ([ :len ($CertVal->"ca") ] > 0) [ $FormatLine "Issuer" ($CertVal->"ca") ] [ $FormatLine "Issuer chain" [ $FormatCertChain $Cert ] ] ] . "\n" . \ - "Validity:\n" . \ - [ $FormatLine " from" ($CertVal->"invalid-before") ] . "\n" . \ - [ $FormatLine " to" ($CertVal->"invalid-after") ] . "\n" . \ - [ $FormatLine "Expires in" [ $IfThenElse (($CertVal->"expired") = true) "expired" [ $FormatExpire ($CertVal->"expires-after") ] ] ]); - } - - :if ([ $ScriptLock $ScriptName ] = false) do={ - :set ExitOK true; - :error false; - } - $WaitFullyConnected; - - :foreach Cert in=[ /certificate/find where !revoked !ca !scep-url expires-after<$CertRenewTime ] do={ - :local CertVal [ /certificate/get $Cert ]; - :local LastName; - :local FetchName; - - :do { - :if ([ :len $CertRenewUrl ] = 0) do={ - $LogPrint info $ScriptName ("No CertRenewUrl given."); - :error false; - } - $LogPrint info $ScriptName ("Attempting to renew certificate '" . ($CertVal->"name") . "'."); - - :local ImportSuccess false; - :set LastName ($CertVal->"common-name"); - :set FetchName $LastName; - :set ImportSuccess [ $CheckCertificatesDownloadImport $ScriptName $LastName $FetchName ]; - :foreach SAN in=($CertVal->"subject-alt-name") do={ - :if ($ImportSuccess = false) do={ - :set LastName [ :pick $SAN ([ :find $SAN ":" ] + 1) [ :len $SAN ] ]; - :set FetchName $LastName; - :set ImportSuccess [ $CheckCertificatesDownloadImport $ScriptName $LastName $FetchName ]; - :if ($ImportSuccess = false && [ :pick $LastName 0 2 ] = "*.") do={ - :set FetchName ("star." . [ :pick $LastName 2 [ :len $LastName ] ]); - :set ImportSuccess [ $CheckCertificatesDownloadImport $ScriptName $LastName $FetchName ]; - } - } - } - :if ($ImportSuccess = false) do={ :error false; } - - :if ([ :len ($CertVal->"fingerprint") ] > 0 && $CertVal->"fingerprint" != [ /certificate/get $Cert fingerprint ]) do={ - $LogPrint debug $ScriptName ("Certificate '" . $CertVal->"name" . "' was updated in place."); - :set CertVal [ /certificate/get $Cert ]; - } else={ - $LogPrint debug $ScriptName ("Certificate '" . $CertVal->"name" . "' was not updated, but replaced."); - - :local CertNew [ /certificate/find where name~("^" . [ $EscapeForRegEx [ $UrlEncode $FetchName ] ] . "\\.(p12|pem)_[0-9]+\$") \ - (common-name=($CertVal->"common-name") or subject-alt-name~("(^|\\W)(DNS|IP):" . [ $EscapeForRegEx $LastName ] . "(\\W|\$)")) \ - fingerprint!=[ :tostr ($CertVal->"fingerprint") ] expires-after>$CertRenewTime ]; - :local CertNewVal [ /certificate/get $CertNew ]; - - :if ([ $CertificateAvailable ([ $ParseKeyValueStore ($CertNewVal->"issuer") ]->"CN") ] = false) do={ - $LogPrint warning $ScriptName ("The certificate chain is not available!"); - } - - :if (($CertVal->"private-key") = true && ($CertVal->"private-key") != ($CertNewVal->"private-key")) do={ - /certificate/remove $CertNew; - $LogPrint warning $ScriptName ("Old certificate '" . ($CertVal->"name") . "' has a private key, new certificate does not. Aborting renew."); - :error false; - } - - /ip/service/set certificate=($CertNewVal->"name") [ find where certificate=($CertVal->"name") ]; - - /ip/ipsec/identity/set certificate=($CertNewVal->"name") [ find where certificate=($CertVal->"name") ]; - /ip/ipsec/identity/set remote-certificate=($CertNewVal->"name") [ find where remote-certificate=($CertVal->"name") ]; - - /ip/hotspot/profile/set ssl-certificate=($CertNewVal->"name") [ find where ssl-certificate=($CertVal->"name") ]; - - /certificate/remove $Cert; - /certificate/set $CertNew name=($CertVal->"name"); - :set Cert $CertNew; - :set CertVal [ /certificate/get $CertNew ]; - } - - $SendNotification2 ({ origin=$ScriptName; silent=true; \ - subject=([ $SymbolForNotification "lock-with-ink-pen" ] . "Certificate renewed: " . ($CertVal->"name")); \ - message=("A certificate on " . $Identity . " has been renewed.\n\n" . [ $FormatInfo $Cert ]) }); - $LogPrint info $ScriptName ("The certificate '" . ($CertVal->"name") . "' has been renewed."); - } on-error={ - $LogPrint debug $ScriptName ("Could not renew certificate '" . ($CertVal->"name") . "'."); - } - } - - :foreach Cert in=[ /certificate/find where !revoked !scep-url !(expires-after=[]) \ - expires-after<$CertWarnTime !(fingerprint=[]) ] do={ - :local CertVal [ /certificate/get $Cert ]; - - :if ([ :len [ /certificate/scep-server/find where ca-cert=($CertVal->"ca") ] ] > 0) do={ - $LogPrint debug $ScriptName ("Certificate '" . ($CertVal->"name") . "' is handled by SCEP, skipping."); - } else={ - :local State [ $IfThenElse (($CertVal->"expired") = true) "expired" "is about to expire" ]; - - $SendNotification2 ({ origin=$ScriptName; \ - subject=([ $SymbolForNotification "warning-sign" ] . "Certificate warning: " . ($CertVal->"name")); \ - message=("A certificate on " . $Identity . " " . $State . ".\n\n" . [ $FormatInfo $Cert ]) }); - $LogPrint info $ScriptName ("The certificate '" . ($CertVal->"name") . "' " . $State . \ - ", it is invalid after " . ($CertVal->"invalid-after") . "."); - } - } -} on-error={ - :global ExitError; $ExitError $ExitOK [ :jobname ]; -} diff --git a/check-health.d/state.rsc b/check-health.d/state.rsc deleted file mode 100644 index 2991935..0000000 --- a/check-health.d/state.rsc +++ /dev/null @@ -1,48 +0,0 @@ -#!rsc by RouterOS -# RouterOS script: check-health.d/state -# Copyright (c) 2019-2025 Christian Hesse -# https://rsc.eworm.de/COPYING.md -# -# requires RouterOS, version=7.15 -# -# check for RouterOS health state - state plugin -# https://rsc.eworm.de/doc/check-health.md - -:global CheckHealthPlugins; - -:set ($CheckHealthPlugins->[ :jobname ]) do={ - :local FuncName [ :tostr $0 ]; - - :global CheckHealthLast; - :global Identity; - - :global LogPrint; - :global SendNotification2; - :global SymbolForNotification; - - :if ([ :len [ /system/health/find where type="" name~"-state\$"] ] = 0) do={ - $LogPrint debug $FuncName ("Your device does not provide any state health values."); - :return false; - } - - :foreach State in=[ /system/health/find where type="" name~"-state\$" ] do={ - :local Name [ /system/health/get $State name ]; - :local Value [ /system/health/get $State value ]; - - :if ([ :typeof ($CheckHealthLast->$Name) ] != "nothing") do={ - :if ($CheckHealthLast->$Name = "ok" && \ - $Value != "ok") do={ - $SendNotification2 ({ origin=$FuncName; \ - subject=([ $SymbolForNotification "cross-mark" ] . "Health warning: " . $Name); \ - message=("The device '" . $Name . "' on " . $Identity . " failed!") }); - } - :if ($CheckHealthLast->$Name != "ok" && \ - $Value = "ok") do={ - $SendNotification2 ({ origin=$FuncName; \ - subject=([ $SymbolForNotification "white-heavy-check-mark" ] . "Health recovery: " . $Name); \ - message=("The device '" . $Name . "' on " . $Identity . " recovered!") }); - } - } - :set ($CheckHealthLast->$Name) $Value; - } -} diff --git a/check-health.d/temperature.rsc b/check-health.d/temperature.rsc deleted file mode 100644 index a2f632d..0000000 --- a/check-health.d/temperature.rsc +++ /dev/null @@ -1,74 +0,0 @@ -#!rsc by RouterOS -# RouterOS script: check-health.d/temperature -# Copyright (c) 2019-2025 Christian Hesse -# https://rsc.eworm.de/COPYING.md -# -# requires RouterOS, version=7.15 -# -# check for RouterOS health state - temperature plugin -# https://rsc.eworm.de/doc/check-health.md - -:global CheckHealthPlugins; - -:set ($CheckHealthPlugins->[ :jobname ]) do={ - :local FuncName [ :tostr $0 ]; - - :global CheckHealthLast; - :global CheckHealthTemperature; - :global CheckHealthTemperatureDeviation; - :global CheckHealthTemperatureNotified; - :global Identity; - - :global LogPrint; - :global SendNotification2; - :global SymbolForNotification; - - :if ([ :len [ /system/health/find where type="C" ] ] = 0) do={ - $LogPrint debug $FuncName ("Your device does not provide any voltage health values."); - :return false; - } - - :local TempToNum do={ - :global CharacterReplace; - :local T [ :toarray [ $CharacterReplace $1 "." "," ] ]; - :return ($T->0 * 10 + $T->1); - } - - :if ([ :typeof $CheckHealthTemperatureNotified ] != "array") do={ - :set CheckHealthTemperatureNotified ({}); - } - - :foreach Temperature in=[ /system/health/find where type="C" ] do={ - :local Name [ /system/health/get $Temperature name ]; - :local Value [ /system/health/get $Temperature value ]; - - :if ([ :typeof ($CheckHealthLast->$Name) ] != "nothing") do={ - :if ([ :typeof ($CheckHealthTemperature->$Name) ] != "num" ) do={ - $LogPrint info $FuncName ("No threshold given for " . $Name . ", assuming 50C."); - :set ($CheckHealthTemperature->$Name) 50; - } - :local Validate [ /system/health/get [ find where name=$Name ] value ]; - :while ($Value != $Validate) do={ - :set Value $Validate; - :set Validate [ /system/health/get [ find where name=$Name ] value ]; - } - :if ($Value > $CheckHealthTemperature->$Name && \ - $CheckHealthTemperatureNotified->$Name != true) do={ - $SendNotification2 ({ origin=$FuncName; \ - subject=([ $SymbolForNotification "fire" ] . "Health warning: " . $Name); \ - message=("The " . $Name . " on " . $Identity . " is above threshold: " . \ - $Value . "\C2\B0" . "C") }); - :set ($CheckHealthTemperatureNotified->$Name) true; - } - :if ($Value <= ($CheckHealthTemperature->$Name - $CheckHealthTemperatureDeviation) && \ - $CheckHealthTemperatureNotified->$Name = true) do={ - $SendNotification2 ({ origin=$FuncName; \ - subject=([ $SymbolForNotification "white-heavy-check-mark" ] . "Health recovery: " . $Name); \ - message=("The " . $Name . " on " . $Identity . " dropped below threshold: " . \ - $Value . "\C2\B0" . "C") }); - :set ($CheckHealthTemperatureNotified->$Name) false; - } - } - :set ($CheckHealthLast->$Name) $Value; - } -} diff --git a/check-health.d/voltage.rsc b/check-health.d/voltage.rsc deleted file mode 100644 index 9071c88..0000000 --- a/check-health.d/voltage.rsc +++ /dev/null @@ -1,63 +0,0 @@ -#!rsc by RouterOS -# RouterOS script: check-health.d/voltage -# Copyright (c) 2019-2025 Christian Hesse -# https://rsc.eworm.de/COPYING.md -# -# requires RouterOS, version=7.15 -# -# check for RouterOS health state - voltage plugin -# https://rsc.eworm.de/doc/check-health.md - -:global CheckHealthPlugins; - -:set ($CheckHealthPlugins->[ :jobname ]) do={ - :local FuncName [ :tostr $0 ]; - - :global CheckHealthLast; - :global CheckHealthVoltageLow; - :global CheckHealthVoltagePercent; - :global Identity; - - :global FormatLine; - :global IfThenElse; - :global LogPrint; - :global SendNotification2; - :global SymbolForNotification; - - :if ([ :len [ /system/health/find where type="V" ] ] = 0) do={ - $LogPrint debug $FuncName ("Your device does not provide any voltage health values."); - :return false; - } - - :foreach Voltage in=[ /system/health/find where type="V" ] do={ - :local Name [ /system/health/get $Voltage name ]; - :local Value [ /system/health/get $Voltage value ]; - - :if ([ :typeof ($CheckHealthLast->$Name) ] != "nothing") do={ - :local NumCurr [ $TempToNum $Value ]; - :local NumLast [ $TempToNum ($CheckHealthLast->$Name) ]; - - :if ($NumLast * (100 + $CheckHealthVoltagePercent) < $NumCurr * 100 || \ - $NumLast * 100 > $NumCurr * (100 + $CheckHealthVoltagePercent)) do={ - $SendNotification2 ({ origin=$FuncName; \ - subject=([ $SymbolForNotification ("high-voltage-sign,chart-" . [ $IfThenElse ($NumLast < \ - $NumCurr) "in" "de" ] . "creasing") ] . "Health warning: " . $Name); \ - message=("The " . $Name . " on " . $Identity . " jumped more than " . $CheckHealthVoltagePercent . "%.\n\n" . \ - [ $FormatLine "old value" ($CheckHealthLast->$Name . " V") 12 ] . "\n" . \ - [ $FormatLine "new value" ($Value . " V") 12 ]) }); - } else={ - :if ($NumCurr <= $CheckHealthVoltageLow && $NumLast > $CheckHealthVoltageLow) do={ - $SendNotification2 ({ origin=$FuncName; \ - subject=([ $SymbolForNotification "high-voltage-sign,chart-decreasing" ] . "Health warning: Low " . $Name); \ - message=("The " . $Name . " on " . $Identity . " dropped to " . $Value . " V below hard limit.") }); - } - :if ($NumCurr > $CheckHealthVoltageLow && $NumLast <= $CheckHealthVoltageLow) do={ - $SendNotification2 ({ origin=$FuncName; \ - subject=([ $SymbolForNotification "high-voltage-sign,chart-increasing" ] . "Health recovery: Low " . $Name); \ - message=("The " . $Name . " on " . $Identity . " recovered to " . $Value . " V above hard limit.") }); - } - } - } - :set ($CheckHealthLast->$Name) $Value; - } -} diff --git a/check-health.rsc b/check-health.rsc deleted file mode 100644 index f02a249..0000000 --- a/check-health.rsc +++ /dev/null @@ -1,110 +0,0 @@ -#!rsc by RouterOS -# RouterOS script: check-health -# Copyright (c) 2019-2025 Christian Hesse -# https://rsc.eworm.de/COPYING.md -# -# requires RouterOS, version=7.15 -# -# check for RouterOS health state -# https://rsc.eworm.de/doc/check-health.md - -:global GlobalFunctionsReady; -:while ($GlobalFunctionsReady != true) do={ :delay 500ms; } - -:local ExitOK false; -:do { - :local ScriptName [ :jobname ]; - - :global CheckHealthCPUUtilization; - :global CheckHealthCPUUtilizationNotified; - :global CheckHealthLast; - :global CheckHealthRAMUtilizationNotified; - :global Identity; - - :global FormatLine; - :global HumanReadableNum; - :global IfThenElse; - :global LogPrint; - :global ScriptLock; - :global SendNotification2; - :global SymbolForNotification; - :global ValidateSyntax; - - :local TempToNum do={ - :global CharacterReplace; - :local T [ :toarray [ $CharacterReplace $1 "." "," ] ]; - :return ($T->0 * 10 + $T->1); - } - - :if ([ $ScriptLock $ScriptName ] = false) do={ - :set ExitOK true; - :error false; - } - - :local Resource [ /system/resource/get ]; - - :set CheckHealthCPUUtilization (($CheckHealthCPUUtilization * 4 + ($Resource->"cpu-load") * 10) / 5); - :if ($CheckHealthCPUUtilization > 750 && $CheckHealthCPUUtilizationNotified != true) do={ - $SendNotification2 ({ origin=$ScriptName; \ - subject=([ $SymbolForNotification "abacus,chart-increasing" ] . "Health warning: CPU utilization"); \ - message=("The average CPU utilization on " . $Identity . " is at " . ($CheckHealthCPUUtilization / 10) . "%!") }); - :set CheckHealthCPUUtilizationNotified true; - } - :if ($CheckHealthCPUUtilization < 650 && $CheckHealthCPUUtilizationNotified = true) do={ - $SendNotification2 ({ origin=$ScriptName; \ - subject=([ $SymbolForNotification "abacus,chart-decreasing" ] . "Health recovery: CPU utilization"); \ - message=("The average CPU utilization on " . $Identity . " decreased to " . ($CheckHealthCPUUtilization / 10) . "%.") }); - :set CheckHealthCPUUtilizationNotified false; - } - - :local CheckHealthRAMUtilization (($Resource->"total-memory" - $Resource->"free-memory") * 100 / $Resource->"total-memory"); - :if ($CheckHealthRAMUtilization >=80 && $CheckHealthRAMUtilizationNotified != true) do={ - $SendNotification2 ({ origin=$ScriptName; \ - subject=([ $SymbolForNotification "card-file-box,chart-increasing" ] . "Health warning: RAM utilization"); \ - message=("The RAM utilization on " . $Identity . " is at " . $CheckHealthRAMUtilization . "%!\n\n" . \ - [ $FormatLine "total" ([ $HumanReadableNum ($Resource->"total-memory") 1024 ] . "B") 8 ] . "\n" . \ - [ $FormatLine "used" ([ $HumanReadableNum ($Resource->"total-memory" - $Resource->"free-memory") 1024 ] . "B") 8 ] . "\n" . \ - [ $FormatLine "free" ([ $HumanReadableNum ($Resource->"free-memory") 1024 ] . "B") 8 ]) }); - :set CheckHealthRAMUtilizationNotified true; - } - :if ($CheckHealthRAMUtilization < 70 && $CheckHealthRAMUtilizationNotified = true) do={ - $SendNotification2 ({ origin=$ScriptName; \ - subject=([ $SymbolForNotification "card-file-box,chart-decreasing" ] . "Health recovery: RAM utilization"); \ - message=("The RAM utilization on " . $Identity . " decreased to " . $CheckHealthRAMUtilization . "%.") }); - :set CheckHealthRAMUtilizationNotified false; - } - - :local Plugins [ /system/script/find where name~"^check-health.d/." ]; - :if ([ :len $Plugins ] = 0) do={ - $LogPrint debug $ScriptName ("No plugins installed."); - :set ExitOK true; - :error true; - } - - :global CheckHealthPlugins ({}); - :if ([ :typeof $CheckHealthLast ] != "array") do={ - :set CheckHealthLast ({}); - } - - :foreach Plugin in=$Plugins do={ - :local PluginVal [ /system/script/get $Plugin ]; - :if ([ $ValidateSyntax ($PluginVal->"source") ] = true) do={ - :do { - /system/script/run $Plugin; - } on-error={ - $LogPrint error $ScriptName ("Plugin '" . $ScriptVal->"name" . "' failed to run."); - } - } else={ - $LogPrint error $ScriptName ("Plugin '" . $ScriptVal->"name" . "' failed syntax validation, skipping."); - } - } - - :foreach PluginName,Discard in=$CheckHealthPlugins do={ - ($CheckHealthPlugins->$PluginName) \ - ("\$CheckHealthPlugins->\"" . $PluginName . "\""); - } - - :set CheckHealthPlugins; -} on-error={ - :global ExitError; $ExitError $ExitOK [ :jobname ]; -} diff --git a/check-lte-firmware-upgrade b/check-lte-firmware-upgrade new file mode 100644 index 0000000..5a50733 --- /dev/null +++ b/check-lte-firmware-upgrade @@ -0,0 +1,32 @@ +#!rsc +# RouterOS script: check-lte-firmware-upgrade +# Copyright (c) 2018-2019 Christian Hesse +# +# check for LTE firmware upgrade, send notification e-mails + +:global Identity; +:global SentLteFirmwareUpgradeNotification; + +:global SendNotification; + +:foreach Interface in=[ / interface lte find ] do={ + :local IntName [ / interface lte get $Interface name ]; + :do { + :local Firmware [ / interface lte firmware-upgrade $Interface once as-value ]; + + :if ($SentLteFirmwareUpgradeNotification = ($Firmware->"latest")) do={ + :log debug ("Already sent the LTE firmware upgrade notification for version " . \ + ($Firmware->"latest") . "."); + } else={ + :if (($Firmware->"installed") != ($Firmware->"latest")) do={ + $SendNotification ("LTE firmware upgrade notification") \ + ("A new firmware version " . ($Firmware->"latest") . " is available for " . \ + "LTE interface " . $IntName . " on " . $Identity . "."); + :set SentLteFirmwareUpgradeNotification ($Firmware->"latest"); + } + } + } on-error={ + :log debug ("Could not get latest LTE firmware version for interface " . \ + $IntName . "."); + } +} diff --git a/check-lte-firmware-upgrade.rsc b/check-lte-firmware-upgrade.rsc deleted file mode 100644 index c5b6cb5..0000000 --- a/check-lte-firmware-upgrade.rsc +++ /dev/null @@ -1,107 +0,0 @@ -#!rsc by RouterOS -# RouterOS script: check-lte-firmware-upgrade -# Copyright (c) 2018-2025 Christian Hesse -# https://rsc.eworm.de/COPYING.md -# -# requires RouterOS, version=7.15 -# -# check for LTE firmware upgrade, send notification -# https://rsc.eworm.de/doc/check-lte-firmware-upgrade.md - -:global GlobalFunctionsReady; -:while ($GlobalFunctionsReady != true) do={ :delay 500ms; } - -:local ExitOK false; -:do { - :local ScriptName [ :jobname ]; - - :global SentLteFirmwareUpgradeNotification; - - :global ScriptLock; - - :if ([ $ScriptLock $ScriptName ] = false) do={ - :set ExitOK true; - :error false; - } - - :if ([ :typeof $SentLteFirmwareUpgradeNotification ] != "array") do={ - :global SentLteFirmwareUpgradeNotification ({}); - } - - :local CheckInterface do={ - :local ScriptName $1; - :local Interface $2; - - :global Identity; - :global SentLteFirmwareUpgradeNotification; - - :global FormatLine; - :global IfThenElse; - :global LogPrint; - :global ScriptFromTerminal; - :global SendNotification2; - :global SymbolForNotification; - - :local IntName [ /interface/lte/get $Interface name ]; - :local Firmware; - :local Info; - :do { - :set Firmware [ /interface/lte/firmware-upgrade $Interface as-value ]; - :set Info [ /interface/lte/monitor $Interface once as-value ]; - } on-error={ - $LogPrint debug $ScriptName ("Could not get latest LTE firmware version for interface " . \ - $IntName . "."); - :return false; - } - - :if ([ :len ($Firmware->"latest") ] = 0) do={ - $LogPrint info $ScriptName ("An empty string is not a valid version."); - :return false; - } - - :if (($Firmware->"installed") = ($Firmware->"latest")) do={ - :if ([ $ScriptFromTerminal $ScriptName ] = true) do={ - $LogPrint info $ScriptName ("No firmware upgrade available for LTE interface " . $IntName . "."); - } - :return true; - } - - :if ([ $ScriptFromTerminal $ScriptName ] = true && \ - [ :len [ /system/script/find where name="unattended-lte-firmware-upgrade" ] ] > 0) do={ - :put ("Do you want to start unattended lte firmware upgrade for interface " . $IntName . "? [y/N]"); - :if (([ /terminal/inkey timeout=60 ] % 32) = 25) do={ - /system/script/run unattended-lte-firmware-upgrade; - $LogPrint info $ScriptName ("Scheduled lte firmware upgrade for interface " . $IntName . "..."); - :return true; - } else={ - :put "Canceled..."; - } - } - - :if (($SentLteFirmwareUpgradeNotification->$IntName) = ($Firmware->"latest")) do={ - $LogPrint debug $ScriptName ("Already sent the LTE firmware upgrade notification for version " . \ - ($Firmware->"latest") . "."); - :return false; - } - - $LogPrint info $ScriptName ("A new firmware version " . ($Firmware->"latest") . " is available for " . \ - "LTE interface " . $IntName . "."); - $SendNotification2 ({ origin=$ScriptName; \ - subject=([ $SymbolForNotification "sparkles" ] . "LTE firmware upgrade"); \ - message=("A new firmware version " . ($Firmware->"latest") . " is available for " . \ - "LTE interface " . $IntName . " on " . $Identity . ".\n\n" . \ - [ $IfThenElse ([ :len ($Info->"manufacturer") ] > 0) ([ $FormatLine "Manufacturer" ($Info->"manufacturer") ] . "\n") ] . \ - [ $IfThenElse ([ :len ($Info->"model") ] > 0) ([ $FormatLine "Model" ($Info->"model") ] . "\n") ] . \ - [ $IfThenElse ([ :len ($Info->"revision") ] > 0) ([ $FormatLine "Revision" ($Info->"revision") ] . "\n") ] . \ - "Firmware version:\n" . \ - [ $FormatLine " Installed" ($Firmware->"installed") ] . "\n" . \ - [ $FormatLine " Available" ($Firmware->"latest") ]); silent=true }); - :set ($SentLteFirmwareUpgradeNotification->$IntName) ($Firmware->"latest"); - } - - :foreach Interface in=[ /interface/lte/find ] do={ - $CheckInterface $ScriptName $Interface; - } -} on-error={ - :global ExitError; $ExitError $ExitOK [ :jobname ]; -} diff --git a/check-routeros-update b/check-routeros-update new file mode 100644 index 0000000..ba420da --- /dev/null +++ b/check-routeros-update @@ -0,0 +1,82 @@ +#!rsc +# RouterOS script: check-routeros-update +# Copyright (c) 2013-2019 Christian Hesse +# +# check for RouterOS update, send notification e-mails + +:global Identity; +:global SafeUpdateUrl; +:global SentRouterosUpdateNotification; + +:global SendNotification; + +:local Update do={ + :if ([ / system script print count-only where name="packages-update" ] > 0) do={ + / system script run packages-update; + } else={ + / system package update install without-paging; + } + :error "Waiting for system to reboot."; +} + +:if ([ / system package print count-only where name="wireless" disabled=no ] > 0) do={ + :if ([ / interface wireless cap get enabled ] = true && \ + [ / caps-man manager get enabled ] = false) do={ + :error "System is managed by CAPsMAN, not checking."; + } +} + +/ system package update check-for-updates without-paging; +:local InstalledVersion [ / system package update get installed-version ]; +:local LatestVersion [ / system package update get latest-version ]; + +:if ($InstalledVersion != $LatestVersion) do={ + :local Channel [ / system package update get channel ]; + :local BoardName [ / system resource get board-name ]; + :local Model [ / system routerboard get model ]; + :local SerialNumber [ / system routerboard get serial-number ]; + + :if ([ :len $SafeUpdateUrl ] > 0) do={ + :local Result; + :do { + :set Result [ / tool fetch check-certificate=yes-without-crl \ + ($SafeUpdateUrl . $Channel . "?installed=" . $InstalledVersion . \ + "&latest=" . $LatestVersion) output=user as-value ]; + } on-error={ + :log warning ("Failed receiving safe version for " . $Channel . "."); + } + :if ($Result->"status" = "finished" && $Result->"data" = $LatestVersion) do={ + :log info ("Version " . $LatestVersion . " is considered safe, updating..."); + $SendNotification ("RouterOS update notification") \ + ("Version " . $LatestVersion . " is considered safe for " . $Channel . \ + ", updating on " . $Identity . "..."); + $Update; + } + } + + :if ([ / system script job print count-only where script="check-routeros-update" parent~"." ] > 0) do={ + :put ("Do you want to install RouterOS version " . $LatestVersion . "? [y/N]"); + :if ([ :terminal inkey timeout=60 ] = 121) do={ + $Update; + } else={ + :put "Canceled..."; + } + } + + :if ($SentRouterosUpdateNotification = $LatestVersion) do={ + :error ("Already sent the RouterOS update notification for version " . \ + $LatestVersion . "."); + } + + $SendNotification ("RouterOS update notification") \ + ("There is a RouterOS update available\n\n" . \ + "Board name: " . $BoardName . "\n" . \ + "Model: " . $Model . "\n" . \ + "Serial number: " . $SerialNumber . "\n" . \ + "Hostname: " . $Identity . "\n" . \ + "Channel: " . $Channel . "\n" . \ + "Installed: " . $InstalledVersion . "\n" . \ + "Available: " . $LatestVersion . "\n\n" .\ + "https://upgrade.mikrotik.com/routeros/" . $LatestVersion . "/CHANGELOG"); + :set SentRouterosUpdateNotification $LatestVersion; +} diff --git a/check-routeros-update.rsc b/check-routeros-update.rsc deleted file mode 100644 index 78161e4..0000000 --- a/check-routeros-update.rsc +++ /dev/null @@ -1,239 +0,0 @@ -#!rsc by RouterOS -# RouterOS script: check-routeros-update -# Copyright (c) 2013-2025 Christian Hesse -# https://rsc.eworm.de/COPYING.md -# -# requires RouterOS, version=7.15 -# requires device-mode, fetch, scheduler -# -# check for RouterOS update, send notification and/or install -# https://rsc.eworm.de/doc/check-routeros-update.md - -:global GlobalFunctionsReady; -:while ($GlobalFunctionsReady != true) do={ :delay 500ms; } - -:local ExitOK false; -:do { - :local ScriptName [ :jobname ]; - - :global Identity; - :global SafeUpdateAll; - :global SafeUpdateNeighbor; - :global SafeUpdateNeighborIdentity; - :global SafeUpdatePatch; - :global SafeUpdateUrl; - :global SentRouterosUpdateNotification; - - :global DeviceInfo; - :global EscapeForRegEx; - :global FetchUserAgentStr; - :global LogPrint; - :global ScriptFromTerminal; - :global ScriptLock; - :global SendNotification2; - :global SymbolForNotification; - :global VersionToNum; - :global WaitFullyConnected; - - :local DoUpdate do={ - :local ScriptName [ :tostr $1 ]; - - :global LogPrint; - - :if ([ :len [ /system/script/find where name="packages-update" ] ] > 0) do={ - /system/script/run packages-update; - } else={ - /system/package/update/install without-paging; - } - $LogPrint info $ScriptName ("Waiting for system to reboot."); - } - - :if ([ $ScriptLock $ScriptName ] = false) do={ - :set ExitOK true; - :error false; - } - - :if ([ :len [ /system/scheduler/find where name="running-from-backup-partition" ] ] > 0) do={ - $LogPrint warning $ScriptName ("Running from backup partition, refusing to act."); - :set ExitOK true; - :error false; - } - - $WaitFullyConnected; - - :if ([ :len [ /system/scheduler/find where name="_RebootForUpdate" ] ] > 0) do={ - :set ExitOK true; - :error "A reboot for update is already scheduled."; - } - - :local License [ /system/license/get ]; - :if ([ :typeof ($License->"deadline-at") ] = "str") do={ - :if ([ :len ($License->"next-renewal-at") ] = 0 && ($License->"limited-upgrades") = true) do={ - $LogPrint warning $ScriptName ("Your license expired on " . ($License->"deadline-at") . "!"); - $SendNotification2 ({ origin=$ScriptName; \ - subject=([ $SymbolForNotification "warning-sign" ] . "License expired!"); \ - message=("Your license expired on " . ($License->"deadline-at") . \ - ", can no longer update RouterOS on " . $Identity . "...") }); - :set ExitOK true; - :error false; - } - - :if ([ :totime ($License->"deadline-at") ] - 3w < [ :timestamp ]) do={ - $LogPrint warning $ScriptName ("Your license will expire on " . ($License->"deadline-at") . "!"); - $SendNotification2 ({ origin=$ScriptName; \ - subject=([ $SymbolForNotification "warning-sign" ] . "License about to expire!"); \ - message=("Your license failed to renew and is about to expire on " . \ - ($License->"deadline-at") . " on " . $Identity . "...") }); - } - } - - $LogPrint debug $ScriptName ("Checking for updates..."); - /system/package/update/check-for-updates without-paging as-value; - :local Update [ /system/package/update/get ]; - - :if (($Update->"installed-version") = ($Update->"latest-version")) do={ - :if ([ $ScriptFromTerminal $ScriptName ] = true) do={ - $LogPrint info $ScriptName ("System is already up to date."); - } - :set ExitOK true; - :error true; - } - - :if ([ :len ($Update->"latest-version") ] = 0) do={ - $LogPrint info $ScriptName ("Received an empty version string from server."); - :set ExitOK true; - :error false; - } - - :local NumInstalled [ $VersionToNum ($Update->"installed-version") ]; - :local NumLatest [ $VersionToNum ($Update->"latest-version") ]; - :local BitMask [ $VersionToNum "255.255zero0" ]; - :local NumInstalledFeature ($NumInstalled & $BitMask); - :local NumLatestFeature ($NumLatest & $BitMask); - :local Link ("https://mikrotik.com/download/changelogs/" . $Update->"channel" . "-release-tree"); - - :if ($NumLatest < [ $VersionToNum "7.0" ]) do={ - $LogPrint warning $ScriptName ("The version '" . ($Update->"latest-version") . "' is not a valid version."); - :set ExitOK true; - :error false; - } - - :if ($NumInstalled < $NumLatest) do={ - :if ($SafeUpdateAll ~ "^YES,? ?PLEASE!?\$") do={ - $LogPrint info $ScriptName ("Installing ALL versions automatically, including " . \ - $Update->"latest-version" . "..."); - $SendNotification2 ({ origin=$ScriptName; \ - subject=([ $SymbolForNotification "sparkles" ] . "RouterOS update: " . $Update->"latest-version"); \ - message=("Installing ALL versions automatically, including " . $Update->"latest-version" . \ - "... Updating on " . $Identity . "..."); link=$Link; silent=true }); - $DoUpdate $ScriptName; - :set ExitOK true; - :error true; - } - - :if ($SafeUpdatePatch = true && $NumInstalledFeature = $NumLatestFeature) do={ - $LogPrint info $ScriptName ("Version " . $Update->"latest-version" . " is a patch release, updating..."); - $SendNotification2 ({ origin=$ScriptName; \ - subject=([ $SymbolForNotification "sparkles" ] . "RouterOS update: " . $Update->"latest-version"); \ - message=("Version " . $Update->"latest-version" . " is a patch update for " . $Update->"channel" . \ - ", updating on " . $Identity . "..."); link=$Link; silent=true }); - $DoUpdate $ScriptName; - :set ExitOK true; - :error true; - } - - :if ($SafeUpdateNeighbor = true) do={ - :local Neighbors [ /ip/neighbor/find where platform="MikroTik" identity~$SafeUpdateNeighborIdentity \ - version~("^" . [ $EscapeForRegEx ($Update->"latest-version") ] . "\\b") ]; - :if ([ :len $Neighbors ] > 0) do={ - :local Neighbor [ /ip/neighbor/get ($Neighbors->0) identity ]; - $LogPrint info $ScriptName ("Seen a neighbor (" . $Neighbor . ") running version " . \ - $Update->"latest-version" . " from " . $Update->"channel" . ", updating..."); - $SendNotification2 ({ origin=$ScriptName; \ - subject=([ $SymbolForNotification "sparkles" ] . "RouterOS update: " . $Update->"latest-version"); \ - message=("Seen a neighbor (" . $Neighbor . ") running version " . $Update->"latest-version" . \ - " from " . $Update->"channel" . ", updating on " . $Identity . "..."); link=$Link; silent=true }); - $DoUpdate $ScriptName; - :set ExitOK true; - :error true; - } - } - - :if ([ :len $SafeUpdateUrl ] > 0) do={ - :local Result; - :do { - :set Result [ /tool/fetch check-certificate=yes-without-crl \ - ($SafeUpdateUrl . $Update->"channel" . "?installed=" . $Update->"installed-version" . \ - "&latest=" . $Update->"latest-version") http-header-field=({ [ $FetchUserAgentStr $ScriptName ] }) \ - output=user as-value ]; - } on-error={ - $LogPrint warning $ScriptName ("Failed receiving safe version for " . $Update->"channel" . "."); - } - :if ($Result->"status" = "finished" && $Result->"data" = $Update->"latest-version") do={ - $LogPrint info $ScriptName ("Version " . $Update->"latest-version" . " is considered safe, updating..."); - $SendNotification2 ({ origin=$ScriptName; \ - subject=([ $SymbolForNotification "sparkles" ] . "RouterOS update: " . $Update->"latest-version"); \ - message=("Version " . $Update->"latest-version" . " is considered safe for " . $Update->"channel" . \ - ", updating on " . $Identity . "..."); link=$Link; silent=true }); - $DoUpdate $ScriptName; - :set ExitOK true; - :error true; - } - } - - :if ([ $ScriptFromTerminal $ScriptName ] = true) do={ - :if (($Update->"channel") = "testing" && $NumInstalledFeature < $NumLatestFeature) do={ - :put ("This is a feature update in testing channel. Switch to channel 'stable'? [y/N]"); - :if (([ /terminal/inkey timeout=60 ] % 32) = 25) do={ - /system/package/update/set channel=stable; - $LogPrint info $ScriptName ("Switched to channel 'stable', please re-run!"); - :set ExitOK true; - :error true; - } - } - - :put ("Do you want to install RouterOS version " . $Update->"latest-version" . "? [y/N]"); - :if (([ /terminal/inkey timeout=60 ] % 32) = 25) do={ - $DoUpdate $ScriptName; - :set ExitOK true; - :error true; - } else={ - :put "Canceled..."; - } - } - - :if ($SentRouterosUpdateNotification = $Update->"latest-version") do={ - $LogPrint info $ScriptName ("Already sent the RouterOS update notification for version " . \ - $Update->"latest-version" . "."); - :set ExitOK true; - :error true; - } - - $SendNotification2 ({ origin=$ScriptName; \ - subject=([ $SymbolForNotification "sparkles" ] . "RouterOS update: " . $Update->"latest-version"); \ - message=("A new RouterOS version " . ($Update->"latest-version") . \ - " is available for " . $Identity . ".\n\n" . \ - [ $DeviceInfo ]); link=$Link; silent=true }); - :set SentRouterosUpdateNotification ($Update->"latest-version"); - } - - :if ($NumInstalled > $NumLatest) do={ - :if ($SentRouterosUpdateNotification = $Update->"latest-version") do={ - $LogPrint info $ScriptName ("Already sent the RouterOS downgrade notification for version " . \ - $Update->"latest-version" . "."); - :set ExitOK true; - :error true; - } - - $SendNotification2 ({ origin=$ScriptName; \ - subject=([ $SymbolForNotification "warning-sign" ] . "RouterOS version: " . $Update->"latest-version"); \ - message=("A different RouterOS version " . ($Update->"latest-version") . \ - " is available for " . $Identity . ", but it is a downgrade.\n\n" . \ - [ $DeviceInfo ]); link=$Link; silent=true }); - $LogPrint info $ScriptName ("A different RouterOS version " . ($Update->"latest-version") . \ - " is available for downgrade."); - :set SentRouterosUpdateNotification ($Update->"latest-version"); - } -} on-error={ - :global ExitError; $ExitError $ExitOK [ :jobname ]; -} diff --git a/collect-wireless-mac.capsman b/collect-wireless-mac.capsman new file mode 100644 index 0000000..4e5a7b0 --- /dev/null +++ b/collect-wireless-mac.capsman @@ -0,0 +1,62 @@ +#!rsc +# RouterOS script: collect-wireless-mac.capsman +# Copyright (c) 2013-2019 Christian Hesse +# +# collect wireless mac adresses in access list +# +# !! Do not edit this file, it is generated from template! + +:global Identity; + +:global GetMacVendor; +:global SendNotification; +:global ScriptLock; + +$ScriptLock "collect-wireless-mac.capsman"; + +:local PlaceBefore [ / caps-man access-list find where comment="--- collected above ---" disabled ]; +:if ([ :len $PlaceBefore ] = 0) do={ + :error "Missing disabled access-list entry with comment '--- collected above ---'"; +} + +:foreach RegTbl in=[ / caps-man registration-table find ] do={ + :local Mac [ / caps-man registration-table get $RegTbl mac-address ]; + :local AccessList ([ / caps-man access-list find where mac-address=$Mac ]->0); + :if ([ :len $AccessList ] = 0) do={ + :local HostName "no dhcp lease"; + :local Address "no dhcp lease"; + :local Lease [ / ip dhcp-server lease find where mac-address=$Mac ]; + :if ([ :len $Lease ] > 0) do={ + :set HostName [ / ip dhcp-server lease get $Lease host-name ]; + :set Address [ / ip dhcp-server lease get $Lease address ]; + } + :if ([ :len $HostName ] = 0) do={ + :set HostName "no hostname"; + } + :if ([ :len $Address ] = 0) do={ + :set Address "no address"; + } + :local RegEntry [ / caps-man registration-table find where mac-address=$Mac ]; + :local Interface [ / caps-man registration-table get $RegEntry interface ]; + :local Ssid [ / caps-man registration-table get $RegEntry ssid ]; + :local DateTime ([ / system clock get date ] . " " . [ / system clock get time ]); + :local Vendor [ $GetMacVendor $Mac ]; + :local Message ("unknown MAC address " . $Mac . " (" . $Vendor . ", " . $HostName . ") " . \ + "first seen on " . $DateTime . " connected to SSID " . $Ssid . ", interface " . $Interface); + / log info $Message; + / caps-man access-list add place-before=$PlaceBefore comment=$Message mac-address=$Mac disabled=yes; + $SendNotification ($Mac . " connected to " . $Ssid) \ + ("A device with unknown MAC address connected to " . $Ssid . " on " . $Identity . ".\n\n" . \ + "Controller: " . $Identity . "\n" . \ + "Interface: " . $Interface . "\n" . \ + "SSID: " . $Ssid . "\n" . \ + "MAC: " . $Mac . "\n" . \ + "Vendor: " . $Vendor . "\n" . \ + "Hostname: " . $HostName . "\n" . \ + "Address: " . $Address . "\n" . \ + "Date: " . $DateTime); + } else={ + :local Comment [ / caps-man access-list get $AccessList comment ]; + :log debug ("MAC address " . $Mac . " already known: " . $Comment); + } +} diff --git a/collect-wireless-mac.capsman.rsc b/collect-wireless-mac.capsman.rsc deleted file mode 100644 index 17e09e3..0000000 --- a/collect-wireless-mac.capsman.rsc +++ /dev/null @@ -1,100 +0,0 @@ -#!rsc by RouterOS -# RouterOS script: collect-wireless-mac.capsman -# Copyright (c) 2013-2025 Christian Hesse -# https://rsc.eworm.de/COPYING.md -# -# provides: lease-script, order=40 -# requires RouterOS, version=7.15 -# -# collect wireless mac adresses in access list -# https://rsc.eworm.de/doc/collect-wireless-mac.md -# -# !! Do not edit this file, it is generated from template! - -:global GlobalFunctionsReady; -:while ($GlobalFunctionsReady != true) do={ :delay 500ms; } - -:local ExitOK false; -:do { - :local ScriptName [ :jobname ]; - - :global Identity; - - :global EitherOr; - :global FormatLine; - :global FormatMultiLines; - :global GetMacVendor; - :global LogPrint; - :global ScriptLock; - :global SendNotification2; - :global SymbolForNotification; - - :if ([ $ScriptLock $ScriptName 10 ] = false) do={ - :set ExitOK true; - :error false; - } - - :if ([ :len [ /caps-man/access-list/find where comment="--- collected above ---" disabled ] ] = 0) do={ - /caps-man/access-list/add comment="--- collected above ---" disabled=yes; - $LogPrint warning $ScriptName ("Added disabled access-list entry with comment '--- collected above ---'."); - } - :local PlaceBefore ([ /caps-man/access-list/find where comment="--- collected above ---" disabled ]->0); - - :foreach Reg in=[ /caps-man/registration-table/find ] do={ - :local RegVal; - :do { - :set RegVal [ /caps-man/registration-table/get $Reg ]; - } on-error={ - $LogPrint debug $ScriptName ("Device already gone... Ignoring."); - } - - :if ([ :len ($RegVal->"mac-address") ] > 0) do={ - :local AccessList ([ /caps-man/access-list/find where mac-address=($RegVal->"mac-address") ]->0); - :if ([ :len $AccessList ] > 0) do={ - $LogPrint debug $ScriptName ("MAC address " . $RegVal->"mac-address" . " already known: " . \ - [ /caps-man/access-list/get $AccessList comment ]); - } - - :if ([ :len $AccessList ] = 0) do={ - :local Address "no dhcp lease"; - :local DnsName "no dhcp lease"; - :local HostName "no dhcp lease"; - :local Lease ([ /ip/dhcp-server/lease/find where active-mac-address=($RegVal->"mac-address") dynamic=yes status=bound ]->0); - :if ([ :len $Lease ] > 0) do={ - :set Address [ /ip/dhcp-server/lease/get $Lease active-address ]; - :set HostName [ $EitherOr [ /ip/dhcp-server/lease/get $Lease host-name ] "no hostname" ]; - :set DnsName "no dns name"; - :local DnsRec ([ /ip/dns/static/find where address=$Address ]->0); - :if ([ :len $DnsRec ] > 0) do={ - :set DnsName ({ [ /ip/dns/static/get $DnsRec name ] }); - :foreach CName in=[ /ip/dns/static/find where type=CNAME cname=($DnsName->0) ] do={ - :set DnsName ($DnsName, [ /ip/dns/static/get $CName name ]); - } - } - } - :local DateTime ([ /system/clock/get date ] . " " . [ /system/clock/get time ]); - :local Vendor [ $GetMacVendor ($RegVal->"mac-address") ]; - :local Message ("MAC address " . $RegVal->"mac-address" . " (" . $Vendor . ", " . $HostName . ") " . \ - "first seen on " . $DateTime . " connected to SSID " . $RegVal->"ssid" . ", interface " . $RegVal->"interface"); - $LogPrint info $ScriptName $Message; - /caps-man/access-list/add place-before=$PlaceBefore comment=$Message mac-address=($RegVal->"mac-address") disabled=yes; - $SendNotification2 ({ origin=$ScriptName; \ - subject=([ $SymbolForNotification "mobile-phone" ] . $RegVal->"mac-address" . " connected to " . $RegVal->"ssid"); \ - message=("A device with unknown MAC address connected to " . $RegVal->"ssid" . " on " . $Identity . ".\n\n" . \ - [ $FormatLine "Controller" $Identity ] . "\n" . \ - [ $FormatLine "Interface" ($RegVal->"interface") ] . "\n" . \ - [ $FormatLine "SSID" ($RegVal->"ssid") ] . "\n" . \ - [ $FormatLine "MAC" ($RegVal->"mac-address") ] . "\n" . \ - [ $FormatLine "Vendor" $Vendor ] . "\n" . \ - [ $FormatLine "Hostname" $HostName ] . "\n" . \ - [ $FormatLine "Address" $Address ] . "\n" . \ - [ $FormatMultiLines "DNS name" $DnsName ] . "\n" . \ - [ $FormatLine "Date" $DateTime ]) }); - } - } else={ - $LogPrint debug $ScriptName ("No mac address available... Ignoring."); - } - } -} on-error={ - :global ExitError; $ExitError $ExitOK [ :jobname ]; -} diff --git a/collect-wireless-mac.local b/collect-wireless-mac.local new file mode 100644 index 0000000..4a4d6e1 --- /dev/null +++ b/collect-wireless-mac.local @@ -0,0 +1,62 @@ +#!rsc +# RouterOS script: collect-wireless-mac.local +# Copyright (c) 2013-2019 Christian Hesse +# +# collect wireless mac adresses in access list +# +# !! Do not edit this file, it is generated from template! + +:global Identity; + +:global GetMacVendor; +:global SendNotification; +:global ScriptLock; + +$ScriptLock "collect-wireless-mac.local"; + +:local PlaceBefore [ / interface wireless access-list find where comment="--- collected above ---" disabled ]; +:if ([ :len $PlaceBefore ] = 0) do={ + :error "Missing disabled access-list entry with comment '--- collected above ---'"; +} + +:foreach RegTbl in=[ / interface wireless registration-table find ] do={ + :local Mac [ / interface wireless registration-table get $RegTbl mac-address ]; + :local AccessList ([ / interface wireless access-list find where mac-address=$Mac ]->0); + :if ([ :len $AccessList ] = 0) do={ + :local HostName "no dhcp lease"; + :local Address "no dhcp lease"; + :local Lease [ / ip dhcp-server lease find where mac-address=$Mac ]; + :if ([ :len $Lease ] > 0) do={ + :set HostName [ / ip dhcp-server lease get $Lease host-name ]; + :set Address [ / ip dhcp-server lease get $Lease address ]; + } + :if ([ :len $HostName ] = 0) do={ + :set HostName "no hostname"; + } + :if ([ :len $Address ] = 0) do={ + :set Address "no address"; + } + :local RegEntry [ / interface wireless registration-table find where mac-address=$Mac ]; + :local Interface [ / interface wireless registration-table get $RegEntry interface ]; + :local Ssid [ / interface wireless get [ find where name=$Interface ] ssid ]; + :local DateTime ([ / system clock get date ] . " " . [ / system clock get time ]); + :local Vendor [ $GetMacVendor $Mac ]; + :local Message ("unknown MAC address " . $Mac . " (" . $Vendor . ", " . $HostName . ") " . \ + "first seen on " . $DateTime . " connected to SSID " . $Ssid . ", interface " . $Interface); + / log info $Message; + / interface wireless access-list add place-before=$PlaceBefore comment=$Message mac-address=$Mac disabled=yes; + $SendNotification ($Mac . " connected to " . $Ssid) \ + ("A device with unknown MAC address connected to " . $Ssid . " on " . $Identity . ".\n\n" . \ + "Controller: " . $Identity . "\n" . \ + "Interface: " . $Interface . "\n" . \ + "SSID: " . $Ssid . "\n" . \ + "MAC: " . $Mac . "\n" . \ + "Vendor: " . $Vendor . "\n" . \ + "Hostname: " . $HostName . "\n" . \ + "Address: " . $Address . "\n" . \ + "Date: " . $DateTime); + } else={ + :local Comment [ / interface wireless access-list get $AccessList comment ]; + :log debug ("MAC address " . $Mac . " already known: " . $Comment); + } +} diff --git a/collect-wireless-mac.local.rsc b/collect-wireless-mac.local.rsc deleted file mode 100644 index 4a38bfa..0000000 --- a/collect-wireless-mac.local.rsc +++ /dev/null @@ -1,101 +0,0 @@ -#!rsc by RouterOS -# RouterOS script: collect-wireless-mac.local -# Copyright (c) 2013-2025 Christian Hesse -# https://rsc.eworm.de/COPYING.md -# -# provides: lease-script, order=40 -# requires RouterOS, version=7.15 -# -# collect wireless mac adresses in access list -# https://rsc.eworm.de/doc/collect-wireless-mac.md -# -# !! Do not edit this file, it is generated from template! - -:global GlobalFunctionsReady; -:while ($GlobalFunctionsReady != true) do={ :delay 500ms; } - -:local ExitOK false; -:do { - :local ScriptName [ :jobname ]; - - :global Identity; - - :global EitherOr; - :global FormatLine; - :global FormatMultiLines; - :global GetMacVendor; - :global LogPrint; - :global ScriptLock; - :global SendNotification2; - :global SymbolForNotification; - - :if ([ $ScriptLock $ScriptName 10 ] = false) do={ - :set ExitOK true; - :error false; - } - - :if ([ :len [ /interface/wireless/access-list/find where comment="--- collected above ---" disabled ] ] = 0) do={ - /interface/wireless/access-list/add comment="--- collected above ---" disabled=yes; - $LogPrint warning $ScriptName ("Added disabled access-list entry with comment '--- collected above ---'."); - } - :local PlaceBefore ([ /interface/wireless/access-list/find where comment="--- collected above ---" disabled ]->0); - - :foreach Reg in=[ /interface/wireless/registration-table/find where ap=no ] do={ - :local RegVal; - :do { - :set RegVal [ /interface/wireless/registration-table/get $Reg ]; - } on-error={ - $LogPrint debug $ScriptName ("Device already gone... Ignoring."); - } - - :if ([ :len ($RegVal->"mac-address") ] > 0) do={ - :local AccessList ([ /interface/wireless/access-list/find where mac-address=($RegVal->"mac-address") ]->0); - :if ([ :len $AccessList ] > 0) do={ - $LogPrint debug $ScriptName ("MAC address " . $RegVal->"mac-address" . " already known: " . \ - [ /interface/wireless/access-list/get $AccessList comment ]); - } - - :if ([ :len $AccessList ] = 0) do={ - :local Address "no dhcp lease"; - :local DnsName "no dhcp lease"; - :local HostName "no dhcp lease"; - :local Lease ([ /ip/dhcp-server/lease/find where active-mac-address=($RegVal->"mac-address") dynamic=yes status=bound ]->0); - :if ([ :len $Lease ] > 0) do={ - :set Address [ /ip/dhcp-server/lease/get $Lease active-address ]; - :set HostName [ $EitherOr [ /ip/dhcp-server/lease/get $Lease host-name ] "no hostname" ]; - :set DnsName "no dns name"; - :local DnsRec ([ /ip/dns/static/find where address=$Address ]->0); - :if ([ :len $DnsRec ] > 0) do={ - :set DnsName ({ [ /ip/dns/static/get $DnsRec name ] }); - :foreach CName in=[ /ip/dns/static/find where type=CNAME cname=($DnsName->0) ] do={ - :set DnsName ($DnsName, [ /ip/dns/static/get $CName name ]); - } - } - } - :set ($RegVal->"ssid") [ /interface/wireless/get [ find where name=($RegVal->"interface") ] ssid ]; - :local DateTime ([ /system/clock/get date ] . " " . [ /system/clock/get time ]); - :local Vendor [ $GetMacVendor ($RegVal->"mac-address") ]; - :local Message ("MAC address " . $RegVal->"mac-address" . " (" . $Vendor . ", " . $HostName . ") " . \ - "first seen on " . $DateTime . " connected to SSID " . $RegVal->"ssid" . ", interface " . $RegVal->"interface"); - $LogPrint info $ScriptName $Message; - /interface/wireless/access-list/add place-before=$PlaceBefore comment=$Message mac-address=($RegVal->"mac-address") disabled=yes; - $SendNotification2 ({ origin=$ScriptName; \ - subject=([ $SymbolForNotification "mobile-phone" ] . $RegVal->"mac-address" . " connected to " . $RegVal->"ssid"); \ - message=("A device with unknown MAC address connected to " . $RegVal->"ssid" . " on " . $Identity . ".\n\n" . \ - [ $FormatLine "Controller" $Identity ] . "\n" . \ - [ $FormatLine "Interface" ($RegVal->"interface") ] . "\n" . \ - [ $FormatLine "SSID" ($RegVal->"ssid") ] . "\n" . \ - [ $FormatLine "MAC" ($RegVal->"mac-address") ] . "\n" . \ - [ $FormatLine "Vendor" $Vendor ] . "\n" . \ - [ $FormatLine "Hostname" $HostName ] . "\n" . \ - [ $FormatLine "Address" $Address ] . "\n" . \ - [ $FormatMultiLines "DNS name" $DnsName ] . "\n" . \ - [ $FormatLine "Date" $DateTime ]) }); - } - } else={ - $LogPrint debug $ScriptName ("No mac address available... Ignoring."); - } - } -} on-error={ - :global ExitError; $ExitError $ExitOK [ :jobname ]; -} diff --git a/collect-wireless-mac.template b/collect-wireless-mac.template new file mode 100644 index 0000000..2449316 --- /dev/null +++ b/collect-wireless-mac.template @@ -0,0 +1,64 @@ +#!rsc +# RouterOS script: collect-wireless-mac%TEMPL% +# Copyright (c) 2013-2019 Christian Hesse +# +# collect wireless mac adresses in access list +# +# !! This is just a template! Replace '%PATH%' with 'caps-man' +# !! or 'interface wireless'! + +:global Identity; + +:global GetMacVendor; +:global SendNotification; +:global ScriptLock; + +$ScriptLock "collect-wireless-mac%TEMPL%"; + +:local PlaceBefore [ / %PATH% access-list find where comment="--- collected above ---" disabled ]; +:if ([ :len $PlaceBefore ] = 0) do={ + :error "Missing disabled access-list entry with comment '--- collected above ---'"; +} + +:foreach RegTbl in=[ / %PATH% registration-table find ] do={ + :local Mac [ / %PATH% registration-table get $RegTbl mac-address ]; + :local AccessList ([ / %PATH% access-list find where mac-address=$Mac ]->0); + :if ([ :len $AccessList ] = 0) do={ + :local HostName "no dhcp lease"; + :local Address "no dhcp lease"; + :local Lease [ / ip dhcp-server lease find where mac-address=$Mac ]; + :if ([ :len $Lease ] > 0) do={ + :set HostName [ / ip dhcp-server lease get $Lease host-name ]; + :set Address [ / ip dhcp-server lease get $Lease address ]; + } + :if ([ :len $HostName ] = 0) do={ + :set HostName "no hostname"; + } + :if ([ :len $Address ] = 0) do={ + :set Address "no address"; + } + :local RegEntry [ / %PATH% registration-table find where mac-address=$Mac ]; + :local Interface [ / %PATH% registration-table get $RegEntry interface ]; + :local Ssid [ / caps-man registration-table get $RegEntry ssid ]; + :local Ssid [ / interface wireless get [ find where name=$Interface ] ssid ]; + :local DateTime ([ / system clock get date ] . " " . [ / system clock get time ]); + :local Vendor [ $GetMacVendor $Mac ]; + :local Message ("unknown MAC address " . $Mac . " (" . $Vendor . ", " . $HostName . ") " . \ + "first seen on " . $DateTime . " connected to SSID " . $Ssid . ", interface " . $Interface); + / log info $Message; + / %PATH% access-list add place-before=$PlaceBefore comment=$Message mac-address=$Mac disabled=yes; + $SendNotification ($Mac . " connected to " . $Ssid) \ + ("A device with unknown MAC address connected to " . $Ssid . " on " . $Identity . ".\n\n" . \ + "Controller: " . $Identity . "\n" . \ + "Interface: " . $Interface . "\n" . \ + "SSID: " . $Ssid . "\n" . \ + "MAC: " . $Mac . "\n" . \ + "Vendor: " . $Vendor . "\n" . \ + "Hostname: " . $HostName . "\n" . \ + "Address: " . $Address . "\n" . \ + "Date: " . $DateTime); + } else={ + :local Comment [ / %PATH% access-list get $AccessList comment ]; + :log debug ("MAC address " . $Mac . " already known: " . $Comment); + } +} diff --git a/collect-wireless-mac.template.rsc b/collect-wireless-mac.template.rsc deleted file mode 100644 index da901be..0000000 --- a/collect-wireless-mac.template.rsc +++ /dev/null @@ -1,118 +0,0 @@ -#!rsc by RouterOS -# RouterOS script: collect-wireless-mac%TEMPL% -# Copyright (c) 2013-2025 Christian Hesse -# https://rsc.eworm.de/COPYING.md -# -# provides: lease-script, order=40 -# requires RouterOS, version=7.15 -# -# collect wireless mac adresses in access list -# https://rsc.eworm.de/doc/collect-wireless-mac.md -# -# !! This is just a template to generate the real script! -# !! Pattern '%TEMPL%' is replaced, paths are filtered. - -:global GlobalFunctionsReady; -:while ($GlobalFunctionsReady != true) do={ :delay 500ms; } - -:local ExitOK false; -:do { - :local ScriptName [ :jobname ]; - - :global Identity; - - :global EitherOr; - :global FormatLine; - :global FormatMultiLines; - :global GetMacVendor; - :global LogPrint; - :global ScriptLock; - :global SendNotification2; - :global SymbolForNotification; - - :if ([ $ScriptLock $ScriptName 10 ] = false) do={ - :set ExitOK true; - :error false; - } - - :if ([ :len [ /caps-man/access-list/find where comment="--- collected above ---" disabled ] ] = 0) do={ - :if ([ :len [ /interface/wifi/access-list/find where comment="--- collected above ---" disabled ] ] = 0) do={ - :if ([ :len [ /interface/wireless/access-list/find where comment="--- collected above ---" disabled ] ] = 0) do={ - /caps-man/access-list/add comment="--- collected above ---" disabled=yes; - /interface/wifi/access-list/add comment="--- collected above ---" disabled=yes; - /interface/wireless/access-list/add comment="--- collected above ---" disabled=yes; - $LogPrint warning $ScriptName ("Added disabled access-list entry with comment '--- collected above ---'."); - } - :local PlaceBefore ([ /caps-man/access-list/find where comment="--- collected above ---" disabled ]->0); - :local PlaceBefore ([ /interface/wifi/access-list/find where comment="--- collected above ---" disabled ]->0); - :local PlaceBefore ([ /interface/wireless/access-list/find where comment="--- collected above ---" disabled ]->0); - - :foreach Reg in=[ /caps-man/registration-table/find ] do={ - :foreach Reg in=[ /interface/wifi/registration-table/find ] do={ - :foreach Reg in=[ /interface/wireless/registration-table/find where ap=no ] do={ - :local RegVal; - :do { - :set RegVal [ /caps-man/registration-table/get $Reg ]; - :set RegVal [ /interface/wifi/registration-table/get $Reg ]; - :set RegVal [ /interface/wireless/registration-table/get $Reg ]; - } on-error={ - $LogPrint debug $ScriptName ("Device already gone... Ignoring."); - } - - :if ([ :len ($RegVal->"mac-address") ] > 0) do={ - :local AccessList ([ /caps-man/access-list/find where mac-address=($RegVal->"mac-address") ]->0); - :local AccessList ([ /interface/wifi/access-list/find where mac-address=($RegVal->"mac-address") ]->0); - :local AccessList ([ /interface/wireless/access-list/find where mac-address=($RegVal->"mac-address") ]->0); - :if ([ :len $AccessList ] > 0) do={ - $LogPrint debug $ScriptName ("MAC address " . $RegVal->"mac-address" . " already known: " . \ - [ /caps-man/access-list/get $AccessList comment ]); - [ /interface/wifi/access-list/get $AccessList comment ]); - [ /interface/wireless/access-list/get $AccessList comment ]); - } - - :if ([ :len $AccessList ] = 0) do={ - :local Address "no dhcp lease"; - :local DnsName "no dhcp lease"; - :local HostName "no dhcp lease"; - :local Lease ([ /ip/dhcp-server/lease/find where active-mac-address=($RegVal->"mac-address") dynamic=yes status=bound ]->0); - :if ([ :len $Lease ] > 0) do={ - :set Address [ /ip/dhcp-server/lease/get $Lease active-address ]; - :set HostName [ $EitherOr [ /ip/dhcp-server/lease/get $Lease host-name ] "no hostname" ]; - :set DnsName "no dns name"; - :local DnsRec ([ /ip/dns/static/find where address=$Address ]->0); - :if ([ :len $DnsRec ] > 0) do={ - :set DnsName ({ [ /ip/dns/static/get $DnsRec name ] }); - :foreach CName in=[ /ip/dns/static/find where type=CNAME cname=($DnsName->0) ] do={ - :set DnsName ($DnsName, [ /ip/dns/static/get $CName name ]); - } - } - } - :set ($RegVal->"ssid") [ /interface/wireless/get [ find where name=($RegVal->"interface") ] ssid ]; - :local DateTime ([ /system/clock/get date ] . " " . [ /system/clock/get time ]); - :local Vendor [ $GetMacVendor ($RegVal->"mac-address") ]; - :local Message ("MAC address " . $RegVal->"mac-address" . " (" . $Vendor . ", " . $HostName . ") " . \ - "first seen on " . $DateTime . " connected to SSID " . $RegVal->"ssid" . ", interface " . $RegVal->"interface"); - $LogPrint info $ScriptName $Message; - /caps-man/access-list/add place-before=$PlaceBefore comment=$Message mac-address=($RegVal->"mac-address") disabled=yes; - /interface/wifi/access-list/add place-before=$PlaceBefore comment=$Message mac-address=($RegVal->"mac-address") disabled=yes; - /interface/wireless/access-list/add place-before=$PlaceBefore comment=$Message mac-address=($RegVal->"mac-address") disabled=yes; - $SendNotification2 ({ origin=$ScriptName; \ - subject=([ $SymbolForNotification "mobile-phone" ] . $RegVal->"mac-address" . " connected to " . $RegVal->"ssid"); \ - message=("A device with unknown MAC address connected to " . $RegVal->"ssid" . " on " . $Identity . ".\n\n" . \ - [ $FormatLine "Controller" $Identity ] . "\n" . \ - [ $FormatLine "Interface" ($RegVal->"interface") ] . "\n" . \ - [ $FormatLine "SSID" ($RegVal->"ssid") ] . "\n" . \ - [ $FormatLine "MAC" ($RegVal->"mac-address") ] . "\n" . \ - [ $FormatLine "Vendor" $Vendor ] . "\n" . \ - [ $FormatLine "Hostname" $HostName ] . "\n" . \ - [ $FormatLine "Address" $Address ] . "\n" . \ - [ $FormatMultiLines "DNS name" $DnsName ] . "\n" . \ - [ $FormatLine "Date" $DateTime ]) }); - } - } else={ - $LogPrint debug $ScriptName ("No mac address available... Ignoring."); - } - } -} on-error={ - :global ExitError; $ExitError $ExitOK [ :jobname ]; -} diff --git a/collect-wireless-mac.wifi.rsc b/collect-wireless-mac.wifi.rsc deleted file mode 100644 index cb217ce..0000000 --- a/collect-wireless-mac.wifi.rsc +++ /dev/null @@ -1,100 +0,0 @@ -#!rsc by RouterOS -# RouterOS script: collect-wireless-mac.wifi -# Copyright (c) 2013-2025 Christian Hesse -# https://rsc.eworm.de/COPYING.md -# -# provides: lease-script, order=40 -# requires RouterOS, version=7.15 -# -# collect wireless mac adresses in access list -# https://rsc.eworm.de/doc/collect-wireless-mac.md -# -# !! Do not edit this file, it is generated from template! - -:global GlobalFunctionsReady; -:while ($GlobalFunctionsReady != true) do={ :delay 500ms; } - -:local ExitOK false; -:do { - :local ScriptName [ :jobname ]; - - :global Identity; - - :global EitherOr; - :global FormatLine; - :global FormatMultiLines; - :global GetMacVendor; - :global LogPrint; - :global ScriptLock; - :global SendNotification2; - :global SymbolForNotification; - - :if ([ $ScriptLock $ScriptName 10 ] = false) do={ - :set ExitOK true; - :error false; - } - - :if ([ :len [ /interface/wifi/access-list/find where comment="--- collected above ---" disabled ] ] = 0) do={ - /interface/wifi/access-list/add comment="--- collected above ---" disabled=yes; - $LogPrint warning $ScriptName ("Added disabled access-list entry with comment '--- collected above ---'."); - } - :local PlaceBefore ([ /interface/wifi/access-list/find where comment="--- collected above ---" disabled ]->0); - - :foreach Reg in=[ /interface/wifi/registration-table/find ] do={ - :local RegVal; - :do { - :set RegVal [ /interface/wifi/registration-table/get $Reg ]; - } on-error={ - $LogPrint debug $ScriptName ("Device already gone... Ignoring."); - } - - :if ([ :len ($RegVal->"mac-address") ] > 0) do={ - :local AccessList ([ /interface/wifi/access-list/find where mac-address=($RegVal->"mac-address") ]->0); - :if ([ :len $AccessList ] > 0) do={ - $LogPrint debug $ScriptName ("MAC address " . $RegVal->"mac-address" . " already known: " . \ - [ /interface/wifi/access-list/get $AccessList comment ]); - } - - :if ([ :len $AccessList ] = 0) do={ - :local Address "no dhcp lease"; - :local DnsName "no dhcp lease"; - :local HostName "no dhcp lease"; - :local Lease ([ /ip/dhcp-server/lease/find where active-mac-address=($RegVal->"mac-address") dynamic=yes status=bound ]->0); - :if ([ :len $Lease ] > 0) do={ - :set Address [ /ip/dhcp-server/lease/get $Lease active-address ]; - :set HostName [ $EitherOr [ /ip/dhcp-server/lease/get $Lease host-name ] "no hostname" ]; - :set DnsName "no dns name"; - :local DnsRec ([ /ip/dns/static/find where address=$Address ]->0); - :if ([ :len $DnsRec ] > 0) do={ - :set DnsName ({ [ /ip/dns/static/get $DnsRec name ] }); - :foreach CName in=[ /ip/dns/static/find where type=CNAME cname=($DnsName->0) ] do={ - :set DnsName ($DnsName, [ /ip/dns/static/get $CName name ]); - } - } - } - :local DateTime ([ /system/clock/get date ] . " " . [ /system/clock/get time ]); - :local Vendor [ $GetMacVendor ($RegVal->"mac-address") ]; - :local Message ("MAC address " . $RegVal->"mac-address" . " (" . $Vendor . ", " . $HostName . ") " . \ - "first seen on " . $DateTime . " connected to SSID " . $RegVal->"ssid" . ", interface " . $RegVal->"interface"); - $LogPrint info $ScriptName $Message; - /interface/wifi/access-list/add place-before=$PlaceBefore comment=$Message mac-address=($RegVal->"mac-address") disabled=yes; - $SendNotification2 ({ origin=$ScriptName; \ - subject=([ $SymbolForNotification "mobile-phone" ] . $RegVal->"mac-address" . " connected to " . $RegVal->"ssid"); \ - message=("A device with unknown MAC address connected to " . $RegVal->"ssid" . " on " . $Identity . ".\n\n" . \ - [ $FormatLine "Controller" $Identity ] . "\n" . \ - [ $FormatLine "Interface" ($RegVal->"interface") ] . "\n" . \ - [ $FormatLine "SSID" ($RegVal->"ssid") ] . "\n" . \ - [ $FormatLine "MAC" ($RegVal->"mac-address") ] . "\n" . \ - [ $FormatLine "Vendor" $Vendor ] . "\n" . \ - [ $FormatLine "Hostname" $HostName ] . "\n" . \ - [ $FormatLine "Address" $Address ] . "\n" . \ - [ $FormatMultiLines "DNS name" $DnsName ] . "\n" . \ - [ $FormatLine "Date" $DateTime ]) }); - } - } else={ - $LogPrint debug $ScriptName ("No mac address available... Ignoring."); - } - } -} on-error={ - :global ExitError; $ExitError $ExitOK [ :jobname ]; -} diff --git a/contrib/logo-color.d/browser-01.avif b/contrib/logo-color.d/browser-01.avif deleted file mode 100644 index 3dc0a1f..0000000 Binary files a/contrib/logo-color.d/browser-01.avif and /dev/null differ diff --git a/contrib/logo-color.d/browser-02.avif b/contrib/logo-color.d/browser-02.avif deleted file mode 100644 index 1867fbe..0000000 Binary files a/contrib/logo-color.d/browser-02.avif and /dev/null differ diff --git a/contrib/logo-color.d/browser-03.avif b/contrib/logo-color.d/browser-03.avif deleted file mode 100644 index dc24bbb..0000000 Binary files a/contrib/logo-color.d/browser-03.avif and /dev/null differ diff --git a/contrib/logo-color.d/script.js b/contrib/logo-color.d/script.js deleted file mode 100644 index 82cc204..0000000 --- a/contrib/logo-color.d/script.js +++ /dev/null @@ -1,12 +0,0 @@ -function invertHex(hex) { - return (Number("0x1" + hex) ^ 0xffffff).toString(16).substr(1); -} - -function color() { - var svg = document.querySelector(".logo").getSVGDocument(); - svg.getElementById("dark-1").setAttribute("stop-color", document.getElementById("color1").value); - svg.getElementById("dark-2").setAttribute("stop-color", document.getElementById("color2").value); - var background = document.getElementById("color3").value; - svg.getElementById("background").setAttribute("fill", background); - svg.getElementById("hexagon").setAttribute("stroke", "#" + invertHex(background.substring(1))); -} diff --git a/contrib/logo-color.d/style.css b/contrib/logo-color.d/style.css deleted file mode 100644 index eb2ec6a..0000000 --- a/contrib/logo-color.d/style.css +++ /dev/null @@ -1,5 +0,0 @@ -body { - font-family: fira-sans, sans-serif; - font-size: 10pt; - background-color: transparent; -} diff --git a/contrib/logo-color.html b/contrib/logo-color.html deleted file mode 100644 index 17942ce..0000000 --- a/contrib/logo-color.html +++ /dev/null @@ -1,40 +0,0 @@ - - - - -RouterOS-Scripts Logo Color Changer - - - - - -RouterOS-Scripts Logo Color Changer - -You want the logo for your own notifications? But you joined the -Telegram Group and want -something that differentiates? Color it! - - - -Select the colors here: - - - - -Then right-click, click "Take Screenshot" and finally select the -logo and download it. - - - - - -(This example is with -Firefox. The workflow -for other browsers may differ.) - -See how to -Set -a profile photo for your Telegram bot. - - - diff --git a/contrib/notification.d/script.js b/contrib/notification.d/script.js deleted file mode 100644 index 91741fd..0000000 --- a/contrib/notification.d/script.js +++ /dev/null @@ -1,6 +0,0 @@ -function visible(cb, element) { - document.getElementById(element).style.display = cb.checked ? "block" : "none"; -} -function update(cb, element) { - document.getElementById(element).innerHTML = cb.value; -} diff --git a/contrib/notification.d/style.css b/contrib/notification.d/style.css deleted file mode 100644 index 648ea23..0000000 --- a/contrib/notification.d/style.css +++ /dev/null @@ -1,36 +0,0 @@ -body { - font-family: fira-sans, sans-serif; - font-size: 10pt; - background-color: transparent; -} -div.notification { - position: relative; - float: right; - width: 600px; - border: 3px outset #6c5d53; - /* border-radius: 5px; */ - padding: 10px; - background-color: #e6e6e6; -} -div.content { - padding-left: 60px; -} -img.logo { - float: left; - border-radius: 50%; -} -p.heading { - margin: 0px; - font-weight: bold; - text-decoration: underline; -} -p.hint { - display: none; -} -pre { - font-family: fira-mono, monospace; - white-space: pre-wrap; -} -span.link { - color: #863600; -} diff --git a/contrib/notification.html b/contrib/notification.html deleted file mode 100644 index 7875036..0000000 --- a/contrib/notification.html +++ /dev/null @@ -1,35 +0,0 @@ - - - - -RouterOS-Scripts Notification Generator - - - - - -RouterOS-Scripts Notification Generator - - - - - [MikroTik] ℹ️ Subject - Message - 🔗 https://eworm.de/ - ⏰ This message was queued since oct/18/2022 18:30:48 and may be obsolete. - ✂️ The message was too long and has been truncated, cut off 13%! - - - -Hostname: -Subject: -Message: Message - Show link: - Queued since - Cut-off with percent - -Then right-click, click "Take Screenshot" and finally select the -notification and download it. - - - diff --git a/daily-psk-schedule b/daily-psk-schedule new file mode 100644 index 0000000..2a01bae --- /dev/null +++ b/daily-psk-schedule @@ -0,0 +1,23 @@ +#!rsc +# RouterOS script: daily-psk-schedule +# Copyright (c) 2013-2019 Christian Hesse +# +# schedule daily-psk on startup + +:local Scheduler [ / system scheduler find where name="daily-psk-schedule" ]; + +:if ([ / system scheduler get $Scheduler interval ] = 0s) do={ + / system scheduler set interval=15s $Scheduler; +} else={ + :if ([ / tool netwatch get [ find where comment=[ / tool e-mail get address ] ] status ] != "up") do={ + :error "Mail server is not up."; + } + + :if ([ / system ntp client get status ] != "synchronized") do={ + :error "Time is not yet synchronized from ntp."; + } + + / system script run [ find where name~"daily-psk\\.(capsman|local)" ]; + + / system scheduler set interval=0s $Scheduler; +} diff --git a/daily-psk.capsman b/daily-psk.capsman new file mode 100644 index 0000000..2783afa --- /dev/null +++ b/daily-psk.capsman @@ -0,0 +1,90 @@ +#!rsc +# RouterOS script: daily-psk.capsman +# Copyright (c) 2013-2019 Christian Hesse +# Michael Gisbers +# +# update daily PSK (pre shared key) + +:global Identity; +:global DailyPskMatchComment; +:global UrlEncode; + +:global SendNotification; + +:local Seen [ :toarray "" ]; + +# return pseudo-random string for PSK +:local GeneratePSK do={ + :local Date [ :tostr $1 ]; + + :global DailyPskSecrets; + + :local Months { "jan"; "feb"; "mar"; "apr"; "may"; "jun"; + "jul"; "aug"; "sep"; "oct"; "nov"; "dec" }; + + :local Month [ :pick $Date 0 3 ]; + :local Day [ :tonum [ :pick $Date 4 6 ] ]; + :local Year [ :pick $Date 7 11 ]; + + :for MIndex from=0 to=[ :len $Months ] do={ + :if ($Months->$MIndex = $Month) do={ + :set Month ($MIndex + 1); + } + } + + :local A ((14 - $Month) / 12); + :local B ($Year - $A); + :local C ($Month + 12 * $A - 2); + :local WeekDay (7000 + $Day + $B + ($B / 4) - ($B / 100) + ($B / 400) + ((31 * $C) / 12)); + :set WeekDay ($WeekDay - (($WeekDay / 7) * 7)); + + :return (($DailyPskSecrets->0->($Day - 1)) . \ + ($DailyPskSecrets->1->($Month - 1)) . \ + ($DailyPskSecrets->2->$WeekDay)); +} + +:local Date [ / system clock get date ]; +:local NewPsk [ $GeneratePSK $Date ]; + +:foreach AccList in=[ / caps-man access-list find where comment~$DailyPskMatchComment ] do={ + :local Ssid [ / caps-man access-list get $AccList ssid-regexp ]; + :local Configuration [ / caps-man configuration get [ find where ssid=$Ssid ] name ]; + :local OldPsk [ / caps-man access-list get $AccList private-passphrase ]; + :local Skip 0; + + :if ($NewPsk != $OldPsk) do={ + :log info ("Updating daily PSK for " . $Ssid . " to " . $NewPsk . " (was " . $OldPsk . ")"); + / caps-man access-list set $AccList private-passphrase=$NewPsk; + + :if ([ / caps-man interface print count-only where configuration=$Configuration ] > 0) do={ + :foreach SeenSsid in=$Seen do={ + :if ($SeenSsid = $Ssid) do={ + :log debug ("Already sent a mail for SSID " . $Ssid . ", skipping."); + :set Skip 1; + } + } + + :if ($Skip = 0) do={ + :set Seen ($Seen, $Ssid); + + :local Url ("https://www.eworm.de/cgi-bin/cqrlogo-wifi.cgi" . \ + "?scale=8&level=1&ssid=" . [ $UrlEncode $Ssid ] . "&pass=" . [ $UrlEncode $NewPsk ]); + :local Attach "qrcode-daily.png"; + + :do { + / tool fetch mode=https check-certificate=yes-without-crl \ + $Url dst-path=$Attach; + } on-error={ + :set Attach ""; + } + + $SendNotification ("daily PSK " . $Ssid) \ + ("This is the daily PSK on " . $Identity . ":\n\n" . \ + "SSID: " . $Ssid . "\n" . \ + "PSK: " . $NewPsk . "\n" . \ + "Date: " . $Date . "\n\n" . \ + $Url) $Attach; + } + } + } +} diff --git a/daily-psk.capsman.rsc b/daily-psk.capsman.rsc deleted file mode 100644 index 5672931..0000000 --- a/daily-psk.capsman.rsc +++ /dev/null @@ -1,96 +0,0 @@ -#!rsc by RouterOS -# RouterOS script: daily-psk.capsman -# Copyright (c) 2013-2025 Christian Hesse -# Michael Gisbers -# https://rsc.eworm.de/COPYING.md -# -# requires RouterOS, version=7.15 -# -# update daily PSK (pre shared key) -# https://rsc.eworm.de/doc/daily-psk.md -# -# !! Do not edit this file, it is generated from template! - -:global GlobalFunctionsReady; -:while ($GlobalFunctionsReady != true) do={ :delay 500ms; } - -:local ExitOK false; -:do { - :local ScriptName [ :jobname ]; - - :global DailyPskMatchComment; - :global DailyPskQrCodeUrl; - :global Identity; - - :global FormatLine; - :global LogPrint; - :global ScriptLock; - :global SendNotification2; - :global SymbolForNotification; - :global UrlEncode; - :global WaitForFile; - :global WaitFullyConnected; - - :if ([ $ScriptLock $ScriptName ] = false) do={ - :set ExitOK true; - :error false; - } - $WaitFullyConnected; - - # return pseudo-random string for PSK - :local GeneratePSK do={ - :local Date [ :tostr $1 ]; - - :global DailyPskSecrets; - - :global ParseDate; - - :set Date [ $ParseDate $Date ]; - - :local A ((14 - ($Date->"month")) / 12); - :local B (($Date->"year") - $A); - :local C (($Date->"month") + 12 * $A - 2); - :local WeekDay (7000 + ($Date->"day") + $B + ($B / 4) - ($B / 100) + ($B / 400) + ((31 * $C) / 12)); - :set WeekDay ($WeekDay - (($WeekDay / 7) * 7)); - - :return (($DailyPskSecrets->0->(($Date->"day") - 1)) . \ - ($DailyPskSecrets->1->(($Date->"month") - 1)) . \ - ($DailyPskSecrets->2->$WeekDay)); - } - - :local Seen ({}); - :local Date [ /system/clock/get date ]; - :local NewPsk [ $GeneratePSK $Date ]; - - :foreach AccList in=[ /caps-man/access-list/find where comment~$DailyPskMatchComment ] do={ - :local SsidRegExp [ /caps-man/access-list/get $AccList ssid-regexp ]; - :local Configuration ([ /caps-man/configuration/find where ssid~$SsidRegExp ]->0); - :local Ssid [ /caps-man/configuration/get $Configuration ssid ]; - :local OldPsk [ /caps-man/access-list/get $AccList private-passphrase ]; - :local Skip 0; - - :if ($NewPsk != $OldPsk) do={ - $LogPrint info $ScriptName ("Updating daily PSK for '" . $Ssid . "' to '" . $NewPsk . "' (was '" . $OldPsk . "')"); - /caps-man/access-list/set $AccList private-passphrase=$NewPsk; - - :if ([ :len [ /caps-man/actual-interface-configuration/find where configuration.ssid=$Ssid !disabled ] ] > 0) do={ - :if ($Seen->$Ssid = 1) do={ - $LogPrint debug $ScriptName ("Already sent a mail for SSID " . $Ssid . ", skipping."); - } else={ - :local Link ($DailyPskQrCodeUrl . \ - "?scale=8&level=1&ssid=" . [ $UrlEncode $Ssid ] . "&pass=" . [ $UrlEncode $NewPsk ]); - $SendNotification2 ({ origin=$ScriptName; \ - subject=([ $SymbolForNotification "calendar" ] . "daily PSK " . $Ssid); \ - message=("This is the daily PSK on " . $Identity . ":\n\n" . \ - [ $FormatLine "SSID" $Ssid 8 ] . "\n" . \ - [ $FormatLine "PSK" $NewPsk 8 ] . "\n" . \ - [ $FormatLine "Date" $Date 8 ] . "\n\n" . \ - "A client device specific rule must not exist!"); link=$Link }); - :set ($Seen->$Ssid) 1; - } - } - } - } -} on-error={ - :global ExitError; $ExitError $ExitOK [ :jobname ]; -} diff --git a/daily-psk.local b/daily-psk.local new file mode 100644 index 0000000..363765f --- /dev/null +++ b/daily-psk.local @@ -0,0 +1,90 @@ +#!rsc +# RouterOS script: daily-psk.local +# Copyright (c) 2013-2019 Christian Hesse +# Michael Gisbers +# +# update daily PSK (pre shared key) + +:global Identity; +:global DailyPskMatchComment; +:global UrlEncode; + +:global SendNotification; + +:local Seen [ :toarray "" ]; + +# return pseudo-random string for PSK +:local GeneratePSK do={ + :local Date [ :tostr $1 ]; + + :global DailyPskSecrets; + + :local Months { "jan"; "feb"; "mar"; "apr"; "may"; "jun"; + "jul"; "aug"; "sep"; "oct"; "nov"; "dec" }; + + :local Month [ :pick $Date 0 3 ]; + :local Day [ :tonum [ :pick $Date 4 6 ] ]; + :local Year [ :pick $Date 7 11 ]; + + :for MIndex from=0 to=[ :len $Months ] do={ + :if ($Months->$MIndex = $Month) do={ + :set Month ($MIndex + 1); + } + } + + :local A ((14 - $Month) / 12); + :local B ($Year - $A); + :local C ($Month + 12 * $A - 2); + :local WeekDay (7000 + $Day + $B + ($B / 4) - ($B / 100) + ($B / 400) + ((31 * $C) / 12)); + :set WeekDay ($WeekDay - (($WeekDay / 7) * 7)); + + :return (($DailyPskSecrets->0->($Day - 1)) . \ + ($DailyPskSecrets->1->($Month - 1)) . \ + ($DailyPskSecrets->2->$WeekDay)); +} + +:local Date [ / system clock get date ]; +:local NewPsk [ $GeneratePSK $Date ]; + +:foreach AccList in=[ / interface wireless access-list find where comment~$DailyPskMatchComment ] do={ + :local IntName [ / interface wireless access-list get $AccList interface ]; + :local Ssid [ / interface wireless get $IntName ssid ]; + :local OldPsk [ / interface wireless access-list get $AccList private-pre-shared-key ]; + :local Skip 0; + + :if ($NewPsk != $OldPsk) do={ + :log info ("Updating daily PSK for " . $Ssid . " to " . $NewPsk . " (was " . $OldPsk . ")"); + / interface wireless access-list set $AccList private-pre-shared-key=$NewPsk; + + :if ([ / interface wireless print count-only where name=$IntName disabled=no ] = 1) do={ + :foreach SeenSsid in=$Seen do={ + :if ($SeenSsid = $Ssid) do={ + :log debug ("Already sent a mail for SSID " . $Ssid . ", skipping."); + :set Skip 1; + } + } + + :if ($Skip = 0) do={ + :set Seen ($Seen, $Ssid); + + :local Url ("https://www.eworm.de/cgi-bin/cqrlogo-wifi.cgi" . \ + "?scale=8&level=1&ssid=" . [ $UrlEncode $Ssid ] . "&pass=" . [ $UrlEncode $NewPsk ]); + :local Attach "qrcode-daily.png"; + + :do { + / tool fetch mode=https check-certificate=yes-without-crl \ + $Url dst-path=$Attach; + } on-error={ + :set Attach ""; + } + + $SendNotification ("daily PSK " . $Ssid) \ + ("This is the daily PSK on " . $Identity . ":\n\n" . \ + "SSID: " . $Ssid . "\n" . \ + "PSK: " . $NewPsk . "\n" . \ + "Date: " . $Date . "\n\n" . \ + $Url) $Attach; + } + } + } +} diff --git a/daily-psk.local.rsc b/daily-psk.local.rsc deleted file mode 100644 index 9dea469..0000000 --- a/daily-psk.local.rsc +++ /dev/null @@ -1,95 +0,0 @@ -#!rsc by RouterOS -# RouterOS script: daily-psk.local -# Copyright (c) 2013-2025 Christian Hesse -# Michael Gisbers -# https://rsc.eworm.de/COPYING.md -# -# requires RouterOS, version=7.15 -# -# update daily PSK (pre shared key) -# https://rsc.eworm.de/doc/daily-psk.md -# -# !! Do not edit this file, it is generated from template! - -:global GlobalFunctionsReady; -:while ($GlobalFunctionsReady != true) do={ :delay 500ms; } - -:local ExitOK false; -:do { - :local ScriptName [ :jobname ]; - - :global DailyPskMatchComment; - :global DailyPskQrCodeUrl; - :global Identity; - - :global FormatLine; - :global LogPrint; - :global ScriptLock; - :global SendNotification2; - :global SymbolForNotification; - :global UrlEncode; - :global WaitForFile; - :global WaitFullyConnected; - - :if ([ $ScriptLock $ScriptName ] = false) do={ - :set ExitOK true; - :error false; - } - $WaitFullyConnected; - - # return pseudo-random string for PSK - :local GeneratePSK do={ - :local Date [ :tostr $1 ]; - - :global DailyPskSecrets; - - :global ParseDate; - - :set Date [ $ParseDate $Date ]; - - :local A ((14 - ($Date->"month")) / 12); - :local B (($Date->"year") - $A); - :local C (($Date->"month") + 12 * $A - 2); - :local WeekDay (7000 + ($Date->"day") + $B + ($B / 4) - ($B / 100) + ($B / 400) + ((31 * $C) / 12)); - :set WeekDay ($WeekDay - (($WeekDay / 7) * 7)); - - :return (($DailyPskSecrets->0->(($Date->"day") - 1)) . \ - ($DailyPskSecrets->1->(($Date->"month") - 1)) . \ - ($DailyPskSecrets->2->$WeekDay)); - } - - :local Seen ({}); - :local Date [ /system/clock/get date ]; - :local NewPsk [ $GeneratePSK $Date ]; - - :foreach AccList in=[ /interface/wireless/access-list/find where comment~$DailyPskMatchComment ] do={ - :local IntName [ /interface/wireless/access-list/get $AccList interface ]; - :local Ssid [ /interface/wireless/get $IntName ssid ]; - :local OldPsk [ /interface/wireless/access-list/get $AccList private-pre-shared-key ]; - :local Skip 0; - - :if ($NewPsk != $OldPsk) do={ - $LogPrint info $ScriptName ("Updating daily PSK for '" . $Ssid . "' to '" . $NewPsk . "' (was '" . $OldPsk . "')"); - /interface/wireless/access-list/set $AccList private-pre-shared-key=$NewPsk; - - :if ([ :len [ /interface/wireless/find where name=$IntName !disabled ] ] = 1) do={ - :if ($Seen->$Ssid = 1) do={ - $LogPrint debug $ScriptName ("Already sent a mail for SSID " . $Ssid . ", skipping."); - } else={ - :local Link ($DailyPskQrCodeUrl . \ - "?scale=8&level=1&ssid=" . [ $UrlEncode $Ssid ] . "&pass=" . [ $UrlEncode $NewPsk ]); - $SendNotification2 ({ origin=$ScriptName; \ - subject=([ $SymbolForNotification "calendar" ] . "daily PSK " . $Ssid); \ - message=("This is the daily PSK on " . $Identity . ":\n\n" . \ - [ $FormatLine "SSID" $Ssid 8 ] . "\n" . \ - [ $FormatLine "PSK" $NewPsk 8 ] . "\n" . \ - [ $FormatLine "Date" $Date 8 ] . "\n\n" . \ - "A client device specific rule must not exist!"); link=$Link }); - :set ($Seen->$Ssid) 1; - } - } - } - } -} on-error={ - :global ExitError; $ExitError $ExitOK [ :jobname ]; -} diff --git a/daily-psk.template b/daily-psk.template new file mode 100644 index 0000000..a9d65f2 --- /dev/null +++ b/daily-psk.template @@ -0,0 +1,96 @@ +#!rsc +# RouterOS script: daily-psk%TEMPL% +# Copyright (c) 2013-2019 Christian Hesse +# Michael Gisbers +# +# update daily PSK (pre shared key) + +:global Identity; +:global DailyPskMatchComment; +:global UrlEncode; + +:global SendNotification; + +:local Seen [ :toarray "" ]; + +# return pseudo-random string for PSK +:local GeneratePSK do={ + :local Date [ :tostr $1 ]; + + :global DailyPskSecrets; + + :local Months { "jan"; "feb"; "mar"; "apr"; "may"; "jun"; + "jul"; "aug"; "sep"; "oct"; "nov"; "dec" }; + + :local Month [ :pick $Date 0 3 ]; + :local Day [ :tonum [ :pick $Date 4 6 ] ]; + :local Year [ :pick $Date 7 11 ]; + + :for MIndex from=0 to=[ :len $Months ] do={ + :if ($Months->$MIndex = $Month) do={ + :set Month ($MIndex + 1); + } + } + + :local A ((14 - $Month) / 12); + :local B ($Year - $A); + :local C ($Month + 12 * $A - 2); + :local WeekDay (7000 + $Day + $B + ($B / 4) - ($B / 100) + ($B / 400) + ((31 * $C) / 12)); + :set WeekDay ($WeekDay - (($WeekDay / 7) * 7)); + + :return (($DailyPskSecrets->0->($Day - 1)) . \ + ($DailyPskSecrets->1->($Month - 1)) . \ + ($DailyPskSecrets->2->$WeekDay)); +} + +:local Date [ / system clock get date ]; +:local NewPsk [ $GeneratePSK $Date ]; + +:foreach AccList in=[ / interface wireless access-list find where comment~$DailyPskMatchComment ] do={ +:foreach AccList in=[ / caps-man access-list find where comment~$DailyPskMatchComment ] do={ + :local IntName [ / interface wireless access-list get $AccList interface ]; + :local Ssid [ / interface wireless get $IntName ssid ]; + :local Ssid [ / caps-man access-list get $AccList ssid-regexp ]; + :local Configuration [ / caps-man configuration get [ find where ssid=$Ssid ] name ]; + :local OldPsk [ / interface wireless access-list get $AccList private-pre-shared-key ]; + :local OldPsk [ / caps-man access-list get $AccList private-passphrase ]; + :local Skip 0; + + :if ($NewPsk != $OldPsk) do={ + :log info ("Updating daily PSK for " . $Ssid . " to " . $NewPsk . " (was " . $OldPsk . ")"); + / interface wireless access-list set $AccList private-pre-shared-key=$NewPsk; + / caps-man access-list set $AccList private-passphrase=$NewPsk; + + :if ([ / interface wireless print count-only where name=$IntName disabled=no ] = 1) do={ + :if ([ / caps-man interface print count-only where configuration=$Configuration ] > 0) do={ + :foreach SeenSsid in=$Seen do={ + :if ($SeenSsid = $Ssid) do={ + :log debug ("Already sent a mail for SSID " . $Ssid . ", skipping."); + :set Skip 1; + } + } + + :if ($Skip = 0) do={ + :set Seen ($Seen, $Ssid); + + :local Url ("https://www.eworm.de/cgi-bin/cqrlogo-wifi.cgi" . \ + "?scale=8&level=1&ssid=" . [ $UrlEncode $Ssid ] . "&pass=" . [ $UrlEncode $NewPsk ]); + :local Attach "qrcode-daily.png"; + + :do { + / tool fetch mode=https check-certificate=yes-without-crl \ + $Url dst-path=$Attach; + } on-error={ + :set Attach ""; + } + + $SendNotification ("daily PSK " . $Ssid) \ + ("This is the daily PSK on " . $Identity . ":\n\n" . \ + "SSID: " . $Ssid . "\n" . \ + "PSK: " . $NewPsk . "\n" . \ + "Date: " . $Date . "\n\n" . \ + $Url) $Attach; + } + } + } +} diff --git a/daily-psk.template.rsc b/daily-psk.template.rsc deleted file mode 100644 index 8202eeb..0000000 --- a/daily-psk.template.rsc +++ /dev/null @@ -1,111 +0,0 @@ -#!rsc by RouterOS -# RouterOS script: daily-psk%TEMPL% -# Copyright (c) 2013-2025 Christian Hesse -# Michael Gisbers -# https://rsc.eworm.de/COPYING.md -# -# requires RouterOS, version=7.15 -# -# update daily PSK (pre shared key) -# https://rsc.eworm.de/doc/daily-psk.md -# -# !! This is just a template to generate the real script! -# !! Pattern '%TEMPL%' is replaced, paths are filtered. - -:global GlobalFunctionsReady; -:while ($GlobalFunctionsReady != true) do={ :delay 500ms; } - -:local ExitOK false; -:do { - :local ScriptName [ :jobname ]; - - :global DailyPskMatchComment; - :global DailyPskQrCodeUrl; - :global Identity; - - :global FormatLine; - :global LogPrint; - :global ScriptLock; - :global SendNotification2; - :global SymbolForNotification; - :global UrlEncode; - :global WaitForFile; - :global WaitFullyConnected; - - :if ([ $ScriptLock $ScriptName ] = false) do={ - :set ExitOK true; - :error false; - } - $WaitFullyConnected; - - # return pseudo-random string for PSK - :local GeneratePSK do={ - :local Date [ :tostr $1 ]; - - :global DailyPskSecrets; - - :global ParseDate; - - :set Date [ $ParseDate $Date ]; - - :local A ((14 - ($Date->"month")) / 12); - :local B (($Date->"year") - $A); - :local C (($Date->"month") + 12 * $A - 2); - :local WeekDay (7000 + ($Date->"day") + $B + ($B / 4) - ($B / 100) + ($B / 400) + ((31 * $C) / 12)); - :set WeekDay ($WeekDay - (($WeekDay / 7) * 7)); - - :return (($DailyPskSecrets->0->(($Date->"day") - 1)) . \ - ($DailyPskSecrets->1->(($Date->"month") - 1)) . \ - ($DailyPskSecrets->2->$WeekDay)); - } - - :local Seen ({}); - :local Date [ /system/clock/get date ]; - :local NewPsk [ $GeneratePSK $Date ]; - - :foreach AccList in=[ /caps-man/access-list/find where comment~$DailyPskMatchComment ] do={ - :foreach AccList in=[ /interface/wifi/access-list/find where comment~$DailyPskMatchComment ] do={ - :foreach AccList in=[ /interface/wireless/access-list/find where comment~$DailyPskMatchComment ] do={ - :local SsidRegExp [ /caps-man/access-list/get $AccList ssid-regexp ]; - :local SsidRegExp [ /interface/wifi/access-list/get $AccList ssid-regexp ]; - :local Configuration ([ /caps-man/configuration/find where ssid~$SsidRegExp ]->0); - :local Configuration ([ /interface/wifi/configuration/find where ssid~$SsidRegExp ]->0); - :local Ssid [ /caps-man/configuration/get $Configuration ssid ]; - :local Ssid [ /interface/wifi/configuration/get $Configuration ssid ]; - :local OldPsk [ /caps-man/access-list/get $AccList private-passphrase ]; - :local OldPsk [ /interface/wifi/access-list/get $AccList passphrase ]; - # /caps-man/ /interface/wifi/ above - /interface/wireless/ below - :local IntName [ /interface/wireless/access-list/get $AccList interface ]; - :local Ssid [ /interface/wireless/get $IntName ssid ]; - :local OldPsk [ /interface/wireless/access-list/get $AccList private-pre-shared-key ]; - :local Skip 0; - - :if ($NewPsk != $OldPsk) do={ - $LogPrint info $ScriptName ("Updating daily PSK for '" . $Ssid . "' to '" . $NewPsk . "' (was '" . $OldPsk . "')"); - /caps-man/access-list/set $AccList private-passphrase=$NewPsk; - /interface/wifi/access-list/set $AccList passphrase=$NewPsk; - /interface/wireless/access-list/set $AccList private-pre-shared-key=$NewPsk; - - :if ([ :len [ /caps-man/actual-interface-configuration/find where configuration.ssid=$Ssid !disabled ] ] > 0) do={ - :if ([ :len [ /interface/wifi/find where configuration.ssid=$Ssid !disabled ] ] > 0) do={ - :if ([ :len [ /interface/wireless/find where name=$IntName !disabled ] ] = 1) do={ - :if ($Seen->$Ssid = 1) do={ - $LogPrint debug $ScriptName ("Already sent a mail for SSID " . $Ssid . ", skipping."); - } else={ - :local Link ($DailyPskQrCodeUrl . \ - "?scale=8&level=1&ssid=" . [ $UrlEncode $Ssid ] . "&pass=" . [ $UrlEncode $NewPsk ]); - $SendNotification2 ({ origin=$ScriptName; \ - subject=([ $SymbolForNotification "calendar" ] . "daily PSK " . $Ssid); \ - message=("This is the daily PSK on " . $Identity . ":\n\n" . \ - [ $FormatLine "SSID" $Ssid 8 ] . "\n" . \ - [ $FormatLine "PSK" $NewPsk 8 ] . "\n" . \ - [ $FormatLine "Date" $Date 8 ] . "\n\n" . \ - "A client device specific rule must not exist!"); link=$Link }); - :set ($Seen->$Ssid) 1; - } - } - } - } -} on-error={ - :global ExitError; $ExitError $ExitOK [ :jobname ]; -} diff --git a/daily-psk.wifi.rsc b/daily-psk.wifi.rsc deleted file mode 100644 index 3de3c5b..0000000 --- a/daily-psk.wifi.rsc +++ /dev/null @@ -1,96 +0,0 @@ -#!rsc by RouterOS -# RouterOS script: daily-psk.wifi -# Copyright (c) 2013-2025 Christian Hesse -# Michael Gisbers -# https://rsc.eworm.de/COPYING.md -# -# requires RouterOS, version=7.15 -# -# update daily PSK (pre shared key) -# https://rsc.eworm.de/doc/daily-psk.md -# -# !! Do not edit this file, it is generated from template! - -:global GlobalFunctionsReady; -:while ($GlobalFunctionsReady != true) do={ :delay 500ms; } - -:local ExitOK false; -:do { - :local ScriptName [ :jobname ]; - - :global DailyPskMatchComment; - :global DailyPskQrCodeUrl; - :global Identity; - - :global FormatLine; - :global LogPrint; - :global ScriptLock; - :global SendNotification2; - :global SymbolForNotification; - :global UrlEncode; - :global WaitForFile; - :global WaitFullyConnected; - - :if ([ $ScriptLock $ScriptName ] = false) do={ - :set ExitOK true; - :error false; - } - $WaitFullyConnected; - - # return pseudo-random string for PSK - :local GeneratePSK do={ - :local Date [ :tostr $1 ]; - - :global DailyPskSecrets; - - :global ParseDate; - - :set Date [ $ParseDate $Date ]; - - :local A ((14 - ($Date->"month")) / 12); - :local B (($Date->"year") - $A); - :local C (($Date->"month") + 12 * $A - 2); - :local WeekDay (7000 + ($Date->"day") + $B + ($B / 4) - ($B / 100) + ($B / 400) + ((31 * $C) / 12)); - :set WeekDay ($WeekDay - (($WeekDay / 7) * 7)); - - :return (($DailyPskSecrets->0->(($Date->"day") - 1)) . \ - ($DailyPskSecrets->1->(($Date->"month") - 1)) . \ - ($DailyPskSecrets->2->$WeekDay)); - } - - :local Seen ({}); - :local Date [ /system/clock/get date ]; - :local NewPsk [ $GeneratePSK $Date ]; - - :foreach AccList in=[ /interface/wifi/access-list/find where comment~$DailyPskMatchComment ] do={ - :local SsidRegExp [ /interface/wifi/access-list/get $AccList ssid-regexp ]; - :local Configuration ([ /interface/wifi/configuration/find where ssid~$SsidRegExp ]->0); - :local Ssid [ /interface/wifi/configuration/get $Configuration ssid ]; - :local OldPsk [ /interface/wifi/access-list/get $AccList passphrase ]; - :local Skip 0; - - :if ($NewPsk != $OldPsk) do={ - $LogPrint info $ScriptName ("Updating daily PSK for '" . $Ssid . "' to '" . $NewPsk . "' (was '" . $OldPsk . "')"); - /interface/wifi/access-list/set $AccList passphrase=$NewPsk; - - :if ([ :len [ /interface/wifi/find where configuration.ssid=$Ssid !disabled ] ] > 0) do={ - :if ($Seen->$Ssid = 1) do={ - $LogPrint debug $ScriptName ("Already sent a mail for SSID " . $Ssid . ", skipping."); - } else={ - :local Link ($DailyPskQrCodeUrl . \ - "?scale=8&level=1&ssid=" . [ $UrlEncode $Ssid ] . "&pass=" . [ $UrlEncode $NewPsk ]); - $SendNotification2 ({ origin=$ScriptName; \ - subject=([ $SymbolForNotification "calendar" ] . "daily PSK " . $Ssid); \ - message=("This is the daily PSK on " . $Identity . ":\n\n" . \ - [ $FormatLine "SSID" $Ssid 8 ] . "\n" . \ - [ $FormatLine "PSK" $NewPsk 8 ] . "\n" . \ - [ $FormatLine "Date" $Date 8 ] . "\n\n" . \ - "A client device specific rule must not exist!"); link=$Link }); - :set ($Seen->$Ssid) 1; - } - } - } - } -} on-error={ - :global ExitError; $ExitError $ExitOK [ :jobname ]; -} diff --git a/dhcp-lease-comment.capsman b/dhcp-lease-comment.capsman new file mode 100644 index 0000000..eda04e4 --- /dev/null +++ b/dhcp-lease-comment.capsman @@ -0,0 +1,21 @@ +#!rsc +# RouterOS script: dhcp-lease-comment.capsman +# Copyright (c) 2013-2019 Christian Hesse +# +# update dhcp-server lease comment with infos from access-list +# +# !! Do not edit this file, it is generated from template! + +:foreach Lease in=[ / ip dhcp-server lease find where dynamic=yes ] do={ + :local MacAddress [ / ip dhcp-server lease get $Lease mac-address ]; + :local OldComment [ / ip dhcp-server lease get $Lease comment ]; + :local NewComment; + :local AccessList ([ / caps-man access-list find where mac-address=$MacAddress ]->0); + :if ([ :len $AccessList ] > 0) do={ + :set NewComment [ / caps-man access-list get $AccessList comment ]; + } + :if ([ :len $NewComment ] != 0 && $OldComment != $NewComment) do={ + :log info ("Updating comment for DHCP lease " . $MacAddress . ": " . $NewComment); + / ip dhcp-server lease set comment=$NewComment $Lease; + } +} diff --git a/dhcp-lease-comment.capsman.rsc b/dhcp-lease-comment.capsman.rsc deleted file mode 100644 index 36b31c8..0000000 --- a/dhcp-lease-comment.capsman.rsc +++ /dev/null @@ -1,43 +0,0 @@ -#!rsc by RouterOS -# RouterOS script: dhcp-lease-comment.capsman -# Copyright (c) 2013-2025 Christian Hesse -# https://rsc.eworm.de/COPYING.md -# -# provides: lease-script, order=60 -# requires RouterOS, version=7.15 -# -# update dhcp-server lease comment with infos from access-list -# https://rsc.eworm.de/doc/dhcp-lease-comment.md -# -# !! Do not edit this file, it is generated from template! - -:global GlobalFunctionsReady; -:while ($GlobalFunctionsReady != true) do={ :delay 500ms; } - -:local ExitOK false; -:do { - :local ScriptName [ :jobname ]; - - :global LogPrint; - :global ScriptLock; - - :if ([ $ScriptLock $ScriptName ] = false) do={ - :set ExitOK true; - :error false; - } - - :foreach Lease in=[ /ip/dhcp-server/lease/find where dynamic=yes status=bound ] do={ - :local LeaseVal [ /ip/dhcp-server/lease/get $Lease ]; - :local NewComment; - :local AccessList ([ /caps-man/access-list/find where mac-address=($LeaseVal->"active-mac-address") ]->0); - :if ([ :len $AccessList ] > 0) do={ - :set NewComment [ /caps-man/access-list/get $AccessList comment ]; - } - :if ([ :len $NewComment ] != 0 && $LeaseVal->"comment" != $NewComment) do={ - $LogPrint info $ScriptName ("Updating comment for DHCP lease " . $LeaseVal->"active-mac-address" . ": " . $NewComment); - /ip/dhcp-server/lease/set comment=$NewComment $Lease; - } - } -} on-error={ - :global ExitError; $ExitError $ExitOK [ :jobname ]; -} diff --git a/dhcp-lease-comment.local b/dhcp-lease-comment.local new file mode 100644 index 0000000..9e2f9d8 --- /dev/null +++ b/dhcp-lease-comment.local @@ -0,0 +1,21 @@ +#!rsc +# RouterOS script: dhcp-lease-comment.local +# Copyright (c) 2013-2019 Christian Hesse +# +# update dhcp-server lease comment with infos from access-list +# +# !! Do not edit this file, it is generated from template! + +:foreach Lease in=[ / ip dhcp-server lease find where dynamic=yes ] do={ + :local MacAddress [ / ip dhcp-server lease get $Lease mac-address ]; + :local OldComment [ / ip dhcp-server lease get $Lease comment ]; + :local NewComment; + :local AccessList ([ / interface wireless access-list find where mac-address=$MacAddress ]->0); + :if ([ :len $AccessList ] > 0) do={ + :set NewComment [ / interface wireless access-list get $AccessList comment ]; + } + :if ([ :len $NewComment ] != 0 && $OldComment != $NewComment) do={ + :log info ("Updating comment for DHCP lease " . $MacAddress . ": " . $NewComment); + / ip dhcp-server lease set comment=$NewComment $Lease; + } +} diff --git a/dhcp-lease-comment.local.rsc b/dhcp-lease-comment.local.rsc deleted file mode 100644 index 35dc6f6..0000000 --- a/dhcp-lease-comment.local.rsc +++ /dev/null @@ -1,43 +0,0 @@ -#!rsc by RouterOS -# RouterOS script: dhcp-lease-comment.local -# Copyright (c) 2013-2025 Christian Hesse -# https://rsc.eworm.de/COPYING.md -# -# provides: lease-script, order=60 -# requires RouterOS, version=7.15 -# -# update dhcp-server lease comment with infos from access-list -# https://rsc.eworm.de/doc/dhcp-lease-comment.md -# -# !! Do not edit this file, it is generated from template! - -:global GlobalFunctionsReady; -:while ($GlobalFunctionsReady != true) do={ :delay 500ms; } - -:local ExitOK false; -:do { - :local ScriptName [ :jobname ]; - - :global LogPrint; - :global ScriptLock; - - :if ([ $ScriptLock $ScriptName ] = false) do={ - :set ExitOK true; - :error false; - } - - :foreach Lease in=[ /ip/dhcp-server/lease/find where dynamic=yes status=bound ] do={ - :local LeaseVal [ /ip/dhcp-server/lease/get $Lease ]; - :local NewComment; - :local AccessList ([ /interface/wireless/access-list/find where mac-address=($LeaseVal->"active-mac-address") ]->0); - :if ([ :len $AccessList ] > 0) do={ - :set NewComment [ /interface/wireless/access-list/get $AccessList comment ]; - } - :if ([ :len $NewComment ] != 0 && $LeaseVal->"comment" != $NewComment) do={ - $LogPrint info $ScriptName ("Updating comment for DHCP lease " . $LeaseVal->"active-mac-address" . ": " . $NewComment); - /ip/dhcp-server/lease/set comment=$NewComment $Lease; - } - } -} on-error={ - :global ExitError; $ExitError $ExitOK [ :jobname ]; -} diff --git a/dhcp-lease-comment.template b/dhcp-lease-comment.template new file mode 100644 index 0000000..a1541e1 --- /dev/null +++ b/dhcp-lease-comment.template @@ -0,0 +1,22 @@ +#!rsc +# RouterOS script: dhcp-lease-comment%TEMPL% +# Copyright (c) 2013-2019 Christian Hesse +# +# update dhcp-server lease comment with infos from access-list +# +# !! This is just a template! Replace '%PATH%' with 'caps-man' +# !! or 'interface wireless'! + +:foreach Lease in=[ / ip dhcp-server lease find where dynamic=yes ] do={ + :local MacAddress [ / ip dhcp-server lease get $Lease mac-address ]; + :local OldComment [ / ip dhcp-server lease get $Lease comment ]; + :local NewComment; + :local AccessList ([ / %PATH% access-list find where mac-address=$MacAddress ]->0); + :if ([ :len $AccessList ] > 0) do={ + :set NewComment [ / %PATH% access-list get $AccessList comment ]; + } + :if ([ :len $NewComment ] != 0 && $OldComment != $NewComment) do={ + :log info ("Updating comment for DHCP lease " . $MacAddress . ": " . $NewComment); + / ip dhcp-server lease set comment=$NewComment $Lease; + } +} diff --git a/dhcp-lease-comment.template.rsc b/dhcp-lease-comment.template.rsc deleted file mode 100644 index 47a8554..0000000 --- a/dhcp-lease-comment.template.rsc +++ /dev/null @@ -1,48 +0,0 @@ -#!rsc by RouterOS -# RouterOS script: dhcp-lease-comment%TEMPL% -# Copyright (c) 2013-2025 Christian Hesse -# https://rsc.eworm.de/COPYING.md -# -# provides: lease-script, order=60 -# requires RouterOS, version=7.15 -# -# update dhcp-server lease comment with infos from access-list -# https://rsc.eworm.de/doc/dhcp-lease-comment.md -# -# !! This is just a template to generate the real script! -# !! Pattern '%TEMPL%' is replaced, paths are filtered. - -:global GlobalFunctionsReady; -:while ($GlobalFunctionsReady != true) do={ :delay 500ms; } - -:local ExitOK false; -:do { - :local ScriptName [ :jobname ]; - - :global LogPrint; - :global ScriptLock; - - :if ([ $ScriptLock $ScriptName ] = false) do={ - :set ExitOK true; - :error false; - } - - :foreach Lease in=[ /ip/dhcp-server/lease/find where dynamic=yes status=bound ] do={ - :local LeaseVal [ /ip/dhcp-server/lease/get $Lease ]; - :local NewComment; - :local AccessList ([ /caps-man/access-list/find where mac-address=($LeaseVal->"active-mac-address") ]->0); - :local AccessList ([ /interface/wifi/access-list/find where mac-address=($LeaseVal->"active-mac-address") ]->0); - :local AccessList ([ /interface/wireless/access-list/find where mac-address=($LeaseVal->"active-mac-address") ]->0); - :if ([ :len $AccessList ] > 0) do={ - :set NewComment [ /caps-man/access-list/get $AccessList comment ]; - :set NewComment [ /interface/wifi/access-list/get $AccessList comment ]; - :set NewComment [ /interface/wireless/access-list/get $AccessList comment ]; - } - :if ([ :len $NewComment ] != 0 && $LeaseVal->"comment" != $NewComment) do={ - $LogPrint info $ScriptName ("Updating comment for DHCP lease " . $LeaseVal->"active-mac-address" . ": " . $NewComment); - /ip/dhcp-server/lease/set comment=$NewComment $Lease; - } - } -} on-error={ - :global ExitError; $ExitError $ExitOK [ :jobname ]; -} diff --git a/dhcp-lease-comment.wifi.rsc b/dhcp-lease-comment.wifi.rsc deleted file mode 100644 index e0f9785..0000000 --- a/dhcp-lease-comment.wifi.rsc +++ /dev/null @@ -1,43 +0,0 @@ -#!rsc by RouterOS -# RouterOS script: dhcp-lease-comment.wifi -# Copyright (c) 2013-2025 Christian Hesse -# https://rsc.eworm.de/COPYING.md -# -# provides: lease-script, order=60 -# requires RouterOS, version=7.15 -# -# update dhcp-server lease comment with infos from access-list -# https://rsc.eworm.de/doc/dhcp-lease-comment.md -# -# !! Do not edit this file, it is generated from template! - -:global GlobalFunctionsReady; -:while ($GlobalFunctionsReady != true) do={ :delay 500ms; } - -:local ExitOK false; -:do { - :local ScriptName [ :jobname ]; - - :global LogPrint; - :global ScriptLock; - - :if ([ $ScriptLock $ScriptName ] = false) do={ - :set ExitOK true; - :error false; - } - - :foreach Lease in=[ /ip/dhcp-server/lease/find where dynamic=yes status=bound ] do={ - :local LeaseVal [ /ip/dhcp-server/lease/get $Lease ]; - :local NewComment; - :local AccessList ([ /interface/wifi/access-list/find where mac-address=($LeaseVal->"active-mac-address") ]->0); - :if ([ :len $AccessList ] > 0) do={ - :set NewComment [ /interface/wifi/access-list/get $AccessList comment ]; - } - :if ([ :len $NewComment ] != 0 && $LeaseVal->"comment" != $NewComment) do={ - $LogPrint info $ScriptName ("Updating comment for DHCP lease " . $LeaseVal->"active-mac-address" . ": " . $NewComment); - /ip/dhcp-server/lease/set comment=$NewComment $Lease; - } - } -} on-error={ - :global ExitError; $ExitError $ExitOK [ :jobname ]; -} diff --git a/dhcp-to-dns b/dhcp-to-dns new file mode 100644 index 0000000..e965faf --- /dev/null +++ b/dhcp-to-dns @@ -0,0 +1,68 @@ +#!rsc +# RouterOS script: dhcp-to-dns +# Copyright (c) 2013-2019 Christian Hesse +# +# check DHCP leases and add/remove/update DNS entries + +:global CharacterReplace; + +:global Identity; +:global Domain; +:global HostNameInZone; + +:local Zone; +:if ($HostNameInZone = true) do={ + :set Zone ("dhcp." . $Identity . "." . $Domain); +} else={ + :set Zone ("dhcp." . $Domain); +} +:local Ttl 5m; +:local CommentPrefix "managed by dhcp-to-dns for "; + +:foreach Static in=[ / ip dns static find where comment ~ $CommentPrefix ] do={ + :local MacAddress [ $CharacterReplace [ / ip dns static get $Static comment ] $CommentPrefix "" ]; + :local IpAddress [ / ip dns static get $Static address ]; + :local HostName [ / ip dns static get $Static name ]; + :if ([ / ip dhcp-server lease print count-only where mac-address=$MacAddress address=$IpAddress dynamic=yes ] > 0) do={ + :log debug ("Lease for " . $MacAddress . " (" . $HostName . ") still exists. Not deleting DNS entry."); + } else={ + :local Found false; + :log info ("Lease expired for " . $MacAddress . " (" . $HostName . "), deleting DNS entry."); + / ip dns static remove $Static; + } +} + +:foreach Lease in=[ / ip dhcp-server lease find where dynamic=yes ] do={ + :local Mac [ / ip dhcp-server lease get $Lease mac-address ]; + :local DhcpIp [ / ip dhcp-server lease get $Lease address ]; + :local Comment ($CommentPrefix . $Mac); + :local HostName [ $CharacterReplace [ / ip dhcp-server lease get $Lease host-name ] " " "" ]; + :if ($HostName = "") do={ + :set HostName [ $CharacterReplace [ / ip dhcp-server lease get $Lease mac-address ] ":" "-" ]; + } + + :local Fqdn ($HostName . "." . $Zone); + :local DnsNode [ / ip dns static find where name=$Fqdn ]; + :if ([ :len $DnsNode ] > 0) do={ + :local DnsIp [ / ip dns static get $DnsNode address ]; + :local Leases [ / ip dhcp-server lease find where host-name=$HostName dynamic=yes ]; + :local HostNameCount [ / ip dhcp-server lease print count-only where host-name=$HostName dynamic=yes ]; + :if ($HostNameCount > 1) do={ + :foreach J,Lease in=$Leases do={ + :if ($J + 1 = $HostNameCount) do={ + :set DhcpIp [ / ip dhcp-server lease get $Lease address ]; + } + } + } + + :if ($DnsIp = $DhcpIp) do={ + :log debug ("DNS entry for " . $Fqdn . " does not need updating."); + } else={ + :log info ("Replacing DNS entry for " . $Fqdn . ", new address is " . $DhcpIp . "."); + / ip dns static set name=$Fqdn address=$DhcpIp ttl=$Ttl comment=$Comment $DnsNode; + } + } else={ + :log info ("Adding new DNS entry for " . $Fqdn . ", address is " . $DhcpIp . "."); + / ip dns static add name=$Fqdn address=$DhcpIp ttl=$Ttl comment=$Comment; + } +} diff --git a/dhcp-to-dns.rsc b/dhcp-to-dns.rsc deleted file mode 100644 index 9b94098..0000000 --- a/dhcp-to-dns.rsc +++ /dev/null @@ -1,130 +0,0 @@ -#!rsc by RouterOS -# RouterOS script: dhcp-to-dns -# Copyright (c) 2013-2025 Christian Hesse -# https://rsc.eworm.de/COPYING.md -# -# provides: lease-script, order=20 -# requires RouterOS, version=7.16 -# -# check DHCP leases and add/remove/update DNS entries -# https://rsc.eworm.de/doc/dhcp-to-dns.md - -:global GlobalFunctionsReady; -:while ($GlobalFunctionsReady != true) do={ :delay 500ms; } - -:local ExitOK false; -:do { - :local ScriptName [ :jobname ]; - - :global Domain; - :global Identity; - - :global CleanName; - :global EitherOr; - :global IfThenElse; - :global LogPrint; - :global LogPrintOnce; - :global ParseKeyValueStore; - :global ScriptLock; - - :if ([ $ScriptLock $ScriptName 10 ] = false) do={ - :set ExitOK true; - :error false; - } - - :local Ttl 5m; - :local CommentPrefix ("managed by " . $ScriptName); - :local CommentString ("--- " . $ScriptName . " above ---"); - - :if ([ :len [ /ip/dns/static/find where (name=$CommentString or (comment=$CommentString and name=-)) type=NXDOMAIN disabled ] ] = 0) do={ - /ip/dns/static/add name=$CommentString type=NXDOMAIN disabled=yes; - $LogPrint warning $ScriptName ("Added disabled static dns record with name '" . $CommentString . "'."); - } - :local PlaceBefore ([ /ip/dns/static/find where (name=$CommentString or (comment=$CommentString and name=-)) type=NXDOMAIN disabled ]->0); - - :foreach DnsRecord in=[ /ip/dns/static/find where comment~("^" . $CommentPrefix . "\\b") type=A ] do={ - :local DnsRecordVal [ /ip/dns/static/get $DnsRecord ]; - :local DnsRecordInfo [ $ParseKeyValueStore ($DnsRecordVal->"comment") ]; - :local MacInServer ($DnsRecordInfo->"macaddress" . " in " . $DnsRecordInfo->"server"); - - :if ([ :len [ /ip/dhcp-server/lease/find where active-mac-address=($DnsRecordInfo->"macaddress") \ - active-address=($DnsRecordVal->"address") server=($DnsRecordInfo->"server") status=bound ] ] > 0) do={ - $LogPrint debug $ScriptName ("Lease for " . $MacInServer . " (" . $DnsRecordVal->"name" . ") still exists. Not deleting record."); - } else={ - :local Found false; - $LogPrint info $ScriptName ("Lease expired for " . $MacInServer . ", deleting record (" . $DnsRecordVal->"name" . ")."); - /ip/dns/static/remove $DnsRecord; - /ip/dns/static/remove [ find where type=CNAME comment=($DnsRecordVal->"comment") ]; - } - } - - :foreach Lease in=[ /ip/dhcp-server/lease/find where status=bound ] do={ - :local LeaseVal; - :do { - :set LeaseVal [ /ip/dhcp-server/lease/get $Lease ]; - :if ([ :len [ /ip/dhcp-server/lease/find where active-mac-address=($LeaseVal->"active-mac-address") status=bound ] ] > 1) do={ - $LogPrintOnce info $ScriptName ("Multiple bound leases found for mac-address " . ($LeaseVal->"active-mac-address") . "!"); - } - } on-error={ - $LogPrint debug $ScriptName ("A lease just vanished, ignoring."); - } - - :if ([ :len ($LeaseVal->"active-address") ] > 0) do={ - :local Comment ($CommentPrefix . ", macaddress=" . $LeaseVal->"active-mac-address" . ", server=" . $LeaseVal->"server"); - :local MacDash [ $CleanName ($LeaseVal->"active-mac-address") ]; - :local HostName [ $CleanName [ $EitherOr ([ $ParseKeyValueStore ($LeaseVal->"comment") ]->"hostname") ($LeaseVal->"host-name") ] ]; - :local Network [ /ip/dhcp-server/network/find where ($LeaseVal->"active-address") in address ]; - :local NetworkVal; - :if ([ :len $Network ] > 0) do={ - :set NetworkVal [ /ip/dhcp-server/network/get ($Network->0) ]; - } - :local NetworkInfo [ $ParseKeyValueStore ($NetworkVal->"comment") ]; - :local NetDomain ([ $IfThenElse ([ :len ($NetworkInfo->"name-extra") ] > 0) ($NetworkInfo->"name-extra" . ".") ] . \ - [ $EitherOr [ $EitherOr ($NetworkInfo->"domain") ($NetworkVal->"domain") ] $Domain ]); - :local FullA ($MacDash . "." . $NetDomain); - :local FullCN ($HostName . "." . $NetDomain); - :local MacInServer ($LeaseVal->"active-mac-address" . " in " . $LeaseVal->"server"); - - :local DnsRecord [ /ip/dns/static/find where comment=$Comment type=A ]; - :if ([ :len $DnsRecord ] > 0) do={ - :local DnsRecordVal [ /ip/dns/static/get $DnsRecord ]; - - :if ($DnsRecordVal->"address" = $LeaseVal->"active-address" && $DnsRecordVal->"name" = $FullA) do={ - $LogPrint debug $ScriptName ("The A record for " . $MacInServer . " (" . $FullA . ") does not need updating."); - } else={ - $LogPrint info $ScriptName ("Updating A record for " . $MacInServer . " (" . $FullA . " -> " . $LeaseVal->"active-address" . ")."); - /ip/dns/static/set address=($LeaseVal->"active-address") name=$FullA $DnsRecord; - } - - :local CName [ /ip/dns/static/find where comment=$Comment type=CNAME ]; - :if ([ :len $CName ] > 0) do={ - :local CNameVal [ /ip/dns/static/get $CName ]; - :if ($CNameVal->"name" != $FullCN || $CNameVal->"cname" != $FullA) do={ - $LogPrint info $ScriptName ("Deleting CNAME record with wrong data for " . $MacInServer . "."); - /ip/dns/static/remove $CName; - } - } - :if ([ :len $HostName ] > 0 && [ :len [ /ip/dns/static/find where name=$FullCN type=CNAME ] ] = 0) do={ - $LogPrint info $ScriptName ("Adding CNAME record for " . $MacInServer . " (" . $FullCN . " -> " . $FullA . ")."); - /ip/dns/static/add name=$FullCN type=CNAME cname=$FullA ttl=$Ttl comment=$Comment place-before=$PlaceBefore; - } - - } else={ - $LogPrint info $ScriptName ("Adding A record for " . $MacInServer . " (" . $FullA . " -> " . $LeaseVal->"active-address" . ")."); - /ip/dns/static/add name=$FullA type=A address=($LeaseVal->"active-address") ttl=$Ttl comment=$Comment place-before=$PlaceBefore; - :if ([ :len $HostName ] > 0 && [ :len [ /ip/dns/static/find where name=$FullCN type=CNAME ] ] = 0) do={ - $LogPrint info $ScriptName ("Adding CNAME record for " . $MacInServer . " (" . $FullCN . " -> " . $FullA . ")."); - /ip/dns/static/add name=$FullCN type=CNAME cname=$FullA ttl=$Ttl comment=$Comment place-before=$PlaceBefore; - } - } - - :if ([ :len [ /ip/dns/static/find where name=$FullA type=A ] ] > 1) do={ - $LogPrintOnce warning $ScriptName ("The name '" . $FullA . "' appeared in more than one A record!"); - } - } else={ - $LogPrint debug $ScriptName ("No address available... Ignoring."); - } - } -} on-error={ - :global ExitError; $ExitError $ExitOK [ :jobname ]; -} diff --git a/doc/accesslist-duplicates.d/01-example.avif b/doc/accesslist-duplicates.d/01-example.avif deleted file mode 100644 index 11b3fc5..0000000 Binary files a/doc/accesslist-duplicates.d/01-example.avif and /dev/null differ diff --git a/doc/accesslist-duplicates.md b/doc/accesslist-duplicates.md deleted file mode 100644 index e4d0c7f..0000000 --- a/doc/accesslist-duplicates.md +++ /dev/null @@ -1,57 +0,0 @@ -Find and remove access list duplicates -====================================== - -[](https://github.com/eworm-de/routeros-scripts/stargazers) -[](https://github.com/eworm-de/routeros-scripts/network) -[](https://github.com/eworm-de/routeros-scripts/watchers) -[](https://mikrotik.com/download/changelogs/) -[](https://t.me/routeros_scripts) -[](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J) - -[⬅️ Go back to main README](../README.md) - -> ℹ️️ **Info**: This script can not be used on its own but requires the base -> installation. See [main README](../README.md) for details. - -Description ------------ - -This script is supposed to run interactively to find and remove duplicate -entries in wireless access list. - -Requirements and installation ------------------------------ - -Depending on whether you use `wifi` package (`/interface/wifi`), legacy -wifi with CAPsMAN (`/caps-man`) or local wireless interface -(`/interface/wireless`) you need to install a different script. - -For `wifi`: - - $ScriptInstallUpdate accesslist-duplicates.wifi; - -For legacy CAPsMAN: - - $ScriptInstallUpdate accesslist-duplicates.capsman; - -For legacy local interface: - - $ScriptInstallUpdate accesslist-duplicates.local; - -Usage and invocation --------------------- - -Run this script from a terminal: - - /system/script/run accesslist-duplicates.wifi; - - - -See also --------- - -* [Collect MAC addresses in wireless access list](collect-wireless-mac.md) - ---- -[⬅️ Go back to main README](../README.md) -[⬆️ Go back to top](#top) diff --git a/doc/backup-cloud.d/notification.avif b/doc/backup-cloud.d/notification.avif deleted file mode 100644 index e533908..0000000 Binary files a/doc/backup-cloud.d/notification.avif and /dev/null differ diff --git a/doc/backup-cloud.md b/doc/backup-cloud.md deleted file mode 100644 index 7286960..0000000 --- a/doc/backup-cloud.md +++ /dev/null @@ -1,76 +0,0 @@ -Upload backup to Mikrotik cloud -=============================== - -[](https://github.com/eworm-de/routeros-scripts/stargazers) -[](https://github.com/eworm-de/routeros-scripts/network) -[](https://github.com/eworm-de/routeros-scripts/watchers) -[](https://mikrotik.com/download/changelogs/) -[](https://t.me/routeros_scripts) -[](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J) - -[⬅️ Go back to main README](../README.md) - -> ℹ️ **Info**: This script can not be used on its own but requires the base -> installation. See [main README](../README.md) for details. - -Description ------------ - -This script uploads -[binary backup to Mikrotik cloud](https://wiki.mikrotik.com/wiki/Manual:IP/Cloud#Backup). - -> ⚠️ **Warning**: The used command can hit errors that a script can with -> workaround only. A notification *should* be sent anyway. But it can result -> in malfunction of fetch command (where all up- and downloads break) for -> some time. Failed notifications are queued then. - -### Sample notification - - - -Requirements and installation ------------------------------ - -Just install the script: - - $ScriptInstallUpdate backup-cloud; - -Configuration -------------- - -The configuration goes to `global-config-overlay`, these are the parameters: - -* `BackupPassword`: password to encrypt the backup with -* `BackupRandomDelay`: delay up to amount of seconds when run from scheduler - -> ℹ️ **Info**: Copy relevant configuration from -> [`global-config`](../global-config.rsc) (the one without `-overlay`) to -> your local `global-config-overlay` and modify it to your specific needs. - -Also notification settings are required for -[e-mail](mod/notification-email.md), -[matrix](mod/notification-matrix.md), -[ntfy](mod/notification-ntfy.md) and/or -[telegram](mod/notification-telegram.md). - -Usage and invocation --------------------- - -Just run the script: - - /system/script/run backup-cloud; - -Creating a scheduler may be an option: - - /system/scheduler/add interval=1w name=backup-cloud on-event="/system/script/run backup-cloud;" start-time=09:20:00; - -See also --------- - -* [Send backup via e-mail](backup-email.md) -* [Save configuration to fallback partition](backup-partition.md) -* [Upload backup to server](backup-upload.md) - ---- -[⬅️ Go back to main README](../README.md) -[⬆️ Go back to top](#top) diff --git a/doc/backup-email.md b/doc/backup-email.md deleted file mode 100644 index 7b8bcfe..0000000 --- a/doc/backup-email.md +++ /dev/null @@ -1,68 +0,0 @@ -Send backup via e-mail -====================== - -[](https://github.com/eworm-de/routeros-scripts/stargazers) -[](https://github.com/eworm-de/routeros-scripts/network) -[](https://github.com/eworm-de/routeros-scripts/watchers) -[](https://mikrotik.com/download/changelogs/) -[](https://t.me/routeros_scripts) -[](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J) - -[⬅️ Go back to main README](../README.md) - -> ℹ️ **Info**: This script can not be used on its own but requires the base -> installation. See [main README](../README.md) for details. - -Description ------------ - -This script sends binary backup (`/system/backup/save`) and complete -configuration export (`/export terse show-sensitive`) via e-mail. - -Requirements and installation ------------------------------ - -Just install the script and the required module: - - $ScriptInstallUpdate mod/notification-email,backup-email; - -Also make sure you configure -[sending notifications via e-mail](mod/notification-email.md). - -Configuration -------------- - -The configuration goes to `global-config-overlay`, these are the parameters: - -* `BackupSendBinary`: whether to send binary backup -* `BackupSendExport`: whether to send configuration export -* `BackupSendGlobalConfig`: whether to send `global-config-overlay` -* `BackupPassword`: password to encrypt the backup with -* `BackupRandomDelay`: delay up to amount of seconds when run from scheduler - -> ℹ️ **Info**: Copy relevant configuration from -> [`global-config`](../global-config.rsc) (the one without `-overlay`) to -> your local `global-config-overlay` and modify it to your specific needs. - -Usage and invocation --------------------- - -Just run the script: - - /system/script/run backup-email; - -Creating a scheduler may be an option: - - /system/scheduler/add interval=1w name=backup-email on-event="/system/script/run backup-email;" start-time=09:15:00; - -See also --------- - -* [Upload backup to Mikrotik cloud](backup-cloud.md) -* [Save configuration to fallback partition](backup-partition.md) -* [Send notifications via e-mail](mod/notification-email.md) -* [Upload backup to server](backup-upload.md) - ---- -[⬅️ Go back to main README](../README.md) -[⬆️ Go back to top](#top) diff --git a/doc/backup-partition.md b/doc/backup-partition.md deleted file mode 100644 index 9d615a5..0000000 --- a/doc/backup-partition.md +++ /dev/null @@ -1,78 +0,0 @@ -Save configuration to fallback partition -======================================== - -[](https://github.com/eworm-de/routeros-scripts/stargazers) -[](https://github.com/eworm-de/routeros-scripts/network) -[](https://github.com/eworm-de/routeros-scripts/watchers) -[](https://mikrotik.com/download/changelogs/) -[](https://t.me/routeros_scripts) -[](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J) - -[⬅️ Go back to main README](../README.md) - -> ℹ️ **Info**: This script can not be used on its own but requires the base -> installation. See [main README](../README.md) for details. - -Description ------------ - -This script saves the current configuration to fallback -[partition](https://wiki.mikrotik.com/wiki/Manual:Partitions). -It can also copy-over the RouterOS installation when run interactively -or just before a feature update. - -For this to work you need a device with sufficient flash storage that is -properly partitioned. - -To make you aware of a possible issue a scheduler logging a warning is -added in the backup partition's configuration. You may want to use -[log-forward](log-forward.md) to be notified. - -> ⚠️ **Warning**: By default only the configuration is saved to backup -> partition. Every now and then you should copy your installation over -> for a recent RouterOS version! See below for options. - -Requirements and installation ------------------------------ - -Just install the script: - - $ScriptInstallUpdate backup-partition; - -Configuration -------------- - -The configuration goes to `global-config-overlay`, the only parameter is: - -* `BackupPartitionCopyBeforeFeatureUpdate`: copy-over the RouterOS - installation when a feature update is pending - -> ℹ️ **Info**: Copy relevant configuration from -> [`global-config`](../global-config.rsc) (the one without `-overlay`) to -> your local `global-config-overlay` and modify it to your specific needs. - -Usage and invocation --------------------- - -Just run the script: - - /system/script/run backup-partition; - -When run interactively from terminal it supports to copy-over the RouterOS -installation when versions differ. - -Creating a scheduler may be an option: - - /system/scheduler/add interval=1w name=backup-partition on-event="/system/script/run backup-partition;" start-time=09:30:00; - -See also --------- - -* [Upload backup to Mikrotik cloud](backup-cloud.md) -* [Send backup via e-mail](backup-email.md) -* [Upload backup to server](backup-upload.md) -* [Forward log messages via notification](log-forward.md) - ---- -[⬅️ Go back to main README](../README.md) -[⬆️ Go back to top](#top) diff --git a/doc/backup-upload.d/notification.avif b/doc/backup-upload.d/notification.avif deleted file mode 100644 index 83cfb18..0000000 Binary files a/doc/backup-upload.d/notification.avif and /dev/null differ diff --git a/doc/backup-upload.md b/doc/backup-upload.md deleted file mode 100644 index 6a5b0e4..0000000 --- a/doc/backup-upload.md +++ /dev/null @@ -1,92 +0,0 @@ -Upload backup to server -======================= - -[](https://github.com/eworm-de/routeros-scripts/stargazers) -[](https://github.com/eworm-de/routeros-scripts/network) -[](https://github.com/eworm-de/routeros-scripts/watchers) -[](https://mikrotik.com/download/changelogs/) -[](https://t.me/routeros_scripts) -[](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J) - -[⬅️ Go back to main README](../README.md) - -> ℹ️ **Info**: This script can not be used on its own but requires the base -> installation. See [main README](../README.md) for details. - -Description ------------ - -This script uploads binary backup (`/system/backup/save`) and complete -configuration export (`/export terse show-sensitive`) to external server. - -> ⚠️ **Warning**: The used command can hit errors that a script can not handle. -> This may result in script termination (where no notification is sent) or -> malfunction of fetch command (where all up- and downloads break) for some -> time. Failed notifications are queued then. - -### Sample notification - - - -Requirements and installation ------------------------------ - -Just install the script: - - $ScriptInstallUpdate backup-upload; - -Configuration -------------- - -The configuration goes to `global-config-overlay`, these are the parameters: - -* `BackupSendBinary`: whether to send binary backup -* `BackupSendExport`: whether to send configuration export -* `BackupSendGlobalConfig`: whether to send `global-config-overlay` -* `BackupPassword`: password to encrypt the backup with -* `BackupRandomDelay`: delay up to amount of seconds when run from scheduler -* `BackupUploadUrl`: url to upload to -* `BackupUploadUser`: username for server authentication -* `BackupUploadPass`: password for server authentication - -> ℹ️ **Info**: Copy relevant configuration from -> [`global-config`](../global-config.rsc) (the one without `-overlay`) to -> your local `global-config-overlay` and modify it to your specific needs. - -Also notification settings are required for -[e-mail](mod/notification-email.md), -[matrix](mod/notification-matrix.md), -[ntfy](mod/notification-ntfy.md) and/or -[telegram](mod/notification-telegram.md). - -### Issues with SFTP client - -The RouterOS SFTP client is picky if it comes to authentication methods. -I had to disable all but password authentication on server side. For openssh -edit `/etc/ssh/sshd_config` and add a directive like this, changed for your -needs: - - Match User mikrotik - AuthenticationMethods password - -Usage and invocation --------------------- - -Just run the script: - - /system/script/run backup-upload; - -Creating a scheduler may be an option: - - /system/scheduler/add interval=1w name=backup-upload on-event="/system/script/run backup-upload;" start-time=09:25:00; - -See also --------- - -* [Upload backup to Mikrotik cloud](backup-cloud.md) -* [Send backup via e-mail](backup-email.md) -* [Save configuration to fallback partition](backup-partition.md) - ---- -[⬅️ Go back to main README](../README.md) -[⬆️ Go back to top](#top) diff --git a/doc/capsman-download-packages.md b/doc/capsman-download-packages.md deleted file mode 100644 index 5722227..0000000 --- a/doc/capsman-download-packages.md +++ /dev/null @@ -1,83 +0,0 @@ -Download packages for CAP upgrade from CAPsMAN -============================================= - -[](https://github.com/eworm-de/routeros-scripts/stargazers) -[](https://github.com/eworm-de/routeros-scripts/network) -[](https://github.com/eworm-de/routeros-scripts/watchers) -[](https://mikrotik.com/download/changelogs/) -[](https://t.me/routeros_scripts) -[](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J) - -[⬅️ Go back to main README](../README.md) - -> ℹ️ **Info**: This script can not be used on its own but requires the base -> installation. See [main README](../README.md) for details. - -Description ------------ - -CAPsMAN can upgrate CAP devices. If CAPsMAN device and CAP device(s) are -differnet architecture you need to store packages for CAP device's -architecture on local storage. - -This script automatically downloads these packages. - -Requirements and installation ------------------------------ - -Make sure you have the `package-path` set in your CAPsMAN configuration, -as that is where packages are downloaded to and where the system expects -them. - -Then just install the script on CAPsMAN device. -Depending on whether you use `wifi` package (`/interface/wifi`) or legacy -wifi with CAPsMAN (`/caps-man`) you need to install a different script. - -For `wifi`: - - $ScriptInstallUpdate capsman-download-packages.wifi; - -For legacy CAPsMAN: - - $ScriptInstallUpdate capsman-download-packages.capsman; - -Optionally add a scheduler to run after startup. For `wifi`: - - /system/scheduler/add name=capsman-download-packages on-event="/system/script/run capsman-download-packages.wifi;" start-time=startup; - -For legacy CAPsMAN: - - /system/scheduler/add name=capsman-download-packages on-event="/system/script/run capsman-download-packages.capsman;" start-time=startup; - -Packages available in local storage in older version are downloaded -unconditionally. - -If no packages are found the script downloads a default set of packages: - - * `wifi`: `routeros` and `wifi-qcom` for *arm* and *arm64*, `wifi-qcom-ac` for *arm* - * legacy CAPsMAN: `routeros` and `wireless` for *arm* and *mipsbe* - -> ℹ️ **Info**: If you have packages in the directory and things go wrong for -> what ever unknown reason: Remove **all** packages and start over. - -Usage and invocation --------------------- - -Run the script manually: - - /system/script/run capsman-download-packages.wifi; - -... or from scheduler. - -After package download all out-of-date CAP devices are upgraded automatically. -For a rolling upgrade install extra script -[capsman-rolling-upgrade](capsman-rolling-upgrade.md). - -See also --------- - -* [Run rolling CAP upgrades from CAPsMAN](capsman-rolling-upgrade.md) - ---- -[⬅️ Go back to main README](../README.md) -[⬆️ Go back to top](#top) diff --git a/doc/capsman-rolling-upgrade.md b/doc/capsman-rolling-upgrade.md deleted file mode 100644 index d277db6..0000000 --- a/doc/capsman-rolling-upgrade.md +++ /dev/null @@ -1,60 +0,0 @@ -Run rolling CAP upgrades from CAPsMAN -===================================== - -[](https://github.com/eworm-de/routeros-scripts/stargazers) -[](https://github.com/eworm-de/routeros-scripts/network) -[](https://github.com/eworm-de/routeros-scripts/watchers) -[](https://mikrotik.com/download/changelogs/) -[](https://t.me/routeros_scripts) -[](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J) - -[⬅️ Go back to main README](../README.md) - -> ℹ️ **Info**: This script can not be used on its own but requires the base -> installation. See [main README](../README.md) for details. - -Description ------------ - -CAPsMAN can upgrade CAP devices. This script runs a rolling upgrade for -out-of-date CAP devices. The idea is to have just a fraction of devices -reboot at a time, having the others to serve wireless connectivity. - -Note that the script does not wait for the CAPs to reconnect, it just defers -the upgrade commands. The more CAPs you have the more will upgrade in -parallel. - -Requirements and installation ------------------------------ - -Just install the script on CAPsMAN device. -Depending on whether you use `wifi` package (`/interface/wifi`) or legacy -wifi with CAPsMAN (`/caps-man`) you need to install a different script. - -For `wifi`: - - $ScriptInstallUpdate capsman-rolling-upgrade.wifi; - -For legacy CAPsMAN: - - $ScriptInstallUpdate capsman-rolling-upgrade.capsman; - -Usage and invocation --------------------- - -This script is intended as an add-on to -[capsman-download-packages](capsman-download-packages.md), being invoked by -that script when required. - -Alternatively run it manually: - - /system/script/run capsman-rolling-upgrade.wifi; - -See also --------- - -* [Download packages for CAP upgrade from CAPsMAN](capsman-download-packages.md) - ---- -[⬅️ Go back to main README](../README.md) -[⬆️ Go back to top](#top) diff --git a/doc/certificate-renew-issued.md b/doc/certificate-renew-issued.md deleted file mode 100644 index c4615b5..0000000 --- a/doc/certificate-renew-issued.md +++ /dev/null @@ -1,61 +0,0 @@ -Renew locally issued certificates -================================= - -[](https://github.com/eworm-de/routeros-scripts/stargazers) -[](https://github.com/eworm-de/routeros-scripts/network) -[](https://github.com/eworm-de/routeros-scripts/watchers) -[](https://mikrotik.com/download/changelogs/) -[](https://t.me/routeros_scripts) -[](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J) - -[⬅️ Go back to main README](../README.md) - -> ℹ️ **Info**: This script can not be used on its own but requires the base -> installation. See [main README](../README.md) for details. - -Description ------------ - -This script renews certificates issued by a local certificate authority (CA). -Optionally the certificates are exported with individual passphrases for -easy pick-up. - -Requirements and installation ------------------------------ - -Just install the script: - - $ScriptInstallUpdate certificate-renew-issued; - -Configuration -------------- - -The configuration goes to `global-config-overlay`, there is just one -parameter: - -* `CertRenewPass`: an array holding individual passphrases for certificates - -> ℹ️ **Info**: Copy relevant configuration from -> [`global-config`](../global-config.rsc) (the one without `-overlay`) to -> your local `global-config-overlay` and modify it to your specific needs. - -Usage and invocation --------------------- - -Run the script to renew certificates issued from a local CA. - - /system/script/run certificate-renew-issued; - -Only scripts with a remaining lifetime of three weeks or less are renewed. -The old certificate is revoked automatically. If a passphrase for a specific -certificate is given in `CertRenewPass` the certificate is exported and -PKCS#12 file (`cert-issued/CN.p12`) can be found on device's storage. - -See also --------- - -* [Renew certificates and notify on expiration](check-certificates.md) - ---- -[⬅️ Go back to main README](../README.md) -[⬆️ Go back to top](#top) diff --git a/doc/check-certificates.d/notification.avif b/doc/check-certificates.d/notification.avif deleted file mode 100644 index 7c250da..0000000 Binary files a/doc/check-certificates.d/notification.avif and /dev/null differ diff --git a/doc/check-certificates.md b/doc/check-certificates.md deleted file mode 100644 index 4c144ba..0000000 --- a/doc/check-certificates.md +++ /dev/null @@ -1,97 +0,0 @@ -Renew certificates and notify on expiration -=========================================== - -[](https://github.com/eworm-de/routeros-scripts/stargazers) -[](https://github.com/eworm-de/routeros-scripts/network) -[](https://github.com/eworm-de/routeros-scripts/watchers) -[](https://mikrotik.com/download/changelogs/) -[](https://t.me/routeros_scripts) -[](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J) - -[⬅️ Go back to main README](../README.md) - -> ℹ️ **Info**: This script can not be used on its own but requires the base -> installation. See [main README](../README.md) for details. - -Description ------------ - -This script tries to download and renew certificates, then notifies about -certificates that are still about to expire. - -### Sample notification - - - -Requirements and installation ------------------------------ - -Just install the script: - - $ScriptInstallUpdate check-certificates; - -Configuration -------------- - -For automatic download and renewal of certificates you need configuration -in `global-config-overlay`, these are the parameters: - -* `CertRenewPass`: an array of passphrases to try -* `CertRenewTime`: on what remaining time to try a renew -* `CertRenewUrl`: the url to download certificates from -* `CertWarnTime`: on what remaining time to warn via notification - -> ℹ️ **Info**: Copy relevant configuration from -> [`global-config`](../global-config.rsc) (the one without `-overlay`) to -> your local `global-config-overlay` and modify it to your specific needs. - -Certificates on the web server should be named by their common name, like -`CN.pem` (`PEM` format) or`CN.p12` (`PKCS#12` format). Alternatively any -subject alternative name (aka *Subject Alt Name* or *SAN*) can be used. - -Also notification settings are required for -[e-mail](mod/notification-email.md), -[matrix](mod/notification-matrix.md), -[ntfy](mod/notification-ntfy.md) and/or -[telegram](mod/notification-telegram.md). - -Usage and invocation --------------------- - -Just run the script: - - /system/script/run check-certificates; - -... or create a scheduler for periodic execution: - - /system/scheduler/add interval=1d name=check-certificates on-event="/system/script/run check-certificates;" start-time=startup; - - -Tips & Tricks -------------- - -### Schedule at startup - -The script checks for full connectivity before acting, so scheduling at -startup is perfectly valid: - - /system/scheduler/add name=check-certificates@startup on-event="/system/script/run check-certificates;" start-time=startup; - -### Initial import - -Given you have a certificate on you server, you can use `check-certificates` -for the initial import. Just create a *dummy* certificate with short lifetime -that matches criteria to be renewed: - - /certificate/add name=example.com common-name=example.com days-valid=1; - /certificate/sign example.com; - /system/script/run check-certificates; - -See also --------- - -* [Renew locally issued certificates](certificate-renew-issued.md) - ---- -[⬅️ Go back to main README](../README.md) -[⬆️ Go back to top](#top) diff --git a/doc/check-health.d/notification-01-cpu-utilization-high.avif b/doc/check-health.d/notification-01-cpu-utilization-high.avif deleted file mode 100644 index 326e7fe..0000000 Binary files a/doc/check-health.d/notification-01-cpu-utilization-high.avif and /dev/null differ diff --git a/doc/check-health.d/notification-02-cpu-utilization-ok.avif b/doc/check-health.d/notification-02-cpu-utilization-ok.avif deleted file mode 100644 index 811ccd7..0000000 Binary files a/doc/check-health.d/notification-02-cpu-utilization-ok.avif and /dev/null differ diff --git a/doc/check-health.d/notification-03-ram-utilization-high.avif b/doc/check-health.d/notification-03-ram-utilization-high.avif deleted file mode 100644 index 59155c5..0000000 Binary files a/doc/check-health.d/notification-03-ram-utilization-high.avif and /dev/null differ diff --git a/doc/check-health.d/notification-04-ram-utilization-ok.avif b/doc/check-health.d/notification-04-ram-utilization-ok.avif deleted file mode 100644 index d995b9a..0000000 Binary files a/doc/check-health.d/notification-04-ram-utilization-ok.avif and /dev/null differ diff --git a/doc/check-health.d/notification-05-voltage.avif b/doc/check-health.d/notification-05-voltage.avif deleted file mode 100644 index 17a385b..0000000 Binary files a/doc/check-health.d/notification-05-voltage.avif and /dev/null differ diff --git a/doc/check-health.d/notification-06-temperature-high.avif b/doc/check-health.d/notification-06-temperature-high.avif deleted file mode 100644 index 60d3802..0000000 Binary files a/doc/check-health.d/notification-06-temperature-high.avif and /dev/null differ diff --git a/doc/check-health.d/notification-07-temperature-ok.avif b/doc/check-health.d/notification-07-temperature-ok.avif deleted file mode 100644 index 4afed02..0000000 Binary files a/doc/check-health.d/notification-07-temperature-ok.avif and /dev/null differ diff --git a/doc/check-health.d/notification-08-state-fail.avif b/doc/check-health.d/notification-08-state-fail.avif deleted file mode 100644 index ad049ac..0000000 Binary files a/doc/check-health.d/notification-08-state-fail.avif and /dev/null differ diff --git a/doc/check-health.d/notification-09-state-ok.avif b/doc/check-health.d/notification-09-state-ok.avif deleted file mode 100644 index 26f5a74..0000000 Binary files a/doc/check-health.d/notification-09-state-ok.avif and /dev/null differ diff --git a/doc/check-health.md b/doc/check-health.md deleted file mode 100644 index 7cf0c33..0000000 --- a/doc/check-health.md +++ /dev/null @@ -1,122 +0,0 @@ -Notify about health state -========================= - -[](https://github.com/eworm-de/routeros-scripts/stargazers) -[](https://github.com/eworm-de/routeros-scripts/network) -[](https://github.com/eworm-de/routeros-scripts/watchers) -[](https://mikrotik.com/download/changelogs/) -[](https://t.me/routeros_scripts) -[](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J) - -[⬅️ Go back to main README](../README.md) - -> ℹ️ **Info**: This script can not be used on its own but requires the base -> installation. See [main README](../README.md) for details. - -Description ------------ - -This script is run from scheduler periodically, sending notification on -health related events. Monitoring CPU and RAM utilization (available -processing and memory resources) works on all devices: - -* high CPU utilization -* high RAM utilization (low available RAM) - -With additional plugins functionality can be extended, depending on -sensors available in hardware: - -* voltage jumps up or down more than configured threshold -* voltage drops below hard lower limit -* fan failed or recovered -* power supply failed or recovered -* temperature is above or below threshold - -> ⚠️ **Warning**: Note that bad initial state will not trigger an event! For -> example rebooting a device that is already too hot will not trigger an -> alert on high temperature. - -### Sample notifications - -#### CPU utilization - - - - -#### RAM utilization (low available RAM) - - - - -#### Voltage - - - -#### Temperature - - - - -#### PSU state - - - - -Requirements and installation ------------------------------ - -Just install the script and create a scheduler: - - $ScriptInstallUpdate check-health; - /system/scheduler/add interval=53s name=check-health on-event="/system/script/run check-health;" start-time=startup; - -> ℹ️ **Info**: Running lots of scripts simultaneously can tamper the -> precision of cpu utilization, escpecially on devices with limited -> resources. Thus an unusual interval is used here. - -### Plugins - -Additional plugins are available for sensors available in hardware. First -check what your hardware supports: - - /system/health/print; - -Then install the plugin for *fan* and *power supply unit* *state*: - - $ScriptInstallUpdate check-health,check-health.d/state; - -... or *temperature*: - - $ScriptInstallUpdate check-health,check-health.d/temperature; - -... or *voltage*: - - $ScriptInstallUpdate check-health,check-health.d/voltage; - -You can also combine the commands and install all or a subset of plugins -in one go: - - $ScriptInstallUpdate check-health,check-health.d/state,check-health.d/temperature,check-health.d/voltage; - -Configuration -------------- - -The configuration goes to `global-config-overlay`, these are the parameters: - -* `CheckHealthTemperature`: an array specifying temperature thresholds for sensors -* `CheckHealthVoltageLow`: value (in volt*10) giving a hard lower limit -* `CheckHealthVoltagePercent`: percentage value to trigger voltage jumps - -> ℹ️ **Info**: Copy relevant configuration from -> [`global-config`](../global-config.rsc) (the one without `-overlay`) to -> your local `global-config-overlay` and modify it to your specific needs. - -Also notification settings are required for -[e-mail](mod/notification-email.md), -[matrix](mod/notification-matrix.md), -[ntfy](mod/notification-ntfy.md) and/or -[telegram](mod/notification-telegram.md). - ---- -[⬅️ Go back to main README](../README.md) -[⬆️ Go back to top](#top) diff --git a/doc/check-lte-firmware-upgrade.d/notification.avif b/doc/check-lte-firmware-upgrade.d/notification.avif deleted file mode 100644 index c440da5..0000000 Binary files a/doc/check-lte-firmware-upgrade.d/notification.avif and /dev/null differ diff --git a/doc/check-lte-firmware-upgrade.md b/doc/check-lte-firmware-upgrade.md deleted file mode 100644 index 3693b71..0000000 --- a/doc/check-lte-firmware-upgrade.md +++ /dev/null @@ -1,58 +0,0 @@ -Notify on LTE firmware upgrade -============================== - -[](https://github.com/eworm-de/routeros-scripts/stargazers) -[](https://github.com/eworm-de/routeros-scripts/network) -[](https://github.com/eworm-de/routeros-scripts/watchers) -[](https://mikrotik.com/download/changelogs/) -[](https://t.me/routeros_scripts) -[](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J) - -[⬅️ Go back to main README](../README.md) - -> ℹ️ **Info**: This script can not be used on its own but requires the base -> installation. See [main README](../README.md) for details. - -Description ------------ - -This script is run from scheduler periodically, checking for LTE firmware -upgrades. Currently supported LTE hardware: - -* R11e-LTE -* R11e-LTE-US -* R11e-4G -* R11e-LTE6 - -### Sample notification - - - -Requirements and installation ------------------------------ - -Just install the script: - - $ScriptInstallUpdate check-lte-firmware-upgrade; - -... and create a scheduler: - - /system/scheduler/add interval=1d name=check-lte-firmware-upgrade on-event="/system/script/run check-lte-firmware-upgrade;" start-time=startup; - -Configuration -------------- - -Also notification settings are required for -[e-mail](mod/notification-email.md), -[matrix](mod/notification-matrix.md) and/or -[telegram](mod/notification-telegram.md). - -See also --------- - -* [Notify on RouterOS update](check-routeros-update.md) -* [Install LTE firmware upgrade](unattended-lte-firmware-upgrade.md) - ---- -[⬅️ Go back to main README](../README.md) -[⬆️ Go back to top](#top) diff --git a/doc/check-routeros-update.d/notification.avif b/doc/check-routeros-update.d/notification.avif deleted file mode 100644 index 50317cf..0000000 Binary files a/doc/check-routeros-update.d/notification.avif and /dev/null differ diff --git a/doc/check-routeros-update.md b/doc/check-routeros-update.md deleted file mode 100644 index 926b4aa..0000000 --- a/doc/check-routeros-update.md +++ /dev/null @@ -1,107 +0,0 @@ -Notify on RouterOS update -========================= - -[](https://github.com/eworm-de/routeros-scripts/stargazers) -[](https://github.com/eworm-de/routeros-scripts/network) -[](https://github.com/eworm-de/routeros-scripts/watchers) -[](https://mikrotik.com/download/changelogs/) -[](https://t.me/routeros_scripts) -[](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J) - -[⬅️ Go back to main README](../README.md) - -> ℹ️ **Info**: This script can not be used on its own but requires the base -> installation. See [main README](../README.md) for details. - -Description ------------ - -The primary use of this script is to notify about RouterOS updates. - -Run from a terminal you can start the update process or schedule it. - -Centrally managing update process of several devices is possibly by -specifying versions safe to be updated on a web server. Versions seen -in neighbor discovery can be specified to be safe as well. - -Also installing patch updates (where just last digit is increased) -automatically is supported. - -> ⚠️ **Warning**: Installing updates is important from a security point -> of view. At the same time it can be source of serve breakage. So test -> versions in lab and read -> [changelog](https://mikrotik.com/download/changelogs/) and -> [forum](https://forum.mikrotik.com/viewforum.php?f=21) before deploying -> to your production environment! Automatic updates should be handled -> with care! - -### Sample notification - - - -Requirements and installation ------------------------------ - -Just install the script: - - $ScriptInstallUpdate check-routeros-update; - -And add a scheduler for automatic update notification: - - /system/scheduler/add interval=1d name=check-routeros-update on-event="/system/script/run check-routeros-update;" start-time=startup; - -Configuration -------------- - -No extra configuration is required to receive notifications. Several -mechanisms are availalbe to enable automatic installation of updates. -The configuration goes to `global-config-overlay`, these are the parameters: - -* `SafeUpdateNeighbor`: install updates automatically if at least one other - device is seen in neighbor list with new version -* `SafeUpdateNeighborIdentity`: regular expression to match identity for - trusted devices, leave empty to match all -* `SafeUpdatePatch`: install patch updates (where just last digit changes) - automatically -* `SafeUpdateUrl`: url on webserver to check for safe update, the channel - (`long-term`, `stable` or `testing`) is appended -* `SafeUpdateAll`: install **all** updates automatically - -> ℹ️ **Info**: Copy relevant configuration from -> [`global-config`](../global-config.rsc) (the one without `-overlay`) to -> your local `global-config-overlay` and modify it to your specific needs. - -Also notification settings are required for -[e-mail](mod/notification-email.md), -[matrix](mod/notification-matrix.md), -[ntfy](mod/notification-ntfy.md) and/or -[telegram](mod/notification-telegram.md). - -Usage and invocation --------------------- - -Be notified when run from scheduler or run it manually: - - /system/script/run check-routeros-update; - -If an update is found you can install it right away. - -Installing script [packages-update](packages-update.md) gives extra options. - -Tips & Tricks -------------- - -The script checks for full connectivity before acting, so scheduling at -startup is perfectly valid: - - /system/scheduler/add name=check-routeros-update@startup on-event="/system/script/run check-routeros-update;" start-time=startup; - -See also --------- - -* [Automatically upgrade firmware and reboot](firmware-upgrade-reboot.md) -* [Manage system update](packages-update.md) - ---- -[⬅️ Go back to main README](../README.md) -[⬆️ Go back to top](#top) diff --git a/doc/cloud-backup.md b/doc/cloud-backup.md deleted file mode 100644 index e161cfa..0000000 --- a/doc/cloud-backup.md +++ /dev/null @@ -1 +0,0 @@ -This script has been renamed. Please see [backup-cloud](backup-cloud.md). diff --git a/doc/collect-wireless-mac.d/notification.avif b/doc/collect-wireless-mac.d/notification.avif deleted file mode 100644 index a2833f0..0000000 Binary files a/doc/collect-wireless-mac.d/notification.avif and /dev/null differ diff --git a/doc/collect-wireless-mac.md b/doc/collect-wireless-mac.md deleted file mode 100644 index 0197522..0000000 --- a/doc/collect-wireless-mac.md +++ /dev/null @@ -1,77 +0,0 @@ -Collect MAC addresses in wireless access list -============================================= - -[](https://github.com/eworm-de/routeros-scripts/stargazers) -[](https://github.com/eworm-de/routeros-scripts/network) -[](https://github.com/eworm-de/routeros-scripts/watchers) -[](https://mikrotik.com/download/changelogs/) -[](https://t.me/routeros_scripts) -[](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J) - -[⬅️ Go back to main README](../README.md) - -> ℹ️ **Info**: This script can not be used on its own but requires the base -> installation. See [main README](../README.md) for details. - -Description ------------ - -This script adds unknown MAC addresses of connected wireless devices to -address list. In addition a notification is sent. - -By default the access list entry is disabled, but you can easily enable -and modify it to your needs. - -### Sample notification - - - -Requirements and installation ------------------------------ - -Depending on whether you use `wifi` package (`/interface/wifi`), legacy -wifi with CAPsMAN (`/caps-man`) or local wireless interface -(`/interface/wireless`) you need to install a different script. - -For `wifi`: - - $ScriptInstallUpdate collect-wireless-mac.wifi; - -For legacy CAPsMAN: - - $ScriptInstallUpdate collect-wireless-mac.capsman; - -For legacy local interface: - - $ScriptInstallUpdate collect-wireless-mac.local; - -Configuration -------------- - -On first run a disabled access list entry acting as marker (with comment -"`--- collected above ---`") is added. Move this entry to define where new -entries are to be added. - -Also notification settings are required for -[e-mail](mod/notification-email.md), -[matrix](mod/notification-matrix.md), -[ntfy](mod/notification-ntfy.md) and/or -[telegram](mod/notification-telegram.md). - -Usage and invocation --------------------- - -Run this script from a dhcp server as lease-script to collect the MAC -address when a new address is leased. You may want to use -[lease-script](lease-script.md). - -See also --------- - -* [Comment DHCP leases with info from access list](dhcp-lease-comment.md) -* [Create DNS records for DHCP leases](dhcp-to-dns.md) -* [Run other scripts on DHCP lease](lease-script.md) - ---- -[⬅️ Go back to main README](../README.md) -[⬆️ Go back to top](#top) diff --git a/doc/daily-psk.d/notification.avif b/doc/daily-psk.d/notification.avif deleted file mode 100644 index dd0b1b6..0000000 Binary files a/doc/daily-psk.d/notification.avif and /dev/null differ diff --git a/doc/daily-psk.md b/doc/daily-psk.md deleted file mode 100644 index 4a3de64..0000000 --- a/doc/daily-psk.md +++ /dev/null @@ -1,88 +0,0 @@ -Use wireless network with daily psk -=================================== - -[](https://github.com/eworm-de/routeros-scripts/stargazers) -[](https://github.com/eworm-de/routeros-scripts/network) -[](https://github.com/eworm-de/routeros-scripts/watchers) -[](https://mikrotik.com/download/changelogs/) -[](https://t.me/routeros_scripts) -[](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J) - -[⬅️ Go back to main README](../README.md) - -> ℹ️ **Info**: This script can not be used on its own but requires the base -> installation. See [main README](../README.md) for details. - -Description ------------ - -This script is supposed to provide a wifi network which changes the -passphrase to a pseudo-random string daily. - -### Sample notification - - - -Requirements and installation ------------------------------ - -Just install this script. - -Depending on whether you use `wifi` package (`/interface/wifi`), legacy -wifi with CAPsMAN (`/caps-man`) or local wireless interface -(`/interface/wireless`) you need to install a different script and add -schedulers to run the script: - -For `wifi`: - - $ScriptInstallUpdate daily-psk.wifi; - /system/scheduler/add interval=1d name=daily-psk on-event="/system/script/run daily-psk.wifi;" start-time=03:00:00; - /system/scheduler/add name=daily-psk@startup on-event="/system/script/run daily-psk.wifi;" start-time=startup; - -For legacy CAPsMAN: - - $ScriptInstallUpdate daily-psk.capsman; - /system/scheduler/add interval=1d name=daily-psk on-event="/system/script/run daily-psk.capsman;" start-time=03:00:00; - /system/scheduler/add name=daily-psk@startup on-event="/system/script/run daily-psk.capsman;" start-time=startup; - -For legacy local interface: - - $ScriptInstallUpdate daily-psk.local; - /system/scheduler/add interval=1d name=daily-psk on-event="/system/script/run daily-psk.local;" start-time=03:00:00; - /system/scheduler/add name=daily-psk@startup on-event="/system/script/run daily-psk.local;" start-time=startup; - -These will update the passphrase on boot and nightly at 3:00. - -Configuration -------------- - -The configuration goes to `global-config-overlay`, these are the parameters: - -* `DailyPskMatchComment`: pattern to match the wireless access list comment -* `DailyPskSecrets`: an array with pseudo random strings - -> ℹ️ **Info**: Copy relevant configuration from -> [`global-config`](../global-config.rsc) (the one without `-overlay`) to -> your local `global-config-overlay` and modify it to your specific needs. - -Then add an access list entry. For `wifi`: - - /interface/wifi/access-list/add comment="Daily PSK" ssid-regexp="-guest\$" passphrase="ToBeChangedDaily"; - -For legacy CAPsMAN: - - /caps-man/access-list/add comment="Daily PSK" ssid-regexp="-guest\$" private-passphrase="ToBeChangedDaily"; - -For legacy local interface: - - /interface/wireless/access-list/add comment="Daily PSK" interface=wl-daily private-pre-shared-key="ToBeChangedDaily"; - -Also notification settings are required for -[e-mail](mod/notification-email.md), -[trix](mod/notification-matrix.md), -[ntfy](mod/notification-ntfy.md) and/or -[telegram](mod/notification-telegram.md). - ---- -[⬅️ Go back to main README](../README.md) -[⬆️ Go back to top](#top) diff --git a/doc/dhcp-lease-comment.md b/doc/dhcp-lease-comment.md deleted file mode 100644 index b02f199..0000000 --- a/doc/dhcp-lease-comment.md +++ /dev/null @@ -1,64 +0,0 @@ -Comment DHCP leases with info from access list -============================================== - -[](https://github.com/eworm-de/routeros-scripts/stargazers) -[](https://github.com/eworm-de/routeros-scripts/network) -[](https://github.com/eworm-de/routeros-scripts/watchers) -[](https://mikrotik.com/download/changelogs/) -[](https://t.me/routeros_scripts) -[](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J) - -[⬅️ Go back to main README](../README.md) - -> ℹ️ **Info**: This script can not be used on its own but requires the base -> installation. See [main README](../README.md) for details. - -Description ------------ - -This script adds comments to dynamic dhcp server leases. Infos are taken -from wireless access list. - -Requirements and installation ------------------------------ - -Depending on whether you use `wifi` package (`/interface/wifi`), legacy -wifi with CAPsMAN (`/caps-man`) or local wireless interface -(`/interface/wireless`) you need to install a different script. - -For `wifi`: - - $ScriptInstallUpdate dhcp-lease-comment.wifi; - -For legacy CAPsMAN: - - $ScriptInstallUpdate dhcp-lease-comment.capsman; - -For legacy local interface: - - $ScriptInstallUpdate dhcp-lease-comment.local; - -Configuration -------------- - -Infos are taken from wireless access list. Add entries with proper comments -there. You may want to use [collect-wireless-mac](collect-wireless-mac.md) -to prepare entries. - -Usage and invocation --------------------- - -Run this script from a dhcp server as lease-script to update the comment -just after a new address is leased. You may want to use -[lease-script](lease-script.md). - -See also --------- - -* [Collect MAC addresses in wireless access list](collect-wireless-mac.md) -* [Create DNS records for DHCP leases](dhcp-to-dns.md) -* [Run other scripts on DHCP lease](lease-script.md) - ---- -[⬅️ Go back to main README](../README.md) -[⬆️ Go back to top](#top) diff --git a/doc/dhcp-to-dns.md b/doc/dhcp-to-dns.md deleted file mode 100644 index 4211d85..0000000 --- a/doc/dhcp-to-dns.md +++ /dev/null @@ -1,93 +0,0 @@ -Create DNS records for DHCP leases -================================== - -[](https://github.com/eworm-de/routeros-scripts/stargazers) -[](https://github.com/eworm-de/routeros-scripts/network) -[](https://github.com/eworm-de/routeros-scripts/watchers) -[](https://mikrotik.com/download/changelogs/) -[](https://t.me/routeros_scripts) -[](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J) - -[⬅️ Go back to main README](../README.md) - -> ℹ️ **Info**: This script can not be used on its own but requires the base -> installation. See [main README](../README.md) for details. - -Description ------------ - -This script adds (and updates & removes) dns records based on dhcp server -leases. An A record based on mac address is created for all bound lease, -additionally a CNAME record is created from host name if available. - -Requirements and installation ------------------------------ - -Just install the script: - - $ScriptInstallUpdate dhcp-to-dns; - -Then run it from dhcp server as lease script. You may want to use -[lease-script](lease-script.md). - -A scheduler cares about cleanup: - - /system/scheduler/add interval=15m name=dhcp-to-dns on-event="/system/script/run dhcp-to-dns;" start-time=startup; - -Configuration -------------- - -On first run a disabled static dns record acting as marker (with comment -"`--- dhcp-to-dns above ---`") is added. Move this entry to define where new -entries are to be added. - -The configuration goes to dhcp server's network definition. The domain is -used to form the dns name: - - /ip/dhcp-server/network/add address=10.0.0.0/24 domain=example.com; - -A bound lease for mac address `00:11:22:33:44:55` with ip address -`10.0.0.50` would result in an A record `00-11-22-33-44-55.example.com` -pointing to the given ip address. - -Additional options can be given from comment, to add an extra level in -dns name or define a different domain. - - /ip/dhcp-server/network/add address=10.0.0.0/24 domain=example.com comment="domain=another-domain.com, name-extra=dhcp"; - -This example would result in name `00-11-22-33-44-55.dhcp.another-domain.com` -for the same lease. - -If no domain is found in dhcp server's network definition a fallback from -`global-config-overlay` is used. This is the parameter: - -* `Domain`: the domain used for dns records - -> ℹ️ **Info**: Copy relevant configuration from -> [`global-config`](../global-config.rsc) (the one without `-overlay`) to -> your local `global-config-overlay` and modify it to your specific needs. - -### Host name from DHCP lease comment - -Overwriting the host name from dhcp lease comment is supported, just add -something like `hostname=new-hostname` in comment, and separate it by comma -from other information if required: - - /ip/dhcp-server/lease/add address=10.0.0.50 comment="my device, hostname=new-hostname" mac-address=00:11:22:33:44:55 server=dhcp; - -Note this information can be configured in wireless access list with -[dhcp-lease-comment](dhcp-lease-comment.md), though it comes with a delay -then due to script execution order. Decrease the scheduler interval to -reduce the effect. - -See also --------- - -* [Collect MAC addresses in wireless access list](collect-wireless-mac.md) -* [Comment DHCP leases with info from access list](dhcp-lease-comment.md) -* [Create DNS records for IPSec peers](ipsec-to-dns.md) -* [Run other scripts on DHCP lease](lease-script.md) - ---- -[⬅️ Go back to main README](../README.md) -[⬆️ Go back to top](#top) diff --git a/doc/early-errors.md b/doc/early-errors.md deleted file mode 100644 index b3c6800..0000000 --- a/doc/early-errors.md +++ /dev/null @@ -1,2 +0,0 @@ -This script has been replaced. Please migrate to -[Forward log messages via notification](log-forward.md). diff --git a/doc/email-backup.md b/doc/email-backup.md deleted file mode 100644 index d674743..0000000 --- a/doc/email-backup.md +++ /dev/null @@ -1 +0,0 @@ -This script has been renamed. Please see [backup-email](backup-email.md). \ No newline at end of file diff --git a/doc/firmware-upgrade-reboot.md b/doc/firmware-upgrade-reboot.md deleted file mode 100644 index 54f1da0..0000000 --- a/doc/firmware-upgrade-reboot.md +++ /dev/null @@ -1,43 +0,0 @@ -Automatically upgrade firmware and reboot -========================================= - -[](https://github.com/eworm-de/routeros-scripts/stargazers) -[](https://github.com/eworm-de/routeros-scripts/network) -[](https://github.com/eworm-de/routeros-scripts/watchers) -[](https://mikrotik.com/download/changelogs/) -[](https://t.me/routeros_scripts) -[](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J) - -[⬅️ Go back to main README](../README.md) - -> ℹ️ **Info**: This script can not be used on its own but requires the base -> installation. See [main README](../README.md) for details. - -Description ------------ - -RouterOS and firmware are upgraded separately, activating the latter -requires an extra reboot. This script handles upgrade and reboot. - -> ⚠️ **Warning**: This *should* be bullet proof, but I can not guarantee. In -> worst case it has potential to cause a boot loop, so handle with care! - -Requirements and installation ------------------------------ - -Just install the script and create a scheduler: - - $ScriptInstallUpdate firmware-upgrade-reboot; - /system/scheduler/add name=firmware-upgrade-reboot on-event="/system/script/run firmware-upgrade-reboot;" start-time=startup; - -Enjoy firmware being up to date and in sync with RouterOS. - -See also --------- - -* [Notify on RouterOS update](check-routeros-update.md) -* [Manage system update](packages-update.md) - ---- -[⬅️ Go back to main README](../README.md) -[⬆️ Go back to top](#top) diff --git a/doc/fw-addr-lists.md b/doc/fw-addr-lists.md deleted file mode 100644 index cb560d7..0000000 --- a/doc/fw-addr-lists.md +++ /dev/null @@ -1,137 +0,0 @@ -Download, import and update firewall address-lists -================================================== - -[](https://github.com/eworm-de/routeros-scripts/stargazers) -[](https://github.com/eworm-de/routeros-scripts/network) -[](https://github.com/eworm-de/routeros-scripts/watchers) -[](https://mikrotik.com/download/changelogs/) -[](https://t.me/routeros_scripts) -[](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J) - -[⬅️ Go back to main README](../README.md) - -> ℹ️ **Info**: This script can not be used on its own but requires the base -> installation. See [main README](../README.md) for details. - -Description ------------ - -This script downloads, imports and updates firewall address-lists. Its main -purpose is to block attacking ip addresses, spam hosts, command-and-control -servers and similar malicious entities. The default configuration contains a -[collective list by GitHub user @stamparm](https://github.com/stamparm/ipsum), -lists from [dshield.org](https://dshield.org/) and -[blocklist.de](https://www.blocklist.de/), and lists from -[spamhaus.org](https://spamhaus.org/) are prepared. - -The address-lists are updated in place, so after initial import you will not -see situation when the lists are not populated. - -To mitigate man-in-the-middle attacks with altered lists the server's -certificate is checked. - -> ⚠️ **Warning**: The script does not limit the size of a list, but keep in -> mind that huge lists can exhaust your device's resources (RAM and CPU), -> and may take a long time to process. - -Requirements and installation ------------------------------ - -Just install the script: - - $ScriptInstallUpdate fw-addr-lists; - -And add two schedulers, first one for initial import after startup, second -one for subsequent updates: - - /system/scheduler/add name="fw-addr-lists@startup" start-time=startup on-event="/system/script/run fw-addr-lists;"; - /system/scheduler/add name="fw-addr-lists" start-time=startup interval=2h on-event="/system/script/run fw-addr-lists;"; - -> ℹ️ **Info**: Modify the interval to your needs, but it is recommended to -> use less than half of the configured timeout for expiration. - -Configuration -------------- - -The configuration goes to `global-config-overlay`, these are the parameters: - -* `FwAddrLists`: a list of firewall address-lists to download and import -* `FwAddrListTimeOut`: the timeout for expiration without renew - -> ℹ️ **Info**: Copy relevant configuration from -> [`global-config`](../global-config.rsc) (the one without `-overlay`) to -> your local `global-config-overlay` and modify it to your specific needs. - -Naming a certificate for a list makes the script verify the server -certificate, so you should add that if possible. You may want to find the -[certificate name from browser](../CERTIFICATES.md). - -Create firewall rules to process the packets that are related to addresses -from address-lists. - -### IPv4 rules - -This rejects the packets from and to IPv4 addresses listed in -address-list `block`. - - /ip/firewall/filter/add chain=input src-address-list=block action=reject reject-with=icmp-admin-prohibited; - /ip/firewall/filter/add chain=forward src-address-list=block action=reject reject-with=icmp-admin-prohibited; - /ip/firewall/filter/add chain=forward dst-address-list=block action=reject reject-with=icmp-admin-prohibited; - /ip/firewall/filter/add chain=output dst-address-list=block action=reject reject-with=icmp-admin-prohibited; - -You may want to have an address-list to allow specific addresses, as prepared -with a list `allow`. In fact you can use any list name, just change the -default ones or add your own - matching in configuration and firewall rules. - - /ip/firewall/filter/add chain=input src-address-list=allow action=accept; - /ip/firewall/filter/add chain=forward src-address-list=allow action=accept; - /ip/firewall/filter/add chain=forward dst-address-list=allow action=accept; - /ip/firewall/filter/add chain=output dst-address-list=allow action=accept; - -Modify these for your needs, but **most important**: Move the rules up in -chains and make sure they actually take effect as expected! - -Alternatively handle the packets in firewall's raw section if you prefer: - - /ip/firewall/raw/add chain=prerouting src-address-list=block action=drop; - /ip/firewall/raw/add chain=prerouting dst-address-list=block action=drop; - /ip/firewall/raw/add chain=output dst-address-list=block action=drop; - -> ⚠️ **Warning**: Just again... The order of firewall rules is important. Make -> sure they actually take effect as expected! - -### IPv6 rules - -These are the same rules, but for IPv6. - -Reject packets in address-list `block`: - - /ipv6/firewall/filter/add chain=input src-address-list=block action=reject reject-with=icmp-admin-prohibited; - /ipv6/firewall/filter/add chain=forward src-address-list=block action=reject reject-with=icmp-admin-prohibited; - /ipv6/firewall/filter/add chain=forward dst-address-list=block action=reject reject-with=icmp-admin-prohibited; - /ipv6/firewall/filter/add chain=output dst-address-list=block action=reject reject-with=icmp-admin-prohibited; - -Allow packets in address-list `allow`: - - /ipv6/firewall/filter/add chain=input src-address-list=allow action=accept; - /ipv6/firewall/filter/add chain=forward src-address-list=allow action=accept; - /ipv6/firewall/filter/add chain=forward dst-address-list=allow action=accept; - /ipv6/firewall/filter/add chain=output dst-address-list=allow action=accept; - -Drop packets in firewall's raw section: - - /ipv6/firewall/raw/add chain=prerouting src-address-list=block action=drop; - /ipv6/firewall/raw/add chain=prerouting dst-address-list=block action=drop; - /ipv6/firewall/raw/add chain=output dst-address-list=block action=drop; - -> ⚠️ **Warning**: Just again... The order of firewall rules is important. Make -> sure they actually take effect as expected! - -See also --------- - -* [Certificate name from browser](../CERTIFICATES.md) - ---- -[⬅️ Go back to main README](../README.md) -[⬆️ Go back to top](#top) diff --git a/doc/global-wait.md b/doc/global-wait.md deleted file mode 100644 index 799cae7..0000000 --- a/doc/global-wait.md +++ /dev/null @@ -1,47 +0,0 @@ -Wait for global functions and modules -===================================== - -[](https://github.com/eworm-de/routeros-scripts/stargazers) -[](https://github.com/eworm-de/routeros-scripts/network) -[](https://github.com/eworm-de/routeros-scripts/watchers) -[](https://mikrotik.com/download/changelogs/) -[](https://t.me/routeros_scripts) -[](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J) - -[⬅️ Go back to main README](../README.md) - -> ℹ️ **Info**: This script can not be used on its own but requires the base -> installation. See [main README](../README.md) for details. - -Description ------------ - -The global functions from `global-functions` and modules are loaded by -scheduler at system startup. Running these functions at system startup may -result in race condition where configuration and/or function are not yet -available. This script is supposed to wait for everything being prepared. - -Do **not** add this script `global-wait` to the `global-scripts` scheduler! -It would inhibit the initialization of configuration and functions. - -Requirements and installation ------------------------------ - -Just install the script: - - $ScriptInstallUpdate global-wait; - -... and add it to your scheduler, for example in combination with the module -to [manage VLANs on bridge ports](mod/bridge-port-vlan.md): - - /system/scheduler/add name=bridge-port-vlan on-event="/system/script/run global-wait; :global BridgePortVlan; \$BridgePortVlan default;" start-time=startup; - -See also --------- - -* [Manage ports in bridge](mod/bridge-port-to.md) -* [Manage VLANs on bridge ports](mod/bridge-port-vlan.md) - ---- -[⬅️ Go back to main README](../README.md) -[⬆️ Go back to top](#top) diff --git a/doc/gps-track.md b/doc/gps-track.md deleted file mode 100644 index 5e4878f..0000000 --- a/doc/gps-track.md +++ /dev/null @@ -1,51 +0,0 @@ -Send GPS position to server -=========================== - -[](https://github.com/eworm-de/routeros-scripts/stargazers) -[](https://github.com/eworm-de/routeros-scripts/network) -[](https://github.com/eworm-de/routeros-scripts/watchers) -[](https://mikrotik.com/download/changelogs/) -[](https://t.me/routeros_scripts) -[](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J) - -[⬅️ Go back to main README](../README.md) - -> ℹ️ **Info**: This script can not be used on its own but requires the base -> installation. See [main README](../README.md) for details. - -Description ------------ - -This script is supposed to run periodically from scheduler and send GPS -position data to a server for tracking. - -A hardware GPS antenna is required. - -Requirements and installation ------------------------------ - -Just install the script: - - $ScriptInstallUpdate gps-track; - -... and create a scheduler: - - /system/scheduler/add interval=1m name=gps-track on-event="/system/script/run gps-track;" start-time=startup; - -Configuration -------------- - -The configuration goes to `global-config-overlay`, the only parameter is: - -* `GpsTrackUrl`: the url to send json data to - -> ℹ️ **Info**: Copy relevant configuration from -> [`global-config`](../global-config.rsc) (the one without `-overlay`) to -> your local `global-config-overlay` and modify it to your specific needs. - -The configured coordinate format (see `/system/gps`) defines the format -sent to the server. - ---- -[⬅️ Go back to main README](../README.md) -[⬆️ Go back to top](#top) diff --git a/doc/hotspot-to-wpa.md b/doc/hotspot-to-wpa.md deleted file mode 100644 index a2e9748..0000000 --- a/doc/hotspot-to-wpa.md +++ /dev/null @@ -1,124 +0,0 @@ -Use WPA network with hotspot credentials -======================================== - -[](https://github.com/eworm-de/routeros-scripts/stargazers) -[](https://github.com/eworm-de/routeros-scripts/network) -[](https://github.com/eworm-de/routeros-scripts/watchers) -[](https://mikrotik.com/download/changelogs/) -[](https://t.me/routeros_scripts) -[](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J) - -[⬅️ Go back to main README](../README.md) - -> ℹ️ **Info**: This script can not be used on its own but requires the base -> installation. See [main README](../README.md) for details. - -Description ------------ - -RouterOS supports an unlimited number of MAC address specific passphrases -for WPA encrypted wifi networks via access list. The idea of this script -is to transfer hotspot credentials to MAC address specific WPA passphrase. - -Requirements and installation ------------------------------ - -You need a properly configured hotspot on one (open) SSID and a WPA enabled -SSID with suffix "`-wpa`". - -Then install the script. -Depending on whether you use `wifi` package (`/interface/wifi`)or legacy -wifi with CAPsMAN (`/caps-man`) you need to install a different script and -set it as `on-login` script in hotspot. - -For `wifi`: - - $ScriptInstallUpdate hotspot-to-wpa.wifi; - /ip/hotspot/user/profile/set on-login="hotspot-to-wpa.wifi" [ find ]; - -For legacy CAPsMAN: - - $ScriptInstallUpdate hotspot-to-wpa.capsman; - /ip/hotspot/user/profile/set on-login="hotspot-to-wpa.capsman" [ find ]; - -### Automatic cleanup - -With just `hotspot-to-wpa` installed the mac addresses will last in the -access list forever. Install the optional script for automatic cleanup -and add a scheduler. - -For `wifi`: - - $ScriptInstallUpdate hotspot-to-wpa-cleanup.wifi,lease-script; - /system/scheduler/add interval=1d name=hotspot-to-wpa-cleanup on-event="/system/script/run hotspot-to-wpa-cleanup.wifi;" start-time=startup; - -For legacy CAPsMAN: - - $ScriptInstallUpdate hotspot-to-wpa-cleanup.capsman,lease-script; - /system/scheduler/add interval=1d name=hotspot-to-wpa-cleanup on-event="/system/script/run hotspot-to-wpa-cleanup.capsman;" start-time=startup; - -And add the lease script and matcher comment to your wpa interfaces' dhcp -server. You can add more information to the comment, separated by comma. In -this example the server is called `hotspot-to-wpa`. - - /ip/dhcp-server/set lease-script=lease-script comment="hotspot-to-wpa=wpa" hotspot-to-wpa; - -You can specify the timeout after which a device is removed from leases and -access-list. The default is four weeks. - - /ip/dhcp-server/set lease-script=lease-script comment="hotspot-to-wpa=wpa, timeout=2w" hotspot-to-wpa; - -Configuration -------------- - -On first run a disabled access list entry acting as marker (with comment -"`--- hotspot-to-wpa above ---`") is added. Move this entry to define where new -entries are to be added. - -Create hotspot login credentials: - - /ip/hotspot/user/add comment="Test User 1" name=user1 password=v3ry; - /ip/hotspot/user/add comment="Test User 2" name=user2 password=s3cr3t; - -This also works with authentication via radius, but is limited then: -Additional information is not available, including the password. - -Additionally templates can be created to give more options for access list: - -* `action`: set to `reject` to ignore logins on that hotspot -* `passphrase` or `private-passphrase`: do **not** use passphrase from - hotspot's user credentials, but given one - or unset (use default - passphrase) with special word `ignore` -* `ssid-regexp`: set a different SSID regular expression to match -* `vlan-id`: connect device to specific VLAN -* `vlan-mode`: set the VLAN mode for device - -For a hotspot called `example` the template could look like this. -For `wifi`: - - /interface/wifi/access-list/add comment="hotspot-to-wpa template example" disabled=yes passphrase="ignore" ssid-regexp="^example\$" vlan-id=10; - -For legacy CAPsMAN: - - /caps-man/access-list/add comment="hotspot-to-wpa template example" disabled=yes private-passphrase="ignore" ssid-regexp="^example\$" vlan-id=10 vlan-mode=use-tag; - -The same settings are available in hotspot user's comment and take precedence -over the template settings: - - /ip/hotspot/user/add comment="private-passphrase=ignore, ssid-regexp=^example\\\$, vlan-id=10, vlan-mode=use-tag" name=user password=v3ry-s3cr3t; - -Usage and invocation --------------------- - -Now let the users connect and login to the hotspot. After that the devices -(identified by MAC address) can connect to the WPA network, using the -passphrase from hotspot credentials. - -See also --------- - -* [Run other scripts on DHCP lease](lease-script.md) - ---- -[⬅️ Go back to main README](../README.md) -[⬆️ Go back to top](#top) diff --git a/doc/ip-addr-bridge.md b/doc/ip-addr-bridge.md deleted file mode 100644 index f9f98e3..0000000 --- a/doc/ip-addr-bridge.md +++ /dev/null @@ -1,39 +0,0 @@ -Manage IP addresses with bridge status -====================================== - -[](https://github.com/eworm-de/routeros-scripts/stargazers) -[](https://github.com/eworm-de/routeros-scripts/network) -[](https://github.com/eworm-de/routeros-scripts/watchers) -[](https://mikrotik.com/download/changelogs/) -[](https://t.me/routeros_scripts) -[](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J) - -[⬅️ Go back to main README](../README.md) - -Description ------------ - -With RouterOS an IP address is always active, even if an interface is down. -Other venders handle this differently - and sometimes this behavior is -expected. This script mimics this behavior. - -Requirements and installation ------------------------------ - -Just install the script: - - $ScriptInstallUpdate ip-addr-bridge; - -... and make it run from scheduler: - - /system/scheduler/add name=ip-addr-bridge on-event="/system/script/run ip-addr-bridge;" start-time=startup; - -This will disable IP addresses on bridges without at least one running port. -The IP address is enabled if at least one port is running. - -Note that IP addresses on bridges without a single port (acting as loopback -interface) are ignored. - ---- -[⬅️ Go back to main README](../README.md) -[⬆️ Go back to top](#top) diff --git a/doc/ipsec-to-dns.md b/doc/ipsec-to-dns.md deleted file mode 100644 index 123656c..0000000 --- a/doc/ipsec-to-dns.md +++ /dev/null @@ -1,57 +0,0 @@ -Create DNS records for IPSec peers -================================== - -[](https://github.com/eworm-de/routeros-scripts/stargazers) -[](https://github.com/eworm-de/routeros-scripts/network) -[](https://github.com/eworm-de/routeros-scripts/watchers) -[](https://mikrotik.com/download/changelogs/) -[](https://t.me/routeros_scripts) -[](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J) - -[⬅️ Go back to main README](../README.md) - -> ℹ️ **Info**: This script can not be used on its own but requires the base -> installation. See [main README](../README.md) for details. - -Description ------------ - -This script adds (and removes) dns records based on IPSec peers and their -dynamic addresses from mode-config. - -Requirements and installation ------------------------------ - -Just install the script: - - $ScriptInstallUpdate ipsec-to-dns; - -This script is run from scheduler: - - /system/scheduler/add interval=1m name=ipsec-to-dns on-event="/system/script/run ipsec-to-dns;" start-time=startup; - -Configuration -------------- - -On first run a disabled static dns record acting as marker (with comment -"`--- ipsec-to-dns above ---`") is added. Move this entry to define where new -entries are to be added. - -The configuration goes to `global-config-overlay`, these are the parameters: - -* `Domain`: the domain used for dns records -* `HostNameInZone`: whether or not to add the ipsec/dns server's hostname -* `PrefixInZone`: whether or not to add prefix `ipsec` - -> ℹ️ **Info**: Copy relevant configuration from -> [`global-config`](../global-config.rsc) (the one without `-overlay`) to -> your local `global-config-overlay` and modify it to your specific needs. - -See also --------- - -* [Create DNS records for DHCP leases](dns-to-dhcp.md) - ---- -[⬅️ Go back to main README](../README.md) -[⬆️ Go back to top](#top) diff --git a/doc/ipv6-update.md b/doc/ipv6-update.md deleted file mode 100644 index 1f009b1..0000000 --- a/doc/ipv6-update.md +++ /dev/null @@ -1,84 +0,0 @@ -Update configuration on IPv6 prefix change -========================================== - -[](https://github.com/eworm-de/routeros-scripts/stargazers) -[](https://github.com/eworm-de/routeros-scripts/network) -[](https://github.com/eworm-de/routeros-scripts/watchers) -[](https://mikrotik.com/download/changelogs/) -[](https://t.me/routeros_scripts) -[](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J) - -[⬅️ Go back to main README](../README.md) - -> ℹ️ **Info**: This script can not be used on its own but requires the base -> installation. See [main README](../README.md) for details. - -Description ------------ - -With changing IPv6 prefix from ISP this script handles to update... - -* ipv6 firewall address-list (prefixes (`/64`) and host addresses (`/128`)) -* dns records - -Requirements and installation ------------------------------ - -Just install the script: - - $ScriptInstallUpdate ipv6-update; - -Your ISP needs to provide an IPv6 prefix, your device receives it via dhcp: - - /ipv6/dhcp-client/add add-default-route=yes interface=ppp-isp pool-name=isp request=prefix script=ipv6-update; - -Note this already adds this script as `script`. The pool name (here: "`isp`") -is important, we need it later. - -Also this expects there is an address assigned from pool to an interface: - - /ipv6/address/add from-pool=isp interface=br-local; - -Sometimes dhcp client is stuck on reconnect and needs to be released. -Installing [ppp-on-up](ppp-on-up.md) may solve this. - -Configuration -------------- - -As an address-list entry is mandatory a dynamic one is created automatically. -It is updated with current prefix and can be used in firewall rules. - -Alternatively a static address-list entry can be used, where comment has to -be "`ipv6-pool-`" and actual pool name. Use what ever list is desired, and -create it with: - - /ipv6/firewall/address-list/add address=2003:cf:2f0f:de00::/56 comment=ipv6-pool-isp list=extern; - -If the dynamic entry exists already you need to remove it before creating -the static one.. - -Address list entries for specific interfaces can be updated as well. The -interface needs to get its address from pool `isp` and the address list entry -has to be associated to an interface in comment: - - /ipv6/firewall/address-list/add address=2003:cf:2f0f:de01::/64 comment="ipv6-pool-isp, interface=br-local" list=local; - -Updating address list entries with host addresses works as well, the new -prefix is combinded with given suffix then: - - /ipv6/firewall/address-list/add address=2003:cf:2f0f:de01:e3e0:f8fa:8cd6:dbe1/128 comment="ipv6-pool-isp, interface=br-local" list=hosts; - -Static DNS records need a special comment to be updated. Again it has to -start with "`ipv6-pool-`" and actual pool name, followed by a comma, -"`interface=`" and the name of interface this address is connected to: - - /ip/dns/static/add address=2003:cf:2f0f:de00:1122:3344:5566:7788 comment="ipv6-pool-isp, interface=br-local" name=test.example.com ttl=15m; - -See also --------- - -* [Run scripts on ppp connection](ppp-on-up.md) - ---- -[⬅️ Go back to main README](../README.md) -[⬆️ Go back to top](#top) diff --git a/doc/lease-script.md b/doc/lease-script.md deleted file mode 100644 index f83c383..0000000 --- a/doc/lease-script.md +++ /dev/null @@ -1,54 +0,0 @@ -Run other scripts on DHCP lease -=============================== - -[](https://github.com/eworm-de/routeros-scripts/stargazers) -[](https://github.com/eworm-de/routeros-scripts/network) -[](https://github.com/eworm-de/routeros-scripts/watchers) -[](https://mikrotik.com/download/changelogs/) -[](https://t.me/routeros_scripts) -[](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J) - -[⬅️ Go back to main README](../README.md) - -> ℹ️ **Info**: This script can not be used on its own but requires the base -> installation. See [main README](../README.md) for details. - -Description ------------ - -This script is supposed to run from dhcp server as lease script. On a dhcp -lease it runs each script containing the following line, where `##` is a -decimal number for ordering: - - # provides: lease-script, order=## - -Currently it runs if available, in order: - -* [dhcp-to-dns](dhcp-to-dns.md) -* [collect-wireless-mac](collect-wireless-mac.md) -* [dhcp-lease-comment](dhcp-lease-comment.md) -* `hotspot-to-wpa-cleanup`, which is an optional cleanup script - of [hotspot-to-wpa](hotspot-to-wpa.md) - -Requirements and installation ------------------------------ - -Just install the script: - - $ScriptInstallUpdate lease-script; - -... and add it as `lease-script` to your dhcp server: - - /ip/dhcp-server/set lease-script=lease-script [ find ]; - -See also --------- - -* [Collect MAC addresses in wireless access list](collect-wireless-mac.md) -* [Comment DHCP leases with info from access list](dhcp-lease-comment.md) -* [Create DNS records for DHCP leases](dhcp-to-dns.md) -* [Use WPA network with hotspot credentials](hotspot-to-wpa.md) - ---- -[⬅️ Go back to main README](../README.md) -[⬆️ Go back to top](#top) diff --git a/doc/leds-mode.md b/doc/leds-mode.md deleted file mode 100644 index a194396..0000000 --- a/doc/leds-mode.md +++ /dev/null @@ -1,57 +0,0 @@ -Manage LEDs dark mode -===================== - -[](https://github.com/eworm-de/routeros-scripts/stargazers) -[](https://github.com/eworm-de/routeros-scripts/network) -[](https://github.com/eworm-de/routeros-scripts/watchers) -[](https://mikrotik.com/download/changelogs/) -[](https://t.me/routeros_scripts) -[](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J) - -[⬅️ Go back to main README](../README.md) - -Description ------------ - -These scripts control LEDs mode and allow to run your device -completely dark. Hardware support for dark mode is required. - -Requirements and installation ------------------------------ - -Just install the scripts: - - $ScriptInstallUpdate leds-day-mode,leds-night-mode,leds-toggle-mode; - -Usage and invocation --------------------- - -To switch the device to dark mode: - - /system/script/run leds-night-mode; - -... and back to normal mode: - - /system/script/run leds-day-mode; - -To toggle between the two modes: - - /system/script/run leds-toggle-mode; - -Add these schedulers to switch to dark mode in the evening and back to -normal mode in the morning: - - /system/scheduler/add interval=1d name=leds-day-mode on-event="/system/script/run leds-day-mode;" start-time=07:00:00; - /system/scheduler/add interval=1d name=leds-night-mode on-event="/system/script/run leds-night-mode;" start-time=21:00:00; - -The script `leds-toggle-mode` can be used from [mode button](mode-button.md) -to toggle mode. - -See also --------- - -* [Mode button with multiple presses](mode-button.md) - ---- -[⬅️ Go back to main README](../README.md) -[⬆️ Go back to top](#top) diff --git a/doc/log-forward.d/notification.avif b/doc/log-forward.d/notification.avif deleted file mode 100644 index a0f9ab3..0000000 Binary files a/doc/log-forward.d/notification.avif and /dev/null differ diff --git a/doc/log-forward.md b/doc/log-forward.md deleted file mode 100644 index 3c19569..0000000 --- a/doc/log-forward.md +++ /dev/null @@ -1,101 +0,0 @@ -Forward log messages via notification -===================================== - -[](https://github.com/eworm-de/routeros-scripts/stargazers) -[](https://github.com/eworm-de/routeros-scripts/network) -[](https://github.com/eworm-de/routeros-scripts/watchers) -[](https://mikrotik.com/download/changelogs/) -[](https://t.me/routeros_scripts) -[](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J) - -[⬅️ Go back to main README](../README.md) - -> ℹ️ **Info**: This script can not be used on its own but requires the base -> installation. See [main README](../README.md) for details. - -Description ------------ - -RouterOS itself supports sending log messages via e-mail or to a syslog -server (see `/system/logging`). This has some limitation, however: - -* does not work early after boot if network connectivity is not - yet established, or breaks intermittently -* lots of messages generate a flood of mails -* Matrix, Ntfy and Telegram are not supported - -The script works around the limitations, for example it does: - -* read from `/log`, including messages from early boot -* skip multi-repeated messages -* rate-limit itself to mitigate flooding -* forward via notification (which includes *e-mail*, *Matrix*, *Ntfy* and - *Telegram* when installed and configured, see below) - -It is intended to be run periodically from scheduler, then collects new -log messages and forwards them via notification. - -### Sample notification - - - -Requirements and installation ------------------------------ - -Just install the script: - - $ScriptInstallUpdate log-forward; - -... and add a scheduler: - - /system/scheduler/add interval=1m name=log-forward on-event="/system/script/run log-forward;" start-time=startup; - -Configuration -------------- - -The default configuration should provide reasonable presets, filtering -*info*, and effectively forwarding *warning* and *error*. - -> 💡️ **Hint**: Please try with defaults first, especially if you are not -> familiar with regular expressions! - -The configuration goes to `global-config-overlay`, these are the parameters: - -* `LogForwardFilter`: define topics *not* to be forwarded -* `LogForwardFilterMessage`: define message text *not* to be forwarded -* `LogForwardInclude`: define topics to be forwarded (even if filter matches) -* `LogForwardIncludeMessage`: define message text to be forwarded (even if - filter matches) - -> ℹ️ **Info**: Copy relevant configuration from -> [`global-config`](../global-config.rsc) (the one without `-overlay`) to -> your local `global-config-overlay` and modify it to your specific needs. - -These patterns are matched as -[regular expressions](https://wiki.mikrotik.com/wiki/Manual:Regular_Expressions). -To forward **all** (ignoring severity) log messages with topics `account` -(which includes user logins) and `dhcp` you need something like: - - :global LogForwardInclude "(account|dhcp)"; - -Also notification settings are required for -[e-mail](mod/notification-email.md), -[matrix](mod/notification-matrix.md), -[ntfy](mod/notification-ntfy.md) and/or -[telegram](mod/notification-telegram.md). - -Tips & Tricks -------------- - -### Notification on reboot - -You want to receive a notification on every device (re-)boot? Quite easy, -just add: - - :global LogForwardIncludeMessage "(^router rebooted)"; - -This will match on every log message beginning with `router rebooted`. - ---- -[⬅️ Go back to main README](../README.md) -[⬆️ Go back to top](#top) diff --git a/doc/mod/bridge-port-to.md b/doc/mod/bridge-port-to.md deleted file mode 100644 index 629c526..0000000 --- a/doc/mod/bridge-port-to.md +++ /dev/null @@ -1,88 +0,0 @@ -Manage ports in bridge -====================== - -[](https://github.com/eworm-de/routeros-scripts/stargazers) -[](https://github.com/eworm-de/routeros-scripts/network) -[](https://github.com/eworm-de/routeros-scripts/watchers) -[](https://mikrotik.com/download/changelogs/) -[](https://t.me/routeros_scripts) -[](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J) - -[⬅️ Go back to main README](../../README.md) - -> ℹ️️ **Info**: This module can not be used on its own but requires the base -> installation. See [main README](../../README.md) for details. - -Description ------------ - -This module and its functio are are supposed to handle interfaces and -switching them from one bridge to another. - -Requirements and installation ------------------------------ - -Just install the module: - - $ScriptInstallUpdate mod/bridge-port-to; - -Configuration -------------- - -The configuration goes to ports' comments (`/interface/bridge/port`). - - /interface/bridge/port/add bridge=br-guest comment="default=dhcp-client, alt=br-guest" disabled=yes interface=en1; - /interface/bridge/port/add bridge=br-intern comment="default=br-intern, alt=br-guest" interface=en2; - /interface/bridge/port/add bridge=br-guest comment="default=br-guest, extra=br-extra" interface=en3; - -Also dhcp client can be handled: - - /ip/dhcp-client/add comment="toggle with bridge port" disabled=no interface=en1; - -Add a scheduler to start with default setup on system startup: - - $ScriptInstallUpdate global-wait; - /system/scheduler/add name=bridge-port-to on-event="/system/script/run global-wait; :global BridgePortTo; \$BridgePortTo default;" start-time=startup; - -Usage and invocation --------------------- - -The usage examples show what happens with the configuration from above. - -Running the function `$BridgePortTo` with parameter `default` applies all -configuration given with `default=`: - - $BridgePortTo default; - -For the three interfaces we get this configuration: - -* The special value `dhcp-client` enables the dhcp client for interface `en1`. The bridge port entry is disabled. -* Interface `en2` is put in bridge `br-intern`. -* Interface `en3` is put in bridge `br-guest`. - -Running the function `$BridgePortTo` with parameter `alt` applies all -configuration given with `alt=`: - - $BridgePortTo alt; - -* Interface `en1` is put in bridge `br-guest`, dhcp client for the interface is disabled. -* Interface `en2` is put in bridge `br-guest`. -* Interface `en3` is unchanged, stays in bridge `br-guest`. - -Running the function `$BridgePortTo` with parameter `extra` applies another -configuration: - - $BridgePortTo extra; - -* Interfaces `en1` and `en2` are unchanged. -* Interface `en3` is put in bridge `br-intern`. - -See also --------- - -* [Wait for global functions und modules](../global-wait.md) -* [Manage VLANs on bridge ports](bridge-port-vlan.md) - ---- -[⬅️ Go back to main README](../../README.md) -[⬆️ Go back to top](#top) diff --git a/doc/mod/bridge-port-vlan.md b/doc/mod/bridge-port-vlan.md deleted file mode 100644 index cf29199..0000000 --- a/doc/mod/bridge-port-vlan.md +++ /dev/null @@ -1,92 +0,0 @@ -Manage VLANs on bridge ports -============================ - -[](https://github.com/eworm-de/routeros-scripts/stargazers) -[](https://github.com/eworm-de/routeros-scripts/network) -[](https://github.com/eworm-de/routeros-scripts/watchers) -[](https://mikrotik.com/download/changelogs/) -[](https://t.me/routeros_scripts) -[](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J) - -[⬅️ Go back to main README](../../README.md) - -> ℹ️️ **Info**: This module can not be used on its own but requires the base -> installation. See [main README](../../README.md) for details. - -Description ------------ - -This module and its function are supposed to handle VLANs on bridge ports. - -Requirements and installation ------------------------------ - -Just install the module: - - $ScriptInstallUpdate mod/bridge-port-vlan; - -Configuration -------------- - -Using named VLANs you have to add comments in bridge vlan menu: - - /interface/bridge/vlan/add bridge=bridge comment=intern tagged=br-local vlan-ids=10; - /interface/bridge/vlan/add bridge=bridge comment=geust tagged=br-local vlan-ids=20; - /interface/bridge/vlan/add bridge=bridge comment=extra tagged=br-local vlan-ids=30; - -The configuration goes to ports' comments (`/interface/bridge/port`). - - /interface/bridge/port/add bridge=bridge comment="default=dhcp-client, alt=guest" disabled=yes interface=en1; - /interface/bridge/port/add bridge=bridge comment="default=intern, alt=guest, extra=30" interface=en2; - /interface/bridge/port/add bridge=bridge comment="default=guest, extra=extra" interface=en3; - -Also dhcp client can be handled: - - /ip/dhcp-client/add comment="toggle with bridge port" disabled=no interface=en1; - -Add a scheduler to start with default setup on system startup: - - $ScriptInstallUpdate global-wait; - /system/scheduler/add name=bridge-port-vlan on-event="/system/script/run global-wait; :global BridgePortVlan; \$BridgePortVlan default;" start-time=startup; - -Usage and invocation --------------------- - -The usage examples show what happens with the configuration from above. - -Running the function `$BridgePortVlan` with parameter `default` applies all -configuration given with `default=`: - - $BridgePortVlan default; - -For the three interfaces we get this configuration: - -* The special value `dhcp-client` enables the dhcp client for interface `en1`. The bridge port entry is disabled. -* Primary VLAN `intern` (ID `10`) is configured on `en2`. -* Primary VLAN `guest` (ID `20`) is configured on `en3`. - -Running the function `$BridgePortVlan` with parameter `alt` applies all -configuration given with `alt=`: - - $BridgePortVlan alt; - -* Primary VLAN `guest` (ID `20`) is configured on `en1`, dhcp client for the interface is disabled. -* Primary VLAN `guest` (ID `20`) is configured on `en2`. -* Interface `en3` is unchanged, primary VLAN `guest` (ID `20`) is unchanged. - -Running the function `$BridgePortVlan` with parameter `extra` applies another -configuration: - -* Interface `en1` is unchanged. -* Primary VLAN `extra` (via its ID `30`) is configured on `en2`. -* Primary VLAN `extra` (ID `30`) is configured on `en3`. - -See also --------- - -* [Wait for global functions und modules](../global-wait.md) -* [Manage ports in bridge](bridge-port-to.md) - ---- -[⬅️ Go back to main README](../../README.md) -[⬆️ Go back to top](#top) diff --git a/doc/mod/inspectvar.d/inspectvar.avif b/doc/mod/inspectvar.d/inspectvar.avif deleted file mode 100644 index f1da1d4..0000000 Binary files a/doc/mod/inspectvar.d/inspectvar.avif and /dev/null differ diff --git a/doc/mod/inspectvar.md b/doc/mod/inspectvar.md deleted file mode 100644 index 7daba15..0000000 --- a/doc/mod/inspectvar.md +++ /dev/null @@ -1,40 +0,0 @@ -Inspect variables -================= - -[](https://github.com/eworm-de/routeros-scripts/stargazers) -[](https://github.com/eworm-de/routeros-scripts/network) -[](https://github.com/eworm-de/routeros-scripts/watchers) -[](https://mikrotik.com/download/changelogs/) -[](https://t.me/routeros_scripts) -[](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J) - -[⬅️ Go back to main README](../../README.md) - -> ℹ️️ **Info**: This module can not be used on its own but requires the base -> installation. See [main README](../../README.md) for details. - -Description ------------ - -RouterOS handles not just scalar variables, but also arrays - even nested. -This module adds a function to inspect variables. - -Requirements and installation ------------------------------ - -Just install the module: - - $ScriptInstallUpdate mod/inspectvar; - -Usage and invocation --------------------- - -Call the function `$InspectVar` with a variable as parameter: - - $InspectVar $ModeButton; - - - ---- -[⬅️ Go back to main README](../../README.md) -[⬆️ Go back to top](#top) diff --git a/doc/mod/ipcalc.d/ipcalc.avif b/doc/mod/ipcalc.d/ipcalc.avif deleted file mode 100644 index fe726e8..0000000 Binary files a/doc/mod/ipcalc.d/ipcalc.avif and /dev/null differ diff --git a/doc/mod/ipcalc.d/ipcalcreturn.avif b/doc/mod/ipcalc.d/ipcalcreturn.avif deleted file mode 100644 index 5e4dd57..0000000 Binary files a/doc/mod/ipcalc.d/ipcalcreturn.avif and /dev/null differ diff --git a/doc/mod/ipcalc.md b/doc/mod/ipcalc.md deleted file mode 100644 index c07853e..0000000 --- a/doc/mod/ipcalc.md +++ /dev/null @@ -1,60 +0,0 @@ -IP address calculation -====================== - -[](https://github.com/eworm-de/routeros-scripts/stargazers) -[](https://github.com/eworm-de/routeros-scripts/network) -[](https://github.com/eworm-de/routeros-scripts/watchers) -[](https://mikrotik.com/download/changelogs/) -[](https://t.me/routeros_scripts) -[](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J) - -[⬅️ Go back to main README](../../README.md) - -> ℹ️️ **Info**: This module can not be used on its own but requires the base -> installation. See [main README](../../README.md) for details. - -Description ------------ - -This module adds functions for IP address calculation. - -Requirements and installation ------------------------------ - -Just install the module: - - $ScriptInstallUpdate mod/ipcalc; - -Usage and invocation --------------------- - -### IPCalc - -The function `$IPCalc` prints information to terminal, including: - -* address -* netmask -* network in CIDR notation -* minimum host address -* maximum host address -* broadcast address - -It expects an IP address in CIDR notation as argument. - - $IPCalc 192.168.88.1/24; - - - -### IPCalcReturn - -The function `$IPCalcReturn` expects an IP address in CIDR notation as -argument as well. But it does not print to terminal, instead it returns -the information in a named array. - - :put ([ $IPCalcReturn 192.168.88.1/24 ]->"broadcast"); - - - ---- -[⬅️ Go back to main README](../../README.md) -[⬆️ Go back to top](#top) diff --git a/doc/mod/notification-email.md b/doc/mod/notification-email.md deleted file mode 100644 index 34d1c09..0000000 --- a/doc/mod/notification-email.md +++ /dev/null @@ -1,88 +0,0 @@ -Send notifications via e-mail -============================= - -[](https://github.com/eworm-de/routeros-scripts/stargazers) -[](https://github.com/eworm-de/routeros-scripts/network) -[](https://github.com/eworm-de/routeros-scripts/watchers) -[](https://mikrotik.com/download/changelogs/) -[](https://t.me/routeros_scripts) -[](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J) - -[⬅️ Go back to main README](../../README.md) - -> ℹ️️ **Info**: This module can not be used on its own but requires the base -> installation. See [main README](../../README.md) for details. - -Description ------------ - -This module adds support for sending notifications via e-mail. A queue is -used to make sure notifications are not lost on failure but sent later. - -Requirements and installation ------------------------------ - -Just install the module: - - $ScriptInstallUpdate mod/notification-email; - -Also you need a valid e-mail account with smtp login credentials. - -Configuration -------------- - -Set up your device's -[e-mail settings](https://wiki.mikrotik.com/wiki/Manual:Tools/email). -Also make sure the device has correct time configured, best is to set up -the ntp client. - -Then edit `global-config-overlay`, add `EmailGeneralTo` with a valid -recipient address. Finally reload the configuration. - -> ℹ️ **Info**: Copy relevant configuration from -> [`global-config`](../../global-config.rsc) (the one without `-overlay`) to -> your local `global-config-overlay` and modify it to your specific needs. - -### Sending to several recipients - -Sending notifications to several recipients is possible as well. Add -`EmailGeneralCc` on top, which can have a single mail address or a comma -separated list. - -Usage and invocation --------------------- - -There's nothing special to do. Every script or function sending a notification -will now send it to your e-mail account. - -But of course you can use the function to send notifications directly. Give -it a try: - - $SendEMail "Subject..." "Body..."; - -Alternatively this sends a notification with all available and configured -methods: - - $SendNotification "Subject..." "Body..."; - -To use the functions in your own scripts you have to declare them first. -Place this before you call them: - - :global SendEMail; - :global SendNotification; - -In case there is a situation when the queue needs to be purged there is a -function available: - - $PurgeEMailQueue; - -See also --------- - -* [Send notifications via Matrix](notification-matrix.md) -* [Send notifications via Ntfy](notification-ntfy.md) -* [Send notifications via Telegram](notification-telegram.md) - ---- -[⬅️ Go back to main README](../../README.md) -[⬆️ Go back to top](#top) diff --git a/doc/mod/notification-matrix.d/01-authenticate.avif b/doc/mod/notification-matrix.d/01-authenticate.avif deleted file mode 100644 index b897943..0000000 Binary files a/doc/mod/notification-matrix.d/01-authenticate.avif and /dev/null differ diff --git a/doc/mod/notification-matrix.d/02-join-room.avif b/doc/mod/notification-matrix.d/02-join-room.avif deleted file mode 100644 index ad99ffd..0000000 Binary files a/doc/mod/notification-matrix.d/02-join-room.avif and /dev/null differ diff --git a/doc/mod/notification-matrix.md b/doc/mod/notification-matrix.md deleted file mode 100644 index 89c1b01..0000000 --- a/doc/mod/notification-matrix.md +++ /dev/null @@ -1,139 +0,0 @@ -Send notifications via Matrix -============================= - -[](https://github.com/eworm-de/routeros-scripts/stargazers) -[](https://github.com/eworm-de/routeros-scripts/network) -[](https://github.com/eworm-de/routeros-scripts/watchers) -[](https://mikrotik.com/download/changelogs/) -[](https://t.me/routeros_scripts) -[](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J) - -[⬅️ Go back to main README](../../README.md) - -> ℹ️️ **Info**: This module can not be used on its own but requires the base -> installation. See [main README](../../README.md) for details. - -Description ------------ - -This module adds support for sending notifications via -[Matrix](https://matrix.org/) via client server api. A queue is used to -make sure notifications are not lost on failure but sent later. - -Requirements and installation ------------------------------ - -Just install the module: - - $ScriptInstallUpdate mod/notification-matrix; - -Also install a Matrix client on at least one of your mobile and/or desktop -devices. Create and setup an account there, we will reference that as -"*general account*" later. - -Configuration -------------- - -Edit `global-config-overlay`, add `MatrixHomeServer`, `MatrixAccessToken` and -`MatrixRoom` - see below on hints how to retrieve this information. Then -reload the configuration. - -> ℹ️ **Info**: Copy relevant configuration from -> [`global-config`](../../global-config.rsc) (the one without `-overlay`) to -> your local `global-config-overlay` and modify it to your specific needs. - -The Matrix server is connected via encrypted https, and certificate -verification is applied. So make sure you have the certificate chain for -your server in device's certificate store. - -The example below is for `matrix.org`, which uses a trust chain from *Google -Trust Services*. Run this to import the required certificate: - - $CertificateAvailable "GTS Root R4"; - -Replace the CA certificate name with what ever is needed for your server. -You may want to find the -[certificate name from browser](../../CERTIFICATES.md). - -### From other device - -If you have setup your Matrix *notification account* before just reuse that. -Copy the relevant configuration to the device to be configured. - -### Setup new account - -As there is no privilege separation you should create a dedicated account -for use with these scripts, in addition to your *general account*. -We will reference that as "*notification account*" in the following steps. - -#### Authenticate - -Matrix user accounts are identified by a unique user id in the form of -`@localpart:domain`. Use that and your password to generate an access token -and write first part of the configuration: - - $SetupMatrixAuthenticate "@example:matrix.org" "v3ry-s3cr3t"; - - - -The configuration is written to a new configuration snippet -`global-config-overlay.d/mod/notification-matrix`. - -#### Join Room - -Every Matix chat is a room, so we have to create one. Do that with your -*general account*, this makes sure your *general account* is the room owner. -Then join the room and invite the *notification account* by its user id -"*@example:matrix.org*". -Look up the *room id* within the Matrix client, it should read like -"*!WUcxpSjKyxSGelouhA:matrix.org*" (starting with an exclamation mark and -ending with the domain). - -Finally make the *notification account* join into the room by accepting -the invite. - - $SetupMatrixJoinRoom "!WUcxpSjKyxSGelouhA:matrix.org"; - - - -The configuration is appended to the configuration snippet -`global-config-overlay.d/mod/notification-matrix`. - -Usage and invocation --------------------- - -There's nothing special to do. Every script or function sending a notification -will now send it to your Matrix account. - -But of course you can use the function to send notifications directly. Give -it a try: - - $SendMatrix "Subject..." "Body..."; - -Alternatively this sends a notification with all available and configured -methods: - - $SendNotification "Subject..." "Body..."; - -To use the functions in your own scripts you have to declare them first. -Place this before you call them: - - :global SendMatrix; - :global SendNotification; - -In case there is a situation when the queue needs to be purged there is a -function available: - - $PurgeMatrixQueue; - -See also --------- - -* [Certificate name from browser](../../CERTIFICATES.md) -* [Send notifications via e-mail](notification-email.md) -* [Send notifications via Ntfy](notification-ntfy.md) -* [Send notifications via Telegram](notification-telegram.md) - ---- -[⬅️ Go back to main README](../../README.md) -[⬆️ Go back to top](#top) diff --git a/doc/mod/notification-ntfy.md b/doc/mod/notification-ntfy.md deleted file mode 100644 index 51756ac..0000000 --- a/doc/mod/notification-ntfy.md +++ /dev/null @@ -1,98 +0,0 @@ -Send notifications via Ntfy -=========================== - -[](https://github.com/eworm-de/routeros-scripts/stargazers) -[](https://github.com/eworm-de/routeros-scripts/network) -[](https://github.com/eworm-de/routeros-scripts/watchers) -[](https://mikrotik.com/download/changelogs/) -[](https://t.me/routeros_scripts) -[](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J) - -[⬅️ Go back to main README](../../README.md) - -> ℹ️️ **Info**: This module can not be used on its own but requires the base -> installation. See [main README](../../README.md) for details. - -Description ------------ - -This module adds support for sending notifications via -[Ntfy](https://ntfy.sh/). A queue is used to make sure -notifications are not lost on failure but sent later. - -Requirements and installation ------------------------------ - -Just install the module: - - $ScriptInstallUpdate mod/notification-ntfy; - -Also install the Ntfy app on your mobile device or use the -[web app](https://ntfy.sh/app) in a browser of your choice. - -Configuration -------------- - -Creating an account is not required. Just choose a topic and you are good -to go. - -> ⚠️ **Warning**: If you use ntfy without sign-up, the topic is essentially -> a password, so pick something that's not easily guessable. - -Edit `global-config-overlay`, add `NtfyServer` (leave it unchanged, unless -you are self-hosting the service) and `NtfyTopic` with your choosen topic. -Then reload the configuration. - -> ℹ️ **Info**: Copy relevant configuration from -> [`global-config`](../../global-config.rsc) (the one without `-overlay`) to -> your local `global-config-overlay` and modify it to your specific needs. - -Using a paid account or running a server on-premises allows to add additional -basic authentication. Configure `NtfyServerUser` and `NtfyServerPass` for this. -Even authentication via access token is possible, adding it as password with -a blank username. - -Also available is `NtfyServerToken` to add a bearer token for authentication. - -For a custom service installing an additional certificate may be required. -You may want to install that certificate manually, after finding the -[certificate name from browser](../../CERTIFICATES.md). - -Usage and invocation --------------------- - -There's nothing special to do. Every script or function sending a notification -will now send it to your Ntfy topic. - -But of course you can use the function to send notifications directly. Give -it a try: - - $SendNtfy "Subject..." "Body..."; - -Alternatively this sends a notification with all available and configured -methods: - - $SendNotification "Subject..." "Body..."; - -To use the functions in your own scripts you have to declare them first. -Place this before you call them: - - :global SendNtfy; - :global SendNotification; - -In case there is a situation when the queue needs to be purged there is a -function available: - - $PurgeNtfyQueue; - -See also --------- - -* [Certificate name from browser](../../CERTIFICATES.md) -* [Send notifications via e-mail](notification-email.md) -* [Send notifications via Matrix](notification-matrix.md) -* [Send notifications via Telegram](notification-telegram.md) - ---- -[⬅️ Go back to main README](../../README.md) -[⬆️ Go back to top](#top) diff --git a/doc/mod/notification-telegram.d/getchatid.avif b/doc/mod/notification-telegram.d/getchatid.avif deleted file mode 100644 index 7792969..0000000 Binary files a/doc/mod/notification-telegram.d/getchatid.avif and /dev/null differ diff --git a/doc/mod/notification-telegram.d/newbot.avif b/doc/mod/notification-telegram.d/newbot.avif deleted file mode 100644 index 1fc7355..0000000 Binary files a/doc/mod/notification-telegram.d/newbot.avif and /dev/null differ diff --git a/doc/mod/notification-telegram.d/setuserpic.avif b/doc/mod/notification-telegram.d/setuserpic.avif deleted file mode 100644 index 2017d20..0000000 Binary files a/doc/mod/notification-telegram.d/setuserpic.avif and /dev/null differ diff --git a/doc/mod/notification-telegram.md b/doc/mod/notification-telegram.md deleted file mode 100644 index 2d00116..0000000 --- a/doc/mod/notification-telegram.md +++ /dev/null @@ -1,123 +0,0 @@ -Send notifications via Telegram -=============================== - -[](https://github.com/eworm-de/routeros-scripts/stargazers) -[](https://github.com/eworm-de/routeros-scripts/network) -[](https://github.com/eworm-de/routeros-scripts/watchers) -[](https://mikrotik.com/download/changelogs/) -[](https://t.me/routeros_scripts) -[](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J) - -[⬅️ Go back to main README](../../README.md) - -> ℹ️️ **Info**: This module can not be used on its own but requires the base -> installation. See [main README](../../README.md) for details. - -Description ------------ - -This module adds support for sending notifications via -[Telegram](https://telegram.org/) via bot api. A queue is used to make sure -notifications are not lost on failure but sent later. - -Requirements and installation ------------------------------ - -Just install the module: - - $ScriptInstallUpdate mod/notification-telegram; - -Also install Telegram on at least one of your mobile and/or desktop devices -and create an account. - -Configuration -------------- - -Open Telegram, then start a chat with [BotFather](https://t.me/BotFather) and -create your own bot: - - - -Set that token from *BotFather* (use your own!) to `TelegramTokenId`, for -now just temporarily: - - :set TelegramTokenId "5214364459:AAHLwf1o7ybbKDo6pY24Kd2bZ5rjCakDXTc"; - -Now open a chat with your bot and start it by clicking the `START` button, -then send your first message. Any text will do. On your device run -`$GetTelegramChatId` to retrieve the chat id: - - $GetTelegramChatId; - - - -Finally edit `global-config-overlay`, add `TelegramTokenId` with the token -from *BotFather* and `TelegramChatId` with your retrieved chat id. Then -reload the configuration. - -> ℹ️ **Info**: Copy relevant configuration from -> [`global-config`](../../global-config.rsc) (the one without `-overlay`) to -> your local `global-config-overlay` and modify it to your specific needs. - -### Notifications to a group - -Sending notifications to a group is possible as well. Add your bot to a group -and make it an admin (required for read access!) and send a message and run -`$GetTelegramChatId` again. Then use that chat id (which starts with a dash) -for `TelegramChatId`. - -Groups can enable the `Topics` feature. Use `TelegramThreadId` to send to a -specific topic in a group. - -Usage and invocation --------------------- - -There's nothing special to do. Every script or function sending a notification -will now send it to your Telegram account. - -But of course you can use the function to send notifications directly. Give -it a try: - - $SendTelegram "Subject..." "Body..."; - -Alternatively this sends a notification with all available and configured -methods: - - $SendNotification "Subject..." "Body..."; - -To use the functions in your own scripts you have to declare them first. -Place this before you call them: - - :global SendTelegram; - :global SendNotification; - -In case there is a situation when the queue needs to be purged there is a -function available: - - $PurgeTelegramQueue; - -Tips & Tricks -------------- - -### Set a profile photo - -You can use a profile photo for your bot to make it recognizable. Open the -chat with [BotFather](https://t.me/BotFather) and set it there. - - - -Have a look at my -[RouterOS-Scripts Logo Color Changer](https://git.eworm.de/cgit/routeros-scripts/plain/contrib/logo-color.html) -to create a colored version of this scripts' logo. - -See also --------- - -* [Chat with your router and send commands via Telegram bot](../telegram-chat.md) -* [Send notifications via e-mail](notification-email.md) -* [Send notifications via Matrix](notification-matrix.md) -* [Send notifications via Ntfy](notification-ntfy.md) - ---- -[⬅️ Go back to main README](../../README.md) -[⬆️ Go back to top](#top) diff --git a/doc/mod/scriptrunonce.d/hello-world.rsc b/doc/mod/scriptrunonce.d/hello-world.rsc deleted file mode 100644 index 6404781..0000000 --- a/doc/mod/scriptrunonce.d/hello-world.rsc +++ /dev/null @@ -1,3 +0,0 @@ -#!rsc by RouterOS - -:put ("Hello World from " . [ /system/identity/get name ] . "!"); diff --git a/doc/mod/scriptrunonce.d/scriptrunonce.avif b/doc/mod/scriptrunonce.d/scriptrunonce.avif deleted file mode 100644 index 27ccd41..0000000 Binary files a/doc/mod/scriptrunonce.d/scriptrunonce.avif and /dev/null differ diff --git a/doc/mod/scriptrunonce.md b/doc/mod/scriptrunonce.md deleted file mode 100644 index 955d12e..0000000 --- a/doc/mod/scriptrunonce.md +++ /dev/null @@ -1,59 +0,0 @@ -Download script and run it once -=============================== - -[](https://github.com/eworm-de/routeros-scripts/stargazers) -[](https://github.com/eworm-de/routeros-scripts/network) -[](https://github.com/eworm-de/routeros-scripts/watchers) -[](https://mikrotik.com/download/changelogs/) -[](https://t.me/routeros_scripts) -[](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J) - -[⬅️ Go back to main README](../../README.md) - -> ℹ️️ **Info**: This module can not be used on its own but requires the base -> installation. See [main README](../../README.md) for details. - -Description ------------ - -This module adds a function that downloads a script, checks for syntax -validity and runs it once. - -Requirements and installation ------------------------------ - -Just install the module: - - $ScriptInstallUpdate mod/scriptrunonce; - -Configuration -------------- - -The optional configuration goes to `global-config-overlay`. - -* `ScriptRunOnceBaseUrl`: base url, prepended to parameter -* `ScriptRunOnceUrlSuffix`: url suffix, appended to parameter - -> ℹ️ **Info**: Copy relevant configuration from -> [`global-config`](../../global-config.rsc) (the one without `-overlay`) to -> your local `global-config-overlay` and modify it to your specific needs. - -If the parameter passed to the function is not a complete URL (starting -with protocol `ftp://`, `http://`, `https://` or `sftp://`) the base-url is -prepended, and file extension `.rsc` and url-suffix are appended. - -Usage and invocation --------------------- - -The function `$ScriptRunOnce` expects an URL (or name if -`ScriptRunOnceBaseUrl` is given) pointing to a script as parameter. - - $ScriptRunOnce https://git.eworm.de/cgit/routeros-scripts/plain/doc/mod/scriptrunonce.d/hello-world.rsc; - - - -Giving multiple scripts is possible, separated by comma. - ---- -[⬅️ Go back to main README](../../README.md) -[⬆️ Go back to top](#top) diff --git a/doc/mod/ssh-keys-import.md b/doc/mod/ssh-keys-import.md deleted file mode 100644 index 344f4bc..0000000 --- a/doc/mod/ssh-keys-import.md +++ /dev/null @@ -1,69 +0,0 @@ -Import ssh keys for public key authentication -============================================= - -[](https://github.com/eworm-de/routeros-scripts/stargazers) -[](https://github.com/eworm-de/routeros-scripts/network) -[](https://github.com/eworm-de/routeros-scripts/watchers) -[](https://mikrotik.com/download/changelogs/) -[](https://t.me/routeros_scripts) -[](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J) - -[⬅️ Go back to main README](../../README.md) - -> ℹ️️ **Info**: This module can not be used on its own but requires the base -> installation. See [main README](../../README.md) for details. - -Description ------------ - -RouterOS supports ssh login with public key authentication. The functions -in this module help importing the keys. - -Requirements and installation ------------------------------ - -Just install the module: - - $ScriptInstallUpdate mod/ssh-keys-import; - -Usage and invocation --------------------- - -### Import single key from terminal - -Call the function `$SSHKeysImport` with key and user as parameter to -import that key: - - $SSHKeysImport "ssh-ed25519 AAAAC3Nza...ZVugJT user" admin; - $SSHKeysImport "ssh-rsa AAAAB3Nza...QYZk8= user" admin; - -The third part of the key (`user` in this example) is inherited as -`key-owner` in RouterOS. Also the `MD5` fingerprint is recorded, this helps -to audit and verify the available keys. - -> ℹ️️ **Info**: Use `ssh-keygen` to show a fingerprint of an existing public -> key file: `ssh-keygen -l -E md5 -f ~/.ssh/id_ed25519.pub` - -### Import several keys from file - -The functions `$SSHKeysImportFile` can read an `authorized_keys`-style file -and import all the keys. The user given to the function can be overwritting -from comments in the file. Create a file `keys.pub` with this content: - -``` -ssh-ed25519 AAAAC3Nza...3OcN8A user@client -ssh-rsa AAAAB3Nza...ozyts= worker@station -# user=example -ssh-rsa AAAAB3Nza...GXQVk= person@host -``` - -Then import it with: - - $SSHKeysImportFile keys.pub admin; - -This will import the first two keys for user `admin` (as given to function) -and the third one for user `example` (as defined in comment). - ---- -[⬅️ Go back to main README](../../README.md) -[⬆️ Go back to top](#top) diff --git a/doc/mode-button.md b/doc/mode-button.md deleted file mode 100644 index be15bc9..0000000 --- a/doc/mode-button.md +++ /dev/null @@ -1,75 +0,0 @@ -Mode button with multiple presses -================================= - -[](https://github.com/eworm-de/routeros-scripts/stargazers) -[](https://github.com/eworm-de/routeros-scripts/network) -[](https://github.com/eworm-de/routeros-scripts/watchers) -[](https://mikrotik.com/download/changelogs/) -[](https://t.me/routeros_scripts) -[](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J) - -[⬅️ Go back to main README](../README.md) - -> ℹ️ **Info**: This script can not be used on its own but requires the base -> installation. See [main README](../README.md) for details. - -Description ------------ - -This script extend the functionality of mode button. Instead of just one -you can trigger several actions by pressing the mode button several times. - -The hardware needs to have a mode button, see -`/system/routerboard/mode-button`. Starting with RouterOS 6.47beta60 you -can configure the reset button to act the same, see -`/system/routerboard/reset-button`. - -Copy this code to terminal to check: - -``` -:if ([ :len [ /system/routerboard/mode-button/print as-value ] ] > 0) do={ - :put "Mode button is supported."; -} else={ - :if ([ :len [ /system/routerboard/reset-button/print as-value ] ] > 0) do={ - :put "Mode button is not supported, but reset button is."; - } else={ - :put "Neither mode button nor reset button is supported."; - } -} -``` - -Requirements and installation ------------------------------ - -Just install the script: - - $ScriptInstallUpdate mode-button; - -Then configure the mode button to run `mode-button`: - - /system/routerboard/mode-button/set enabled=yes on-event="/system/script/run mode-button;"; - -To use the reset button instead: - - /system/routerboard/reset-button/set enabled=yes on-event="/system/script/run mode-button;"; - -Configuration -------------- - -The configuration goes to `global-config-overlay`, these are the parameters: - -* `ModeButton`: an array with defined actions -* `ModeButtonLED`: led to give visual feedback, `type` must be `on` or `off` - -> ℹ️ **Info**: Copy relevant configuration from -> [`global-config`](../global-config.rsc) (the one without `-overlay`) to -> your local `global-config-overlay` and modify it to your specific needs. - -Usage and invocation --------------------- - -Press the mode button. 😜 - ---- -[⬅️ Go back to main README](../README.md) -[⬆️ Go back to top](#top) diff --git a/doc/netwatch-dns.md b/doc/netwatch-dns.md deleted file mode 100644 index 0d94918..0000000 --- a/doc/netwatch-dns.md +++ /dev/null @@ -1,99 +0,0 @@ -Manage DNS and DoH servers from netwatch -======================================== - -[](https://github.com/eworm-de/routeros-scripts/stargazers) -[](https://github.com/eworm-de/routeros-scripts/network) -[](https://github.com/eworm-de/routeros-scripts/watchers) -[](https://mikrotik.com/download/changelogs/) -[](https://t.me/routeros_scripts) -[](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J) - -[⬅️ Go back to main README](../README.md) - -> ℹ️ **Info**: This script can not be used on its own but requires the base -> installation. See [main README](../README.md) for details. - -Description ------------ - -This script reads server state from netwatch and manages used DNS and -DoH (DNS over HTTPS) servers. - -Requirements and installation ------------------------------ - -Just install the script: - - $ScriptInstallUpdate netwatch-dns; - -Then add a scheduler to run it periodically: - - /system/scheduler/add interval=1m name=netwatch-dns on-event="/system/script/run netwatch-dns;" start-time=startup; - -Configuration -------------- - -The DNS and DoH servers to be checked have to be added to netwatch with -specific comment: - - /tool/netwatch/add comment="doh" host=1.1.1.1; - /tool/netwatch/add comment="dns" host=8.8.8.8; - /tool/netwatch/add comment="doh, dns" host=9.9.9.9; - -This will configure *cloudflare-dns* for DoH (`https://1.1.1.1/dnsquery`), and -*google-dns* and *quad-nine* for regular DNS (`8.8.8.8,9.9.9.9`) if up. -If *cloudflare-dns* is down the script will fall back to *quad-nine* for DoH. - -Giving a specific query url for DoH is possible: - - /tool/netwatch/add comment="doh, doh-url=https://dns.nextdns.io/dns-query" host=199.247.16.158; - -Note that using a name in DoH url may introduce a chicken-and-egg issue! - -Adding a static DNS record has the same result for the url, but always -resolves to the same address. - - /ip/dns/static/add name="cloudflare-dns.com" address=1.1.1.1; - /tool/netwatch/add comment="doh" host=1.1.1.1; - -Be aware that you have to keep the ip address in sync with real world -manually! - -Importing a certificate automatically is possible. You may want to find the -[certificate name from browser](../CERTIFICATES.md). - - /tool/netwatch/add comment="doh, doh-cert=DigiCert Global Root G2" host=1.1.1.1; - /tool/netwatch/add comment="doh, doh-cert=DigiCert Global Root G3" host=9.9.9.9; - /tool/netwatch/add comment="doh, doh-cert=GTS Root R1" host=8.8.8.8; - -> ⚠️ **Warning**: Combining these techniques can cause some confusion and -> troubles! Chances are that a service uses different certificates based -> on indicated server name. - -Sometimes using just one specific (possibly internal) DNS server may be -desired, with fallback in case it fails. This is possible as well: - - /tool/netwatch/add comment="dns" host=10.0.0.10; - /tool/netwatch/add comment="dns-fallback" host=1.1.1.1; - -Tips & Tricks -------------- - -### Use in combination with notifications - -Netwatch entries can be created to work with both - this script and -[netwatch-notify](netwatch-notify.md). Just give options for both: - - /tool/netwatch/add comment="doh, notify, name=cloudflare-dns" host=1.1.1.1; - -Also this allows to update host address, see option `resolve`. - -See also --------- - -* [Certificate name from browser](../CERTIFICATES.md) -* [Notify on host up and down](netwatch-notify.md) - ---- -[⬅️ Go back to main README](../README.md) -[⬆️ Go back to top](#top) diff --git a/doc/netwatch-notify.d/notification-01-down.avif b/doc/netwatch-notify.d/notification-01-down.avif deleted file mode 100644 index 894fb23..0000000 Binary files a/doc/netwatch-notify.d/notification-01-down.avif and /dev/null differ diff --git a/doc/netwatch-notify.d/notification-02-up.avif b/doc/netwatch-notify.d/notification-02-up.avif deleted file mode 100644 index 9021a93..0000000 Binary files a/doc/netwatch-notify.d/notification-02-up.avif and /dev/null differ diff --git a/doc/netwatch-notify.md b/doc/netwatch-notify.md deleted file mode 100644 index 81adfe9..0000000 --- a/doc/netwatch-notify.md +++ /dev/null @@ -1,194 +0,0 @@ -Notify on host up and down -========================== - -[](https://github.com/eworm-de/routeros-scripts/stargazers) -[](https://github.com/eworm-de/routeros-scripts/network) -[](https://github.com/eworm-de/routeros-scripts/watchers) -[](https://mikrotik.com/download/changelogs/) -[](https://t.me/routeros_scripts) -[](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J) - -[⬅️ Go back to main README](../README.md) - -> ℹ️ **Info**: This script can not be used on its own but requires the base -> installation. See [main README](../README.md) for details. - -Description ------------ - -This script sends notifications about host UP and DOWN events. In comparison -to just netwatch (`/tool/netwatch`) and its `up-script` and `down-script` -this script implements a simple state machine and dependency model. Host -down events are triggered only if the host is down for several checks and -optional parent host is not down to avoid false alerts. - -### Sample notifications - - - - -Requirements and installation ------------------------------ - -Just install the script: - - $ScriptInstallUpdate netwatch-notify; - -Then add a scheduler to run it periodically: - - /system/scheduler/add interval=1m name=netwatch-notify on-event="/system/script/run netwatch-notify;" start-time=startup; - -Configuration -------------- - -The hosts to be checked have to be added to netwatch with specific comment: - - /tool/netwatch/add comment="notify, name=example.com" host=[ :resolve "example.com" ]; - -Also notification settings are required for -[e-mail](mod/notification-email.md), -[matrix](mod/notification-matrix.md), -[ntfy](mod/notification-ntfy.md) and/or -[telegram](mod/notification-telegram.md). - -### Hooks - -It is possible to run an up hook command (`up-hook`) or down hook command -(`down-hook`) when a notification is triggered. This has to be added in -comment, note that some characters need extra escaping: - - /tool/netwatch/add comment=("notify, name=device, down-hook=/interface/ethernet \\{ disable \\\"en2\\\"; enable \\\"en2\\\"; \\}") host=10.0.0.20; - -Also there is a `pre-down-hook` that fires at two thirds of failed checks -required for the notification. The idea is to fix the issue before a -notification is sent. - -Getting the escaping right may be troublesome. Please consider adding a -script in `/system/script`, then running that from hook. - -### Count threshold - -The count threshold (default is 5 checks) is configurable as well: - - /tool/netwatch/add comment="notify, name=example.com, count=10" host=104.18.144.11; - -### Parents & dependencies - -If the host is behind another checked host add a dependency, this will -suppress notification if the parent host is down: - - /tool/netwatch/add comment="notify, name=gateway" host=93.184.216.1; - /tool/netwatch/add comment="notify, name=example.com, parent=gateway" host=93.184.216.34; - -Note that every configured parent in a chain increases the check count -threshold by one. - -### Update from DNS - -The host address can be updated dynamically. Give extra parameter `resolve` -with a resolvable name: - - /tool/netwatch/add comment="notify, name=example.com, resolve=example.com" host=0.0; - -This supports multiple A records for a name just fine, even a CNAME -to those. An update happens only if no more record with the configured host -address is found. - -The address family is preserved, so if you want AAAA records (for IPv6) -use this: - - /tool/netwatch/add comment="notify, name=example.com, resolve=example.com" host=::; - -### No notification on host down - -Also suppressing the notification on host down is possible with parameter -`no-down-notification`. This may be desired for devices that are usually -powered off, but accessibility is of interest. - - /tool/netwatch/add comment="notify, name=printer, no-down-notification" host=10.0.0.30; - -Go and get your coffee ☕️ before sending the print job. - -### No log on failed resolve - -A message is writting to log after three failed attemts to resolve a host. -However this can cause some noise for hosts that are expected to have -failures, for example when the name is dynamically added by -[`dhcp-to-dns`](dhcp-to-dns.md). This can be suppressed: - - /tool/netwatch/add comment="notify, name=client, resolve=client.dhcp.example.com, no-resolve-fail" host=10.0.0.0; - -### Add a note in notification - -For some extra information it is possible to add a text note. This is -included verbatim into the notification. - - /tool/netwatch/add comment="notify, name=example, note=Do not touch!" host=10.0.0.31; - -### Add a link in notification - -It is possible to add a link in notification, that is added below the -formatted notification text. - - /tool/netwatch/add comment="notify, name=example.com, resolve=example.com, link=https://example.com/" host=0.0; - -Tips & Tricks -------------- - -### One of several hosts - -Sometimes it is sufficient if one of a number of hosts is available. You can -make `netwatch-notify` check for that by adding several items with same -`name`. Note that `count` has to be multiplied to keep the actual time. - - /tool/netwatch/add comment="notify, name=service, count=10" host=10.0.0.10; - /tool/netwatch/add comment="notify, name=service, count=10" host=10.0.0.20; - -### Checking internet connectivity - -Sometimes you can not check your gateway for internet connectivity, for -example when it does not respond to pings or has a dynamic address. You could -check `1.1.1.1` (Cloudflare DNS), `9.9.9.9` (Quad-nine DNS), `8.8.8.8` -(Google DNS) or any other reliable address that indicates internet -connectivity. - - /tool/netwatch/add comment="notify, name=internet" host=1.1.1.1; - -A target like this suits well to be parent for other checks. - - /tool/netwatch/add comment="notify, name=example.com, parent=internet" host=93.184.216.34; - -### Checking specific ISP - -Having several ISPs for redundancy a failed link may go unnoticed without -proper monitoring. You can use routing-mark to monitor specific connections. -Create a route and firewall mangle rule. - - /routing/table/add fib name=via-isp1; - /ip/route/add distance=1 gateway=isp1 routing-table=via-isp1; - /ip/firewall/mangle/add action=mark-routing chain=output new-routing-mark=via-isp1 dst-address=1.0.0.1 passthrough=yes; - -Finally monitor the address with `netwatch-notify`. - - /tool/netwatch/add comment="notify, name=quad-one via isp1" host=1.0.0.1; - -Note that *all* traffic to the given address is routed that way. In case of -link failure this address is not available, so use something reliable but -non-essential. In this example the address `1.0.0.1` is used, the same service -(Cloudflare DNS) is available at `1.1.1.1`. - -### Use in combination with DNS and DoH management - -Netwatch entries can be created to work with both - this script and -[netwatch-dns](netwatch-dns.md). Just give options for both: - - /tool/netwatch/add comment="doh, notify, name=cloudflare-dns" host=1.1.1.1; - -See also --------- - -* [Manage DNS and DoH servers from netwatch](netwatch-dns.md) - ---- -[⬅️ Go back to main README](../README.md) -[⬆️ Go back to top](#top) diff --git a/doc/netwatch-syslog.md b/doc/netwatch-syslog.md deleted file mode 100644 index 6a337d4..0000000 --- a/doc/netwatch-syslog.md +++ /dev/null @@ -1,5 +0,0 @@ -This script has been dropped. Filtering in firewall is advised, which should -look something like this: - - /ip/firewall/filter/add action=reject chain=output out-interface-list=WAN port=514 protocol=udp reject-with=icmp-admin-prohibited; - /ip/firewall/filter/add action=reject chain=forward out-interface-list=WAN port=514 protocol=udp reject-with=icmp-admin-prohibited; diff --git a/doc/ospf-to-leds.md b/doc/ospf-to-leds.md deleted file mode 100644 index 3694d35..0000000 --- a/doc/ospf-to-leds.md +++ /dev/null @@ -1,44 +0,0 @@ -Visualize OSPF state via LEDs -============================= - -[](https://github.com/eworm-de/routeros-scripts/stargazers) -[](https://github.com/eworm-de/routeros-scripts/network) -[](https://github.com/eworm-de/routeros-scripts/watchers) -[](https://mikrotik.com/download/changelogs/) -[](https://t.me/routeros_scripts) -[](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J) - -[⬅️ Go back to main README](../README.md) - -> ℹ️ **Info**: This script can not be used on its own but requires the base -> installation. See [main README](../README.md) for details. - -Description ------------ - -Physical interfaces have their state LEDs, software-defined connectivity -does not. This script helps to visualize whether or not an OSPF instance -is running. - -Requirements and installation ------------------------------ - -Just install the script: - - $ScriptInstallUpdate ospf-to-leds; - -... and add a scheduler to run the script periodically: - - /system/scheduler/add interval=20s name=ospf-to-leds on-event="/system/script/run ospf-to-leds;" start-time=startup; - -Configuration -------------- - -The configuration goes to OSPF instance's comment. To visualize state for -instance `default` via LED `user-led` set this: - - /routing/ospf/instance/set default comment="ospf-to-leds, leds=user-led"; - ---- -[⬅️ Go back to main README](../README.md) -[⬆️ Go back to top](#top) diff --git a/doc/packages-update.md b/doc/packages-update.md deleted file mode 100644 index 75225fe..0000000 --- a/doc/packages-update.md +++ /dev/null @@ -1,78 +0,0 @@ -Manage system update -==================== - -[](https://github.com/eworm-de/routeros-scripts/stargazers) -[](https://github.com/eworm-de/routeros-scripts/network) -[](https://github.com/eworm-de/routeros-scripts/watchers) -[](https://mikrotik.com/download/changelogs/) -[](https://t.me/routeros_scripts) -[](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J) - -[⬅️ Go back to main README](../README.md) - -> ℹ️ **Info**: This script can not be used on its own but requires the base -> installation. See [main README](../README.md) for details. - -Description ------------ - -In rare cases RouterOS fails to properly downlaod package on update -(`/system/package/update/install`), resulting in borked system with missing -packages. This script tries to avoid this situation by doing some basic -verification. - -But it provides some extra functionality: - -* upload backup to Mikrotik cloud if [backup-cloud](backup-cloud.md) is - installed -* send backup via e-mail if [backup-email](backup-email.md) is installed -* save configuration to fallback partition if - [backup-partition](backup-partition.md) is installed -* upload backup to server if [backup-upload](backup-upload.md) is installed -* schedule reboot at night - -Requirements and installation ------------------------------ - -Just install the script: - - $ScriptInstallUpdate packages-update; - -It is automatically run by [check-routeros-update](check-routeros-update.md) -if available. - -Configuration -------------- - -The configuration goes to `global-config-overlay`, this is the only parameter: - -* `PackagesUpdateDeferReboot`: defer the reboot for night (between 3 AM - and 5 AM) - -By modifying the scheduler's `start-time` you can force the reboot at -different time. - -> ℹ️ **Info**: Copy relevant configuration from -> [`global-config`](../global-config.rsc) (the one without `-overlay`) to -> your local `global-config-overlay` and modify it to your specific needs. - -Usage and invocation --------------------- - -Alternatively run it manually: - - /system/script/run packages-update; - -See also --------- - -* [Upload backup to Mikrotik cloud](backup-cloud.md) -* [Send backup via e-mail](backup-email.md) -* [Save configuration to fallback partition](backup-partition.md) -* [Upload backup to server](backup-upload.md) -* [Notify on RouterOS update](check-routeros-update.md) -* [Automatically upgrade firmware and reboot](firmware-upgrade-reboot.md) - ---- -[⬅️ Go back to main README](../README.md) -[⬆️ Go back to top](#top) diff --git a/doc/ppp-on-up.md b/doc/ppp-on-up.md deleted file mode 100644 index 305afc1..0000000 --- a/doc/ppp-on-up.md +++ /dev/null @@ -1,44 +0,0 @@ -Run scripts on ppp connection -============================= - -[](https://github.com/eworm-de/routeros-scripts/stargazers) -[](https://github.com/eworm-de/routeros-scripts/network) -[](https://github.com/eworm-de/routeros-scripts/watchers) -[](https://mikrotik.com/download/changelogs/) -[](https://t.me/routeros_scripts) -[](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J) - -[⬅️ Go back to main README](../README.md) - -> ℹ️ **Info**: This script can not be used on its own but requires the base -> installation. See [main README](../README.md) for details. - -Description ------------ - -This script is supposed to run on established ppp connection. Currently -it does: - -* release IPv6 dhcp leases (and thus force a renew) -* run [update-tunnelbroker](update-tunnelbroker.md) - -Requirements and installation ------------------------------ - -Just install the script: - - $ScriptInstallUpdate ppp-on-up; - -... and make it the `on-up` script for ppp profile: - - /ppp/profile/set on-up=ppp-on-up [ find ]; - -See also --------- - -* [Update configuration on IPv6 prefix change](ipv6-update.md) -* [Update tunnelbroker configuration](update-tunnelbroker.md) - ---- -[⬅️ Go back to main README](../README.md) -[⬆️ Go back to top](#top) diff --git a/doc/rotate-ntp.md b/doc/rotate-ntp.md deleted file mode 100644 index 9a016a3..0000000 --- a/doc/rotate-ntp.md +++ /dev/null @@ -1,3 +0,0 @@ -This script has been dropped as the limitation does no longer exist with -RouterOS 7.x, where you can enable a ntp server and use a name for the client -at the same time. diff --git a/doc/sms-action.md b/doc/sms-action.md deleted file mode 100644 index b696c85..0000000 --- a/doc/sms-action.md +++ /dev/null @@ -1,63 +0,0 @@ -Act on received SMS -=================== - -[](https://github.com/eworm-de/routeros-scripts/stargazers) -[](https://github.com/eworm-de/routeros-scripts/network) -[](https://github.com/eworm-de/routeros-scripts/watchers) -[](https://mikrotik.com/download/changelogs/) -[](https://t.me/routeros_scripts) -[](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J) - -[⬅️ Go back to main README](../README.md) - -> ℹ️ **Info**: This script can not be used on its own but requires the base -> installation. See [main README](../README.md) for details. - -Description ------------ - -RouterOS can act on received SMS. Reboot the device from remote or do -whatever is required. - -A broadband interface with SMS support is required. - -Requirements and installation ------------------------------ - -Just install the script: - - $ScriptInstallUpdate sms-action; - -Configuration -------------- - -The configuration goes to `global-config-overlay`, this is the only parameter: - -* `SmsAction`: an array with pre-defined actions - -> ℹ️ **Info**: Copy relevant configuration from -> [`global-config`](../global-config.rsc) (the one without `-overlay`) to -> your local `global-config-overlay` and modify it to your specific needs. - -Then enable SMS actions: - - /tool/sms/set allowed-number=+491234567890 receive-enabled=yes secret=s3cr3t; - -Usage and invocation --------------------- - -Send a SMS from allowed number to your device's phone number: - - :cmd s3cr3t script sms-action action=reboot; - -The value given by "`action=`" is one of the pre-defined actions from -`SmsAction`. - -See also --------- - -* [Forward received SMS](sms-forward.md) - ---- -[⬅️ Go back to main README](../README.md) -[⬆️ Go back to top](#top) diff --git a/doc/sms-forward.d/notification.avif b/doc/sms-forward.d/notification.avif deleted file mode 100644 index 01eb7ba..0000000 Binary files a/doc/sms-forward.d/notification.avif and /dev/null differ diff --git a/doc/sms-forward.md b/doc/sms-forward.md deleted file mode 100644 index ccb6482..0000000 --- a/doc/sms-forward.md +++ /dev/null @@ -1,98 +0,0 @@ -Forward received SMS -==================== - -[](https://github.com/eworm-de/routeros-scripts/stargazers) -[](https://github.com/eworm-de/routeros-scripts/network) -[](https://github.com/eworm-de/routeros-scripts/watchers) -[](https://mikrotik.com/download/changelogs/) -[](https://t.me/routeros_scripts) -[](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J) - -[⬅️ Go back to main README](../README.md) - -> ℹ️ **Info**: This script can not be used on its own but requires the base -> installation. See [main README](../README.md) for details. - -Description ------------ - -RouterOS can receive SMS. This script forwards SMS as notification. - -A broadband interface with SMS support is required. - -### Sample notification - - - -Requirements and installation ------------------------------ - -Just install the script: - - $ScriptInstallUpdate sms-forward; - -... and add a scheduler to run it periodically: - - /system/scheduler/add interval=2m name=sms-forward on-event="/system/script/run sms-forward;" start-time=startup; - -Configuration -------------- - -You have to enable receiving of SMS: - - /tool/sms/set receive-enabled=yes; - -The configuration goes to `global-config-overlay`, this is the only parameter: - -* `SmsForwardHooks`: an array with pre-defined hooks, where each hook consists - of `match` (which is matched against the received message), `allowed-number` - (which is matched against the sending phone number or name) and `command`. - For `match` and `allowed-number` regular expressions are supported. Actual - phone number (`$Phone`) and message (`$Message`) are available for the hook. - -> ℹ️ **Info**: Copy relevant configuration from -> [`global-config`](../global-config.rsc) (the one without `-overlay`) to -> your local `global-config-overlay` and modify it to your specific needs. - -Notification settings are required for -[e-mail](mod/notification-email.md), -[matrix](mod/notification-matrix.md), -[ntfy](mod/notification-ntfy.md) and/or -[telegram](mod/notification-telegram.md). - -Tips & Tricks -------------- - -### Take care of harmful commands! - -It is easy to fake the sending phone number! So make sure you do not rely on -that number for potentially harmful commands. Add a shared secret to match -into the text instead, for example: `reboot-53cr3t-5tr1n9` instead of just -`reboot`. - -### Order new volume - -Most broadband providers include a volume limit for their data plans. The -hook functionality can be used to order new volume automatically. - -Let's assume an imaginary provider **ABC** sends a message when the available -volume is about to deplete. The message is sent from `ABC` and the text -contains the string `80%`. New volume can be ordered by sending a SMS back to -the phone number `1234` with the text `data-plan`. - - :global SmsForwardHooks { - { match="80%"; - allowed-number="ABC"; - command="/tool/sms/send lte1 phone-number=1234 message=\"data-plan\";" }; - }; - -Adjust the values to your own needs. - -See also --------- - -* [Act on received SMS](sms-action.md) - ---- -[⬅️ Go back to main README](../README.md) -[⬆️ Go back to top](#top) diff --git a/doc/ssh-keys-import.md b/doc/ssh-keys-import.md deleted file mode 100644 index d1325aa..0000000 --- a/doc/ssh-keys-import.md +++ /dev/null @@ -1,2 +0,0 @@ -This script has been replaced by a module. Please see -[Import ssh keys for public key authentication](mod/ssh-keys-import.md). diff --git a/doc/super-mario-theme.md b/doc/super-mario-theme.md deleted file mode 100644 index c72f220..0000000 --- a/doc/super-mario-theme.md +++ /dev/null @@ -1,38 +0,0 @@ -Play Super Mario theme -====================== - -[](https://github.com/eworm-de/routeros-scripts/stargazers) -[](https://github.com/eworm-de/routeros-scripts/network) -[](https://github.com/eworm-de/routeros-scripts/watchers) -[](https://mikrotik.com/download/changelogs/) -[](https://t.me/routeros_scripts) -[](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J) - -[⬅️ Go back to main README](../README.md) - -Description ------------ - -This script plays Super Mario theme. - -The hardware needs a beeper. - -Requirements and installation ------------------------------ - -Just install the script: - - $ScriptInstallUpdate super-mario-theme; - -Usage and invocation --------------------- - -Just run the script to play: - - /system/script/run super-mario-theme; - -For extra fun use it for dhcp lease script. :) - ---- -[⬅️ Go back to main README](../README.md) -[⬆️ Go back to top](#top) diff --git a/doc/telegram-chat.d/01-chat-specific.avif b/doc/telegram-chat.d/01-chat-specific.avif deleted file mode 100644 index ab75f78..0000000 Binary files a/doc/telegram-chat.d/01-chat-specific.avif and /dev/null differ diff --git a/doc/telegram-chat.d/02-chat-all.avif b/doc/telegram-chat.d/02-chat-all.avif deleted file mode 100644 index ed1a389..0000000 Binary files a/doc/telegram-chat.d/02-chat-all.avif and /dev/null differ diff --git a/doc/telegram-chat.d/03-reply.avif b/doc/telegram-chat.d/03-reply.avif deleted file mode 100644 index 515853e..0000000 Binary files a/doc/telegram-chat.d/03-reply.avif and /dev/null differ diff --git a/doc/telegram-chat.md b/doc/telegram-chat.md deleted file mode 100644 index 1e6f70f..0000000 --- a/doc/telegram-chat.md +++ /dev/null @@ -1,152 +0,0 @@ -Chat with your router and send commands via Telegram bot -======================================================== - -[](https://github.com/eworm-de/routeros-scripts/stargazers) -[](https://github.com/eworm-de/routeros-scripts/network) -[](https://github.com/eworm-de/routeros-scripts/watchers) -[](https://mikrotik.com/download/changelogs/) -[](https://t.me/routeros_scripts) -[](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J) - -[⬅️ Go back to main README](../README.md) - -> ℹ️ **Info**: This script can not be used on its own but requires the base -> installation. See [main README](../README.md) for details. - -Description ------------ - -This script makes your device poll a Telegram bot for new messages. With -these messages you can send commands to your device and make it run them. -The resulting output is send back to you. - -Requirements and installation ------------------------------ - -Just install the script and the module for notifications via Telegram: - - $ScriptInstallUpdate telegram-chat,mod/notification-telegram; - -Then create a schedule that runs the script periodically: - - /system/scheduler/add start-time=startup interval=30s name=telegram-chat on-event="/system/script/run telegram-chat;"; - -> ⚠️ **Warning**: Make sure to keep the interval in sync when installing -> on several devices. Differing polling intervals will result in missed -> messages. - -Configuration -------------- - -Make sure to configure -[notifications via telegram](mod/notification-telegram.md) first. The -additional configuration goes to `global-config-overlay`, these are the -parameters: - -* `TelegramChatIdsTrusted`: an array with trusted chat ids or user names -* `TelegramChatGroups`: define the groups a device should belong to - -> ℹ️ **Info**: Copy relevant configuration from -> [`global-config`](../global-config.rsc) (the one without `-overlay`) to -> your local `global-config-overlay` and modify it to your specific needs. - -Usage and invocation --------------------- - -### Activating device(s) - -This script is capable of chatting with multiple devices. By default a -device is passive and not acting on messages. To activate it send a message -containing `! identity` (exclamation mark, optional space and system's -identity). To query all dynamic ip addresses form a device named "*MikroTik*" -send `! MikroTik`, followed by `/ip/address/print where dynamic;`. - - - -Devices can be grouped to chat with them simultaneously. The default group -"*all*" can be activated by sending `! @all`, which will make all devices -act on your commands. - - - -Send a single exclamation mark or non-existent identity to make all -devices passive again. - -### Reply to message - -Let's assume you received a message from a device before, and want to send -a command to that device. No need to activate it, you can just reply to -that message. - - - -Associated messages are cleared on device reboot. - -### Ask for devices - -Send a message with a single question mark (`?`) to query for devices -currenty online. The answer can be used for command via reply then. - -Known limitations ------------------ - -### Do not use numeric ids! - -Numeric ids are valid within a session only. Usually you can use something -like this to print all ip addresses and remove the first one: - - /ip/address/print; - /ip/address/remove 0; - -This will fail when sent in separate messages. Instead you should use basic -scripting capabilities. Try to print what you want to act on... - - /ip/address/print where interface=eth; - -... verify and finally remove it. - - /ip/address/remove [ find where interface=eth ]; - -What does work is using the persistent ids: - - /ip/address/print show-ids; - -The output contains an id starting with asterisk that can be used: - - /ip/address/remove *E; - -### Mind command runtime - -The command is run in background while the script waits for it - about -20 seconds at maximum. A command exceeding that time continues to run in -background, but the output in the message is missing or truncated then. - -If you still want a response you can work around this by making your code -send information on its own. Something like this should do the job: - - :global SendTelegram; - :delay 30s; - $SendTelegram "Command finished" "Your command finished..."; - -### Output size - -Telegram messages have a limit of 4096 characters. If output is too large it -is truncated, and a warning is added to the message. - -### Sending commands to a group - -Adding a bot to a group allows it to send messages to that group. To allow -it to receive messages you have to make it an admin of that group! It is -fine to deny all permissions, though. - -Also adding an admin to a group can cause the group id to change, so check -that if notifications break suddenly. - -See also --------- - -* [Send notifications via Telegram](mod/notification-telegram.md) - ---- -[⬅️ Go back to main README](../README.md) -[⬆️ Go back to top](#top) diff --git a/doc/unattended-lte-firmware-upgrade.md b/doc/unattended-lte-firmware-upgrade.md deleted file mode 100644 index cb96aa1..0000000 --- a/doc/unattended-lte-firmware-upgrade.md +++ /dev/null @@ -1,54 +0,0 @@ -Install LTE firmware upgrade -============================ - -[](https://github.com/eworm-de/routeros-scripts/stargazers) -[](https://github.com/eworm-de/routeros-scripts/network) -[](https://github.com/eworm-de/routeros-scripts/watchers) -[](https://mikrotik.com/download/changelogs/) -[](https://t.me/routeros_scripts) -[](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J) - -[⬅️ Go back to main README](../README.md) - -Description ------------ - -This script upgrades LTE firmware on compatible devices: - -* R11e-LTE -* R11e-LTE-US -* R11e-4G -* R11e-LTE6 -* ... and more - probably what ever Mikrotik builds into their devices - -A temporary scheduler is created to be independent from terminal. Thus -starting the upgrade process over the broadband connection is supported. - -Requirements and installation ------------------------------ - -The firmware is downloaded over the air, so a working broadband connection -on the lte interface to be updated is required! Having internet access from -different gateway is not sufficient! - -Just install the script: - - $ScriptInstallUpdate unattended-lte-firmware-upgrade; - -Usage and invocation --------------------- - -Run the script if an upgrade for your LTE hardware is available: - - /system/script/run unattended-lte-firmware-upgrade; - -Then be patient, go for a coffee and wait for the upgrade process to finish. - -See also --------- - -* [Notify on LTE firmware upgrade](check-lte-firmware-upgrade.md) - ---- -[⬅️ Go back to main README](../README.md) -[⬆️ Go back to top](#top) diff --git a/doc/update-gre-address.md b/doc/update-gre-address.md deleted file mode 100644 index de9f622..0000000 --- a/doc/update-gre-address.md +++ /dev/null @@ -1,48 +0,0 @@ -Update GRE configuration with dynamic addresses -=============================================== - -[](https://github.com/eworm-de/routeros-scripts/stargazers) -[](https://github.com/eworm-de/routeros-scripts/network) -[](https://github.com/eworm-de/routeros-scripts/watchers) -[](https://mikrotik.com/download/changelogs/) -[](https://t.me/routeros_scripts) -[](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J) - -[⬅️ Go back to main README](../README.md) - -> ℹ️ **Info**: This script can not be used on its own but requires the base -> installation. See [main README](../README.md) for details. - -Description ------------ - -Running a GRE tunnel over IPSec with IKEv2 is a common scenario. This is -easy to configure on client, but has an issue on server side: client IP -addresses are assigned dynamically via mode-config and have to be updated -for GRE interface. - -This script handles the address updates and disables the interface if the -client is disconnected. - -Requirements and installation ------------------------------ - -Just install the script: - - $ScriptInstallUpdate update-gre-address; - -... and add a scheduler to run the script periodically: - - /system/scheduler/add interval=30s name=update-gre-address on-event="/system/script/run update-gre-address;" start-time=startup; - -Configuration -------------- - -The configuration goes to interface's comment. Add the client's IKEv2 -certificate CN into the comment: - - /interface/gre/set comment="ikev2-client1" gre-client1; - ---- -[⬅️ Go back to main README](../README.md) -[⬆️ Go back to top](#top) diff --git a/doc/update-tunnelbroker.md b/doc/update-tunnelbroker.md deleted file mode 100644 index ee0fe98..0000000 --- a/doc/update-tunnelbroker.md +++ /dev/null @@ -1,50 +0,0 @@ -Update tunnelbroker configuration -================================= - -[](https://github.com/eworm-de/routeros-scripts/stargazers) -[](https://github.com/eworm-de/routeros-scripts/network) -[](https://github.com/eworm-de/routeros-scripts/watchers) -[](https://mikrotik.com/download/changelogs/) -[](https://t.me/routeros_scripts) -[](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J) - -[⬅️ Go back to main README](../README.md) - -> ℹ️ **Info**: This script can not be used on its own but requires the base -> installation. See [main README](../README.md) for details. - -Description ------------ - -Connecting to [tunnelbroker.net](//tunnelbroker.net) from dynamic public -ip address requires the address to be sent to the remote, and to be set -locally. This script does both. - -Requirements and installation ------------------------------ - -Just install the script: - - $ScriptInstallUpdate update-tunnelbroker; - -Installing [ppp-on-up](ppp-on-up.md) makes this script run when ever a ppp -connection is established. - -Configuration -------------- - -The configuration goes to interface's comment: - - /interface/6to4/set comment="tunnelbroker, user=user, id=12345, pass=s3cr3t" tunnelbroker; - -You should know you user name from login. The `id` is the tunnel's numeric -id, `pass` is the *update key* found on the tunnel's advanced tab. - -See also --------- - -* [Run scripts on ppp connection](ppp-on-up.md) - ---- -[⬅️ Go back to main README](../README.md) -[⬆️ Go back to top](#top) diff --git a/doc/upload-backup.md b/doc/upload-backup.md deleted file mode 100644 index 83c9991..0000000 --- a/doc/upload-backup.md +++ /dev/null @@ -1 +0,0 @@ -This script has been renamed. Please see [backup-upload](backup-upload.md). diff --git a/email-backup b/email-backup new file mode 100644 index 0000000..63e2fbe --- /dev/null +++ b/email-backup @@ -0,0 +1,85 @@ +#!rsc +# RouterOS script: email-backup +# Copyright (c) 2013-2019 Christian Hesse +# +# create and email backup and config file + +:global Identity; +:global Domain; +:global EmailBackupTo; +:global EmailBackupCc; +:global BackupSendBinary; +:global BackupSendExport; +:global BackupCloud; +:global BackupPassword; + +:if ($BackupSendBinary != true && \ + $BackupSendExport != true && \ + $BackupCloud != true) do={ + :error ("Configured to send neither backup nor config export."); +} + +# filename based on identity +:local FileName ($Identity . "." . $Domain); +:local CloudStatus $BackupCloud; +:local BackupStatus $BackupSendBinary; +:local ConfigStatus $BackupSendExport; +:local Attach [ :toarray "" ]; + +# get some system information +:local BoardName [ / system resource get board-name ]; +:local Model [ / system routerboard get model ]; +:local SerialNumber [ / system routerboard get serial-number ]; +:local Channel [ / system package update get channel ]; +:local InstalledVersion [ / system package update get installed-version ]; + +# binary backup +:if ($BackupSendBinary = true || \ + $BackupCloud = true) do={ + / system backup save encryption=aes-sha256 name=$FileName password=$BackupPassword; + + # attach to mail + :if ($BackupSendBinary = true) do={ + :set BackupStatus ($FileName . ".backup"); + :set Attach ($Attach, $BackupStatus); + } + + # upload to cloud + :if ($BackupCloud = true) do={ + :do { + # we are not interested in output, but print without count-only is + # required to fetch information from cloud + / system backup cloud print as-value; + :if ([ / system backup cloud print count-only ] > 0) do={ + / system backup cloud remove-file [ find ]; + } + / system backup cloud upload-file action=upload src-file=($FileName . ".backup"); + :set CloudStatus [ / system backup cloud get [ find ] secret-download-key ]; + } on-error={ + :set CloudStatus "failed"; + } + } +} + +# create configuration export +:if ($BackupSendExport = true) do={ + / export terse file=$FileName; + :set ConfigStatus ($FileName . ".rsc"); + :set Attach ($Attach, $ConfigStatus); +} + +# send email with status and files +/ tool e-mail send to=$EmailBackupTo cc=$EmailBackupCc \ + subject=("[" . $Identity . "] Backup & Config") \ + body=("Backup and config export for " . $Identity . ".\n\n" . \ + "Board name: " . $BoardName . "\n" . \ + "Model: " . $Model . "\n" . \ + "Serial number: " . $SerialNumber . "\n" . \ + "Hostname: " . $Identity . "\n" . \ + "Channel: " . $Channel . "\n" . \ + "RouterOS: " . $InstalledVersion . "\n\n" . \ + "Backup attached: " . $BackupStatus . "\n" . \ + "Config attached: " . $ConfigStatus . "\n" . \ + "Cloud backup: " . $CloudStatus) \ + file=$Attach; +} diff --git a/firmware-upgrade-reboot.rsc b/firmware-upgrade-reboot.rsc deleted file mode 100644 index 86a9a8c..0000000 --- a/firmware-upgrade-reboot.rsc +++ /dev/null @@ -1,60 +0,0 @@ -#!rsc by RouterOS -# RouterOS script: firmware-upgrade-reboot -# Copyright (c) 2022-2025 Christian Hesse -# https://rsc.eworm.de/COPYING.md -# -# requires RouterOS, version=7.15 -# -# install firmware upgrade, and reboot -# https://rsc.eworm.de/doc/firmware-upgrade-reboot.md - -:global GlobalFunctionsReady; -:while ($GlobalFunctionsReady != true) do={ :delay 500ms; } - -:local ExitOK false; -:do { - :local ScriptName [ :jobname ]; - - :global LogPrint; - :global ScriptLock; - :global VersionToNum; - - :if ([ $ScriptLock $ScriptName ] = false) do={ - :set ExitOK true; - :error false; - } - - :local RouterBoard [ /system/routerboard/get ]; - :if ($RouterBoard->"current-firmware" = $RouterBoard->"upgrade-firmware") do={ - $LogPrint info $ScriptName ("Current and upgrade firmware match with version " . \ - $RouterBoard->"current-firmware" . "."); - :set ExitOK true; - :error true; - } - :if ([ $VersionToNum ($RouterBoard->"current-firmware") ] > [ $VersionToNum ($RouterBoard->"upgrade-firmware") ]) do={ - $LogPrint info $ScriptName ("Different firmware version is available, but it is a downgrade. Ignoring."); - :set ExitOK true; - :error true; - } - - :if ([ /system/routerboard/settings/get auto-upgrade ] = false) do={ - $LogPrint info $ScriptName ("Firmware version " . $RouterBoard->"upgrade-firmware" . \ - " is available, upgrading."); - /system/routerboard/upgrade; - } - - :while ([ :len [ /log/find where topics=({"system";"info";"critical"}) \ - message="Firmware upgraded successfully, please reboot for changes to take effect!" ] ] = 0) do={ - :delay 1s; - } - - :local Uptime [ /system/resource/get uptime ]; - :if ($Uptime < 1m) do={ - :delay $Uptime; - } - - $LogPrint info $ScriptName ("Firmware upgrade successful, rebooting."); - /system/reboot; -} on-error={ - :global ExitError; $ExitError $ExitOK [ :jobname ]; -} diff --git a/fw-addr-lists.d/allow b/fw-addr-lists.d/allow deleted file mode 100644 index 8b59ed7..0000000 --- a/fw-addr-lists.d/allow +++ /dev/null @@ -1,3 +0,0 @@ -# an ip address list for use with fw-addr-lists script -# https://git.eworm.de/cgit/routeros-scripts/about/doc/fw-addr-lists.md -git.eworm.de diff --git a/fw-addr-lists.d/block b/fw-addr-lists.d/block deleted file mode 100644 index 5e9fef2..0000000 --- a/fw-addr-lists.d/block +++ /dev/null @@ -1,5 +0,0 @@ -# an ip address list for use with fw-addr-lists script -# https://git.eworm.de/cgit/routeros-scripts/about/doc/fw-addr-lists.md - -# example.net -93.184.216.34 diff --git a/fw-addr-lists.d/mikrotik b/fw-addr-lists.d/mikrotik deleted file mode 100644 index 3b31a94..0000000 --- a/fw-addr-lists.d/mikrotik +++ /dev/null @@ -1,5 +0,0 @@ -# AS51894 Mikrotikls SIA -# https://bgp.he.net/AS51894 -159.148.147.0/24 -159.148.172.0/24 -2a02:610:7501::/48 diff --git a/fw-addr-lists.rsc b/fw-addr-lists.rsc deleted file mode 100644 index f0940fe..0000000 --- a/fw-addr-lists.rsc +++ /dev/null @@ -1,214 +0,0 @@ -#!rsc by RouterOS -# RouterOS script: fw-addr-lists -# Copyright (c) 2023-2025 Christian Hesse -# https://rsc.eworm.de/COPYING.md -# -# requires RouterOS, version=7.16 -# -# download, import and update firewall address-lists -# https://rsc.eworm.de/doc/fw-addr-lists.md - -:global GlobalFunctionsReady; -:while ($GlobalFunctionsReady != true) do={ :delay 500ms; } - -:local ExitOK false; -:do { - :local ScriptName [ :jobname ]; - - :global FwAddrLists; - :global FwAddrListTimeOut; - - :global CertificateAvailable; - :global EitherOr; - :global FetchHuge; - :global HumanReadableNum; - :global LogPrint; - :global LogPrintOnce; - :global LogPrintVerbose; - :global ScriptLock; - :global WaitFullyConnected; - - :local FindDelim do={ - :local ValidChars "0123456789.:/ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-"; - :for I from=0 to=[ :len $1 ] do={ - :if ([ :typeof [ :find $ValidChars [ :pick ($1 . " ") $I ] ] ] != "num") do={ - :return $I; - } - } - } - - :local GetBranch do={ - :global EitherOr; - :return [ :pick [ :convert transform=md5 to=hex [ :pick $1 0 [ $EitherOr [ :find $1 "/" ] [ :len $1 ] ] ] ] 0 2 ]; - } - - :if ([ $ScriptLock $ScriptName ] = false) do={ - :set ExitOK true; - :error false; - } - $WaitFullyConnected; - - :local ListComment ("managed by " . $ScriptName); - - :foreach FwListName,FwList in=$FwAddrLists do={ - :local CntAdd 0; - :local CntRenew 0; - :local CntRemove 0; - :local IPv4Addresses ({}); - :local IPv6Addresses ({}); - :local Failure false; - - :foreach List in=$FwList do={ - :local CheckCertificate false; - :local Data false; - :local TimeOut [ $EitherOr [ :totime ($List->"timeout") ] $FwAddrListTimeOut ]; - - :if ([ :len ($List->"cert") ] > 0) do={ - :set CheckCertificate true; - :if ([ $CertificateAvailable ($List->"cert") ] = false) do={ - $LogPrint warning $ScriptName ("Downloading required certificate (" . $FwListName . \ - " / " . $List->"url" . ") failed, trying anyway."); - } - } - - :for I from=1 to=5 do={ - :if ($Data = false) do={ - :set Data [ :tolf [ $FetchHuge $ScriptName ($List->"url") $CheckCertificate ] ]; - :if ($Data = false) do={ - :if ($I < 5) do={ - $LogPrint debug $ScriptName ("Failed downloading for list '" . $FwListName . \ - "', " . $I . ". try from: " . $List->"url"); - :delay (($I * $I) . "s"); - } - } - } - } - - :if ($Data = false) do={ - :set Data ""; - :set Failure true; - $LogPrint warning $ScriptName ("Failed downloading for list '" . $FwListName . \ - "' from: " . $List->"url"); - } else={ - $LogPrint debug $ScriptName ("Downloaded " . [ $HumanReadableNum [ :len $Data ] 1024 ] . \ - "B for list '" . $FwListName . "' from: " . $List->"url"); - } - - :foreach Line in=[ :deserialize $Data delimiter="\n" from=dsv options=dsv.plain ] do={ - :set Line ($Line->0); - :local Address; - :if ([ :pick $Line 0 1 ] = "{") do={ - :do { - :set Address [ :tostr ([ :deserialize from=json $Line ]->"cidr") ]; - } on-error={ } - } else={ - :set Address ([ :pick $Line 0 [ $FindDelim $Line ] ] . ($List->"cidr")); - } - :do { - :local Branch [ $GetBranch $Address ]; - :if ($Address ~ "^[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}(/[0-9]{1,2})?\$") do={ - :if ($Address ~ "/32\$") do={ - :set Address [ :pick $Address 0 ([ :len $Address ] - 3) ]; - } - :set ($IPv4Addresses->$Branch->$Address) $TimeOut; - :error true; - } - :if ($Address ~ "^[0-9a-zA-Z]*:[0-9a-zA-Z:\\.]+(/[0-9]{1,3})?\$") do={ - :if ($Address ~ "/128\$") do={ - :set Address [ :pick $Address 0 ([ :len $Address ] - 4) ]; - } - :set ($IPv6Addresses->$Branch->$Address) $TimeOut; - :error true; - } - :if ($Address ~ "^[\\.a-zA-Z0-9-]+\\.[a-zA-Z]{2,}\$") do={ - :set ($IPv4Addresses->$Branch->$Address) $TimeOut; - :set ($IPv6Addresses->$Branch->$Address) $TimeOut; - :error true; - } - } on-error={ } - } - } - - :foreach Entry in=[ /ip/firewall/address-list/find where \ - list=$FwListName comment=$ListComment ] do={ - :local Address [ /ip/firewall/address-list/get $Entry address ]; - :local Branch [ $GetBranch $Address ]; - :local TimeOut ($IPv4Addresses->$Branch->$Address); - :if ([ :typeof $TimeOut ] = "time") do={ - $LogPrintVerbose debug $ScriptName ("Renewing IPv4 address in list '" . $FwListName . \ - "' with " . $TimeOut . ": " . $Address); - /ip/firewall/address-list/set $Entry timeout=$TimeOut; - :set ($IPv4Addresses->$Branch->$Address); - :set CntRenew ($CntRenew + 1); - } else={ - :if ($Failure = false) do={ - $LogPrintVerbose debug $ScriptName ("Removing IPv4 address from list '" . $FwListName . \ - "': " . $Address); - /ip/firewall/address-list/remove $Entry; - :set CntRemove ($CntRemove + 1); - } - } - } - - :foreach Entry in=[ /ipv6/firewall/address-list/find where \ - list=$FwListName comment=$ListComment ] do={ - :local Address [ /ipv6/firewall/address-list/get $Entry address ]; - :local Branch [ $GetBranch $Address ]; - :local TimeOut ($IPv6Addresses->$Branch->$Address); - :if ([ :typeof $TimeOut ] = "time") do={ - $LogPrintVerbose debug $ScriptName ("Renewing IPv6 address in list '" . $FwListName . \ - "' with " . $TimeOut . ": " . $Address); - /ipv6/firewall/address-list/set $Entry timeout=$TimeOut; - :set ($IPv6Addresses->$Branch->$Address); - :set CntRenew ($CntRenew + 1); - } else={ - :if ($Failure = false) do={ - $LogPrintVerbose debug $ScriptName ("Removing IPv6 address from list '" . $FwListName . \ - "': " . $Address); - /ipv6/firewall/address-list/remove $Entry; - :set CntRemove ($CntRemove + 1); - } - } - } - - :foreach BranchName,Branch in=$IPv4Addresses do={ - $LogPrintVerbose debug $ScriptName ("Handling branch: " . $BranchName); - :foreach Address,Timeout in=$Branch do={ - $LogPrintVerbose debug $ScriptName ("Adding IPv4 address to list '" . $FwListName . \ - "' with " . $Timeout . ": " . $Address); - :do { - /ip/firewall/address-list/add list=$FwListName comment=$ListComment \ - address=$Address timeout=$Timeout; - :set CntAdd ($CntAdd + 1); - } on-error={ - $LogPrint warning $ScriptName ("Failed to add IPv4 address to list '" . $FwListName . \ - "': " . $Address); - } - } - } - - :foreach BranchName,Branch in=$IPv6Addresses do={ - $LogPrintVerbose debug $ScriptName ("Handling branch: " . $BranchName); - :foreach Address,Timeout in=$Branch do={ - $LogPrintVerbose debug $ScriptName ("Adding IPv6 address to list '" . $FwListName . \ - "' with " . $Timeout . ": " . $Address); - :do { - /ipv6/firewall/address-list/add list=$FwListName comment=$ListComment \ - address=$Address timeout=$Timeout; - :set CntAdd ($CntAdd + 1); - } on-error={ - $LogPrint warning $ScriptName ("Failed to add IPv6 address to list '" . $FwListName . \ - "': " . $Address); - } - } - } - - $LogPrint info $ScriptName ("list: " . $FwListName . \ - " (" . [ $HumanReadableNum ($CntAdd + $CntRenew) 1000 ] . ")" . \ - " -- added: " . [ $HumanReadableNum $CntAdd 1000 ] . \ - " - renewed: " . [ $HumanReadableNum $CntRenew 1000 ] . \ - " - removed: " . [ $HumanReadableNum $CntRemove 1000 ]); - } -} on-error={ - :global ExitError; $ExitError $ExitOK [ :jobname ]; -} diff --git a/global-config b/global-config new file mode 100644 index 0000000..b57c212 --- /dev/null +++ b/global-config @@ -0,0 +1,103 @@ +#!rsc +# RouterOS script: global-config +# Copyright (c) 2013-2019 Christian Hesse +# +# global configuration + +# Make sure all configuration properties are up to date and this +# value is in sync with value in script 'global-functions'! +:global GlobalConfigVersion 3; + +# This is used for DNS and backup file. +:global Domain "example.com"; +:global HostNameInZone true; + +# These addresses are used to send e-mails to. The to-addresses need +# to be filled, cc-addresses can be empty, one address or a comma +# separated list of addresses. +:global EmailGeneralTo "mail@example.com"; +:global EmailGeneralCc "another@example.com"; +:global EmailBackupTo "mail@example.com"; +:global EmailBackupCc ""; + +# You can send Telegram notifications. Register a bot +# and add the token and chat ids here. +:global TelegramTokenId ""; +:global TelegramChatId ""; +#:global TelegramTokenId "123456:ABCDEF-GHI"; +#:global TelegramChatId "12345678"; + +# This defines what backups to generate and what password to use. +:global BackupSendBinary false; +:global BackupSendExport true; +:global BackupCloud false; +:global BackupPassword "v3ry-s3cr3t"; + +# Specify an address to enable auto update to version assumed safe. +# The configured channel (bugfix, current, release-candidate) is appended. +:global SafeUpdateUrl ""; +#:global SafeUpdateUrl "https://example.com/ros/safe-update/"; + +# This controls what configuration is activated by bridge-port-to-default. +:global BridgePortTo "default"; + +# Access-list entries matching this comment are updated +# with daily pseudo-random PSK. +:global DailyPskMatchComment "Daily PSK"; +:global DailyPskSecrets { + { "Abusive"; "Aggressive"; "Bored"; "Chemical"; "Cold"; + "Cruel"; "Curved"; "Delightful"; "Discreet"; "Elite"; + "Evasive"; "Faded"; "Flat"; "Future"; "Grandiose"; + "Hanging"; "Humorous"; "Interesting"; "Magenta"; + "Magnificent"; "Numerous"; "Optimal"; "Pathetic"; + "Possessive"; "Remarkable"; "Rightful"; "Ruthless"; + "Stale"; "Unusual"; "Useless"; "Various" }; + { "Adhesive"; "Amusing"; "Astonishing"; "Frantic"; + "Kindhearted"; "Limping"; "Roasted"; "Robust"; + "Staking"; "Thundering"; "Ultra"; "Unusual" }; + { "Belief"; "Button"; "Curtain"; "Edge"; "Jewel"; + "String"; "Whistle" } +} + +# Run different commands with multiple mode-button presses. +:global ModeButton { + 1="/ system script run leds-toggle-mode;"; + 2=":global SendNotification; :global Identity; \$SendNotification (\"Hello...\") (\"Hello world, \" . \$Identity . \" calling!\");"; + 3="/ system shutdown;"; + 4="/ system reboot;"; + 5="/ system script run bridge-port-toggle;"; +# add more here... +}; + +# Run commands on SMS action. +:global SmsAction { + bridge-port-toggle="/ system script run bridge-port-toggle;"; + reboot="/ system reboot;"; + shutdown="/ system shutdown;"; +# add more here... +}; + +# This address should resolve ntp servers and is used to update +# ntp settings. A pool can rotate servers. +:global NtpPool "pool.ntp.org"; + +# This is the address used to send gps data to. +:global GpsTrackUrl "https://example.com/index.php"; + +# Enable this to fetch scripts from given url. +:global ScriptUpdatesFetch true; +:global ScriptUpdatesBaseUrl "https://git.eworm.de/cgit.cgi/routeros-scripts/plain/"; +#:global ScriptUpdatesBaseUrl "https://raw.githubusercontent.com/eworm-de/routeros-scripts/master/"; +#:global ScriptUpdatesBaseUrl "https://gitlab.com/eworm-de/routeros-scripts/raw/master/"; +:global ScriptUpdatesUrlSuffix ""; +:global ScriptUpdatesIgnore { + "global-config" +} + +# Use this for certificate auto-renew +:global CertRenewUrl ""; +#:global CertRenewUrl "https://example.com/certificates/"; +:global CertRenewPass { + "v3ry-s3cr3t"; + "4n0th3r-s3cr3t"; +} diff --git a/global-config-overlay.rsc b/global-config-overlay.rsc deleted file mode 100644 index 9afaceb..0000000 --- a/global-config-overlay.rsc +++ /dev/null @@ -1,12 +0,0 @@ -# Overlay for global configuration by RouterOS Scripts -# Copyright (c) 2013-2025 Christian Hesse -# https://rsc.eworm.de/COPYING.md -# -# global configuration, custom overlay -# https://rsc.eworm.de/#editing-configuration - -# Copy relevant configuration from global-config, paste and modify it here. -# https://rsc.eworm.de/global-config.rsc - - -# End of global-config-overlay diff --git a/global-config.changes b/global-config.changes new file mode 100644 index 0000000..86a130f --- /dev/null +++ b/global-config.changes @@ -0,0 +1,9 @@ +# RouterOS global-config changes +# Copyright (c) 2019 Christian Hesse + +# Changes for global-config to be added to notification on script-updates +:global GlobalConfigChanges { + 1="moved variables from global-config to global-functions for independence"; + 2="variable names became CamelCase to work around scripting issues"; + 3="variable for certificate renew passphrase became an array to support multiple passphrases"; +}; diff --git a/global-config.rsc b/global-config.rsc deleted file mode 100644 index fa32b16..0000000 --- a/global-config.rsc +++ /dev/null @@ -1,271 +0,0 @@ -#!rsc by RouterOS -# RouterOS script: global-config -# Copyright (c) 2013-2025 Christian Hesse -# https://rsc.eworm.de/COPYING.md -# -# global configuration -# https://rsc.eworm.de/ - -# Set this to 'true' to disable news and change notifications. -:global NoNewsAndChangesNotification false; - -# Add extra text (or emojis) in notification tags. -:global IdentityExtra ""; - -# This is used in DNS scripts ('ipsec-to-dns' and fallback in 'dhcp-to-dns') -# and backup scripts for file names. -:global Domain "example.com"; - -# You can send e-mail notifications. Configure the system's mail settings -# (/tool/e-mail), then install the module: -# $ScriptInstallUpdate mod/notification-email -# The to-address needs to be filled; cc-address can be empty, one address -# or a comma separated list of addresses. -:global EmailGeneralTo ""; -:global EmailGeneralCc ""; -#:global EmailGeneralTo "mail@example.com"; -#:global EmailGeneralCc "another@example.com,third@example.com"; - -# You can send Telegram notifications. Register a bot -# and add the token and chat ids here, then install the module: -# $ScriptInstallUpdate mod/notification-telegram -:global TelegramTokenId ""; -:global TelegramChatId ""; -#:global TelegramTokenId "123456:ABCDEF-GHI"; -#:global TelegramChatId "12345678"; -# Use this to send notifications to a specific topic in group. -:global TelegramThreadId ""; -# Using telegram-chat you have to define trusted chat ids (not group ids!) -# or user names. Groups allow to chat with devices simultaneously. -#:global TelegramChatIdsTrusted { -# "12345678"; -# "example_user"; -#}; -:global TelegramChatGroups "(all)"; -#:global TelegramChatGroups "(all|home|office)"; - -# You can send Matrix notifications. Configure these settings and -# install the module: -# $ScriptInstallUpdate mod/notification-matrix -:global MatrixHomeServer ""; -:global MatrixAccessToken ""; -:global MatrixRoom ""; -#:global MatrixHomeServer "matrix.org"; -#:global MatrixAccessToken "123456ABCDEFGHI..."; -#:global MatrixRoom "!example:matrix.org"; - -# You can send Ntfy notifications. Configure these settings and -# install the module: -# $ScriptInstallUpdate mod/notification-ntfy -:global NtfyServer "ntfy.sh"; -:global NtfyServerUser ""; -:global NtfyServerPass ""; -:global NtfyServerToken ""; -:global NtfyTopic ""; - -# It is possible to override e-mail, Telegram, Matrix and Ntfy setting -# for every script. This is done in arrays, where 'Override' is appended -# to the variable name, like this: -#:global EmailGeneralToOverride { -# "check-certificates"="override@example.com"; -# "backup-email"="backup@example.com"; -#} - -# Toggle this to disable symbols in notifications. -:global NotificationsWithSymbols true; -# Toggle this to disable color output in terminal/cli. -:global TerminalColorOutput true; - -# This defines what backups to generate and what password to use. -:global BackupSendBinary false; -:global BackupSendExport true; -:global BackupSendGlobalConfig true; -:global BackupPassword "v3ry-s3cr3t"; -:global BackupRandomDelay 0; -# These credentials are used to upload backup and config export files. -# SFTP authentication is tricky, you may have to limit authentication -# methods for your SSH server. -:global BackupUploadUrl "sftp://example.com/backup/"; -:global BackupUploadUser "mikrotik"; -:global BackupUploadPass "v3ry-s3cr3t"; -# Copy the RouterOS installation to backup partition before feature update. -:global BackupPartitionCopyBeforeFeatureUpdate false; - -# This defines the settings for firewall address-lists (fw-addr-lists). -# Warning: Mind your device's resources - memory and processing! -:global FwAddrLists { -# "allow"={ -# { url="https://rsc.eworm.de/main/fw-addr-lists.d/allow"; -# cert="ISRG Root X2"; timeout=1w }; -# }; - "block"={ -# { url="https://rsc.eworm.de/main/fw-addr-lists.d/block"; -# cert="ISRG Root X2" }; - { url="https://raw.githubusercontent.com/stamparm/ipsum/refs/heads/master/levels/4.txt"; -# # higher level (decrease the numerical value) for more addresses, and vice versa - cert="USERTrust RSA Certification Authority" }; - { url="https://www.dshield.org/block.txt"; cidr="/24"; - cert="ISRG Root X1" }; - { url="https://lists.blocklist.de/lists/strongips.txt"; - cert="Certum Trusted Network CA" }; -# { url="https://www.spamhaus.org/drop/drop_v4.json"; -# cert="GTS Root R4" }; -# { url="https://www.spamhaus.org/drop/drop_v6.json"; -# cert="GTS Root R4" }; - }; -# "mikrotik"={ -# { url="https://rsc.eworm.de/main/fw-addr-lists.d/mikrotik"; -# cert="ISRG Root X2"; timeout=1w }; -# }; -}; -:global FwAddrListTimeOut 1d; - -# This defines what log messages to filter or include by topic or message -# text. Regular expressions are supported. An empty string has a special -# meaning not to filter or include anything. -# These are filters, so excluding messages from forwarding. -:global LogForwardFilter "(debug|info|packet|raw)"; -:global LogForwardFilterMessage ""; -#:global LogForwardFilterMessage "message text"; -#:global LogForwardFilterMessage "(message text|another text|...)"; -# ... and another setting with reverse logic. This includes messages even -# if filtered above. -:global LogForwardInclude ""; -:global LogForwardIncludeMessage ""; -#:global LogForwardInclude "account"; -#:global LogForwardIncludeMessage "message text"; - -# Specify an address to enable auto update to version assumed safe. -# The configured channel (bugfix, current, release-candidate) is appended. -:global SafeUpdateUrl ""; -#:global SafeUpdateUrl "https://example.com/ros/safe-update/"; -# Allow to install patch updates automatically. -:global SafeUpdatePatch false; -# Allow to install updates automatically if seen in neighbor list. -:global SafeUpdateNeighbor false; -:global SafeUpdateNeighborIdentity ""; -# Install *ALL* updates automatically! -# Set to all upper-case "Yes, please!" to enable. -:global SafeUpdateAll "no"; - -# Defer the reboot for night on automatic (non-interactive) update -:global PackagesUpdateDeferReboot false; - -# These thresholds control when to send health notification -# on temperature and voltage. -:global CheckHealthTemperature { - temperature=50; - cpu-temperature=70; - board-temperature1=50; - board-temperature2=50; -}; -# This is deviation on recovery threshold against notification flooding. -:global CheckHealthTemperatureDeviation 3; -:global CheckHealthVoltageLow 115; -:global CheckHealthVoltagePercent 10; - -# Access-list entries matching this comment are updated -# with daily pseudo-random PSK. -:global DailyPskMatchComment "Daily PSK"; -:global DailyPskQrCodeUrl "https://www.eworm.de/cgi-bin/cqrlogo-wifi.cgi"; -:global DailyPskSecrets { - { "Abusive"; "Aggressive"; "Bored"; "Chemical"; "Cold"; - "Cruel"; "Curved"; "Delightful"; "Discreet"; "Elite"; - "Evasive"; "Faded"; "Flat"; "Future"; "Grandiose"; - "Hanging"; "Humorous"; "Interesting"; "Magenta"; - "Magnificent"; "Numerous"; "Optimal"; "Pathetic"; - "Possessive"; "Remarkable"; "Rightful"; "Ruthless"; - "Stale"; "Unusual"; "Useless"; "Various" }; - { "Adhesive"; "Amusing"; "Astonishing"; "Frantic"; - "Kindhearted"; "Limping"; "Roasted"; "Robust"; - "Staking"; "Thundering"; "Ultra"; "Unreal" }; - { "Belief"; "Button"; "Curtain"; "Edge"; "Jewel"; - "String"; "Whistle" } -}; - -# Specify how to assemble DNS names in ipsec-to-dns. -:global HostNameInZone true; -:global PrefixInZone true; - -# Run different commands with multiple mode-button presses. -:global ModeButton { - 1="/system/leds/settings/set all-leds-off=(({ \"never\"=\"immediate\"; \"immediate\"=\"never\" })->[ get all-leds-off ]);"; - 2=":global Identity; :global SendNotification; :global SymbolForNotification; \$SendNotification ([ \$SymbolForNotification \"earth\" ] . \"Hello...\") (\"Hello world, \" . \$Identity . \" calling!\");"; - 3="/system/shutdown;"; - 4="/system/reboot;"; - 5=":global BridgePortVlan; \$BridgePortVlan alt;"; -# add more here... -}; -# This led gives visual feedback if type is 'on' or 'off'. -:global ModeButtonLED "user-led"; - -# Run commands on SMS action. -:global SmsAction { - bridge-port-vlan-alt=":global BridgePortVlan; \$BridgePortVlan alt;"; - reboot="/system/reboot;"; - shutdown="/system/shutdown;"; -# add more here... -}; - -# Run commands by hooking into SMS forward. -:global SmsForwardHooks { - { match="magic string"; - allowed-number="12345678"; - command="/system/script/run ..." }; -# add more here... -}; - -# This is the address used to send gps data to. -:global GpsTrackUrl "https://example.com/index.php"; - -# This is the base url to fetch scripts from. -:global ScriptUpdatesBaseUrl "https://rsc.eworm.de/main/"; -# alternative urls - main: stable code - next: currently in development -#:global ScriptUpdatesBaseUrl "https://rsc.eworm.de/next/"; -#:global ScriptUpdatesBaseUrl "https://git.eworm.de/cgit/routeros-scripts/plain/"; -#:global ScriptUpdatesBaseUrl "https://raw.githubusercontent.com/eworm-de/routeros-scripts/main/"; -#:global ScriptUpdatesBaseUrl "https://raw.githubusercontent.com/eworm-de/routeros-scripts/next/"; -#:global ScriptUpdatesBaseUrl "https://gitlab.com/eworm-de/routeros-scripts/raw/main/"; -#:global ScriptUpdatesBaseUrl "https://gitlab.com/eworm-de/routeros-scripts/raw/next/"; -:global ScriptUpdatesUrlSuffix ""; -# use next branch with my git url (git.eworm.de) -#:global ScriptUpdatesUrlSuffix "?h=next"; - -# Use this for defaults with $ScriptRunOnce -# Install module with: -# $ScriptInstallUpdate mod/scriptrunonce -:global ScriptRunOnceBaseUrl ""; -:global ScriptRunOnceUrlSuffix ""; - -# This project is developed in private spare time and usage is free of charge -# for you. If you like the scripts and think this is of value for you or your -# business please consider a donation: -# https://rsc.eworm.de/#donate -# Enable this to silence donation hint. -:global IDonate false; - -# Use this for certificate auto-renew -:global CertRenewUrl ""; -#:global CertRenewUrl "https://example.com/certificates/"; -:global CertRenewTime 3w; -:global CertRenewPass { - "v3ry-s3cr3t"; - "4n0th3r-s3cr3t"; -}; -:global CertWarnTime 2w; -:global CertIssuedExportPass { - "cert1-cn"="v3ry-s3cr3t"; - "cert2-cn"="4n0th3r-s3cr3t"; -}; - -# load custom settings from overlay and snippets -# Warning: Do *NOT* copy this code to overlay! -:foreach Script in=([ /system/script/find where name="global-config-overlay" ], \ - [ /system/script/find where name~"^global-config-overlay.d/" ]) do={ - :do { - /system/script/run $Script; - } on-error={ - :log error ("Loading configuration from overlay or snippet " . \ - [ /system/script/get $Script name ] . " failed!"); - } -} diff --git a/global-functions b/global-functions new file mode 100644 index 0000000..69f2c36 --- /dev/null +++ b/global-functions @@ -0,0 +1,197 @@ +#!rsc +# RouterOS script: global-functions +# Copyright (c) 2013-2019 Christian Hesse +# +# global functions + +# expected configuration version +:global ExpectedConfigVersion 3; + +# global variables not to be changed by user +:global SentRouterosUpdateNotification "-"; +:global SentLteFirmwareUpgradeNotification "-"; +:global Identity [ / system identity get name ]; + +# url encoding +:global UrlEncode do={ + :local Input [ :tostr $1 ]; + :local Return ""; + + :if ([ :len $Input ] > 0) do={ + :local Chars " !\"#\$%&'()*+,-./:;<=>\?@[\\]^_`{|}~"; + :local Subs { "%20"; "%21"; "%22"; "%23"; "%24"; "%25"; "%26"; "%27"; "%28"; "%29"; + "%2A"; "%2B"; "%2C"; "%2D"; "%2E"; "%2F"; "%3A"; "%3B"; "%3C"; "%3D"; + "%3E"; "%3F"; "%40"; "%5B"; "%5C"; "%5D"; "%5E"; "%5F"; "%60"; "%7B"; + "%7C"; "%7D"; "%7E" }; + + :for I from=0 to=([ :len $Input ] - 1) do={ + :local Char [ :pick $Input $I ]; + :local Replace [ :find $Chars $Char ]; + + :if ([ :len $Replace ] > 0) do={ + :set Char ($Subs->$Replace); + } + :set Return ($Return . $Char); + } + } + + :return $Return; +} + +# character replace +:global CharacterReplace do={ + :local String [ :tostr $1 ]; + :local ReplaceFrom [ :tostr $2 ]; + :local ReplaceWith [ :tostr $3 ]; + :local Len [ :len $ReplaceFrom ]; + :local Return ""; + + :if ($ReplaceFrom = "") do={ + :return $String; + } + + :while ($String ~ $ReplaceFrom) do={ + :local Pos [ :find $String $ReplaceFrom ]; + :set Return ($Return . [ :pick $String 0 $Pos ] . $ReplaceWith); + :set String [ :pick $String ($Pos + $Len) 999 ]; + } + + :return ($Return . $String); +} + +# check and import required certificates +:global CertificateAvailable do={ + :local CommonName [ :tostr $1 ]; + :local FileName ([ :tostr $2 ] . ".pem"); + + :global ScriptUpdatesBaseUrl; + :global ScriptUpdatesUrlSuffix; + + :if ([ / certificate print count-only where common-name=$CommonName ] = 0) do={ + :log info ("Certificate with CommonName " . $CommonName . \ + " not available, downloading and importing."); + :do { + / tool fetch check-certificate=yes-without-crl \ + ($ScriptUpdatesBaseUrl . "certs/" . \ + $FileName . $ScriptUpdatesUrlSuffix) \ + dst-path=$FileName; + / certificate import file-name=$FileName passphrase=""; + } on-error={ + :log warning "Failed imprting certificate!"; + } + } +} + +# send notification via e-mail and telegram +# Note that attachment is ignored for telegram! +:global SendNotification do={ + :local Subject [ :tostr $1 ]; + :local Message [ :tostr $2 ]; + :local Attach [ :tostr $3 ]; + + :global Identity; + :global EmailGeneralTo; + :global EmailGeneralCc; + :global TelegramTokenId; + :global TelegramChatId; + + :global UrlEncode; + :global CertificateAvailable; + + :if ([ :len $EmailGeneralTo ] > 0) do={ + :do { + / tool e-mail send to=$EmailGeneralTo cc=$EmailGeneralCc \ + subject=("[" . $Identity . "] " . $Subject) body=$Message file=$Attach; + } on-error={ + :log warning "Failed sending notification mail!"; + } + } + + :if ([ :len $TelegramTokenId ] > 0 && [ :len $TelegramChatId ] > 0) do={ + $CertificateAvailable "Go Daddy Secure Certificate Authority - G2" "godaddy"; + :do { + / tool fetch check-certificate=yes-without-crl keep-result=no http-method=post \ + ("https://api.telegram.org/bot" . $TelegramTokenId . "/sendMessage") \ + http-data=("chat_id=" . $TelegramChatId . "&text=" . \ + [ $UrlEncode ("[" . $Identity . "] " . $Subject . "\n\n" . $Message) ]); + } on-error={ + :log warning "Failed sending telegram notification!"; + } + } +} + +# get MAC vendor +:global GetMacVendor do={ + :local Mac [ :tostr $1 ]; + + :global CertificateAvailable; + + :do { + :local Vendor; + $CertificateAvailable "Let's Encrypt Authority X3" "letsencrypt"; + :set Vendor ([ / tool fetch mode=https check-certificate=yes-without-crl \ + ("https://api.macvendors.com/" . [ :pick $Mac 0 8 ]) output=user as-value ]->"data"); + :return $Vendor; + } on-error={ + :return "unknown vendor"; + } +} + +# clean file path +:global CleanFilePath do={ + :local Path [ :tostr $1 ]; + + :global CharacterReplace; + + :while ($Path ~ "//") do={ + :set $Path [ $CharacterReplace $Path "//" "/" ]; + } + :if ([ :pick $Path 0 ] = "/") do={ + :set Path [ :pick $Path 1 [ :len $Path ] ]; + } + :if ([ :pick $Path ([ :len $Path ] - 1) ] = "/") do={ + :set Path [ :pick $Path 0 ([ :len $Path ] - 1) ]; + } + + :return $Path; +} + +# download package from upgrade server +:global DownloadPackage do={ + :local PkgName [ :tostr $1 ]; + :local PkgVer [ :tostr $2 ]; + :local PkgArch [ :tostr $3 ]; + :local PkgDir [ :tostr $4 ]; + + :global CertificateAvailable; + :global CleanFilePath; + + :if ([ :len $PkgName ] = 0) do={ return false; } + :if ([ :len $PkgVer ] = 0) do={ :set PkgVer [ / system package update get installed-version ]; } + :if ([ :len $PkgArch ] = 0) do={ :set PkgArch [ / system resource get architecture-name ]; } + + :local PkgFile ($PkgName . "-" . $PkgVer . "-" . $PkgArch . ".npk"); + :local PkgDest [ $CleanFilePath ($PkgDir . "/" . $PkgFile) ]; + + $CertificateAvailable "Let's Encrypt Authority X3" "letsencrypt"; + :do { + / tool fetch mode=https check-certificate=yes-without-crl \ + ("https://upgrade.mikrotik.com/routeros/" . $PkgVer . "/" . $PkgFile) \ + dst-path=$PkgDest; + } on-error={ + / file remove [ find where name=$PkgDest ]; + :return false; + } + + :return true; +} + +# lock script against multiple invocation +:global ScriptLock do={ + :local Script [ :tostr $1 ]; + + :if ([ / system script job print count-only where script=$Script ] > 1) do={ + :log debug ("Script " . $Script . " started more than once... Aborting."); + :error "Locked." + } +} diff --git a/global-functions.rsc b/global-functions.rsc deleted file mode 100644 index 8ae7bb8..0000000 --- a/global-functions.rsc +++ /dev/null @@ -1,1777 +0,0 @@ -#!rsc by RouterOS -# RouterOS script: global-functions -# Copyright (c) 2013-2025 Christian Hesse -# Michael Gisbers -# https://rsc.eworm.de/COPYING.md -# -# requires RouterOS, version=7.15 -# requires device-mode, fetch, scheduler -# -# global functions -# https://rsc.eworm.de/ - -:local ScriptName [ :jobname ]; - -# Git commit id & info, expected configuration version -:global CommitId "unknown"; -:global CommitInfo "unknown"; -:global ExpectedConfigVersion 135; - -# global variables not to be changed by user -:global GlobalFunctionsReady false; -:global Identity [ /system/identity/get name ]; - -# global functions -:global AlignRight; -:global CertificateAvailable; -:global CertificateDownload; -:global CertificateNameByCN; -:global CharacterMultiply; -:global CharacterReplace; -:global CleanFilePath; -:global CleanName; -:global DeviceInfo; -:global Dos2Unix; -:global DownloadPackage; -:global EitherOr; -:global EscapeForRegEx; -:global ExitError; -:global FetchHuge; -:global FetchUserAgentStr; -:global FormatLine; -:global FormatMultiLines; -:global GetMacVendor; -:global GetRandom20CharAlNum; -:global GetRandom20CharHex; -:global GetRandomNumber; -:global Grep; -:global HexToNum; -:global HumanReadableNum; -:global IfThenElse; -:global IsDefaultRouteReachable; -:global IsDNSResolving; -:global IsFullyConnected; -:global IsMacLocallyAdministered; -:global IsTimeSync; -:global LogPrint; -:global LogPrintOnce; -:global LogPrintVerbose; -:global MAX; -:global MIN; -:global MkDir; -:global NotificationFunctions; -:global ParseDate; -:global ParseKeyValueStore; -:global PrettyPrint; -:global ProtocolStrip; -:global RandomDelay; -:global RequiredRouterOS; -:global RmDir; -:global RmFile; -:global ScriptFromTerminal; -:global ScriptInstallUpdate; -:global ScriptLock; -:global SendNotification; -:global SendNotification2; -:global SymbolByUnicodeName; -:global SymbolForNotification; -:global Unix2Dos; -:global UrlEncode; -:global ValidateSyntax; -:global VersionToNum; -:global WaitDefaultRouteReachable; -:global WaitDNSResolving; -:global WaitForFile; -:global WaitFullyConnected; -:global WaitTimeSync; - -# align string to the right -:set AlignRight do={ - :local Input [ :tostr $1 ]; - :local Len [ :tonum $2 ]; - - :global CharacterMultiply; - :global EitherOr; - - :set Len [ $EitherOr $Len 8 ]; - :local Spaces [ $CharacterMultiply " " $Len ]; - - :return ([ :pick $Spaces 0 ($Len - [ :len $Input ]) ] . $Input); -} - -# check and download required certificate -:set CertificateAvailable do={ - :local CommonName [ :tostr $1 ]; - - :global CertificateDownload; - :global LogPrint; - :global ParseKeyValueStore; - - :if ([ /system/resource/get free-hdd-space ] < 8388608 && \ - [ /certificate/settings/get crl-download ] = true && \ - [ /certificate/settings/get crl-store ] = "system") do={ - $LogPrint warning $0 ("This system has low free flash space but " . \ - "is configured to download certificate CRLs to system!"); - } - - :if ([ :len $CommonName ] = 0) do={ - $LogPrint warning $0 ("No CommonName given!"); - :return false; - } - - :if ([ :len [ /certificate/find where common-name=$CommonName ] ] = 0) do={ - $LogPrint info $0 ("Certificate with CommonName '" . $CommonName . "' not available."); - :if ([ $CertificateDownload $CommonName ] = false) do={ - :return false; - } - } - - :local CertVal [ /certificate/get [ find where common-name=$CommonName ] ]; - :while (($CertVal->"akid") != "" && ($CertVal->"akid") != ($CertVal->"skid")) do={ - :if ([ :len [ /certificate/find where skid=($CertVal->"akid") ] ] = 0) do={ - $LogPrint info $0 ("Certificate chain for '" . $CommonName . \ - "' is incomplete, missing '" . ([ $ParseKeyValueStore ($CertVal->"issuer") ]->"CN") . "'."); - :if ([ $CertificateDownload $CommonName ] = false) do={ - :return false; - } - } - :set CertVal [ /certificate/get [ find where skid=($CertVal->"akid") ] ]; - } - :return true; -} - -# download and import certificate -:set CertificateDownload do={ - :local CommonName [ :tostr $1 ]; - - :global ScriptUpdatesBaseUrl; - :global ScriptUpdatesUrlSuffix; - - :global CertificateAvailable; - :global CertificateNameByCN; - :global CleanName; - :global FetchUserAgentStr; - :global LogPrint; - :global RmFile; - :global WaitForFile; - - $LogPrint info $0 ("Downloading and importing certificate with " . \ - "CommonName '" . $CommonName . "'."); - :local FileName ([ $CleanName $CommonName ] . ".pem"); - :do { - /tool/fetch check-certificate=yes-without-crl http-header-field=({ [ $FetchUserAgentStr $0 ] }) \ - ($ScriptUpdatesBaseUrl . "certs/" . $FileName . $ScriptUpdatesUrlSuffix) \ - dst-path=$FileName as-value; - $WaitForFile $FileName; - } on-error={ - $LogPrint warning $0 ("Failed downloading certificate with CommonName '" . $CommonName . \ - "' from repository! Trying fallback to mkcert.org..."); - :do { - :if ([ $CertificateAvailable "ISRG Root X1" ] = false) do={ - $LogPrint error $0 ("Downloading required certificate failed."); - :return false; - } - /tool/fetch check-certificate=yes-without-crl http-header-field=({ [ $FetchUserAgentStr $0 ] }) \ - "https://mkcert.org/generate/" http-data=[ :serialize to=json ({ $CommonName }) ] \ - dst-path=$FileName as-value; - $WaitForFile $FileName; - :if ([ /file/get $FileName size ] = 0) do={ - $RmFile $FileName; - :error false; - } - } on-error={ - $LogPrint warning $0 ("Failed downloading certificate with CommonName '" . $CommonName . "'!"); - :return false; - } - } - - /certificate/import file-name=$FileName passphrase="" as-value; - :delay 1s; - $RmFile $FileName; - - :if ([ :len [ /certificate/find where common-name=$CommonName ] ] = 0) do={ - /certificate/remove [ find where name~("^" . $FileName . "_[0-9]+\$") ]; - $LogPrint warning $0 ("Certificate with CommonName '" . $CommonName . "' still unavailable!"); - :return false; - } - - :foreach Cert in=[ /certificate/find where name~("^" . $FileName . "_[0-9]+\$") ] do={ - $CertificateNameByCN [ /certificate/get $Cert common-name ]; - } - :return true; -} - -# name a certificate by its common-name -:set CertificateNameByCN do={ - :local CommonName [ :tostr $1 ]; - - :global CleanName; - - :local Cert [ /certificate/find where common-name=$CommonName ]; - /certificate/set $Cert name=[ $CleanName $CommonName ]; -} - -# multiply given character(s) -:set CharacterMultiply do={ - :local Return ""; - :for I from=1 to=$2 do={ - :set Return ($Return . $1); - } - :return $Return; -} - -# character replace -:set CharacterReplace do={ - :local String [ :tostr $1 ]; - :local ReplaceFrom [ :tostr $2 ]; - :local ReplaceWith [ :tostr $3 ]; - :local Return ""; - - :if ($ReplaceFrom = "") do={ - :return $String; - } - - :while ([ :typeof [ :find $String $ReplaceFrom ] ] != "nil") do={ - :local Pos [ :find $String $ReplaceFrom ]; - :set Return ($Return . [ :pick $String 0 $Pos ] . $ReplaceWith); - :set String [ :pick $String ($Pos + [ :len $ReplaceFrom ]) [ :len $String ] ]; - } - - :return ($Return . $String); -} - -# clean file path -:set CleanFilePath do={ - :local Path [ :tostr $1 ]; - - :global CharacterReplace; - - :while ($Path ~ "//") do={ - :set $Path [ $CharacterReplace $Path "//" "/" ]; - } - :if ([ :pick $Path 0 ] = "/") do={ - :set Path [ :pick $Path 1 [ :len $Path ] ]; - } - :if ([ :pick $Path ([ :len $Path ] - 1) ] = "/") do={ - :set Path [ :pick $Path 0 ([ :len $Path ] - 1) ]; - } - - :return $Path; -} - -# clean name for DNS, file and more -:set CleanName do={ - :local Input [ :tostr $1 ]; - - :local Return ""; - - :for I from=0 to=([ :len $Input ] - 1) do={ - :local Char [ :pick $Input $I ]; - :if ([ :typeof [ find "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" $Char ] ] = "nil") do={ - :do { - :if ([ :len $Return ] = 0) do={ - :error true; - } - :if ([ :pick $Return ([ :len $Return ] - 1) ] = "-") do={ - :error true; - } - :set Char "-"; - } on-error={ - :set Char ""; - } - } - :set Return ($Return . $Char); - } - :return $Return; -} - -# get readable device info -:set DeviceInfo do={ - :global CommitId; - :global CommitInfo; - :global ExpectedConfigVersion; - :global Identity; - - :global IfThenElse; - :global FormatLine; - - :local License [ /system/license/get ]; - :local Resource [ /system/resource/get ]; - :local RouterBoard; - :do { - :set RouterBoard [[ :parse "/system/routerboard/get" ]]; - } on-error={ } - :local Snmp [ /snmp/get ]; - :local Update [ /system/package/update/get ]; - - :return ( \ - [ $FormatLine "Hostname" $Identity ] . "\n" . \ - [ $IfThenElse ([ :len ($Snmp->"location") ] > 0) \ - ([ $FormatLine "Location" ($Snmp->"location") ] . "\n") ] . \ - [ $IfThenElse ([ :len ($Snmp->"contact") ] > 0) \ - ([ $FormatLine "Contact" ($Snmp->"contact") ] . "\n") ] . \ - "Hardware:\n" . \ - [ $FormatLine " Board" ($Resource->"board-name") ] . "\n" . \ - [ $FormatLine " Arch" ($Resource->"architecture-name") ] . "\n" . \ - [ $IfThenElse ($RouterBoard->"routerboard" = true) \ - ([ $FormatLine " Model" ($RouterBoard->"model") ] . \ - [ $IfThenElse ([ :len ($RouterBoard->"revision") ] > 0) \ - (" " . $RouterBoard->"revision") ] . "\n" . \ - [ $FormatLine " Serial" ($RouterBoard->"serial-number") ] . "\n") ] . \ - [ $IfThenElse ([ :len ($License->"nlevel") ] > 0) \ - ([ $FormatLine " License" ("level " . ($License->"nlevel")) ] . "\n") ] . \ - "RouterOS:\n" . \ - [ $IfThenElse ([ :len ($License->"level") ] > 0) \ - ([ $FormatLine " License" ("level " . ($License->"level")) ] . "\n") ] . \ - [ $FormatLine " Channel" ($Update->"channel") ] . "\n" . \ - [ $FormatLine " Installed" ($Update->"installed-version") ] . "\n" . \ - [ $IfThenElse ([ :typeof ($Update->"latest-version") ] != "nothing" && \ - $Update->"installed-version" != $Update->"latest-version") \ - ([ $FormatLine " Available" ($Update->"latest-version") ] . "\n") ] . \ - [ $IfThenElse ($RouterBoard->"routerboard" = true && \ - $RouterBoard->"current-firmware" != $RouterBoard->"upgrade-firmware") \ - ([ $FormatLine " Firmware" ($RouterBoard->"current-firmware") ] . "\n") ] . \ - "RouterOS-Scripts:\n" . \ - [ $IfThenElse ($CommitId != "unknown") \ - ([ $FormatLine " Commit" ($CommitInfo . "/" . [ :pick $CommitId 0 8 ]) ] . "\n") ] . \ - [ $FormatLine " Version" $ExpectedConfigVersion ]); -} - -# convert line endings, DOS -> UNIX -:set Dos2Unix do={ - :return [ :tolf [ :tostr $1 ] ]; -} - -# download package from upgrade server -:set DownloadPackage do={ - :local PkgName [ :tostr $1 ]; - :local PkgVer [ :tostr $2 ]; - :local PkgArch [ :tostr $3 ]; - :local PkgDir [ :tostr $4 ]; - - :global CertificateAvailable; - :global CleanFilePath; - :global LogPrint; - :global MkDir; - :global RmFile; - :global WaitForFile; - - :if ([ :len $PkgName ] = 0) do={ :return false; } - :if ([ :len $PkgVer ] = 0) do={ :set PkgVer [ /system/package/update/get installed-version ]; } - :if ([ :len $PkgArch ] = 0) do={ :set PkgArch [ /system/resource/get architecture-name ]; } - - :if ($PkgName = "system") do={ :set PkgName "routeros"; } - - :local PkgFile ($PkgName . "-" . $PkgVer . "-" . $PkgArch . ".npk"); - :if ($PkgArch = "x86_64") do={ :set PkgFile ($PkgName . "-" . $PkgVer . ".npk"); } - :local PkgDest [ $CleanFilePath ($PkgDir . "/" . $PkgFile) ]; - - :if ([ $MkDir $PkgDir ] = false) do={ - $LogPrint warning $0 ("Failed creating directory, not downloading package."); - :return false; - } - - :if ([ :len [ /file/find where name=$PkgDest type="package" ] ] > 0) do={ - $LogPrint info $0 ("Package file " . $PkgName . " already exists."); - :return true; - } - - :if ([ $CertificateAvailable "ISRG Root X1" ] = false) do={ - $LogPrint error $0 ("Downloading required certificate failed."); - :return false; - } - - :local Url ("https://upgrade.mikrotik.com/routeros/" . $PkgVer . "/" . $PkgFile); - $LogPrint info $0 ("Downloading package file '" . $PkgName . "'..."); - $LogPrint debug $0 ("... from url: " . $Url); - :local Retry 3; - :while ($Retry > 0) do={ - :do { - /tool/fetch check-certificate=yes-without-crl $Url dst-path=$PkgDest; - $WaitForFile $PkgDest; - - :if ([ /file/get [ find where name=$PkgDest ] type ] = "package") do={ - :return true; - } - } on-error={ - $LogPrint debug $0 ("Downloading package file failed."); - } - - $RmFile $PkgDest; - :set Retry ($Retry - 1); - } - - $LogPrint warning $0 ("Downloading package file '" . $PkgName . "' failed."); - :return false; -} - -# return either first (if "true") or second -:set EitherOr do={ - :global IfThenElse; - - :if ([ :typeof $1 ] = "num") do={ - :return [ $IfThenElse ($1 != 0) $1 $2 ]; - } - :if ([ :typeof $1 ] = "time") do={ - :return [ $IfThenElse ($1 > 0s) $1 $2 ]; - } - # this works for boolean values, literal ones with parentheses - :return [ $IfThenElse ([ :len [ :tostr $1 ] ] > 0) $1 $2 ]; -} - -# escape for regular expression -:set EscapeForRegEx do={ - :local Input [ :tostr $1 ]; - - :if ([ :len $Input ] = 0) do={ - :return ""; - } - - :local Return ""; - :local Chars ("^.[]\$()|*+?{}\\"); - - :for I from=0 to=([ :len $Input ] - 1) do={ - :local Char [ :pick $Input $I ]; - :if ([ :find $Chars $Char ]) do={ - :set Char ("\\" . $Char); - } - :set Return ($Return . $Char); - } - - :return $Return; -} - -# simple macro to print error message on unintentional error -:set ExitError do={ - :local ExitOK [ :tostr $1 ]; - :local Name [ :tostr $2 ]; - - :global IfThenElse; - :global LogPrint; - - :if ($ExitOK = "false") do={ - $LogPrint error $Name ([ $IfThenElse ([ :pick $Name 0 1 ] = "\$") \ - "Function" "Script" ] . " '" . $Name . "' exited with error."); - } -} - -# fetch huge data to file, read in chunks -:set FetchHuge do={ - :local ScriptName [ :tostr $1 ]; - :local Url [ :tostr $2 ]; - :local CheckCert [ :tostr $3 ]; - - :global CleanName; - :global FetchUserAgentStr; - :global GetRandom20CharAlNum; - :global IfThenElse; - :global LogPrint; - :global MkDir; - :global RmDir; - :global RmFile; - :global WaitForFile; - - :set CheckCert [ $IfThenElse ($CheckCert = "false") "no" "yes-without-crl" ]; - - :local DirName ("tmpfs/" . [ $CleanName $ScriptName ]); - :if ([ $MkDir $DirName ] = false) do={ - $LogPrint error $0 ("Failed creating directory!"); - :return false; - } - - :local FileName ($DirName . "/" . [ $CleanName $0 ] . "-" . [ $GetRandom20CharAlNum ]); - :do { - /tool/fetch check-certificate=$CheckCert $Url dst-path=$FileName \ - http-header-field=({ [ $FetchUserAgentStr $ScriptName ] }) as-value; - } on-error={ - :if ([ $WaitForFile $FileName 500ms ] = true) do={ - $RmFile $FileName; - } - $LogPrint debug $0 ("Failed downloading from: " . $Url); - $RmDir $DirName; - :return false; - } - $WaitForFile $FileName; - - :local FileSize [ /file/get $FileName size ]; - :local Return ""; - :local VarSize 0; - :while ($VarSize != $FileSize) do={ - :set Return ($Return . ([ /file/read offset=$VarSize chunk-size=32768 file=$FileName as-value ]->"data")); - :set FileSize [ /file/get $FileName size ]; - :set VarSize [ :len $Return ]; - :if ($VarSize > $FileSize) do={ - :delay 100ms; - } - } - $RmDir $DirName; - :return $Return; -} - -# generate user agent string for fetch -:set FetchUserAgentStr do={ - :local Caller [ :tostr $1 ]; - - :local Resource [ /system/resource/get ]; - - :return ("User-Agent: Mikrotik/" . $Resource->"version" . " " . \ - $Resource->"architecture-name" . " " . $Caller . "/Fetch (https://rsc.eworm.de/)"); -} - -# format a line for output -:set FormatLine do={ - :local Key [ :tostr $1 ]; - :local Value [ :tostr $2 ]; - :local Indent [ :tonum $3 ]; - :local Spaces; - :local Return ""; - - :global CharacterMultiply; - :global EitherOr; - - :set Indent [ $EitherOr $Indent 16 ]; - :local Spaces [ $CharacterMultiply " " $Indent ]; - - :if ([ :len $Key ] > 0) do={ :set Return ($Key . ":"); } - :if ([ :len $Key ] > ($Indent - 2)) do={ - :set Return ($Return . "\n" . [ :pick $Spaces 0 $Indent ] . $Value); - } else={ - :set Return ($Return . [ :pick $Spaces 0 ($Indent - [ :len $Return ]) ] . $Value); - } - - :return $Return; -} - -# format multiple lines for output -:set FormatMultiLines do={ - :local Key [ :tostr $1 ]; - :local Values [ :toarray $2 ]; - :local Indent [ :tonum $3 ]; - :local Return; - - :global FormatLine; - - :set Return [ $FormatLine $Key ($Values->0) $Indent ]; - :foreach Value in=[ :pick $Values 1 [ :len $Values ] ] do={ - :set Return ($Return . "\n" . [ $FormatLine "" $Value $Indent ]); - } - - :return $Return; -} - -# get MAC vendor -:set GetMacVendor do={ - :local Mac [ :tostr $1 ]; - - :global CertificateAvailable; - :global IsMacLocallyAdministered; - :global LogPrint; - - :if ([ $IsMacLocallyAdministered $Mac ] = true) do={ - :return "locally administered"; - } - - :do { - :if ([ $CertificateAvailable "GTS Root R4" ] = false) do={ - $LogPrint warning $0 ("Downloading required certificate failed."); - :error false; - } - :local Vendor ([ /tool/fetch check-certificate=yes-without-crl \ - ("https://api.macvendors.com/" . [ :pick $Mac 0 8 ]) output=user as-value ]->"data"); - :return $Vendor; - } on-error={ - :do { - /tool/fetch check-certificate=yes-without-crl ("https://api.macvendors.com/") \ - output=none as-value; - $LogPrint debug $0 ("The mac vendor is not known in database."); - } on-error={ - $LogPrint warning $0 ("Failed getting mac vendor."); - } - :return "unknown vendor"; - } -} - -# generate random 20 chars alphabetical (A-Z & a-z) and numerical (0-9) -:set GetRandom20CharAlNum do={ - :global EitherOr; - - :return [ :rndstr length=[ $EitherOr [ :tonum $1 ] 20 ] from="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" ]; -} - -# generate random 20 chars hex (0-9 and a-f) -:set GetRandom20CharHex do={ - :global EitherOr; - - :return [ :rndstr length=[ $EitherOr [ :tonum $1 ] 20 ] from="0123456789abcdef" ]; -} - -# generate random number -:set GetRandomNumber do={ - :global EitherOr; - - :return [ :rndnum from=0 to=[ $EitherOr [ :tonum $1 ] 4294967295 ] ]; -} - -# return first line that matches a pattern -:set Grep do={ - :local Input ([ :tostr $1 ] . "\n"); - :local Pattern [ :tostr $2 ]; - - :if ([ :typeof [ :find $Input $Pattern ] ] = "nil") do={ - :return []; - } - - :do { - :local Line [ :pick $Input 0 [ :find $Input "\n" ] ]; - :if ([ :typeof [ :find $Line $Pattern ] ] = "num") do={ - :return $Line; - } - :set Input [ :pick $Input ([ :find $Input "\n" ] + 1) [ :len $Input ] ]; - } while=([ :len $Input ] > 0); - - :return []; -} - -# convert from hex (string) to num -:set HexToNum do={ - :local Input [ :tostr $1 ]; - - :global HexToNum; - - :if ([ :pick $Input 0 ] = "*") do={ - :return [ $HexToNum [ :pick $Input 1 [ :len $Input ] ] ]; - } - - :return [ :tonum ("0x" . $Input) ]; -} - -# return human readable number -:set HumanReadableNum do={ - :local Input [ :tonum $1 ]; - :local Base [ :tonum $2 ]; - - :global EitherOr; - :global IfThenElse; - - :local Prefix "kMGTPE"; - :local Pow 1; - - :set Base [ $EitherOr $Base 1024 ]; - :local Bin [ $IfThenElse ($Base = 1024) "i" "" ]; - - :if ($Input < $Base) do={ - :return $Input; - } - - :for I from=0 to=[ :len $Prefix ] do={ - :set Pow ($Pow * $Base); - :if ($Input / $Base < $Pow) do={ - :set Prefix [ :pick $Prefix $I ]; - :local Tmp1 ($Input * 100 / $Pow); - :local Tmp2 ($Tmp1 / 100); - :if ($Tmp2 >= 100) do={ - :return ($Tmp2 . $Prefix . $Bin); - } - :return ($Tmp2 . "." . \ - [ :pick $Tmp1 [ :len $Tmp2 ] ([ :len $Tmp1 ] - [ :len $Tmp2 ] + 1) ] . \ - $Prefix . $Bin); - } - } -} - -# mimic conditional/ternary operator (condition ? consequent : alternative) -:set IfThenElse do={ - :if ([ :tostr $1 ] = "true" || [ :tobool $1 ] = true) do={ - :return $2; - } - :return $3; -} - -# check if default route is reachable -:set IsDefaultRouteReachable do={ - :if ([ :len [ /ip/route/find where dst-address=0.0.0.0/0 active routing-table=main ] ] > 0) do={ - :return true; - } - :return false; -} - -# check if DNS is resolving -:set IsDNSResolving do={ - :do { - :resolve "low-ttl.eworm.de"; - } on-error={ - :return false; - } - :return true; -} - -# check if system is is fully connected (default route reachable, DNS resolving, time sync) -:set IsFullyConnected do={ - :global IsDefaultRouteReachable; - :global IsDNSResolving; - :global IsTimeSync; - - :if ([ $IsDefaultRouteReachable ] = false) do={ - :return false; - } - :if ([ $IsDNSResolving ] = false) do={ - :return false; - } - :if ([ $IsTimeSync ] = false) do={ - :return false; - } - :return true; -} - -# check if mac address is locally administered -:set IsMacLocallyAdministered do={ - :if ([ :tonum ("0x" . [ :pick $1 0 [ :find $1 ":" ] ]) ] & 2 = 2) do={ - :return true; - } - :return false; -} - -# check if system time is sync -:set IsTimeSync do={ - :global IsTimeSyncCached; - :global IsTimeSyncResetNtp; - - :global LogPrintOnce; - - :if ($IsTimeSyncCached = true) do={ - :return true; - } - - :if ([ /system/ntp/client/get enabled ] = true) do={ - :if ([ /system/ntp/client/get status ] = "synchronized") do={ - :set IsTimeSyncCached true; - :return true; - } - - :local Uptime [ /system/resource/get uptime ]; - :if ([ :typeof $IsTimeSyncResetNtp ] = "nothing") do={ - :set IsTimeSyncResetNtp $Uptime; - } - :if ($Uptime - $IsTimeSyncResetNtp < 3m) do={ - :return false; - } - - $LogPrintOnce warning $0 ("The ntp client is configured, but did not sync."); - :set IsTimeSyncResetNtp $Uptime; - /system/ntp/client/set enabled=no; - :delay 20ms; - /system/ntp/client/set enabled=yes; - :return false; - } - - :if ([ /system/license/get ]->"level" = "free" || \ - [ /system/resource/get ]->"board-name" = "x86") do={ - $LogPrintOnce debug $0 ("No ntp client configured, relying on RTC for CHR free license and x86."); - :return true; - } - - :if ([ /ip/cloud/get update-time ] = true) do={ - :if ([ :typeof [ /ip/cloud/get public-address ] ] = "ip") do={ - :set IsTimeSyncCached true; - :return true; - } - :return false; - } - - $LogPrintOnce debug $0 ("No time source configured! Returning gracefully..."); - :return true; -} - -# log and print with same text -:set LogPrint do={ - :local Severity [ :tostr $1 ]; - :local Name [ :tostr $2 ]; - :local Message [ :tostr $3 ]; - - :global PrintDebug; - :global PrintDebugOverride; - - :global EitherOr; - - :local Debug [ $EitherOr ($PrintDebugOverride->$Name) $PrintDebug ]; - - :local PrintSeverity do={ - :global TerminalColorOutput; - - :if ($TerminalColorOutput != true) do={ - :return $1; - } - - :local Color { debug=96; info=97; warning=93; error=91 }; - :return ("\1B[" . $Color->$1 . "m" . $1 . "\1B[0m"); - } - - :local Log ([ $EitherOr $Name "" ] . ": " . $Message); - :if ($Severity ~ ("^(debug|error|info)\$")) do={ - :if ($Severity = "debug") do={ :log debug $Log; } - :if ($Severity = "error") do={ :log error $Log; } - :if ($Severity = "info" ) do={ :log info $Log; } - } else={ - :log warning $Log; - :set Severity "warning"; - } - - :if ($Severity != "debug" || $Debug = true) do={ - :put ([ $PrintSeverity $Severity ] . ": " . $Message); - } -} - -# log and print, once until reboot -:set LogPrintOnce do={ - :local Severity [ :tostr $1 ]; - :local Name [ :tostr $2 ]; - :local Message [ :tostr $3 ]; - - :global LogPrint; - - :global LogPrintOnceMessages; - - :if ([ :typeof $LogPrintOnceMessages ] = "nothing") do={ - :set LogPrintOnceMessages ({}); - } - - :if ($LogPrintOnceMessages->$Message = 1) do={ - :return false; - } - - :if ([ :len [ /log/find where message=($Name . ": " . $Message) ] ] > 0) do={ - $LogPrint warning $0 \ - ("The message is already in log, scripting subsystem may have crashed before!"); - } - - :set ($LogPrintOnceMessages->$Message) 1; - $LogPrint $Severity $Name $Message; - :return true; -} - -# The function $LogPrintVerbose is declared, but has no code, intentionally. -# https://rsc.eworm.de/DEBUG.md#verbose-output - -# get max value -:set MAX do={ - :if ($1 > $2) do={ :return $1; } - :return $2; -} - -# get min value -:set MIN do={ - :if ($1 < $2) do={ :return $1; } - :return $2; -} - -# create directory -:set MkDir do={ - :local Path [ :tostr $1 ]; - - :global CleanFilePath; - :global LogPrint; - :global RmDir; - :global WaitForFile; - - :local MkTmpfs do={ - :global LogPrint; - :global WaitForFile; - - :local TmpFs [ /disk/find where slot=tmpfs type=tmpfs ]; - :if ([ :len $TmpFs ] = 1) do={ - :if ([ /disk/get $TmpFs disabled ] = true) do={ - $LogPrint info $0 ("The tmpfs is disabled, enabling."); - /disk/enable $TmpFs; - } - :return true; - } - - $LogPrint info $0 ("Creating disk of type tmpfs."); - $RmDir "tmpfs"; - :do { - /disk/add slot=tmpfs type=tmpfs tmpfs-max-size=([ /system/resource/get total-memory ] / 3); - $WaitForFile "tmpfs"; - } on-error={ - $LogPrint warning $0 ("Creating disk of type tmpfs failed!"); - :return false; - } - :return true; - } - - :set Path [ $CleanFilePath $Path ]; - - :if ($Path = "") do={ - :return true; - } - - $LogPrint debug $0 ("Making directory: " . $Path); - - :if ([ :len [ /file/find where name=$Path type="directory" ] ] = 1) do={ - $LogPrint debug $0 ("... which already exists."); - :return true; - } - - :if ([ :pick $Path 0 5 ] = "tmpfs") do={ - :if ([ $MkTmpfs ] = false) do={ - :return false; - } - } - - :do { - /file/add type="directory" name=$Path; - $WaitForFile $Path; - } on-error={ - $LogPrint warning $0 ("Making directory '" . $Path . "' failed!"); - :return false; - } - - :return true; -} - -# prepare NotificationFunctions array -:if ([ :typeof $NotificationFunctions ] != "array") do={ - :set NotificationFunctions ({}); -} - -# parse the date and return a named array -:set ParseDate do={ - :local Date [ :tostr $1 ]; - - :return ({ "year"=[ :tonum [ :pick $Date 0 4 ] ]; - "month"=[ :tonum [ :pick $Date 5 7 ] ]; - "day"=[ :tonum [ :pick $Date 8 10 ] ] }); -} - -# parse key value store -:set ParseKeyValueStore do={ - :local Source $1; - - :if ([ :pick $Source 0 1 ] = "{") do={ - :do { - :return [ :deserialize from=json $Source ]; - } on-error={ } - } - - :if ([ :typeof $Source ] != "array") do={ - :set Source [ :tostr $1 ]; - } - :local Result ({}); - :foreach KeyValue in=[ :toarray $Source ] do={ - :if ([ :find $KeyValue "=" ]) do={ - :local Key [ :pick $KeyValue 0 [ :find $KeyValue "=" ] ]; - :local Value [ :pick $KeyValue ([ :find $KeyValue "=" ] + 1) [ :len $KeyValue ] ]; - :if ($Value="true") do={ :set Value true; } - :if ($Value="false") do={ :set Value false; } - :set ($Result->$Key) $Value; - } else={ - :set ($Result->$KeyValue) true; - } - } - :return $Result; -} - -# print lines with trailing carriage return -:set PrettyPrint do={ - :put [ :tocrlf [ :tostr $1 ] ]; -} - -# strip protocol from from url string -:set ProtocolStrip do={ - :local Input [ :tostr $1 ]; - - :local Pos [ :find $Input "://" ]; - :if ([ :typeof $Pos ] = "nil") do={ - :return $Input; - } - :return [ :pick $Input ($Pos + 3) [ :len $Input ] ]; -} - -# delay a random amount of seconds -:set RandomDelay do={ - :local Time [ :tonum $1 ]; - :local Unit [ :tostr $2 ]; - - :global EitherOr; - :global GetRandomNumber; - :global MAX; - - :if ($Time = 0) do={ - :return false; - } - - :delay ([ $MAX 10 [ $GetRandomNumber ([ :tonsec [ :totime ($Time . [ $EitherOr $Unit "s" ]) ] ] / 1000000) ] ] . "ms"); -} - -# check for required RouterOS version -:set RequiredRouterOS do={ - :local Caller [ :tostr $1 ]; - :local Required [ :tostr $2 ]; - :local Warn [ :tostr $3 ]; - - :global IfThenElse; - :global LogPrint; - :global VersionToNum; - - :if (!($Required ~ "^\\d+\\.\\d+((alpha|beta|rc|\\.)\\d+|)\$")) do={ - $LogPrint error $0 ("No valid RouterOS version: " . $Required); - :return false; - } - - :if ([ $VersionToNum $Required ] > [ $VersionToNum [ /system/package/update/get installed-version ] ]) do={ - :if ($Warn = "true") do={ - $LogPrint warning $0 ("This " . [ $IfThenElse ([ :pick $Caller 0 ] = ("\$")) "function" "script" ] . \ - " '" . $Caller . "' (at least specific functionality) requires RouterOS " . $Required . ". Please update!"); - } - :return false; - } - :return true; -} - -# remove directory -:set RmDir do={ - :local DirName [ :tostr $1 ]; - - :global LogPrint; - - $LogPrint debug $0 ("Removing directory: ". $DirName); - - :if ([ :len [ /file/find where name=$DirName type!=directory ] ] > 0) do={ - $LogPrint error $0 ("Directory '" . $DirName . "' is not a directory."); - :return false; - } - - :local Dir [ /file/find where name=$DirName type=directory ]; - :if ([ :len $Dir ] = 0) do={ - $LogPrint debug $0 ("... which does not exist."); - :return true; - } - - :do { - /file/remove $Dir; - } on-error={ - $LogPrint error $0 ("Removing directory '" . $DirName . "' (" . $Dir . ") failed."); - :return false; - } - :return true; -} - -# remove file -:set RmFile do={ - :local FileName [ :tostr $1 ]; - - :global LogPrint; - - $LogPrint debug $0 ("Removing file: ". $FileName); - - :if ([ :len [ /file/find where name=$FileName (type=directory or type=disk) ] ] > 0) do={ - $LogPrint error $0 ("File '" . $FileName . "' is not a file."); - :return false; - } - - :local File [ /file/find where name=$FileName !(type=directory or type=disk) ]; - :if ([ :len $File ] = 0) do={ - $LogPrint debug $0 ("... which does not exist."); - :return true; - } - - :do { - /file/remove $File; - } on-error={ - $LogPrint error $0 ("Removing file '" . $FileName . "' (" . $File . ") failed."); - :return false; - } - :return true; -} - -# check if script is run from terminal -:set ScriptFromTerminal do={ - :local Script [ :tostr $1 ]; - - :global LogPrint; - :global ScriptLock; - - :if ([ $ScriptLock $Script ] = false) do={ - :return false; - } - - :foreach Job in=[ /system/script/job/find where script=$Script ] do={ - :set Job [ /system/script/job/get $Job ]; - :while ([ :typeof ($Job->"parent") ] = "id") do={ - :set Job [ /system/script/job/get [ find where .id=($Job->"parent") ] ]; - } - :if (($Job->"type") = "login") do={ - $LogPrint debug $0 ("Script " . $Script . " started from terminal."); - :return true; - } - } - - $LogPrint debug $0 ("Script " . $Script . " NOT started from terminal."); - :return false; -} - -# install new scripts, update existing scripts -:set ScriptInstallUpdate do={ :do { - :local Scripts [ :toarray $1 ]; - :local NewComment [ :tostr $2 ]; - - :global CommitId; - :global CommitInfo; - :global ExpectedConfigVersion; - :global Identity; - :global IDonate; - :global NoNewsAndChangesNotification; - :global ScriptUpdatesBaseUrl; - :global ScriptUpdatesCRLF; - :global ScriptUpdatesUrlSuffix; - - :global CertificateAvailable; - :global EitherOr; - :global FetchUserAgentStr; - :global Grep; - :global IfThenElse; - :global LogPrint; - :global LogPrintOnce; - :global ParseKeyValueStore; - :global RequiredRouterOS; - :global SendNotification2; - :global SymbolForNotification; - :global ValidateSyntax; - - :if ([ $CertificateAvailable "ISRG Root X2" ] = false) do={ - $LogPrint warning $0 ("Downloading certificate failed, trying without."); - } - - :foreach Script in=$Scripts do={ - :if ([ :len [ /system/script/find where name=$Script ] ] = 0) do={ - $LogPrint info $0 ("Adding new script: " . $Script); - /system/script/add name=$Script owner=$Script source="#!rsc by RouterOS\n" comment=$NewComment; - } - } - - :local CommitIdBefore $CommitId; - :local ExpectedConfigVersionBefore $ExpectedConfigVersion; - :local ReloadGlobalFunctions false; - :local ReloadGlobalConfig false; - :local DeviceMode [ /system/device-mode/get ]; - - :local CheckSums ({}); - :do { - :local Url ($ScriptUpdatesBaseUrl . "checksums.json" . $ScriptUpdatesUrlSuffix); - $LogPrint debug $0 ("Fetching checksums from url: " . $Url); - :set CheckSums [ :deserialize from=json ([ /tool/fetch check-certificate=yes-without-crl \ - http-header-field=({ [ $FetchUserAgentStr $0 ] }) $Url output=user as-value ]->"data") ]; - } on-error={ } - - :foreach Script in=[ /system/script/find where source~"^#!rsc by RouterOS\r?\n" ] do={ - :local ScriptVal [ /system/script/get $Script ]; - :local ScriptInfo [ $ParseKeyValueStore ($ScriptVal->"comment") ]; - :local SourceNew; - - :foreach Scheduler in=[ /system/scheduler/find where on-event~("\\b" . $ScriptVal->"name" . "\\b") ] do={ - :local SchedulerVal [ /system/scheduler/get $Scheduler ]; - :if ($ScriptVal->"policy" != $SchedulerVal->"policy") do={ - $LogPrint warning $0 ("Policies differ for script '" . $ScriptVal->"name" . \ - "' and its scheduler '" . $SchedulerVal->"name" . "'!"); - } - } - - :do { - :if ($ScriptInfo->"ignore" = true) do={ - $LogPrint debug $0 ("Ignoring script '" . $ScriptVal->"name" . "', as requested."); - :error true; - } - - :local CheckSum ($CheckSums->($ScriptVal->"name")); - :if ([ :len ($ScriptInfo->"base-url") ] = 0 && [ :len ($ScriptInfo->"url-suffix") ] = 0 && \ - [ :convert transform=md5 to=hex [ :tolf ($ScriptVal->"source") ] ] = $CheckSum) do={ - $LogPrint debug $0 ("Checksum for script '" . $ScriptVal->"name" . "' matches, ignoring."); - :error true; - } - - :do { - :local BaseUrl [ $EitherOr ($ScriptInfo->"base-url") $ScriptUpdatesBaseUrl ]; - :local UrlSuffix [ $EitherOr ($ScriptInfo->"url-suffix") $ScriptUpdatesUrlSuffix ]; - :local Url ($BaseUrl . $ScriptVal->"name" . ".rsc" . $UrlSuffix); - $LogPrint debug $0 ("Fetching script '" . $ScriptVal->"name" . "' from url: " . $Url); - :local Result [ /tool/fetch check-certificate=yes-without-crl \ - http-header-field=({ [ $FetchUserAgentStr $0 ] }) $Url output=user as-value ]; - :if ($Result->"status" = "finished") do={ - :set SourceNew [ :tolf ($Result->"data") ]; - } - } on-error={ - :if ($ScriptVal->"source" = "#!rsc by RouterOS\n") do={ - $LogPrint warning $0 ("Failed fetching script '" . $ScriptVal->"name" . \ - "', removing dummy. Typo on installation?"); - /system/script/remove $Script; - } else={ - $LogPrint warning $0 ("Failed fetching script '" . $ScriptVal->"name" . "'!"); - } - :error false; - } - - :if ([ :len $SourceNew ] = 0) do={ - $LogPrint debug $0 ("No update for script '" . $ScriptVal->"name" . "'."); - :error false; - } - - :local SourceCRLF [ :tocrlf $SourceNew ]; - :if ($SourceNew = $ScriptVal->"source" || $SourceCRLF = $ScriptVal->"source") do={ - $LogPrint debug $0 ("Script '" . $ScriptVal->"name" . "' did not change."); - :error false; - } - - :if ([ :pick $SourceNew 0 18 ] != "#!rsc by RouterOS\n") do={ - $LogPrint warning $0 ("Looks like new script '" . $ScriptVal->"name" . \ - "' is not valid (missing shebang). Ignoring!"); - :error false; - } - - :local RequiredROS ([ $ParseKeyValueStore [ $Grep $SourceNew ("\23 requires RouterOS, ") ] ]->"version"); - :if ([ $RequiredRouterOS $0 [ $EitherOr $RequiredROS "0.0" ] false ] = false) do={ - $LogPrintOnce warning $0 ("The script '" . $ScriptVal->"name" . "' requires RouterOS " . \ - $RequiredROS . ", which is not met by your installation. Ignoring!"); - :error false; - } - - :local RequiredDM [ $ParseKeyValueStore [ $Grep $SourceNew ("\23 requires device-mode, ") ] ]; - :local MissingDM ({}); - :foreach Feature,Value in=$RequiredDM do={ - :if ([ :typeof ($DeviceMode->$Feature) ] = "bool" && ($DeviceMode->$Feature) = false) do={ - :set MissingDM ($MissingDM, $Feature); - } - } - :if ([ :len $MissingDM ] > 0) do={ - $LogPrintOnce warning $0 ("The script '" . $ScriptVal->"name" . "' requires disabled " . \ - "device-mode features (" . [ :tostr $MissingDM ] . "). Ignoring!"); - :error false; - } - - :if ([ $ValidateSyntax $SourceNew ] = false) do={ - $LogPrint warning $0 ("Syntax validation for script '" . $ScriptVal->"name" . "' failed! Ignoring!"); - :error false; - } - - $LogPrint info $0 ("Updating script: " . $ScriptVal->"name"); - /system/script/set owner=($ScriptVal->"name") \ - source=[ $IfThenElse ($ScriptUpdatesCRLF = true) $SourceCRLF $SourceNew ] $Script; - :if ($ScriptVal->"name" = "global-config") do={ - :set ReloadGlobalConfig true; - } - :if ($ScriptVal->"name" = "global-functions" || $ScriptVal->"name" ~ ("^mod/.")) do={ - :set ReloadGlobalFunctions true; - } - } on-error={ } - } - - :if ($ReloadGlobalFunctions = true) do={ - $LogPrint info $0 ("Reloading global functions."); - :do { - /system/script/run global-functions; - } on-error={ - $LogPrint error $0 ("Reloading global functions failed!"); - } - } - - :if ($ReloadGlobalConfig = true) do={ - $LogPrint info $0 ("Reloading global configuration."); - :do { - /system/script/run global-config; - } on-error={ - $LogPrint error $0 ("Reloading global configuration failed!" . \ - " Syntax error or missing overlay?"); - } - } - - :if ($CommitId != "unknown" && $CommitIdBefore != $CommitId) do={ - $LogPrint info $0 ("Updated to commit: " . $CommitInfo . "/" . [ :pick $CommitId 0 8 ]); - } - - :if ($ExpectedConfigVersionBefore > $ExpectedConfigVersion) do={ - $LogPrint warning $0 ("The configuration version decreased from " . \ - $ExpectedConfigVersionBefore . " to " . $ExpectedConfigVersion . \ - ". Installed an older version?"); - } - - :if ($ExpectedConfigVersionBefore < $ExpectedConfigVersion) do={ - :global GlobalConfigChanges; - :global GlobalConfigMigration; - :local ChangeLogCode; - - :do { - :local Url ($ScriptUpdatesBaseUrl . "news-and-changes.rsc" . $ScriptUpdatesUrlSuffix); - $LogPrint debug $0 ("Fetching news, changes and migration: " . $Url); - :local Result [ /tool/fetch check-certificate=yes-without-crl \ - http-header-field=({ [ $FetchUserAgentStr $0 ] }) $Url output=user as-value ]; - :if ($Result->"status" = "finished") do={ - :set ChangeLogCode ($Result->"data"); - } - } on-error={ - $LogPrint warning $0 ("Failed fetching news, changes and migration!"); - } - - :if ([ :len $ChangeLogCode ] > 0) do={ - :if ([ $ValidateSyntax $ChangeLogCode ] = true) do={ - :do { - [ :parse $ChangeLogCode ]; - } on-error={ - $LogPrint warning $0 ("The changelog failed to run!"); - } - } else={ - $LogPrint warning $0 ("The changelog failed syntax validation!"); - } - } - - :if ([ :len $GlobalConfigMigration ] > 0) do={ - :for I from=($ExpectedConfigVersionBefore + 1) to=$ExpectedConfigVersion do={ - :local Migration ($GlobalConfigMigration->[ :tostr $I ]); - :do { - :if ([ :typeof $Migration ] != "str") do={ - $LogPrint debug $0 ("Migration code for change " . $I . " is not available."); - :error false; - } - - :if ([ $ValidateSyntax $Migration ] = false) do={ - $LogPrint warning $0 ("Migration code for change " . $I . " failed syntax validation!"); - :error false; - } - - $LogPrint info $0 ("Applying migration for change " . $I . ": " . $Migration); - :do { - [ :parse $Migration ]; - } on-error={ - $LogPrint warning $0 ("Migration code for change " . $I . " failed to run!"); - } - } on-error={ } - } - } - - :local NotificationMessage ("The configuration version on " . $Identity . " increased " . \ - "to " . $ExpectedConfigVersion . ", current configuration may need modification. " . \ - "Please review and update global-config-overlay, then re-run global-config."); - $LogPrint info $0 ($NotificationMessage); - - :if ([ :len $GlobalConfigChanges ] > 0) do={ - :set NotificationMessage ($NotificationMessage . "\n\nChanges:"); - :for I from=($ExpectedConfigVersionBefore + 1) to=$ExpectedConfigVersion do={ - :local Change ($GlobalConfigChanges->[ :tostr $I ]); - :set NotificationMessage ($NotificationMessage . "\n " . \ - [ $SymbolForNotification "pushpin" "*" ] . $Change); - $LogPrint info $0 ("Change " . $I . ": " . $Change); - } - } else={ - :set NotificationMessage ($NotificationMessage . "\n\nNews and changes are not available."); - } - - :if ($NoNewsAndChangesNotification != true) do={ - :local Link; - :if ($IDonate != true) do={ - :set NotificationMessage ($NotificationMessage . \ - "\n\n==== donation hint ====\n" . \ - "This project is developed in private spare time and usage is " . \ - "free of charge for you. If you like the scripts and think this is " . \ - "of value for you or your business please consider a donation."); - :set Link "https://rsc.eworm.de/#donate"; - } - - $SendNotification2 ({ origin=$0; \ - subject=([ $SymbolForNotification "pushpin" ] . "News and configuration changes"); \ - message=$NotificationMessage; link=$Link }); - } - - :set GlobalConfigChanges; - :set GlobalConfigMigration; - } -} on-error={ - :global ExitError; $ExitError false $0; -} } - -# lock script against multiple invocation -:set ScriptLock do={ - :local Script [ :tostr $1 ]; - :local WaitMax ([ :tonum $3 ] * 10); - - :global GetRandom20CharAlNum; - :global IfThenElse; - :global LogPrint; - - :global ScriptLockOrder; - :if ([ :typeof $ScriptLockOrder ] = "nothing") do={ - :set ScriptLockOrder ({}); - } - :if ([ :typeof ($ScriptLockOrder->$Script) ] = "nothing") do={ - :set ($ScriptLockOrder->$Script) ({}); - } - - :local JobCount do={ - :local Script [ :tostr $1 ]; - - :return [ :len [ /system/script/job/find where script=$Script ] ]; - } - - :local TicketCount do={ - :local Script [ :tostr $1 ]; - - :global ScriptLockOrder; - - :local Count 0; - :foreach Ticket in=($ScriptLockOrder->$Script) do={ - :if ([ :typeof $Ticket ] != "nothing") do={ - :set Count ($Count + 1); - } - } - :return $Count; - } - - :local IsFirstTicket do={ - :local Script [ :tostr $1 ]; - :local Check [ :tostr $2 ]; - - :global ScriptLockOrder; - - :foreach Ticket in=($ScriptLockOrder->$Script) do={ - :if ($Ticket = $Check) do={ :return true; } - :if ([ :typeof $Ticket ] != "nothing" && $Ticket != $Check) do={ :return false; } - } - :return false; - } - - :local AddTicket do={ - :local Script [ :tostr $1 ]; - :local Add [ :tostr $2 ]; - - :global ScriptLockOrder; - - :while (true) do={ - :local Pos [ :len ($ScriptLockOrder->$Script) ]; - :set ($ScriptLockOrder->$Script->$Pos) $Add; - :delay 10ms; - :if (($ScriptLockOrder->$Script->$Pos) = $Add) do={ :return true; } - } - } - - :local RemoveTicket do={ - :local Script [ :tostr $1 ]; - :local Remove [ :tostr $2 ]; - - :global ScriptLockOrder; - - :foreach Id,Ticket in=($ScriptLockOrder->$Script) do={ - :while (($ScriptLockOrder->$Script->$Id) = $Remove) do={ - :set ($ScriptLockOrder->$Script->$Id); - :delay 10ms; - } - } - } - - :local CleanupTickets do={ - :local Script [ :tostr $1 ]; - - :global ScriptLockOrder; - - :foreach Ticket in=($ScriptLockOrder->$Script) do={ - :if ([ :typeof $Ticket ] != "nothing") do={ - :return false; - } - } - - :set ($ScriptLockOrder->$Script) ({}); - } - - :if ([ :len [ /system/script/find where name=$Script ] ] = 0) do={ - $LogPrint error $0 ("A script named '" . $Script . "' does not exist!"); - :error false; - } - - :if ([ $JobCount $Script ] = 0) do={ - $LogPrint error $0 ("No script '" . $Script . "' is running!"); - :error false; - } - - :if ([ $TicketCount $Script ] >= [ $JobCount $Script ]) do={ - $LogPrint error $0 ("More tickets than running scripts '" . $Script . "', resetting!"); - :set ($ScriptLockOrder->$Script) ({}); - /system/script/job/remove [ find where script=$Script ]; - } - - :local MyTicket [ $GetRandom20CharAlNum 6 ]; - $AddTicket $Script $MyTicket; - - :local WaitCount 0; - :while ($WaitMax > $WaitCount && \ - ([ $IsFirstTicket $Script $MyTicket ] = false || \ - [ $TicketCount $Script ] < [ $JobCount $Script ])) do={ - :set WaitCount ($WaitCount + 1); - :delay 100ms; - } - - :if ([ $IsFirstTicket $Script $MyTicket ] = true && \ - [ $TicketCount $Script ] = [ $JobCount $Script ]) do={ - $RemoveTicket $Script $MyTicket; - $CleanupTickets $Script; - :return true; - } - - $RemoveTicket $Script $MyTicket; - $LogPrint debug $0 ("Script '" . $Script . "' started more than once" . \ - [ $IfThenElse ($WaitCount > 0) " and timed out waiting for lock" "" ] . "..."); - :return false; -} - -# send notification via NotificationFunctions - expects at least two string arguments -:set SendNotification do={ :do { - :global SendNotification2; - - $SendNotification2 ({ origin=$0; subject=$1; message=$2; link=$3; silent=$4 }); -} on-error={ - :global ExitError; $ExitError false $0; -} } - -# send notification via NotificationFunctions - expects one array argument -:set SendNotification2 do={ - :local Notification $1; - - :global NotificationFunctions; - - :foreach FunctionName,Discard in=$NotificationFunctions do={ - ($NotificationFunctions->$FunctionName) \ - ("\$NotificationFunctions->\"" . $FunctionName . "\"") \ - $Notification; - } -} - -# return UTF-8 symbol for unicode name -:set SymbolByUnicodeName do={ - :local Name [ :tostr $1 ]; - - :global LogPrintOnce; - - :local Symbols { - "abacus"="\F0\9F\A7\AE"; - "alarm-clock"="\E2\8F\B0"; - "arrow-down"="\E2\AC\87"; - "arrow-up"="\E2\AC\86"; - "calendar"="\F0\9F\93\85"; - "card-file-box"="\F0\9F\97\83"; - "chart-decreasing"="\F0\9F\93\89"; - "chart-increasing"="\F0\9F\93\88"; - "cloud"="\E2\98\81"; - "cross-mark"="\E2\9D\8C"; - "earth"="\F0\9F\8C\8D"; - "fire"="\F0\9F\94\A5"; - "floppy-disk"="\F0\9F\92\BE"; - "gear"="\E2\9A\99"; - "heart"="\E2\99\A5"; - "high-voltage-sign"="\E2\9A\A1"; - "incoming-envelope"="\F0\9F\93\A8"; - "information"="\E2\84\B9"; - "large-orange-circle"="\F0\9F\9F\A0"; - "large-red-circle"="\F0\9F\94\B4"; - "link"="\F0\9F\94\97"; - "lock-with-ink-pen"="\F0\9F\94\8F"; - "memo"="\F0\9F\93\9D"; - "mobile-phone"="\F0\9F\93\B1"; - "pushpin"="\F0\9F\93\8C"; - "scissors"="\E2\9C\82"; - "smiley-partying-face"="\F0\9F\A5\B3"; - "smiley-smiling-face"="\E2\98\BA"; - "smiley-winking-face-with-tongue"="\F0\9F\98\9C"; - "sparkles"="\E2\9C\A8"; - "speech-balloon"="\F0\9F\92\AC"; - "star"="\E2\AD\90"; - "warning-sign"="\E2\9A\A0"; - "white-heavy-check-mark"="\E2\9C\85" - } - - :if ([ :len ($Symbols->$Name) ] = 0) do={ - $LogPrintOnce warning $0 ("No symbol available for name '" . $Name . "'!"); - :return ""; - } - - :return (($Symbols->$Name) . "\EF\B8\8F"); -} - -# return symbol for notification -:set SymbolForNotification do={ - :global NotificationsWithSymbols; - :global SymbolByUnicodeName; - :global IfThenElse; - - :if ($NotificationsWithSymbols != true) do={ - :return [ $IfThenElse ([ :len $2 ] > 0) ([ :tostr $2 ] . " ") "" ]; - } - :local Return ""; - :foreach Symbol in=[ :toarray $1 ] do={ - :set Return ($Return . [ $SymbolByUnicodeName $Symbol ]); - } - :return ($Return . " "); -} - -# convert line endings, UNIX -> DOS -:set Unix2Dos do={ - :return [ :tocrlf [ :tostr $1 ] ]; -} - -# url encoding -:set UrlEncode do={ - :local Input [ :tostr $1 ]; - - :if ([ :len $Input ] = 0) do={ - :return ""; - } - - :local Return ""; - :local Chars ("\n\r !\"#\$%&'()*+,:;<=>?@[\\]^`{|}~"); - :local Subs { "%0A"; "%0D"; "%20"; "%21"; "%22"; "%23"; "%24"; "%25"; "%26"; "%27"; - "%28"; "%29"; "%2A"; "%2B"; "%2C"; "%3A"; "%3B"; "%3C"; "%3D"; "%3E"; "%3F"; - "%40"; "%5B"; "%5C"; "%5D"; "%5E"; "%60"; "%7B"; "%7C"; "%7D"; "%7E" }; - - :for I from=0 to=([ :len $Input ] - 1) do={ - :local Char [ :pick $Input $I ]; - :local Replace [ :find $Chars $Char ]; - - :if ([ :typeof $Replace ] = "num") do={ - :set Char ($Subs->$Replace); - } - :set Return ($Return . $Char); - } - - :return $Return; -} - -# basic syntax validation -:set ValidateSyntax do={ - :local Code [ :tostr $1 ]; - - :do { - [ :parse (":local Validate do={\n" . $Code . "\n}") ]; - } on-error={ - :return false; - } - :return true; -} - -# convert version string to numeric value -:set VersionToNum do={ - :local Input [ :tostr $1 ]; - :local Multi 0x1000000; - :local Return 0; - - :global CharacterReplace; - - :set Input [ $CharacterReplace $Input "." "," ]; - :foreach I in={ "zero"; "alpha"; "beta"; "rc" } do={ - :set Input [ $CharacterReplace $Input $I ("," . $I . ",") ]; - } - - :foreach Value in=([ :toarray $Input ], 0) do={ - :local Num [ :tonum $Value ]; - :if ($Multi = 0x100) do={ - :if ([ :typeof $Num ] = "num") do={ - :set Return ($Return + 0xff00); - :set Multi ($Multi / 0x100); - } else={ - :if ($Value = "zero") do={ } - :if ($Value = "alpha") do={ :set Return ($Return + 0x3f00); } - :if ($Value = "beta") do={ :set Return ($Return + 0x5f00); } - :if ($Value = "rc") do={ :set Return ($Return + 0x7f00); } - } - } - :if ([ :typeof $Num ] = "num") do={ :set Return ($Return + ($Value * $Multi)); } - :set Multi ($Multi / 0x100); - } - - :return $Return; -} - -# wait for default route to be reachable -:set WaitDefaultRouteReachable do={ - :global IsDefaultRouteReachable; - - :while ([ $IsDefaultRouteReachable ] = false) do={ - :delay 1s; - } -} - -# wait for DNS to resolve -:set WaitDNSResolving do={ - :global IsDNSResolving; - - :while ([ $IsDNSResolving ] = false) do={ - :delay 1s; - } -} - -# wait for file to be available -:set WaitForFile do={ - :local FileName [ :tostr $1 ]; - :local WaitTime [ :totime $2 ]; - - :global CleanFilePath; - :global EitherOr; - :global MAX; - - :set FileName [ $CleanFilePath $FileName ]; - :local I 1; - :local Delay ([ $MAX [ $EitherOr $WaitTime 2s ] 100ms ] / 10); - - :while ([ :len [ /file/find where name=$FileName ] ] = 0) do={ - :if ($I >= 10) do={ - :return false; - } - :delay $Delay; - :set I ($I + 1); - } - - :while ([ :len [ /file/find where name=$FileName ] ] > 0) do={ - :do { - /file/get $FileName; - :return true; - } on-error={ } - :delay $Delay; - :set Delay ($Delay * 3 / 2); - } - - :return false; -} - -# wait to be fully connected (default route is reachable, time is sync, DNS resolves) -:set WaitFullyConnected do={ - :global WaitDefaultRouteReachable; - :global WaitDNSResolving; - :global WaitTimeSync; - - $WaitDefaultRouteReachable; - $WaitTimeSync; - $WaitDNSResolving; -} - -# wait for time to become synced -:set WaitTimeSync do={ - :global IsTimeSync; - - :while ([ $IsTimeSync ] = false) do={ - :delay 1s; - } -} - -# load modules -:foreach Script in=[ /system/script/find where name ~ "^mod/." ] do={ - :local ScriptVal [ /system/script/get $Script ]; - :if ([ $ValidateSyntax ($ScriptVal->"source") ] = true) do={ - :do { - /system/script/run $Script; - } on-error={ - $LogPrint error $0 ("Module '" . $ScriptVal->"name" . "' failed to run."); - } - } else={ - $LogPrint error $0 ("Module '" . $ScriptVal->"name" . "' failed syntax validation, skipping."); - } -} - -# Log success -:local Resource [ /system/resource/get ]; -$LogPrintOnce info $ScriptName ("Loaded on " . $Resource->"board-name" . \ - " with RouterOS " . $Resource->"version" . "."); - -# signal we are ready -:set GlobalFunctionsReady true; diff --git a/global-wait.rsc b/global-wait.rsc deleted file mode 100644 index ca3fc0c..0000000 --- a/global-wait.rsc +++ /dev/null @@ -1,12 +0,0 @@ -#!rsc by RouterOS -# RouterOS script: global-wait -# Copyright (c) 2020-2025 Christian Hesse -# https://rsc.eworm.de/COPYING.md -# -# requires RouterOS, version=7.15 -# -# wait for global-functions to finish -# https://rsc.eworm.de/doc/global-wait.md - -:global GlobalFunctionsReady; -:while ($GlobalFunctionsReady != true) do={ :delay 500ms; } diff --git a/gps-track b/gps-track new file mode 100644 index 0000000..3870c60 --- /dev/null +++ b/gps-track @@ -0,0 +1,29 @@ +#!rsc +# RouterOS script: gps-track +# Copyright (c) 2018-2019 Christian Hesse +# +# track gps data by sending json data to http server + +:global Identity; +:global GpsTrackUrl; + +:local CoordinateFormat [ /system gps get coordinate-format ]; +:local Gps [ / system gps monitor once as-value ]; + +if ($Gps->"valid" = true) do={ + :set ($Gps->"latitude") [ :pick ($Gps->"latitude") 0 [ :find ($Gps->"latitude") "\00" ] ]; + :set ($Gps->"longitude") [ :pick ($Gps->"longitude") 0 [ :find ($Gps->"longitude") "\00" ] ]; + :tool fetch mode=https check-certificate=yes-without-crl \ + $GpsTrackUrl keep-result=no \ + http-method=post http-header-field="Content-Type: application/json" \ + http-data=("{" . \ + "\"lat\":\"" . ($Gps->"latitude") . "\"," . \ + "\"lon\":\"" . ($Gps->"longitude") . "\"," . \ + "\"identity\":\"" . $Identity . "\"" . \ + "}"); + :log debug ("Sending GPS data in " . $CoordinateFormat . " format: " . \ + "lat: " . ($Gps->"latitude") . " " . \ + "lon: " . ($Gps->"longitude")); +} else={ + :log debug ("GPS data not valid."); +} diff --git a/gps-track.rsc b/gps-track.rsc deleted file mode 100644 index dea56d2..0000000 --- a/gps-track.rsc +++ /dev/null @@ -1,53 +0,0 @@ -#!rsc by RouterOS -# RouterOS script: gps-track -# Copyright (c) 2018-2025 Christian Hesse -# https://rsc.eworm.de/COPYING.md -# -# requires RouterOS, version=7.15 -# requires device-mode, fetch -# -# track gps data by sending json data to http server -# https://rsc.eworm.de/doc/gps-track.md - -:global GlobalFunctionsReady; -:while ($GlobalFunctionsReady != true) do={ :delay 500ms; } - -:local ExitOK false; -:do { - :local ScriptName [ :jobname ]; - - :global GpsTrackUrl; - :global Identity; - - :global FetchUserAgentStr; - :global LogPrint; - :global ScriptLock; - :global WaitFullyConnected; - - :if ([ $ScriptLock $ScriptName ] = false) do={ - :set ExitOK true; - :error false; - } - $WaitFullyConnected; - - :local CoordinateFormat [ /system/gps/get coordinate-format ]; - :local Gps [ /system/gps/monitor once as-value ]; - - :if ($Gps->"valid" = true) do={ - :do { - /tool/fetch check-certificate=yes-without-crl output=none http-method=post \ - http-header-field=({ [ $FetchUserAgentStr $ScriptName ]; "Content-Type: application/json" }) \ - http-data=[ :serialize to=json { "identity"=$Identity; \ - "lat"=($Gps->"latitude"); "lon"=($Gps->"longitude") } ] $GpsTrackUrl as-value; - $LogPrint debug $ScriptName ("Sending GPS data in " . $CoordinateFormat . " format: " . \ - "lat: " . ($Gps->"latitude") . " " . \ - "lon: " . ($Gps->"longitude")); - } on-error={ - $LogPrint warning $ScriptName ("Failed sending GPS data!"); - } - } else={ - $LogPrint debug $ScriptName ("GPS data not valid."); - } -} on-error={ - :global ExitError; $ExitError $ExitOK [ :jobname ]; -} diff --git a/hotspot-to-wpa-cleanup.capsman.rsc b/hotspot-to-wpa-cleanup.capsman.rsc deleted file mode 100644 index 033d0e7..0000000 --- a/hotspot-to-wpa-cleanup.capsman.rsc +++ /dev/null @@ -1,80 +0,0 @@ -#!rsc by RouterOS -# RouterOS script: hotspot-to-wpa-cleanup.capsman -# Copyright (c) 2021-2025 Christian Hesse -# https://rsc.eworm.de/COPYING.md -# -# provides: lease-script, order=80 -# requires RouterOS, version=7.15 -# requires device-mode, hotspot -# -# manage and clean up private WPA passphrase after hotspot login -# https://rsc.eworm.de/doc/hotspot-to-wpa.md -# -# !! Do not edit this file, it is generated from template! - -:global GlobalFunctionsReady; -:while ($GlobalFunctionsReady != true) do={ :delay 500ms; } - -:local ExitOK false; -:do { - :local ScriptName [ :jobname ]; - - :global EitherOr; - :global LogPrint; - :global ParseKeyValueStore; - :global ScriptLock; - - :if ([ $ScriptLock $ScriptName 10 ] = false) do={ - :set ExitOK true; - :error false; - } - - :local DHCPServers ({}); - :foreach Server in=[ /ip/dhcp-server/find where comment~"hotspot-to-wpa" ] do={ - :local ServerVal [ /ip/dhcp-server/get $Server ] - :local ServerInfo [ $ParseKeyValueStore ($ServerVal->"comment") ]; - :if (($ServerInfo->"hotspot-to-wpa") = "wpa") do={ - :set ($DHCPServers->($ServerVal->"name")) \ - [ :totime [ $EitherOr ($ServerInfo->"timeout") 4w ] ]; - } - } - - :foreach Client in=[ /caps-man/registration-table/find where comment~"^hotspot-to-wpa:" ] do={ - :local ClientVal [ /caps-man/registration-table/get $Client ]; - :foreach Lease in=[ /ip/dhcp-server/lease/find where dynamic \ - mac-address=($ClientVal->"mac-address") ] do={ - :if (($DHCPServers->[ /ip/dhcp-server/lease/get $Lease server ]) > 0s) do={ - $LogPrint info $ScriptName ("Client with mac address " . ($ClientVal->"mac-address") . \ - " connected to WPA, making lease static."); - /ip/dhcp-server/lease/make-static $Lease; - /ip/dhcp-server/lease/set comment=($ClientVal->"comment") $Lease; - } - } - } - - :foreach Client in=[ /caps-man/access-list/find where comment~"^hotspot-to-wpa:" \ - !(comment~[ /system/clock/get date ]) mac-address ] do={ - :local ClientVal [ /caps-man/access-list/get $Client ]; - :if ([ :len [ /ip/dhcp-server/lease/find where !dynamic comment~"^hotspot-to-wpa:" \ - mac-address=($ClientVal->"mac-address") ] ] = 0) do={ - $LogPrint info $ScriptName ("Client with mac address " . ($ClientVal->"mac-address") . \ - " did not connect to WPA, removing from access list."); - /caps-man/access-list/remove $Client; - } - } - - :foreach Server,Timeout in=$DHCPServers do={ - :local TimeoutExtra ($Timeout + [ /system/clock/get time ]); - :foreach Lease in=[ /ip/dhcp-server/lease/find where !dynamic status="waiting" \ - server=$Server last-seen>$TimeoutExtra comment~"^hotspot-to-wpa:" ] do={ - :local LeaseVal [ /ip/dhcp-server/lease/get $Lease ]; - $LogPrint info $ScriptName ("Client with mac address " . ($LeaseVal->"mac-address") . \ - " was not seen for " . ($LeaseVal->"last-seen") . ", removing."); - /caps-man/access-list/remove [ find where comment~"^hotspot-to-wpa:" \ - mac-address=($LeaseVal->"mac-address") ]; - /ip/dhcp-server/lease/remove $Lease; - } - } -} on-error={ - :global ExitError; $ExitError $ExitOK [ :jobname ]; -} diff --git a/hotspot-to-wpa-cleanup.template.rsc b/hotspot-to-wpa-cleanup.template.rsc deleted file mode 100644 index 0f8c490..0000000 --- a/hotspot-to-wpa-cleanup.template.rsc +++ /dev/null @@ -1,87 +0,0 @@ -#!rsc by RouterOS -# RouterOS script: hotspot-to-wpa-cleanup%TEMPL% -# Copyright (c) 2021-2025 Christian Hesse -# https://rsc.eworm.de/COPYING.md -# -# provides: lease-script, order=80 -# requires RouterOS, version=7.15 -# requires device-mode, hotspot -# -# manage and clean up private WPA passphrase after hotspot login -# https://rsc.eworm.de/doc/hotspot-to-wpa.md -# -# !! This is just a template to generate the real script! -# !! Pattern '%TEMPL%' is replaced, paths are filtered. - -:global GlobalFunctionsReady; -:while ($GlobalFunctionsReady != true) do={ :delay 500ms; } - -:local ExitOK false; -:do { - :local ScriptName [ :jobname ]; - - :global EitherOr; - :global LogPrint; - :global ParseKeyValueStore; - :global ScriptLock; - - :if ([ $ScriptLock $ScriptName 10 ] = false) do={ - :set ExitOK true; - :error false; - } - - :local DHCPServers ({}); - :foreach Server in=[ /ip/dhcp-server/find where comment~"hotspot-to-wpa" ] do={ - :local ServerVal [ /ip/dhcp-server/get $Server ] - :local ServerInfo [ $ParseKeyValueStore ($ServerVal->"comment") ]; - :if (($ServerInfo->"hotspot-to-wpa") = "wpa") do={ - :set ($DHCPServers->($ServerVal->"name")) \ - [ :totime [ $EitherOr ($ServerInfo->"timeout") 4w ] ]; - } - } - - :foreach Client in=[ /caps-man/registration-table/find where comment~"^hotspot-to-wpa:" ] do={ - :foreach Client in=[ /interface/wifi/registration-table/find where comment~"^hotspot-to-wpa:" ] do={ - :local ClientVal [ /caps-man/registration-table/get $Client ]; - :local ClientVal [ /interface/wifi/registration-table/get $Client ]; - :foreach Lease in=[ /ip/dhcp-server/lease/find where dynamic \ - mac-address=($ClientVal->"mac-address") ] do={ - :if (($DHCPServers->[ /ip/dhcp-server/lease/get $Lease server ]) > 0s) do={ - $LogPrint info $ScriptName ("Client with mac address " . ($ClientVal->"mac-address") . \ - " connected to WPA, making lease static."); - /ip/dhcp-server/lease/make-static $Lease; - /ip/dhcp-server/lease/set comment=($ClientVal->"comment") $Lease; - } - } - } - - :foreach Client in=[ /caps-man/access-list/find where comment~"^hotspot-to-wpa:" \ - :foreach Client in=[ /interface/wifi/access-list/find where comment~"^hotspot-to-wpa:" \ - !(comment~[ /system/clock/get date ]) mac-address ] do={ - :local ClientVal [ /caps-man/access-list/get $Client ]; - :local ClientVal [ /interface/wifi/access-list/get $Client ]; - :if ([ :len [ /ip/dhcp-server/lease/find where !dynamic comment~"^hotspot-to-wpa:" \ - mac-address=($ClientVal->"mac-address") ] ] = 0) do={ - $LogPrint info $ScriptName ("Client with mac address " . ($ClientVal->"mac-address") . \ - " did not connect to WPA, removing from access list."); - /caps-man/access-list/remove $Client; - /interface/wifi/access-list/remove $Client; - } - } - - :foreach Server,Timeout in=$DHCPServers do={ - :local TimeoutExtra ($Timeout + [ /system/clock/get time ]); - :foreach Lease in=[ /ip/dhcp-server/lease/find where !dynamic status="waiting" \ - server=$Server last-seen>$TimeoutExtra comment~"^hotspot-to-wpa:" ] do={ - :local LeaseVal [ /ip/dhcp-server/lease/get $Lease ]; - $LogPrint info $ScriptName ("Client with mac address " . ($LeaseVal->"mac-address") . \ - " was not seen for " . ($LeaseVal->"last-seen") . ", removing."); - /caps-man/access-list/remove [ find where comment~"^hotspot-to-wpa:" \ - /interface/wifi/access-list/remove [ find where comment~"^hotspot-to-wpa:" \ - mac-address=($LeaseVal->"mac-address") ]; - /ip/dhcp-server/lease/remove $Lease; - } - } -} on-error={ - :global ExitError; $ExitError $ExitOK [ :jobname ]; -} diff --git a/hotspot-to-wpa-cleanup.wifi.rsc b/hotspot-to-wpa-cleanup.wifi.rsc deleted file mode 100644 index dfec697..0000000 --- a/hotspot-to-wpa-cleanup.wifi.rsc +++ /dev/null @@ -1,80 +0,0 @@ -#!rsc by RouterOS -# RouterOS script: hotspot-to-wpa-cleanup.wifi -# Copyright (c) 2021-2025 Christian Hesse -# https://rsc.eworm.de/COPYING.md -# -# provides: lease-script, order=80 -# requires RouterOS, version=7.15 -# requires device-mode, hotspot -# -# manage and clean up private WPA passphrase after hotspot login -# https://rsc.eworm.de/doc/hotspot-to-wpa.md -# -# !! Do not edit this file, it is generated from template! - -:global GlobalFunctionsReady; -:while ($GlobalFunctionsReady != true) do={ :delay 500ms; } - -:local ExitOK false; -:do { - :local ScriptName [ :jobname ]; - - :global EitherOr; - :global LogPrint; - :global ParseKeyValueStore; - :global ScriptLock; - - :if ([ $ScriptLock $ScriptName 10 ] = false) do={ - :set ExitOK true; - :error false; - } - - :local DHCPServers ({}); - :foreach Server in=[ /ip/dhcp-server/find where comment~"hotspot-to-wpa" ] do={ - :local ServerVal [ /ip/dhcp-server/get $Server ] - :local ServerInfo [ $ParseKeyValueStore ($ServerVal->"comment") ]; - :if (($ServerInfo->"hotspot-to-wpa") = "wpa") do={ - :set ($DHCPServers->($ServerVal->"name")) \ - [ :totime [ $EitherOr ($ServerInfo->"timeout") 4w ] ]; - } - } - - :foreach Client in=[ /interface/wifi/registration-table/find where comment~"^hotspot-to-wpa:" ] do={ - :local ClientVal [ /interface/wifi/registration-table/get $Client ]; - :foreach Lease in=[ /ip/dhcp-server/lease/find where dynamic \ - mac-address=($ClientVal->"mac-address") ] do={ - :if (($DHCPServers->[ /ip/dhcp-server/lease/get $Lease server ]) > 0s) do={ - $LogPrint info $ScriptName ("Client with mac address " . ($ClientVal->"mac-address") . \ - " connected to WPA, making lease static."); - /ip/dhcp-server/lease/make-static $Lease; - /ip/dhcp-server/lease/set comment=($ClientVal->"comment") $Lease; - } - } - } - - :foreach Client in=[ /interface/wifi/access-list/find where comment~"^hotspot-to-wpa:" \ - !(comment~[ /system/clock/get date ]) mac-address ] do={ - :local ClientVal [ /interface/wifi/access-list/get $Client ]; - :if ([ :len [ /ip/dhcp-server/lease/find where !dynamic comment~"^hotspot-to-wpa:" \ - mac-address=($ClientVal->"mac-address") ] ] = 0) do={ - $LogPrint info $ScriptName ("Client with mac address " . ($ClientVal->"mac-address") . \ - " did not connect to WPA, removing from access list."); - /interface/wifi/access-list/remove $Client; - } - } - - :foreach Server,Timeout in=$DHCPServers do={ - :local TimeoutExtra ($Timeout + [ /system/clock/get time ]); - :foreach Lease in=[ /ip/dhcp-server/lease/find where !dynamic status="waiting" \ - server=$Server last-seen>$TimeoutExtra comment~"^hotspot-to-wpa:" ] do={ - :local LeaseVal [ /ip/dhcp-server/lease/get $Lease ]; - $LogPrint info $ScriptName ("Client with mac address " . ($LeaseVal->"mac-address") . \ - " was not seen for " . ($LeaseVal->"last-seen") . ", removing."); - /interface/wifi/access-list/remove [ find where comment~"^hotspot-to-wpa:" \ - mac-address=($LeaseVal->"mac-address") ]; - /ip/dhcp-server/lease/remove $Lease; - } - } -} on-error={ - :global ExitError; $ExitError $ExitOK [ :jobname ]; -} diff --git a/hotspot-to-wpa.capsman.rsc b/hotspot-to-wpa.capsman.rsc deleted file mode 100644 index 3f51475..0000000 --- a/hotspot-to-wpa.capsman.rsc +++ /dev/null @@ -1,105 +0,0 @@ -#!rsc by RouterOS -# RouterOS script: hotspot-to-wpa.capsman -# Copyright (c) 2019-2025 Christian Hesse -# https://rsc.eworm.de/COPYING.md -# -# requires RouterOS, version=7.15 -# requires device-mode, hotspot -# -# add private WPA passphrase after hotspot login -# https://rsc.eworm.de/doc/hotspot-to-wpa.md -# -# !! Do not edit this file, it is generated from template! - -:global GlobalFunctionsReady; -:while ($GlobalFunctionsReady != true) do={ :delay 500ms; } - -:local ExitOK false; -:do { - :local ScriptName [ :jobname ]; - - :global EitherOr; - :global LogPrint; - :global ParseKeyValueStore; - :global ScriptLock; - - :local MacAddress $"mac-address"; - :local UserName $username; - - :if ([ $ScriptLock $ScriptName ] = false) do={ - :set ExitOK true; - :error false; - } - - :if ([ :typeof $MacAddress ] = "nothing" || [ :typeof $UserName ] = "nothing") do={ - $LogPrint error $ScriptName ("This script is supposed to run from hotspot on login."); - :set ExitOK true; - :error false; - } - - :local Date [ /system/clock/get date ]; - :local UserVal ({}); - :if ([ :len [ /ip/hotspot/user/find where name=$UserName ] ] > 0) do={ - :set UserVal [ /ip/hotspot/user/get [ find where name=$UserName ] ]; - } - :local UserInfo [ $ParseKeyValueStore ($UserVal->"comment") ]; - :local Hotspot [ /ip/hotspot/host/get [ find where mac-address=$MacAddress authorized ] server ]; - - :if ([ :len [ /caps-man/access-list/find where comment="--- hotspot-to-wpa above ---" disabled ] ] = 0) do={ - /caps-man/access-list/add comment="--- hotspot-to-wpa above ---" disabled=yes; - $LogPrint warning $ScriptName ("Added disabled access-list entry with comment '--- hotspot-to-wpa above ---'."); - } - :local PlaceBefore ([ /caps-man/access-list/find where comment="--- hotspot-to-wpa above ---" disabled ]->0); - - :if ([ :len [ /caps-man/access-list/find where \ - comment=("hotspot-to-wpa template " . $Hotspot) disabled ] ] = 0) do={ - /caps-man/access-list/add comment=("hotspot-to-wpa template " . $Hotspot) disabled=yes place-before=$PlaceBefore; - $LogPrint warning $ScriptName ("Added template in access-list for hotspot '" . $Hotspot . "'."); - } - :local Template [ /caps-man/access-list/get ([ find where \ - comment=("hotspot-to-wpa template " . $Hotspot) disabled ]->0) ]; - - :if ($Template->"action" = "reject") do={ - $LogPrint info $ScriptName ("Ignoring login for hotspot '" . $Hotspot . "'."); - :set ExitOK true; - :error true; - } - - # allow login page to load - :delay 1s; - - $LogPrint info $ScriptName ("Adding/updating access-list entry for mac address " . $MacAddress . \ - " (user " . $UserName . ")."); - /caps-man/access-list/remove [ find where mac-address=$MacAddress comment~"^hotspot-to-wpa: " ]; - /caps-man/access-list/add private-passphrase=($UserVal->"password") ssid-regexp="-wpa\$" \ - mac-address=$MacAddress comment=("hotspot-to-wpa: " . $UserName . ", " . $MacAddress . ", " . $Date) \ - action=reject place-before=$PlaceBefore; - - :local Entry [ /caps-man/access-list/find where mac-address=$MacAddress \ - comment=("hotspot-to-wpa: " . $UserName . ", " . $MacAddress . ", " . $Date) ]; - :local PrivatePassphrase [ $EitherOr ($UserInfo->"private-passphrase") ($Template->"private-passphrase") ]; - :if ([ :len $PrivatePassphrase ] > 0) do={ - :if ($PrivatePassphrase = "ignore") do={ - /caps-man/access-list/set $Entry !private-passphrase; - } else={ - /caps-man/access-list/set $Entry private-passphrase=$PrivatePassphrase; - } - } - :local SsidRegexp [ $EitherOr ($UserInfo->"ssid-regexp") ($Template->"ssid-regexp") ]; - :if ([ :len $SsidRegexp ] > 0) do={ - /caps-man/access-list/set $Entry ssid-regexp=$SsidRegexp; - } - :local VlanId [ $EitherOr ($UserInfo->"vlan-id") ($Template->"vlan-id") ]; - :if ([ :len $VlanId ] > 0) do={ - /caps-man/access-list/set $Entry vlan-id=$VlanId; - } - :local VlanMode [ $EitherOr ($UserInfo->"vlan-mode") ($Template->"vlan-mode") ]; - :if ([ :len $VlanMode] > 0) do={ - /caps-man/access-list/set $Entry vlan-mode=$VlanMode; - } - - :delay 2s; - /caps-man/access-list/set $Entry action=accept; -} on-error={ - :global ExitError; $ExitError $ExitOK [ :jobname ]; -} diff --git a/hotspot-to-wpa.template.rsc b/hotspot-to-wpa.template.rsc deleted file mode 100644 index 068241d..0000000 --- a/hotspot-to-wpa.template.rsc +++ /dev/null @@ -1,125 +0,0 @@ -#!rsc by RouterOS -# RouterOS script: hotspot-to-wpa%TEMPL% -# Copyright (c) 2019-2025 Christian Hesse -# https://rsc.eworm.de/COPYING.md -# -# requires RouterOS, version=7.15 -# requires device-mode, hotspot -# -# add private WPA passphrase after hotspot login -# https://rsc.eworm.de/doc/hotspot-to-wpa.md -# -# !! This is just a template to generate the real script! -# !! Pattern '%TEMPL%' is replaced, paths are filtered. - -:global GlobalFunctionsReady; -:while ($GlobalFunctionsReady != true) do={ :delay 500ms; } - -:local ExitOK false; -:do { - :local ScriptName [ :jobname ]; - - :global EitherOr; - :global LogPrint; - :global ParseKeyValueStore; - :global ScriptLock; - - :local MacAddress $"mac-address"; - :local UserName $username; - - :if ([ $ScriptLock $ScriptName ] = false) do={ - :set ExitOK true; - :error false; - } - - :if ([ :typeof $MacAddress ] = "nothing" || [ :typeof $UserName ] = "nothing") do={ - $LogPrint error $ScriptName ("This script is supposed to run from hotspot on login."); - :set ExitOK true; - :error false; - } - - :local Date [ /system/clock/get date ]; - :local UserVal ({}); - :if ([ :len [ /ip/hotspot/user/find where name=$UserName ] ] > 0) do={ - :set UserVal [ /ip/hotspot/user/get [ find where name=$UserName ] ]; - } - :local UserInfo [ $ParseKeyValueStore ($UserVal->"comment") ]; - :local Hotspot [ /ip/hotspot/host/get [ find where mac-address=$MacAddress authorized ] server ]; - - :if ([ :len [ /caps-man/access-list/find where comment="--- hotspot-to-wpa above ---" disabled ] ] = 0) do={ - :if ([ :len [ /interface/wifi/access-list/find where comment="--- hotspot-to-wpa above ---" disabled ] ] = 0) do={ - /caps-man/access-list/add comment="--- hotspot-to-wpa above ---" disabled=yes; - /interface/wifi/access-list/add comment="--- hotspot-to-wpa above ---" disabled=yes; - $LogPrint warning $ScriptName ("Added disabled access-list entry with comment '--- hotspot-to-wpa above ---'."); - } - :local PlaceBefore ([ /caps-man/access-list/find where comment="--- hotspot-to-wpa above ---" disabled ]->0); - :local PlaceBefore ([ /interface/wifi/access-list/find where comment="--- hotspot-to-wpa above ---" disabled ]->0); - - :if ([ :len [ /caps-man/access-list/find where \ - :if ([ :len [ /interface/wifi/access-list/find where \ - comment=("hotspot-to-wpa template " . $Hotspot) disabled ] ] = 0) do={ - /caps-man/access-list/add comment=("hotspot-to-wpa template " . $Hotspot) disabled=yes place-before=$PlaceBefore; - /interface/wifi/access-list/add comment=("hotspot-to-wpa template " . $Hotspot) disabled=yes place-before=$PlaceBefore; - $LogPrint warning $ScriptName ("Added template in access-list for hotspot '" . $Hotspot . "'."); - } - :local Template [ /caps-man/access-list/get ([ find where \ - :local Template [ /interface/wifi/access-list/get ([ find where \ - comment=("hotspot-to-wpa template " . $Hotspot) disabled ]->0) ]; - - :if ($Template->"action" = "reject") do={ - $LogPrint info $ScriptName ("Ignoring login for hotspot '" . $Hotspot . "'."); - :set ExitOK true; - :error true; - } - - # allow login page to load - :delay 1s; - - $LogPrint info $ScriptName ("Adding/updating access-list entry for mac address " . $MacAddress . \ - " (user " . $UserName . ")."); - /caps-man/access-list/remove [ find where mac-address=$MacAddress comment~"^hotspot-to-wpa: " ]; - /interface/wifi/access-list/remove [ find where mac-address=$MacAddress comment~"^hotspot-to-wpa: " ]; - /caps-man/access-list/add private-passphrase=($UserVal->"password") ssid-regexp="-wpa\$" \ - /interface/wifi/access-list/add passphrase=($UserVal->"password") ssid-regexp="-wpa\$" \ - mac-address=$MacAddress comment=("hotspot-to-wpa: " . $UserName . ", " . $MacAddress . ", " . $Date) \ - action=reject place-before=$PlaceBefore; - - :local Entry [ /caps-man/access-list/find where mac-address=$MacAddress \ - :local Entry [ /interface/wifi/access-list/find where mac-address=$MacAddress \ - comment=("hotspot-to-wpa: " . $UserName . ", " . $MacAddress . ", " . $Date) ]; -# NOT /caps-man/ # - :set ($Template->"private-passphrase") ($Template->"passphrase"); -# NOT /caps-man/ # - :local PrivatePassphrase [ $EitherOr ($UserInfo->"private-passphrase") ($Template->"private-passphrase") ]; - :if ([ :len $PrivatePassphrase ] > 0) do={ - :if ($PrivatePassphrase = "ignore") do={ - /caps-man/access-list/set $Entry !private-passphrase; - /interface/wifi/access-list/set $Entry !passphrase; - } else={ - /caps-man/access-list/set $Entry private-passphrase=$PrivatePassphrase; - /interface/wifi/access-list/set $Entry passphrase=$PrivatePassphrase; - } - } - :local SsidRegexp [ $EitherOr ($UserInfo->"ssid-regexp") ($Template->"ssid-regexp") ]; - :if ([ :len $SsidRegexp ] > 0) do={ - /caps-man/access-list/set $Entry ssid-regexp=$SsidRegexp; - /interface/wifi/access-list/set $Entry ssid-regexp=$SsidRegexp; - } - :local VlanId [ $EitherOr ($UserInfo->"vlan-id") ($Template->"vlan-id") ]; - :if ([ :len $VlanId ] > 0) do={ - /caps-man/access-list/set $Entry vlan-id=$VlanId; - /interface/wifi/access-list/set $Entry vlan-id=$VlanId; - } -# NOT /interface/wifi/ # - :local VlanMode [ $EitherOr ($UserInfo->"vlan-mode") ($Template->"vlan-mode") ]; - :if ([ :len $VlanMode] > 0) do={ - /caps-man/access-list/set $Entry vlan-mode=$VlanMode; - } -# NOT /interface/wifi/ # - - :delay 2s; - /caps-man/access-list/set $Entry action=accept; - /interface/wifi/access-list/set $Entry action=accept; -} on-error={ - :global ExitError; $ExitError $ExitOK [ :jobname ]; -} diff --git a/hotspot-to-wpa.wifi.rsc b/hotspot-to-wpa.wifi.rsc deleted file mode 100644 index cc5e2fc..0000000 --- a/hotspot-to-wpa.wifi.rsc +++ /dev/null @@ -1,102 +0,0 @@ -#!rsc by RouterOS -# RouterOS script: hotspot-to-wpa.wifi -# Copyright (c) 2019-2025 Christian Hesse -# https://rsc.eworm.de/COPYING.md -# -# requires RouterOS, version=7.15 -# requires device-mode, hotspot -# -# add private WPA passphrase after hotspot login -# https://rsc.eworm.de/doc/hotspot-to-wpa.md -# -# !! Do not edit this file, it is generated from template! - -:global GlobalFunctionsReady; -:while ($GlobalFunctionsReady != true) do={ :delay 500ms; } - -:local ExitOK false; -:do { - :local ScriptName [ :jobname ]; - - :global EitherOr; - :global LogPrint; - :global ParseKeyValueStore; - :global ScriptLock; - - :local MacAddress $"mac-address"; - :local UserName $username; - - :if ([ $ScriptLock $ScriptName ] = false) do={ - :set ExitOK true; - :error false; - } - - :if ([ :typeof $MacAddress ] = "nothing" || [ :typeof $UserName ] = "nothing") do={ - $LogPrint error $ScriptName ("This script is supposed to run from hotspot on login."); - :set ExitOK true; - :error false; - } - - :local Date [ /system/clock/get date ]; - :local UserVal ({}); - :if ([ :len [ /ip/hotspot/user/find where name=$UserName ] ] > 0) do={ - :set UserVal [ /ip/hotspot/user/get [ find where name=$UserName ] ]; - } - :local UserInfo [ $ParseKeyValueStore ($UserVal->"comment") ]; - :local Hotspot [ /ip/hotspot/host/get [ find where mac-address=$MacAddress authorized ] server ]; - - :if ([ :len [ /interface/wifi/access-list/find where comment="--- hotspot-to-wpa above ---" disabled ] ] = 0) do={ - /interface/wifi/access-list/add comment="--- hotspot-to-wpa above ---" disabled=yes; - $LogPrint warning $ScriptName ("Added disabled access-list entry with comment '--- hotspot-to-wpa above ---'."); - } - :local PlaceBefore ([ /interface/wifi/access-list/find where comment="--- hotspot-to-wpa above ---" disabled ]->0); - - :if ([ :len [ /interface/wifi/access-list/find where \ - comment=("hotspot-to-wpa template " . $Hotspot) disabled ] ] = 0) do={ - /interface/wifi/access-list/add comment=("hotspot-to-wpa template " . $Hotspot) disabled=yes place-before=$PlaceBefore; - $LogPrint warning $ScriptName ("Added template in access-list for hotspot '" . $Hotspot . "'."); - } - :local Template [ /interface/wifi/access-list/get ([ find where \ - comment=("hotspot-to-wpa template " . $Hotspot) disabled ]->0) ]; - - :if ($Template->"action" = "reject") do={ - $LogPrint info $ScriptName ("Ignoring login for hotspot '" . $Hotspot . "'."); - :set ExitOK true; - :error true; - } - - # allow login page to load - :delay 1s; - - $LogPrint info $ScriptName ("Adding/updating access-list entry for mac address " . $MacAddress . \ - " (user " . $UserName . ")."); - /interface/wifi/access-list/remove [ find where mac-address=$MacAddress comment~"^hotspot-to-wpa: " ]; - /interface/wifi/access-list/add passphrase=($UserVal->"password") ssid-regexp="-wpa\$" \ - mac-address=$MacAddress comment=("hotspot-to-wpa: " . $UserName . ", " . $MacAddress . ", " . $Date) \ - action=reject place-before=$PlaceBefore; - - :local Entry [ /interface/wifi/access-list/find where mac-address=$MacAddress \ - comment=("hotspot-to-wpa: " . $UserName . ", " . $MacAddress . ", " . $Date) ]; - :set ($Template->"private-passphrase") ($Template->"passphrase"); - :local PrivatePassphrase [ $EitherOr ($UserInfo->"private-passphrase") ($Template->"private-passphrase") ]; - :if ([ :len $PrivatePassphrase ] > 0) do={ - :if ($PrivatePassphrase = "ignore") do={ - /interface/wifi/access-list/set $Entry !passphrase; - } else={ - /interface/wifi/access-list/set $Entry passphrase=$PrivatePassphrase; - } - } - :local SsidRegexp [ $EitherOr ($UserInfo->"ssid-regexp") ($Template->"ssid-regexp") ]; - :if ([ :len $SsidRegexp ] > 0) do={ - /interface/wifi/access-list/set $Entry ssid-regexp=$SsidRegexp; - } - :local VlanId [ $EitherOr ($UserInfo->"vlan-id") ($Template->"vlan-id") ]; - :if ([ :len $VlanId ] > 0) do={ - /interface/wifi/access-list/set $Entry vlan-id=$VlanId; - } - - :delay 2s; - /interface/wifi/access-list/set $Entry action=accept; -} on-error={ - :global ExitError; $ExitError $ExitOK [ :jobname ]; -} diff --git a/initial-commands b/initial-commands new file mode 100644 index 0000000..c42b001 --- /dev/null +++ b/initial-commands @@ -0,0 +1,28 @@ +#!rsc +# RouterOS script: initial-commands +# Copyright (c) 2018-2019 Christian Hesse + +{ + / tool fetch "https://git.eworm.de/cgit.cgi/routeros-scripts/plain/certs/letsencrypt.pem" dst-path="letsencrypt.pem"; + :delay 1s; + / certificate { + import file-name=letsencrypt.pem passphrase=""; + set name="ISRG-Root-X1" [ find where fingerprint="96bcec06264976f37460779acf28c5a7cfe8a3c0aae11a8ffcee05c0bddf08c6" ]; + set name="Let-s-Encrypt-Authority-X3" [ find where fingerprint="731d3d9cfaa061487a1d71445a42f67df0afca2a6c2d2f98ff7b3ce112b1f568" ]; + set name="DST-Root-CA-X3" [ find where fingerprint="0687260331a72403d909f105e69bcf0d32e1bd2493ffc6d9206d11bcd6770739" ]; + } + :if ([ / certificate print count-only where fingerprint="96bcec06264976f37460779acf28c5a7cfe8a3c0aae11a8ffcee05c0bddf08c6" or fingerprint="731d3d9cfaa061487a1d71445a42f67df0afca2a6c2d2f98ff7b3ce112b1f568" or fingerprint="0687260331a72403d909f105e69bcf0d32e1bd2493ffc6d9206d11bcd6770739" ] != 3) do={ + :error "Anything is wrong with your certificates!"; + } + :foreach Script in={ "global-config"; "global-functions"; "script-updates" } do={ + / system script add name=$Script source=([ / tool fetch check-certificate=yes-without-crl ("https://git.eworm.de/cgit.cgi/routeros-scripts/plain/" . $Script) output=user as-value]->"data"); + } + / system script { + run global-config; + run global-functions; + } + / system scheduler { + add name=global-config start-time=startup on-event=global-config; + add name=global-functions start-time=startup on-event=global-functions; + } +} diff --git a/ip-addr-bridge b/ip-addr-bridge new file mode 100644 index 0000000..f503310 --- /dev/null +++ b/ip-addr-bridge @@ -0,0 +1,16 @@ +#!rsc +# RouterOS script: ip-addr-bridge +# Copyright (c) 2018-2019 Christian Hesse +# +# enable or disable ip addresses based on bridge port state + +:foreach Bridge in=[ / interface bridge find ] do={ + :local BrName [ / interface bridge get $Bridge name ]; + :if ([ / interface bridge port print count-only where bridge=$BrName ] > 0) do={ + :if ([ / interface bridge port print count-only where bridge=$BrName and inactive=no ] = 0) do={ + / ip address disable [ find where !dynamic interface=$BrName ]; + } else={ + / ip address enable [ find where !dynamic interface=$BrName ]; + } + } +} diff --git a/ip-addr-bridge.rsc b/ip-addr-bridge.rsc deleted file mode 100644 index 68ff4a4..0000000 --- a/ip-addr-bridge.rsc +++ /dev/null @@ -1,18 +0,0 @@ -#!rsc by RouterOS -# RouterOS script: ip-addr-bridge -# Copyright (c) 2018-2025 Christian Hesse -# https://rsc.eworm.de/COPYING.md -# -# enable or disable ip addresses based on bridge port state -# https://rsc.eworm.de/doc/ip-addr-bridge.md - -:foreach Bridge in=[ /interface/bridge/find ] do={ - :local BrName [ /interface/bridge/get $Bridge name ]; - :if ([ :len [ /interface/bridge/port/find where bridge=$BrName ] ] > 0) do={ - :if ([ :len [ /interface/bridge/port/find where bridge=$BrName and inactive=no ] ] = 0) do={ - /ip/address/disable [ find where !dynamic interface=$BrName ]; - } else={ - /ip/address/enable [ find where !dynamic interface=$BrName ]; - } - } -} diff --git a/ipsec-to-dns.rsc b/ipsec-to-dns.rsc deleted file mode 100644 index 26dab0a..0000000 --- a/ipsec-to-dns.rsc +++ /dev/null @@ -1,84 +0,0 @@ -#!rsc by RouterOS -# RouterOS script: ipsec-to-dns -# Copyright (c) 2021-2025 Christian Hesse -# https://rsc.eworm.de/COPYING.md -# -# requires RouterOS, version=7.15 -# requires device-mode, ipsec -# -# and add/remove/update DNS entries from IPSec mode-config -# https://rsc.eworm.de/doc/ipsec-to-dns.md - -:global GlobalFunctionsReady; -:while ($GlobalFunctionsReady != true) do={ :delay 500ms; } - -:local ExitOK false; -:do { - :local ScriptName [ :jobname ]; - - :global Domain; - :global HostNameInZone; - :global Identity; - :global PrefixInZone; - - :global CharacterReplace; - :global EscapeForRegEx; - :global IfThenElse; - :global LogPrint; - :global ScriptLock; - - :if ([ $ScriptLock $ScriptName ] = false) do={ - :set ExitOK true; - :error false; - } - - :local Zone \ - ([ $IfThenElse ($PrefixInZone = true) "ipsec." ] . \ - [ $IfThenElse ($HostNameInZone = true) ($Identity . ".") ] . $Domain); - :local Ttl 5m; - :local CommentPrefix ("managed by " . $ScriptName . " for "); - :local CommentString ("--- " . $ScriptName . " above ---"); - - :if ([ :len [ /ip/dns/static/find where (name=$CommentString or (comment=$CommentString and name=-)) type=NXDOMAIN disabled ] ] = 0) do={ - /ip/dns/static/add name=$CommentString type=NXDOMAIN disabled=yes; - $LogPrint warning $ScriptName ("Added disabled static dns record with name '" . $CommentString . "'."); - } - :local PlaceBefore ([ /ip/dns/static/find where (name=$CommentString or (comment=$CommentString and name=-)) type=NXDOMAIN disabled ]->0); - - :foreach DnsRecord in=[ /ip/dns/static/find where comment~("^" . $CommentPrefix) ] do={ - :local DnsRecordVal [ /ip/dns/static/get $DnsRecord ]; - :local PeerId [ $CharacterReplace ($DnsRecordVal->"comment") $CommentPrefix "" ]; - :if ([ :len [ /ip/ipsec/active-peers/find where id~("^(CN=)?" . [ $EscapeForRegEx $PeerId ] . "\$") \ - dynamic-address=($DnsRecordVal->"address") ] ] > 0) do={ - $LogPrint debug $ScriptName ("Peer " . $PeerId . " (" . $DnsRecordVal->"name" . ") still exists. Not deleting DNS entry."); - } else={ - :local Found false; - $LogPrint info $ScriptName ("Peer " . $PeerId . " (" . $DnsRecordVal->"name" . ") has gone, deleting DNS entry."); - /ip/dns/static/remove $DnsRecord; - } - } - - :foreach Peer in=[ /ip/ipsec/active-peers/find where !(dynamic-address=[]) ] do={ - :local PeerVal [ /ip/ipsec/active-peers/get $Peer ]; - :local PeerId [ $CharacterReplace ($PeerVal->"id") "CN=" "" ]; - :local Comment ($CommentPrefix . $PeerId); - :local HostName [ :pick $PeerId 0 [ :find ($PeerId . ".") "." ] ]; - - :local Fqdn ($HostName . "." . $Zone); - :local DnsRecord [ /ip/dns/static/find where name=$Fqdn ]; - :if ([ :len $DnsRecord ] > 0) do={ - :local DnsIp [ /ip/dns/static/get $DnsRecord address ]; - :if ($DnsIp = $PeerVal->"dynamic-address") do={ - $LogPrint debug $ScriptName ("DNS entry for " . $Fqdn . " does not need updating."); - } else={ - $LogPrint info $ScriptName ("Replacing DNS entry for " . $Fqdn . ", new address is " . $PeerVal->"dynamic-address" . "."); - /ip/dns/static/set name=$Fqdn address=($PeerVal->"dynamic-address") ttl=$Ttl comment=$Comment $DnsRecord; - } - } else={ - $LogPrint info $ScriptName ("Adding new DNS entry for " . $Fqdn . ", address is " . $PeerVal->"dynamic-address" . "."); - /ip/dns/static/add name=$Fqdn address=($PeerVal->"dynamic-address") ttl=$Ttl comment=$Comment place-before=$PlaceBefore; - } - } -} on-error={ - :global ExitError; $ExitError $ExitOK [ :jobname ]; -} diff --git a/ipv6-update b/ipv6-update new file mode 100644 index 0000000..f8ad3c1 --- /dev/null +++ b/ipv6-update @@ -0,0 +1,36 @@ +#!rsc +# RouterOS script: ipv6-update +# Copyright (c) 2013-2019 Christian Hesse +# +# update firewall and dns settings on IPv6 prefix change + +:local PdPrefix $"pd-prefix"; + +:local Pool [ / ipv6 pool get [ find where prefix=$PdPrefix ] name ]; +:local AddrList [ / ipv6 firewall address-list find where comment=("ipv6-pool-" . $Pool) ]; +:local OldPrefix [ / ipv6 firewall address-list get $AddrList address ]; + +# give the interfaces a moment to receive their addresses +:delay 2s; + +if ($OldPrefix != $PdPrefix) do={ + :log info ("Updating IPv6 address list with new IPv6 prefix " . $PdPrefix); + / ipv6 firewall address-list set address=$PdPrefix $AddrList; + + :foreach Record in=[ / ip dns static find where comment~("ipv6-pool-" . $Pool) ] do={ + :local Comment [ :toarray [ / ip dns static get $Record comment ] ]; + :local IntName [ :pick ($Comment->1) 10 99 ]; + :local Suffix [ :pick ($Comment->2) 7 99 ]; + + :local Prefix [ / ipv6 address get [ find where interface=$IntName from-pool=$Pool global ] address ]; + :local Prefix64 [ :pick $Prefix 0 [ :find $Prefix "::/64" ] ]; + + :local Name [ / ip dns static get $Record name ]; + :if ([ :len $Name ] = 0) do={ + :set Name [ / ip dns static get $Record regex ]; + } + + :log info ("Updating DNS record for " . $Name . " to " . $Prefix64 . ":" . $Suffix); + / ip dns static set address=($Prefix64 . ":" . $Suffix) $Record; + } +} diff --git a/ipv6-update.rsc b/ipv6-update.rsc deleted file mode 100644 index 94bd1bc..0000000 --- a/ipv6-update.rsc +++ /dev/null @@ -1,107 +0,0 @@ -#!rsc by RouterOS -# RouterOS script: ipv6-update -# Copyright (c) 2013-2025 Christian Hesse -# https://rsc.eworm.de/COPYING.md -# -# requires RouterOS, version=7.15 -# -# update firewall and dns settings on IPv6 prefix change -# https://rsc.eworm.de/doc/ipv6-update.md - -:global GlobalFunctionsReady; -:while ($GlobalFunctionsReady != true) do={ :delay 500ms; } - -:local ExitOK false; -:do { - :local ScriptName [ :jobname ]; - - :global LogPrint; - :global ParseKeyValueStore; - :global ScriptLock; - - :local NaAddress $"na-address"; - :local NaValid $"na-valid"; - :local PdPrefix $"pd-prefix"; - :local PdValid $"pd-valid"; - - :if ([ $ScriptLock $ScriptName ] = false) do={ - :set ExitOK true; - :error false; - } - - :if ([ :typeof $NaAddress ] = "str") do={ - $LogPrint info $ScriptName ("An address (" . $NaAddress . ") was acquired, not a prefix. Ignoring."); - :set ExitOK true; - :error false; - } - - :if ([ :typeof $PdPrefix ] = "nothing" || [ :typeof $PdValid ] = "nothing") do={ - $LogPrint error $ScriptName ("This script is supposed to run from ipv6 dhcp-client."); - :set ExitOK true; - :error false; - } - - :if ($PdValid != 1) do={ - $LogPrint info $ScriptName ("The prefix " . $PdPrefix . " is no longer valid. Ignoring."); - :set ExitOK true; - :error false; - } - - :local Pool [ /ipv6/pool/get [ find where prefix=$PdPrefix ] name ]; - :if ([ :len [ /ipv6/firewall/address-list/find where comment=("ipv6-pool-" . $Pool) ] ] = 0) do={ - /ipv6/firewall/address-list/add list=("ipv6-pool-" . $Pool) address=:: comment=("ipv6-pool-" . $Pool) dynamic=yes; - $LogPrint warning $ScriptName ("Added dynamic ipv6 address list entry for ipv6-pool-" . $Pool); - } - :local AddrList [ /ipv6/firewall/address-list/find where comment=("ipv6-pool-" . $Pool) ]; - :local OldPrefix [ /ipv6/firewall/address-list/get ($AddrList->0) address ]; - - :if ($OldPrefix != $PdPrefix) do={ - $LogPrint info $ScriptName ("Updating IPv6 address list with new IPv6 prefix " . $PdPrefix); - /ipv6/firewall/address-list/set address=$PdPrefix $AddrList; - - # give the interfaces a moment to receive their addresses - :delay 2s; - - :foreach ListEntry in=[ /ipv6/firewall/address-list/find where comment~("^ipv6-pool-" . $Pool . ",") ] do={ - :local ListEntryVal [ /ipv6/firewall/address-list/get $ListEntry ]; - :local Comment [ $ParseKeyValueStore ($ListEntryVal->"comment") ]; - - :local Prefix [ /ipv6/address/find where from-pool=$Pool interface=($Comment->"interface") global ]; - :if ([ :len $Prefix ] = 1) do={ - :set Prefix [ /ipv6/address/get $Prefix address ]; - - :if ([ :typeof [ :find ($ListEntryVal->"address") "/128" ] ] = "num" ) do={ - :set Prefix ([ :toip6 [ :pick $Prefix 0 [ :find $Prefix "/64" ] ] ] & ffff:ffff:ffff:ffff::); - :local Address ($ListEntryVal->"address"); - :local Address ($Prefix | ([ :toip6 [ :pick $Address 0 [ :find $Address "/128" ] ] ] & ::ffff:ffff:ffff:ffff)); - - $LogPrint info $ScriptName ("Updating IPv6 address list with new IPv6 host address " . $Address . \ - " from interface " . ($Comment->"interface")); - /ipv6/firewall/address-list/set address=$Address $ListEntry; - } else={ - $LogPrint info $ScriptName ("Updating IPv6 address list with new IPv6 prefix " . $Prefix . \ - " from interface " . ($Comment->"interface")); - /ipv6/firewall/address-list/set address=$Prefix $ListEntry; - } - } - } - - :foreach Record in=[ /ip/dns/static/find where comment~("^ipv6-pool-" . $Pool . ",") ] do={ - :local RecordVal [ /ip/dns/static/get $Record ]; - :local Comment [ $ParseKeyValueStore ($RecordVal->"comment") ]; - - :local Prefix [ /ipv6/address/find where from-pool=$Pool interface=($Comment->"interface") global ]; - :if ([ :len $Prefix ] = 1) do={ - :set Prefix [ /ipv6/address/get $Prefix address ]; - :set Prefix ([ :toip6 [ :pick $Prefix 0 [ :find $Prefix "/64" ] ] ] & ffff:ffff:ffff:ffff::); - :local Address ($Prefix | ([ :toip6 ($RecordVal->"address") ] & ::ffff:ffff:ffff:ffff)); - - $LogPrint info $ScriptName ("Updating DNS record for " . ($RecordVal->"name") . \ - ($RecordVal->"regexp") . " to " . $Address); - /ip/dns/static/set address=$Address $Record; - } - } - } -} on-error={ - :global ExitError; $ExitError $ExitOK [ :jobname ]; -} diff --git a/learn-mac-based-vlan b/learn-mac-based-vlan new file mode 100644 index 0000000..8ebb16a --- /dev/null +++ b/learn-mac-based-vlan @@ -0,0 +1,12 @@ +#!rsc +# RouterOS script: learn-mac-based-vlan +# Copyright (c) 2013-2019 Christian Hesse +# +# learn MAC address for MAC-based-VLAN + +:local NewVlanId 33; + +:if ( [ / interface ethernet switch mac-based-vlan print count-only where src-mac-address=$leaseActMAC ] = 0 ) do={ + :log info ("MAC-based-VLAN: learning MAC address " . $leaseActMAC . " for VLAN " . $NewVlanId . "."); + / interface ethernet switch mac-based-vlan add src-mac-address=$leaseActMAC new-customer-vid=$NewVlanId; +} diff --git a/lease-script b/lease-script new file mode 100644 index 0000000..d8d6562 --- /dev/null +++ b/lease-script @@ -0,0 +1,38 @@ +#!rsc +# RouterOS script: lease-script +# Copyright (c) 2013-2019 Christian Hesse +# +# run scripts on DHCP lease + +:local Scripts; +:local ScriptsAssign { + "dhcp-to-dns"; + "collect-wireless-mac.local"; + "dhcp-lease-comment.local"; + "collect-wireless-mac.capsman"; + "dhcp-lease-comment.capsman" +} +:local ScriptsDeAssign { + "dhcp-to-dns" +} + +:local State ""; +:if ($leaseBound = 0) do={ + :set State "de"; + :set Scripts $ScriptsDeAssign; +} else={ + :set Scripts $ScriptsAssign; +} + +:log debug ("DHCP Server " . $leaseServerName . " " . \ + $State . "assigned lease " . $leaseActIP . " to " . $leaseActMAC); + +# delay a moment to update the lease table, do not run in parallel for de/assign +:delay ((1 + $leaseBound) . "s"); + +:foreach Script in=$Scripts do={ + :if ([ / system script print count-only where name=$Script ] > 0) do={ + :log debug ("Running script from lease-script: " . $Script); + / system script run $Script; + } +} diff --git a/lease-script.rsc b/lease-script.rsc deleted file mode 100644 index bf27fda..0000000 --- a/lease-script.rsc +++ /dev/null @@ -1,65 +0,0 @@ -#!rsc by RouterOS -# RouterOS script: lease-script -# Copyright (c) 2013-2025 Christian Hesse -# https://rsc.eworm.de/COPYING.md -# -# requires RouterOS, version=7.15 -# -# run scripts on DHCP lease -# https://rsc.eworm.de/doc/lease-script.md - -:global GlobalFunctionsReady; -:while ($GlobalFunctionsReady != true) do={ :delay 500ms; } - -:local ExitOK false; -:do { - :local ScriptName [ :jobname ]; - - :global Grep; - :global IfThenElse; - :global LogPrint; - :global ParseKeyValueStore; - :global ScriptLock; - - :if ([ :typeof $leaseActIP ] = "nothing" || \ - [ :typeof $leaseActMAC ] = "nothing" || \ - [ :typeof $leaseServerName ] = "nothing" || \ - [ :typeof $leaseBound ] = "nothing") do={ - $LogPrint error $ScriptName ("This script is supposed to run from ip dhcp-server."); - :set ExitOK true; - :error false; - } - - $LogPrint debug $ScriptName ("DHCP Server " . $leaseServerName . " " . [ $IfThenElse ($leaseBound = 0) \ - "de" "" ] . "assigned lease " . $leaseActIP . " to " . $leaseActMAC); - - :if ([ $ScriptLock $ScriptName 10 ] = false) do={ - :set ExitOK true; - :error false; - } - - :if ([ :len [ /system/script/job/find where script=$ScriptName ] ] > 1) do={ - $LogPrint debug $ScriptName ("More invocations are waiting, exiting early."); - :set ExitOK true; - :error true; - } - - :local RunOrder ({}); - :foreach Script in=[ /system/script/find where source~("\n# provides: lease-script\\b") ] do={ - :local ScriptVal [ /system/script/get $Script ]; - :local Store [ $ParseKeyValueStore [ $Grep ($ScriptVal->"source") ("\23 provides: lease-script, ") ] ]; - - :set ($RunOrder->($Store->"order" . "-" . $ScriptVal->"name")) ($ScriptVal->"name"); - } - - :foreach Order,Script in=$RunOrder do={ - :do { - $LogPrint debug $ScriptName ("Running script with order " . $Order . ": " . $Script); - /system/script/run $Script; - } on-error={ - $LogPrint warning $ScriptName ("Running script '" . $Script . "' failed!"); - } - } -} on-error={ - :global ExitError; $ExitError $ExitOK [ :jobname ]; -} diff --git a/leds-day-mode b/leds-day-mode new file mode 100644 index 0000000..4fcc41b --- /dev/null +++ b/leds-day-mode @@ -0,0 +1,7 @@ +#!rsc +# RouterOS script: leds-day-mode +# Copyright (c) 2013-2019 Christian Hesse +# +# enable LEDs + +/ system leds settings set all-leds-off=never; diff --git a/leds-day-mode.rsc b/leds-day-mode.rsc deleted file mode 100644 index 7344fde..0000000 --- a/leds-day-mode.rsc +++ /dev/null @@ -1,9 +0,0 @@ -#!rsc by RouterOS -# RouterOS script: leds-day-mode -# Copyright (c) 2013-2025 Christian Hesse -# https://rsc.eworm.de/COPYING.md -# -# enable LEDs -# https://rsc.eworm.de/doc/leds-mode.md - -/system/leds/settings/set all-leds-off=never; diff --git a/leds-night-mode b/leds-night-mode new file mode 100644 index 0000000..58a3720 --- /dev/null +++ b/leds-night-mode @@ -0,0 +1,7 @@ +#!rsc +# RouterOS script: leds-night-mode +# Copyright (c) 2013-2019 Christian Hesse +# +# disable LEDs + +/ system leds settings set all-leds-off=immediate; diff --git a/leds-night-mode.rsc b/leds-night-mode.rsc deleted file mode 100644 index 8bd028e..0000000 --- a/leds-night-mode.rsc +++ /dev/null @@ -1,9 +0,0 @@ -#!rsc by RouterOS -# RouterOS script: leds-night-mode -# Copyright (c) 2013-2025 Christian Hesse -# https://rsc.eworm.de/COPYING.md -# -# disable LEDs -# https://rsc.eworm.de/doc/leds-mode.md - -/system/leds/settings/set all-leds-off=immediate; diff --git a/leds-toggle-mode b/leds-toggle-mode new file mode 100644 index 0000000..d2c5bff --- /dev/null +++ b/leds-toggle-mode @@ -0,0 +1,11 @@ +#!rsc +# RouterOS script: leds-toggle-mode +# Copyright (c) 2018-2019 Christian Hesse +# +# toggle LEDs mode + +:if ([ / system leds settings get all-leds-off ] = "never") do={ + / system leds settings set all-leds-off=immediate; +} else={ + / system leds settings set all-leds-off=never; +} diff --git a/leds-toggle-mode.rsc b/leds-toggle-mode.rsc deleted file mode 100644 index b55e351..0000000 --- a/leds-toggle-mode.rsc +++ /dev/null @@ -1,9 +0,0 @@ -#!rsc by RouterOS -# RouterOS script: leds-toggle-mode -# Copyright (c) 2018-2025 Christian Hesse -# https://rsc.eworm.de/COPYING.md -# -# toggle LEDs mode -# https://rsc.eworm.de/doc/leds-mode.md - -/system/leds/settings/set all-leds-off=(({ "never"="immediate"; "immediate"="never" })->[ get all-leds-off ]); diff --git a/log-forward.rsc b/log-forward.rsc deleted file mode 100644 index afeb3f2..0000000 --- a/log-forward.rsc +++ /dev/null @@ -1,113 +0,0 @@ -#!rsc by RouterOS -# RouterOS script: log-forward -# Copyright (c) 2020-2025 Christian Hesse -# https://rsc.eworm.de/COPYING.md -# -# requires RouterOS, version=7.15 -# -# forward log messages via notification -# https://rsc.eworm.de/doc/log-forward.md - -:global GlobalFunctionsReady; -:while ($GlobalFunctionsReady != true) do={ :delay 500ms; } - -:local ExitOK false; -:do { - :local ScriptName [ :jobname ]; - - :global Identity; - :global LogForwardFilter; - :global LogForwardFilterMessage; - :global LogForwardInclude; - :global LogForwardIncludeMessage; - :global LogForwardLast; - :global LogForwardRateLimit; - - :global EitherOr; - :global HexToNum; - :global IfThenElse; - :global LogForwardFilterLogForwarding; - :global LogPrint; - :global MAX; - :global ScriptLock; - :global SendNotification2; - :global SymbolForNotification; - - :if ([ $ScriptLock $ScriptName ] = false) do={ - :set ExitOK true; - :error false; - } - - :if ([ :typeof $LogForwardRateLimit ] = "nothing") do={ - :set LogForwardRateLimit 0; - } - - :if ($LogForwardRateLimit > 30) do={ - :set LogForwardRateLimit ($LogForwardRateLimit - 1); - $LogPrint info $ScriptName ("Rate limit in action, not forwarding logs, if any!"); - :set ExitOK true; - :error false; - } - - :local Count 0; - :local Duplicates false; - :local Last [ $IfThenElse ([ :len $LogForwardLast ] > 0) [ $HexToNum $LogForwardLast ] -1 ]; - :local Messages ""; - :local Warning false; - :local MessageVal; - :local MessageDups ({}); - - :set LogForwardFilter [ $EitherOr $LogForwardFilter [] ]; - :set LogForwardFilterMessage [ $EitherOr $LogForwardFilterMessage [] ]; - :set LogForwardInclude [ $EitherOr $LogForwardInclude [] ]; - :set LogForwardIncludeMessage [ $EitherOr $LogForwardIncludeMessage [] ]; - - :local LogForwardFilterLogForwardingCached [ $EitherOr [ $LogForwardFilterLogForwarding ] ("\$^") ]; - :foreach Message in=[ /log/find where (!(message="") and \ - !(message~$LogForwardFilterLogForwardingCached) and \ - !(topics~$LogForwardFilter) and !(message~$LogForwardFilterMessage)) or \ - topics~$LogForwardInclude or message~$LogForwardIncludeMessage ] do={ - :set MessageVal [ /log/get $Message ]; - :local Bullet "information"; - - :if ($Last < [ $HexToNum ($MessageVal->".id") ]) do={ - :local DupCount ($MessageDups->($MessageVal->"message")); - :if ($MessageVal->"topics" ~ "(warning)") do={ - :set Warning true; - :set Bullet "large-orange-circle"; - } - :if ($MessageVal->"topics" ~ "(emergency|alert|critical|error)") do={ - :set Warning true; - :set Bullet "large-red-circle"; - } - :if ($DupCount < 3) do={ - :set Messages ($Messages . "\n" . [ $SymbolForNotification $Bullet ] . \ - $MessageVal->"time" . " " . [ :tostr ($MessageVal->"topics") ] . " " . $MessageVal->"message"); - } else={ - :set Duplicates true; - } - :set ($MessageDups->($MessageVal->"message")) ($DupCount + 1); - :set Count ($Count + 1); - } - } - - :if ($Count > 0) do={ - :set LogForwardRateLimit ($LogForwardRateLimit + 10); - - $SendNotification2 ({ origin=$ScriptName; \ - subject=([ $SymbolForNotification [ $IfThenElse ($Warning = true) "warning-sign" "memo" ] ] . \ - "Log Forwarding"); \ - message=("The log on " . $Identity . " contains " . [ $IfThenElse ($Count = 1) "this message" \ - ("these " . $Count . " messages") ] . " after " . [ /system/resource/get uptime ] . " uptime." . \ - [ $IfThenElse ($Duplicates = true) (" Multi-repeated messages have been skipped.") ] . \ - [ $IfThenElse ($LogForwardRateLimit > 30) ("\nRate limit in action, delaying forwarding.") ] . \ - "\n" . $Messages) }); - } else={ - :set LogForwardRateLimit [ $MAX 0 ($LogForwardRateLimit - 1) ]; - } - - :local LogAll [ /log/find ]; - :set LogForwardLast ($LogAll->([ :len $LogAll ] - 1) ); -} on-error={ - :global ExitError; $ExitError $ExitOK [ :jobname ]; -} diff --git a/logo.avif b/logo.avif deleted file mode 100644 index 399a2f5..0000000 Binary files a/logo.avif and /dev/null differ diff --git a/logo.png b/logo.png deleted file mode 100644 index d97b75d..0000000 Binary files a/logo.png and /dev/null differ diff --git a/logo.svg b/logo.svg deleted file mode 100644 index a30e04e..0000000 --- a/logo.svg +++ /dev/null @@ -1,29 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/manage-umts b/manage-umts new file mode 100644 index 0000000..e416ee2 --- /dev/null +++ b/manage-umts @@ -0,0 +1,28 @@ +#!rsc +# RouterOS script: manage-umts +# Copyright (c) 2013-2019 Christian Hesse +# +# manage UMTS interface based on ethernet and wireless status + +:local EtherInt "en1"; +:local WlanInt "wl-station"; +:local UmtsInt "t-mobile"; + +:local EtherStatus [ / interface ethernet get $EtherInt running ]; +:local WlanStatus [ / interface wireless get $WlanInt running ]; + +:if ($EtherStatus = true || $WlanStatus = true) do={ + :if ([ / interface get $UmtsInt disabled ] = false) do={ + :log info ("Ethernet (" . $EtherInt . " / " . $EtherStatus . ") or " . \ + "wireless (" . $WlanInt . " / " . $WlanStatus . ") is running, " . \ + "UMTS interface " . $UmtsInt . " is enabled. Disabling..."); + / interface set disabled=yes $UmtsInt; + } +} else={ + :if ([ / interface get $UmtsInt disabled ] = true) do={ + :log info ("Neither ethernet (" . $EtherInt . ") nor wireless (" . \ + $WlanInt . ") interface is running, UMTS interface " . $UmtsInt . \ + " is disabled. Enabling..."); + / interface set disabled=no $UmtsInt; + } +} diff --git a/mod/bridge-port-to.rsc b/mod/bridge-port-to.rsc deleted file mode 100644 index 39a036e..0000000 --- a/mod/bridge-port-to.rsc +++ /dev/null @@ -1,70 +0,0 @@ -#!rsc by RouterOS -# RouterOS script: mod/bridge-port-to -# Copyright (c) 2013-2025 Christian Hesse -# https://rsc.eworm.de/COPYING.md -# -# requires RouterOS, version=7.15 -# -# reset bridge ports to default bridge -# https://rsc.eworm.de/doc/mod/bridge-port-to.md - -:global BridgePortTo; - -:set BridgePortTo do={ :do { - :local BridgePortTo [ :tostr $1 ]; - - :global IfThenElse; - :global LogPrint; - :global ParseKeyValueStore; - - :local InterfaceReEnable ({}); - :foreach BridgePort in=[ /interface/bridge/port/find where !(comment=[]) ] do={ - :local BridgePortVal [ /interface/bridge/port/get $BridgePort ]; - :foreach Config,BridgeDefault in=[ $ParseKeyValueStore ($BridgePortVal->"comment") ] do={ - :if ($Config = $BridgePortTo) do={ - :local DHCPClient [ /ip/dhcp-client/find where interface=$BridgePortVal->"interface" comment="toggle with bridge port" ]; - - :if ($BridgeDefault = "dhcp-client") do={ - :if ([ :len $DHCPClient ] != 1) do={ - $LogPrint warning $0 ([ $IfThenElse ([ :len $DHCPClient ] = 0) "Missing" "Duplicate" ] . \ - " dhcp client configuration for interface " . $BridgePortVal->"interface" . "!"); - :return false; - } - :local DHCPClientDisabled [ /ip/dhcp-client/get $DHCPClient disabled ]; - - :if ($BridgePortVal->"disabled" = false || $DHCPClientDisabled = true) do={ - $LogPrint info $0 ("Disabling bridge port for interface " . $BridgePortVal->"interface" . ", enabling dhcp client."); - /interface/bridge/port/disable $BridgePort; - :delay 200ms; - /ip/dhcp-client/enable $DHCPClient; - } - } else={ - :if ($BridgePortVal->"disabled" = true || $BridgeDefault != $BridgePortVal->"bridge") do={ - $LogPrint info $0 ("Enabling bridge port for interface " . $BridgePortVal->"interface" . ", changing to " . $BridgePortTo . \ - " bridge " . $BridgeDefault . ", disabling dhcp client."); - :if ([ :len $DHCPClient ] = 1) do={ - /ip/dhcp-client/disable $DHCPClient; - :delay 200ms; - } - :local Disable [ /interface/ethernet/find where name=$BridgePortVal->"interface" ]; - :if ([ :len $Disable ] > 0) do={ - /interface/ethernet/disable $Disable; - :set InterfaceReEnable ($InterfaceReEnable, $Disable); - } - /interface/bridge/port/set disabled=no bridge=$BridgeDefault $BridgePort; - } else={ - $LogPrint debug $0 ("Interface " . $BridgePortVal->"interface" . " already connected to " . $BridgePortTo . \ - " bridge " . $BridgeDefault . "."); - } - } - } - } - } - :if ([ :len $InterfaceReEnable ] > 0) do={ - :delay 5s; - $LogPrint info $0 ("Re-enabling interfaces..."); - /interface/ethernet/enable $InterfaceReEnable; - } -} on-error={ - :global ExitError; $ExitError false $0; -} } diff --git a/mod/bridge-port-vlan.rsc b/mod/bridge-port-vlan.rsc deleted file mode 100644 index 0eeb9b5..0000000 --- a/mod/bridge-port-vlan.rsc +++ /dev/null @@ -1,79 +0,0 @@ -#!rsc by RouterOS -# RouterOS script: mod/bridge-port-vlan -# Copyright (c) 2013-2025 Christian Hesse -# https://rsc.eworm.de/COPYING.md -# -# requires RouterOS, version=7.15 -# -# manage VLANs on bridge ports -# https://rsc.eworm.de/doc/mod/bridge-port-vlan.md - -:global BridgePortVlan; - -:global BridgePortVlan do={ :do { - :local ConfigTo [ :tostr $1 ]; - - :global IfThenElse; - :global LogPrint; - :global ParseKeyValueStore; - - :local InterfaceReEnable ({}); - :foreach BridgePort in=[ /interface/bridge/port/find where !(comment=[]) ] do={ - :local BridgePortVal [ /interface/bridge/port/get $BridgePort ]; - :foreach Config,Vlan in=[ $ParseKeyValueStore ($BridgePortVal->"comment") ] do={ - :if ($Config = $ConfigTo) do={ - :local DHCPClient [ /ip/dhcp-client/find where interface=$BridgePortVal->"interface" comment="toggle with bridge port" ]; - - :if ($Vlan = "dhcp-client") do={ - :if ([ :len $DHCPClient ] != 1) do={ - $LogPrint warning $0 ([ $IfThenElse ([ :len $DHCPClient ] = 0) "Missing" "Duplicate" ] . \ - " dhcp client configuration for interface " . $BridgePortVal->"interface" . "!"); - :return false; - } - :local DHCPClientDisabled [ /ip/dhcp-client/get $DHCPClient disabled ]; - - :if ($BridgePortVal->"disabled" = false || $DHCPClientDisabled = true) do={ - $LogPrint info $0 ("Disabling bridge port for interface " . $BridgePortVal->"interface" . ", enabling dhcp client."); - /interface/bridge/port/disable $BridgePort; - :delay 200ms; - /ip/dhcp-client/enable $DHCPClient; - } - } else={ - :local VlanName $Vlan; - :if ($Vlan != [ :tostr [ :tonum $Vlan ] ]) do={ - :do { - :set $Vlan ([ /interface/bridge/vlan/get [ find where comment=$Vlan ] vlan-ids ]->0); - } on-error={ - $LogPrint warning $0 ("Could not find VLAN '" . $Vlan . "' for interface " . $BridgePortVal->"interface" . "!"); - :return false; - } - } - :if ($BridgePortVal->"disabled" = true || $Vlan != $BridgePortVal->"pvid") do={ - $LogPrint info $0 ("Enabling bridge port for interface " . $BridgePortVal->"interface" . ", changing to " . $ConfigTo . \ - " vlan " . $Vlan . [ $IfThenElse ($Vlan != $VlanName) (" (" . $VlanName . ")") ] . ", disabling dhcp client."); - :if ([ :len $DHCPClient ] = 1) do={ - /ip/dhcp-client/disable $DHCPClient; - :delay 200ms; - } - :local Disable [ /interface/ethernet/find where name=$BridgePortVal->"interface" ]; - :if ([ :len $Disable ] > 0) do={ - /interface/ethernet/disable $Disable; - :set InterfaceReEnable ($InterfaceReEnable, $Disable); - } - /interface/bridge/port/set disabled=no pvid=$Vlan $BridgePort; - } else={ - $LogPrint debug $0 ("Interface " . $BridgePortVal->"interface" . " already connected to " . $ConfigTo . \ - " vlan " . $Vlan . "."); - } - } - } - } - } - :if ([ :len $InterfaceReEnable ] > 0) do={ - :delay 5s; - $LogPrint info $0 ("Re-enabling interfaces..."); - /interface/ethernet/enable $InterfaceReEnable; - } -} on-error={ - :global ExitError; $ExitError false $0; -} } diff --git a/mod/inspectvar.rsc b/mod/inspectvar.rsc deleted file mode 100644 index c861557..0000000 --- a/mod/inspectvar.rsc +++ /dev/null @@ -1,60 +0,0 @@ -#!rsc by RouterOS -# RouterOS script: mod/inspectvar -# Copyright (c) 2020-2025 Christian Hesse -# https://rsc.eworm.de/COPYING.md -# -# requires RouterOS, version=7.15 -# -# inspect variables -# https://rsc.eworm.de/doc/mod/inspectvar.md - -:global InspectVar; -:global InspectVarReturn; - -# inspect variable and print on terminal -:set InspectVar do={ :do { - :global InspectVarReturn; - - :put [ :tocrlf [ $InspectVarReturn $1 ] ]; -} on-error={ - :global ExitError; $ExitError false $0; -} } - -# inspect variable and return formatted string -:set InspectVarReturn do={ - :local Input $1; - :local Level (0 + [ :tonum $2 ]); - - :global IfThenElse; - :global InspectVarReturn; - - :local IndentReturn do={ - :local Prefix [ :tostr $1 ]; - :local Value [ :tostr $2 ]; - :local Level [ :tonum $3 ]; - - :local Indent ""; - :for I from=1 to=$Level step=1 do={ - :set Indent ($Indent . " "); - } - :return ($Indent . "-" . $Prefix . "-> " . $Value); - } - - :local TypeOf [ :typeof $Input ]; - :local Return [ $IndentReturn "type" $TypeOf $Level ]; - - :if ($TypeOf = "array") do={ - :foreach Key,Value in=$Input do={ - :set $Return ($Return . "\n" . \ - [ $IndentReturn "key" $Key ($Level + 1) ] . "\n" . \ - [ $InspectVarReturn $Value ($Level + 2) ]); - } - } else={ - :if ($TypeOf != "nothing") do={ - :set $Return ($Return . "\n" . \ - [ $IndentReturn "value" [ $IfThenElse ([ :len $Input ] > 80) \ - ([ :pick $Input 0 77 ] . "...") $Input ] $Level ]); - } - } - :return $Return; -} diff --git a/mod/ipcalc.rsc b/mod/ipcalc.rsc deleted file mode 100644 index 477cf4a..0000000 --- a/mod/ipcalc.rsc +++ /dev/null @@ -1,53 +0,0 @@ -#!rsc by RouterOS -# RouterOS script: mod/ipcalc -# Copyright (c) 2020-2025 Christian Hesse -# https://rsc.eworm.de/COPYING.md -# -# requires RouterOS, version=7.15 -# -# ip address calculation -# https://rsc.eworm.de/doc/mod/ipcalc.md - -:global IPCalc; -:global IPCalcReturn; - -# print netmask, network, min host, max host and broadcast -:set IPCalc do={ :do { - :local Input [ :tostr $1 ]; - - :global FormatLine; - :global IPCalcReturn; - - :local Values [ $IPCalcReturn $1 ]; - - :put [ :tocrlf ( \ - [ $FormatLine "Address" ($Values->"address") ] . "\n" . \ - [ $FormatLine "Netmask" ($Values->"netmask") ] . "\n" . \ - [ $FormatLine "Network" ($Values->"network") ] . "\n" . \ - [ $FormatLine "HostMin" ($Values->"hostmin") ] . "\n" . \ - [ $FormatLine "HostMax" ($Values->"hostmax") ] . "\n" . \ - [ $FormatLine "Broadcast" ($Values->"broadcast") ]) ]; -} on-error={ - :global ExitError; $ExitError false $0; -} } - -# calculate and return netmask, network, min host, max host and broadcast -:set IPCalcReturn do={ - :local Input [ :tostr $1 ]; - :local Address [ :toip [ :pick $Input 0 [ :find $Input "/" ] ] ]; - :local Bits [ :tonum [ :pick $Input ([ :find $Input "/" ] + 1) [ :len $Input ] ] ]; - :local Mask ((255.255.255.255 << (32 - $Bits)) & 255.255.255.255); - - :local Return { - "address"=$Address; - "netmask"=$Mask; - "networkaddress"=($Address & $Mask); - "networkbits"=$Bits; - "network"=(($Address & $Mask) . "/" . $Bits); - "hostmin"=(($Address & $Mask) | 0.0.0.1); - "hostmax"=(($Address | ~$Mask) ^ 0.0.0.1); - "broadcast"=($Address | ~$Mask); - } - - :return $Return; -} diff --git a/mod/notification-email.rsc b/mod/notification-email.rsc deleted file mode 100644 index 7b89d98..0000000 --- a/mod/notification-email.rsc +++ /dev/null @@ -1,266 +0,0 @@ -#!rsc by RouterOS -# RouterOS script: mod/notification-email -# Copyright (c) 2013-2025 Christian Hesse -# https://rsc.eworm.de/COPYING.md -# -# requires RouterOS, version=7.15 -# requires device-mode, email, scheduler -# -# send notifications via e-mail -# https://rsc.eworm.de/doc/mod/notification-email.md - -:global EMailGenerateFrom; -:global FlushEmailQueue; -:global LogForwardFilterLogForwarding; -:global NotificationEMailSubject; -:global NotificationFunctions; -:global PurgeEMailQueue; -:global QuotedPrintable; -:global SendEMail; -:global SendEMail2; - -# generate from-property with display name -:set EMailGenerateFrom do={ - :global Identity; - - :global CleanName; - - :local From [ /tool/e-mail/get from ]; - - :if ($From ~ "<.*>\$") do={ - :return $From; - } - - :return ([ $CleanName $Identity ] . " via routeros-scripts <" . $From . ">"); -} - -# flush e-mail queue -:set FlushEmailQueue do={ :do { - :global EmailQueue; - - :global EitherOr; - :global EMailGenerateFrom; - :global IsDNSResolving; - :global IsTimeSync; - :global LogPrint; - - :local AllDone true; - :local QueueLen [ :len $EmailQueue ]; - :local Scheduler [ /system/scheduler/find where name="_FlushEmailQueue" ]; - - :if ([ :len $Scheduler ] > 0 && $QueueLen = 0) do={ - $LogPrint warning $0 ("Flushing E-Mail messages from scheduler, but queue is empty."); - /system/scheduler/remove $Scheduler; - :return false; - } - - :if ($QueueLen = 0) do={ - :return true; - } - - :if ([ :len $Scheduler ] < 0) do={ - /system/scheduler/add name="_FlushEmailQueue" interval=1m start-time=startup \ - comment="Doing initial checks..." on-event=(":global FlushEmailQueue; \$FlushEmailQueue;"); - :set Scheduler [ /system/scheduler/find where name="_FlushEmailQueue" ]; - } - - :local SchedVal [ /system/scheduler/get $Scheduler ]; - :if (($SchedVal->"interval") < 1m) do={ - /system/scheduler/set interval=1m comment="Doing initial checks..." $Scheduler; - } - - :if ([ /tool/e-mail/get last-status ] = "in-progress") do={ - $LogPrint debug $0 ("Sending mail is currently in progress, not flushing."); - :return false; - } - - :if ([ $IsTimeSync ] = false) do={ - $LogPrint debug $0 ("Time is not synced, not flushing."); - :return false; - } - - :local EMailSettings [ /tool/e-mail/get ]; - :if ([ :typeof [ :toip ($EMailSettings->"server") ] ] != "ip" && [ $IsDNSResolving ] = false) do={ - $LogPrint debug $0 ("Server address is a DNS name and resolving fails, not flushing."); - :return false; - } - - /system/scheduler/set interval=($QueueLen . "m") comment="Sending..." $Scheduler; - - :foreach Id,Message in=$EmailQueue do={ - :if ([ :typeof $Message ] = "array" ) do={ - :local Attach ({}); - :while ([ /tool/e-mail/get last-status ] = "in-progress") do={ :delay 1s; } - :foreach File in=[ :toarray [ $EitherOr ($Message->"attach") "" ] ] do={ - :if ([ :len [ /file/find where name=$File ] ] = 1) do={ - :set Attach ($Attach, $File); - } else={ - $LogPrint warning $0 ("File '" . $File . "' does not exist, can not attach."); - } - } - /tool/e-mail/send from=[ $EMailGenerateFrom ] to=($Message->"to") cc=($Message->"cc") \ - subject=($Message->"subject") body=($Message->"body") file=$Attach; - :local Wait true; - :do { - :delay 1s; - :local Status [ /tool/e-mail/get last-status ]; - :if ($Status = "succeeded") do={ - :set ($EmailQueue->$Id); - :set Wait false; - :if (($Message->"remove-attach") = true) do={ - :foreach File in=$Attach do={ - /file/remove $File; - } - } - } - :if ($Status = "failed") do={ - :set AllDone false; - :set Wait false; - } - } while=($Wait = true); - } - } - - :if ($AllDone = true && $QueueLen = [ :len $EmailQueue ]) do={ - /system/scheduler/remove $Scheduler; - :set EmailQueue; - :return true; - } - - :if ([ :len [ /system/scheduler/find where name="_FlushEmailQueue" ] ] = 0 && \ - [ :typeof $EmailQueue ] = "nothing") do={ - $LogPrint info $0 ("Queue was purged? Exiting."); - :return false; - } - - /system/scheduler/set interval=(($SchedVal->"run-count") . "m") \ - comment="Waiting for retry..." $Scheduler; -} on-error={ - :global ExitError; $ExitError false $0; -} } - -# generate filter for log-forward -:set LogForwardFilterLogForwarding do={ - :global EscapeForRegEx; - :global NotificationEMailSubject; - :global SymbolForNotification; - - :return ("^Error sending e-mail <(" . \ - [ $EscapeForRegEx [ $NotificationEMailSubject ([ $SymbolForNotification \ - "memo" ] . "Log Forwarding") ] ] . "|" . \ - [ $EscapeForRegEx [ $NotificationEMailSubject ([ $SymbolForNotification \ - "warning-sign" ] . "Log Forwarding") ] ] . ")>:"); -} - -# generate the e-mail subject -:set NotificationEMailSubject do={ - :global Identity; - :global IdentityExtra; - - :global QuotedPrintable; - - :return [ $QuotedPrintable ("[" . $IdentityExtra . $Identity . "] " . $1) ]; -} - -# send notification via e-mail - expects one array argument -:set ($NotificationFunctions->"email") do={ - :local Notification $1; - - :global EmailGeneralTo; - :global EmailGeneralToOverride; - :global EmailGeneralCc; - :global EmailGeneralCcOverride; - :global EmailQueue; - - :global EitherOr; - :global IfThenElse; - :global NotificationEMailSignature; - :global NotificationEMailSubject; - - :local To [ $EitherOr ($EmailGeneralToOverride->($Notification->"origin")) $EmailGeneralTo ]; - :local Cc [ $EitherOr ($EmailGeneralCcOverride->($Notification->"origin")) $EmailGeneralCc ]; - - :local EMailSettings [ /tool/e-mail/get ]; - :if ([ :len $To ] = 0 || ($EMailSettings->"server") = "0.0.0.0" || ($EMailSettings->"from") = "<>") do={ - :return false; - } - - :if ([ :typeof $EmailQueue ] = "nothing") do={ - :set EmailQueue ({}); - } - :local Signature [ $EitherOr [ $NotificationEMailSignature ] [ /system/note/get note ] ]; - :set ($EmailQueue->[ :len $EmailQueue ]) { - to=$To; cc=$Cc; - subject=[ $NotificationEMailSubject ($Notification->"subject") ]; - body=(($Notification->"message") . \ - [ $IfThenElse ([ :len ($Notification->"link") ] > 0) ("\n\n" . ($Notification->"link")) "" ] . \ - [ $IfThenElse ([ :len $Signature ] > 0) ("\n-- \n" . $Signature) "" ]); \ - attach=($Notification->"attach"); remove-attach=($Notification->"remove-attach") }; - :if ([ :len [ /system/scheduler/find where name="_FlushEmailQueue" ] ] = 0) do={ - /system/scheduler/add name="_FlushEmailQueue" interval=1s start-time=startup \ - comment="Queuing new mail..." on-event=(":global FlushEmailQueue; \$FlushEmailQueue;"); - } -} - -# purge the e-mail queue -:set PurgeEMailQueue do={ - :global EmailQueue; - - /system/scheduler/remove [ find where name="_FlushEmailQueue" ]; - :set EmailQueue; -} - -# convert string to quoted-printable -:global QuotedPrintable do={ - :local Input [ :tostr $1 ]; - - :global CharacterMultiply; - - :if ([ :len $Input ] = 0) do={ - :return $Input; - } - - :local Return ""; - :local Chars ( \ - "\00\01\02\03\04\05\06\07\08\09\0A\0B\0C\0D\0E\0F\10\11\12\13\14\15\16\17\18\19\1A\1B\1C\1D\1E\1F" . \ - [ $CharacterMultiply ("\00") 29 ] . "=\00?" . [ $CharacterMultiply ("\00") 63 ] . "\7F" . \ - "\80\81\82\83\84\85\86\87\88\89\8A\8B\8C\8D\8E\8F\90\91\92\93\94\95\96\97\98\99\9A\9B\9C\9D\9E\9F" . \ - "\A0\A1\A2\A3\A4\A5\A6\A7\A8\A9\AA\AB\AC\AD\AE\AF\B0\B1\B2\B3\B4\B5\B6\B7\B8\B9\BA\BB\BC\BD\BE\BF" . \ - "\C0\C1\C2\C3\C4\C5\C6\C7\C8\C9\CA\CB\CC\CD\CE\CF\D0\D1\D2\D3\D4\D5\D6\D7\D8\D9\DA\DB\DC\DD\DE\DF" . \ - "\E0\E1\E2\E3\E4\E5\E6\E7\E8\E9\EA\EB\EC\ED\EE\EF\F0\F1\F2\F3\F4\F5\F6\F7\F8\F9\FA\FB\FC\FD\FE\FF"); - :local Hex "0123456789ABCDEF"; - - :for I from=0 to=([ :len $Input ] - 1) do={ - :local Char [ :pick $Input $I ]; - :local Replace [ :find $Chars $Char ]; - - :if ([ :typeof $Replace ] = "num") do={ - :set Char ("=" . [ :pick $Hex ($Replace / 16)] . [ :pick $Hex ($Replace % 16) ]); - } - :set Return ($Return . $Char); - } - - :if ($Input = $Return) do={ - :return $Input; - } - - :return ("=?utf-8?Q?" . $Return . "?="); -} - -# send notification via e-mail - expects at least two string arguments -:set SendEMail do={ :do { - :global SendEMail2; - - $SendEMail2 ({ origin=$0; subject=$1; message=$2; link=$3 }); -} on-error={ - :global ExitError; $ExitError false $0; -} } - -# send notification via e-mail - expects one array argument -:set SendEMail2 do={ - :local Notification $1; - - :global NotificationFunctions; - - ($NotificationFunctions->"email") ("\$NotificationFunctions->\"email\"") $Notification; -} diff --git a/mod/notification-matrix.rsc b/mod/notification-matrix.rsc deleted file mode 100644 index e989ee0..0000000 --- a/mod/notification-matrix.rsc +++ /dev/null @@ -1,271 +0,0 @@ -#!rsc by RouterOS -# RouterOS script: mod/notification-matrix -# Copyright (c) 2013-2025 Michael Gisbers -# Christian Hesse -# https://rsc.eworm.de/COPYING.md -# -# requires RouterOS, version=7.15 -# requires device-mode, fetch, scheduler -# -# send notifications via Matrix -# https://rsc.eworm.de/doc/mod/notification-matrix.md - -:global FlushMatrixQueue; -:global NotificationFunctions; -:global PurgeMatrixQueue; -:global SendMatrix; -:global SendMatrix2; -:global SetupMatrixAuthenticate; -:global SetupMatrixJoinRoom; - -# flush Matrix queue -:set FlushMatrixQueue do={ :do { - :global MatrixQueue; - - :global IsFullyConnected; - :global LogPrint; - - :if ([ $IsFullyConnected ] = false) do={ - $LogPrint debug $0 ("System is not fully connected, not flushing."); - :return false; - } - - :local AllDone true; - :local QueueLen [ :len $MatrixQueue ]; - - :if ([ :len [ /system/scheduler/find where name="_FlushMatrixQueue" ] ] > 0 && $QueueLen = 0) do={ - $LogPrint warning $0 ("Flushing Matrix messages from scheduler, but queue is empty."); - } - - :foreach Id,Message in=$MatrixQueue do={ - :if ([ :typeof $Message ] = "array" ) do={ - :do { - /tool/fetch check-certificate=yes-without-crl output=none \ - http-header-field=($Message->"headers") http-method=post \ - http-data=[ :serialize to=json { "msgtype"="m.text"; "body"=($Message->"plain"); - "format"="org.matrix.custom.html"; "formatted_body"=($Message->"formatted") } ] \ - ("https://" . $Message->"homeserver" . "/_matrix/client/r0/rooms/" . $Message->"room" . \ - "/send/m.room.message?access_token=" . $Message->"accesstoken") as-value; - :set ($MatrixQueue->$Id); - } on-error={ - $LogPrint debug $0 ("Sending queued Matrix message failed."); - :set AllDone false; - } - } - } - - :if ($AllDone = true && $QueueLen = [ :len $MatrixQueue ]) do={ - /system/scheduler/remove [ find where name="_FlushMatrixQueue" ]; - :set MatrixQueue; - } -} on-error={ - :global ExitError; $ExitError false $0; -} } - -# send notification via Matrix - expects one array argument -:set ($NotificationFunctions->"matrix") do={ - :local Notification $1; - - :global Identity; - :global IdentityExtra; - :global MatrixAccessToken; - :global MatrixAccessTokenOverride; - :global MatrixHomeServer; - :global MatrixHomeServerOverride; - :global MatrixQueue; - :global MatrixRoom; - :global MatrixRoomOverride; - - :global EitherOr; - :global FetchUserAgentStr; - :global LogPrint; - :global ProtocolStrip; - :global SymbolForNotification; - - :local PrepareText do={ - :local Input [ :tostr $1 ]; - - :if ([ :len $Input ] = 0) do={ - :return ""; - } - - :local Return ""; - :local Chars { "\""; "\n"; "&"; "<"; ">" }; - :local Subs { """; ""; "&"; "<"; ">" }; - - :for I from=0 to=([ :len $Input ] - 1) do={ - :local Char [ :pick $Input $I ]; - :local Replace [ :find $Chars $Char ]; - - :if ([ :typeof $Replace ] = "num") do={ - :set Char ($Subs->$Replace); - } - :set Return ($Return . $Char); - } - - :return $Return; - } - - :local AccessToken [ $EitherOr ($MatrixAccessTokenOverride->($Notification->"origin")) $MatrixAccessToken ]; - :local HomeServer [ $EitherOr ($MatrixHomeServerOverride->($Notification->"origin")) $MatrixHomeServer ]; - :local Room [ $EitherOr ($MatrixRoomOverride->($Notification->"origin")) $MatrixRoom ]; - - :if ([ :len $AccessToken ] = 0 || [ :len $HomeServer ] = 0 || [ :len $Room ] = 0) do={ - :return false; - } - - :local Headers ({ [ $FetchUserAgentStr ($Notification->"origin") ] }); - :local Plain ("## [" . $IdentityExtra . $Identity . "] " . \ - ($Notification->"subject") . "\n```\n" . ($Notification->"message") . "\n```"); - :local Formatted ("" . [ $PrepareText ("[" . $IdentityExtra . $Identity . "] " . \ - ($Notification->"subject")) ] . "" . "" . \ - [ $PrepareText ($Notification->"message") ] . ""); - :if ([ :len ($Notification->"link") ] > 0) do={ - :local Label [ $ProtocolStrip ($Notification->"link") ]; - :set Plain ($Plain . "\n" . [ $SymbolForNotification "link" ] . \ - "[" . $Label . "](" . $Notification->"link" . ")"); - :set Formatted ($Formatted . "" . [ $SymbolForNotification "link" ] . \ - ""link") ] . "\">" . \ - [ $PrepareText $Label ] . ""); - } - - :do { - /tool/fetch check-certificate=yes-without-crl output=none \ - http-header-field=$Headers http-method=post \ - http-data=[ :serialize to=json { "msgtype"="m.text"; "body"=$Plain; - "format"="org.matrix.custom.html"; "formatted_body"=$Formatted } ] \ - ("https://" . $HomeServer . "/_matrix/client/r0/rooms/" . $Room . \ - "/send/m.room.message?access_token=" . $AccessToken) as-value; - } on-error={ - $LogPrint info $0 ("Failed sending Matrix notification! Queuing..."); - - :if ([ :typeof $MatrixQueue ] = "nothing") do={ - :set MatrixQueue ({}); - } - :local Symbol [ $SymbolForNotification "alarm-clock" ]; - :local DateTime ([ /system/clock/get date ] . " " . [ /system/clock/get time ]); - :set Plain ($Plain . "\n" . $Symbol . "This message was queued since *" . \ - $DateTime . "* and may be obsolete."); - :set Formatted ($Formatted . "" . $Symbol . "This message was queued since " . \ - $DateTime . " and may be obsolete."); - :set ($MatrixQueue->[ :len $MatrixQueue ]) { headers=$Headers; \ - accesstoken=$AccessToken; homeserver=$HomeServer; room=$Room; \ - plain=$Plain; formatted=$Formatted }; - :if ([ :len [ /system/scheduler/find where name="_FlushMatrixQueue" ] ] = 0) do={ - /system/scheduler/add name="_FlushMatrixQueue" interval=1m start-time=startup \ - on-event=(":global FlushMatrixQueue; \$FlushMatrixQueue;"); - } - } -} - -# purge the Matrix queue -:set PurgeMatrixQueue do={ - :global MatrixQueue; - - /system/scheduler/remove [ find where name="_FlushMatrixQueue" ]; - :set MatrixQueue; -} - -# send notification via Matrix - expects at least two string arguments -:set SendMatrix do={ :do { - :global SendMatrix2; - - $SendMatrix2 ({ origin=$0; subject=$1; message=$2; link=$3 }); -} on-error={ - :global ExitError; $ExitError false $0; -} } - -# send notification via Matrix - expects one array argument -:set SendMatrix2 do={ - :local Notification $1; - - :global NotificationFunctions; - - ($NotificationFunctions->"matrix") ("\$NotificationFunctions->\"matrix\"") $Notification; -} - -# setup - get home server and access token -:set SetupMatrixAuthenticate do={ - :local User [ :tostr $1 ]; - :local Pass [ :tostr $2 ]; - - :global FetchUserAgentStr; - :global LogPrint; - - :global MatrixAccessToken; - :global MatrixHomeServer; - - :local Domain [ :pick $User ([ :find $User ":" ] + 1) [ :len $User] ]; - :do { - :local Data ([ /tool/fetch check-certificate=yes-without-crl output=user \ - http-header-field=({ [ $FetchUserAgentStr $0 ] }) \ - ("https://" . $Domain . "/.well-known/matrix/client") as-value ]->"data"); - :set MatrixHomeServer ([ :deserialize from=json value=$Data ]->"m.homeserver"->"base_url"); - $LogPrint debug $0 ("Home server is: " . $MatrixHomeServer); - } on-error={ - $LogPrint error $0 ("Failed getting home server!"); - :return false; - } - - :if ([ :pick $MatrixHomeServer 0 8 ] = "https://") do={ - :set MatrixHomeServer [ :pick $MatrixHomeServer 8 [ :len $MatrixHomeServer ] ]; - } - - :do { - :local Data ([ /tool/fetch check-certificate=yes-without-crl output=user \ - http-header-field=({ [ $FetchUserAgentStr $0 ] }) http-method=post \ - http-data=[ :serialize to=json { "type"="m.login.password"; "user"=$User; "password"=$Pass } ] \ - ("https://" . $MatrixHomeServer . "/_matrix/client/r0/login") as-value ]->"data"); - :set MatrixAccessToken ([ :deserialize from=json value=$Data ]->"access_token"); - $LogPrint debug $0 ("Access token is: " . $MatrixAccessToken); - } on-error={ - $LogPrint error $0 ("Failed logging in (and getting access token)!"); - :return false; - } - - :do { - /system/script/remove [ find where name="global-config-overlay.d/mod/notification-matrix" ]; - /system/script/add name="global-config-overlay.d/mod/notification-matrix" source=( \ - "# configuration snippet: mod/notification-matrix\n\n" . \ - ":global MatrixHomeServer \"" . $MatrixHomeServer . "\";\n" . \ - ":global MatrixAccessToken \"" . $MatrixAccessToken . "\";\n"); - $LogPrint info $0 ("Added configuration snippet. Now create and join a room, please!"); - } on-error={ - $LogPrint error $0 ("Failed adding configuration snippet!"); - :return false; - } -} - -# setup - join a room -:set SetupMatrixJoinRoom do={ - :global MatrixRoom [ :tostr $1 ]; - - :global FetchUserAgentStr; - :global LogPrint; - :global UrlEncode; - - :global MatrixAccessToken; - :global MatrixHomeServer; - :global MatrixRoom; - - :do { - /tool/fetch check-certificate=yes-without-crl output=none \ - http-header-field=({ [ $FetchUserAgentStr $0 ] }) http-method=post http-data="" \ - ("https://" . $MatrixHomeServer . "/_matrix/client/r0/rooms/" . [ $UrlEncode $MatrixRoom ] . \ - "/join?access_token=" . [ $UrlEncode $MatrixAccessToken ]) as-value; - $LogPrint debug $0 ("Joined the room."); - } on-error={ - $LogPrint error $0 ("Failed joining the room!"); - :return false; - } - - :do { - :local Snippet [ /system/script/find where name="global-config-overlay.d/mod/notification-matrix" ]; - /system/script/set $Snippet source=([ get $Snippet source ] . \ - ":global MatrixRoom \"" . $MatrixRoom . "\";\n"); - $LogPrint info $0 ("Appended configuration to configuration snippet. Please review!"); - } on-error={ - $LogPrint error $0 ("Failed appending configuration to snippet!"); - :return false; - } -} diff --git a/mod/notification-ntfy.rsc b/mod/notification-ntfy.rsc deleted file mode 100644 index aac6d6c..0000000 --- a/mod/notification-ntfy.rsc +++ /dev/null @@ -1,162 +0,0 @@ -#!rsc by RouterOS -# RouterOS script: mod/notification-ntfy -# Copyright (c) 2013-2025 Christian Hesse -# https://rsc.eworm.de/COPYING.md -# -# requires RouterOS, version=7.15 -# requires device-mode, fetch, scheduler -# -# send notifications via Ntfy (ntfy.sh) -# https://rsc.eworm.de/doc/mod/notification-ntfy.md - -:global FlushNtfyQueue; -:global NotificationFunctions; -:global PurgeNtfyQueue; -:global SendNtfy; -:global SendNtfy2; - -# flush ntfy queue -:set FlushNtfyQueue do={ :do { - :global NtfyQueue; - :global NtfyMessageIDs; - - :global IsFullyConnected; - :global LogPrint; - - :if ([ $IsFullyConnected ] = false) do={ - $LogPrint debug $0 ("System is not fully connected, not flushing."); - :return false; - } - - :local AllDone true; - :local QueueLen [ :len $NtfyQueue ]; - - :if ([ :len [ /system/scheduler/find where name="_FlushNtfyQueue" ] ] > 0 && $QueueLen = 0) do={ - $LogPrint warning $0 ("Flushing Ntfy messages from scheduler, but queue is empty."); - } - - :foreach Id,Message in=$NtfyQueue do={ - :if ([ :typeof $Message ] = "array" ) do={ - :do { - /tool/fetch check-certificate=yes-without-crl output=none http-method=post \ - http-header-field=($Message->"headers") http-data=($Message->"text") \ - ($Message->"url") as-value; - :set ($NtfyQueue->$Id); - } on-error={ - $LogPrint debug $0 ("Sending queued Ntfy message failed."); - :set AllDone false; - } - } - } - - :if ($AllDone = true && $QueueLen = [ :len $NtfyQueue ]) do={ - /system/scheduler/remove [ find where name="_FlushNtfyQueue" ]; - :set NtfyQueue; - } -} on-error={ - :global ExitError; $ExitError false $0; -} } - -# send notification via ntfy - expects one array argument -:set ($NotificationFunctions->"ntfy") do={ - :local Notification $1; - - :global Identity; - :global IdentityExtra; - :global NtfyQueue; - :global NtfyServer; - :global NtfyServerOverride; - :global NtfyServerPass; - :global NtfyServerPassOverride; - :global NtfyServerToken; - :global NtfyServerTokenOverride; - :global NtfyServerUser; - :global NtfyServerUserOverride; - :global NtfyTopic; - :global NtfyTopicOverride; - - :global CertificateAvailable; - :global EitherOr; - :global FetchUserAgentStr; - :global IfThenElse; - :global LogPrint; - :global SymbolForNotification; - :global UrlEncode; - - :local Server [ $EitherOr ($NtfyServerOverride->($Notification->"origin")) $NtfyServer ]; - :local User [ $EitherOr ($NtfyServerUserOverride->($Notification->"origin")) $NtfyServerUser ]; - :local Pass [ $EitherOr ($NtfyServerPassOverride->($Notification->"origin")) $NtfyServerPass ]; - :local Token [ $EitherOr ($NtfyServerTokenOverride->($Notification->"origin")) $NtfyServerToken ]; - :local Topic [ $EitherOr ($NtfyTopicOverride->($Notification->"origin")) $NtfyTopic ]; - - :if ([ :len $Topic ] = 0) do={ - :return false; - } - - :local Url ("https://" . $Server . "/" . [ $UrlEncode $Topic ]); - :local Headers ({ [ $FetchUserAgentStr ($Notification->"origin") ]; \ - ("Priority: " . [ $IfThenElse ($Notification->"silent") "low" "default" ]); \ - ("Title: " . "[" . $IdentityExtra . $Identity . "] " . ($Notification->"subject")) }); - :if ([ :len $User ] > 0 || [ :len $Pass ] > 0) do={ - :set Headers ($Headers, ("Authorization: Basic " . [ :convert to=base64 ($User . ":" . $Pass) ])); - } - :if ([ :len $Token ] > 0) do={ - :set Headers ($Headers, ("Authorization: Bearer " . $Token)); - } - :local Text (($Notification->"message") . "\n"); - :if ([ :len ($Notification->"link") ] > 0) do={ - :set Text ($Text . "\n" . [ $SymbolForNotification "link" ] . ($Notification->"link")); - } - - :do { - :if ($Server = "ntfy.sh") do={ - :if ([ $CertificateAvailable "ISRG Root X1" ] = false) do={ - $LogPrint warning $0 ("Downloading required certificate failed."); - :error false; - } - } - /tool/fetch check-certificate=yes-without-crl output=none http-method=post \ - http-header-field=$Headers http-data=$Text $Url as-value; - } on-error={ - $LogPrint info $0 ("Failed sending ntfy notification! Queuing..."); - - :if ([ :typeof $NtfyQueue ] = "nothing") do={ - :set NtfyQueue ({}); - } - :set Text ($Text . "\n" . [ $SymbolForNotification "alarm-clock" ] . \ - "This message was queued since " . [ /system/clock/get date ] . " " . \ - [ /system/clock/get time ] . " and may be obsolete."); - :set ($NtfyQueue->[ :len $NtfyQueue ]) \ - { url=$Url; headers=$Headers; text=$Text }; - :if ([ :len [ /system/scheduler/find where name="_FlushNtfyQueue" ] ] = 0) do={ - /system/scheduler/add name="_FlushNtfyQueue" interval=1m start-time=startup \ - on-event=(":global FlushNtfyQueue; \$FlushNtfyQueue;"); - } - } -} - -# purge the Ntfy queue -:set PurgeNtfyQueue do={ - :global NtfyQueue; - - /system/scheduler/remove [ find where name="_FlushNtfyQueue" ]; - :set NtfyQueue; -} - -# send notification via ntfy - expects at least two string arguments -:set SendNtfy do={ :do { - :global SendNtfy2; - - $SendNtfy2 ({ origin=$0; subject=$1; message=$2; link=$3; silent=$4 }); -} on-error={ - :global ExitError; $ExitError false $0; -} } - -# send notification via ntfy - expects one array argument -:set SendNtfy2 do={ - :local Notification $1; - - :global NotificationFunctions; - - ($NotificationFunctions->"ntfy") ("\$NotificationFunctions->\"ntfy\"") $Notification; -} diff --git a/mod/notification-telegram.rsc b/mod/notification-telegram.rsc deleted file mode 100644 index 68e913f..0000000 --- a/mod/notification-telegram.rsc +++ /dev/null @@ -1,243 +0,0 @@ -#!rsc by RouterOS -# RouterOS script: mod/notification-telegram -# Copyright (c) 2013-2025 Christian Hesse -# https://rsc.eworm.de/COPYING.md -# -# requires RouterOS, version=7.15 -# requires device-mode, fetch, scheduler -# -# send notifications via Telegram -# https://rsc.eworm.de/doc/mod/notification-telegram.md - -:global FlushTelegramQueue; -:global GetTelegramChatId; -:global NotificationFunctions; -:global PurgeTelegramQueue; -:global SendTelegram; -:global SendTelegram2; - -# flush telegram queue -:set FlushTelegramQueue do={ :do { - :global TelegramQueue; - :global TelegramMessageIDs; - - :global IsFullyConnected; - :global LogPrint; - - :if ([ $IsFullyConnected ] = false) do={ - $LogPrint debug $0 ("System is not fully connected, not flushing."); - :return false; - } - - :local AllDone true; - :local QueueLen [ :len $TelegramQueue ]; - - :if ([ :len [ /system/scheduler/find where name="_FlushTelegramQueue" ] ] > 0 && $QueueLen = 0) do={ - $LogPrint warning $0 ("Flushing Telegram messages from scheduler, but queue is empty."); - } - - :foreach Id,Message in=$TelegramQueue do={ - :if ([ :typeof $Message ] = "array" ) do={ - :do { - :local Data ([ /tool/fetch check-certificate=yes-without-crl output=user http-method=post \ - ("https://api.telegram.org/bot" . ($Message->"tokenid") . "/sendMessage") \ - http-data=($Message->"http-data") as-value ]->"data"); - :set ($TelegramQueue->$Id); - :set ($TelegramMessageIDs->[ :tostr ([ :deserialize from=json value=$Data ]->"result"->"message_id") ]) 1; - } on-error={ - $LogPrint debug $0 ("Sending queued Telegram message failed."); - :set AllDone false; - } - } - } - - :if ($AllDone = true && $QueueLen = [ :len $TelegramQueue ]) do={ - /system/scheduler/remove [ find where name="_FlushTelegramQueue" ]; - :set TelegramQueue; - } -} on-error={ - :global ExitError; $ExitError false $0; -} } - -# get the chat id -:set GetTelegramChatId do={ :do { - :global TelegramTokenId; - - :global CertificateAvailable; - :global LogPrint; - - :if ([ $CertificateAvailable "Go Daddy Root Certificate Authority - G2" ] = false) do={ - $LogPrint warning $0 ("Downloading required certificate failed."); - :return false; - } - - :local Data; - :do { - :set Data ([ /tool/fetch check-certificate=yes-without-crl output=user \ - ("https://api.telegram.org/bot" . $TelegramTokenId . "/getUpdates?offset=0" . \ - "&allowed_updates=%5B%22message%22%5D") as-value ]->"data"); - } on-error={ - $LogPrint warning $0 ("Fetching data failed!"); - :return false; - } - - :local JSON [ :deserialize from=json value=$Data ]; - :local Count [ :len ($JSON->"result") ]; - - :if ($Count = 0) do={ - $LogPrint info $0 ("No message received."); - :return false; - } - - :local Message ($JSON->"result"->($Count - 1)->"message"); - $LogPrint info $0 ("The chat id is: " . ($Message->"chat"->"id")); - :if (($Message->"is_topic_message") = true) do={ - $LogPrint info $0 ("The thread id is: " . ($Message->"message_thread_id")); - } -} on-error={ - :global ExitError; $ExitError false $0; -} } - -# send notification via telegram - expects one array argument -:set ($NotificationFunctions->"telegram") do={ - :local Notification $1; - - :global Identity; - :global IdentityExtra; - :global TelegramChatId; - :global TelegramChatIdOverride; - :global TelegramMessageIDs; - :global TelegramQueue; - :global TelegramThreadId; - :global TelegramThreadIdOverride; - :global TelegramTokenId; - :global TelegramTokenIdOverride; - - :global CertificateAvailable; - :global CharacterReplace; - :global EitherOr; - :global IfThenElse; - :global LogPrint; - :global ProtocolStrip; - :global SymbolForNotification; - :global UrlEncode; - - :local EscapeMD do={ - :local Text [ :tostr $1 ]; - :local Mode [ :tostr $2 ]; - :local Excl [ :tostr $3 ]; - - :global CharacterReplace; - :global IfThenElse; - - :local Chars { - "body"={ "\\"; "`" }; - "plain"={ "_"; "*"; "["; "]"; "("; ")"; "~"; "`"; ">"; - "#"; "+"; "-"; "="; "|"; "{"; "}"; "."; "!" }; - } - :foreach Char in=($Chars->$Mode) do={ - :if ([ :typeof [ :find $Excl $Char ] ] = "nil") do={ - :set Text [ $CharacterReplace $Text $Char ("\\" . $Char) ]; - } - } - - :if ($Mode = "body") do={ - :return ("```\n" . $Text . "\n```"); - } - - :return $Text; - } - - :local ChatId [ $EitherOr ($Notification->"chatid") \ - [ $EitherOr ($TelegramChatIdOverride->($Notification->"origin")) $TelegramChatId ] ]; - :local ThreadId [ $EitherOr ($Notification->"threadid") \ - [ $EitherOr ($TelegramThreadIdOverride->($Notification->"origin")) $TelegramThreadId ] ]; - :local TokenId [ $EitherOr ($TelegramTokenIdOverride->($Notification->"origin")) $TelegramTokenId ]; - - :if ([ :len $TokenId ] = 0 || [ :len $ChatId ] = 0) do={ - :return false; - } - - :if ([ :typeof $TelegramMessageIDs ] = "nothing") do={ - :set TelegramMessageIDs ({}); - } - - :local Truncated false; - :local Text ("*__" . [ $EscapeMD ("[" . $IdentityExtra . $Identity . "] " . \ - ($Notification->"subject")) "plain" ] . "__*\n\n"); - :local LenSubject [ :len $Text ]; - :local LenMessage [ :len ($Notification->"message") ]; - :local LenLink ([ :len ($Notification->"link") ] * 2); - :local LenSum ($LenSubject + $LenMessage + $LenLink); - :if ($LenSum > 3968) do={ - :set Text ($Text . [ $EscapeMD ([ :pick ($Notification->"message") 0 (3840 - $LenSubject - $LenLink) ] . "...") "body" ]); - :set Truncated true; - } else={ - :set Text ($Text . [ $EscapeMD ($Notification->"message") "body" ]); - } - :if ($LenLink > 0) do={ - :set Text ($Text . "\n" . [ $SymbolForNotification "link" ] . \ - "[" . [ $EscapeMD [ $ProtocolStrip ($Notification->"link") ] "plain" ] . "]" . \ - "(" . [ $EscapeMD ($Notification->"link") "plain" ] . ")"); - } - :if ($Truncated = true) do={ - :set Text ($Text . "\n" . [ $SymbolForNotification "scissors" ] . \ - [ $EscapeMD ("The message was too long and has been truncated, cut off _" . \ - (($LenSum - [ :len $Text ]) * 100 / $LenSum) . "%_!") "plain" "_" ]); - } - - :local HTTPData ("chat_id=" . $ChatId . "&disable_notification=" . ($Notification->"silent") . \ - "&reply_to_message_id=" . ($Notification->"replyto") . "&message_thread_id=" . $ThreadId . \ - "&disable_web_page_preview=true&parse_mode=MarkdownV2"); - :do { - :if ([ $CertificateAvailable "Go Daddy Root Certificate Authority - G2" ] = false) do={ - $LogPrint warning $0 ("Downloading required certificate failed."); - :error false; - } - :local Data ([ /tool/fetch check-certificate=yes-without-crl output=user http-method=post \ - ("https://api.telegram.org/bot" . $TokenId . "/sendMessage") \ - http-data=($HTTPData . "&text=" . [ $UrlEncode $Text ]) as-value ]->"data"); - :set ($TelegramMessageIDs->[ :tostr ([ :deserialize from=json value=$Data ]->"result"->"message_id") ]) 1; - } on-error={ - $LogPrint info $0 ("Failed sending Telegram notification! Queuing..."); - - :if ([ :typeof $TelegramQueue ] = "nothing") do={ - :set TelegramQueue ({}); - } - :set Text ($Text . "\n" . [ $SymbolForNotification "alarm-clock" ] . \ - [ $EscapeMD ("This message was queued since _" . [ /system/clock/get date ] . \ - " " . [ /system/clock/get time ] . "_ and may be obsolete.") "plain" "_" ]); - :set ($TelegramQueue->[ :len $TelegramQueue ]) { tokenid=$TokenId; - http-data=($HTTPData . "&text=" . [ $UrlEncode $Text ]) }; - :if ([ :len [ /system/scheduler/find where name="_FlushTelegramQueue" ] ] = 0) do={ - /system/scheduler/add name="_FlushTelegramQueue" interval=1m start-time=startup \ - on-event=(":global FlushTelegramQueue; \$FlushTelegramQueue;"); - } - } -} - -# purge the Telegram queue -:set PurgeTelegramQueue do={ - :global TelegramQueue; - - /system/scheduler/remove [ find where name="_FlushTelegramQueue" ]; - :set TelegramQueue; -} - -# send notification via telegram - expects at least two string arguments -:set SendTelegram do={ :do { - :global SendTelegram2; - - $SendTelegram2 ({ origin=$0; subject=$1; message=$2; link=$3; silent=$4 }); -} on-error={ - :global ExitError; $ExitError false $0; -} } - -# send notification via telegram - expects one array argument -:set SendTelegram2 do={ - :local Notification $1; - - :global NotificationFunctions; - - ($NotificationFunctions->"telegram") ("\$NotificationFunctions->\"telegram\"") $Notification; -} diff --git a/mod/scriptrunonce.rsc b/mod/scriptrunonce.rsc deleted file mode 100644 index 7fcd5b5..0000000 --- a/mod/scriptrunonce.rsc +++ /dev/null @@ -1,56 +0,0 @@ -#!rsc by RouterOS -# RouterOS script: mod/scriptrunonece -# Copyright (c) 2020-2025 Christian Hesse -# https://rsc.eworm.de/COPYING.md -# -# requires RouterOS, version=7.15 -# -# download script and run it once -# https://rsc.eworm.de/doc/mod/scriptrunonce.md - -:global ScriptRunOnce; - -# fetch and run script(s) once -:set ScriptRunOnce do={ :do { - :local Scripts [ :toarray $1 ]; - - :global ScriptRunOnceBaseUrl; - :global ScriptRunOnceUrlSuffix; - - :global FetchHuge; - :global LogPrint; - :global ValidateSyntax; - - :foreach Script in=$Scripts do={ - :if (!($Script ~ "^(ftp|https?|sftp)://")) do={ - :if ([ :len $ScriptRunOnceBaseUrl ] = 0) do={ - $LogPrint warning $0 ("Script '" . $Script . "' is not an url and base url is not available."); - :return false; - } - :set Script ($ScriptRunOnceBaseUrl . $Script . ".rsc" . $ScriptRunOnceUrlSuffix); - } - - :local Source [ $FetchHuge $0 $Script true ]; - :if ($Source = false) do={ - $LogPrint warning $0 ("Failed fetching script '" . $Script . "'!"); - :return false; - } - - :if ([ $ValidateSyntax $Source ] = false) do={ - $LogPrint warning $0 ("The script '" . $Script . "' failed syntax validation!"); - :return false; - } - - :do { - $LogPrint info $0 ("Running script '" . $Script . "' now."); - [ :parse $Source ]; - } on-error={ - $LogPrint warning $0 ("The script '" . $Script . "' failed to run!"); - :return false; - } - - :return true; - } -} on-error={ - :global ExitError; $ExitError false $0; -} } diff --git a/mod/ssh-keys-import.rsc b/mod/ssh-keys-import.rsc deleted file mode 100644 index 2fae4b1..0000000 --- a/mod/ssh-keys-import.rsc +++ /dev/null @@ -1,114 +0,0 @@ -#!rsc by RouterOS -# RouterOS script: mod/ssh-keys-import -# Copyright (c) 2020-2025 Christian Hesse -# https://rsc.eworm.de/COPYING.md -# -# requires RouterOS, version=7.16 -# -# import ssh keys for public key authentication -# https://rsc.eworm.de/doc/mod/ssh-keys-import.md - -:global SSHKeysImport; -:global SSHKeysImportFile; - -# import single key passed as string -:set SSHKeysImport do={ :do { - :local Key [ :tostr $1 ]; - :local User [ :tostr $2 ]; - - :global GetRandom20CharAlNum; - :global LogPrint; - :global MkDir; - :global RmDir; - :global WaitForFile; - - :if ([ :len $Key ] = 0 || [ :len $User ] = 0) do={ - $LogPrint warning $0 ("Missing argument(s), please pass key and user!"); - :return false; - } - - :if ([ :len [ /user/find where name=$User ] ] = 0) do={ - $LogPrint warning $0 ("User '" . $User . "' does not exist."); - :return false; - } - - :local KeyVal ([ :deserialize $Key delimiter=" " from=dsv options=dsv.plain ]->0); - :if (!($KeyVal->0 = "ssh-ed25519" || $KeyVal->0 = "ssh-rsa")) do={ - $LogPrint warning $0 ("SSH key of type '" . $KeyVal->0 . "' is not supported."); - :return false; - } - - :local FingerPrintMD5 [ :convert from=base64 transform=md5 to=hex ($KeyVal->1) ]; - - :if ([ :len [ /user/ssh-keys/find where user=$User key-owner~("\\bmd5=" . $FingerPrintMD5 . "\\b") ] ] > 0) do={ - $LogPrint warning $0 ("The ssh public key (MD5:" . $FingerPrintMD5 . \ - ") is already available for user '" . $User . "'."); - :return false; - } - - :if ([ $MkDir "tmpfs/ssh-keys-import" ] = false) do={ - $LogPrint warning $0 ("Creating directory 'tmpfs/ssh-keys-import' failed!"); - :return false; - } - - :local FileName ("tmpfs/ssh-keys-import/key-" . [ $GetRandom20CharAlNum 6 ] . ".pub"); - /file/add name=$FileName contents=($Key . ", md5=" . $FingerPrintMD5); - $WaitForFile $FileName; - - :do { - /user/ssh-keys/import public-key-file=$FileName user=$User; - $LogPrint info $0 ("Imported ssh public key (" . $KeyVal->2 . ", " . $KeyVal->0 . ", " . \ - "MD5:" . $FingerPrintMD5 . ") for user '" . $User . "'."); - $RmDir "tmpfs/ssh-keys-import"; - } on-error={ - $LogPrint warning $0 ("Failed importing key."); - $RmDir "tmpfs/ssh-keys-import"; - :return false; - } -} on-error={ - :global ExitError; $ExitError false $0; -} } - -# import keys from a file -:set SSHKeysImportFile do={ :do { - :local FileName [ :tostr $1 ]; - :local User [ :tostr $2 ]; - - :global EitherOr; - :global LogPrint; - :global ParseKeyValueStore; - :global SSHKeysImport; - - :if ([ :len $FileName ] = 0 || [ :len $User ] = 0) do={ - $LogPrint warning $0 ("Missing argument(s), please pass file name and user!"); - :return false; - } - - :local File [ /file/find where name=$FileName ]; - :if ([ :len $File ] = 0) do={ - $LogPrint warning $0 ("File '" . $FileName . "' does not exist."); - :return false; - } - :local Keys [ :tolf [ /file/get $FileName contents ] ]; - - :foreach KeyVal in=[ :deserialize $Keys delimiter=" " from=dsv options=dsv.plain ] do={ - :local Continue false; - :if ($KeyVal->0 = "ssh-ed25519" || $KeyVal->0 = "ssh-rsa") do={ - :do { - $SSHKeysImport ($KeyVal->0 . " " . $KeyVal->1 . " " . $KeyVal->2) $User; - } on-error={ - $LogPrint warning $0 ("Failed importing key for user '" . $User . "'."); - } - :set Continue true; - } - :if ($Continue = false && $KeyVal->0 = "#") do={ - :set User [ $EitherOr ([ $ParseKeyValueStore ($KeyVal->1) ]->"user") $User ]; - :set Continue true; - } - :if ($Continue = false && [ :len ($KeyVal->0) ] > 0) do={ - $LogPrint warning $0 ("SSH key of type '" . $KeyVal->0 . "' is not supported."); - } - } -} on-error={ - :global ExitError; $ExitError false $0; -} } diff --git a/mode-button-event b/mode-button-event new file mode 100644 index 0000000..82c1f4b --- /dev/null +++ b/mode-button-event @@ -0,0 +1,19 @@ +#!rsc +# RouterOS script: mode-button-event +# Copyright (c) 2018-2019 Christian Hesse +# +# run on mode-button event and count button presses + +:global ModeButton; + +:set ($ModeButton->"count") ($ModeButton->"count" + 1); + +:local Scheduler [ / system scheduler find where name="mode-button-scheduler" ]; + +:if ([ :len $Scheduler ] = 0) do={ + :log info "Creating mode-button scheduler, counting presses..."; + / system scheduler add name=mode-button-scheduler on-event=mode-button-scheduler interval=3s; +} else={ + :log debug "Updating mode-button-scheduler..."; + / system scheduler set $Scheduler start-time=[ /system clock get time ]; +} diff --git a/mode-button-scheduler b/mode-button-scheduler new file mode 100644 index 0000000..bce6089 --- /dev/null +++ b/mode-button-scheduler @@ -0,0 +1,18 @@ +#!rsc +# RouterOS script: mode-button-scheduler +# Copyright (c) 2018-2019 Christian Hesse +# +# act on multiple mode-botton presses from scheduler + +:global ModeButton; + +:local Count ($ModeButton->"count"); +:local Code ($ModeButton->[ :tostr $Count ]); +:local Parsed [ :parse $Code ]; + +:set ($ModeButton->"count") 0; +/ system scheduler remove mode-button-scheduler; + +:log info ("Acting on " . $Count . " mode-button presses: " . $Code); +:delay 1s; +$Parsed; diff --git a/mode-button.rsc b/mode-button.rsc deleted file mode 100644 index edc5f40..0000000 --- a/mode-button.rsc +++ /dev/null @@ -1,96 +0,0 @@ -#!rsc by RouterOS -# RouterOS script: mode-button -# Copyright (c) 2018-2025 Christian Hesse -# https://rsc.eworm.de/COPYING.md -# -# requires RouterOS, version=7.15 -# requires device-mode, scheduler -# -# act on multiple mode and reset button presses -# https://rsc.eworm.de/doc/mode-button.md - -:global GlobalFunctionsReady; -:while ($GlobalFunctionsReady != true) do={ :delay 500ms; } - -:local ExitOK false; -:do { - :local ScriptName [ :jobname ]; - - :global ModeButton; - - :global LogPrint; - - :set ($ModeButton->"count") ($ModeButton->"count" + 1); - - :local Scheduler [ /system/scheduler/find where name="_ModeButtonScheduler" ]; - - :if ([ :len $Scheduler ] = 0) do={ - $LogPrint info $ScriptName ("Creating scheduler _ModeButtonScheduler, counting presses..."); - :global ModeButtonScheduler do={ :do { - :local FuncName $0; - - :global ModeButton; - - :global LogPrint; - :global ModeButtonScheduler; - :global ValidateSyntax; - - :local LEDInvert do={ - :global ModeButtonLED; - - :global IfThenElse; - - :local LED [ /system/leds/find where leds=$ModeButtonLED \ - !disabled type~"^(on|off)\$" interface=[] ]; - :if ([ :len $LED ] = 0) do={ - :return false; - } - /system/leds/set type=[ $IfThenElse ([ get $LED type ] = "on") "off" "on" ] $LED; - } - - :local Count ($ModeButton->"count"); - :local Code ($ModeButton->[ :tostr $Count ]); - - :set ($ModeButton->"count") 0; - :set ModeButtonScheduler; - /system/scheduler/remove [ find where name="_ModeButtonScheduler" ]; - - :if ([ :len $Code ] > 0) do={ - :if ([ $ValidateSyntax $Code ] = true) do={ - $LogPrint info $FuncName ("Acting on " . $Count . " mode-button presses: " . $Code); - - :for I from=1 to=$Count do={ - $LEDInvert; - :if ([ /system/routerboard/settings/get silent-boot ] = false) do={ - :beep length=200ms; - } - :delay 200ms; - $LEDInvert; - :delay 200ms; - } - - :do { - [ :parse $Code ]; - } on-error={ - $LogPrint warning $FuncName \ - ("The code for " . $Count . " mode-button presses failed with runtime error!"); - } - } else={ - $LogPrint warning $FuncName \ - ("The code for " . $Count . " mode-button presses failed syntax validation!"); - } - } else={ - $LogPrint info $FuncName ("No action defined for " . $Count . " mode-button presses."); - } - } on-error={ - :global ExitError; $ExitError false $0; - } } - /system/scheduler/add name="_ModeButtonScheduler" \ - on-event=":global ModeButtonScheduler; \$ModeButtonScheduler;" interval=3s; - } else={ - $LogPrint debug $ScriptName ("Updating scheduler _ModeButtonScheduler..."); - /system/scheduler/set $Scheduler start-time=[ /system/clock/get time ]; - } -} on-error={ - :global ExitError; $ExitError $ExitOK [ :jobname ]; -} diff --git a/netwatch-dns.rsc b/netwatch-dns.rsc deleted file mode 100644 index 467d636..0000000 --- a/netwatch-dns.rsc +++ /dev/null @@ -1,150 +0,0 @@ -#!rsc by RouterOS -# RouterOS script: netwatch-dns -# Copyright (c) 2022-2025 Christian Hesse -# https://rsc.eworm.de/COPYING.md -# -# requires RouterOS, version=7.16 -# requires device-mode, fetch -# -# monitor and manage dns/doh with netwatch -# https://rsc.eworm.de/doc/netwatch-dns.md - -:global GlobalFunctionsReady; -:while ($GlobalFunctionsReady != true) do={ :delay 500ms; } - -:local ExitOK false; -:do { - :local ScriptName [ :jobname ]; - - :global CertificateAvailable; - :global EitherOr; - :global IsDNSResolving; - :global IsTimeSync; - :global LogPrint; - :global LogPrintOnce; - :global ParseKeyValueStore; - :global ScriptLock; - - :if ([ $ScriptLock $ScriptName ] = false) do={ - :set ExitOK true; - :error false; - } - - :local SettleTime (5m30s - [ /system/resource/get uptime ]); - :if ($SettleTime > 0s) do={ - $LogPrint info $ScriptName ("System just booted, giving netwatch " . $SettleTime . " to settle."); - :set ExitOK true; - :error true; - } - - :local DnsServers ({}); - :local DnsFallback ({}); - :local DnsCurrent [ /ip/dns/get servers ]; - - :foreach Host in=[ /tool/netwatch/find where comment~"\\bdns\\b" status="up" ] do={ - :local HostVal [ /tool/netwatch/get $Host ]; - :local HostInfo [ $ParseKeyValueStore ($HostVal->"comment") ]; - - :if ($HostInfo->"disabled" != true) do={ - :if ($HostInfo->"dns" = true) do={ - :set DnsServers ($DnsServers, $HostVal->"host"); - } - :if ($HostInfo->"dns-fallback" = true) do={ - :set DnsFallback ($DnsFallback, $HostVal->"host"); - } - } - } - - :if ([ :len $DnsServers ] > 0) do={ - :if ($DnsServers != $DnsCurrent) do={ - $LogPrint info $ScriptName ("Updating DNS servers: " . [ :tostr $DnsServers ]); - /ip/dns/set servers=$DnsServers; - /ip/dns/cache/flush; - } - } else={ - :if ([ :len $DnsFallback ] > 0) do={ - :if ($DnsFallback != $DnsCurrent) do={ - $LogPrint info $ScriptName ("Updating DNS servers to fallback: " . [ :tostr $DnsFallback ]); - /ip/dns/set servers=$DnsFallback; - /ip/dns/cache/flush; - } - } - } - - :local DohCurrent [ /ip/dns/get use-doh-server ]; - :local DohServers ({}); - - :if ([ :len $DohCurrent ] > 0 && [ $IsDNSResolving ] = false && [ $IsTimeSync ] = false) do={ - $LogPrint info $ScriptName ("Time is not sync, disabling DoH: " . $DohCurrent); - /ip/dns/set use-doh-server=""; - :set DohCurrent ""; - } - - :foreach Host in=[ /tool/netwatch/find where comment~"\\bdoh\\b" status="up" ] do={ - :local HostVal [ /tool/netwatch/get $Host ]; - :local HostInfo [ $ParseKeyValueStore ($HostVal->"comment") ]; - :local HostName [ /ip/dns/static/find where name address=($HostVal->"host") \ - (type="A" or type="AAAA") !disabled !dynamic ]; - :if ([ :len $HostName ] > 0) do={ - :set HostName [ /ip/dns/static/get ($HostName->0) name ]; - } - - :if ($HostInfo->"doh" = true && $HostInfo->"disabled" != true) do={ - :if ([ :len ($HostInfo->"doh-url") ] = 0) do={ - :set ($HostInfo->"doh-url") ("https://" . [ $EitherOr $HostName ($HostVal->"host") ] . "/dns-query"); - } - - :if ($DohCurrent = $HostInfo->"doh-url") do={ - $LogPrint debug $ScriptName ("Current DoH server is still up: " . $DohCurrent); - :set ExitOK true; - :error true; - } - - :set ($DohServers->[ :len $DohServers ]) $HostInfo; - } - } - - :if ([ :len $DohCurrent ] > 0) do={ - $LogPrint info $ScriptName ("Current DoH server is down, disabling: " . $DohCurrent); - /ip/dns/set use-doh-server=""; - /ip/dns/cache/flush; - } - - :foreach DohServer in=$DohServers do={ - :if ([ :len ($DohServer->"doh-cert") ] > 0) do={ - :if ([ $CertificateAvailable ($DohServer->"doh-cert") ] = false) do={ - $LogPrint warning $ScriptName ("Downloading certificate failed, trying without."); - } - } - - :local Data false; - :do { - :set Data ([ /tool/fetch check-certificate=yes-without-crl output=user \ - http-header-field=({ "accept: application/dns-message" }) \ - url=(($DohServer->"doh-url") . "?dns=" . [ :convert to=base64 ([ :rndstr length=2 ] . \ - "\01\00" . "\00\01" . "\00\00" . "\00\00" . "\00\00" . "\09doh-check\05eworm\02de\00" . \ - "\00\10" . "\00\01") ]) as-value ]->"data"); - } on-error={ - $LogPrint warning $ScriptName ("Request to DoH server failed (network or certificate issue): " . \ - ($DohServer->"doh-url")); - } - - :if ($Data != false) do={ - :if ([ :typeof [ :find $Data "doh-check-OK" ] ] = "num") do={ - /ip/dns/set use-doh-server=($DohServer->"doh-url") verify-doh-cert=yes; - :if ([ /certificate/settings/get crl-use ] = true) do={ - $LogPrintOnce warning $ScriptName ("Configured to use CRL, that can cause severe issue!"); - } - /ip/dns/cache/flush; - $LogPrint info $ScriptName ("Setting DoH server: " . ($DohServer->"doh-url")); - :set ExitOK true; - :error true; - } else={ - $LogPrint warning $ScriptName ("Received unexpected response from DoH server: " . \ - ($DohServer->"doh-url")); - } - } - } -} on-error={ - :global ExitError; $ExitError $ExitOK [ :jobname ]; -} diff --git a/netwatch-notify.rsc b/netwatch-notify.rsc deleted file mode 100644 index 0b8a8dc..0000000 --- a/netwatch-notify.rsc +++ /dev/null @@ -1,229 +0,0 @@ -#!rsc by RouterOS -# RouterOS script: netwatch-notify -# Copyright (c) 2020-2025 Christian Hesse -# https://rsc.eworm.de/COPYING.md -# -# requires RouterOS, version=7.15 -# -# monitor netwatch and send notifications -# https://rsc.eworm.de/doc/netwatch-notify.md - -:global GlobalFunctionsReady; -:while ($GlobalFunctionsReady != true) do={ :delay 500ms; } - -:local ExitOK false; -:do { - :local ScriptName [ :jobname ]; - - :global NetwatchNotify; - - :global EitherOr; - :global IfThenElse; - :global IsDNSResolving; - :global LogPrint; - :global ParseKeyValueStore; - :global ScriptFromTerminal; - :global ScriptLock; - :global SendNotification2; - :global SymbolForNotification; - - :local NetwatchNotifyHook do={ - :local ScriptName [ :tostr $1 ]; - :local Name [ :tostr $2 ]; - :local Type [ :tostr $3 ]; - :local State [ :tostr $4 ]; - :local Hook [ :tostr $5 ]; - - :global LogPrint; - :global ValidateSyntax; - - :if ([ $ValidateSyntax $Hook ] = true) do={ - :do { - [ :parse $Hook ]; - } on-error={ - $LogPrint warning $ScriptName ("The " . $State . "-hook for " . $Type . " '" . $Name . "' failed to run."); - :return ("The hook failed to run."); - } - } else={ - $LogPrint warning $ScriptName ("The " . $State . "-hook for " . $Type . " '" . $Name . "' failed syntax validation."); - :return ("The hook failed syntax validation."); - } - - $LogPrint info $ScriptName ("Ran hook on " . $Type . " '" . $Name . "' " . $State . ": " . $Hook); - :return ("Ran hook:\n" . $Hook); - } - - :local ResolveExpected do={ - :local ScriptName [ :tostr $1 ]; - :local Name [ :tostr $2 ]; - :local Expected [ :tostr $3 ]; - - :global GetRandom20CharAlNum; - - :local FwAddrList ($ScriptName . "-" . [ $GetRandom20CharAlNum ]); - :if ([ :typeof [ :toip $Expected ] ] = "ip") do={ - /ip/firewall/address-list/add address=$Name list=$FwAddrList dynamic=yes timeout=10s; - :delay 20ms; - :if ([ :len [ /ip/firewall/address-list/find where list=$FwAddrList address=$Expected ] ] > 0) do={ - :return true; - } - } - :if ([ :typeof [ :toip6 $Expected ] ] = "ip6") do={ - /ipv6/firewall/address-list/add address=$Name list=$FwAddrList dynamic=yes timeout=10s; - :delay 20ms; - :if ([ :len [ /ipv6/firewall/address-list/find where list=$FwAddrList address=$Expected ] ] > 0) do={ - :return true; - } - } - - :return false; - } - - :if ([ $ScriptLock $ScriptName ] = false) do={ - :set ExitOK true; - :error false; - } - - :local ScriptFromTerminalCached [ $ScriptFromTerminal $ScriptName ]; - - :if ([ :typeof $NetwatchNotify ] = "nothing") do={ - :set NetwatchNotify ({}); - } - - :foreach Host in=[ /tool/netwatch/find where comment~"\\bnotify\\b" !disabled status!="unknown" ] do={ - :local HostVal [ /tool/netwatch/get $Host ]; - :local Type [ $IfThenElse ($HostVal->"type" ~ "^(https?-get|tcp-conn)\$") "service" "host" ]; - :local HostInfo [ $ParseKeyValueStore ($HostVal->"comment") ]; - :local HostDetails ($HostVal->"host" . \ - [ $IfThenElse ([ :len ($HostInfo->"resolve") ] > 0) (", " . $HostInfo->"resolve") ]); - - :if ($HostInfo->"notify" = true && $HostInfo->"disabled" != true) do={ - :local Name [ $EitherOr ($HostInfo->"name") ($HostVal->"name") ]; - - :local Metric { "count-down"=0; "count-up"=0; "notified"=false; "resolve-failcnt"=0 }; - :if ([ :typeof ($NetwatchNotify->$Name) ] = "array") do={ - :set $Metric ($NetwatchNotify->$Name); - } - - :if ([ :typeof ($HostInfo->"resolve") ] = "str") do={ - :if ([ $IsDNSResolving ] = true) do={ - :do { - :local Resolve [ :resolve type=[ $IfThenElse ([ :typeof ($HostVal->"host") ] = "ip") \ - "ipv4" "ipv6" ] ($HostInfo->"resolve") ]; - :if ($Resolve != $HostVal->"host") do={ - :if ([ $ResolveExpected $ScriptName ($HostInfo->"resolve") ($HostVal->"host") ] = false) do={ - $LogPrint info $ScriptName ("Name '" . $HostInfo->"resolve" . [ $IfThenElse \ - ($HostInfo->"resolve" != $HostInfo->"name") ("' for " . $Type . " '" . \ - $HostInfo->"name") "" ] . "' resolves to different address " . $Resolve . \ - ", updating."); - /tool/netwatch/set host=$Resolve $Host; - :set ($Metric->"resolve-failcnt") 0; - :set ($HostVal->"status") "unknown"; - } - } - } on-error={ - :set ($Metric->"resolve-failcnt") ($Metric->"resolve-failcnt" + 1); - :if ($Metric->"resolve-failcnt" = 3) do={ - $LogPrint [ $IfThenElse ($HostInfo->"no-resolve-fail" != true) warning debug ] \ - $ScriptName ("Resolving name '" . $HostInfo->"resolve" . [ $IfThenElse \ - ($HostInfo->"resolve" != $HostInfo->"name") ("' for " . $Type . " '" . \ - $HostInfo->"name") "" ] . "' failed."); - } - } - } - } - - :if ($HostVal->"status" = "up") do={ - :local CountDown ($Metric->"count-down"); - :if ($CountDown > 0) do={ - $LogPrint info $ScriptName \ - ("The " . $Type . " '" . $Name . "' (" . $HostDetails . ") is up."); - :set ($Metric->"count-down") 0; - } - :set ($Metric->"count-up") ($Metric->"count-up" + 1); - :if ($Metric->"notified" = true) do={ - :local Message ("The " . $Type . " '" . $Name . "' (" . $HostDetails . \ - ") is up since " . $HostVal->"since" . ".\n" . \ - "It was down for " . $CountDown . " checks since " . ($Metric->"since") . "."); - :if ([ :typeof ($HostInfo->"note") ] = "str") do={ - :set Message ($Message . "\n\nNote:\n" . ($HostInfo->"note")); - } - :if ([ :typeof ($HostInfo->"up-hook") ] = "str") do={ - :set Message ($Message . "\n\n" . [ $NetwatchNotifyHook $ScriptName $Name $Type "up" \ - ($HostInfo->"up-hook") ]); - } - $SendNotification2 ({ origin=[ $EitherOr ($HostInfo->"origin") $ScriptName ]; silent=($HostInfo->"silent"); \ - subject=([ $SymbolForNotification "white-heavy-check-mark" ] . "Netwatch Notify: " . $Name . " up"); \ - message=$Message; link=($HostInfo->"link") }); - } - :set ($Metric->"notified") false; - :set ($Metric->"parent") ($HostInfo->"parent"); - :set ($Metric->"since"); - } - - :if ($HostVal->"status" = "down") do={ - :set ($Metric->"count-down") ($Metric->"count-down" + 1); - :set ($Metric->"count-up") 0; - :set ($Metric->"parent") ($HostInfo->"parent"); - :set ($Metric->"since") ($HostVal->"since"); - :local CountDown [ $IfThenElse ([ :tonum ($HostInfo->"count") ] > 0) ($HostInfo->"count") 5 ]; - :local Parent ($HostInfo->"parent"); - :local ParentUp false; - :while ([ :len $Parent ] > 0) do={ - :set CountDown ($CountDown + 1); - :set Parent ($NetwatchNotify->$Parent->"parent"); - } - :set Parent ($HostInfo->"parent"); - :local ParentNotified false; - :while ($ParentNotified = false && [ :len $Parent ] > 0) do={ - :set ParentNotified [ $IfThenElse (($NetwatchNotify->$Parent->"notified") = true) \ - true false ]; - :set ParentUp ($NetwatchNotify->$Parent->"count-up"); - :if ($ParentNotified = false) do={ - :set Parent ($NetwatchNotify->$Parent->"parent"); - } - } - :if ($Metric->"notified" = false || $Metric->"count-down" % 120 = 0 || \ - $ScriptFromTerminalCached = true) do={ - $LogPrint [ $IfThenElse ($HostInfo->"no-down-notification" != true) info debug ] $ScriptName \ - ("The " . $Type . " '" . $Name . "' (" . $HostDetails . ") is down for " . \ - $Metric->"count-down" . " checks, " . [ $IfThenElse ($ParentNotified = false) [ $IfThenElse \ - ($Metric->"notified" = true) ("already notified.") ($CountDown - $Metric->"count-down" . \ - " to go.") ] ("parent " . $Type . " " . $Parent . " is down.") ]); - } - :if ((($CountDown * 2) - ($Metric->"count-down" * 3)) / 2 = 0 && \ - [ :typeof ($HostInfo->"pre-down-hook") ] = "str") do={ - $NetwatchNotifyHook $ScriptName $Name $Type "pre-down" ($HostInfo->"pre-down-hook"); - } - :if ($ParentNotified = false && $Metric->"count-down" >= $CountDown && \ - ($ParentUp = false || $ParentUp > 2) && $Metric->"notified" != true) do={ - :local Message ("The " . $Type . " '" . $Name . "' (" . $HostDetails . \ - ") is down since " . $HostVal->"since" . "."); - :if ([ :typeof ($HostInfo->"note") ] = "str") do={ - :set Message ($Message . "\n\nNote:\n" . ($HostInfo->"note")); - } - :if ([ :typeof ($HostInfo->"down-hook") ] = "str") do={ - :set Message ($Message . "\n\n" . [ $NetwatchNotifyHook $ScriptName $Name $Type "down" \ - ($HostInfo->"down-hook") ]); - } - :if ($HostInfo->"no-down-notification" != true) do={ - $SendNotification2 ({ origin=[ $EitherOr ($HostInfo->"origin") $ScriptName ]; silent=($HostInfo->"silent"); \ - subject=([ $SymbolForNotification "cross-mark" ] . "Netwatch Notify: " . $Name . " down"); \ - message=$Message; link=($HostInfo->"link") }); - } - :set ($Metric->"notified") true; - } - } - - :set ($NetwatchNotify->$Name) { - "count-down"=($Metric->"count-down"); - "count-up"=($Metric->"count-up"); - "notified"=($Metric->"notified"); - "parent"=($Metric->"parent"); - "resolve-failcnt"=($Metric->"resolve-failcnt"); - "since"=($Metric->"since") }; - } - } -} on-error={ - :global ExitError; $ExitError $ExitOK [ :jobname ]; -} diff --git a/netwatch-syslog b/netwatch-syslog new file mode 100644 index 0000000..03aa1a5 --- /dev/null +++ b/netwatch-syslog @@ -0,0 +1,15 @@ +#!rsc +# RouterOS script: netwatch-syslog +# Copyright (c) 2013-2019 Christian Hesse +# +# requires: dont-require-permissions=yes +# +# manage remote logging facilities + +:local Remote [ /system logging action get ([ find where target=remote ]->0) remote ]; + +if ([ / tool netwatch get [ find where host=$Remote ] status ] = "up") do={ + / system logging set disabled=no [ find where action=remote disabled=yes ]; +} else={ + / system logging set disabled=yes [ find where action=remote disabled=no ]; +} diff --git a/news-and-changes.rsc b/news-and-changes.rsc deleted file mode 100644 index 459326f..0000000 --- a/news-and-changes.rsc +++ /dev/null @@ -1,72 +0,0 @@ -# News, changes and migration by RouterOS Scripts -# Copyright (c) 2019-2025 Christian Hesse -# https://rsc.eworm.de/COPYING.md - -:global IDonate; - -:global IfThenElse; -:global RequiredRouterOS; -:global SymbolForNotification; - -:local Resource [ /system/resource/get ]; - -# News, changes and migration up to change 95: -# https://git.eworm.de/cgit/routeros-scripts/plain/global-config.changes?h=change-95 - -# Changes for global-config to be added to notification on script updates -:global GlobalConfigChanges { - 96="Added support for notes in 'netwatch-notify', these are included verbatim into the notification."; - 97="Modified 'dhcp-to-dns' to always add A records for names with mac address, and optionally add CNAME records if the host name is available."; - 98="Extended 'check-certificates' to download new certificate by SubjectAltNames if download by CommonName fails."; - 99="Modified 'dhcp-to-dns', which dropped global configuration. Settings moved to dhcp server's network definitions."; - 100="The script 'ssh-keys-import' became a module 'mod/ssh-keys-import' with enhanced functionality."; - 101="Introduced new script 'fw-addr-lists' to download, import and update firewall address-lists."; - 102="Modified 'hotspot-to-wpa' to support non-local (radius) users."; - 103="Dropped hard-coded name and timeout from 'hotspot-to-wpa-cleanup', instead a comment is required for dhcp server now."; - 104="All relevant scripts were ported to new wifiwave2 and are available for AX devices now!"; - 105="Extended 'check-routeros-update' to support automatic update from specific neighbor(s)."; - 106="Modified 'telegram-chat' to make it act on message replies, without activation. Also made it answer a single question mark with a short notice."; - 107="Dropped support for non-fixed width font in Telegram notifications."; - 108="Enhanced 'log-forward' to list log messages with colorful bullets to indicate severity."; - 109="Added support to send notifications via Ntfy (ntfy.sh)."; - 110="Dropped support for loading scripts from local storage."; - 111="Modified 'dhcp-to-dns' to allow multiple records for one mac address."; - 112="Enhanced 'mod/ssh-keys-import' to record the fingerprint of keys."; - 113="Added helper functions for easier setup to Matrix notification module."; - 114="All relevant scripts were ported to new wifi package for RouterOS 7.13 and later. Migration is complex and thus not done automatically!"; - 115=("Celebrating " . [ $SymbolForNotification "sparkles,star" ] . "1.000 stars " . [ $SymbolForNotification "star,sparkles" ] . "on Github! Please continue starring..."); - 116=("... and also please keep in mind that it takes a huge amount of time maintaining these scripts. " . [ $IfThenElse ($IDonate != true) \ - ("Following the donation hint " . [ $SymbolForNotification "arrow-down" "below" ] . "to keep me motivated is much appreciated. Thanks!") \ - ("Looks like you did donate already. " . [ $SymbolForNotification "heart" "<3" ] . "Much appreciated, thanks!") ]); - 117="Enhanced 'packages-update' to support deferred reboot on automatically installed updates."; - 118=("RouterOS packages increase in size with each release. This becomes a problem for devices with 16MB storage and below. " . \ - [ $IfThenElse ($Resource->"total-hdd-space" < 16000000) ("Your " . $Resource->"board-name" . " is specifically affected! ") \ - [ $IfThenElse ($Resource->"free-hdd-space" > 4000000) ("(Your " . $Resource->"board-name" . " does not suffer this issue.) ") ] ] . \ - "Huge configuration and lots of scripts give an extra risk. Take care!"); - 119="Added support for IPv6 to script 'fw-addr-lists'."; - 120="Implemented a workaround in 'backup-cloud'. Now script should no longer just crash, but send notification with error."; - 121="The 'wifiwave2' scripts are finally gone. Development continues with 'wifi' in RouterOS 7.13 and later."; - 122="The global configuration was enhanced to support loading snippets. Configuration can be split off to scripts where name starts with 'global-config-overlay.d/'."; - 123="Introduced new function '\$LogPrint', and deprecated '\$LogPrintExit2'. Please update custom scripts if you use it."; - 124="Added support for links in 'netwatch-notify', these are added below the formatted notification text."; - 125=("April's Fool! " . [ $SymbolForNotification "smiley-partying-face" ] . "Well, you missed it... - no charge nor fees. (Anyway... Donations are much appreciated, " . [ $SymbolForNotification "smiley-smiling-face" ] . "thanks!)"); - 126="Made 'telegram-chat' capable of handling large command output. Telegram messages still limit the size, so it is truncated now."; - 127="Added support for authentication to Ntfy notification module."; - 128="Added another list from blocklist.de to default configuration for 'fw-addr-lists'."; - 129="Extended 'backup-partition' to support RouterOS copy-over - interactively or before feature update."; - 130="Dropped intermediate certificates, depending on just root certificates now."; - 131="Enhanced certificate download to fallback to mkcert.org, so all (commonly trusted) root certificates are available now."; - 132="Split off plugins from 'check-health', so the script works on all devices to monitor CPU and RAM. The supported plugins for sensors in hardware are installed automatically."; - 133="Updated the default configuration for 'fw-addr-lists', deprecated lists were removed, a collective list was added."; - 134="Enhanced 'mod/notification-telegram' and 'telegram-chat' to support topics in groups."; - 135="Introduced helper function '\$GetTelegramChatId' for 'mod/notification-telegram' which helps retrieve information."; -}; - -# Migration steps to be applied on script updates -:global GlobalConfigMigration { - 97=":local Rec [ /ip/dns/static/find where comment~\"^managed by dhcp-to-dns for \" ]; :if ([ :len \$Rec ] > 0) do={ /ip/dns/static/remove \$Rec; /system/script/run dhcp-to-dns; }"; - 100=":global ScriptInstallUpdate; :if ([ :len [ /system/script/find where name=\"ssh-keys-import\" source~\"^#!rsc by RouterOS\\r?\\n\" ] ] > 0) do={ /system/script/set name=\"mod/ssh-keys-import\" ssh-keys-import; \$ScriptInstallUpdate; }"; - 104=":global CharacterReplace; :global ScriptInstallUpdate; :foreach Script in={ \"capsman-download-packages\"; \"capsman-rolling-upgrade\"; \"hotspot-to-wpa\"; \"hotspot-to-wpa-cleanup\" } do={ /system/script/set name=(\$Script . \".capsman\") [ find where name=\$Script ]; :foreach Scheduler in=[ /system/scheduler/find where on-event~(\$Script . \"([^-.]|\\\$)\") ] do={ /system/scheduler/set \$Scheduler on-event=[ \$CharacterReplace [ get \$Scheduler on-event ] \$Script (\$Script . \".capsman\") ]; }; }; /ip/hotspot/user/profile/set on-login=\"hotspot-to-wpa.capsman\" [ find where on-login=\"hotspot-to-wpa\" ]; \$ScriptInstallUpdate;"; - 111=":local Rec [ /ip/dns/static/find where comment~\"^managed by dhcp-to-dns for \" ]; :if ([ :len \$Rec ] > 0) do={ /ip/dns/static/remove \$Rec; /system/script/run dhcp-to-dns; }"; - 132=":if ([ :len [ /system/script/find where name=\"check-health\" ] ] > 0) do={ :local Code \":local Install \\\"check-health\\\"; :if ([ :len [ /system/health/find where type=\\\"\\\" name~\\\"-state\\\\\\\$\\\" ] ] > 0) do={ :set Install (\\\$Install . \\\",check-health.d/state\\\"); }; :if ([ :len [ /system/health/find where type=\\\"C\\\" ] ] > 0) do={ :set Install (\\\$Install . \\\",check-health.d/temperature\\\"); }; :if ([ :len [ /system/health/find where type=\\\"V\\\" ] ] > 0) do={ :set Install (\\\$Install . \\\",check-health.d/voltage\\\"); }; :global ScriptInstallUpdate; \\\$ScriptInstallUpdate \\\$Install;\"; :global ValidateSyntax; :if ([ \$ValidateSyntax \$Code ] = true) do={ :do { [ :parse \$Code ]; } on-error={ }; }; }"; -}; diff --git a/ospf-to-leds.rsc b/ospf-to-leds.rsc deleted file mode 100644 index a8662b3..0000000 --- a/ospf-to-leds.rsc +++ /dev/null @@ -1,49 +0,0 @@ -#!rsc by RouterOS -# RouterOS script: ospf-to-leds -# Copyright (c) 2020-2025 Christian Hesse -# https://rsc.eworm.de/COPYING.md -# -# requires RouterOS, version=7.15 -# -# visualize ospf instance state via leds -# https://rsc.eworm.de/doc/ospf-to-leds.md - -:global GlobalFunctionsReady; -:while ($GlobalFunctionsReady != true) do={ :delay 500ms; } - -:local ExitOK false; -:do { - :local ScriptName [ :jobname ]; - - :global LogPrint; - :global ParseKeyValueStore; - :global ScriptLock; - - :if ([ $ScriptLock $ScriptName ] = false) do={ - :set ExitOK true; - :error false; - } - - :foreach Instance in=[ /routing/ospf/instance/find where comment~"^ospf-to-leds," ] do={ - :local InstanceVal [ /routing/ospf/instance/get $Instance ]; - :local LED ([ $ParseKeyValueStore ($InstanceVal->"comment") ]->"leds"); - :local LEDType [ /system/leds/get [ find where leds=$LED ] type ]; - - :local NeighborCount 0; - :foreach Area in=[ /routing/ospf/area/find where instance=($InstanceVal->"name") ] do={ - :local AreaName [ /routing/ospf/area/get $Area name ]; - :set NeighborCount ($NeighborCount + [ :len [ /routing/ospf/neighbor/find where area=$AreaName ] ]); - } - - :if ($NeighborCount > 0 && $LEDType = "off") do={ - $LogPrint info $ScriptName ("OSPF instance " . $InstanceVal->"name" . " has " . $NeighborCount . " neighbors, led on!"); - /system/leds/set type=on [ find where leds=$LED ]; - } - :if ($NeighborCount = 0 && $LEDType = "on") do={ - $LogPrint info $ScriptName ("OSPF instance " . $InstanceVal->"name" . " has no neighbors, led off!"); - /system/leds/set type=off [ find where leds=$LED ]; - } - } -} on-error={ - :global ExitError; $ExitError $ExitOK [ :jobname ]; -} diff --git a/packages-update b/packages-update new file mode 100644 index 0000000..683e9f8 --- /dev/null +++ b/packages-update @@ -0,0 +1,29 @@ +#!rsc +# RouterOS script: packages-update +# Copyright (c) 2019 Christian Hesse +# +# download packages and reboot for installation + +:global DownloadPackage; + +:local InstalledVersion [ / system package update get installed-version ]; +:local LatestVersion [ / system package update get latest-version ]; + +:if ($InstalledVersion = $LatestVersion) do={ + :error ("Version " . $LatestVersion . " is already installed."); +} + +:foreach Package in=[ / system package find where !bundle ] do={ + :local PkgName [ / system package get $Package name ]; + if ([ $DownloadPackage $PkgName $LatestVersion ] = false) do={ + :error ("Download for package " . $PkgName . " failed."); + } +} + +:if ([ / system script print count-only where name="email-backup" ] > 0) do={ + / system script run email-backup; +} + +:log info ("Rebooting for update."); +:delay 1s; +/ system reboot; diff --git a/packages-update.rsc b/packages-update.rsc deleted file mode 100644 index b11596e..0000000 --- a/packages-update.rsc +++ /dev/null @@ -1,168 +0,0 @@ -#!rsc by RouterOS -# RouterOS script: packages-update -# Copyright (c) 2019-2025 Christian Hesse -# https://rsc.eworm.de/COPYING.md -# -# requires RouterOS, version=7.15 -# requires device-mode, scheduler -# -# download packages and reboot for installation -# https://rsc.eworm.de/doc/packages-update.md - -:global GlobalFunctionsReady; -:while ($GlobalFunctionsReady != true) do={ :delay 500ms; } - -:local ExitOK false; -:do { - :local ScriptName [ :jobname ]; - - :global BackupRandomDelay; - :global PackagesUpdateDeferReboot; - :global PackagesUpdateBackupFailure; - - :global DownloadPackage; - :global Grep; - :global LogPrint; - :global ParseKeyValueStore; - :global ScriptFromTerminal; - :global ScriptLock; - :global VersionToNum; - - :local Schedule do={ - :local ScriptName [ :tostr $1 ]; - - :global GetRandomNumber; - :global LogPrint; - - :global RebootForUpdate do={ - /system/reboot; - } - - :local StartTime [ :tostr [ :totime (10800 + [ $GetRandomNumber 7200 ]) ] ]; - /system/scheduler/add name="_RebootForUpdate" start-time=$StartTime interval=1d \ - on-event=("/system/scheduler/remove \"_RebootForUpdate\"; " . \ - ":global RebootForUpdate; \$RebootForUpdate;"); - $LogPrint info $ScriptName ("Scheduled reboot for update at " . $StartTime . \ - " local time (" . [ /system/clock/get time-zone-name ] . ")."); - :return true; - } - - :if ([ $ScriptLock $ScriptName ] = false) do={ - :set ExitOK true; - :error false; - } - - :if ([ :len [ /system/scheduler/find where name="running-from-backup-partition" ] ] > 0) do={ - $LogPrint warning $ScriptName ("Running from backup partition, refusing to act."); - :set ExitOK true; - :error false; - } - - :local Update [ /system/package/update/get ]; - - :if ([ :typeof ($Update->"latest-version") ] = "nothing") do={ - $LogPrint warning $ScriptName ("Latest version is not known."); - :set ExitOK true; - :error false; - } - - :if ($Update->"installed-version" = $Update->"latest-version") do={ - $LogPrint info $ScriptName ("Version " . $Update->"latest-version" . " is already installed."); - :set ExitOK true; - :error true; - } - - :local RunOrder ({}); - :foreach Script in=[ /system/script/find where source~("\n# provides: backup-script\\b") ] do={ - :local ScriptVal [ /system/script/get $Script ]; - :local Store [ $ParseKeyValueStore [ $Grep ($ScriptVal->"source") ("\23 provides: backup-script, ") ] ]; - - :set ($RunOrder->($Store->"order" . "-" . $ScriptVal->"name")) ($ScriptVal->"name"); - } - - :local BackupRandomDelayBefore $BackupRandomDelay; - :foreach Order,Script in=$RunOrder do={ - :set BackupRandomDelay 0; - :set PackagesUpdateBackupFailure false; - :do { - $LogPrint info $ScriptName ("Running backup script " . $Script . " before update."); - /system/script/run $Script; - } on-error={ - :set PackagesUpdateBackupFailure true; - } - :set BackupRandomDelay $BackupRandomDelayBefore; - - :if ($PackagesUpdateBackupFailure = true) do={ - $LogPrint warning $ScriptName ("Running backup script " . $Script . " before update failed!"); - :if ([ $ScriptFromTerminal $ScriptName ] = true) do={ - :put "Do you want to continue anyway? [y/N]"; - :if (([ /terminal/inkey timeout=60 ] % 32) = 25) do={ - $LogPrint info $ScriptName ("User requested to continue anyway."); - } else={ - $LogPrint info $ScriptName ("Canceled update..."); - :set ExitOK true; - :error false; - } - } else={ - $LogPrint warning $ScriptName ("Canceled non-interactive update."); - :set ExitOK true; - :error false; - } - } - } - - :local NumInstalled [ $VersionToNum ($Update->"installed-version") ]; - :local NumLatest [ $VersionToNum ($Update->"latest-version") ]; - - :local DoDowngrade false; - :if ($NumInstalled > $NumLatest) do={ - :if ([ $ScriptFromTerminal $ScriptName ] = true) do={ - :put "Latest version is older than installed one. Want to downgrade? [y/N]"; - :if (([ /terminal/inkey timeout=60 ] % 32) = 25) do={ - :set DoDowngrade true; - } else={ - :put "Canceled..."; - } - } else={ - $LogPrint warning $ScriptName ("Not installing downgrade automatically."); - :set ExitOK true; - :error false; - } - } - - :foreach Package in=[ /system/package/find where !bundle !available ] do={ - :local PkgName [ /system/package/get $Package name ]; - :if ([ $DownloadPackage $PkgName ($Update->"latest-version") ] = false) do={ - $LogPrint error $ScriptName ("Download for package " . $PkgName . " failed, update aborted."); - :set ExitOK true; - :error false; - } - } - - :if ($DoDowngrade = true) do={ - $LogPrint info $ScriptName ("Rebooting for downgrade."); - :delay 1s; - /system/package/downgrade; - } - - :if ([ $ScriptFromTerminal $ScriptName ] = true) do={ - :put "Do you want to (s)chedule reboot or (r)eboot now? [s/R]"; - :if (([ /terminal/inkey timeout=60 ] % 32) = 19) do={ - $Schedule $ScriptName; - :set ExitOK true; - :error true; - } - } else={ - :if ($PackagesUpdateDeferReboot = true) do={ - $Schedule $ScriptName; - :set ExitOK true; - :error true; - } - } - - $LogPrint info $ScriptName ("Rebooting for update."); - :delay 1s; - /system/reboot; -} on-error={ - :global ExitError; $ExitError $ExitOK [ :jobname ]; -} diff --git a/ppp-on-up b/ppp-on-up new file mode 100644 index 0000000..629e340 --- /dev/null +++ b/ppp-on-up @@ -0,0 +1,23 @@ +#!rsc +# RouterOS script: ppp-on-up +# Copyright (c) 2013-2019 Christian Hesse +# +# run scripts on ppp up + +# variable $interface is available in ppp on-up script +:local Interface $interface; +:local IntName [ / interface get $Interface name ]; +:log info ("PPP interface " . $IntName . " is up."); + +/ ipv6 dhcp-client release [ find where interface=$IntName !disabled ]; + +:local Scripts { + "update-tunnelbroker" +} + +:foreach Script in=$Scripts do={ + :if ([ / system script print count-only where name=$Script ] > 0) do={ + :log debug ("Running script from ppp-on-up: " . $Script); + / system script run $Script; + } +} diff --git a/ppp-on-up.rsc b/ppp-on-up.rsc deleted file mode 100644 index e09bd9d..0000000 --- a/ppp-on-up.rsc +++ /dev/null @@ -1,44 +0,0 @@ -#!rsc by RouterOS -# RouterOS script: ppp-on-up -# Copyright (c) 2013-2025 Christian Hesse -# https://rsc.eworm.de/COPYING.md -# -# requires RouterOS, version=7.15 -# -# run scripts on ppp up -# https://rsc.eworm.de/doc/ppp-on-up.md - -:global GlobalFunctionsReady; -:while ($GlobalFunctionsReady != true) do={ :delay 500ms; } - -:local ExitOK false; -:do { - :local ScriptName [ :jobname ]; - - :global LogPrint; - - :local Interface $interface; - - :if ([ :typeof $Interface ] = "nothing") do={ - $LogPrint error $ScriptName ("This script is supposed to run from ppp on-up script hook."); - :set ExitOK true; - :error false; - } - - :local IntName [ /interface/get $Interface name ]; - $LogPrint info $ScriptName ("PPP interface " . $IntName . " is up."); - - /ipv6/dhcp-client/release [ find where interface=$IntName !disabled bound ]; - - :foreach Script in=[ /system/script/find where source~("\n# provides: ppp-on-up\r?\n") ] do={ - :local ScriptName [ /system/script/get $Script name ]; - :do { - $LogPrint debug $ScriptName ("Running script: " . $ScriptName); - /system/script/run $Script; - } on-error={ - $LogPrint warning $ScriptName ("Running script '" . $ScriptName . "' failed!"); - } - } -} on-error={ - :global ExitError; $ExitError $ExitOK [ :jobname ]; -} diff --git a/rotate-ntp b/rotate-ntp new file mode 100644 index 0000000..f342f98 --- /dev/null +++ b/rotate-ntp @@ -0,0 +1,17 @@ +#!rsc +# RouterOS script: rotate-ntp +# Copyright (c) 2013-2019 Christian Hesse +# +# rotate the ntp servers + +:global NtpPool; + +:local Ntp1 [ :resolve ("0." . $NtpPool) ]; +:local Ntp2 [ :resolve ("1." . $NtpPool) ]; + +:if ([ / system ntp client get enabled ] != true) do={ + :log warning "NTP client is not enabled!"; +} + +:log info ("Updating NTP servers to " . $Ntp1 . " and " . $Ntp2); +/ system ntp client set primary-ntp=$Ntp1 secondary-ntp=$Ntp2; diff --git a/script-updates b/script-updates new file mode 100644 index 0000000..d2390db --- /dev/null +++ b/script-updates @@ -0,0 +1,107 @@ +#!rsc +# RouterOS script: script-updates +# Copyright (c) 2013-2019 Christian Hesse +# +# update installed scripts from file or url + +:global GlobalConfigVersion; +:global ExpectedConfigVersion; +:global Identity; +:global ScriptUpdatesFetch; +:global ScriptUpdatesBaseUrl; +:global ScriptUpdatesUrlSuffix; +:global ScriptUpdatesIgnore; + +:global SendNotification; + +:foreach Script in=[ / system script find ] do={ + :local Ignore 0; + :local ScriptName [ / system script get $Script name ]; + :local ScriptPolicy [ / system script get $Script policy ]; + :local ScriptFile [ / file find where name=("script-updates/" . $ScriptName) ]; + :local SourceNew; + :if ([ :len $ScriptFile ] > 0) do={ + :set SourceNew [ / file get $ScriptFile content ]; + / file remove $ScriptFile; + } + + :foreach Scheduler in=[ / system scheduler find where on-event=$ScriptName ] do={ + :local SchedulerName [ / system scheduler get $Scheduler name ]; + :local SchedulerPolicy [ / system scheduler get $Scheduler policy ]; + :if ($ScriptPolicy != $SchedulerPolicy) do={ + :log warning ("Policies differ for script " . $ScriptName . \ + " and its scheduler " . $SchedulerName . "!"); + } + } + + :if ([ :len $SourceNew ] = 0 && $ScriptUpdatesFetch = true) do={ + :foreach IgnoreLoop in=$ScriptUpdatesIgnore do={ + :if ($IgnoreLoop = $ScriptName) do={ :set Ignore 1; } + } + + :if ($Ignore = 0) do={ + :log debug ("Fetching script from url: " . $ScriptName); + :do { + :local Result [ / tool fetch check-certificate=yes-without-crl \ + ($ScriptUpdatesBaseUrl . $ScriptName . $ScriptUpdatesUrlSuffix) \ + output=user as-value ]; + :if ($Result->"status" = "finished") do={ + :set SourceNew ($Result->"data"); + } + } on-error={ + :log info ("Failed fetching " . $ScriptName); + } + } + } + + :if ([ :len $SourceNew ] > 0) do={ + :if ([ :pick $SourceNew 0 5 ] = "#!rsc") do={ + :local SourceCurrent [ / system script get $Script source ]; + :if ($SourceNew != $SourceCurrent) do={ + :local DontRequirePermissions \ + ($SourceNew~"\n# requires: dont-require-permissions=yes\n"); + :log info ("Updating script: " . $ScriptName); + / system script set owner=$ScriptName source=$SourceNew \ + dont-require-permissions=$DontRequirePermissions $Script; + :if ($ScriptName = "global-functions") do={ + / system script run global-functions; + } + } else={ + :log debug ("Script " . $ScriptName . " did not change."); + } + } else={ + :log warning ("Looks like new script " . $ScriptName . " is not valid. Ignoring!"); + } + } else={ + :log debug ("No update for script " . $ScriptName . "."); + } +} + +:if ($GlobalConfigVersion < $ExpectedConfigVersion) do={ + :global GlobalConfigChanges; + :local ChangeLogCode; + :local Changes; + + :log debug ("Fetching changelog."); + :do { + :local Result [ / tool fetch check-certificate=yes-without-crl \ + ($ScriptUpdatesBaseUrl . "global-config.changes" . $ScriptUpdatesUrlSuffix) \ + output=user as-value ]; + :if ($Result->"status" = "finished") do={ + :set ChangeLogCode ($Result->"data"); + } + } on-error={ + :log info ("Failed fetching changes!"); + } + [ :parse $ChangeLogCode ]; + :for I from=($GlobalConfigVersion + 1) to=$ExpectedConfigVersion do={ + :set Changes ( $Changes . "\n * " . $GlobalConfigChanges->[ :tostr $I ] ); + } + + $SendNotification "Configuration warning!" \ + ("Current configuration on " . $Identity . " is out of date. " . \ + "Please update global-config, then increase variable " . \ + "GlobalConfigVersion (currently " . $GlobalConfigVersion . \ + ") to " . $ExpectedConfigVersion . " and re-run global-config.\n\n" . \ + "Changes:" . $Changes); +} diff --git a/sms-action b/sms-action new file mode 100644 index 0000000..b651c57 --- /dev/null +++ b/sms-action @@ -0,0 +1,16 @@ +#!rsc +# RouterOS script: sms-action +# Copyright (c) 2018-2019 Christian Hesse +# +# run action on received SMS + +:global SmsAction; + +:local Action $action; + +:local Code ($SmsAction->$Action); +:local Parsed [ :parse $Code ]; + +:log info ("Acting on SMS action '" . $Action . "': " . $Code); +:delay 1s; +$Parsed; diff --git a/sms-action.rsc b/sms-action.rsc deleted file mode 100644 index 3c8307a..0000000 --- a/sms-action.rsc +++ /dev/null @@ -1,41 +0,0 @@ -#!rsc by RouterOS -# RouterOS script: sms-action -# Copyright (c) 2018-2025 Christian Hesse -# https://rsc.eworm.de/COPYING.md -# -# requires RouterOS, version=7.15 -# -# run action on received SMS -# https://rsc.eworm.de/doc/sms-action.md - -:global GlobalFunctionsReady; -:while ($GlobalFunctionsReady != true) do={ :delay 500ms; } - -:local ExitOK false; -:do { - :local ScriptName [ :jobname ]; - - :global SmsAction; - - :global LogPrint; - :global ValidateSyntax; - - :local Action $action; - - :if ([ :typeof $Action ] = "nothing") do={ - $LogPrint error $ScriptName ("This script is supposed to run from SMS hook with action=..."); - :set ExitOK true; - :error false; - } - - :local Code ($SmsAction->$Action); - :if ([ $ValidateSyntax $Code ] = true) do={ - :log info ("Acting on SMS action '" . $Action . "': " . $Code); - :delay 1s; - [ :parse $Code ]; - } else={ - $LogPrint warning $ScriptName ("The code for action '" . $Action . "' failed syntax validation!"); - } -} on-error={ - :global ExitError; $ExitError $ExitOK [ :jobname ]; -} diff --git a/sms-forward b/sms-forward new file mode 100644 index 0000000..f22949f --- /dev/null +++ b/sms-forward @@ -0,0 +1,37 @@ +#!rsc +# RouterOS script: sms-forward +# Copyright (c) 2013-2019 Christian Hesse +# +# forward SMS to e-mail + +:global Identity; + +:global SendNotification; + +# check mail server +:if ([ / tool netwatch get [ find where comment=[ / tool e-mail get address ] ] status ] != "up") do={ + :error "Mail server is not up."; +} + +:local Allowed [ / tool sms get allowed-number ]; +:local Secret [ / tool sms get secret ]; + +# forward SMS in a loop +:foreach Sms in=[ / tool sms inbox find ] do={ + :local Message [ / tool sms inbox get $Sms message ]; + :local Phone [ / tool sms inbox get $Sms phone ]; + :local TimeStamp [ / tool sms inbox get $Sms timestamp ]; + :local Type [ / tool sms inbox get $Sms type ]; + + :if ($Phone = $Allowed && $Message~("^:cmd " . $Secret . " script ")) do={ + :log debug "Ignoring SMS, which starts a script."; + } else={ + $SendNotification ("SMS Forwarding") \ + ("A message was received by " . $Identity . ":\n\n" . \ + "Phone: " . $Phone . "\n" . \ + "Timestamp: " . $TimeStamp . "\n" . \ + "Type: " . $Type . "\n\n" . \ + "Message:\n" . $Message); + / tool sms inbox remove $Sms; + } +} diff --git a/sms-forward.rsc b/sms-forward.rsc deleted file mode 100644 index 8169022..0000000 --- a/sms-forward.rsc +++ /dev/null @@ -1,101 +0,0 @@ -#!rsc by RouterOS -# RouterOS script: sms-forward -# Copyright (c) 2013-2025 Christian Hesse -# Anatoly Bubenkov -# https://rsc.eworm.de/COPYING.md -# -# requires RouterOS, version=7.15 -# -# forward SMS to e-mail -# https://rsc.eworm.de/doc/sms-forward.md - -:global GlobalFunctionsReady; -:while ($GlobalFunctionsReady != true) do={ :delay 500ms; } - -:local ExitOK false; -:do { - :local ScriptName [ :jobname ]; - - :global Identity; - :global SmsForwardHooks; - - :global IfThenElse; - :global LogPrint; - :global LogPrintOnce; - :global ScriptLock; - :global SendNotification2; - :global SymbolForNotification; - :global ValidateSyntax; - :global WaitFullyConnected; - - :if ([ $ScriptLock $ScriptName ] = false) do={ - :set ExitOK true; - :error false; - } - - :if ([ /tool/sms/get receive-enabled ] = false) do={ - $LogPrintOnce warning $ScriptName ("Receiving of SMS is not enabled."); - :set ExitOK true; - :error false; - } - - $WaitFullyConnected; - - :local Settings [ /tool/sms/get ]; - - :if ([ /interface/lte/get ($Settings->"port") running ] != true) do={ - $LogPrint info $ScriptName ("The LTE interface is not in running state, skipping."); - :set ExitOK true; - :error true; - } - - # forward SMS in a loop - :while ([ :len [ /tool/sms/inbox/find ] ] > 0) do={ - :local Phone [ /tool/sms/inbox/get ([ find ]->0) phone ]; - :local Messages ""; - :local Delete ({}); - - :foreach Sms in=[ /tool/sms/inbox/find where phone=$Phone ] do={ - :local SmsVal [ /tool/sms/inbox/get $Sms ]; - - :if ($Phone = $Settings->"allowed-number" && \ - ($SmsVal->"message")~("^:cmd " . $Settings->"secret" . " script ")) do={ - $LogPrint debug $ScriptName ("Removing SMS, which started a script."); - /tool/sms/inbox/remove $Sms; - } else={ - :set Messages ($Messages . "\n\nOn " . $SmsVal->"timestamp" . \ - " type " . $SmsVal->"type" . ":\n" . $SmsVal->"message"); - :foreach Hook in=$SmsForwardHooks do={ - :if ($Phone~($Hook->"allowed-number") && ($SmsVal->"message")~($Hook->"match")) do={ - :if ([ $ValidateSyntax ($Hook->"command") ] = true) do={ - $LogPrint info $ScriptName ("Running hook '" . $Hook->"match" . "': " . $Hook->"command"); - :do { - :local Command [ :parse ($Hook->"command") ]; - $Command Phone=$Phone Message=($SmsVal->"message"); - :set Messages ($Messages . "\n\nRan hook '" . $Hook->"match" . "':\n" . $Hook->"command"); - } on-error={ - $LogPrint warning $ScriptName ("The code for hook '" . $Hook->"match" . "' failed to run!"); - } - } else={ - $LogPrint warning $ScriptName ("The code for hook '" . $Hook->"match" . "' failed syntax validation!"); - } - } - } - :set Delete ($Delete, $Sms); - } - } - - :if ([ :len $Messages ] > 0) do={ - :local Count [ :len $Delete ]; - $SendNotification2 ({ origin=$ScriptName; \ - subject=([ $SymbolForNotification "incoming-envelope" ] . "SMS Forwarding from " . $Phone); \ - message=("Received " . [ $IfThenElse ($Count = 1) "this message" ("these " . $Count . " messages") ] . \ - " by " . $Identity . " from " . $Phone . ":" . $Messages) }); - :foreach Sms in=$Delete do={ - /tool/sms/inbox/remove $Sms; - } - } - } -} on-error={ - :global ExitError; $ExitError $ExitOK [ :jobname ]; -} diff --git a/ssh-keys-import b/ssh-keys-import new file mode 100644 index 0000000..0f7fb5b --- /dev/null +++ b/ssh-keys-import @@ -0,0 +1,11 @@ +#!rsc +# RouterOS script: ssh-keys-import +# Copyright (c) 2013-2019 Christian Hesse +# +# import ssh keys from file + +# Split files with several keys from a shell... +# while read type key name; do echo $type $key $name > $name.pub; done < keys.pub +# ... then transfer with scp/sftp. + +:foreach Key in=[ / file find where type="ssh key" ] do={ / user ssh-key import user=admin public-key-file=[ / file get $Key name ]; } diff --git a/super-mario-theme.rsc b/super-mario-theme similarity index 95% rename from super-mario-theme.rsc rename to super-mario-theme index 726c526..3e077c3 100644 --- a/super-mario-theme.rsc +++ b/super-mario-theme @@ -1,10 +1,8 @@ -#!rsc by RouterOS +#!rsc # RouterOS script: super-mario-theme -# Copyright (c) 2013-2025 Christian Hesse -# https://rsc.eworm.de/COPYING.md +# Copyright (c) 2013-2019 Christian Hesse # # play Super Mario theme -# https://rsc.eworm.de/doc/super-mario-theme.md :local Beeps { { 660; 100 }; 150; { 660; 100 }; 300; { 660; 100 }; 300; diff --git a/telegram-chat.rsc b/telegram-chat.rsc deleted file mode 100644 index 5db4860..0000000 --- a/telegram-chat.rsc +++ /dev/null @@ -1,195 +0,0 @@ -#!rsc by RouterOS -# RouterOS script: telegram-chat -# Copyright (c) 2023-2025 Christian Hesse -# https://rsc.eworm.de/COPYING.md -# -# requires RouterOS, version=7.15 -# requires device-mode, fetch -# -# use Telegram to chat with your Router and send commands -# https://rsc.eworm.de/doc/telegram-chat.md - -:global GlobalFunctionsReady; -:while ($GlobalFunctionsReady != true) do={ :delay 500ms; } - -:local ExitOK false; -:do { - :local ScriptName [ :jobname ]; - - :global Identity; - :global TelegramChatActive; - :global TelegramChatGroups; - :global TelegramChatId; - :global TelegramChatIdsTrusted; - :global TelegramChatOffset; - :global TelegramChatRunTime; - :global TelegramMessageIDs; - :global TelegramRandomDelay; - :global TelegramTokenId; - - :global CertificateAvailable; - :global EitherOr; - :global EscapeForRegEx; - :global GetRandom20CharAlNum; - :global IfThenElse; - :global LogPrint; - :global MAX; - :global MIN; - :global MkDir; - :global RandomDelay; - :global RmDir; - :global ScriptLock; - :global SendTelegram2; - :global SymbolForNotification; - :global ValidateSyntax; - :global WaitForFile; - :global WaitFullyConnected; - - :if ([ $ScriptLock $ScriptName ] = false) do={ - :set ExitOK true; - :error false; - } - - $WaitFullyConnected; - - :if ([ :typeof $TelegramChatOffset ] != "array") do={ - :set TelegramChatOffset { 0; 0; 0 }; - } - :if ([ :typeof $TelegramRandomDelay ] != "num") do={ - :set TelegramRandomDelay 0; - } - - :if ([ $CertificateAvailable "Go Daddy Root Certificate Authority - G2" ] = false) do={ - $LogPrint warning $ScriptName ("Downloading required certificate failed."); - :set ExitOK true; - :error false; - } - - $RandomDelay $TelegramRandomDelay; - - :local Data false; - :for I from=1 to=4 do={ - :if ($Data = false) do={ - :do { - :set Data ([ /tool/fetch check-certificate=yes-without-crl output=user \ - ("https://api.telegram.org/bot" . $TelegramTokenId . "/getUpdates?offset=" . \ - $TelegramChatOffset->0 . "&allowed_updates=%5B%22message%22%5D") as-value ]->"data"); - :set TelegramRandomDelay [ $MAX 0 ($TelegramRandomDelay - 1) ]; - } on-error={ - :if ($I < 4) do={ - $LogPrint debug $ScriptName ("Fetch failed, " . $I . ". try."); - :set TelegramRandomDelay [ $MIN 15 ($TelegramRandomDelay + 5) ]; - :delay (($I * $I) . "s"); - } - } - } - } - - :if ($Data = false) do={ - $LogPrint warning $ScriptName ("Failed getting updates."); - :set ExitOK true; - :error false; - } - - :local JSON [ :deserialize from=json value=$Data ]; - :local UpdateID 0; - :local Uptime [ /system/resource/get uptime ]; - :foreach Update in=($JSON->"result") do={ - :set UpdateID ($Update->"update_id"); - :local Message ($Update->"message"); - :local IsReply ([ :typeof ($Message->"reply_to_message") ] = "string"); - :local IsMyReply ($TelegramMessageIDs->[ :tostr ($Message->"reply_to_message"->"message_id") ]); - :if (($IsMyReply = 1 || $TelegramChatOffset->0 > 0 || $Uptime > 5m) && $UpdateID >= $TelegramChatOffset->2) do={ - :local Trusted false; - :local Chat ($Message->"chat"); - :local From ($Message->"from"); - :local Command ($Message->"text"); - :local ThreadId [ $IfThenElse ($Message->"is_topic_message") ($Message->"message_thread_id") "" ]; - - :foreach IdsTrusted in=($TelegramChatId, $TelegramChatIdsTrusted) do={ - :if ($From->"id" = $IdsTrusted || $From->"username" = $IdsTrusted) do={ - :set Trusted true; - } - } - - :if ($Trusted = true) do={ - :local Done false; - :if ($Command = "?") do={ - $LogPrint info $ScriptName ("Sending notice for update " . $UpdateID . "."); - $SendTelegram2 ({ origin=$ScriptName; chatid=($Chat->"id"); silent=true; \ - replyto=($Message->"message_id"); threadid=$ThreadId; \ - subject=([ $SymbolForNotification "speech-balloon" ] . "Telegram Chat"); \ - message=([ $IfThenElse ([ :len ($From->"first_name") ] > 0) ("Hello " . ($From->"first_name") . "!\n\n") ] . \ - "Online" . [ $IfThenElse $TelegramChatActive " (and active!)" ] . ", awaiting your commands!") }); - :set Done true; - } - :if ($Done = false && [ :pick $Command 0 1 ] = "!") do={ - :if ($Command ~ ("^! *(" . [ $EscapeForRegEx $Identity ] . "|@" . $TelegramChatGroups . ")\$")) do={ - :set TelegramChatActive true; - } else={ - :set TelegramChatActive false; - } - $LogPrint info $ScriptName ("Now " . [ $IfThenElse $TelegramChatActive "active" "passive" ] . \ - " from update " . $UpdateID . "!"); - :set Done true; - } - :if ($Done = false && ($IsMyReply = 1 || ($IsReply = false && \ - $TelegramChatActive = true)) && [ :len $Command ] > 0) do={ - :if ([ $ValidateSyntax $Command ] = true) do={ - :local State ""; - :local File ("tmpfs/telegram-chat/" . [ $GetRandom20CharAlNum 6 ]); - :if ([ $MkDir "tmpfs/telegram-chat" ] = false) do={ - $LogPrint error $ScriptName ("Failed creating directory!"); - :set ExitOK true; - :error false; - } - $LogPrint info $ScriptName ("Running command from update " . $UpdateID . ": " . $Command); - :execute script=(":do {\n" . $Command . "\n} on-error={ /file/add name=\"" . $File . ".failed\" };" . \ - "/file/add name=\"" . $File . ".done\"") file=($File . "\00"); - :if ([ $WaitForFile ($File . ".done") [ $EitherOr $TelegramChatRunTime 20s ] ] = false) do={ - :set State ([ $SymbolForNotification "warning-sign" ] . "The command did not finish, still running in background.\n\n"); - } - :if ([ :len [ /file/find where name=($File . ".failed") ] ] > 0) do={ - :set State ([ $SymbolForNotification "cross-mark" ] . "The command failed with an error!\n\n"); - } - :local Content ([ /file/read chunk-size=32768 file=$File as-value ]->"data"); - $SendTelegram2 ({ origin=$ScriptName; chatid=($Chat->"id"); silent=true; \ - replyto=($Message->"message_id"); threadid=$ThreadId; \ - subject=([ $SymbolForNotification "speech-balloon" ] . "Telegram Chat"); \ - message=([ $SymbolForNotification "gear" ] . "Command:\n" . $Command . "\n\n" . \ - $State . [ $IfThenElse ([ :len $Content ] > 0) \ - ([ $SymbolForNotification "memo" ] . "Output:\n" . $Content) \ - ([ $SymbolForNotification "memo" ] . "No output.") ]) }); - $RmDir "tmpfs/telegram-chat"; - } else={ - $LogPrint info $ScriptName ("The command from update " . $UpdateID . " failed syntax validation!"); - $SendTelegram2 ({ origin=$ScriptName; chatid=($Chat->"id"); silent=false; \ - replyto=($Message->"message_id"); threadid=$ThreadId; \ - subject=([ $SymbolForNotification "speech-balloon" ] . "Telegram Chat"); \ - message=([ $SymbolForNotification "gear" ] . "Command:\n" . $Command . "\n\n" . \ - [ $SymbolForNotification "cross-mark" ] . "The command failed syntax validation!") }); - } - } - } else={ - :local MessageText ("Received a message from untrusted contact " . \ - [ $IfThenElse ([ :len ($From->"username") ] = 0) "without username" ("'" . $From->"username" . "'") ] . \ - " (ID " . $From->"id" . ") in update " . $UpdateID . "!"); - :if ($Command ~ ("^! *" . [ $EscapeForRegEx $Identity ] . "\$")) do={ - $LogPrint warning $ScriptName $MessageText; - $SendTelegram2 ({ origin=$ScriptName; chatid=($Chat->"id"); silent=false; \ - replyto=($Message->"message_id"); threadid=$ThreadId; \ - subject=([ $SymbolForNotification "speech-balloon" ] . "Telegram Chat"); \ - message=("You are not trusted.") }); - } else={ - $LogPrint info $ScriptName $MessageText; - } - } - } else={ - $LogPrint debug $ScriptName ("Already handled update " . $UpdateID . "."); - } - } - :set TelegramChatOffset ([ :pick $TelegramChatOffset 1 3 ], \ - [ $IfThenElse ($UpdateID >= $TelegramChatOffset->2) ($UpdateID + 1) ($TelegramChatOffset->2) ]); -} on-error={ - :global ExitError; $ExitError $ExitOK [ :jobname ]; -} diff --git a/template.md b/template.md new file mode 100644 index 0000000..8a9aeb7 --- /dev/null +++ b/template.md @@ -0,0 +1,47 @@ +Script `template` +================= + +[◀ Go back to main README](README.md) + +Description +----------- + +Short description... + +In detail +--------- + +Get all the details... + +Requirements and installation +----------------------------- + +We need... + +... then we install: + + [admin@MikroTik] > / system script add name=template + [admin@MikroTik] > / system script run script-updates + +Configuration +------------- + +The configuration goes to `global-config`, These are the parameters: + +* ... + +Usage and invocation +-------------------- + +This is intended... + +See also +-------- + +* [another script](template.md) +* ... + +--- +[◀ Go back to main README](README.md) + +[▲ Go back to top](#top) diff --git a/unattended-lte-firmware-upgrade b/unattended-lte-firmware-upgrade new file mode 100644 index 0000000..73115c9 --- /dev/null +++ b/unattended-lte-firmware-upgrade @@ -0,0 +1,27 @@ +#!rsc +# RouterOS script: unattended-lte-firmware-upgrade +# Copyright (c) 2018-2019 Christian Hesse +# +# schedule unattended lte firmware upgrade + +:foreach Interface in=[ / interface lte find ] do={ + :local Firmware; + :local IntName [ / interface lte get $Interface name ]; + :do { + :set Firmware [ / interface lte firmware-upgrade $Interface once as-value ]; + } on-error={ + :log debug ("Could not get latest LTE firmware version for interface " . $IntName . "."); + } + + :if (($Firmware->"installed") != ($Firmware->"latest")) do={ + :log info ("Scheduling LTE firmware upgrade for interface " . $IntName . "."); + / system script add name=($IntName . "-firmware-upgrade") source=("# unattended-lte-firmware-upgrade\n" . \ + "/ system scheduler remove " . $IntName . "-firmware-upgrade;\n" . \ + "/ system script remove " . $IntName . "-firmware-upgrade;\n" . \ + "/ interface lte firmware-upgrade " . $IntName . " upgrade=yes;\n" . \ + ":log info (\"LTE firmware upgrade finished, waiting for installation before reset.\");\n" . \ + ":delay 150s;\n" . \ + "/ interface lte at-chat " . $IntName . " input=\"AT+RESET\";"); + / system scheduler add name=($IntName . "-firmware-upgrade") on-event=($IntName . "-firmware-upgrade") interval=1m; + } +} diff --git a/unattended-lte-firmware-upgrade.rsc b/unattended-lte-firmware-upgrade.rsc deleted file mode 100644 index 83925fd..0000000 --- a/unattended-lte-firmware-upgrade.rsc +++ /dev/null @@ -1,52 +0,0 @@ -#!rsc by RouterOS -# RouterOS script: unattended-lte-firmware-upgrade -# Copyright (c) 2018-2025 Christian Hesse -# https://rsc.eworm.de/COPYING.md -# -# requires RouterOS, version=7.15 -# requires device-mode, scheduler -# -# schedule unattended lte firmware upgrade -# https://rsc.eworm.de/doc/unattended-lte-firmware-upgrade.md - -:foreach Interface in=[ /interface/lte/find where running ] do={ - :local Firmware; - :local IntName [ /interface/lte/get $Interface name ]; - :do { - :set Firmware [ /interface/lte/firmware-upgrade $Interface as-value ]; - } on-error={ - :log debug ("Could not get latest LTE firmware version for interface " . $IntName . "."); - } - - :if ([ :typeof $Firmware ] = "array") do={ - :if (($Firmware->"installed") != ($Firmware->"latest")) do={ - :log info ("Scheduling LTE firmware upgrade for interface " . $IntName . "."); - - :global LTEFirmwareUpgrade do={ - :global LTEFirmwareUpgrade; - :set LTEFirmwareUpgrade; - - /system/scheduler/remove ($1 . "-firmware-upgrade"); - :do { - /interface/lte/firmware-upgrade $1 upgrade=yes; - :log info ("LTE firmware upgrade on '" . $1 . "' finished, waiting for reset."); - :delay 240s; - :local Firmware [ /interface/lte/firmware-upgrade $1 as-value ]; - :if ([ :len ($Firmware->"latest") ] > 0 && \ - ($Firmware->"installed") != ($Firmware->"latest")) do={ - :log warning ("LTE firmware versions still differ. Upgrade failed anyway?"); - } - } on-error={ - :log error ("LTE firmware upgrade on '" . $1 . "' failed."); - } - } - - /system/scheduler/add name=($IntName . "-firmware-upgrade") start-time=startup interval=2s \ - on-event=(":global LTEFirmwareUpgrade; \$LTEFirmwareUpgrade \"" . $IntName . "\";"); - } else={ - :log info ("The LTE firmware is up to date on interface " . $IntName . "."); - } - } else={ - :log info ("No LTE firmware information available for interface " . $IntName . "."); - } -} diff --git a/update-gre-address b/update-gre-address new file mode 100644 index 0000000..2964d46 --- /dev/null +++ b/update-gre-address @@ -0,0 +1,24 @@ +#!rsc +# RouterOS script: update-gre-address +# Copyright (c) 2013-2019 Christian Hesse +# +# update gre interface remote address with dynamic address from +# ipsec remote peer + +/ interface gre set remote-address=0.0.0.0 disabled=yes [ find where !running !disabled ]; + +:foreach Peer in=[ / ip ipsec remote-peers find ] do={ + :local Id [ / ip ipsec remote-peers get $Peer id ]; + :local GreInt [ / interface gre find where comment=$Id ]; + :if ([ :len $GreInt ] > 0) do={ + :local GreName [ / interface gre get $GreInt name ]; + :local AddrOld [ / interface gre get $GreInt remote-address ]; + :local Disabled [ / interface gre get $GreInt disabled ]; + :local AddrNew [ / ip ipsec remote-peers get $Peer dynamic-address ]; + :if ($AddrNew != $AddrOld || $Disabled = true) do={ + :log info ("Update remote address for interface " . $GreName . " to " . $AddrNew); + / interface gre set remote-address=0.0.0.0 disabled=yes [ find where remote-address=$AddrNew name!=$GreName ]; + / interface gre set $GreInt remote-address=$AddrNew disabled=no; + } + } +} diff --git a/update-gre-address.rsc b/update-gre-address.rsc deleted file mode 100644 index cddfa92..0000000 --- a/update-gre-address.rsc +++ /dev/null @@ -1,46 +0,0 @@ -#!rsc by RouterOS -# RouterOS script: update-gre-address -# Copyright (c) 2013-2025 Christian Hesse -# https://rsc.eworm.de/COPYING.md -# -# requires RouterOS, version=7.15 -# -# update gre interface remote address with dynamic address from -# ipsec remote peer -# https://rsc.eworm.de/doc/update-gre-address.md - -:global GlobalFunctionsReady; -:while ($GlobalFunctionsReady != true) do={ :delay 500ms; } - -:local ExitOK false; -:do { - :local ScriptName [ :jobname ]; - - :global CharacterReplace; - :global LogPrint; - :global ScriptLock; - - :if ([ $ScriptLock $ScriptName ] = false) do={ - :set ExitOK true; - :error false; - } - - /interface/gre/set remote-address=0.0.0.0 disabled=yes [ find where !running !disabled ]; - - :foreach Peer in=[ /ip/ipsec/active-peers/find ] do={ - :local PeerVal [ /ip/ipsec/active-peers/get $Peer ]; - :local GreInt [ /interface/gre/find where comment=($PeerVal->"id") or comment=[ $CharacterReplace ($PeerVal->"id") "CN=" "" ] ]; - :if ([ :len $GreInt ] > 0) do={ - :local GreIntVal [ /interface/gre/get $GreInt ]; - :if ([ :typeof ($PeerVal->"dynamic-address") ] = "str" && \ - ($PeerVal->"dynamic-address" != $GreIntVal->"remote-address" || \ - $GreIntVal->"disabled" = true)) do={ - $LogPrint info $ScriptName ("Updating remote address for interface " . $GreIntVal->"name" . " to " . $PeerVal->"dynamic-address"); - /interface/gre/set remote-address=0.0.0.0 disabled=yes [ find where remote-address=$PeerVal->"dynamic-address" name!=$GreIntVal->"name" ]; - /interface/gre/set $GreInt remote-address=($PeerVal->"dynamic-address") disabled=no; - } - } - } -} on-error={ - :global ExitError; $ExitError $ExitOK [ :jobname ]; -} diff --git a/update-tunnelbroker b/update-tunnelbroker new file mode 100644 index 0000000..0460028 --- /dev/null +++ b/update-tunnelbroker @@ -0,0 +1,38 @@ +#!rsc +# RouterOS script: update-tunnelbroker +# Copyright (c) 2013-2019 Christian Hesse +# Michael Gisbers + +:global CertificateAvailable; + +:if ([ / ip cloud get ddns-enabled ] != true) do={ + :error "IP cloud DDNS is not enabled."; +} + +# Get the current ip address from cloud +/ ip cloud force-update; +:while ([ / ip cloud get status ] != "updated") do={ + :delay 1s; +} +:local PublicAddress [ / ip cloud get public-address ]; + +:foreach Interface in=[ / interface 6to4 find where comment~"^tunnelbroker" !disabled ] do={ + :local IntName [ / interface 6to4 get $Interface name ]; + :local LastAddress [ / interface 6to4 get $Interface local-address ]; + + :if ($PublicAddress != $LastAddress) do={ + :local Comment [ :toarray [ / interface 6to4 get $Interface comment ] ]; + :local User [ :pick ($Comment->1) 5 99 ]; + :local Pass [ :pick ($Comment->2) 5 99 ]; + :local Id [ :pick ($Comment->3) 3 99 ]; + + $CertificateAvailable "Starfield Secure Certificate Authority - G2" "starfield"; + :log info ("Local address changed, sending UPDATE to tunnelbroker! New address: " . $PublicAddress); + / tool fetch mode=https check-certificate=yes-without-crl \ + ("https://ipv4.tunnelbroker.net/nic/update\?hostname=" . $Id) \ + user=$User password=$Pass keep-result=no; + / interface 6to4 set $Interface local-address=$PublicAddress; + } else={ + :log debug ("All tunnelbroker configuration is up to date for interface " . $IntName . "."); + } +} diff --git a/update-tunnelbroker.rsc b/update-tunnelbroker.rsc deleted file mode 100644 index 45afa6f..0000000 --- a/update-tunnelbroker.rsc +++ /dev/null @@ -1,74 +0,0 @@ -#!rsc by RouterOS -# RouterOS script: update-tunnelbroker -# Copyright (c) 2013-2025 Christian Hesse -# Michael Gisbers -# https://rsc.eworm.de/COPYING.md -# -# provides: ppp-on-up -# requires RouterOS, version=7.15 -# requires device-mode, fetch -# -# update local address of tunnelbroker interface -# https://rsc.eworm.de/doc/update-tunnelbroker.md - -:global GlobalFunctionsReady; -:while ($GlobalFunctionsReady != true) do={ :delay 500ms; } - -:local ExitOK false; -:do { - :local ScriptName [ :jobname ]; - - :global CertificateAvailable; - :global LogPrint; - :global ParseKeyValueStore; - :global ScriptLock; - - :if ([ $ScriptLock $ScriptName ] = false) do={ - :set ExitOK true; - :error false; - } - - :if ([ $CertificateAvailable "Starfield Root Certificate Authority - G2" ] = false) do={ - $LogPrint error $ScriptName ("Downloading required certificate failed."); - :set ExitOK true; - :error false; - } - - :foreach Interface in=[ /interface/6to4/find where comment~"^tunnelbroker" !disabled ] do={ - :local Data false; - :local InterfaceVal [ /interface/6to4/get $Interface ]; - :local Comment [ $ParseKeyValueStore ($InterfaceVal->"comment") ]; - - :for I from=2 to=0 do={ - :if ($Data = false) do={ - :do { - :set Data ([ /tool/fetch check-certificate=yes-without-crl \ - ("https://ipv4.tunnelbroker.net/nic/update?hostname=" . $Comment->"id") \ - user=($Comment->"user") password=($Comment->"pass") output=user as-value ]->"data"); - } on-error={ - $LogPrint debug $ScriptName ("Failed downloading, " . $I . " retries pending."); - :delay 2s; - } - } - } - - :if (!($Data ~ "^(good|nochg) ")) do={ - $LogPrint error $ScriptName ("Failed sending the local address to tunnelbroker or unexpected response!"); - :set ExitOK true; - :error false; - } - - :local PublicAddress [ :pick $Data ([ :find $Data " " ] + 1) [ :find $Data "\n" ] ]; - - :if ($PublicAddress != $InterfaceVal->"local-address") do={ - :if ([ :len [ /ip/address find where address~("^" . $PublicAddress . "/") ] ] < 1) do={ - $LogPrint warning $ScriptName ("The address " . $PublicAddress . " is not configured on your device. NAT by ISP?"); - } - - $LogPrint info $ScriptName ("Local address changed, updating tunnel configuration with address: " . $PublicAddress); - /interface/6to4/set $Interface local-address=$PublicAddress; - } - } -} on-error={ - :global ExitError; $ExitError $ExitOK [ :jobname ]; -}
You want the logo for your own notifications? But you joined the -Telegram Group and want -something that differentiates? Color it!
Select the colors here: - - -
Then right-click, click "Take Screenshot" and finally select the -logo and download it.
- -
(This example is with -Firefox. The workflow -for other browsers may differ.)
See how to -Set -a profile photo for your Telegram bot.
[MikroTik] ℹ️ Subject
Message
🔗 https://eworm.de/
⏰ This message was queued since oct/18/2022 18:30:48 and may be obsolete.
✂️ The message was too long and has been truncated, cut off 13%!
Hostname:
Subject:
Message: Message
Show link:
Queued since
Cut-off with percent
Then right-click, click "Take Screenshot" and finally select the -notification and download it.
" . \ - [ $PrepareText ($Notification->"message") ] . "