Проблема: рутина при смене сетей
Когда постоянно мотаешься между отделами или часто работаешь из дома, ручное переключение VPN быстро начинает бесить. Ты берешь ноутбук, переходишь в другую зону, цепляешься к новому Wi-Fi — и туннель, естественно, отваливается. Приходится каждый раз лезть в клиент и переподключаться руками.
Хотелось получить простое решение: чтобы ноут сам понимал, где он сейчас находится — внутри доверенной корпоративной сети (где VPN не нужен) или снаружи, и автоматически поднимал туннель без моего участия.
Почему Планировщик задач, а не скрипт в фоне? Первая мысль при решении таких задач — написать скрипт с бесконечным циклом, закинуть его в фон, и пусть он проверяет сеть каждую минуту. Но это классический костыль. Постоянно крутящийся скрипт впустую ест ресурсы.
Правильнее ловить ивенты самой операционной системы. Когда Windows успешно подключается к любой сети (неважно, Wi-Fi это или патч-корд), служба NetworkProfile генерирует в системном журнале событие с ID 10000. Мы просто вешаем запуск нашего PowerShell-скрипта на этот триггер через Task Scheduler. В итоге скрипт спит и «дергается» только в момент реальной смены сети.
Как общаться с vpncli.exe У AnyConnect есть консольная утилита vpncli.exe. Сложность в том, что она интерактивная. При попытке подключения она начинает сыпать вопросами в консоль: просит подтвердить недоверенный сертификат, выбрать группу доступа, ввести логин и пароль.
Чтобы автоматизировать это, мы формируем в скрипте одну строку со всеми ответами по порядку, разделяя их переносом строки (символ `n). Затем просто скармливаем эту матрицу ответов утилите через конвейер, используя ключ -s для чтения стандартного ввода.
Безопасность: как не засветить пароль Хранить пароль от VPN в открытом текстовом файле или внутри самого скрипта нельзя. Заюзаем встроенный механизм Windows — DPAPI.
Мы один раз вводим пароль руками, скрипт превращает его в SecureString и сохраняет в файл. Фишка в том, что винда шифрует эту строку ключом вашей текущей учетной записи. Этот файл физически невозможно расшифровать, если скопировать его на другой комп или попытаться прочитать из-под другого пользователя. Основной скрипт будет на лету читать этот файл, расшифровывать пароль в оперативную память на долю секунды, передавать в AnyConnect и тут же затирать переменные. Само собой это тоже не супер защищенный момент но щито поделать-десу.
Пошаговая настройка и финальный скрипт
Шаг 1. Создаем файл с зашифрованным паролем Откройте PowerShell (с теми правами, под которыми вы работаете) и выполните эту команду. Она запросит пароль и сложит зашифрованный хеш в текстовик:
Тык
$cred = Read-Host -Prompt "Введите пароль от VPN" -AsSecureString
$cred | ConvertFrom-SecureString | Set-Content -Path "C:\Scripts\vpn_pass.txt" -Force
Шаг 2. Основной скрипт Создаём файл C:\Scripts\AutoVPN.ps1 и контрол-вэшним в него этот код. Главное не забыть вписать свои названия банковских сетей в массив $bankNetworks что бы исключить их их этого цирка.
Тык
# Массив имен доверенных сетей (SSID или домены)
$bankNetworks = @("Bank_WiFi_1", "Bank_WiFi_2", "Corp_LAN_Domain")
$vpncli = "C:\Program Files (x86)\Cisco\Cisco AnyConnect Secure Mobility Client\vpncli.exe"
$vpnProfile = "11.22.33.44:1234"
$username = "username"
$encryptedPassPath = "C:\Scripts\vpn_pass.txt"
$currentNetworks = Get-NetConnectionProfile | Select-Object -ExpandProperty Name
$isBankNetwork = $false
foreach ($net in $currentNetworks) {
if ($bankNetworks -contains $net) {
$isBankNetwork = $true
break
}
}
# Если активно подключение к одной из сетей банка - прерываем выполнение
if ($isBankNetwork) {
Exit
}
$vpnState = & $vpncli state
# Если туннель уже поднят - прерываем выполнение (защита от зацикливания)
if ($vpnState -match "state: Connected") {
Exit
}
# Читаем файл как единую строку и отсекаем возможные невидимые символы
$encryptedString = (Get-Content $encryptedPassPath -Raw).Trim()
$secureString = ConvertTo-SecureString $encryptedString
$bstr = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($secureString)
$plainPassword = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($bstr)
[System.Runtime.InteropServices.Marshal]::ZeroFreeBSTR($bstr) # Очистка памяти BSTR
$vpnInput = "y`n1`n$username`n$plainPassword"
# Передача матрицы в стандартный ввод vpncli
$vpnInput | & $vpncli -s connect $vpnProfile
$plainPassword = $null
$vpnInput = $null
Шаг 3. Настройка Планировщика задач (Task Scheduler)
Ну, тут все по классике, единственное что посоветую, делать delay на секунд 15 на случай если получение IP от маршрутизатора затянется.
В целом полезно. Спасибо за внимание!
