OpenVPN no encuentra ca.crt aunque el perfil se cargue
La ruta del perfil no es la base de los archivos que contiene. Vea por qué un --config absoluto sigue fallando en ca.crt y cómo la ejecución decide la respuesta.
El servicio arranca, lee el perfil sin quejarse y luego muere en una línea que parece no tener relación: no puede abrir ca.crt. Nada del certificado ha cambiado, y la ruta del perfil en la unidad es absoluta. Esa combinación es la pista. La ruta que pasó localiza un archivo, y los nombres dentro de ese archivo se resuelven en otro lugar por completo, así que un arranque que parece plenamente cualificado puede seguir leyendo sus claves desde un directorio que nadie pretendía.
El fallo tiene pocas causas, y conviene separarlas por orden: el directorio base que el proceso tenía en realidad, el contexto de arranque del servicio y los permisos o el sandbox alrededor del archivo una vez que la ruta es correcta. Cada una deja un rastro distinto: en la unidad, en el proceso y en el sistema de archivos.
La ruta del perfil no es un directorio base
Imagine un proceso que arranca en /tmp y recibe /etc/vpn/prod/tenant-a.ovpn. Dentro de ese perfil, la línea ca ca.crt nombra una ruta relativa. Nada en el argumento que localizó el perfil reescribe ese nombre, así que el proceso busca /tmp/ca.crt e informa de un archivo que falta.
Lo confuso es el orden de los errores. OpenVPN analiza primero la configuración y abre los archivos referenciados a medida que llega a ellos, así que una errata en el perfil se reporta antes de tocar ninguna clave, mientras que un certificado ausente solo aparece cuando el análisis llega hasta ahí. Por tanto, un perfil que carga es prueba de que la sintaxis es correcta, no de que se hayan encontrado los archivos que nombra.
Qué separa el manual: --config y --cd
El manual de OpenVPN 2.6 describe dos opciones distintas. Una carga un archivo de configuración. La otra cambia el directorio del proceso antes de que se lean la configuración, la clave, el script y otros archivos, y exige un directorio absoluto sin referencia al actual.
openvpn --cd /etc/vpn/prod --config tenant-a.ovpn
Esa invocación es una forma, no una instrucción para lanzar el perfil de otro. Lo que aporta es determinismo: la base de cada nombre relativo de la cadena se declara en el comando en lugar de heredarse de la shell o del gestor que haya iniciado el proceso. La alternativa es igual de válida y a menudo mejor para un servicio: deje el directorio de trabajo en paz y haga absoluto cada archivo referenciado dentro del perfil.
Elija lo que elija, audite toda la cadena y no el primer fallo. Un perfil puede incorporar otro archivo de configuración, y los nombres relativos del archivo incluido se resuelven contra el mismo directorio del proceso, no contra el directorio del archivo que los incluyó. Las rutas de registro, los archivos de autenticación y los scripts auxiliares siguen la misma regla.
El servicio arranca en otro sitio
Una shell interactiva en el directorio del perfil no demuestra nada sobre una unidad empaquetada. Los gestores fijan su propio directorio de trabajo, y un script envoltorio puede volver a cambiarlo antes de que se ejecute el binario. La unidad que falla es el único arranque sobre el que vale la pena razonar.
systemctl show -p WorkingDirectory -p ExecStart --value openvpn@tenant-a
ls -l /proc/$(pgrep -n openvpn)/cwd
El segundo comando muestra el directorio que el proceso vivo mantiene en realidad, algo más fiable que el archivo de la unidad cuando hay un envoltorio. Si ambos no coinciden, el envoltorio es su respuesta y la unidad es una pista falsa.
Tras la búsqueda: permisos, chroot y sandbox
Un directorio base correcto puede producir igualmente el mismo mensaje, porque resolución y acceso son pasos separados. El proceso necesita permiso de ejecución en cada directorio padre y permiso de lectura en el archivo, y una unidad de servicio que oculta parte del sistema de archivos cambia lo que el proceso puede ver siquiera.
Tres variantes habituales producen el mismo error. El archivo solo es legible por root mientras el servicio cede privilegios antes de abrirlo. La unidad establece una vista protectora del sistema de archivos que no incluye el directorio del perfil. El demonio se ejecuta con chroot, así que la ruta que existe en el host no existe dentro de esa vista.
Diagnostique con la propiedad del archivo y la identidad del proceso, no aflojando permisos. Hacer que una clave privada sea legible por todos para acallar este error cambia un fallo ruidoso por uno silencioso, y la clave es la parte del directorio que menos desea exponer.
Demuestre la corrección con el arranque propio
Reinicie la unidad real desde un directorio sin relación con el perfil y observe la misma línea de arranque. Después confirme que el proceso mantiene el directorio previsto mientras se ejecuta y que el certificado que cargó es el que espera.
systemctl restart openvpn@tenant-a
systemctl status openvpn@tenant-a --no-pager
journalctl -u openvpn@tenant-a -n 20 --no-pager
Cuando la carga de rutas tiene éxito, la autenticación y el enrutamiento siguen siendo etapas separadas con sus propios fallos, y una conexión que sube pero no pasa tráfico es otra investigación. La corrección de aquí solo trata de de qué directorio vinieron los archivos, y debería verse en la invocación en lugar de recordarla quien la ejecutó a mano la última vez.
Si el perfil se despliega con un gestor de configuración, ponga también allí el directorio base o los nombres absolutos de los archivos. Una corrección manual en un solo host es la versión de este problema que vuelve tras la siguiente reconstrucción.