27 мая 2020 г.

Проект #31. Переводим пользователей на удаленку

Корона внесла неожиданные изменения в нашу работу, приходится подстраиваться. Крупные организации, или кого не зацепило спокойно скупили ноутбуки сотрудникам и радуются жизни. В нашем случае пришлось искать другие пути.
Кто-то пользуется Teamviewer`ом, ну что ж, флаг им в руки, а я буду держаться подальше по следующим причинам:
  1. Программа платная (использование за пределами одной сети = коммерческое использование)
  2. Использование 3 точки в цепочке
  3. Повышенная нагрузка на сервис
  4. Режим работы без блокировки монитора
  5. и т.д.

Можно использовать RDP, хорошо, но:
  1. Нужно иметь сервер терминалов или подключаться к пользовательским ПК
  2. Проброс порта RDP не помеха для сканера, гигантская дыра в безопасности
  3. Пробросить порты на RDP на 5 машин можно, но на 50+ и без статики на тачках - такое себе
  4. и т.д.
На ум напрашивается VPN и пущай пользователь как-то подключается к ресурсу (тот же Teamviewer,LiteManager, RDP)
На всех объектах используется роутер Mikrotik.
Я избрал следующий путь для одного объекта:
  1. Настраиваем ПК пользователей
    1. Включаем Wake-On-LAN и проверяем
    2. Персонифицируем имена компьютеров (допустим по фамилии сотрудника)
    3. Включаем RDP (напоминаю, он есть только в редакциях PRO и выше)
    4. Проверяем настройки файервола и антивируса, чтобы они не блокировали трафик для удаленного доступа от пула адресов VPN сервера
  2. Настраиваем роутер:
    1. Пул DHCP сервера должен быть отличен от 192.168.Х.Х, т.к. у пользователей дома именно такая сеть.
    2. Фиксируем лизы для компьютеров
    3. Настраиваем L2TP IPsec PSK (или другой VPN сервер на роутере)
    4. Создаем пользователей (имя пользователя соответствует имени его ПК)
    5. Проверяем настройки файервола, чтобы трафик из сети VPN ходил в сеть организации
    6. В разделе PPP>Profiles>НашПрофиль>вкладка Scripts, здесь мы пишем скрипт в On Up (получаем MAC,шлем магический пакет):
      :local macAddress [/ip dhcp-server lease get [/ip dhcp-server lease find host-name=$user] mac-address];
      tool wol interface=bridge mac=$macAddress
      :log info message=("User " . $user . " connetcted. Sending magic packet for MAC="  . $macAddress)
  3. Настраиваем подключение с личного ПК пользователя, обязательно отключаем "Использовать как основной шлюз"
На другом объекте машин 50+, и DHCP сервер находится на контроллере домена, хоть шлюзом и выступает Mikrotik. Почесав репу, вспомнил что список MAC-NAME_PC у меня есть на сервере для бэкапов. Вот и решил его использовать. Логика остается та же:
  1. Имя пользователя ПК=имени ПК=имя пользователя VPN
  2. При подключении находим MAC, отправляем магический пакет
  3. Если через 60 секунд машина не пингуется, то сбросить подключение
  4. Иначе добавить правило в файервол, разрешающее трафик на порт 3389 от конкретного пользователя к конкретному ПК
  5. При отключении удалить правило
Мысль огонь, мне зашло как надо, но в ходе реализации я столкнулся с кучей проблем:
  1. Язык Mikrotik оставляет желать лучшего, аналога awk нету
  2. Область действия переменных. Т.к. итоговый скрипт длинный, я решил его записать в разделе System>Script, но для того чтобы его вызвать при подключении пользователя, нужно объявить переменные (параметры для скрипта) как глобальные. Глобальные переменные работают на все подключения и тут возникает гигантская коллизия. Пришлось при получении параметра сразу уничтожать глобальную переменную.
  3. Попытка разбора файла на сервере с UNIX системой потерпела фиаско из-за требования входить на сервер по сертификату
  4. и т.д.
В итоге листинг скрипта:

#get name user, resolve, wol, ping, add rule
#Example run
#:global Iuser $user;:global IremoteAddress $"remote-address";/system script run plug_user_local
#Two input param 
:global Iuser;
:global IremoteAddress;

:put ("Input: " .$Iuser. " ". $IremoteAddress)
:local user [$Iuser]
:local remoteAddress [$IremoteAddress];

# Remove global var
/system script environment remove IremoteAddress
/system script environment remove Iuser

:local dnsServer "10.20.0.2"
:local HOST ($user . ".mydomain.ru");
:local PINGCOUNT "2";
:local CodeExitPing;
:local ipAdr;
:local macAddr;

:local patern $user;
:local FileName "compoff2.txt"; 

:local StatusCode "1"

:put ("LocalInput: " .$user. " ". $remoteAddress)
:put ("plug: " . $HOST  . " ". $PINGCOUNT  . " ". $dnsServer)
#####################
##FUNC##

###GETMAC
:local getMac do={
 :local macAddr "";
 :do { [/file get [/file find name=$FileName]]} on-error={:set macAddr "0"}
 :put ("FN " . $FileName . " " .$patern)
 if ([ :len $FileName]!=0 and [ :len $patern ]!=0 and  [/file get [/file find name=$FileName] size] != 0 and $macAddr="") do={
  :put ("getMac begin")
  :local content [/file get [/file find name=$FileName] contents] ;
  :local contentLen [ :len $content ] ;  
  :local lineEnd 0;
  :local line "";
  :local lastEnd 0;   
  :do {
   :set lineEnd [:find $content "\n" $lastEnd ] ;
   :set line [:pick $content $lastEnd $lineEnd] ;
   :set lastEnd ( $lineEnd + 1 ) ;   
   :if ( [:pick $line 0 1] != "#" ) do={   
    :local entry [:pick $line 0 $lineEnd ]
    :if ( [:len $entry ] > 0 ) do={
     if ([:find $entry $patern]>0) do={
      :set macAddr [:pick $entry ([:len $entry]-20) ([:len $entry]-3)]
      #break
      :set lastEnd ( $contentLen + 1 ) ;   
     }
    }
   }
  } while ($lastEnd < $contentLen)
 }
 if ($macAddr="") do={:set macAddr "0"}
 :return $macAddr
}
###checkPing
:local checkPing do={
 :put ("ping: " . $HOST ." ". $PINGCOUNT ." ". $dnsServer)
 :local ipAdr;
 :local CodeExitPing;
 if ( $HOST != "" and $PINGCOUNT != "" and $dnsServer != "") do={
  :do {:set ipAdr [:resolve $HOST server=$dnsServer]} on-error={:set ipAdr "0"};
  if ($ipAdr=0) do={
   :set CodeExitPing "0"
  } else {
   :set CodeExitPing [/ping $ipAdr interval=1 count=$PINGCOUNT];
  }
 } else={
  :set CodeExitPing "0";
  :put "Empty param"
 }
 :return $CodeExitPing
}

###FUNC##
#####################

:set CodeExitPing [$checkPing HOST=$HOST PINGCOUNT=$PINGCOUNT dnsServer=$dnsServer]
:put ("Exit code " . $CodeExitPing);
if ($CodeExitPing=0) do={
 set macAddr [$getMac patern=$patern FileName=$FileName]
 :put ("macAddr " . $macAddr)
 if ($macAddr != 0) do={
  :put ("|" . $macAddr . "|")
  /tool wol mac=$macAddr interface=LAN_Bridge
  :put "Delay 60s"
  :log info message=("Send magic packet on mac:".$macAddr.", for user ".$patern)
  :delay delay-time=60
  :set CodeExitPing [$checkPing HOST=$HOST PINGCOUNT=$PINGCOUNT dnsServer=$dnsServer]
  if ($CodeExitPing>0) do={
   :set StatusCode "0"
  }
 }
} else={
 :put "if else"
 :set StatusCode "0"
}

:put ("Pre error" . $StatusCode)

if ($StatusCode = 0) do={
 :set ipAdr [:resolve $HOST server=$dnsServer]
 :local email "it@mydomain.ru"
 /tool e-mail send to=$email subject="[VPN_RDP] User $user connected" body="User $user connected at $[/system clock get time].\r\nIP-address - $"caller-id".\r\nInfo - http://apps.db.ripe.net/search/query.html?searchtext=$"caller-id""
 :log info message=("Conected " . $user . " ip pc_office" . $ipAdr . " ip pc_vpn " . $remoteAddress)
 :local p [/ip firewall filter find comment~"VPN_AUTO_END"]; #Специально созданное правило-метка (оно еще разрешает DNS запросы)
 :local comm ("VPN_auto.".$user . "." . $remoteAddress) #унифицированная метка правила
 /ip firewall filter  add action=accept chain=forward comment=$comm dst-port=3389 out-interface=LAN_Bridge protocol=tcp src-address=$remoteAddress dst-address=$ipAdr
 /ip firewall filter  add action=accept chain=forward comment=$comm dst-port=3389 out-interface=LAN_Bridge protocol=udp src-address=$remoteAddress dst-address=$ipAdr
 /ip firewall filter move [find comment=$comm] destination=$p
} else={
 :log info message=("PC not found" . $remoteAddress)
 /ppp active remove [find where name=$user]
}

И собственно в On up:

:global Iuser $user;
:global IremoteAddress $"remote-address";
/system script run plug_user_local

И собственно в On down:

:local email "it@mydomain.ru"
/tool e-mail send to=$email subject="[VPN_RDP] User $user disconnected" body="User $user disconnected at $[/system clock get time]."
:local comm ("VPN_auto.".$user . "." . $"remote-address"); /ip firewall filter remove [find comment=$comm]


UP 27/05/2020
Забыл добавить, в последнем варианте при помощи GPO включается RDP на клиентских машинах и в локальную группу "Пользователи удаленного рабочего стола" добавляется разрешенный пользователь.
Именно для этого и было создано соответствие Имя Пользователя ПК (домена)=Имени ПК
Политика нацелена на ПК входящие в группу разрешенных и логика выглядит так:
Если ПК в группе Разрешенные, то добавить пользователя с именем mydomain\%computername% в группу



Комментариев нет:

Отправка комментария