Скрипт, который запускает OpenVPN и так и не подключается, обычно не проблема сети: клиент стоит у приглашения, которое негде нарисовать. OpenVPN имеет ровно два поддерживаемых способа ответить на это приглашение без человека — файл учётных данных за auth-user-pass и управляющий сокет, — и они не взаимозаменяемы. Файл стоит одной директивы и отвечает на одно приглашение; сокет отвечает на любое приглашение и даёт всему, что умеет открыть порт loopback, неаутентифицированный канал управления. Что из этого нужно вашему развёртыванию, зависит от того, какое приглашение действительно блокирует, и от того, какой секрет вы можете позволить себе оставить в покое.

Назовите приглашение, прежде чем выбирать механизм

Четыре приглашения выглядят на терминале одинаково. Пара учётных данных приходит из настройки PAM или auth-user-pass-verify сервера, и на неё отвечает auth-user-pass. Парольная фраза закрытого ключа принадлежит файлу ключа и читается через --askpass или вводится вручную. Пакет PKCS#12 запрашивает собственный пароль. Аутентификация прокси использует auth-файл, который принимает --http-proxy, а не учётные данные аккаунта.

Когда OpenVPN работает как дочерний процесс systemd, PHP или Node, терминала нет, поэтому любое из этих приглашений блокирует молча. Процесс стоит, журнал прекращается после строки шифра, и модуль в конце концов упирается в тайм-аут; эта тишина — причина, по которой люди добавляют цикл повторов или sleep вместо того, чтобы посмотреть, какое приглашение ждёт. Ответить на все четыре может только управляющий сокет. Файл учётных данных отвечает на первое и ни на что больше.

auth-user-pass: один файл, одно приглашение

Файл содержит имя пользователя в первой строке и пароль во второй, и ничего больше: без кавычек, без имён ключей, без комментария. Задайте режим 600 и сделайте владельцем ту учётную запись, которая запускает openvpn; для службы это часто отдельный пользователь, а не ваша учётная запись. Файл, принадлежащий root, под /root — это разница между командой, которая работает в вашей оболочке, и модулем, который падает при загрузке.

Две детали превращают правильный файл в приглашение. Редактор, сохраняющий CRLF, оставляет приклеенный к паролю возврат каретки, поэтому сервер отклоняет пароль, который в файле выглядит правильным; проверяйте через cat -A или od -c, а не на глаз. А auth-user-pass без аргумента внутри профиля по замыслу означает «спросить пользователя», и вторая директива в командной строке может этого не переопределить, потому что профиль разбирается там, где лежит.

Порядок опций — третья ловушка, и он зависит от версии. Некоторые сборки OpenVPN 2.4 игнорируют --auth-user-pass, когда он стоит перед --config, поэтому те же две опции в другом порядке подключаются. Поместите директиву в профиль или после --config в командной строке и перед любыми другими изменениями поищите в профиле все вхождения.

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

Управляющий сокет: любое приглашение, больше поверхности

Запустите клиент со слушателем и явно объявите, что приглашения будут сообщаться на нём, используя директивы, задокументированные в руководстве OpenVPN 2.6:

management 127.0.0.1 3030
management-query-passwords

Из bash держите соединение на свободном файловом дескрипторе и отвечайте по имени приглашения, а не по позиции, потому что переподключение может спрашивать в другом порядке:

exec 3<>/dev/tcp/127.0.0.1/3030
while read -r -u 3 line; do
  case "$line" in
    *"Need 'Auth' username/password"*)
      echo 'username "Auth" alice' >&3
      echo 'password "Auth" secret' >&3 ;;
    *"Need 'Private Key' password"*)
      echo 'password "Private Key" secret' >&3 ;;
  esac
done

Граница — транспорт. Протокол управления — открытый текст по TCP на loopback, поэтому слушатель нельзя публиковать на адресе LAN или туннеля, чтобы до него дотянулся удалённый контроллер; удалённый контроллер означает перенаправление SSH, а не более широкую привязку. Демон сообщества принимает и только одного клиента управления за раз, поэтому GUI или монитор не могут делить сокет с вашим скриптом. Второе соединение отклоняется, а не ставится в очередь, и сбой выглядит как скрипт, который оглох.

Этот сокет — ещё и канал управления. Без пароля управления всё, что может подключиться к 127.0.0.1:3030, может читать состояние, убить туннель или само ответить на приглашение. Пароль управления и файл учётных данных — секреты одного класса, и оба заслуживают одинаковой заботы.

Что подходит развёртыванию

Выбирайте файл учётных данных, когда единственное приглашение — пара учётных данных, клиент работает как служба со стабильным владельцем, и вы можете держать файл 600 вне резервных копий, которые не контролируете. Выбирайте управляющий сокет, когда в игре парольная фраза ключа или пароль PKCS#12, когда обёртка уже присматривает за процессом, или когда нужно наблюдать состояние и приглашения при отладке переподключения.

Два условия двигают решение. Если обёртка — /bin/sh, модуль systemd, вызывающий бинарник напрямую, или минимальный контейнер, клей для сокета только на bash недоступен, и файл — более дешёвый ответ. Если секрет часто вращается, ни один файл не является хорошим домом для него: запросите связку ключей из маленькой обёртки и позвольте модулю вызывать обёртку, что держит ротацию в одном месте, а пароль — вне истории оболочки.

Подстановка процессов стоит между ними и не принадлежит ни одному. Однострочник ниже работает потому, что bash раскрывает его до запуска openvpn, а не потому, что клиент понимает синтаксис:

openvpn --config client.ovpn --auth-user-pass <(printf "%s
%s
" "$USER" "$PASS")

Он ломается под /bin/sh, в модуле, обходящем оболочку, и в образах без bash или /dev/fd. Держите его для интерактивных тестов и никогда — как контракт, от которого зависит служба.

Защитите секрет, затем проверьте по журналу

Оба механизма оставляют секрет, читаемый процессом OpenVPN. Никогда не помещайте пароль в командную строку или в файл модуля, где ps и systemctl показывают его каждому локальному пользователю; передавайте путь к файлу, а не значение внутри. Там, где сертификаты могут заменить пару учётных данных, это убирает категорию приглашения, а не прячет её.

Правильный запуск показывает завершение последовательности приглашений и выход сеанса на «Initialization Sequence Completed». Если клиент останавливается на том же приглашении, сопоставление не удалось: напечатайте точные строки, которые отправил сокет, проверьте кавычки и имя приглашения и исправьте шаблон вместо добавления sleep. Туннель, который сообщает об успехе и ничего не несёт, — это проблема маршрутизации или push, а не учётных данных.

Затем проверьте переподключение, а не только первый запуск. Сбросьте сеанс на стороне сервера или дождитесь его тайм-аута; клиент спросит снова, и именно там файл, который при загрузке выглядел правильным, покажет, читается ли он ещё той учётной записью, которая переподключается.

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