Tres cosas deciden qué fuente de credenciales debe usar: si hay una persona sentada en la consola, si el secreto puede vivir en el disco y si otro proceso debe responder a la solicitud. Si elige mal, el cliente informa de un problema con stdin mientras la contraseña es perfectamente válida, lo que manda a la mayoría al servidor sin motivo. Elija primero la ruta; los mensajes de error solo tienen sentido cuando sabe qué ruta los produjo.

Las tres condiciones que lo deciden

La solicitud en la consola es la única ruta que necesita a una persona, así que es la única que falla en silencio dentro de una unidad systemd o un contenedor. Si una persona inicia el cliente y escribe la contraseña, no interviene ningún archivo y nada sensible acaba en el disco. Si el cliente arranca al inicio, esa misma solicitud se convierte en un cuelgue, y el tiempo de espera suele aparecer como un fallo de conexión genérico en lugar de un error de credenciales.

Un archivo de credenciales es la respuesta correcta cuando el proceso arranca sin supervisión y el secreto puede sobrevivir en el disco bajo un conjunto de permisos controlado. Cuando el secreto no debe escribirse en absoluto - una política solo de almacén, un token de corta duración - necesita un flujo que el cliente pueda abrir, lo que en la práctica significa sustitución de proceso o un ayudante que escribe en un descriptor de archivo. La interfaz de gestión cubre el cuarto caso: un controlador que ya gestiona el ciclo de vida del túnel y puede responder preguntas por un socket.

  • Persona en la consola, sin automatización: solicitud interactiva.
  • Arranque sin supervisión, el secreto puede almacenarse: archivo de credenciales con modo 0600.
  • Arranque sin supervisión, el secreto no puede almacenarse: sustitución de proceso o un fd escrito por un agente de secretos.
  • Otro proceso es dueño del cliente: interfaz de gestión.

Cuando encajan dos respuestas, prefiera la que mantiene el secreto fuera de argv y fuera del historial del shell. Una contraseña pasada como argumento de programa es legible por cualquier usuario local a través de la lista de procesos, y esa exposición es difícil de deshacer.

Qué hace realmente cada ruta de entrada

--auth-user-pass por sí solo pide un nombre de usuario y una contraseña en la consola. Con un nombre de archivo, el cliente toma la primera línea como nombre de usuario y la segunda como contraseña; nada admite comillas, comentarios ni sintaxis clave-valor. Ese es todo el formato, así que quien lo edite esperando un archivo INI lo rompe. Un bloque <auth-user-pass> en línea contiene esas mismas dos líneas dentro del perfil, lo que convierte el perfil en un secreto y lo sitúa en la misma clase de manejo que una clave privada.

Las otras dos rutas son un flujo y un socket. OpenVPN solo necesita un descriptor de archivo legible, así que aceptará una sustitución de proceso, una entrada /dev/fd respaldada por una tubería o un nombre de archivo que en realidad es un FIFO. La interfaz de gestión es distinta por naturaleza: el cliente pide los valores a un proceso supervisor en lugar de leerlos él mismo. Esa diferencia importa al reconectar: un archivo de credenciales, un FIFO en el que un ayudante de secretos puede volver a escribir y la interfaz de gestión pueden volver a suministrar sin una persona; la sustitución de proceso no, porque el descriptor se consume en la primera lectura.

El archivo tiene que ser exactamente de dos líneas

Dos modos de fallo cubren la mayoría de los archivos de credenciales rotos. Un editor de Windows deja finales CRLF, el retorno de carro pasa a formar parte de la contraseña y el servidor rechaza un valor que parece correcto en pantalla. Una segunda línea en blanco - una línea vacía donde debería estar la contraseña - deja al cliente con una contraseña vacía, que el servidor rechaza aunque el valor en pantalla parezca correcto. Un salto de línea final después de la contraseña es inofensivo. Escriba el archivo desde el shell, no desde un editor, y compruebe los bytes:

install -m 600 /dev/null /etc/openvpn/client.creds
printf '%s\n%s\n' "$VPN_USER" "$VPN_PASS" > /etc/openvpn/client.creds
od -c /etc/openvpn/client.creds | head -3

La ruta importa tanto como el contenido. Una ruta relativa se resuelve contra el directorio de trabajo del proceso, que para un servicio rara vez es el directorio donde probó. Use una ruta absoluta, haga que el archivo sea propiedad de la cuenta que ejecuta el cliente y mantenga el modo 0600 para que el grupo y los demás no puedan leerlo. El manual de OpenVPN 2.6 enumera las formas aceptadas de la directiva; consúltelo antes de inventar una tercera.

Pruebe el archivo antes de culpar al servidor. Ejecutar openvpn --config client.ovpn --auth-user-pass /etc/openvpn/client.creds --verb 4 imprime si se abrió el archivo y, después, si se aceptaron las credenciales, lo que le dice qué capa cambiar a continuación.

