Tcpdump muestra un puerto de redirección diferente después de agregar la regla REDIRECT en iptables

Tcpdump muestra un puerto de redirección diferente después de agregar la regla REDIRECT en iptables

Estoy intentando dirigir el tráfico del cliente a un clúster de Kubernetes que escucha NodePort 192.168.1.100.30000.

El cliente necesita realizar una solicitud, 192.168.1.100.8000por lo que agregué la siguiente regla REDIRECT en iptables:

iptables -t nat -I PREROUTING -p tcp --dst 192.168.1.100 --dport 8000 -j REDIRECT --to-port 30000

Luego emito un curl 192.168.1.100:8000, sin embargo, en tcpdump veo un puerto diferente:

# tcpdump -i lo -nnvvv host 192.168.1.100 and port 8000
tcpdump: listening on lo, link-type EN10MB (Ethernet), capture size 262144 bytes
[Interface: lo] 20:39:22.685968 IP (tos 0x0, ttl 64, id 20590, offset 0, flags [DF], proto TCP (6), length 40)
[Interface: lo]     192.168.1.100.8000 > 192.168.1.100.49816: Flags [R.], cksum 0xacda (correct), seq 0, ack 3840205844, win 0, length 0
[Interface: lo] 20:39:37.519256 IP (tos 0x0, ttl 64, id 34221, offset 0, flags [DF], proto TCP (6), length 40)

Esperaría que tcpdump mostrara algo como

192.168.1.100.8000 > 192.168.1.100.30000

Sin embargo, se muestra y provoca un error de conexión rechazada ya que no aparece ningún proceso en 192.168.1.100.49816.

192.168.1.100.8000 > 192.168.1.100.49816

Estoy usando un entorno de prueba, por lo que no tengo acceso a dispositivos remotos, por eso lo estoy usando curlpara probar la ruta REDIRECTA de iptables.

¿Existe alguna razón por la cual agregar una regla REDIRECT hace que tcpdump redirija el tráfico a un puerto diferente al especificado?

Editar:

Después de la sugerencia de @AB, se agregó la siguiente regla de SALIDA:

iptables -t nat -I OUTPUT -d 192.168.1.100 -p tcp --dport 8000 -j REDIRECT --to-port 30000

y curl continúa, el recuento de paquetes para la cadena de SALIDA aumenta (aunque el paquete de la cadena PREROUTING REDIRECT no aumentó):

2       10   600 REDIRECT   tcp  --  *      *       0.0.0.0/0            192.168.1.100         tcp dpt:8000 redir ports 30000

Sin embargo, aparece el siguiente error:

