В прошлой статье мы разобрали как настроить маршрутизацию трафика через 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
. Смотрите документацию.