Статья является продолжением первой, второй и третьей частей, посвящённых организации практической безопасности сетей, построенных на оборудовании MikroTik. Ранее были рассмотрены общие рекомендации, безопасность уровней L1, L2 и L3, реализация централизованного логирования. Настало время поговорить про развёртывание IDS и её интеграцию в инфраструктуру RouterOS.
11. Настройка IDS
В качестве системы обнаружения вторжений выберем решение с открытым программным кодом Suricata. Оно умеет работать со следующими прикладными протоколами: http, ftp, smtp, tls, ssh, imap, msn, smb, dce\rpc, dns, nfs, ntp, tftp, ikev, krb5 и dhcp. Организуем режим работы promiscuous, при котором маршрутизатор будет посылать копии пакетов на IDS и фундаментально не влиять на их прохождение. На RouterOS это можно сделать двумя способами.
Посредством зеркалирования информации, передающейся через конкретный порт роутера (для этого необходимо проверить на сайте производителя, умеет ли он так делать). Тип имеющейся микросхемы можно посмотреть так:
/interface ethernet switch print
Columns: NAME, TYPE, L3-HW-OFFLOADING
# NAME TYPE L3
0 switch1 QCA-8337 no
Этот вариант подойдёт, если вы развернёте Suricata внутри вашего LAN на железе, так как должно быть физическое соединение между зеркалируемым портом и его коллектором, тогда:
/interface ethernet switch set switch1 mirror-source=ether2 mirror-target=ether3
Сохранять передающийся трафик можно с помощью Tcpdump. Если такой вариант вас не устраивает, тогда рекомендую использовать арендованный VDS сервер более мощной конфигурации:
Для зеркалирования трафика можно использовать встроенное в RouterOS программное обеспечение Packet-sniffer:
/tool sniffer set filter-interface=WAN filter-stream=yes memory-scroll=no streaming-enabled=yes streaming-server=192.168.15.29
Довольно грубое решение. Пакеты пересылать, разумеется, нужно по шифрованному VPN туннелю, который может быть достаточно узким, и тогда часть пакетов будет теряться. Для зеркалирования MikroTik применяет протокол TZSP, трафик будет обрамляться служебными заголовками и передаваться UDP пакетами, и тут уже без потерь никак. Для более точной настройки можно вместо Packet-sniffer использовать Firewall mangle (в примере отбираются пакеты для Winbox службы и SSH):
/ip firewall mangle
add action=mark-connection chain=prerouting comment="Managment connections" \
dst-port=22,8291 new-connection-mark=\
"Managment connections" passthrough=yes protocol=tcp
add action=mark-packet chain=output comment="Managment packets" \
connection-mark="Managment connections" new-packet-mark=\
"Managment packets" passthrough=yes
add action=sniff-tzsp chain=postrouting comment="Sniffer Managments" \
packet-mark=" Managment packets" sniff-target=192.168.15.29 sniff-target-port=37008
Firewall mangle подробно изучается на курсе MTCTCE (MikroTik Certified Traffic Control Engineer). Скажем только, что гибкость при его настройке достаточно высокая, и умелыми руками можно здорово дифференцировать пакеты. Сохранять передающийся трафик, конечно, можно с помощью Tcpdump, однако наличие служебных заголовков протокола TZSP доставляет хлопот:
Для обратного преобразования зеркалируемого трафика к первоначальному виду можно использовать программы: Trafr (настройка рассмотрена здесь) или Tzsp2pcap:
apt install git libpcap-dev screen tcpreplay
git clone https://github.com/thefloweringash/tzsp2pcap.git
make
make install
После этого создаем виртуальный интерфейс, поднимаем для него MTU, чтобы пакеты не обрезались (IP адрес 192.168.3.254 выбран случайно):
ip link add dummy0 type dummy
ip link set dummy0 mtu 3000
ip link set name eth10 dev dummy0
ip addr add 192.168.3.254/24 dev eth10
ip link set eth10 up
В режиме реального времени сервер будет принимать зеркалируемый трафик, снимать с него служебные заголовки протокола TZSP и ретранслировать его на виртуальный интерфейс eth10:
tzsp2pcap -f | tcpreplay-edit --mtu=3000 --mtu-trunc -i eth10 -
Трансмиссию привели в порядок, приступим к настройке IDS. С параметрами по умолчанию все будет хорошо работать (правила хранятся в /etc/suricata/rules/), не забудем только уточнить домашнюю сеть (192.168.15.9 – IP адрес роутера в VPN):
apt install suricata
vi /etc/suricata/suricata.yaml
HOME_NET: "[192.168.1.0/24,192.168.15.9/32]"
af-packet:
- interface: eth10
Лог всего трафика, проходящего через Suricata, можно посмотреть так:
tail -f /var/log/suricata/eve.json
{"timestamp":"2021-08-12T07:17:28.408914+0600","flow_id":1497450928422226,"in_iface":"eth10","event_type":"dns","src_ip":"10.0.5.174","src_port":50460,"dest_ip":"1.1.1.1","dest_port":53,"proto":"UDP","dns":{"type":"query","id":39632,"rrname":"dr.habracdn.net","rrtype":"A","tx_id":0}}
{"timestamp":"2021-08-12T07:17:28.548078+0600","flow_id":1410381203911063,"in_iface":"eth10","event_type":"tls","src_ip":"10.0.5.174","src_port":22443,"dest_ip":"178.248.237.68","dest_port":443,"proto":"TCP","tls":{"sni":"habr.com","version":"TLS 1.3","ja3":{}}}
Лог детектированных угроз лежит здесь /var/log/suricata/fast.log. Настало время испытаний.
12. Из IDS в IPS
IDS расшифровывается как Intrusion Detection System – система обнаружения вторжений. IPS (Intrusion Prevention System) – система предотвращения вторжений. На текущий момент наша Suricata анализирует зеркалируемый на нее трафик и, в случае обнаружения угроз, сохраняет их описание в лог. Посмотрим, справляется ли она со своими задачами. Попробуем развернуть bruteforce атаку на SSH службу нашего роутера:
hydra 192.168.15.9 ssh -l admin -P rockyou.txt -s 22 -vv
tail -f /var/log/suricata/fast.log -F
08/16/2021-06:57:38.341563 [**] [1:2001219:20] ET SCAN Potential SSH Scan [**] [Classification: Attempted Information Leak] [Priority: 2] {TCP} 192.168.15.1:37126 -> 192.168.15.9:22
Как мы видим, угроза была детектирована: «Potential SSH Scan». Попробуем выполнить nmap сканирование роутера:
nmap 192.168.15.9
PORT STATE SERVICE
22/tcp open ssh
53/tcp open domain
80/tcp open http
2000/tcp open cisco-sccp
8291/tcp open unknown
Suricata и в этом видит угрозы:
08/17/2021-07:08:38.242647 [**] [1:2010937:3] ET SCAN Suspicious inbound to mySQL port 3306 [**] [Classification: Potentially Bad Traffic] [Priority: 2] {TCP} 192.168.15.1:38863 -> 192.168.15.9:3306
08/17/2021-07:08:38.799069 [**] [1:2010936:3] ET SCAN Suspicious inbound to Oracle SQL port 1521 [**] [Classification: Potentially Bad Traffic] [Priority: 2] {TCP} 192.168.15.1:38863 -> 192.168.15.9:1521
08/17/2021-07:08:39.617393 [**] [1:2010939:3] ET SCAN Suspicious inbound to PostgreSQL port 5432 [**] [Classification: Potentially Bad Traffic] [Priority: 2] {TCP} 192.168.15.1:38863 -> 192.168.15.9:5432
08/17/2021-07:08:39.761901 [**] [1:2002911:6] ET SCAN Potential VNC Scan 5900-5920
Теперь наша задача обрабатывать получаемый лог, выделять угрозообразующие IP адреса и блокировать их на MikroTik-е. Для этого уже существует готовое решение: PHP скрипт fast2mikrotik по API добавляет их в /ip firewall address-list. Однако у нас он в итоге так и не заработал, поэтому далее мы представим вариант собственной реализации. Здесь немного отвлечемся от темы. Управлять роутером посредством API это очень заманчиво, так как уже имеется готовый PHP класс.
Активируем API в RouterOS:
/ip service set api disabled=no
И кидаем на маршрутизатор команды, примерно так:
<?php
require('routeros_api.class.php');
header('Content-Type: text/plain');
$API = new RouterosAPI();
try {
$API->connect('192.168.15.9', 'admin', 'verySTRONGpassword!!');
} catch (Exception $e) {
die('Unable to connect to RouterOS. Error:' . $e);
}
$ARRAY = $API->comm("/tool/sniffer/stop");
$API->disconnect();
?>
Можно, конечно, работать через API по SSL, подготовив заранее сертификаты, чтобы все было безопасно, но мы решили выполнить интеграцию по-другому и пошли своим путем, может, не самым эффективным, но удобным, так как отсутствует необходимость запуска и настройки дополнительных сервисов, таких как база данных, например. Команды будем передавать примерно так, как показано ниже:
sshpass -p verySTRONGpassword!! ssh admin@192.168.15.9 /tool sniffer stop
Полный Bash скрипт под спойлером.
#/bin/sh
#Сюда вбиваем IP адрес нашего MikroTik
MIKROTIK_IP=192.168.15.9
#Логин от него
MIKROTIK_USER=admin
#Пароль:
MIKROTIK_PASS=verySTRONGpassword!!
#Где лежит лог от Suricata
LOG=/var/log/suricata/fast.log
#Файл, необходимый для работы скрипта (будет создан самим скриптом)
INLOG_FILE=/root/test_after_parser.log
declare -A IP_OUR_MASC
#IP address 192.168.15.9, получаемый по VPN (статический)
#IP address 10.0.14.144, получаемый по WAN (динамический)
#Ниже задаются шаблоны соответствия, по которым будет отрабатывать
#скрипт при анализе IP адресов из fast.log. IP_OUR_MASC[0,1]=10. означает,
#что WAN интерфейс получает адрес, формата 10.0.0.0/8
IP_OUR_MASC[0,0]=IPS; IP_OUR_MASC[0,1]=10.; IP_OUR_MASC[0,2]=12
IP_OUR_MASC[1,0]=OVPN; IP_OUR_MASC[1,1]=192.168.15.9; IP_OUR_MASC[1,2]=12
#Имя вашего WAN интерфейса в RouterOS, необходим для получения текущего IP адреса
NAME_WAN_INTERFACE=WAN
#IP адрес IDS сервера
IP_SURICATA_SERVER=192.168.15.29
#Блокировать угрозы будем на 24 часа
BLOCK_TIMEOUT=24h
#Периодичность просмотра лога от Suricata 10 секунд
SLEEP_TIMEOUT=10
#Имя, создаваемого самим скриптом, address-list-а в RouterOS
NAME_BLOCK_LIST=Suricata_rules
IP_SRC_DEFAULT='IP_OUR'
#Сообщения с таким содержанием будем игнорировать
EVENT_NOT_INTEREST='SURICATA TLS invalid handshake message'
EVENT_NOT_INTEREST2='SURICATA TLS invalid record/traffic'
declare -p ARRAY > /dev/null
while [ 1 = 1 ]; do
#Проверяем, пустой ли лог
STRINGS_IN_LOG_FILE=$(cat $LOG | wc | awk {'print $1'})
if [ "$STRINGS_IN_LOG_FILE" -lt 1 ]; then
echo "Fastlog is empty, sleep $SLEEP_TIMEOUT second..."
sleep $SLEEP_TIMEOUT
else
#Имеем, что лог не пустой. Начинаем с ним работу
cat $LOG > $INLOG_FILE
#Очищаем лог Suricata. В этом месте, конечно, можно терять записи, вносимые IDS системой
#в текущий момент времени, что является допущением текущей реализации
echo -n '' > $LOG
#Парсим лог по переменным
readarray -t ARRAY < $INLOG_FILE
for LINE in "${ARRAY[@]}"; do
DATE=$(echo $LINE | awk -F " " '{print $1}' | cut -c1-10)
TIME=$(echo $LINE | awk -F " " '{print $1}' | cut -c12-26)
EVENT=$(echo $LINE | awk -F '[**]' '{print $3}' | awk '!($2="")' | cut -c3- | rev | cut -c 3- | rev)
CLASS=$(echo $LINE | awk -F '[**]' '{print $5}' | cut -c4- | rev | awk '{$1=$2=$3=$4=$5=$6=""; print $0}' | cut -c 8- | rev)
PRIOR=$(echo $LINE | awk -F '[**]' '{print $5}' | rev | awk {'print $5'} | cut -c 2- | rev)
PROTOCOL=$(echo $LINE | awk -F '[**]' '{print $5}' | rev | awk {'print $4'} | cut -c 2- | rev | cut -c 2-)
IP_SRC=$(echo $LINE | awk -F '[**]' '{print $5}' | rev | awk {'print $3'} | rev | cut -d : -f 1)
IP_SRC_PORT=$(echo $LINE | awk -F '[**]' '{print $5}' | rev | awk {'print $3'} | rev | cut -d : -f 2-)
IP_DST=$(echo $LINE | awk -F '[**]' '{print $5}' | rev | awk {'print $1'} | rev | cut -d : -f 1)
IP_DST_PORT=$(echo $LINE | awk -F '[**]' '{print $5}' | rev | awk {'print $1'} | rev | cut -d : -f 2-)
echo "$DATE $TIME $EVENT $CLASS $PRIOR $PROTOCOL $IP_SRC $IP_SRC_PORT $IP_DST $IP_DST_PORT"
#Проверяем текущую строку лога на наличие неинтересного для нас сообщения
echo -n "$EVENT" | grep "$EVENT_NOT_INTEREST" > /dev/null
if [ $? = 0 ]; then
echo -e "Find $EVENT_NOT_INTEREST, do nothing..."
else
#Проверяем текущую строку лога на наличие неинтересного для нас сообщения
echo -n "$EVENT" | grep "$EVENT_NOT_INTEREST2" > /dev/null
if [ $? = 0 ]; then
echo -e "Find $EVENT_NOT_INTEREST2, do nothing..."
else
#Проверяем не попала ли сама IDS в сработку, так как команды управления будут идти по SSH
#и могут быть восприняты как брутфорс атака
if [ x"$IP_SRC" = x"$IP_SURICATA_SERVER" ]; then
echo -e "Find IP of the Suricata server, do nothing..."
elif [ x"$IP_DST" = x"$IP_SURICATA_SERVER" ]; then
echo -e "Find IP of the Suricata server, do nothing..."
else
#Уточняем текущий IP адрес на WAN интерфейсе
IP_OUR_MASC[0,1]=$(sshpass -p $MIKROTIK_PASS ssh $MIKROTIK_USER@$MIKROTIK_IP \
/ip address print where interface=$NAME_WAN_INTERFACE | grep '/' | awk {'print $3'} | rev | cut -d / -f 2- | rev)
#Проверки для работы скрипта
IP_SRC_TEST=$(echo $IP_SRC | cut -c1-${IP_OUR_MASC[0,2]})
if [ x"$IP_SRC_TEST" = x"${IP_OUR_MASC[0,1]}" ]; then
IP_SRC=$IP_SRC_DEFAULT
else
IP_SRC_TEST=$(echo $IP_SRC | cut -c1-${IP_OUR_MASC[1,2]})
if [ x"$IP_SRC_TEST" = x"${IP_OUR_MASC[1,1]}" ]; then
IP_SRC=$IP_SRC_DEFAULT
fi
fi
#Проверки для работы скрипта
IP_SRC_TEST=$(echo $IP_DST | cut -c1-${IP_OUR_MASC[0,2]})
if [ x"$IP_SRC_TEST" = x"${IP_OUR_MASC[0,1]}" ]; then
IP_DST=$IP_SRC_DEFAULT
else
IP_SRC_TEST=$(echo $IP_DST | cut -c1-${IP_OUR_MASC[1,2]})
if [ x"$IP_SRC_TEST" = x"${IP_OUR_MASC[1,1]}" ]; then
IP_DST=$IP_SRC_DEFAULT
fi
fi
#Находится ли детектированный IP уже в address-list на маршрутизаторе
if [ x"$IP_SRC" = x'IP_OUR' ]; then
NUMBER_IN_ADDRESS_LIST=$(sshpass -p $MIKROTIK_PASS ssh $MIKROTIK_USER@$MIKROTIK_IP \
/ip firewall address-list print where list=$NAME_BLOCK_LIST | grep -w "Suricata $IP_DST" | awk {'print $1'})
if [ -z "$NUMBER_IN_ADDRESS_LIST" ]; then
#Добавляем новую запись в address-list маршрутизатора
sshpass -p $MIKROTIK_PASS ssh $MIKROTIK_USER@$MIKROTIK_IP \
/ip firewall address-list add address=$IP_DST timeout=$BLOCK_TIMEOUT \
comment=\"Suricata $IP_DST:$IP_DST_PORT to $IP_SRC_PORT $PROTOCOL priority $PRIOR \### $CLASS \### $EVENT\" list=$NAME_BLOCK_LIST
else
#Обновляем таймаут для уже существующей записи
sshpass -p $MIKROTIK_PASS ssh $MIKROTIK_USER@$MIKROTIK_IP \
/ip firewall address-list set timeout=$BLOCK_TIMEOUT numbers=$NUMBER_IN_ADDRESS_LIST
fi
else
#Тоже самое, относительно другой переменной
NUMBER_IN_ADDRESS_LIST=$(sshpass -p $MIKROTIK_PASS ssh $MIKROTIK_USER@$MIKROTIK_IP \
/ip firewall address-list print where list=$NAME_BLOCK_LIST | grep -w "Suricata $IP_SRC" | awk {'print $1'})
if [ -z "$NUMBER_IN_ADDRESS_LIST" ]; then
sshpass -p $MIKROTIK_PASS ssh $MIKROTIK_USER@$MIKROTIK_IP \
/ip firewall address-list add address=$IP_SRC timeout=$BLOCK_TIMEOUT \
comment=\"Suricata $IP_SRC:$IP_SRC_PORT to $IP_DST_PORT $PROTOCOL priority $PRIOR \### $CLASS \### $EVENT\" list=$NAME_BLOCK_LIST
else
sshpass -p $MIKROTIK_PASS ssh $MIKROTIK_USER@$MIKROTIK_IP \
/ip firewall address-list set timeout=$BLOCK_TIMEOUT numbers=$NUMBER_IN_ADDRESS_LIST
fi
fi
echo 'End ...'
fi
fi
fi
done
fi
done
Код прокомментирован и ясен. Результат его работы на RouterOS выглядит примерно так:
Мы пропускали неинтересные для нас сработки «TLS invalid handshake message» и «TLS invalid record/traffic». Можно переделать скрипт и пропускать сообщения с приоритетом 3, так как это не угрозы безопасности оберегаемой сети, и они носят скорее уведомительный характер. Блокировать IP адреса, обнаруживаемые IDS, будем посредством Firewall:
/ip firewall raw add action=drop comment="Block from Suricata" src-address-list=Suricata_rules
/ip firewall raw add action=drop comment="Block from Suricata" dst-address-list=Suricata_rules
Лучше это делать в RAW filter, так как пакеты будут отброшены в самом начале обработки их маршрутизатором и не потратят зря имеющиеся ресурсы. Это хорошо видно в схеме прохождения трафика внутри RouterOS, взятой у этих ребят:
Попробуем повторить брутфорс службы SSH роутера:
Попробуем что-нибудь серьезнее, загрузим эксплоит для уязвимости службы Winbox (CVE-2018-14847):
git clone https://github.com/BasuCert/WinboxPoC
python3 WinboxExploit.py 192.168.15.9
Как видно, все было детектировано и соответственно заблокировано на маршрутизаторе. Поэтому можно говорить, что из IDS наша реализация превратилась в IPS. Здесь стоит еще упомянуть коммерческий проект, в котором уже все это решено за нас, можно брать и пользоваться. Поставляется в виде готовой сборки ISO.
13. Заключение
Вот и подошёл к концу цикл наших статей, посвящённый широким возможностям операционной системы RouterOS, а также сопрягаемых с ней opensource решений. В комплексе организованная безопасность уровней L1, L2 и L3, реализация централизованного логирования, интегрированное IPS решение позволяют говорить, что наш MikroTik – это полноценный барьер для разноуровневых угроз, нацеленных на защищаемую сеть. То, что не умеет делать RouterOS, можно допилить самому готовыми бесплатными решениями.
И вот еще о чем важном мы не упомянули. Всегда помните, что в любой, даже в самой технически защищённой системе, остаётся слабое место – человек её обслуживающий. Здесь использован перифраз известной цитаты Кевина Митника из книги «Искусство обмана»: «Человеческий фактор по-настоящему самое слабое звено в безопасности». Поэтому гигиена, в том числе цифровая, никогда не теряет актуальности, особенно в период пандемии.
Мой MikroTik — (часть 2.Настройка протокола Dot1X и работа Firewall)
Мой MikroTik — (часть 3. Варианты реализации централизованного логирования)
Мой MikroTik — (часть 4. Развёртывание IDS и её интеграцию в инфраструктуру RouterOS)
Комментарии (16)
ksiva
21.09.2021 06:56К сожалению Микротик сейчас используется не для защиты, а для атак по всему миру. И "крепостью" у него стать так и не получилось. Вот например, новость про пойманный сотрудниками Ростелеком-Солар ботнет из 45000 устройств Mikrotik. https://habr.com/ru/company/solarsecurity/blog/578900/
Что делает разработчик для исправления данной проблемы?DaemonGloom
21.09.2021 14:57+2Что может сделать разработчик против людей, которые ставят простой пароль и открывают управление наружу? Запретить подобное? Ну так на это начнут ругаться клиенты, которые то же управление открывают в контролируемые сети.
Автообновление? Перезагрузка в неизвестный момент времени, шанс на несовместимость? Опять же клиенты не оценят. Это же не Win10, тут есть альтернативы оборудованию, на которые можно уйти.ksiva
21.09.2021 17:02Разошлите ваши рекомендации по безопасности вашим клиентам. Они же есть? Там наверняка есть простые советы:
- Подписаться на рассылку ваших бюллетней по безопасности
- Не выставлять порт управления прямо в Интернет, только изнутри или через VPN
- Рекомендации по парольной политике
- Добавьте автоматический сервис проверки безопасности в самом устройстве.
- Включите IPS для самого себя - ведь suricata же теперь внутри. Значит может видеть эту несчастную атаку на Mikrotik 2018 года (CVE-2018-14847)Наверняка производитель может сам или попросить своих партнеров связаться с клиентами и оповестить их о проблемах. Проблема ведь серьезная.
IgnatIvchenko
21.09.2021 16:20Glupteba работает через брутфорс паролей по ssh и эксплуатации уязвимости CVE-2018-14847
А причём разработчик к тому что пользователь не настраивает оборудование? Не ставит пароль?
Так можно любого обвинить, производителя дверей, замков и т.д. или по-вашему производитель должен настраивать ваше оборудование и устанавливать %VeryStronPassword123%?
touchscan
21.09.2021 16:21Предполагаю, что проблема скомпрометированных устройств именно в базовой настройке. Люди часто мыслят в русле "я же не президент, чтобы меня ломать пытались"... Разработчик ничего, никому не должен.
Отвертка - инструмент, но в плохих руках - оружие. Никто ведь изготовителя не спрашивает, что он сделал для исправления такой проблемы.
Домашние роутеры настраиваются вписыванием логина и пароля + тип подключения, там по сути вообще кот наплакал настроек - безопасность на совести разработчика прошивки.
Микротик не совсем домашний, он все-таки ближе к коммерческому сектору, и требует квалификации.
ИМХО: не разработчики должны решать проблемы конечной настройки, а человек должен не лениться и обучаться. Век живи - век учись.
Cursorious
22.09.2021 13:44Когда нужно просто добавлять адрес в стоп-лист на микротике, я использую ICMP:
/ip firewall filter add action=add-dst-to-address-list address-list=block-list address-list-timeout=23h59m59s chain=forward comment="IP_Ban" packet-size=128 protocol=icmp src-address=192.168.1.100.133 /ip firewall raw add action=drop chain=prerouting comment="Drop block-list" src-address-list=block-list
Если на защищаемом сервере с адресом 192.168.1.100 отправить icmp пакет нужного размера в сторону злоумышленика:
ping 98.137.11.164 -l 100
Он будет пойман файрволом и адрес назначения отправлен в бан. Тут, конечно, нужно чтобы дефолтный маршрут вел на микротик. Несколько микротов одним IDS так не охватить. Зато профит: не храним и не передаем пароли в скрипте, не нужно обрабатывать список забаненых - таймаут обновляется с каждым новым пакетом. Примерно таким манером был самобытный fail2ban для конечных RDP реализован, например.
А у сурикаты же есть опция реагирования на угрозы, выполнение команды. Почему пошли через анализ логов, так оказалось проще?
olegtsss Автор
22.09.2021 13:47Суриката только детектирует и результат помещает в лог, на чем и базируется показанное решение .
event1
Вроде RouterOS умеет аутентификацию по публичному ключу. А вот так sshpass оставлять в статье для широкого круга читателей, это как-то не комильфо.
olegtsss Автор
В первой части это рассмотрено:
habr.com/ru/company/ruvds/blog/575808
sshpass — вариант реализации, допилить под себя готовое решение не составит труда!