Скриптинг VPN-клиента: выберите уровень до пароля
CLI поставщика, rasdial, намерение приложения или автоматизация интерфейса сбоят каждый по-своему. Выберите уровень, держите секрет вне argv и ждите состояния, а не кодов выхода.
Скрипты запускают VPN-клиенты на том уровне, который выбрал автор, и этот уровень редко оказывается тем, который был нужен задаче. Командная строка поставщика, дозвонщик операционной системы и намерение мобильного приложения возвращаются в разные моменты жизни соединения. Они и секрет обрабатывают по-разному, и эта разница решает, виден ли пароль каждому пользователю машины. Выберите уровень до того, как напишете первую строку скрипта.
Уровень 1: командная строка поставщика
Большинство настольных клиентов поставляют командную строку, принимающую имя профиля и, слишком часто, пароль. Старый клиент Cisco принимал глагол connect с аргументами пользователя и пароля; macOS предоставляет встроенную службу через networksetup; NetworkManager отвечает на nmcli. Эти вызовы возвращаются, когда команда завершается, а это не тот момент, когда туннель становится пригодным. Прочитайте документацию поставщика для развёртываемой версии, потому что эти интерфейсы меняются, а некоторые полностью удаляются.
nmcli connection up "Work VPN"
networksetup -connectpppoeservice "Work VPN"
Преимущество в том, что поставщик знает собственное хранилище профилей, поэтому вызов использует ту же конфигурацию, что и GUI. Недостаток в том, что каждый поставщик выдумывает свои глаголы, и скрипт для одного клиента не переносится на другой. Считайте этот уровень кратчайшим путём, когда клиент фиксирован, а версия закреплена.
Уровень 2: собственная служба операционной системы
В Windows дозвонщик является частью операционной системы и работает с записью в телефонной книге, а не с хранилищем профилей поставщика. Это делает его переносимее между клиентами и проще для скриптов из сервисной учётной записи. Это также означает, что соединение может существовать в одном месте и отсутствовать в другом, что путает людей, которые смотрят не в тот список.
rasdial "Work VPN" user pass
Get-VpnConnection -Name "Work VPN" | Select-Object ConnectionStatus
Используйте поверхность PowerShell, когда нужно создать или удалить запись, а не набирать её. Уровни WMI и cmdlet предоставляют одни и те же объекты под разными именами, а удаление — отдельная операция от отключения. Скрипт, который только набирает, оставит устаревшие записи после миграции. Проверьте, в какое хранилище пишет ваша автоматизация, прежде чем обещать, что очистка произойдёт.
Remove-VpnConnection -Name "Old VPN" -Force
Get-VpnConnection
В macOS эквивалентом является список сетевых служб, где существующая служба подключается по имени, а не создаётся на лету.
Учётные данные утекают на каждом уровне, поэтому выбирайте канал
Всё, что вы передаёте в командной строке, появляется в таблице процессов для каждого локального пользователя и обычно переживает и историю оболочки. Поддерживаемая альтернатива OpenVPN — читать секрет из файла с --askpass, и сам файл нуждается в правах, соответствующих доверию, которое вы в него вкладываете. Именованный канал избавляет от записи секрета на диск, но позволяет любому процессу, который может его открыть, писать туда. В Windows храните секрет в диспетчере учётных данных или в файле, который может читать только сервисная учётная запись, и никогда как литерал в batch-файле.
openvpn --config /etc/openvpn/work.conf --askpass /etc/openvpn/work.secret --auth-nocache --daemon
ls -l /etc/openvpn/work.secret
То же правило относится к токенам и одноразовым кодам: если автоматизации они нужны, автоматизации нужен канал, не являющийся аргументом командной строки. Меняйте секрет при смене механизма, потому что старое значение где-то лежало в журнале. Скрипт, который работает и утекает, — не работающий скрипт.
Дождитесь состояния, затем наблюдайте за одним владельцем
Каждый из этих вызовов возвращается до того, как туннель готов, поэтому опрашивайте состояние, которое предоставляет клиент, или проверяйте назначение внутри него с тайм-аутом. Копирование файлов или открытие соединения с базой данных сразу после вызова подключения — это гонка, и она периодически срывается так, что это выглядит как сбой сети.
ip link show tun0
nmcli -t -f GENERAL.STATE connection show "Work VPN"
curl -sS --max-time 5 https://app.internal.example/health -o /dev/null
Отключение имеет ту же проблему в обратную сторону: процесс может завершиться до того, как маршруты исчезнут. Используйте хуки клиента там, где они есть, например скрипты up и down OpenVPN или ExecStartPost юнита systemd, потому что они выполняются в той точке, которая вам действительно важна. Хуки выполняются с привилегиями того, что их вызвало, поэтому хуку, перезапускающему сеть, нужно явное правило, а не предположение. Держите ровно одного владельца на всё время жизни соединения.
Когда два супервизора запускают и останавливают один и тот же профиль, результатом становится цикл переподключения, который читается как сбой провайдера. Runbook заканчивается, когда уровень задокументирован, у секрета есть канал, не являющийся argv, условие ожидания — проверка состояния с тайм-аутом, и жизненным циклом владеет один процесс.