
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.8000
por 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 curl
para 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:
La razón es cómo funciona NAT en Linux:iptablesve un paquete en la nat
tabla 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 nat
tabla (como está escrito en el esquema: nat
la tabla solo se consulta para NEW
conexiones). 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 lo
interfaz 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 lo
interfaz, 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 --> ... -->
no
local process receiving on port
8000
Como no hay nada escuchando en el puerto 8000, el sistema operativo devuelve un TCP RST.
Para que esto funcione en el sistema local, REDIRECT
también se debe poner una regla en la nat/OUTPUT
cadena:
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/PREROUTING
y hacecurl 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/
tcpdump
y NATtcpdump
sucede en losAF_PACKET
pasos 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 veznat/OUTPUT
agregada la regla, capturará el puerto 30000.Simplemente no confíe ciegamente en la dirección/puerto que se muestra al
tcpdump
realizar NAT: depende del caso y de dónde se produce la captura.