Aprendiendo iptables desde cero.
By José Roberto Negreira a.k.a Xous
Version 2005-MAR-09
Disclaimer
Este artículo se provee absolutamente sin garantía de NINGUN TIPO.
(De hecho, difícilmente encuentren documentación en la cual los autores se hagan
cargo de las cosas que puedan salir mal). El eventual lector es responsable de las
acciones y consecuencias que éste realice y produzca. Espero que esta aclaración no
haya herido la susceptibilidad de nadie, pero la considero muy pero muy oportuna,
para evitar cualquier tipo de problemas...porque a alguien se le haya ocurrido
toquetear/desconfigurar/reventar el firewall de su empresa, siguiendo las indicaciones
de este articulo. Podés distribuir este documento siempre y cuando mantengas la info
de copyright, y el link a www.iptableslinux.com
Historial
2005-ABR-08: publicado la versión en español en iptableslinux.com
2005-MAR-08: published on iptableslinux.com
2005-MAR-08: 3 years after! translated to english
2002-MAR-02: Published on XousLAB (Thanks to myself :p)
2002-MAR-02: Publicado en XousLab (Grax XouS :p)
2002-FEB-27: Se corrigió un error de una regla (Gracias Kremar)
2002-FEB-24: Publicado en PsicoFXP - Seguridad
2002-FEB-16: Publicado en el boletín de HTeam (Gracias CyRaNo)
Introducción
Al conectarnos a internet en nuestras casas, de forma explícita nos
estamos conectando, en AMBOS sentidos: directamente a la red, "desnudos" si se me
permite la analogía. El objetivo de este artículo es, medinte iptables, lograr
cierta protección y seguridad. Nótese que un firewall no garantiza 100% de seguridad.
Para este artículo, se requieren conocimientos básicos/intermedios acerca de linux
y TCP/IP, obviamente implementado bajo dicho sistema operativo.
Cabe destacar que para utilizar iptables, es necesario tener un kernel preparado para
éste, y el módulo ip_tables cargado. Del mismo modo, si estas utilizando ipchains, se
deben descargar sus módulos antes de cargar los de iptables.
Iptables es una utilidad de linux que se encarga de darle directivas al kernel, acerca
del filtrado de paquetes TCP/IP. Un paquete TCP/IP consta de varios campos, con
información adicional a los datos que se transmiten en sí. No viene al caso describir
cada uno de ellos, sino los que consideraremos mas relevantes, es decir, aquellos campos
que mediante iptables, empezaremos a "vigilar". Ejemplo: direccion origen, direccion
destino, puerto origen, puerto de destino, etc.
Cómo funciona?
El kernel de linux posee (predefinidas "de fábrica") 3 cadenas (chains)
de reglas: INPUT, FORWARD y OUTPUT. Cada paquete TCP/IP que ingresa/atraviesa/sale
desde/hacia una maquina con iptables funcionando, pasará por la cadena INPUT, FORWARD
o OUTPUT respectivamente. Las cadenas, no son mas que un listado de reglas, con las
cuales controlamos cada uno de los paquetes que pasan. Ahora se preguntaran,
que es una regla?. Para evitar las confusiones, vamos a simplificar su definición al máximo,
y luego mostrarles algunos ejemplos. Una regla consta de 2 partes, y no es mas que una
condición y una acción. Si se cumple la condición se ejecuta la acción. Simple, verdad?
Algo para tener en cuenta mas adelante: si un paquete atravesó todas las reglas de una
cadena, sin hallar coincidencia, iptables se fijará en la politica por defecto de esa
cadena(default policy).
Kernel? forward? paquetes? cadenas? chains? reglas? Me siento un poco mareado...
A no perder la calma...Resumiendo lo anteriormente dicho:
1) Lo mas importante: tener conocimientos previos en linux y TCP/IP !!!
2) El kernel de linux trae 3 cadenas primarias de reglas (INPUT, FORWARD y OUTPUT)
3) Con iptables se pueden agregar/eliminar reglas (entre otras cosas).
Y este es el momento ideal para teclear "man iptables", si no lo hiciste antes,
para tener una idea del funcionamiento de esta utilidad. No obstante, esto no
es requisito obligatorio para continuar leyendo.. =)
Supongamos la siguiente situación...Había una vez, una red local (192.168.1.0),
con 50 PC's, de las cuales destacaremos:
1) intruso.mired.lan (192.168.1.15) <--- rebautizada para la ocasion =)
2) neutral.mired.lan (192.168.1.18) <--- alguien que no corta ni pincha.
3) jose.mired.lan (192.168.1.20) <--- nuestra adorable Linux Box
Nosotros somos los operadores de "jose.mired.lan", estamos corriendo un servicio
ftp (wu-ftpd*), y estamos enterados de que el muchacho que usa "intruso",
es alguien muy curioso, que le gusta investigar en máquinas ajenas,
utilizando su interfaz de red.
* NOTA: No es objetivo del presente artículo discutir vulnerabilidades de servicios.
Experimentando con paquetes icmp
Este es nuestro primer objetivo real con iptables. Veamos el estado actual
de nuestras 3 cadenas, listando su contenido, ejecutaremos este comando:
[root@jose /]# /sbin/iptables -nL
Chain INPUT (policy ACCEPT)
target prot opt source destination
Chain FORWARD (policy ACCEPT)
target prot opt source destination
Chain OUTPUT (policy ACCEPT)
target prot opt source destination
Si la salida obtenida no se parece a lo que ven aqui arriba,
significa que no tienen instalados los módulos correspondientes en el kernel,
o que ya hay un firewall funcionando, o para confundirlos más: significa que
ya se ejecutó un script (probablemente en el inicio del sistema) donde se cargan
las reglas del firewall. Consulten la documentación de la distribución que instalaron :P
Suponiendo que la salida que obtuvieron es igual a la escrita aqui arriba,
su situación es la siguiente: quiere decir que el firewall esta aceptando
todos los paquetes, o lo que es lo mismo, no filtra absolutamente nada.
Esto nos indica 2 cosas:
- Las 3 cadenas (INPUT/FORWARD/OUTPUT) estan vacías
- Las 3 cadenas tienen como politica default "ACCEPT".
Es normal que al hacer un ping a localhost (nosotros mismos) recibamos respuesta:
[root@jose /]# ping localhost
PING localhost.localdomain (127.0.0.1) from 127.0.0.1 : 56(84) bytes
of data.
64 bytes from localhost.localdomain (127.0.0.1): icmp_seq=0 ttl=255
time=0.3 ms
64 bytes from localhost.localdomain (127.0.0.1): icmp_seq=1 ttl=255
time=0.1 ms
64 bytes from localhost.localdomain (127.0.0.1): icmp_seq=2 ttl=255
time=0.1 ms
(and so forth...)
Vamos a agregar una regla, para entender el funcionamiento:
[root@jose /]# /sbin/iptables --append INPUT --protocol icmp --source
127.0.0.1 --jump DROP
Qué es esto? Simple: Agregar (append) la siguiente regla a la
cadena INPUT: al recibir un paquete del tipo icmp (--protocol) con origen
(--source) "127.0.0.1" y con cualquier destino (ya que no lo especificamos),
enviamos ese paquete (--jump) a DROP, que por cierto es lo mismo que
dejarlo tirado =).
Que? No pasó nada, verdad? Seguro? Entonces listemos otra vez
las reglas que tenemos cargadas (te olvidaste como se hacia??)
[root@jose /]# /sbin/iptables -nL
Chain INPUT (policy ACCEPT)
target prot opt source destination
DROP icmp -- 127.0.0.1 0.0.0.0/0
Chain FORWARD (policy ACCEPT)
target prot opt source destination
Chain OUTPUT (policy ACCEPT)
target prot opt source destination
Notamos que en la cadena INPUT, se nos agregó una regla,
cuya función es impedir que se haga ping desde nuestra propia máquina.
Podrán notar que esta regla no es demasiado útil a los fines prácticos,
pero si lo es para ejemplificar el uso. Y que es lo que faltaría?
Comprobar su funcionamiento!! por supuesto!!
[root@jose /]# ping localhost
PING localhost.localdomain (127.0.0.1) from 127.0.0.1 : 56(84) bytes
of data.
(y se quedará esperando una respuesta que nunca llegará...)
Nuestro firewall está invisible? Claro que no. De hecho, veremos
que desde la pc "intruso", hacemos ping sin problemas, y nuestra
maquina le responde.
[root@intruso /]# ping jose.iptableslinux.lan
PING jose.iptableslinux.lan (192.168.1.20) from 192.168.1.15 : 56(84)
bytes of data.
64 bytes from 192.168.1.20: icmp_seq=0 ttl=255 time=2.524 msec
64 bytes from 192.168.1.20: icmp_seq=1 ttl=255 time=1.319 msec
Con lo cual, con un simple ping, nuestro amigo operador de "intruso"
puede notar la presencia de nuestra PC. A no desesperar que esto recien comienza.
Cómo funciona?
Puesto que nuestro firewall está filtrando los paquetes ICMP locales,
y deja pasar los remotos. Vamos a cambiar un poco las reglas "del juego". Borramos
la regla que anteriormente agregamos:)
Esto se logra con la opción "--delete", y el resto, es la
transcripción EXACTA de la regla que queremos borrar.
[root@jose /]# /sbin/iptables --delete INPUT --protocol icmp --source
127.0.0.1 --jump DROP
NOTA: conociendo que era la única regla, hubieramos logrado
el mismo resultado escribiendo:
[root@jose /]# /sbin/iptables --delete INPUT 1
Ahora lo que importa: Restringir el acceso a paquetes icmp desde la red!!!
[root@jose /]# /sbin/iptables -A INPUT -p icmp -s 192.168.1.15
-j DROP
Notese que es indistinto escribir -A o --append, -D o --delete, -p o --protocol, etc.
Ahora, listamos nuestra cadena INPUT:
[root@jose /]# /sbin/iptables -nL INPUT
Chain INPUT (policy ACCEPT)
target prot opt source destination
DROP icmp -- 192.168.1.15 0.0.0.0/0
Listo, ahora cualquier paquete icmp con origen en 192.168.1.15
será "DROPeado". Por ello, cuando nuestro amigo curioso hace ping desde su
máquina a la nuestra, jamás tendrá respuesta..
[root@intruso /]# ping jose.mired.lan
PING jose.mired.lan (192.168.1.20) from 192.168.1.15 : 56(84) bytes
of data.
Fantástico. Para nuestro amigo, nuestra máquina está invisible,
por consiguiente, logramos nuestro objetivo de asegurarla. Cierto?
NOOO!! Sacrilegio!! NO no no y nooooO!?
No! NO NO NO!!
Nuestro amigo, extrañado por que el comando ping no respondió,
en un principio pensó que tendríamos la PC apagada, pero luego ejecutó...
[root@intruso /]# ftp jose.mired.lan
Connected to jose.mired.lan.
220 jose FTP server (Version wu-2.6.1-16) ready.
Name (jose:root):
Oh Oh! Nos hemos olvidado que teníamos un FTP server corriendo...
Y corriendo apurados agregamos la siguiente regla:
[root@jose /]# /sbin/iptables -A INPUT -p tcp -s 192.168.1.15 --dport
21 -j DROP
Listamos la cadena INPUT y obtenemos
[root@jose /]# /sbin/iptables -nL INPUT
Chain INPUT (policy ACCEPT)
target prot opt source destination
DROP icmp -- 192.168.1.15 0.0.0.0/0
DROP tcp -- 192.168.1.15 0.0.0.0/0 tcp dpt:21
Por su parte, nuestro "amigo" se aburrirá de esperar hasta que nuestro
FTP le responda (cosa que no sucederá). Interesante, verdad?
Ahora: pensemos lo siguiente, que pasa si en nuestra red, de las 50 PC's
que la conforman, la mayoría son "amigos curiosos" ??. Agregaremos una regla
para cada uno de ellos? Sería un trabajo bastante tedioso, y poco eficiente.
Además, un detalle realmente importante: si bien todas estas reglas, se aplicarían
sin problemas, y son correctas sintácticamente, es MUY ACONSEJABLE seguir la
filosofía de "Todo lo que no está EXPLICITAMENTE permitido, entonces está prohibido".
Como logramos esto? Como primer paso, setearemos la politica por defecto en
DROP (descartar). Cualquier paquete que circula por la cadena INPUT es DROPeado
si no coincide con ninguna regla.
En caso de que estén editando el iptables desde una consola local, pueden ignorar el parrafo siguiente:
NOTA IMPORTANTE: para aquellos que estan toqueteando el iptables remotamente
(ya sea por telnet o ssh), si setean como política DROP para la cadena INPUT
se les caerá la conexión. De hecho me pasó a mi, es por eso que previamente
deberan agregar la siguiente regla:
[root@jose /]# /sbin/iptables -A INPUT -p tcp -s #ORIGEN# --dport #PUERTO# -j DROP
Donde #ORIGEN# es el IP desde donde se están conectando al linux con iptables
y #PUERTO# debe ser el 22 (si es conexion ssh) o 23 (si usan telnet)
Ahora, como decíamos, continuaremos seteando la política de INPUT en DROP (con la opción -P):
[root@jose /]# /sbin/iptables -P INPUT -j DROP
Vaciamos todas las cadenas con la opción -F (flush)
[root@jose /]# /sbin/iptables -F
Listamos lo que tenemos
[root@jose /root]# /sbin/iptables -nL INPUT
Chain INPUT (policy DROP)
target prot opt source destination
Aunque no tengamos ninguna regla, nadie va a poder acceder
a nuestra PC por ningun motivo. Justamente, cualquier paquete que entra, es
expuesto a cada regla de la cadena (en nuestro caso nuestra cadena esta vacía),
al no encontrar coincidencias, el destino del paquete, lo decide la política
de la cadena, osea DROP. El problema aquí es que aunque nosotros podamos realizar
conexiones salientes, las respuestas de los servidores serán descartadas.
Para solucionar esto, agregamos la siguiente regla:
[root@jose /root]# /sbin/iptables -A INPUT -m state --state ESTABLISHED,RELATED
-j ACCEPT
Por la cual dejamos pasar cualquier paquete cuya conexión ya se ha establecido
(ESTABLISHED), o cuya conexión es nueva, pero está relacionada a una conexión ya establecida
(RELATED), según las man pages del iptables. (les dije que las lean, no?)
Por enésima vez, listamos nuestra cadena INPUT:
[root@jose /root]# /sbin/iptables -nL INPUT
Chain INPUT (policy DROP)
target prot opt source destination
ACCEPT all -- 0.0.0.0/0 0.0.0.0/0 state RELATED,ESTABLISHED
Con lo cual, nadie podrá iniciar una conexion a nuestra maquina, salvo que especifiquemos...
Como verán, por defecto tenemos un DROP. Ahora podemos empezar a permitir conexiones.
Acceso Local:
[root@jose /root]# /sbin/iptables -A INPUT -i lo -s 127.0.0.1 -j
ACCEPT
-i lo : Con esto especifico como interfaz de red a localhost (lo)
[root@jose /root]# /sbin/iptables -nL INPUT
Chain INPUT (policy DROP)
target prot opt source destination
ACCEPT all -- 0.0.0.0/0 0.0.0.0/0 state RELATED,ESTABLISHED
ACCEPT all -- 127.0.0.1 0.0.0.0/0
Le permito a "neutral", el acceso ftp (puerto 21)
[root@jose /root]# /sbin/iptables -A INPUT -p tcp -s 192.168.1.18
--dport 21 -j ACCEPT
[root@jose /root]# /sbin/iptables -nL INPUT
Chain INPUT (policy DROP)
target prot opt source destination
ACCEPT all -- 0.0.0.0/0 0.0.0.0/0 state RELATED,ESTABLISHED
ACCEPT all -- 127.0.0.1 0.0.0.0/0
ACCEPT tcp -- 192.168.1.18 0.0.0.0/0 tcp dpt:21
Consejos útiles y comentarios finales
=================================
A medida que vayan interiorizándose en la utilidad iptables, podrán ir permitiendo (o no) el acceso a sus máquinas, redes, servicios, según
el puerto origen o destino, host (o red) de origen, etc. Esta es solo una ínfima parte de lo que pueden realizar con la herramienta
iptables.
Por último, aún no apaguen su máquina. Los cambios realizados 'on-the-fly' a iptables, no quedan guardados en ningún archivo, sino que se
almacenan en memoria, y como sabrán, al reiniciar, los cambios se borrarán. Tengan en cuenta los siguientes consejos:
1) Guarden todas sus reglas en un script prolijo, y con comentarios explicando cada regla o grupo de reglas aplicadas
2) En caso de ejecutar el script automáticamente al iniciar la máquina, consideren hacerlo antes de levantar ningún servicio, o mejor aún,
antes de levantar ninguna interfaz de red. No se preocupen por las reglas que hacen referencia a interfaces no levantadas, igualmente son
aceptadas por iptables.
3) Continúen leyendo la documentación que viene con iptables.
Ojo que esto *no es* un consejo tipo 'RTFM' usual, porque las man pages de iptables
constituye una real fuente y bien completa de información, y por supuesto también,
la infinita documentación que encontrarán en internet
Y finalmente, espero que hayan disfrutado la lectura de estas lineas tanto como yo lo he hecho al escribirlas :)
Me gustaría mucho recibir algún tipo de feedback (no puteadas, por favor),
solo para saber si este documento le resultó util a alguien o si lo desean
linkear o subir a algún sitio. Y por supuesto, sugerencias, correcciones, etc.
dirección: feedback at iptableslinux.com
Conseguí la última versión de este documento en:
http://www.iptableslinux.com
NOTA
Cabe destacar que el servicio FTP es atípico, o al menos diferente a otros protocolos
(TELNET, SSH, etc), el cual posee 2 modos de conexión: activo y pasivo. El modo "activo"
que es soportado por cualquier cliente FTP, además de la conexión hecha desde el cliente al
servidor, el servidor abre otra conexión auxiliar al cliente (desde y hacia cualquier puerto,
salvo que el servidor permita especificar un rango). Por el contrario, con el modo "pasivo",
ambas conexiones son abiertas desde el cliente al servidor (puertos 21 y 20, por defecto).
Esto deberían tenerlo en cuenta cuando uds., siendo los clientes y estando detrás de un firewall,
en una red "protegida", desean conectarse a algún FTP "del otro lado" del firewall.
|