# curl -vk https://192.168.1.100:8000/v1/api
* About to connect() to 192.168.1.100 port 8000 (#0)
*   Trying 192.168.1.100...
* Connected to 192.168.1.100 (192.168.1.100) port 8000 (#0)
* Initializing NSS with certpath: sql:/etc/pki/nssdb
* NSS error -12263 (SSL_ERROR_RX_RECORD_TOO_LONG)
* SSL received a record that exceeded the maximum permissible length.
* Closing connection 0
curl: (35) SSL received a record that exceeded the maximum permissible length.

Además, intenté agregar una red de sistema remoto, esta vez el recuento de paquetes de PREROUTING REDIRECT CHAIN ​​aumenta después de la ejecución remotesystem curl ...(pero la CADENA DE SALIDA no aumenta):

2       34  2040 REDIRECT   tcp  --  *      *       0.0.0.0/0            172.16.128.1         tcp dpt:8000 redir ports 30000

Error:

# ip netns exec remotesystem curl -vk https://192.168.1.100:8000/v1/api
* About to connect() to 192.168.1.100 port 8000 (#0)
*   Trying 192.168.1.100...
* Connection timed out
* Failed connect to 192.168.1.100:8000; Connection timed out
* Closing connection 0
curl: (7) Failed connect to 192.168.1.100:8000; Connection timed out

Respuesta1

Para ser claros: la prueba del OP se realiza desde el sistema 192.168.1.100 hacia sí mismo, no desde un sistema remoto, y esa es la causa del problema. El puerto no se cambió en este caso porque no coincidía ninguna regla NAT, mientras que habría coincidido si se hubiera hecho desde un sistema remoto.

El siguiente esquema muestra cómo se realizan el orden de las operaciones en un paquete:

Flujo de paquetes en Netfilter y General Networking

La razón es cómo funciona NAT en Linux:iptablesve un paquete en la nattabla solo para el primer paquete de un nuevo flujo de seguimiento de conexión (que, por lo tanto, está en estado NUEVO).

Esta regla funciona bien desde un sistema remoto. En este caso, el primer paquete visto será un paquete entrante:

to port 8000 --> AF_PACKET (tcpdump) --> conntrack --> nat/PREROUTING (iptables REDIRECT): to port 30000
--> routing decision --> ... --> local process receiving on port 30000

Todos los paquetes siguientes en el mismo flujo harán que conntrack maneje directamente el cambio de puerto (o la reversión de puerto para respuestas) y omitirá cualquier regla de iptables en la nattabla (como está escrito en el esquema: natla tabla solo se consulta para NEWconexiones). Entonces, (omitiendo la parte del paquete de respuesta), el siguiente paquete entrante pasará por esto:

to port 8000 --> AF_PACKET (tcpdump) --> conntrack: to port 30000
--> routing decision --> ... --> local process receiving on port 30000

Para una prueba del sistema consigo mismo, el primer paquete no es un paquete entrante sino un paquete saliente. En cambio, esto sucede usando la lointerfaz saliente:

local process client curl --> routing decision --> conntrack --> nat/OUTPUT (no rule here)
--> reroute check --> AF_PACKET (tcpdump) --> to port 8000

Y ahora este paquete vuelve a aparecer en la lointerfaz, reaparece como un paquete que ya no es el primer paquete en una conexión, por lo que sigue el segundo caso como el anterior: solo conntrack se encarga del NAT y no llama nat/PREROUTING. Excepto que no se le indicó en el paso anterior que hiciera ninguna NAT:

to port 8000 --> AF_PACKET (tcpdump) --> conntrack
--> routing decision --> ... -->nolocal process receiving on port8000

Como no hay nada escuchando en el puerto 8000, el sistema operativo devuelve un TCP RST.

Para que esto funcione en el sistema local, REDIRECTtambién se debe poner una regla en la nat/OUTPUTcadena:

iptables -t nat -I OUTPUT -d 192.168.1.100 -p tcp --dport 8000 -j REDIRECT --to-port 30000

Notas adicionales

  • si el caso está destinado a uso remoto, no realice la prueba desde el sistema local: las reglas que atraviesa la prueba no son las mismas. Esto hace que la prueba no refleje la realidad.

    Simplemente use un espacio de nombres de red para crear un sistema remoto de bolsillo en caso de que no haya otro sistema disponible. Ejemplo que debería funcionar con un sistema que solo tiene la regla de OP nat/PREROUTINGy hace curl http://192.168.1.100/(que no requiere DNS):

    ip netns add remotesystem
    ip link add name vethremote up type veth peer netns remotesystem name eth0
    ip address add 192.0.2.1/24 dev vethremote
    ip -n remotesystem address add 192.0.2.2/24 dev eth0
    ip -n remotesystem link set eth0 up
    ip -n remotesystem route add 192.168.1.100 via 192.0.2.1
    ip netns exec remotesystem curl http://192.168.1.100:8000/
    
  • tcpdumpy NAT

    tcpdumpsucede en los AF_PACKETpasos del esquema anterior: muy temprano para el ingreso y muy tarde para la salida. Eso significa que, en el caso de un sistema remoto, nunca capturará el puerto 30000, incluso cuando esté funcionando. Para el caso del sistema local, una vez nat/OUTPUTagregada la regla, capturará el puerto 30000.

    Simplemente no confíe ciegamente en la dirección/puerto que se muestra al tcpdumprealizar NAT: depende del caso y de dónde se produce la captura.

información relacionada