La petición siempre es una variante de la misma frase: mantén la VPN para navegar, pero deja que SSH o una tarea de copia de seguridad usen la conexión normal. El consejo que circula para esto, reenviar el puerto con reglas NAT de iptables, no lo hace. Una regla NAT reescribe direcciones y puertos dentro de un flujo; nunca elige por qué interfaz sale el paquete. Esa decisión pertenece a la consulta de enrutamiento, y la consulta de enrutamiento necesita ayuda para ver un puerto.

Por qué una ruta normal no puede coincidir con un puerto

Linux elige una ruta buscando la dirección de destino en una tabla, después de filtrar las tablas candidatas mediante reglas de política. Esas reglas pueden coincidir con una marca de cortafuegos, una dirección de origen, una interfaz de entrada o un UID. La entrada de ruta en sí no tiene campo de puerto, porque la ruta se elige antes de que importe la cabecera de transporte.

Los iproute2 y los núcleos actuales amplían el selector de la regla: ipproto, sport y dport se aceptan, así que ip rule add ipproto tcp dport 22 table 100 expresa la intención en una línea. Ejecuta ip rule add help en el host real para ver si tu compilación lista dport. Si lo hace, has terminado con un solo comando; si no, el camino por marca de abajo es el equivalente.

En cualquier caso la regla solo es un puntero. La tabla 100 sigue necesitando una ruta, y esa es la pieza que la gente olvida.

Tres piezas, o una

La forma moderna es la regla única de arriba más una ruta en la tabla alternativa. La forma clásica añade netfilter en medio: marca el paquete y luego haz coincidir la marca en una regla. Ambas acaban en la misma consulta; se diferencian en dónde se lee el puerto.

El enfoque por marca existe porque netfilter ve la cabecera de transporte y el código de enrutamiento históricamente no la veía. Ese es todo el mecanismo, y explica por qué el recurso alternativo son tres piezas que cooperan y no un comando ingenioso.

Las tres piezas en orden

Primero, marca los flujos que quieres desviar, en una cadena que se ejecute lo bastante pronto para que la marca exista antes de la decisión de ruta:

nft add table inet vpnexempt
nft 'add chain inet vpnexempt out { type route hook output priority mangle; }'
nft add rule inet vpnexempt out tcp dport 22 meta mark set 0x1

Segundo, envía los paquetes marcados a una tabla aparte:

ip rule add fwmark 0x1/0x1 table 100

Tercero, da a esa tabla una ruta por defecto por el enlace físico. Las direcciones son ilustrativas; sustituye las tuyas:

ip route add default via 192.168.1.1 dev wlan0 table 100

Sáltate el tercer paso y el paquete marcado no encuentra ruta en la tabla 100, así que falla. El síntoma parece que la regla rompió la red, cuando en realidad eliminó el único camino que le quedaba al paquete.

Qué lo mantiene funcionando después del primer paquete

Las marcas se aplican a los paquetes salientes. La respuesta llega por la interfaz física con la dirección que asignó ese enlace, y la tabla principal puede seguir prefiriendo el túnel para ese destino. Dos mecanismos mantienen coherente el camino de vuelta: una regla basada en el origen para la dirección del enlace y el seguimiento de conexiones, que conserva la decisión durante toda la vida del flujo. Guardar la marca en conntrack y restaurarla en la respuesta es la forma habitual de hacer estable una excepción con estado.

El filtrado de camino inverso es el otro asesino silencioso. Si el núcleo valida el camino de vuelta de forma estricta y la respuesta llega por una interfaz que la tabla principal no elegiría, el paquete se descarta sin mensaje. rp_filter los valores por defecto varían según la distribución y según si la interfaz se reenvía, así que lee el valor efectivo en lugar de suponer que está desactivado.

No marques el transporte propio del túnel. Un túnel WireGuard o UDP OpenVPN no es TCP 22, así que la receta de arriba es segura, pero una excepción escrita como todo excepto el destino del túnel puede capturar los paquetes cifrados y romper el túnel de una forma que parece un bucle de enrutamiento.

Compruébalo en lugar de suponerlo

Pregunta al núcleo qué camino tomaría, para el destino y para el puerto, y luego ejecuta la transferencia real:

ip route get 203.0.113.10
ip rule show
nft list ruleset

Si la marca existe pero los contadores nunca se mueven, la regla no coincide. Los contadores son la diferencia entre una configuración que escribiste y una política que el núcleo aplica.

Cuándo es esta la herramienta equivocada

Las excepciones basadas en puertos son frágiles por naturaleza. Los servicios cambian de puerto, HTTP/3 llega como UDP 443 y una aplicación puede abrir varios flujos de distinto tipo. Si el requisito real es que este programa use el enlace normal, la capa de enrutamiento es el lugar equivocado; un espacio de nombres de red o un proxy por aplicación encajan mejor.

Si el requisito es que esta subred use el enlace normal, omite las marcas por completo y añade una ruta más específica. Es más simple, se ve en una sola tabla y la siguiente persona puede auditarlo sin leer un conjunto de reglas.

Anota cuál de los tres requisitos tienes en realidad antes de tocar el cortafuegos. El mecanismo de arriba es preciso, y la precisión aplicada a la capa equivocada solo es una caída más larga.

Artículos relacionados