通过 MikroTik 和 SMS 对 VPN 用户进行双因素身份验证

同事们大家好! 今天,当围绕“远程工作”的热情稍稍消退,大多数管理员赢得了员工远程访问公司网络的任务时,是时候分享我长期以来在提高VPN安全性方面的经验了。 这篇文章现在不会流行IPSec IKEv2和xAuth。 这是关于构建一个系统。 双因素身份验证 (2FA) 当 MikroTik 充当 VPN 服务器时的 VPN 用户。 即,当使用“经典”协议(例如 PPP)时。

通过 MikroTik 和 SMS 对 VPN 用户进行双因素身份验证

今天我将告诉您如何保护 MikroTik PPP-VPN,即使用户帐户被“劫持”。 当这个方案被介绍给我的一位客户时,他简短地描述道:“好吧,现在就像在银行一样!”。

该方法不使用外部验证器服务。 这些任务由路由器本身在内部执行。 连接客户端无需付费。 该方法适用于PC客户端和移动设备。

一般保护方案如下:

  1. 成功连接VPN服务器的用户的内部IP地址将自动列入灰名单。
  2. 连接事件会自动生成一个一次性代码,该代码会使用一种可用方法发送给用户。
  3. 此列表中的地址对本地网络资源的访问受到限制,但“身份验证器”服务除外,该服务正在等待接收一次性密码。
  4. 提供代码后,用户就可以访问网络的内部资源。

第一 我必须面对的最小问题是存储用户的联系信息以便向他发送 2FA 代码。 由于无法在 Mikrotik 中创建与用户相对应的任意数据字段,因此使用现有的“评论”字段:

/ppp 秘密添加名称=Petrov 密码=4M@ngr! 评论=“89876543210”

第二个 问题变得更加严重——交付代码的路径和方法的选择。 目前实施了三种方案: a) 通过 USB 调制解调器发送短信 b) 电子邮件 c) 通过电子邮件发送短信,可供红色蜂窝运营商的企业客户使用。

是的,短信方案会带来成本。 但如果你仔细观察,就会发现“安全总是与金钱有关”(c)。
我个人不喜欢电子邮件方案。 不是因为它要求邮件服务器可供正在验证的客户端使用 - 分割流量不是问题。 但是,如果客户不小心将 VPN 和电子邮件密码保存在浏览器中,然后丢失了笔记本电脑,则攻击者将获得对公司网络的完全访问权限。

因此,我们决定 - 我们使用短信发送一次性代码。

第三 问题出在哪里 如何在 MikroTik 中生成 2FA 的伪随机代码。 RouterOS 脚本语言中没有类似的 random() 函数,我之前见过几个拐杖脚本伪随机数生成器。 由于各种原因,我不喜欢他们中的任何一个。

事实上,MikroTik 中有一个伪随机序列生成器! 在 /certificates scep-server 的上下文中,从表面上看它是隐藏的。 第一种方法 获取一次性密码既简单又简单 - 使用命令 /certificates scep-server otp 生成。 如果我们执行简单的变量赋值操作,我们将获得一个数组值,稍后可以在脚本中使用。

第二个方法 获取一个也易于使用的一次性密码 - 使用外部服务 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 和 SMS 对 VPN 用户进行双因素身份验证

MikroTik 路由器上必须有一个服务可以接受代码并将其与特定客户端进行匹配。 如果提供的代码与预期相符,则客户的地址应包含在某个“白”名单中,该名单中的地址将被允许访问公司的内部网络。

由于服务选择不佳,决定使用 Mikrotik 内置的网络代理通过 http 接受代码。 由于防火墙可以使用动态 IP 地址列表,因此防火墙会执行代码搜索,将其与客户端 IP 进行匹配,并使用 Layer7 正则表达式将其添加到“白”列表中。 路由器本身已被分配一个条件 DNS 名称“gw.local”,已在其上创建静态 A 记录以发布给 PPP 客户端:

DNS
/ip dns static add 名称=gw.本地地址=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. 授权成功后,将客户端浏览器重定向至通知授权成功的页面或图片:

代理配置
/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

我将列出重要的配置元素:

  1. 接口列表“2fa” - 客户端接口的动态列表,来自该接口的流量需要在 2FA 内进行处理;
  2. 地址列表“2fa_jailed”-VPN 客户端隧道 IP 地址的“灰色”列表;
  3. address_list "2fa_approved" - 已成功通过双因素身份验证的 VPN 客户端的隧道 IP 地址“白”列表。
  4. 防火墙链“input_2fa” - 它检查 TCP 数据包是否存在授权代码,并将代码发送者的 IP 地址与所需的 IP 地址进行匹配。 链中的规则是动态添加和删除的。

数据包处理的简化流程图如下所示:

通过 MikroTik 和 SMS 对 VPN 用户进行双因素身份验证

为了对来自“灰”名单中尚未通过第二阶段身份验证的客户端的流量进行第 7 层检查,已在标准“输入”链中创建了一条规则:

代码
/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 服务器或单个用户。 同时,分配给用户的配置文件具有优先权,用其指定的参数覆盖为整个服务器选择的配置文件的参数。

