Devolver una dirección desde un script client-connect parece que debería establecer la IP del cliente. No lo hace: el único canal del hook hacia el servidor es un archivo temporal cuya ruta llega como último argumento del script, y la dirección debe escribirse allí con la sintaxis de ifconfig-push. Un script que imprime la dirección o sale con cero sin escribir nada produce un cliente conectado con una dirección del pool, y el registro del servidor parece perfectamente sano.

La confusión es razonable, porque varios otros hooks sí responden mediante códigos de salida o variables de entorno. Este tiene su propio contrato, y el contrato empieza con un nombre de archivo.

El hook escribe un archivo, no un valor de retorno

La declaración son dos líneas: script-security 2 para permitir la llamada, y luego client-connect /etc/openvpn/server/assign-address.sh. Cuando un cliente se autentica, el servidor ejecuta ese script con la ruta del archivo generado añadida como último argumento, que es $1 solo si la declaración no pasa argumentos adicionales propios. Todo lo que se aplica a ese cliente va en ese archivo como directivas ordinarias, una por línea, exactamente como aparecerían en una configuración. Un envoltorio que añade sus propios parámetros desplaza la posición, y el síntoma clásico es un script que escribe en una ruta que parece --verbose.

El estado de salida es la otra mitad del contrato. Cero significa aceptar y leer el archivo; cualquier otra cosa rechaza la conexión, y así es como una identidad no asignada se deniega en lugar de volver silenciosamente al pool. Escribe la directiva completa antes de devolver éxito, para que un archivo a medio escribir nunca se convierta en la configuración de un cliente.

Dos directivas merecen atención porque interactúan con todo lo demás. ifconfig-push fija la dirección, mientras que una route enviada indica al cliente qué es alcanzable a través del túnel; devuelve la primera y olvida la segunda y obtienes un cliente con dirección y sin sitio adonde ir. El orden dentro del archivo no importa, pero la integridad sí. Un hook que envía una ruta para un prefijo al que ningún par puede llegar es el mismo error visto desde el otro extremo.

Las entradas llegan por variables de entorno: common_name, trusted_ip, ifconfig_pool_remote_ip y el resto. Trátalas todas como cadenas de fuera del servidor, nunca como fragmentos de shell.

El mapeo de direcciones es una política, no aritmética

Un atajo tentador es analizar el nombre del certificado y usar sus dígitos como último octeto. Eso no es un asignador, y falla de formas corrientes: dos nombres con el mismo sufijo colisionan, un nombre que acaba en 300 no es una dirección, y un certificado renombrado por un administrador mueve un dispositivo en silencio. Valida la pertenencia a la subred, el rango de hosts utilizable, las direcciones reservadas y la unicidad en cada llamada, y mantén el mapeo en una tabla que una persona pueda leer.

Quien controla el nombre controla la dirección, así que decide qué representa realmente common_name en este despliegue. Con username-as-common-name activado, la identidad es el inicio de sesión y no el sujeto del certificado. Nunca pases ese valor a la evaluación del shell, y nunca dejes que el cliente influya en una ruta de archivo.

Un asignador respaldado por un registro debe sobrevivir a la concurrencia. Reserva la dirección de forma atómica, maneja una escritura fallida sin admitir un cliente a medio configurar, y etiqueta cada asignación con la conexión que la posee, para que una desconexión tardía de una sesión antigua no pueda liberar una dirección que una más nueva ya tiene. Varias instancias de servidor que comparten un pool hacen ese requisito más agudo, no opcional.

La topología decide qué significa ifconfig-push

Con topology subnet en 10.77.0.0/24, ifconfig-push 10.77.0.20 255.255.255.0 es una dirección más su máscara de red. Bajo topology net30 el segundo argumento no es ninguna máscara sino la dirección del par, así que copiar la forma de subred en un servidor net30 te da un cliente que se asocia y no puede pasar tráfico. Cambia la topología a propósito, nunca para que un ejemplo parezca coherente. Un servidor que ya funciona con net30 tiene clientes vivos en el direccionamiento antiguo, así que el cambio es una migración con ventana de mantenimiento, no una edición de una línea.

Las direcciones estáticas deben quedar fuera del pool dinámico y fuera de cualquier otra asignación estática, incluidas las que reparten los archivos client-config-dir ordinarios. Si dos mecanismos pueden producir la misma dirección para clientes distintos, uno de ellos acabará haciéndolo.

Prueba cuatro casos antes de confiar en él

Ejecuta las mismas comprobaciones contra una identidad asignada, una no asignada, una reconexión y un fallo de registro simulado. openvpn --config client.ovpn --verb 4 muestra la dirección asignada en la respuesta push, y en el servidor grep -A2 PUSH_REPLY /var/log/openvpn/openvpn.log confirma qué se envió realmente. Para la identidad no asignada el resultado esperado es una conexión rechazada, no una dirección del pool.

Después inspecciona el directorio en el que el demonio escribe esos archivos temporales. El script se ejecuta en el contexto de servicio degradado, así que una ruta escribible por root pero no por el demonio convierte cada conexión en un rechazo con un error de permisos enterrado varias líneas más abajo en el registro.

Asignar no es autorizar: una dirección fijada sigue sin dar acceso a cada destino detrás del servidor. Mantén la política de enrutamiento y cortafuegos en su propia capa, y verifícala contra la dirección que acabas de fijar.

Artículos relacionados