Flujos, stdin y los errores que producen

Pasar la contraseña por una tubería es lo primero que prueba la mayoría y la ruta con más sorpresas. La sustitución de proceso funciona porque el cliente solo necesita un descriptor legible: openvpn --config client.ovpn --verb 3 --auth-user-pass <(printf '%s\n%s\n' "$VPN_USER" "$VPN_PASS"). Pasar /dev/stdin solo funciona cuando stdin sigue abierto y legible en el momento en que el cliente pregunta, lo que no se cumple en una tubería que ya lo ha consumido.

ERROR: could not read Auth username from stdin tiene dos causas distintas. En algunos clientes 2.3.x el mensaje venía de un defecto de manejo de stdin que las versiones 2.3 posteriores corrigieron, así que un cliente tan antiguo solo mejora actualizando. En un cliente actual la misma redacción significa que el cliente pidió entrada de consola y que stdin no era un terminal - exactamente la situación en una unidad systemd, una tarea cron o un contenedor iniciado sin -t.

auth-nocache añade un tercer bucle que parece una contraseña rota. Descarta las credenciales de la memoria tras su uso, así que una reconexión provocada por un cambio de red no tiene nada que reproducir y la solicitud vuelve. Eso es la directiva funcionando como se diseñó. auth-nocache cambia lo que el cliente guarda en memoria, no lo que el servidor o el proveedor de identidad piensa de la cuenta, así que toda la solución es volver a suministrar el mismo archivo, releído al reiniciar - y para una conexión gestionada ese suministro debería venir del almacén de secretos aprobado, no de una contraseña en un script.

La interfaz de gestión cuando otro proceso es dueño de la solicitud

Un controlador que ya supervisa el túnel también puede responder a sus preguntas. Inicie el cliente con --management 127.0.0.1 7505, lea las líneas de estado y vigile >PASSWORD:Need 'Auth' username/password. El controlador responde con username "Auth" alice seguido de password "Auth" ... en líneas de comando separadas. El token 'Auth' hace eco del identificador de la petición y tiene que coincidir, o la respuesta se ignora y el cliente espera.

El coste es una máquina de estados que ahora posee: orden de conexiones, reinicios del cliente y un socket local que otros usuarios del host no deben alcanzar. Lo que compra es una credencial suministrada en tiempo de ejecución, sin archivo en el disco y sin una solicitud que pueda colgarse. Si nada más de su pila ya habla el protocolo, esta ruta añade un componente que mantener.

Fallos que parecen problemas de credenciales

Un protocolo de enlace TLS que nunca se completa, o un VERIFY ERROR: depth=0, error=certificate signature failure, es un problema de confianza y ocurre antes de que se lea ningún nombre de usuario. Un cliente proto tcp apuntado a un escucha UDP falla en el mismo punto: los paquetes nunca llegan a la fase de autenticación, así que toda contraseña parece incorrecta por asociación. Mantenga la verificación de certificados activada antes de enviar la credencial; una solicitud familiar no dice nada sobre quién recibe la contraseña. Compruebe la cadena de certificados, el nombre común y el transporte antes de tocar la credencial.

Los errores de shell en el script que ensambla el perfil son el otro rastro falso. Un espacio que falta dentro de [ ! -f "$file" ] produce command not found, el script se detiene antes, el perfil nunca se reconstruye y el cliente arranca con el anterior. Ese error pertenece al shell, no a la autenticación, y por mucho que reescriba la contraseña no lo va a borrar.

No toda solicitud en este flujo es esta credencial. Una frase de paso de clave privada, un inicio de sesión de proxy HTTP y la contraseña del servicio son tres secretos distintos pedidos en fases diferentes, y una frase de paso de clave suele gestionarla el cliente o un agente en lugar de auth-user-pass. Si la solicitud aparece antes de que la sesión TLS esté en marcha, está respondiendo la pregunta equivocada y ningún restablecimiento de cuenta ayudará. Una vez excluida la solicitud equivocada, las comprobaciones restantes empiezan por la capa más temprana:

  • Lea el primer error de protocolo, no la última línea de credenciales: TLS, luego autenticación, luego push.
  • Confirme que el archivo tiene dos líneas, no contiene retornos de carro y se referencia con una ruta absoluta.
  • Confirme que el perfil y la línea de comandos no discrepan sobre la directiva.
  • Separe "credenciales aceptadas" de "el tráfico fluye" - el enrutamiento y el DNS son una capa posterior.

Cuando el cliente llegue a la fase push, deje de mirar las credenciales y empiece a leer las rutas y los servidores DNS que ha enviado el servidor. Ese es el siguiente problema, y no tiene nada que ver con la contraseña que acaba de corregir.

Artículos relacionados