En mi script de iptables, he estado experimentando con la escritura de reglas tan finas como sea posible. Limito qué usuarios pueden usar qué servicios, en parte por seguridad y en parte como un ejercicio de aprendizaje.
Usando iptables v1.4.16.2 en Debian 6.0.6 ejecutando el kernel 3.6.2.
Sin embargo, he encontrado un problema que aún no entiendo ...
puertos de salida para todos los usuarios
Esto funciona perfectamente bien. No tengo reglas genéricas de seguimiento de estado.
## Puerto de salida 81 $ IPTABLES -A OUTPUT -p tcp --dport 81 -m conntrack --ctstate NUEVO, ESTABLECIDO -j ACEPTAR $ IPTABLES -A INPUT -p tcp --sport 81 -s $ MYIP -m conntrack --ctstate ESTABLISHED -j ACCEPT
puertos de salida con coincidencia de usuario
## puerto de salida 80 para cuenta de uso $ IPTABLES -A SALIDA --propietario propietario --uid-propietario useraccount -p tcp --dport 80 -m conntrack --ctstate NUEVO, ESTABLECIDO --sport 1024: 65535 -j ACEPTAR $ IPTABLES -A INPUT -p tcp --sport 80 --dport 1024: 65535 -d $ MYIP -m conntrack --ctstate ESTABLISHED -j ACCEPT
Esto permite que el puerto 80 salga solo para la cuenta "useraccount", pero reglas como esta para el tráfico TCP tienen problemas.
## Registros salientes predeterminados + reglas de bloqueo $ IPTABLES -A OUTPUT -j LOG --log-prefix "BAD OUTGOING" --log-ip-options --log-tcp-options --log-uid $ IPTABLES -A SALIDA -j DROP
La cuestión
Lo anterior funciona, el usuario "useraccount" puede obtener archivos perfectamente bien. Ningún otro usuario en el sistema puede hacer conexiones salientes al puerto 80.
useraccount @ host: $ wget http://cachefly.cachefly.net/10mb.test
Pero el wget anterior deja entradas caídas x7 en mi syslog:
18 de octubre 02:00:35 kernel xxxx: MALA SALIDA IN = OUT = eth0 SRC = xx.xx.xx.xx DST = 205.234.175.175 LEN = 40 TOS = 0x00 PREC = 0x00 TTL = 64 ID = 12170 DF PROTO = TCP SPT = 37792 DPT = 80 SEQ = 164520678 ACK = 3997126942 VENTANA = 979 RES = 0x00 ACK URGP = 0
No obtengo estas caídas para reglas similares con tráfico UDP. Ya tengo reglas establecidas que limitan qué usuarios pueden realizar solicitudes de DNS.
Los paquetes ACK salientes descartados parecen provenir de la cuenta raíz (URGP = 0) que no entiendo. Incluso cuando cambio useraccount por root.
Creo que los paquetes ACK se clasifican como nuevos porque conntrack comienza a rastrear las conexiones después del tercer paso del protocolo de enlace de 3 vías, pero ¿por qué se descartan?
¿Se pueden ignorar estas gotas de forma segura?
Editar
Así que a menudo veo reglas como estas, que funcionan bien para mí:
$ IPTABLES -A SALIDA -s $ MYIP -p tcp -m tcp --dport 80 -m estado - estado NUEVO, ESTABLECIDO -j ACEPTAR $ IPTABLES -A INPUT -p tcp -m tcp --sport 80 -d $ MYIP -m state --state ESTABLISHED -j ACCEPT
Cambié "-m state --state" por "-m conntrack --ctstate" ya que la coincidencia de estado es aparentemente obsoleta.
¿Es una buena práctica tener reglas genéricas de seguimiento de estado? ¿Las reglas anteriores no se consideran correctas?
Para un control estricto sobre las conexiones de los usuarios salientes, ¿sería mejor algo como esto?
$ IPTABLES -A ENTRADA -m conntrack --ctstate ESTABLECIDO -j ACEPTAR $ IPTABLES -A OUTPUT -m conntrack --ctstate ESTABLISHED -j ACCEPT $ IPTABLES -A OUTPUT -p tcp --dport 80 -s $ SERVER_IP_TUNNEL -m conntrack --ctstate NEW -m owner --uid-owner useraccount -j ACCEPT $ IPTABLES -A OUTPUT -p tcp --dport 80 -s $ SERVER_IP_TUNNEL -m conntrack --ctstate NEW -m owner --uid-owner otheraccount -j ACCEPT
Respuestas:
Para abreviar una larga historia, ese ACK se envió cuando el socket no pertenecía a nadie. En lugar de permitir paquetes que pertenecen a un socket que pertenece al usuario
x
, permita paquetes que pertenezcan a una conexión iniciada por un socket del usuariox
.La historia más larga.
Para comprender el problema, es útil comprender cómo funcionan
wget
las solicitudes HTTP en general.En
wget
establece una conexión TCPcachefly.cachefly.net
y, una vez establecida, envía una solicitud en el protocolo HTTP que dice: "Por favor, envíeme el contenido de/10mb.test
(GET /10mb.test HTTP/1.1
) y, por cierto, ¿podría cerrar la conexión una vez que haya terminado?Connection: Keep-alive
). lo hace porque en caso de que el servidor responda con una redirección para una URL en la misma dirección IP, puede reutilizar la conexión.Ahora el servidor puede responder con: "aquí vienen los datos que solicitó, tenga cuidado con su tamaño de 10 MB (
Content-Length: 10485760
), y sí, dejo la conexión abierta". O si no conoce el tamaño de los datos, "Aquí están los datos, lo siento, no puedo dejar la conexión abierta, pero le diré cuándo puede dejar de descargar los datos cerrando mi final de la conexión".En la URL anterior, estamos en el primer caso.
Entonces, tan pronto como
wget
haya obtenido los encabezados para la respuesta, sabe que su trabajo se realiza una vez que ha descargado 10 MB de datos.Básicamente, lo que
wget
hace es leer los datos hasta que se hayan recibido 10 MB y salir. Pero en ese punto, hay más por hacer. ¿Qué hay del servidor? Se le ha dicho que deje la conexión abierta.Antes de salir,
wget
cierra (close
llamada al sistema) el descriptor de archivo para el socket. Luego, elclose
sistema termina de reconocer los datos enviados por el servidor y envía un mensajeFIN
para decir: "No enviaré más datos". En ese puntoclose
regresa ywget
sale. Ya no hay ningún socket asociado a la conexión TCP (al menos ninguno de los cuales es propiedad de ningún usuario). Sin embargo, aún no está terminado. Al recibir esoFIN
, el servidor HTTP ve el final del archivo al leer la siguiente solicitud del cliente. En HTTP, eso significa "no más solicitudes, cerraré mi final". Por lo tanto, también envía su FIN, para decir: "No enviaré nada tampoco, esa conexión se va a ir".Al recibir esa FIN, el cliente envía un "ACK". Pero, en ese punto, ya no
wget
existe, por lo que ACK no es de ningún usuario. Es por eso que está bloqueado por su firewall. Debido a que el servidor no recibe el ACK, enviará el FIN una y otra vez hasta que se dé por vencido y verá más ACK caídos. Eso también significa que al soltar esos ACK, está utilizando innecesariamente los recursos del servidor (que necesita mantener un socket en el estado ÚLTIMO ACK) durante bastante tiempo.El comportamiento habría sido diferente si el cliente no hubiera solicitado "Keep-alive" o el servidor no hubiera respondido con "Keep-alive".
Como ya se mencionó, si está utilizando el rastreador de conexión, lo que quiere hacer es dejar pasar cada paquete en los estados ESTABLECIDO y RELACIONADO y solo preocuparse por los
NEW
paquetes.Si permite
NEW
paquetes de usuariox
pero no paquetes de usuarioy
, entoncesx
pasarán otros paquetes para conexiones establecidas por usuario , y debido a que no puede haber conexiones establecidas por usuarioy
(ya que estamos bloqueando losNEW
paquetes que establecerían la conexión), no habrá ningún paquete para lasy
conexiones de usuario que se realicen.fuente
- Bueno, al menos las reglas que has mostrado no implican esto, en realidad.
También hay un lugar para el asesoramiento: no verifique los usuarios en las transmisiones ESTABLECIDAS, solo verifique NUEVO. Tampoco veo un punto en verificar el puerto de origen cuando verifico Entrante ESTABLECIDO, cuál es la diferencia de qué puerto era, ya está en estado ESTABLECIDO del PoV de conntrack. El cortafuegos debe ser lo más simple posible pero eficiente, por lo que el enfoque de navaja de Occam es la mejor opción.
fuente