Encadenar dos clientes OpenVPN en un solo host Linux con fwmark
Ambos clientes quieren la ruta predeterminada. Fije el servidor interno con una ruta de host, evite que el segundo cliente reescriba tablas y luego dirija los flujos con fwmark.
Apilar un segundo cliente OpenVPN en un solo host Linux falla en uno de dos puntos. O el cliente interno no puede llegar a su propio servidor porque el túnel externo ha capturado ese transporte, o el segundo cliente reescribe la tabla de rutas y el primer túnel deja de funcionar. Ambos problemas están en el enrutamiento, no en el cifrado, y ambos se resuelven antes de que el nuevo túnel transporte tráfico de aplicaciones. Las direcciones de aquí son ilustrativas: tun0 es el túnel externo, 203.0.113.9 es el servidor interno y TCP 9999 es el flujo que debe usar la ruta interna.
Dos modos de fallo, una tabla de rutas
Un cliente interno abre un socket UDP hacia su servidor, y ese socket sigue la tabla de rutas actual como cualquier otro. Si el túnel externo ya posee la ruta predeterminada, los paquetes del handshake entran en el túnel externo. Eso solo funciona cuando el servidor externo puede alcanzar al servidor interno y el otro extremo acepta transportar el flujo; si no, el handshake se detiene y el cliente vuelve a intentarlo.
El segundo fallo viene del push del servidor. El push habitual es redirect-gateway def1, que instala 0.0.0.0/1 y 128.0.0.0/1 en lugar de sustituir la ruta predeterminada. Por eso dos clientes que instalan cada uno su propio par /1 chocan, y el par más reciente gana para casi cualquier destino. Busque esos dos prefijos en la tabla cuando un túnel muere después de conectarse el segundo, porque puede que la propia ruta predeterminada nunca haya cambiado.
Marque el flujo y dele una tabla
Dirija el tráfico seleccionado marcándolo en la tabla mangle y haciendo coincidir la marca en una regla de política. La regla apunta a una tabla dedicada cuya ruta predeterminada es el dispositivo interno, así que solo los paquetes marcados toman la ruta interna.
iptables -t mangle -A PREROUTING -p tcp -m multiport --dport 9999 -j MARK --set-mark 0x9999
ip rule add fwmark 0x9999 table 200 pref 1000
ip route add default dev tun1 table 200La mayoría de los fallos de este conjunto de reglas vienen de dos errores. El valor de marca que establece el cortafuegos y el valor que hace coincidir la regla deben ser idénticos, porque una discrepancia no produce ni error ni efecto. Y PREROUTING solo ve paquetes que llegan de fuera, así que una conexión iniciada en este host necesita una regla OUTPUT con la misma coincidencia antes de que se marque siquiera.
Quite la propiedad de las rutas al segundo cliente
Dos opciones impiden que el segundo cliente edite la tabla y actúan en puntos distintos. route-nopull ignora las opciones de ruta empujadas, así que no se instala nada. route-noexec acepta las opciones empujadas en el entorno del cliente pero deja el cambio de tabla a un hook, que es justo lo que quiere cuando necesita ver qué ofreció el servidor.
El hook se ejecuta con los valores empujados en su entorno y debería añadir solo las entradas que ha aprobado. Hágalo idempotente: vacíe la tabla que posee antes de reconstruirla y no suponga que la ejecución anterior dejó un estado limpio. Una reconexión vuelve a invocar el hook, y un hook que añade sin limpiar acumulará rutas duplicadas.
Elija un solo propietario y déjelo por escrito. Un perfil que establece ambas opciones, o un hook que compite con un script manual, produce una tabla que nadie podrá explicar después.
Fije las dos direcciones de los servidores
El servidor interno necesita una ruta de host a través del dispositivo externo antes de que se conecte el cliente interno. Esa entrada es la que mantiene el transporte interno dentro del túnel externo en lugar de dejar que siga la ruta predeterminada.
ip route add 203.0.113.9/32 dev tun0
ip route get 203.0.113.9Añada también una ruta de host correspondiente para el servidor externo en la tabla principal, o una reconexión puede encaminar el transporte del propio cliente externo hacia su propio túnel. El orden no es opcional: añada estas entradas después de que exista la interfaz correspondiente, es decir, mediante un hook o un script que se ejecute tras la conexión. Una ruta que apunta a un dispositivo caído se elimina o se ignora, así que una entrada escrita al arrancar no sobrevive a la secuencia que supuso.
Compruebe la búsqueda en lugar de leer toda la tabla. La salida de la búsqueda de ruta nombra el dispositivo y la dirección de origen que usará el núcleo, que es exactamente lo que obtiene el cliente cuando abre su socket.
Pruebe la cadena y haga visible el fallo
Capture en la interfaz externa mientras se conecta el cliente interno. El handshake interno debería aparecer como carga cifrada dentro del túnel externo; si aparece como UDP simple hacia 203.0.113.9, falta la ruta de host y la cadena nunca se construyó.
Una cadena que vuelve en silencio al enlace físico es el peor resultado, porque el estado de la interfaz no anuncia nada y el tráfico interno sale con la dirección de origen equivocada. Si el segundo cliente puede reconectarse directamente cuando cae el túnel externo, desactive esa vía o únala a la interfaz externa para que el fallo sea visible.
Termine con dos flujos y un volcado de reglas: una conexión marcada y otra sin marcar deberían tomar caminos distintos, y ip rule show debería listar la preferencia que pretendía. Si ambos flujos toman el mismo camino, la marca no se está aplicando donde usted cree.