Script to auto create address-list from plain ip-ranges URLs

Compatible with both IPv4, IPv6 and survives reboots. A 1d interval scheduler is enough.

# Function to update an address list from multiple URLs
:local updateAddressList do={
  :do {
    # Clear existing entries if not updated
    /ip firewall address-list set [find list=$listName dynamic=no] timeout=$timeout dynamic=yes
    /ipv6 firewall address-list set [find list=$listName dynamic=no] timeout=$timeout dynamic=yes

    # Loop through each URL
    :foreach url in=$urls do={
      # Fetch data from the current URL
      :local data ([:tool fetch url=$url output=user as-value]->"data")

      :local listEnd false

      # Process each line in the fetched data
      :while ([:len $data] != 0 && !$listEnd) do={
        :local endPos [:find $data "\n"]

        # Determine if this is the last line
        :if ($endPos < 0) do={
          :set listEnd true
          :set endPos [:len $data]
        }

        # Extract the IP address from the current line
        :local ip [:pick $data 0 $endPos]

        # Go to IPv4 list
        /ip firewall address-list

        # Validate that the line contains an IPv4 address
        :if ($ip ~ "^[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}") do={
          :do {
            # Try to add the IP to the address list
            add list=$listName address=$ip comment=$description timeout=0 dynamic=no
          } on-error={
            # Update the existing entry if it already exists
            set [find list=$listName address=$ip] comment=$description timeout=0 dynamic=no
          }
        }

        # Go to IPv6 list
        /ipv6 firewall address-list

        # Validate that the line contains an IPv6 address
        :if ($ip~"^([a-f0-9:]+:+)+[a-f0-9]+") do={
          :do {
            # Try to add the IP to the address list
            add list=$listName address=$ip comment=$description timeout=0 dynamic=no
          } on-error={
            # Update the existing entry if it already exists
            set [find list=$listName address=$ip] comment=$description timeout=0 dynamic=no
          }
        }

        # Remove the processed line from the data
        :set data [:pick $data ($endPos + 1) [:len $data]]
      }
    }
  } on-error={
    :log warning "Failed to update address list <$listName> from <$url>"
  }
}

# Call the function to update the Cloudflare address lists
:local urls {"https://www.cloudflare.com/ips-v4"; "https://www.cloudflare.com/ips-v6"}
$updateAddressList urls=$urls listName="CLOUDFLARE" timeout=1m description="Cloudflare IPs"

https://gist.github.com/miguelangel-nubla/137fea6653a3d612ca66cd7f8ea7edb6

This is based on someone else script I used for long time, can’t find source. Please let me know to give credit.

More than one person have written on this script for several years now.

http://forum.mikrotik.com/t/address-lists-downloader-dshield-spamhaus-drop-edrop-etc/133640/288

Very useful, thanks.

:if ($ip ~ "^[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}") do={
          :do {
            # Try to add the IP to the address list
            add list=$listName address=$ip comment=$description timeout=0 dynamic=no
          } on-error={
            # Update the existing entry if it already exists
            set [find list=$listName address=$ip] comment=$description timeout=0 dynamic=no

Wrong code, if add fail for invalid IP, set can’t find anything to “set”…
RegEx is wrong. 666.777.888.999 is not a valid IP…
There is nothing that prevent something wrong to happen, like add 0.0.0.0/0, or 8.0.0.0/8 to the address list…

Correct RegEx:
http://forum.mikrotik.com/t/address-lists-downloader-dshield-spamhaus-drop-edrop-etc/133640/88

Obviously not all address lists can be imported, as usual. Read @msatter post #2

@rextended
You are missing the point, in order for this to be of any use you must trust the source.

Imposible to replicate all the checks RouterOS is going to do to the input, so I stop at zero, the next best thing is to fail gracefully, which it does. If the input is not garbage it will work reliably, that’s a win on my book.
Sure, that can be expanded ad infinitum, but why?

I am always open to improvements but not so much wrong code with no upside.

Regarding 0.0.0.0/0, or 8.0.0.0/8 being wrong on an address list, did I miss the RFC?

I think we can say that the script is intended for pre-validated address lists, i.e. all the checks needs to be done before and outside this script, that merely “imports” the (already filtered/corrected/whatever) data.
It seems fair enough to me.

No, sorry, but you missed the point: Anyone can make mistakes (and, also, the list provider can change the format without warning).


This question speaks more than a hundred answers…


Simple example.
If by some error on the list go 0.0.0.0/0:

  1. If the list is permissive, it will allow anything;
  2. If the list is blocking, it will block anything;
  3. If the list is manipulative, it will manipulate anything.

Again:

It will fail gracefully anyway, no upside. Do I also need to worry about background radiation, bit flips and handle it in the script?

Thanks for the confirmation you missed the point. My bad for not including a :wink: at the end.

Miguel,

The CLOUDFLARE list is locked in RAW?

/ip firewall raw
add action=drop chain=prerouting src-address-list=CLOUDFLARE

thx.

It’s simply a generic list of addresses, you can use it anywhere depending on what you want to achieve.

You can get them from a variety of sources:

  • Internet companies advertise their own IPs in so called ip-ranges files so you can know if a certain connection comes from them.
    Ex: Cloudflare, Google, and most large internet companies.
    If you depend on connections coming from them you might want to drop everything incoming but the address list (allow list).
    For example when using Cloudflare in front of your site you tipically want every incoming connection to pass through Cloudflare first, so you block everything not coming from them.
/ip firewall nat
add action=dst-nat chain=dstnat dst-port=443 src-address-list=CLOUDFLARE to-addresses=192.168.0.123
/ip firewall filter
add action=drop chain=forward comment="defconf: drop all from WAN not DSTNATed" connection-nat-state=!dstnat connection-state=new in-interface-list=WAN

But that is only useful in that particular use case when hosting a website, and keep in mind cloudflare will only protect you in certain significant situations but will happily pass through everything else such as xss, etc

  • Third parties create custom address lists as compilations of known threat actors, such as those listed in /viewtopic.php?p=1077165; Spamhaus, Abuse.ch, etc:
    You might want to use that list as a blocklist as a general measure, although it is fairly easy to circumvent. They can definitely still reach you using another IP, but you wont be bothered as often.

In the example I use it with Cloudflare but I see you are trying to use it as blocklist.
I don’t think allow all and specifically blocking Cloudflare is what you want to do.

Keep in mind you trust that list and how you got it for whatever you do want to achieve.
Getting it from Cloudflare itself is not the same as getting it from “Random IT experts”, and even then, getting it over HTTPS is not the same as reading it with HTTP, etc.

Oops, sorry, I actually meant it as a serious question…
Now I understand…