通过这种方法,我们可以为双因素身份验证创建一个特殊的配置文件,并将其分配给那些认为有必要这样做的用户,而不是所有用户。 如果您不仅使用 PPP 服务连接最终用户,而且同时建立站点到站点连接,这可能是相关的。

在新创建的特殊配置文件中,我们将连接用户的地址和接口动态添加到地址和接口的“灰”列表中:

赢宝盒
通过 MikroTik 和 SMS 对 VPN 用户进行双因素身份验证

代码
/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

有必要同时使用“地址列表”和“接口列表”列表来检测和捕获来自 dstnat(预路由)链中非辅助 VPN 客户端的流量。

准备工作完成后,将创建额外的防火墙链和配置文件,我们将编写一个脚本,负责自动生成 2FA 代码和单独的防火墙规则。

文档 wiki.mikrotik.com PPP-Profile 为我们提供了与 PPP 客户端连接-断开事件相关的变量信息 “在用户登录事件上执行脚本。 这些是事件脚本可访问的可用变量:用户、本地地址、远程地址、主叫方 ID、被叫方 ID、接口”。 其中一些对我们非常有用。

PPP 开启连接事件配置文件中使用的代码

#Логируем для отладки полученные переменные 
:log info (

quot;local-address")
:log info (


quot;remote-address")
:log info (


quot;caller-id")
:log info (


quot;called-id")
:log info ([/int pptp-server get (


quot;interface") name])
#Объявляем свои локальные переменные
:local listname "2fa_jailed"
:local viamodem false
:local modemport "usb2"
#ищем автоматически созданную запись в адрес-листе "2fa_jailed"
:local recnum1 [/ip fi address-list find address=(


quot;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 [email protected] [email protected] subject="@".$vphone body=$msgboby }

#Генерируем Layer7 regexp
local vregexp ("otp\/".$comm1)
:local vcomment ("2fa_".(


quot;remote-address"))
/ip firewall layer7-protocol add name=(


quot;vcomment") comment=(


quot;remote-address") regexp=(


quot;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=(


quot;vcomment") protocol=tcp src-address=(


quot;remote-address") dst-limit=1,1,src-address/1m40s

特别是对于那些喜欢盲目复制粘贴的人,我警告您 - 代码取自测试版本,可能包含轻微的拼写错误。 对于一个懂事的人来说,不难弄清楚到底在哪里。

当用户断开连接时,会生成“On-Down”事件,并调用带有参数的相应脚本。 该脚本的任务是清理为断开连接的用户创建的防火墙规则。

PPP 开启-关闭连接事件配置文件中使用的代码

:local vcomment ("2fa_".(

quot;remote-address"))
/ip firewall address-list remove [find address=(


quot;remote-address") list=2fa_approved] /ip firewall filter remove [find chain="input_2fa" src-address=(


quot;remote-address") ] /ip firewall layer7-protocol remove [find name=$vcomment]
然后,您可以创建用户并将全部或部分用户分配给双因素身份验证配置文件。

赢宝盒
通过 MikroTik 和 SMS 对 VPN 用户进行双因素身份验证

代码
/ppp secrets set [find name=Petrov] profile=2FA

它在客户端的外观如何。

建立 VPN 连接后,带有 SIM 卡的 Android/iOS 手机/平板电脑会收到如下短信:

短信
通过 MikroTik 和 SMS 对 VPN 用户进行双因素身份验证

如果直接从手机/平板电脑建立连接,则只需单击消息中的链接即可完成 2FA。 很舒服。

如果 VPN 连接是从 PC 建立的,则用户将需要输入最少的密码形式。 设置 VPN 时,会向用户提供 HTML 文件形式的小表格。 该文件甚至可以通过邮件发送,以便用户保存它并在方便的地方创建快捷方式。 它看起来像这样:

桌子上的标签
通过 MikroTik 和 SMS 对 VPN 用户进行双因素身份验证

用户单击该快捷方式,将打开一个简单的代码输入表单,该表单会将代码粘贴到打开的 URL 中:

屏幕形式
通过 MikroTik 和 SMS 对 VPN 用户进行双因素身份验证

以最原始的形式为例。 有需要的可以自行修改。

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 徽标,这应该表示身份验证成功:

通过 MikroTik 和 SMS 对 VPN 用户进行双因素身份验证

请注意,图像是使用 WebProxy Deny Redirect 从内置 MikroTik Web 服务器返回的。

我想可以使用“热点”工具自定义图像,上传您自己的版本并使用 WebProxy 设置拒绝重定向 URL。

对于那些试图以 20 美元购买最便宜的“玩具”Mikrotik 并用它替换 500 美元路由器的人来说,这是一个很大的要求 - 不要这样做。 像“hAP Lite”/“hAP mini”(家庭接入点)这样的设备的CPU(smips)非常弱,它们很可能无法应对业务部分的负载。

警告! 该解决方案有一个缺点:当客户端连接或断开连接时,配置会发生更改,路由器会尝试将其保存在其非易失性内存中。 由于大量客户端以及频繁的连接和断开连接,这可能会导致路由器内部存储性能下降。

PS:只要你的编程能力足够,向客户端交付代码的方法是可以扩展和补充的。 例如,您可以向 telegram 发送消息或...建议选项!

我希望这篇文章对您有用,并有助于使中小型企业的网络更加安全。

来源: habr.com