Архив

MikroTik автоматическое обновление листов

В прошлой статье мы разобрали как настроить маршрутизацию трафика через VPN, на основе Address Lists. В этой статье хочется собрать информацию и способы для автоматического обновления и создания листов. 

Обновление листов представляет из себя выполнение shell команд, которые исполняются на Router OS. Их можно предварительно сгенерировать и выполнить на маршрутизаторе, либо выполнять сразу, в runtime, без создания отдельного файла.

Шаблон файла обновления

/ip firewall address-list
remove numbers=[find list=spotify]
add list=spotify address=104.154.127.47
add list=spotify address=78.31.8.0/21

В моем случае обновление листа подразумевает удаление всех правил и добавление новых. Необходимости делать точечные изменения у меня не возникало.

В листе выше, мы удаляем все адреса которые присутствуют в листе spotify - remove numbers=[find list=spotify].  Затем идут команды добавления новых адресов add list=spotify address=78.31.8.0/21 их можно добавлять сколь угодно много, но большие обработка больших листов будет отнимать ресурсы роутера, поэтому заливать туда полный список от РКН не рекомендую.

Обновление из сгенерированного файла

Допустим у нас есть сформированный по шаблону файл, который мы генерируем в интернете: на гитхабе или на личном сервере. Для импорта нужно создать скрипт в RouterOS который скачает и выполнит и наш файл.

Для добавления через консоль:

/system script
add dont-require-permissions=no name=sp_import owner=owner policy=\
    read,write,test source="%тут листинг скрипта%"

Мы добавили скрипт, с минимальными правами read,write,test, но набор этих прав позволит читать и записывать настройки на роутере, кроме тех которые отвечают за пользователей, поэтому нужно быть уверенным кто контролирует скрипт который будет исполнен на роутере. Подробнее о правах доступа

Сам скрипт:

/tool fetch url="https://127.0.0.1/mikrotik_spotify_list.txt" dst-path=spotify.txt
:import spotify.txt

В нем мы получаем с удаленного сервера сформированный листинг (см. Шаблон файла обновления), сохраняем его в файл spotify.txt и выполняем этот файл.

Почти готово, осталось автоматизировать.

/system scheduler
add interval=2h name=update_spotify_list on-event="/system script run sp_import" \
policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \
start-date=jan/01/2019 start-time=09:00:00

Мы добавили в планировщик задание name=update_spotify_list на выполнение каждые два часа interval=2h. С policy тут несколько сложнее, если из планировщика выполняется другой скрипт, то нужно предоставить все права. Теоретически можно было весь листинг скрипта положить в планировщик и ограничить права, но я предпочитаю когда скрипты лежат в отдельных файлах.

Итак. Мы создали скрипт, который обновляет список адресов в файрволе. Создали скрипт который скачивает этот список и добавили его в планировщик. 

Обновление листа без промежуточных файлов

В вышеописанном решение все хорошо, за исключением того, что требуется где-то хостить наш список и формировать его. Как сделать тоже самое на роутере?

:local ListName "spotify"
:local ListIps {"104.154.127.47";"78.31.8.0/21"}

/ip firewall address-list remove numbers=[find list=$ListName]

:foreach v in $ListIps do={
  /ip firewall address-list add address=$v list=$ListName
}

Делает все тоже самое. Документация по MikroTik Scripting.

Обновление листа через определение ip доменов

Пока искал ссылку на документацию по MikroTik Scripting, наткнулся на статью Писать скрипты для Mikrotik RouterOS — это просто

В ней приведен листинг скрипта, который может делать определение ip адресов доменов и формировать список из них. Весьма удобно, так как иногда бывает что сайты часто меняют свои адреса в попытках борьбы с блокировками.

Для истории приведу листинг скрипта ниже, а его полный разбор с комментариями доступен в оригинальной статье.

Листинг приведенный ниже оставлен для истории. Актуальный находится ниже.

:local DNSList {"example.com";"non-exist.domain.net";"server.local";"hostname"}
:local ListName "MyList"
:local DNSServers ( [ip dns get dynamic-servers], [ip dns get servers ], 8.8.8.8 )
:foreach addr in $DNSList do={
     :foreach DNSServer in $DNSServers do={
          :do {:resolve server=$DNSServer $addr} on-error={:log debug ("failed to resolve $addr on $DNSServer")}
     }
}
/ip firewall address-list remove [find where list~$ListName]
/ip dns cache all
:foreach i in=[find type="A"] do={
    :local bNew true
    :local cacheName [get $i name]
    :local match false
    :foreach addr in=$DNSList do={
       :if (:typeof [:find $cacheName $addr] >= 0) do={
           :set $match true
       }
    }
    :if ( $match ) do={
        :local tmpAddress [/ip dns cache get $i address]
        :if ( [/ip firewall address-list find ] = "") do={
            :log debug ("added entry: $[/ip dns cache get $i name] IP $tmpAddress")
            /ip firewall address-list add address=$tmpAddress list=$ListName comment=$cacheName
        } else={
            :foreach j in=[/ip firewall address-list find ] do={
                :if ( [/ip firewall address-list get $j address] = $tmpAddress ) do={
                    :set bNew false
                }
            }
            :if ( $bNew ) do={
                :log debug ("added entry: $[/ip dns cache get $i name] IP $tmpAddress")
                /ip firewall address-list add address=$tmpAddress list=$ListName comment=$cacheName
            }
        }
    }
}

Update 03.08.2020

Скрипт приведенный выше, в какой-то момент перестал работать. Я не обратил внимание когда это произошло, но, кажется, после того как я перешел на dns-over-https.

Во время дебага и в попытках локализовать проблему, я переписал скрипт.

:local hostnames {"domain1.ru";"domain2.com";"domain3.com"}
:local listname "vpn"
:local ips

/ip firewall address-list remove [find where list=$listname]

:foreach hostname in $hostnames do={
    :do {:set ips [:resolve $hostname]} on-error={:log debug ("failed to resolve $hostname")}

    /ip firewall address-list add address=$ips list=$listname comment=$hostname
}

Получилось сильно сократить код сохранив всю функциональность:

  • В списке hostnames указываем какие домены нужно резолвить
  • В переменную listname записываем название для нашего списка, главное чтобы оно совпадало с правилами маршрутизации
  • Обратите внимание что уровень ошибок обозначен как debug, это значит что он не будет сообщать обо всех ошибках в общий лог. Если необходимо, то нужно заменить его, например, на info. Смотрите документацию.

Другие статьи в блоге:

⟵ Настройка маршрутизации трафика через VPN в RouterOS
Перенос mac приложений в brew ⟶