Здравствуйте коллеги! Сегодня, когда накал страстей вокруг «удалёнки» немного спал, большинство админов победило задачу удаленного доступа сотрудников к корпоративной сети, пришло время поделиться моей давней наработкой по повышению безопасности VPN. В этой статье не будет модных ныне IPSec IKEv2 и xAuth. Речь пойдет о построении системы двухфакторной аутентификации (2FA) пользователей VPN, когда MikroTik выступает в качестве VPN-сервера. А именно, когда используются «классические» протоколы типа PPP.
Сегодня я расскажу как защитить MikroTik PPP-VPN даже в случае «угона» пользовательской учётки. Когда эта схема была внедрена одному из моих заказчиков, он охарактеризовал его коротко «ну, теперь прямо как в банке!».
Метод не использует внешних сервисов-аутентификаторов. Задачи выполняются внутренними средствами самого маршрутизатора. Без затрат для подключаемого клиента. Способ работает как для клиентов PC, так и для мобильных устройств.
Общая схема защиты выглядит следующим образом:
Первая самая маленькая проблема с которой пришлось столкнуться, это хранение контактной информации о пользователе для пересылки ему кода 2FA. Поскольку, произвольных полей данных, соответствующих пользователям в микротике создать нельзя, было использовано существующее поле «comment»:
/ppp secrets add name=Petrov password=4M@ngr! comment=«89876543210»
Вторая проблема оказалась посерьёзнее — выбор пути и способа доставки кода. В текущий момент реализованы три схемы: а) SMS через USB-модем б) e-mail в) SMS via e-mail доступный для корпоративных клиентов красного сотового оператора.
Да, схемы с SMS приносят затраты. Но если разобраться, «безопасность это всегда про деньги» (с).
Схема с e-mail лично мне не нравится. Не потому, что требует доступности почтового сервера для аутентифицируемого клиента — это не проблема разделить трафик. Однако, если клиент беспечно сохранял пароли на и на vpn и на почту в браузере, а затем потерял свой ноутбук, злоумышленник получит с него полный доступ к корпоративной сети.
Итак, решено — доставляем одноразовый код при помощи SMS-сообщений.
Третья проблема была в том, где и как в MikroTik сгенерировать псевдослучайный код для 2FA. В скриптовом языке RouterOS нет аналога функции random() и прежде я видел несколько костыльных скриптовых генераторов псевдослучайных чисел. Ни один из них мне не понравился по разным причинам.
На самом деле, генератор псевдослучайных последовательностей в MikroTik ЕСТЬ! Припрятан он от поверхностного взгляда в контексте /certificates scep-server. Первый способ получить одноразовый пароль легок и прост — командой /certificates scep-server otp generate. Если выполнить простую операцию присвоения переменной, то мы получим значение типа array, которое можно использовать в дальнейшем в скриптах.
Второй способ получения одноразового пароля который тоже легко применить — использование внешнего сервиса random.org для генерации желаемого вида последовательности псевдослучайных чисел. Вот упрощенный консольный пример получения данных в переменную:
Запрос сформатированный для консоли (в теле скрипта потребуется экранирование спецсимволов) получает строку из шести символов-цифр в переменную $rnd1. Следующая команда «put» просто отображает переменную в консоли MikroTik.
Четвертая проблема, которую пришлось оперативно решать — это каким образом и куда подключенный клиент будет передавать свой одноразовый код на второй стадии аутентификации.
На роутере MikroTik должна существовать служба способная принять код и сопоставить его с конкретным клиентом. При совпадении предоставленного кода с ожидаемым адрес клиента должен попасть в некий «белый» список, адресам из которого разрешен доступ к внутренней сети компании.
Ввиду небогатого выбора служб, было принято решение принимать коды по http при помощи встроенного в микротик webproxy. А поскольку фаервол умеет работать с динамическими списками IP-адресов, то поиск кода, сопоставление его с клиентским IP и внесение в «белый» список выполняет именно фаервол посредством Layer7 regexp. Самому маршрутизатору присвоено условное DNS-имя «gw.local», на нем создана статическая А-запись для выдачи PPP-клиентам:
В данном случае у прокси две функции.
1. Открывать tcp-соединения с клиентами;
2. В случае успешной авторизации переадресовать клиентский браузер на страничку или картинку извещающую об успешном прохождении аутентификации:
Перечислю важные элементы конфигурации:
Упрощенная блок-схема обработки пакета выглядит так:
Для попадания в проверку по Layer7 трафика от клиентов из «серого» списка еще не прошедших второй этап аутентификации, в стандартной цепочке «input» создано правило:
Теперь начнем прикручивать всё это богатство к сервису PPP. MikroTik позволяет использовать скрипты в профилях (ppp-profile) и назначать их на события установки и разрыва ppp-соединения. Настройки ppp-profile могут применяться как для PPP-сервера в целом, так и для отдельных пользователей. При этом назначенный пользователю profile имеет приоритет перекрывая своими заданными параметрами параметры profile выбранного для сервера в целом.
В результате такого подхода, мы можем создать специальный профиль для двухфакторной аутентификации и назначить его не всем пользователям, а только тем, кому считаем нужным это сделать. Это может быть актуально в случае если службы PPP у вас используются не только для подключения конечных пользователей, но одновременно и для построения site-to-site соединений.
Во вновь созданном специальном профиле используем динамическое добавление адреса и интерфейса подключившегося пользователя в «серые» списки адресов и интерфейсов:
Использовать совместно списки «address-list» и «interface-list» необходимо, чтобы определять и захватывать трафик от не прошедших вторичную авторизацию VPN-клиентов в цепочке dstnat (prerouting).
Когда подготовка закончена, дополнительные цепочки фаервола и профиль созданы, напишем скрипт отвечающий за автогенерацию кода 2FA и отдельных правил фаервола.
Документация wiki.mikrotik.com на PPP-Profile обогащает нас информацией о переменных, связанных с событиями подключения-отключения PPP-клиента «Execute script on user login-event. These are available variables that are accessible for the event script: user, local-address, remote-address, caller-id, called-id, interface». Некоторые из них нам очень пригодятся.
Специально для любителей бездумно копипастить предупреждаю — код взят с тестовой версии и может содержать незначительные очепятки. Разобраться где именно понимающему человеку труда не составит.
При отключении пользователя генерируется событие «On-Down» и вызывается соответствующий скрипт с параметрами. Задача этого скрипта — почистить за собой правила фаервола, созданные для отключившегося пользователя.
После этого можно создавать пользователей и назначать всем или некоторым из них профиль с двухфакторной аутентификацией.
Как это выглядит на стороне клиента.
При установке VPN-соединения, на телефон/планшет на андроид/iOS с сим-картой приходит SMS примерно такого вида:
Если соединение установлено непосредственно с телефона/планшета, то можно пройти 2FA просто кликнув по ссылке из сообщения. Это удобно.
Если VPN-соединение устанавливается с PC, то для пользователя потребуется минимальная форма ввода пароля. Небольшая форма в виде файла HTML передается пользователю при настройке VPN. Файлик можно даже по почте переслать, чтобы пользователь сохранил его у себя и создал ярлык в удобном месте. Примерно так это выглядит:
Пользователь щёлкает мышкой по ярлыку, открывается простая форма ввода кода, которая вставит код в открываемый URL:
Форма самая примитивная, дана для примера. Желающие могут доработать под себя.
Если авторизация прошла успешно, пользователь в браузере увидит лого MikroTik, что должно послужить сигналом об успешной аутентификации:
Замечу, что картинка возвращается из встроенного web-сервера MikroTik с помощью WebProxy Deny Redirect.
Полагаю, картинку можно кастомизировать, используя инструмент «hotspot», загрузив туда свой вариант и задав на него Deny Redirect URL с WebProxy.
Большая просьба к тем, кто пытается купив самый дешевый «игрушечный» микротик за $20, заменить им маршрутизатор за $500 — не надо так поступать. Устройства типа «hAP Lite»/«hAP mini» (home access point) имеют очень слабый CPU(smips), и вполне вероятно не справятся с нагрузкой в бизнес-сегменте.
Warning! У данного решения есть один минус: при подключении-отключении клиентов происходят изменения конфигурации, которую маршрутизатор пытается сохранять в своей энергонезависимой памяти. При большом количестве клиентов и частых подключениях-отключениях, это может привести к деградации внутреннего накопителя в маршрутизаторе.
P.S.: методы доставки кода клиенту могут быть расширены и дополнены насколько хватит ваших возможностей в программировании. Например, можно отправлять сообщения в telegram или… предлагайте варианты!
Надеюсь статья окажется вам полезной и поможет сделать сети малого и среднего бизнеса еще чуть безопаснее.
Сегодня я расскажу как защитить MikroTik PPP-VPN даже в случае «угона» пользовательской учётки. Когда эта схема была внедрена одному из моих заказчиков, он охарактеризовал его коротко «ну, теперь прямо как в банке!».
Метод не использует внешних сервисов-аутентификаторов. Задачи выполняются внутренними средствами самого маршрутизатора. Без затрат для подключаемого клиента. Способ работает как для клиентов PC, так и для мобильных устройств.
Общая схема защиты выглядит следующим образом:
- Внутренний IP-адрес пользователя успешно подключившегося к в VPN-серверу, автоматически попадает в «серый» список.
- Событие подключения автоматически генерирует одноразовый код, который пересылается пользователю одним из доступных способов.
- Адресам, находящимся в этом списке ограничен доступ к ресурсам локальной сети, за исключением сервиса «аутентификатора», ожидающего получение одноразового кода-пароля.
- После предъявления кода, пользователю открывается доступ к внутренним ресурсам сети.
Первая самая маленькая проблема с которой пришлось столкнуться, это хранение контактной информации о пользователе для пересылки ему кода 2FA. Поскольку, произвольных полей данных, соответствующих пользователям в микротике создать нельзя, было использовано существующее поле «comment»:
/ppp secrets add name=Petrov password=4M@ngr! comment=«89876543210»
Вторая проблема оказалась посерьёзнее — выбор пути и способа доставки кода. В текущий момент реализованы три схемы: а) SMS через USB-модем б) e-mail в) SMS via e-mail доступный для корпоративных клиентов красного сотового оператора.
Да, схемы с SMS приносят затраты. Но если разобраться, «безопасность это всегда про деньги» (с).
Схема с e-mail лично мне не нравится. Не потому, что требует доступности почтового сервера для аутентифицируемого клиента — это не проблема разделить трафик. Однако, если клиент беспечно сохранял пароли на и на vpn и на почту в браузере, а затем потерял свой ноутбук, злоумышленник получит с него полный доступ к корпоративной сети.
Итак, решено — доставляем одноразовый код при помощи SMS-сообщений.
Третья проблема была в том, где и как в MikroTik сгенерировать псевдослучайный код для 2FA. В скриптовом языке RouterOS нет аналога функции random() и прежде я видел несколько костыльных скриптовых генераторов псевдослучайных чисел. Ни один из них мне не понравился по разным причинам.
На самом деле, генератор псевдослучайных последовательностей в MikroTik ЕСТЬ! Припрятан он от поверхностного взгляда в контексте /certificates scep-server. Первый способ получить одноразовый пароль легок и прост — командой /certificates scep-server otp generate. Если выполнить простую операцию присвоения переменной, то мы получим значение типа array, которое можно использовать в дальнейшем в скриптах.
Второй способ получения одноразового пароля который тоже легко применить — использование внешнего сервиса random.org для генерации желаемого вида последовательности псевдослучайных чисел. Вот упрощенный консольный пример получения данных в переменную:
Код
:global rnd1 [:pick ([/tool fetch url="https://www.random.org/strings/?num=1&len=7&digits=on&unique=on&format=plain&rnd=new" as-value output=user ]->"da
ta") 1 6]
:put $rnd1
Запрос сформатированный для консоли (в теле скрипта потребуется экранирование спецсимволов) получает строку из шести символов-цифр в переменную $rnd1. Следующая команда «put» просто отображает переменную в консоли MikroTik.
Четвертая проблема, которую пришлось оперативно решать — это каким образом и куда подключенный клиент будет передавать свой одноразовый код на второй стадии аутентификации.
На роутере MikroTik должна существовать служба способная принять код и сопоставить его с конкретным клиентом. При совпадении предоставленного кода с ожидаемым адрес клиента должен попасть в некий «белый» список, адресам из которого разрешен доступ к внутренней сети компании.
Ввиду небогатого выбора служб, было принято решение принимать коды по http при помощи встроенного в микротик webproxy. А поскольку фаервол умеет работать с динамическими списками IP-адресов, то поиск кода, сопоставление его с клиентским IP и внесение в «белый» список выполняет именно фаервол посредством Layer7 regexp. Самому маршрутизатору присвоено условное DNS-имя «gw.local», на нем создана статическая А-запись для выдачи PPP-клиентам:
DNS
/ip dns static add name=gw.local address=172.31.1.1
Захват на прокси трафика непроверенных клиентов:
/ip firewall nat add chain=dstnat dst-port=80,443 in-interface=2fa protocol=tcp !src-address-list=2fa_approved action=redirect to-ports=3128
В данном случае у прокси две функции.
1. Открывать tcp-соединения с клиентами;
2. В случае успешной авторизации переадресовать клиентский браузер на страничку или картинку извещающую об успешном прохождении аутентификации:
Proxy config
/ip proxy
set enabled=yes port=3128
/ip proxy access
add action=deny disabled=no redirect-to=gw.local./mikrotik_logo.png src-address=0.0.0.0/0
Перечислю важные элементы конфигурации:
- interface-list «2fa» — динамический список клиентских интерфейсов, трафик с которых требует обработки в рамках 2FA;
- address-list «2fa_jailed» — «серый» список туннельных IP-адресов клиентов VPN;
- address_list «2fa_approved» — «белый» список туннельных IP-адресов клиентов VPN, успешно прошедших двухфакторную аутентификацию.
- цепочка фаервола «input_2fa» — в ней происходит проверка tcp-пакетов на наличие кода авторизации и совпадение IP-адреса отправителя кода с требуемым. Правила в цепочку добавляются и удаляются динамически.
Упрощенная блок-схема обработки пакета выглядит так:
Для попадания в проверку по Layer7 трафика от клиентов из «серого» списка еще не прошедших второй этап аутентификации, в стандартной цепочке «input» создано правило:
Код
/ip firewall filter add chain=input !src-address-list=2fa_approved action=jump jump-target=input_2fa
Теперь начнем прикручивать всё это богатство к сервису PPP. MikroTik позволяет использовать скрипты в профилях (ppp-profile) и назначать их на события установки и разрыва ppp-соединения. Настройки ppp-profile могут применяться как для PPP-сервера в целом, так и для отдельных пользователей. При этом назначенный пользователю profile имеет приоритет перекрывая своими заданными параметрами параметры profile выбранного для сервера в целом.
В результате такого подхода, мы можем создать специальный профиль для двухфакторной аутентификации и назначить его не всем пользователям, а только тем, кому считаем нужным это сделать. Это может быть актуально в случае если службы PPP у вас используются не только для подключения конечных пользователей, но одновременно и для построения site-to-site соединений.
Во вновь созданном специальном профиле используем динамическое добавление адреса и интерфейса подключившегося пользователя в «серые» списки адресов и интерфейсов:
Код
/ppp profile add address-list=2fa_jailed change-tcp-mss=no local-address=192.0.2.254 name=2FA interface-list=2fa only-one=yes remote-address=dhcp_pool1 use-compression=no use-encryption= required use-mpls=no use-upnp=no dns-server=172.31.1.1
Использовать совместно списки «address-list» и «interface-list» необходимо, чтобы определять и захватывать трафик от не прошедших вторичную авторизацию VPN-клиентов в цепочке dstnat (prerouting).
Когда подготовка закончена, дополнительные цепочки фаервола и профиль созданы, напишем скрипт отвечающий за автогенерацию кода 2FA и отдельных правил фаервола.
Документация wiki.mikrotik.com на PPP-Profile обогащает нас информацией о переменных, связанных с событиями подключения-отключения PPP-клиента «Execute script on user login-event. These are available variables that are accessible for the event script: user, local-address, remote-address, caller-id, called-id, interface». Некоторые из них нам очень пригодятся.
Код, используемый в профиле для события подключения PPP on-up
#Логируем для отладки полученные переменные
:log info ($"local-address")
:log info ($"remote-address")
:log info ($"caller-id")
:log info ($"called-id")
:log info ([/int pptp-server get ($"interface") name])
#Объявляем свои локальные переменные
:local listname "2fa_jailed"
:local viamodem false
:local modemport "usb2"
#ищем автоматически созданную запись в адрес-листе "2fa_jailed"
:local recnum1 [/ip fi address-list find address=($"remote-address") list=$listname]
#получаем псевдослучайный код через random.org
#:local rnd1 [:pick ([/tool fetch url="https://www.random.org/strings/?num=1&len=7&digits=on&unique=on&format=plain&rnd=new" as-value output=user]->"data") 0 4]
#либо получаем псевдослучайный код через локальный генератор
#:local rnd1 [pick ([/cert scep-server otp generate as-value minutes-valid=1]->"password") 0 4 ]
#Ищем и обновляем коммент к записи в адрес-листе. Вносим искомый код для отладки
/ip fir address-list set $recnum1 comment=$rnd1
#получаем номер телефона куда слать SMS
:local vphone [/ppp secret get [find name=$user] comment]
#Готовим тело сообщения. Если клиент подключается к VPN прямо с телефона ему достаточно
#будет перейти прямо по ссылке из полученного сообщения
:local msgboby ("Your code: ".$comm1."\n Or open link http://gw.local/otp/".$comm1."/")
# Отправляем SMS по выбранному каналу - USB-модем или email-to-sms
if $viamodem do={ /tool sms send phone-number=$vphone message=$msgboby port=$modemport } else={ /tool e-mail send server=a.b.c.d from=admin@mydomain.example to=mail2sms@mcommunicator.ru subject="@".$vphone body=$msgboby }
#Генерируем Layer7 regexp
local vregexp ("otp\\/".$comm1)
:local vcomment ("2fa_".($"remote-address"))
/ip firewall layer7-protocol add name=($"vcomment") comment=($"remote-address") regexp=($"vregexp")
#Генерируем правило проверяющее по Layer7 трафик клиента в поисках нужного кода
#и небольшой защитой от брутфорса кодов с помощью dst-limit
/ip firewall filter add action=add-src-to-address-list address-list=2fa_approved address-list-timeout=none-dynamic chain=input_2fa dst-port=80,443,3128 layer7-protocol=($"vcomment") protocol=tcp src-address=($"remote-address") dst-limit=1,1,src-address/1m40s
Специально для любителей бездумно копипастить предупреждаю — код взят с тестовой версии и может содержать незначительные очепятки. Разобраться где именно понимающему человеку труда не составит.
При отключении пользователя генерируется событие «On-Down» и вызывается соответствующий скрипт с параметрами. Задача этого скрипта — почистить за собой правила фаервола, созданные для отключившегося пользователя.
Код, используемый в профиле для события подключения PPP on-down
:local vcomment ("2fa_".($"remote-address"))
/ip firewall address-list remove [find address=($"remote-address") list=2fa_approved]
/ip firewall filter remove [find chain="input_2fa" src-address=($"remote-address") ]
/ip firewall layer7-protocol remove [find name=$vcomment]
После этого можно создавать пользователей и назначать всем или некоторым из них профиль с двухфакторной аутентификацией.
winbox
Код
/ppp secrets set [find name=Petrov] profile=2FA
Как это выглядит на стороне клиента.
При установке VPN-соединения, на телефон/планшет на андроид/iOS с сим-картой приходит SMS примерно такого вида:
SMSка
Если соединение установлено непосредственно с телефона/планшета, то можно пройти 2FA просто кликнув по ссылке из сообщения. Это удобно.
Если VPN-соединение устанавливается с PC, то для пользователя потребуется минимальная форма ввода пароля. Небольшая форма в виде файла HTML передается пользователю при настройке VPN. Файлик можно даже по почте переслать, чтобы пользователь сохранил его у себя и создал ярлык в удобном месте. Примерно так это выглядит:
Ярлык на столе
Пользователь щёлкает мышкой по ярлыку, открывается простая форма ввода кода, которая вставит код в открываемый URL:
Скрин формы
Форма самая примитивная, дана для примера. Желающие могут доработать под себя.
2fa_login_mini.html
<html>
<head> <title>SMS OTP login</title> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> </head>
<body>
<form name="login" action="location.href='http://gw.local/otp/'+document.getElementById(‘text').value" method="post"
<input id="text" type="text"/>
<input type="button" value="Login" onclick="location.href='http://gw.local/otp/'+document.getElementById('text').value"/>
</form>
</body>
</html>
Если авторизация прошла успешно, пользователь в браузере увидит лого MikroTik, что должно послужить сигналом об успешной аутентификации:
Замечу, что картинка возвращается из встроенного web-сервера MikroTik с помощью WebProxy Deny Redirect.
Полагаю, картинку можно кастомизировать, используя инструмент «hotspot», загрузив туда свой вариант и задав на него Deny Redirect URL с WebProxy.
Большая просьба к тем, кто пытается купив самый дешевый «игрушечный» микротик за $20, заменить им маршрутизатор за $500 — не надо так поступать. Устройства типа «hAP Lite»/«hAP mini» (home access point) имеют очень слабый CPU(smips), и вполне вероятно не справятся с нагрузкой в бизнес-сегменте.
Warning! У данного решения есть один минус: при подключении-отключении клиентов происходят изменения конфигурации, которую маршрутизатор пытается сохранять в своей энергонезависимой памяти. При большом количестве клиентов и частых подключениях-отключениях, это может привести к деградации внутреннего накопителя в маршрутизаторе.
P.S.: методы доставки кода клиенту могут быть расширены и дополнены насколько хватит ваших возможностей в программировании. Например, можно отправлять сообщения в telegram или… предлагайте варианты!
Надеюсь статья окажется вам полезной и поможет сделать сети малого и среднего бизнеса еще чуть безопаснее.
imbasoft
Интересная схема, плюс в том что все делается на Tike. Минус анти-юзер френдли. С мобилки подключаться «для не айтишников» очень неудобно будет.
Сам внедрял 2FA на Tike по следующей схеме:
1. Tik — VPN сервер
2. Коммерческий RADIUS (для проверки одноразовых кодов [SMS]) + сервер и NPS сервер Microsoft (для проверки по AD)
Схема подключения была следующая:
1. У пользователя была SMS-ка с одноразовым паролем. Она высылалась при первоначальном подключении или при не успешной попытке входа.
2. При авторизации в VPN пользователь указывал свой AD логин, а в поле пароль указывал <АD пароль><одноразовый код>
Минус моей схемы в том, что тут есть платные элементы — коммерческий RADIUS (100k руб. в год на 500 пользователей).
Как вариант развития можете посмотреть в сторону генерации одноразовых кодов с помощью Google Auth их тоже можно скрестить с Tikом
skovpen
FreeRADIUS+LinOTP