OpenVPN не находит ca.crt, хотя профиль загружается
Путь к профилю не является базой для файлов внутри него. Узнайте, почему абсолютный --config всё равно не находит ca.crt и как запуск определяет ответ.
Служба запускается, читает профиль без замечаний, а затем падает на строке, которая выглядит не связанной с делом: она не может открыть 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
Когда загрузка путей удалась, аутентификация и маршрутизация по-прежнему отдельные этапы со своими сбоями, а соединение, которое поднимается, но не пропускает трафик, — другое расследование. Исправление здесь касается только того, из какого каталога пришли файлы, и оно должно быть видно в вызове, а не вспоминаться тем, кто последний раз запускал вручную.
Если профиль разворачивается менеджером конфигурации, положите туда же базовый каталог или абсолютные имена файлов. Ручное исправление на одном хосте — версия этой проблемы, которая возвращается после следующей пересборки.