BGP.guru

BGP.guru

Nerd blog.

29 Apr 2024

Syncing RouterOS address-lists

Introduction

One of the complexities that comes up when you manage a large number of routers is keeping their configs all in sync. Specifically any commonly access-lists that are used. I wanted to solve this using the Mikrotik API, but that has changed formats between v6 and v7 so its a bit more complex to cover off both versions with one codebase at this time. However by pulling instead of pushing, I was able to massively simplify the process.

Router Config

I got the idea from a “self updating” firewall script I saw. It would generate its own mikrotik config lines.

A script such as follows would download and execute the updated ACLs:

:do {
    :put "Installing acls from web";
    :local aclserver "https://example.org/sample-v4";
    :local scriptName "acl4.rsc";
    :put "Downloading update script...";
    :do {
        /tool fetch url="$aclserver" mode=https port=443 dst-path="/$scriptName";
    } on-error={
        :put "Error. Download failed";
    }
   :put "Importing update script...";
    :do {
        /import "$scriptName";
    } on-error={
        :put "import failed. unknown error.";
    }
    :put "Removing update script...";
    :do {
        /file remove "$scriptName";
    } on-error={}
    :put "Update Complete.";
}

The contents of the script contain a number of objects in specifically named lists, as well as comments. When the script is run it adds/updates the “Validated ….” comment, allowing you to sort by comment and essentially erase anything not updated last run if you so desire.

/ip firewall address-list
:if ([:len [/ip firewall address-list find list="Any" and address=0.0.0.0/0]] = 0) do={add list="Any" address=0.0.0.0/0 comment="Validated 2024-04-29T22:59:14-05:00 - Any";} else={set [find list="Any" and address=0.0.0.0/0 ] comment="Validated 2024-04-29T22:59:14-05:00 - Any";}

Webserver

This requires a webserver, with a dynamic scripting language like PHP or python installed, as well as a database to store ACL contents in.

The webserver then queries the backend, and outputs an if like like above for each of the ACL lines, and outputs these to the client/router making the request. The client/router as part of the above script then executes the contents of the address-list to update its own address-list entries.

Observations

I’ve been using this method for over a year now. It works great, as long as every firewall references the same named objects, and has the same functionality. Adding new functionality still requires going into each router, but the number of times I’ve needed to do that since adopting this address-list syncing method has been drastically reduced.

It was pointeed out to me on LinkedIn when I posted this by Alptekin Sünnetci that /fetch has a src-address parameter, which honestly I had totally missed in all my years of Mikrotik usage. I’ve been solving this by using /32 routes with preferred source set to loopback!