En este artículo vamos a explicar brevemente el firewall iptables. La configuración por Iptables es compleja y complicada. Requiere muchas veces, largas cadenas de parámetros. Por ese motivo suele preferirse utilizar interfaces de firewall como 'ufw' o 'firewallD' pero hay que tener muy presente que siempre por debajo estará Iptables que es el verdadero firewall de Linux. De ahí que se llame a Iptables como el firewall del núcleo o kernel. Profundizando un poco más, debemos decir que iptables es parte de netfilter y sería este el verdadero firewall pero la cultura popular acabó usando el término iptables.
Por la gran cantidad de opciones posibles en este primer artículo solamente es posible cubrir una introducción y una lista de ejemplos
de casos muy comunes. Como siempre recomiendo consultar man iptables
Hacer experimentos con iptables en el firewall de un VPS tiene el riesgo de que por error filtremos las conexiones SSH y no podemos acceder a nuestro propio servidor. Algunos proveedores de servidores tienen un panel de control por web que permite anular la reglas de firewall en caso de emergencia.
Por prudencia, algunas de las prácticas serán demostradas en una máquina virtual en local mientras que otras serán en un VPS real.
Una sentencia con iptables puede tener una estructura como esta:
# iptables -t [tabla] [commando] [cadena] [match] -j [target]
Como veremos pronto en los ejemplos, esta estructura puede ser mas corta o larga dependiendo de la regla a aplicar. Tomaremos esto
como un punto de inicio para comentar algunos de los parámetros comunes que se usan. Aunque no es una extructura exacta nos ayudará
a construir y comprender la creación de reglas. Si ya has consultado la 'manpage' te habras dado cuenta de la magnitud de parámetros
variados que puede tener iptables
.
En la sinopsis anterior de iptables hay 5 partes: tabla, comando, cadena, match y target.
1.- Las tablas puede tener estos valores:
Si se omite la tabla se toma como predeterminada la tabla filter. En este artículo solo veremos ejemplos de este tipo.
2.- En comando se define la acción sobre la cadena. Las más comunes son:
3.- Cada grupo de reglas según sea para entradas, salidas, redireccionar,... se define como una cadena.
Las tablas filter tienen estas cadenas:
Las tablas nat tienen estas cadenas:
Consulta la 'manpage' para cadenas de raw y mangle.
4.- En match con el parámetro -m
o --match
se pueden añadir los llamados módulos o extensiones
opcionales que sirven para poner una o mas condiciones que deben cumplirse. Módulos hay muchos, ejemplos: -m mac –mac-source 00:11:22:33:44:55
para filtrar una MAC, -m multiport –source-port 22,23,80:100
para poner varios puertos o rangos de puertos. Uno que usaremos mucho es
-m conntrack
que se usa conjuntamente con --ctstate
para hacer seguimiento de si el paquete es de una conexión nueva,
una ya establecida en curso, etc. Cuando filtremos los puertos comunes de web http y https hay que incluir en la sentencia
-m conntrack --ctstate NEW,ESTABLISHED
. Mas info en
https://ipset.netfilter.org/iptables-extensions.man.html
Las más comunes son:
5.- -j
viene de jump y el target define que acción realizar con el paquete cuando cumple los criterios del match:
Tras esta saturación de parámetros como conclusión hay que tener muy claro que en la tabla 'filter' tenemos 3 políticas:
y que cada una de estas tablas tendrá unas cadenas que definen las reglas.
Por medio de parámetros podremos añadir, modificar y borrar reglas a la cadena.
Una buena forma de tocar iptables es conociendo cuales son las reglas que actualmente tiene el firewall. Con la siguiente sentencia vemos todas las reglas existentes mostrándolas en líneas numeradas.
sudo iptables --line-numbers -nL
El parámetro --line-numbers
numera cada una de las reglas. Muy útil para borrar reglas por su número. El parámetro nL
en verdad son 2: la n
muestra con números las direcciones IP y puertos. La L
es para Listar reglas.
Se pueden listar entradas o salidas de manera independiente:
# Para entradas añadir INPUT
sudo iptables --line-numbers -nL INPUT
# Para salidas añadir OUTPUT
sudo iptables --line-numbers -nL OUTPUT
# Para redirecionado añadir FORWARD
sudo iptables --line-numbers -nL FORWARD
Para borrar absolutamente todo de iptables y configurarlo de cero tenemos que entrar varias líneas. Antes de nada y para evitar quedarnos sin poder entrar por SSH debemos establecer por defecto que aceptamos todas las conexiones en las 3 políticas.
sudo iptables -P INPUT ACCEPT
sudo iptables -P FORWARD ACCEPT
sudo iptables -P OUTPUT ACCEPT
A continuación podemos borrar todas las reglas de las tablas nat, mangle y filter
sudo iptables -t nat -F
sudo iptables -t mangle -F
sudo iptables -F # como filter es la tabla por defecto no face falta -t filter
Por último, borramos todas las cadenas que no se usan.
sudo iptables -X
Tras esto podemos comprobar que se permiten todas las entradas y salidas y no tenemos reglas. Con el ya conocido comando
iptables -nL
nos aparecen las 3 cadenas limpias.
Los cambios en las tablas no son permanentes. Si reiniciamos el servidor volverá a tener la última configuración predeterminada. Para dejar los cambios permanentes se puede hacer de varias maneras y dependerá sobre que sistema Ubuntu estemos.
Una manera sencilla es con el comando iptables-save
pero veo que en esta máquina no funciona como se espera. Tras
pruebas, consultar manuales y perder mucho tiempo encuentro la solución en Stackoverflow. Es necesario instalar los siguientes
paquetes porque en mi sistema iptables
venía incompleto.
Realmente, fue un poco doloroso encontrar la forma de
que funcione ya que, como siempre pasa, hay muchos versiones de servidores y aparecen excepciones y comportamientos
inesperados. En esta versión de Ubuntu al incorporar el firewall ufw
afecta a la parte de iptables
.
En otros nos podemos encontrar firewallD
que también puede hacer que al manipular directamente las reglas sobre
iptables
no funcione como se espere. Por supuesto que si lo que queremos es gestionar el firewall desde iptables
se debe deshabilitar cualquier otra interfaz de firewall que tengamos. A fecha de hoy 'ufw' o 'firewallD' son dos interfaces que
suelen venir predeterminadas en las distros Ubuntu.
# instalar paquetes para que iptables tenga persistencia
sudo apt install iptables-persistent netfilter-persistent
Tras instalar los paquetes se crearán en /dir/iptables/
los archivos rules.v4
y rules.v6
. Cada
vez que se reinicie el servicio iptables
o el propio servidor se cargará las reglas de estos archivos. Podemos editarlos
manualmente y añadir o
quitar reglas o bien ponemos usar un comando que graba la configuración actual en el archivo. En mi caso como uso el protocolo
IPv4 grabaremos sobre rules.v4
Con el operador de redirección >
cargamos en el archivo la configuración actual de iptables.
sudo /sbin/iptables-save > /etc/iptables/rules.v4
También tenemos la posibilidad de cargar archivos de reglas ya sea porque tenemos varios perfiles de firewall o para restaurar la configuración.
sudo /sbin/iptables-restore < /etc/iptables/otrasreglas.v4
Para comprobar que los cambios son persistentes reiniciamos el servicio iptables.(NOTA: Algo extraño que me ocurre a veces es que tras reiniciar iptables las reglas aparecen duplicadas y tengo que reiniciar el servidor para que salgan solo una vez.)
sudo service iptables restart
Con esto aprendido me sale la duda de que pasará en mis servidores Oracle Cloud. Tras algunos pruebas veo que todo funciona
bien según lo explicado anteriormente. Incluso
aunque trae ufw
instalado, la isntancia de Ubuntu ya incluye la persistencia sin tener que instalar nada. No ocurre
igual en mis pruebas con Hostinger. Tengo también Ubuntu 18.01 con firewallD pero no hay persistencia y se requiere instalar los
paquetes iptables-persistent y netfilter-persistent
. Como ves cada servidor es un mundo.
Aprovechando que estoy en Oracle Cloud vamos a comentar donde crear las reglas en el archivo rules.v4
. Este
servidor está practicamente vacío y solo trae las reglas de origen.
Listando las reglas numeradas de la cadena INPUT obtenemos esto:
sudo iptables --line-numbers -nL INPUT
Vamos a comentar brevemente que significa cada línea:
¿Qué pasaría si no existiera la regla 6? Pues que como anteriormente le hemos dicho que acepte todas las conexiones el servidor quedará expuesto a cualquier entrada venga de donde venga. En el caso concreto de Oracle Cloud como hay también un firewall de infraesctructura salvo que también exista esa peligrosa regla de aceptar todo, no estará el servidor abierto.
¿Qué pasaría si la regla 6 ocupara la posición 1? iptables aplica las reglas en orden de aparición. Esto quiere decir que si la primera regla es rechazar todo, las reglas siguientes no van a funcionar.
El borrado y creación de nuevas reglas se puede hacer de 2 formas: editando el archivo rules.v4 o por comandos. Comentamos
anteriormente que el parámetro (add) añade una regla, -I
inserta una regla en una posición y el comando
-D
(delete) borra. Editar directamente el archivo rules.v4 se tiene que hacer con sumo cuidado y no tener errores
de sintaxis o entrar reglas que no permitan tener control del servidor. Personalmente prefiero probar las reglas con comandos
y cuando veo que funcionan como se espera las hago permanentes.
Siguiendo con el servidor Oracle Cloud vamos a abrir los puertos 80 y 443 de http y https. En el artículo 'Introducción a Oracle Cloud Infraestructura OCI. Parte 2' se explica como crear las reglas del firewall de infraestructura. Aquí solo veremos como crear las reglas del lado VPS Ubuntu.
¿En cual posición de la cadena INPUT colocar la regla? Antes de la regla que rechaza todo que aparece en último lugar. Entonces eso
quiere decir que no puede usar el parámetro -A
ya que añade reglas al final de la cadena. Usaremos -I
con un número de
índice. Si ponemos el 6, ocupará este lugar y la regla REJECT será desplazada a la línea 7.
Llegados a este punto ya se que el comienzo de mi sentencia iptables empezará así: sudo iptables -I INPUT 6
. Traducido
en lenguaje humano "Insertar en la posición 6 de la cadena para entradas INPUT"
La sentencia también debe indicar protocolo y puerto. Para ello -p tcp
y --dport 80
. Como http usa el puerto
tcp 80 se lo indico en la cadena.
El siguiente parámetro es más complejo, el apartado -m
o --match
. Recordemos el enlace donde sacar
la documentación de los módulos:
https://ipset.netfilter.org/iptables-extensions.man.html
En el caso de abrir entradas para http y https se incluye lo siguiente:
-m conntrack --ctstate NEW,ESTABLISHED
. "conntrack" viene de
connection tracking. Es un módulo usado para crear condiciones al tipo de conexión. En este caso a "conntrack" se le añade la opción
ctstate
para condicionar el tipo de conexión del paquete. NEW para nuevas conexiones y ESTABLISHED para conexiones previamente
establecidas.
El último parámetro es la acción: -j ACCEPT
. Si se cumple la regla "se acepta."
La instrucción completa quedará así:
sudo iptables -I INPUT 6 -p tcp --dport 80 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT
Tras esta línea podemos comprobar con sudo iptables --line-numbers -nL INPUT
que en la posición 6 se añadió esta
regla y la regla final REJECT se desplazó a la línea 7.
Ahora para el puerto 443 podemos repetir el código anterior únicamente cambiando el puerto. También sería posible en una sola
línea especificar varios puertos separados por ,
o rangos de puertos separados por :
. Para ello se
usa el módulo multiport. Pronto veremos un ejemplo.
Vamos a borrar esta nueva regla creada en la línea 6 y crearemos una nueva que permita entradas por varios puertos de una vez.
Para eliminar una regla sabiendo su número de línea se usa sudo iptables -D cadena índice
. La D
viene de Delete, la cadena es INPUT
y como índice el valor 6 que corresponde con la línea que queremos eliminar.
La instrucción completa quedará así:
sudo iptables -D INPUT 6
Tras esto añadiremos en una sola orden 2 puertos. Es necesario añadir el módulo multiport
y se cambia el parámetro
dport
por dports
sudo iptables -I INPUT 6 -p tcp -m multiport --dports 80,443 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT
Al usar el multiport
la regla queda concentrada en una sola línea aunque sean varios puertos.
Otra posibilidad para crear reglas es sustituir el número de puerto por el del servicio. Por ejemplo para abrir el puerto 80
se puede poner -p tcp --dport http
, ftp en lugar de 21, https para 443, etc
Todas estas instrucciones en la cadena INPUT para entradas se deberá repetir para la cadena OUTPUT si no tenemos por defecto todas las salidas abiertas. Cada vez es más frecuente en los servidores y redes privadas filtrar las salidas. Será una decisión personal como administras tu accesos a la red.
Vamos a demostrar esto que ocurre cuando cerramos puertos. Actualmente la política de esta instancia de VPS es aceptar todas
las salidas. Esto lo podemos comprobar con el conocido sudo iptables --line-numbers -nL OUTPUT
Para comprobar que en la práctica tenemos salida por el puerto 80 podemos usar el comando curl
a cualquier página
web y la terminal se llenará con caracteres y html que se descarga de esa web. Por ejemplo probamos con
curl www.google.es
o cualquier otra web que se te ocurra y si aparecen muchas líneas con carecteres quiere decir
que hemos descargado algo por URL.
A continuación filtramos la salidas http y https. El formato de la instrucción es idéntico salvo la orden final de ACCEPT, REJECT o DROP. En este caso pondremos REJECT.
sudo iptables -A OUTPUT -p tcp -m multiport --dports http,https -m conntrack --ctstate NEW,ESTABLISHED -j REJECT
Tras este comando las salidas web serán rechazadas. Repetimos nuevamente curl www.google.es
y comprobamos
que el servidor nos rechaza la conexión con mensaje por protocolo ICMP.
¿Que ocurriría si cambiamos la orden de REJECT
por DROP
? Tampoco conseguerimos la conexión pero ahora el
error es muy opaco. Simplemente dice "que no puede establecerse conexión con esa dirección al no encotrar nada". Para una mayor
seguridad del servidor es mejor usar DROP antes que REJECT porque así no damos pistas sobre si realmente existe el servidor evitando
ataques indiscriminados a rangos de direcciones IP.
Como estamos de pruebas y no queremos que los cambies sean permanentes haremos un reboot
para restablecer las reglas.
Se pueden bloquear paquetes dirigidos o prevenientes en función de la IP. Se usa el parámetro -s
de source
(origen) o -d
destination (destino)
# Filtrar los paquetes dirigidos a la IP 10.0.0.100
iptables -A OUTPUT -d 10.0.0.100 -j DROP
# Filtrar los paquetes que vengan de la IP 10.0.0.100
iptables -A INPUT -s 10.0.0.100 -j DROP
# Filtrar los paquetes con origen del segmento de red 10.0.0.0
iptables -A INPUT -s 10.0.0.0/24 -j DROP
# Filtrar los paquetes con origen del segmento de red 10.0.0.0 que entren por puerto 22 SSH
# --dport es --destination-port abreviado
iptables -A INPUT -s 10.0.0.0/24 -p tcp --dport ssh -j DROP
Un ejemplo interesante que encontré es filtrar las conexiones a servidores web externos. Supongamos que en una intranet se quieren evitar las conexiones WEB del exterior. Para ello debe filtrarse el protocolo SYN con destino al puerto 80. Cuando dos equipos inician conexión por tcp, el que inicia el servicio envía un paquete SYN para indicar que está listo. Si filtramos los paquetes SYN no se podrá iniciar la comunicación.
# Filtrar los paquetes SYN
iptables -A OUTPUT -p tcp --syn --dport 80 -j DROP
A continuación una recopilación de reglas para permitir algunos servicios de uso muy común como web, ftp, ping, etc. Es importante tener en cuenta que estas reglas solo las muestro para la cadena INPUT porque tengo una regla que permite todas las conexiones salientes. Si no fuera este el caso habría que crear para cada regla los permisos de OUTPUT.
En los ejemplos hago uso de la orden ACCEPT para determinados puertos según el servicio.
FTP
FTP es el acrónimo de File Transfer Protocol. Se usa para el intercambio de ficheros entre equipos conectados a una red. Las cuentas de FTP son el medio estándar para gestionar el contenido alojado en un servidor web: enviar archivos, descargarlos, crear directorios, borrar ficheros, etc.
Una opción más segura para el intercambio de ficheros es el protocolo SFTP (SSH File Transfer Protocol) que usa cifrado. Utiliza el puerto 22.
Ftp requiere el puerto 20 para la transferencia y 21 para establecer la conexión.
iptables -A INPUT -p tcp -m multiport --dports 20,21 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT
SSH
SSH (o Secure SHell) es un protocolo usado para establecer comunicaciones seguras entre dos sistemas usando una arquitectura cliente/servidor y que permite a los usuarios conectarse a un host remotamente.
Normalmente usa el puerto 22 aunque es recomendable cambiarlo por otro para evitar ataques.
iptables -A INPUT -p tcp -m state --state NEW -m tcp --dport 22 -j ACCEPT
Servicios WEB
Los protocolos http y https requieren los puertos 80 y 443 respectivamente.
iptables -A INPUT -p tcp -m multiport --dports http,https -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT
Samba
Samba es una implementación de código abierto del protocolo Server Message Block (SMB). Permite la interconexión de redes Microsoft Windows, Linux, UNIX y otros sistemas operativos juntos, permitiendo el acceso a archivos basados en Windows y compartir impresoras.
El protocolo Samba requiere los puertos 137,138,139 y 445
sudo iptables -A INPUT -p udp -m udp --dport 137 -j ACCEPT
sudo iptables -A INPUT -p udp -m udp --dport 138 -j ACCEPT
sudo iptables -A INPUT -p tcp -m tcp --dport 139 -j ACCEPT
sudo iptables -A INPUT -p tcp -m tcp --dport 445 -j ACCEPT
Bases de datos MySQL y MariaDB
MySQL es la base de datos mas popular del mundo. Inicialmente era un proyecto software libre pero pasó a manos privadas a Sun MicroSystems en 2008 y posteriormente a Oracle en 2010 que la mantiene con licendia dual pública/comercial. Uno de los desarrolladores originales del proyecto descontento con que pasara a manos privadas creó un nuevo proyecto totalmente libre llamado MariaDB con una muy alta compatibilidad con MySQL.
MySQL y MariaDB están en el puerto 3306. Podemos abrir ese puerto para todas las entradas o ser mas restrictivo y que solo se pueda acceder desde el propio servidor.
# aceptar todas las entradas para MySql
iptables -A INPUT -p tcp -m tcp --dport 3306 -j ACCEPT
# aceptar solo conexiones de MySql desde el propio servidor. Sustituir a.b.c.d por la IP del servidor
iptables -I INPUT 8 -p tcp --dport 33306 -s a.b.c.d -j ACCEPT
Ping
El comando ping
utiliza el protocolo icmp para enviar y recibir respuestas entre dispositivos.
iptables -A INPUT -p icmp -j ACCEPT
iptables -A OUTPUT -p icmp -j ACCEPT
El potente iptables nos permite afinar muchísimo que es lo que queremos filtrar. La siguientes líneas sirven de
ejemplo de como permitir icmp solo del tipo requerido para ping y además teniendo en cuenta las interfaces
de entrada y salida. Recuerda que para saber el nombre de las interfaces se usa ip address
iptables -A OUTPUT -o enp0s3 -p icmp -m icmp --icmp-type echo-request -j ACCEPT
iptables -A INPUT -i enp0s3 -p icmp -m icmp --icmp-type echo-reply -j ACCEPT
DNS Domain Name System
Para resolver dominios con DNS se utiliza el puerto 53 para entradas y salidas en los protocolos tcp y udp. En iptables esto lo hacemos con:
iptables -A INPUT -p tcp --dport 53 -j ACCEPT
iptables -A INPUT -p udp --dport 53 -j ACCEPT
iptables -A OUTPUT -p tcp --dport 53 -j ACCEPT
iptables -A OUTPUT -p udp --dport 53 -j ACCEPT
Servidor Minecraft
Un uso lúdico de los VPS es la creación de servidores de juegos para uso particular entre grupos de amigos. En el caso de Minecraft se necesita permitir los accesos al puerto 25565 para tcp y udp.
iptables -A INPUT -p tcp --dport 25565 -j ACCEPT
iptables -A INPUT -p udp --dport 25565 -j ACCEPT
Desde iptables se pueden crear reglas complejas para protegernos de ataques de DoS (denegación de servicio). Esta técnica consiste en generar una petición masiva de solicitudes desde una misma IP saturando al servidor por no tener capacidad de respuesta y empezará a rechazar peticiones. El filtro es el siguiente:
iptables -A INPUT -p tcp --syn --dport 25565 -m connlimit --connlimit-above 10 -j DROP
Con este ejemplo no se admitirán mas de 10 conexiones simultáneas por cliente al puerto indicado 25565 (usado por Minecraft).
Con lo explicado en este artículo ya sabemos movernos con iptables en la tabla filter pero queda muchísimo por saber. Si quieres profundizar te recomiendo estos enlaces:
http://redesdecomputadores.umh.es/iptables.htm