Служба запускается, читает профиль без замечаний, а затем падает на строке, которая выглядит не связанной с делом: она не может открыть ca.crt. В сертификате ничего не изменилось, а путь к профилю в unit-файле абсолютный. Именно это сочетание и есть подсказка. Переданный вами путь указывает на один файл, а имена внутри этого файла разрешаются совсем в другом месте, поэтому запуск, который выглядит полностью определённым, всё ещё может читать ключи из каталога, который никто не имел в виду.

У сбоя немного причин, и их стоит разделить по порядку: базовый каталог, который процесс действительно имел, контекст запуска службы и права или песочница вокруг файла, когда путь уже верен. Каждая оставляет свой след — в unit-файле, в процессе и в файловой системе.

Путь к профилю — не базовый каталог

Представьте процесс, который стартует в /tmp и получает /etc/vpn/prod/tenant-a.ovpn. Внутри этого профиля строка ca ca.crt задаёт относительный путь. Ничто в аргументе, который нашёл профиль, не переписывает это имя, поэтому процесс ищет /tmp/ca.crt и сообщает об отсутствующем файле.

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

Что разделяет руководство: --config и --cd

Руководство OpenVPN 2.6 описывает две разные опции. Одна загружает файл конфигурации. Другая меняет каталог процесса до чтения конфигурации, ключа, сценария и других файлов и требует абсолютный каталог без ссылки на текущий.

openvpn --cd /etc/vpn/prod --config tenant-a.ovpn

Такой вызов — это форма, а не указание запускать чужой профиль. Что он даёт — детерминизм: база для каждого относительного имени в цепочке указана в команде, а не унаследована от оболочки или менеджера, запустившего процесс. Альтернатива столь же допустима и часто лучше для службы: оставьте рабочий каталог в покое и сделайте каждый упомянутый файл абсолютным внутри профиля.

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

Служба стартует в другом месте

Интерактивная оболочка в каталоге профиля ничего не доказывает о пакетированном unit-файле. Менеджеры задают свой рабочий каталог, а скрипт-обёртка может снова его изменить до запуска бинарника. Падающий unit — единственный запуск, о котором стоит рассуждать.

systemctl show -p WorkingDirectory -p ExecStart --value openvpn@tenant-a
ls -l /proc/$(pgrep -n openvpn)/cwd

Вторая команда показывает каталог, который живой процесс действительно держит, и это надёжнее unit-файла, когда участвует обёртка. Если они расходятся, ответ — обёртка, а unit-файл — ложный след.

После поиска: права, chroot и песочница

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

Три частых варианта дают одну и ту же ошибку. Файл доступен только root, а служба сбрасывает привилегии до его открытия. Unit задаёт защитное представление файловой системы, не включающее каталог профиля. Демон работает с chroot, поэтому путь, существующий на хосте, в этом представлении отсутствует.

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

Докажите исправление собственным запуском

Перезапустите настоящий unit из каталога, не связанного с профилем, и понаблюдайте за той же строкой запуска. Затем убедитесь, что процесс во время работы держит нужный каталог и что загруженный сертификат — ожидаемый.

systemctl restart openvpn@tenant-a
systemctl status openvpn@tenant-a --no-pager
journalctl -u openvpn@tenant-a -n 20 --no-pager

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

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

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