Dos registros A, una dirección, un demonio: escriba el usuario el nombre que escriba, el paquete que llega es idéntico, y el proceso ligado al puerto 1194 no tiene con qué distinguirlos. No es una configuración errónea esperando reparación; es el límite de lo que puede expresar una consulta de nombre. Cualquier cosa que elija entre dos instancias de OpenVPN tiene que ser un valor que el cliente ponga en la conexión y que el frontend pueda releer, y un cliente OpenVPN estándar no pone ningún valor así en claro. Lo que sigue es adónde te deja eso y qué disposiciones separan de verdad el tráfico.

Empieza por lo que devuelve el resolutor

Un cliente pide vpn-a.example y recibe una dirección; esa dirección más un puerto es todo lo que marca. El nombre se consume localmente, durante la consulta, y nunca se convierte en un campo en el cable, así que el listener del otro extremo no puede ver qué etiqueta se usó. Apunta vpn-a.example y vpn-b.example a un mismo host y ambas consultas darán el mismo destino, y por eso el demonio absorbe las dos conexiones en una sola instancia sin registrar diferencia alguna. Los perfiles basados en dirección se comportan exactamente igual que los basados en nombre, por la misma razón.

Esto también cierra una familia de soluciones tentadoras: alias adicionales, elegir un certificado por nombre en un solo socket o una regla de cortafuegos atada al nombre consultado. Ninguna de ellas puede actuar sobre información que ya se descartó antes de que el primer paquete saliera del cliente. Si dos servidores tienen que seguir separados, la separación tiene que viajar con la propia conexión, es decir, una dirección, un puerto o un campo de carga útil que defina el protocolo.

El único selector que cuenta viaja dentro del paquete

HTTP lo resuelve con una cabecera Host, y TLS con una extensión de nombre de servidor en el ClientHello, y así una dirección sirve muchos sitios detrás de un solo listener. Ambos funcionan porque el protocolo en el cable lleva una petición para un servicio concreto. El hecho relevante sobre OpenVPN es que ninguno de esos campos está presente en la capa que inspecciona una caja intermedia.

En una disposición estándar el handshake TLS que protege el canal de control ocurre una vez establecida la sesión exterior y se transporta dentro de ella, así que un listener que no ha terminado esa sesión ve un flujo opaco. Aun así puede decir que esto es OpenVPN, porque el opcode y el tramado de los primeros bytes son reconocibles, pero el nombre de servidor, si existe, está en la parte cifrada. Un diseño que espera leer un host virtual del paquete exterior está leyendo un campo que el protocolo nunca escribe ahí. Eso viene de cómo está construido el túnel, no de un hueco que una coincidencia de patrones más lista pudiera cerrar.

Donde un divisor de protocolos se queda corto

Un multiplexor del tipo que documenta el proyecto sslh escucha en un puerto, inspecciona los primeros bytes y entrega la conexión al backend que hable el protocolo que reconoció. Eso es valor real cuando un solo puerto tiene que servir SSH, HTTPS y OpenVPN a la vez.

sslh mantiene una sonda aparte por protocolo, y su sonda TLS puede extraer un nombre de servidor o un valor ALPN de un ClientHello genuino. Reconocer OpenVPN nativo es una clasificación distinta de leer una extensión TLS, y las dos no se componen, porque un cliente que nunca envía un ClientHello al socket de escucha deja la sonda sin nada que leer. Por eso una regla que coincide con OpenVPN responde «este es un cliente OpenVPN» y no va más allá. Dos backends OpenVPN detrás de esa regla son indistinguibles, el frontend no tiene base para elegir, y cada sesión aterriza en el mismo sitio. El multiplexado comparte un puerto entre servicios distintos; no hace alojamiento virtual dentro de un servicio.

Disposiciones que separan de verdad dos servidores

La separación honesta más barata es una segunda dirección pública, una por instancia, y los perfiles existentes ya llevan lo necesario porque apuntan a direcciones distintas. La siguiente es un puerto distinto en la misma dirección, que no cuesta nada más allá de un número en cada perfil. TCP y UDP mantienen tablas de puertos separadas, así que 1194/tcp y 1194/udp son sockets no relacionados y uno de cada uno puede coexistir en un mismo host; eso es separación de transporte, no enrutamiento por nombre, y a menudo se confunde con él. Ambas opciones sobreviven a una revisión de cortafuegos, porque la regla coincide con algo que el paquete contiene de verdad.

Si la frontera que necesitas discurre entre grupos de usuarios en lugar de entre dos implementaciones, una instancia con configuraciones de cliente distintas, su propio manejo de certificados y una política de acceso explícita puede cubrirlo. Lo que eso compra es política dentro de un servidor, no aislamiento entre servidores, y son requisitos distintos. El aislamiento que debe sostenerse bajo compromiso sigue necesitando la dirección o el borde del cortafuegos.

Un terminador TLS colocado delante de OpenVPN puede exponer un nombre de servidor real a un frontend capaz, pero solo cuando el lado cliente también está configurado para esa capa extra. Los perfiles existentes no adquieren el comportamiento por apuntarlos a la misma dirección.

Demuestra el selector antes de construir

Nombra el campo que identificará un backend y luego confirma que el cliente lo envía de verdad; si la respuesta es una dirección, el plan se sostiene, y si es un nombre de servidor dentro de un handshake que nunca llega al listener, ninguna cantidad de trabajo de DNS lo salvará. Tres comandos resuelven rápido el lado que escucha.

ss -lntup | grep -E ':443|:1194'
openssl s_client -connect vpn-a.example:443 -servername vpn-a.example </dev/null | head -n 20
journalctl -u openvpn-server@server-a -n 20 --no-pager

La línea de openssl solo da un certificado donde algo en esa dirección termina TLS; contra un listener OpenVPN nativo se atasca o falla, y eso es en sí la respuesta sobre qué expone la capa exterior. La línea de journal, leída en cada backend, muestra qué instancia aceptó la sesión. Los registros del frontend anotan qué se clasificó y nunca qué se pretendía, así que el backend sigue siendo la única autoridad sobre dónde aterrizó un usuario.

Termina con dos perfiles pensados para alcanzar servidores distintos, conéctalos de uno en uno y anota el registro del backend para cada uno. Si ambas sesiones aparecen en el mismo journal, falta el selector en lugar de estar mal configurado, y el arreglo es una dirección o un puerto que el cliente pueda atacar. Editar la regla de enrutamiento no ayudará, porque a la regla nunca se le dio nada por lo que enrutar. Una vez que la separación es real, escríbela junto a los perfiles para que la siguiente persona no añada un segundo alias y espere que haga el trabajo.

Artículos relacionados