Cuarenta archivos .ovpn de un país llegan de un proveedor, y el plan parece obvio: copiar uno al azar en su sitio y luego iniciar el túnel. Escrito como una tubería dentro de ExecStart, ese plan muere en el arranque con un código de estado que no explica nada. El archivo de unidad no es un script, y la reparación consiste en traer un intérprete de órdenes de forma explícita o en dejar de elegir archivos por completo.

El síntoma: una unidad que arranca y muere de inmediato

El estado muestra que el servicio falló en una fracción de segundo, y la línea del registro nombra un comando que ningún intérprete de órdenes habría recibido jamás. No aparece nada sobre el túnel, porque el túnel nunca comenzó.

Leer la línea de comandos exacta del registro es la forma más rápida de confirmarlo. systemd repite lo que ejecutó, y la cadena mostrará los caracteres de tubería como argumentos literales en lugar de operadores.

Por qué ExecStart no puede ejecutar una tubería

systemd divide la línea de comandos por sí mismo, aplica su propio entrecomillado y sustitución de variables, y luego ejecuta el resultado directamente. Las tuberías, la redirección, la expansión de patrones por parte de un intérprete de órdenes y la sustitución de comandos simplemente no forman parte de esa gramática. Es una decisión de diseño y no una omisión: una unidad declara qué ejecutar, y la lógica de intérprete de órdenes pertenece a un script que la unidad invoca. Ambas reparaciones se derivan de eso, y ninguna exige dar más privilegios a la unidad.

También explica el código de salida engañoso. El primer argumento se trata como el binario, así que una línea mal formada falla antes de que se ejecute nada de su lógica.

Haga la selección en ExecStartPre

La reparación directa es un paso de preparación que llama a un intérprete de órdenes de forma explícita, manteniendo la selección y el arranque en la misma unidad para que un fallo en uno no deje al otro funcionando solo.

[Service]
Type=notify
ExecStartPre=/bin/bash -c "ls /etc/openvpn/profiles/au* | shuf -n 1 | xargs -r -I{} cp -f {} /etc/openvpn/%i.conf"
ExecStart=/usr/sbin/openvpn --config /etc/openvpn/%i.conf

Tres detalles deciden si esto sobrevive al contacto con una máquina real. El nombrado es la primera trampa: systemd sustituye %i mientras analiza la unidad, mucho antes de que bash se ejecute, así que el especificador debe quedar fuera de las comillas simples. xargs -r impide que cp se ejecute sin argumento cuando el patrón no coincide con nada. Y un paso de preparación fallido detiene la unidad, que es el resultado correcto: arrancar con la configuración de ayer es peor que no arrancar. Una selección aleatoria que siempre toma el mismo archivo normalmente no es mala suerte. Compruebe si el archivo copiado está siendo sobrescrito por una actualización del paquete, o si el patrón coincide con una sola entrada obsoleta.

O deje que OpenVPN elija el extremo

OpenVPN ya puede tener varios extremos en un perfil y elegir entre ellos al azar, lo que elimina por completo el intérprete de órdenes, la copia y el acceso de escritura de la ruta de arranque.

client
remote-random
<connection>
remote au1.example.net 1194 udp
</connection>
<connection>
remote au2.example.net 1194 udp
</connection>

Esta forma mantiene la elección de servidor dentro del archivo que redactó el proveedor, así que una rotación que renombra archivos no rompe la unidad. También hace auditable la lista de candidatos, algo que un patrón sobre un directorio nunca es. La mezcla sigue mereciendo su lugar cuando los perfiles difieren en más que un extremo: certificados distintos, directivas de enrutamiento distintas, política de cifrado distinta. En ese caso la elección es entre configuraciones enteras y un paso de intérprete de órdenes es la manera honesta de expresarlo.

Vuelva a leer la instancia y el archivo que utilizó

Confirme qué unidad se ejecuta realmente antes de depurar su contenido. El servicio meta empaquetado y la plantilla son infraestructura; las unidades que corresponden a sus archivos son las instancias que llevan su nombre.

systemctl list-units 'openvpn*'
systemctl cat [email protected]
journalctl -u openvpn@au -n 30 --no-pager

Una instancia que nadie recuerda haber activado suele remontarse a un archivo de configuración perdido en el directorio del paquete. Desactive esa instancia con nombre en lugar de la plantilla, de lo contrario la siguiente actualización del paquete la traerá de vuelta. Después del cambio, inicie la unidad y lea la línea de comandos que informa el registro, y luego confirme la ruta de configuración que realmente abrió. Esa única comprobación separa una selección aleatoria que funciona de un servicio que reutiliza en silencio un archivo del mes pasado.

Artículos relacionados