OpenConnect связывает два отдельных обмена одной короткоживущей величиной: веб-вход и туннель VPN. Первый обмен — обычный HTTPS: учётные данные, круговой рейс SAML или токен уходят к шлюзу, а ответ несёт cookie аутентификации. Второй обмен предъявляет это cookie тому же шлюзу как доказательство, что веб-фаза уже произошла, и только тогда поднимается туннель.

Во время входа о туннеле ничего не согласуется. Именно это стоит понять, потому что большинство ошибок в скриптах предполагают, что cookie — это пароль для хранения или ключ, защищающий трафик, тогда как на самом деле это предъявительский билет для конкретной сессии шлюза.

Что клиент и шлюз обменивают, по порядку

Фаза аутентификации выглядит как обычный защищённый сайт. OpenConnect отправляет учётные данные пользователя или следует перенаправлению шлюза к поставщику идентичности, а шлюз отвечает документом, в который встроено cookie аутентификации. В протоколе AnyConnect это значение путешествует в заголовке cookie при последующих запросах, обычно под именем вроде webvpn, и руководство рассматривает его как учётные данные, которые несут сессию дальше.

Вторая фаза — это запрос CONNECT к VPN-эндпоинту шлюза с прикреплённым cookie. Шлюз проверяет его, назначает адрес, и клиент запускает CSTP поверх TLS, а DTLS служит необязательным более быстрым путём данных, когда сессия уже существует. Только в этот момент устанавливаются параметры туннеля, маршруты, настройки DNS и ключи трафика сессии. Cookie провело клиента через дверь; оно ничего не зашифровало.

Эта последовательность объясняет, почему существует openconnect --authenticate. Запустите его, и он выполнит веб-фазу, выведет небольшой блок переменных оболочки вроде COOKIE, HOST, CONNECT_URL и FINGERPRINT, а затем завершится до построения какого-либо туннеля. Скрипт-обёртка может затем начать настоящее соединение с этими значениями, и это именно то разделение, которое нужно развёртыванию без экрана или в двух контейнерах.

Cookie — носитель сессии, а не ключ

Относитесь к значению так, как вы относились бы к идентификатору сессии в веб-приложении. Кто владеет им в течение его жизни, может запросить у шлюза туннель, на который эта сессия имеет право, поэтому его секретность важна. Но копирование не раскрывает трафик, потому что конфиденциальность туннеля берётся из рукопожатия TLS и производных ключей, а не из строки cookie.

Это и не постоянное разрешение. Шлюз и поставщик идентичности решают, сколько живёт сессия, а браузер, который всё ещё показывает страницу входа, ничего не говорит о том, действительна ли сессия туннеля. Повторное подключение после истечения означает повторить веб-фазу и создать свежее cookie, и в этом смысл повторной аутентификации, а не помеха, которую нужно обходить инженерными средствами.

Не рассматривайте cookie как универсальную замену другим учётным данным. Утверждение SAML, пароль браузера и cookie, выданное другой службой, — это разные объекты с разной аудиторией, и вставка одного в поле другого обычно заканчивается неудачей на шлюзе или, что хуже, успехом против неверной границы доверия.

Передача cookie без оставленных копий

Самая безопасная механическая передача в Linux — стандартный ввод. --cookie-on-stdin читает значение из stdin, поэтому cookie никогда не появляется в списке аргументов процесса, где любой локальный пользователь может прочитать его через ps. Конвейер, который аутентифицируется в одном процессе и питает туннель в другом, держит значение в канале, а не в файле или переменной окружения.

Аргументы и переменные окружения слабее по разным причинам. Аргумент виден в таблице процессов всё время жизни клиента и часто попадает в историю оболочки. Переменная окружения наследуется дочерними процессами, а юнит systemd, который задаёт её в файле юнита, хранит секрет в читаемой конфигурации. Если файл всё же нужен, ограничьте его служебной учётной записью и удаляйте по окончании сессии.

Держите и границу привилегий осознанной. Процесс, который разговаривает с поставщиком идентичности и, возможно, открывает браузер, должен работать от имени пользователя, которому принадлежит этот вход. Процессу, который настраивает маршруты, DNS и устройство туннеля, может понадобиться больше привилегий. Запуск всего браузера от root, чтобы его cookie было доступно туннелю, принадлежащему root, — неверный обмен, а современные конфигурации OpenConnect умеют явно передавать значение через эту границу.

Журналирование заслуживает того же подозрения. Подробный вывод клиента, вспомогательные скрипты и диагностика шлюза могут повторять материал запроса, и не всякий уровень журнала скрывает cookie. Записывайте минимальную нужную запись об ошибке, держите её вне общих каналов и проверяйте отредактированный образец, прежде чем приложить его к тикету.

Проверка и истечение во второй фазе

Вторая фаза всё ещё должна проверять сервер. FINGERPRINT, который выводит --authenticate, — это закрепление сертификата в форме pin-sha256:, и передача его вызову туннеля с --servercert удерживает проверку привязанной к тому шлюзу, против которого вы аутентифицировались. Отключение проверки сертификатов ради упрощения скрипта убирает единственное доказательство того, что cookie передаётся нужному хосту.

Когда шлюз отвергает cookie, решение — новый круг аутентификации, а не более длинный кеш. Храните cookie только на время соединения, удаляйте временные файлы при выходе и используйте процедуру удаления сессии поставщика идентичности или шлюза, когда сессия раскрыта или устройство потеряно. Кешированное cookie не является освобождением от MFA.

Прежде чем автоматизировать это, проверьте четыре вещи: туннель работает под правильной учётной записью, cookie приходит через stdin, отпечаток сертификата закреплён, а сессия отбрасывается при закрытии туннеля. Если чего-то не хватает, автоматизация расширила доверие к cookie вместо того, чтобы воспроизвести ручной поток.

Похожие статьи