Почему хук OpenVPN не находит файл рядом со своим скриптом
Относительные пути в хуке разрешаются относительно процесса, а не скрипта. Закрепите их через __file__, затем проверьте пространство имён и идентичность раньше прав.
Хуки, которые работают без ошибок и всё же сообщают об отсутствующем файле, обычно смотрят не туда, а не терпят неудачу в своей работе. Относительные пути в скрипте разрешаются относительно рабочего каталога процесса, а служба стартует с рабочим каталогом, который выбрали не вы. Файл рядом со скриптом и файл, который открывает процесс, одно и то же только тогда, когда что-то их закрепляет. Этот зазор объясняет значительную часть отказов хуков, которые ошибочно читаются как проблемы с правами.
Проверьте путь раньше прав, потому что ошибка прав на неправильном файле всё равно остаётся неправильным файлом.
Относительный путь разрешается относительно процесса, а не файла
Когда скрипт вызывает open("policy.json"), ядро разрешает имя относительно текущего рабочего каталога процесса. Ничто из расположения самого скрипта в этом поиске не участвует. OpenVPN запускает хуки из каталога, который выбирает сам, и параметр cd может его изменить, поэтому скрипт, проверенный вручную из своей папки, ведёт себя иначе, когда его запускает демон. Ручной тест проходит, запуск службы срывается, а строка журнала упоминает только отсутствующее имя.
Python раскрывает расположение скрипта как __file__, и именно это делает закрепление возможным. Path(__file__).resolve().parent / "policy.json" строит путь от файла, а не от процесса. Эта одна строка убирает зависимость от рабочего каталога, ничего не меняя в привилегиях.
Закрепите путь осознанно, затем проверьте, что делает resolve
resolve() следует по символическим ссылкам, поэтому установка, которая связывает хук на месте, закрепляется на цели ссылки, а не на каталоге, содержащем ссылку. Обычно это то, что нужно для версионированного развёртывания, и это неверно для раскладки, которая ожидает данные рядом с символической ссылкой. Решите, что имеет в виду развёртывание, и напишите код соответственно, а не обнаруживайте разницу в продакшене. Path(__file__).parent без resolve() сохраняет собственный каталог ссылки.
Если данные принадлежат экземпляру, а не программе, возьмите абсолютный путь из конфигурации. Жёсткое кодирование места привязывает хук к одной раскладке хоста.
Пройдите четыре границы по порядку
Начните с рабочего каталога: сравните WorkingDirectory, параметр cd OpenVPN и любую обёртку, которая меняет каталог перед exec. Затем подтвердите разрешённое расположение, потому что файл с похожим именем в другом развёртывании может ответить на поиск и скрыть ошибку. Дальше идёт взгляд файловой системы: chroot или пространство имён монтирования службы могут просто сделать абсолютный путь хоста отсутствующим. Последняя идентичность доступа, это вопрос отдельный от видимости.
ProtectHome и PrivateTmp скрывают разные места, и ни одно из них не появляется в листинге каталога, снятом с хоста. Путь, который существует для root в оболочке, может быть невидим внутри пространства имён юнита, и поэтому проверка той же команды вне службы доказывает так мало.
Исправьте поиск, не расширяя службу
Закрепление пути решает только первую границу. Оно не открывает каталог, который скрывает служба, и не даёт непривилегированной учётной записи доступ к файлу, доступному только root. Повышение script-security меняет, какие скрипты будет запускать OpenVPN; на то, где разрешается относительное имя, это не влияет. Смена рабочего каталога для всей службы шире проблемы, потому что каждый другой относительный путь сертификата, журнала или помощника движется вместе с ним.
Предпочитайте узкое исправление: детерминированный путь в скрипте, явное значение конфигурации для данных экземпляра и владение файлами, соответствующее учётной записи, под которой хук действительно работает.
Записывайте доказательства, которые называют границу
Полезный отчёт о сбое содержит три вещи: точный путь, который попробовал хук, среду, которая его создала, и какая граница отказала. Напечатайте разрешённый путь и исключение, не выливая содержимое файла. Эта тройка отделяет неверное закрепление от скрытого монтирования и от отказа в правах, а это три разных исправления.
Утверждение, что root может прочитать файл из терминала, отвечает на один из этих вопросов и ни на один из остальных. Вместо этого проверяйте из собственного контекста службы.