Чому хук 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 може прочитати файл з термінала, відповідає на одне з тих питань і на жодне з інших. Натомість перевіряйте з власного контексту служби.