Por qué un hook de OpenVPN no encuentra el archivo junto a su script
Las rutas relativas en un hook se resuelven contra el proceso, no contra el script. Ancle las con __file__ y luego compruebe espacio de nombres e identidad antes que los permisos.
Los hooks que se ejecutan sin error y aun así informan de un archivo ausente suelen mirar en el lugar equivocado, no fallar en su tarea. Las rutas relativas de un script se resuelven contra el directorio de trabajo del proceso, y un servicio arranca con un directorio de trabajo que usted no eligió. El archivo junto al script y el archivo que abre el proceso solo son lo mismo cuando algo los ancla. Esa brecha explica buena parte de los fallos de hook que se leen mal como problemas de permisos.
Compruebe la ruta antes que los permisos, porque un error de permisos sobre el archivo equivocado sigue siendo el archivo equivocado.
Una ruta relativa se resuelve contra el proceso, no contra el archivo
Cuando un script llama a open("policy.json"), el núcleo resuelve el nombre contra el directorio de trabajo actual del proceso. Nada de la ubicación del propio script participa en esa búsqueda. OpenVPN inicia los hooks desde un directorio que él mismo elige, y la opción cd puede cambiarlo, así que un script probado a mano desde su propia carpeta se comporta distinto cuando lo ejecuta el demonio. La prueba manual pasa, la ejecución del servicio falla y la línea de registro solo menciona el nombre ausente.
Python expone la ubicación del script como __file__, y eso es lo que hace posible el anclaje. Path(__file__).resolve().parent / "policy.json" construye una ruta a partir del archivo en lugar del proceso. Esa única línea elimina la dependencia del directorio de trabajo sin cambiar nada de los privilegios.
Ancle la ruta a propósito y luego compruebe qué hace resolve
resolve() sigue los enlaces simbólicos, así que una instalación que enlaza el hook en su sitio se ancla en el destino del enlace, no en el directorio que contiene el enlace. Eso suele ser lo que se quiere para un despliegue versionado, y es incorrecto para una disposición que espera datos junto al enlace simbólico. Decida cuál de las dos cosas significa el despliegue y escriba el código en consecuencia, en lugar de descubrir la diferencia en producción. Path(__file__).parent sin resolve() conserva el directorio propio del enlace.
Si los datos pertenecen a la instancia y no al programa, tome una ruta absoluta de la configuración. Codificar una ubicación fija ata el hook a una sola disposición de host.
Recorra cuatro fronteras en orden
Empiece por el directorio de trabajo: compare el WorkingDirectory, la opción cd de OpenVPN y cualquier envoltorio que cambie de directorio antes del exec. Después confirme la ubicación resuelta, porque un archivo con un nombre parecido en otro despliegue puede responder a la búsqueda y ocultar el error. Luego viene la vista del sistema de archivos: un chroot o un espacio de nombres de montaje del servicio pueden hacer que una ruta absoluta del host sencillamente no exista. Al final, la identidad de acceso, que es una cuestión aparte de la visibilidad.
ProtectHome y PrivateTmp ocultan ubicaciones distintas, y ninguna de las dos aparece en un listado de directorio tomado del host. Una ruta que existe para root en una shell puede ser invisible dentro del espacio de nombres de la unidad, y por eso probar el mismo comando fuera del servicio demuestra tan poco.
Corrija la búsqueda sin ampliar el servicio
Anclar la ruta solo resuelve la primera frontera. No expone un directorio que el servicio oculta ni concede a una cuenta sin privilegios acceso a un archivo solo para root. Subir script-security cambia qué scripts ejecutará OpenVPN; no tiene efecto sobre dónde se resuelve un nombre relativo. Cambiar el directorio de trabajo de todo el servicio es más amplio que el problema, porque cualquier otra ruta relativa de certificado, registro o auxiliar se mueve con él.
Prefiera la corrección estrecha: una ruta determinista en el script, un valor de configuración explícito para los datos de la instancia y una propiedad de archivos que coincida con la cuenta con la que el hook se ejecuta realmente.
Registre pruebas que nombren la frontera
Un informe de fallo útil contiene tres cosas: la ruta exacta que el hook intentó, el entorno que la produjo y qué frontera se negó. Imprima la ruta resuelta y la excepción sin volcar el contenido del archivo. Esa terna separa un anclaje equivocado de un montaje oculto y de una denegación de permisos, que son tres correcciones distintas.
La afirmación de que root puede leer el archivo desde un terminal responde a una de esas preguntas y a ninguna de las otras. Pruebe desde el contexto propio del servicio.