Bash script para configurar el muro de fuego (firewall)

guivaloz 2009-06-29 13:15 Bash script, Firewall, Gentoo, Kernel

Introducción

Un muro de fuego o cortafuegos (o firewall en inglés) es un parte de un sistema o una red que está diseñado para bloquear el acceso no autorizado, permitiendo al mismo tiempo el paso autorizado de ciertas comunicaciones. ( Fuente: Wikipedia )

GNU/Linux es el mejor sistema operativo. Es confiable, estable y seguro. El simple hecho de usar GNU/Linux y NO Windows nos da una protección mayor que este segundo.

Podemos incrementar la seguridad de nuestro GNU/Linux levantando un muro de fuego. Esto lo recomiendo para equipos portátiles que se conecten a redes públicas. Dicho de otro modo, cuando estés fuera de tu hogar o lugar de trabajo levanta el muro de fuego. Siempre ten presente que pudiera haber algún otro equipo, en la red local o desde internet, buscando vulnerabilidades en nuestra portátil.

El muro de fuego en GNU/Linux está en el mismo kernel, el Linux. Para configurarlo se usa el comando iptables.

Por ejemplo, abre una terminal y ejecuta el siguiente comando:

$ sudo iptables -t filter -L -n

Si tu muro de fuego no está levantado, recibirás un texto parecido al siguiente:

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

Aquí se muestra que no hay filtros en esas cadenas (chains), lo que significa que todas las comunicaciones pasarán sin reestricciones entre tu equipo y la red a la que estés conectado.

A continuación les mostraré el Bash Script que ejecuto para habilitar o deshabiltar el muro de fuego en mi computadora portátil.

Objetivos

Cada quien puede tener sus necesidades particulares. El manejo completo de muros de fuego es un tema muy extenso y hay muchos libros que tratan de ello. Para su servidor, he hecho un script que me resulve tres situaciones:

  • Que cuando necesite trabajar sin muro de fuego pueda limpiar todas las reglas del mismo.
  • Que cuando me encuentre en una red confiable pueda tener abiertos los puertos para ssh, http, samba, rsync y todos los mayores e igual a 1024.
  • Que cuando me encuentre en una red insegura cierre todos los puertos para las peticiones desde afuera hacia mi equipo.

Requerimientos

  • Este script ha sido usado en Gentoo Linux. Teóricamente debería funcionar en cualquier otra distribución GNU/Linux, aunque tal vez necesite algunas modificaciones.
  • Kernel Linux 2.6 con soporte para iptables. Si usa un kernel genérico, como el de Debian o Ubuntu, seguramente tendrá esta opción lista. Si no, vea la configuración de mi kernel 2.6.29
  • Por defecto, el comando iptables sólo lo puede ejecutar root. Si desea ejecutar este script como un usuario normal, deberá poder ejecutar sudo iptables sin que le pida contraseña, de lo contrario se la solicitará muchas veces. Configure sudo si es necesario.

El script firewall.sh

Puede descargar el script aquí o también seleccionar el texto siguiente, copiar y pegar.

#!/bin/bash

#
# firewall.sh - Script para configurar el muro de fuego
# 28 de julio de 2009
# Copyright (C) 2009  Guillermo Valdes Lozano
# http://www.movimientolibre.com/
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#
# Este script ha sido probado en Gentoo Linux. Requiere:
#
# 1) Kernel con soporte para IPTables
#
# 2) Que este instalado "net-firewall/iptables".
#
# 3) Que el comando /sbin/iptables pueda ejecutarse por el usuario.
#    Por defecto, sólo root puede hacerlo. Para ello, instale "app-admin/sudo",
#    luego ejecute "visudo" como root y agregue la siguiente linea:
#
#    %wheel  ALL=(ALL)  NOPASSWD: /sbin/iptables
#

# Nombre de este script
SOY="[Firewall]"

# Comando a ejecutar
CMD="sudo /sbin/iptables"

# Constantes que definen los tipos de errores
EXITO=0
E_NOARGS=65
E_DUPLICADO=98
E_FATAL=99

# Mensaje para mostrar por falta de argumentos
function ayuda() {
	echo "$SOY Ayuda"
	echo
	echo "Objetivo:"
	echo " Configurar el muro de fuego"
	echo
	echo "Sintaxis:"
	echo " firewall.sh <DISPOSITIVO> <CONFIANZA>"
	echo " firewall.sh limpio"
	echo
	echo "Parametros:"
	echo " <DISPOSITIVO> eth0, eth1 o wlan0,"
	echo " <CONFIANZA>   insegura o confiable"
	echo
	echo "Ejemplos:"
	echo " firewall.sh limpio"
	echo " firewall.sh eth0 confiable"
	echo " firewall.sh eth1 inseguro"
	echo
}

# Si el primer parametro es "limpio"
# Vaciamos las cadenas del muro de fuego y
# aceptamos todas las conexiones y terminamos
if [ "$1" = "limpio" ]; then
	echo "$SOY Limpiando las cadenas."
	$CMD -t filter -F
	$CMD -t nat    -F
	echo "$SOY ADVERTENCIA: SE ACEPTARAN TODAS LAS CONEXIONES."
	$CMD -P INPUT   ACCEPT
	$CMD -P OUTPUT  ACCEPT
	$CMD -P FORWARD ACCEPT
	echo "$SOY Script terminado."
	exit $EXITO
fi

#
# Parametros
#

# Primer parámetro: dispositivo de red
case "$1" in
	"eth0")
		INTERFAZ="eth0"
		;;
	"eth1")
		INTERFAZ="eth1"
		;;
	"wlan0")
		INTERFAZ="wlan0"
		;;
	*)
		echo "$SOY ERROR: Dispositivo de red incorrecto."
		ayuda
		exit $E_FATAL
		;;
esac

# Segundo parámetro: confiabilidad de la red
case "$2" in
	"confiable")
		CONFIABILIDAD="confiable"
		;;
	"insegura" | "inseguro")
		CONFIABILIDAD="insegura"
		;;
	*)
		echo "$SOY Por defecto la red se considera INSEGURA."
		CONFIABILIDAD="insegura"
		;;
esac

#
# Proceso
#

# Vaciar las reglas
echo "$SOY Vaciando las reglas actuales."
$CMD -t filter -F
$CMD -t nat    -F

if [ $CONFIABILIDAD = "confiable" ]; then
	echo "$SOY La red es CONFIABLE."
	$CMD -P INPUT   DROP
	$CMD -P OUTPUT  ACCEPT
	$CMD -P FORWARD ACCEPT
fi

if [ $CONFIABILIDAD = "insegura" ]; then
	echo "$SOY La red es INSEGURA."
	$CMD -P INPUT   DROP
	$CMD -P OUTPUT  DROP
	$CMD -P FORWARD DROP
fi

echo "$SOY Aceptar toda entrada y salida por loopback."
$CMD -A INPUT  -i lo -j ACCEPT
$CMD -A OUTPUT -o lo -j ACCEPT

echo "$SOY Aceptar las comunicaciones establecidas."
$CMD -A INPUT -m state --state ESTABLISHED -j ACCEPT
$CMD -A INPUT -m state --state RELATED     -j ACCEPT

if [ $CONFIABILIDAD = "insegura" ]; then
	echo "$SOY SOLO PERMITIR la salida de paquetes nuevos y establecidos."
	$CMD -A OUTPUT -m state --state NEW,RELATED,ESTABLISHED -j ACCEPT
fi

if [ $CONFIABILIDAD = "confiable" ]; then

	echo "$SOY Aceptar SSH (TCP 22) en $INTERFAZ"
	$CMD -A INPUT -i $INTERFAZ -p tcp --dport 22 -j ACCEPT

	echo "$SOY Aceptar HTTP (TCP 80) en $INTERFAZ"
	$CMD -A INPUT -i $INTERFAZ -p tcp --dport 80 -j ACCEPT

	echo "$SOY Aceptar Samba (TCP y UDP 137, 138, 139, 445) en $INTERFAZ"
	$CMD -A INPUT -i $INTERFAZ -p tcp --dport 137:139 -j ACCEPT
	$CMD -A INPUT -i $INTERFAZ -p udp --dport 137:139 -j ACCEPT
	$CMD -A INPUT -i $INTERFAZ -p tcp --dport 445     -j ACCEPT
	$CMD -A INPUT -i $INTERFAZ -p udp --dport 445     -j ACCEPT

	echo "$SOY Aceptar rsync (TCP 873) en $INTERFAZ"
	$CMD -A INPUT -i $INTERFAZ -p tcp --dport 873 -j ACCEPT

	echo "$SOY ACEPTAR TODO por puertos del 1024 al 65535 en $INTERFAZ"
	$CMD -A INPUT -i $INTERFAZ -p tcp --dport 1024:65535 -j ACCEPT
	$CMD -A INPUT -i $INTERFAZ -p udp --dport 1024:65535 -j ACCEPT

	echo "$SOY Aceptar pings."
	$CMD -A INPUT -p icmp -j ACCEPT

fi

# Para depurar quite los comentarios a los siguientes comandos
# echo "$SOY Configuración lista:"
# $CMD -L -n -v -t filter
# echo

# Mensaje final
echo "$SOY Script terminado."

Haga el script ejecutable

No olvide hacer ejecutable el archivo firewall.sh con el siguiente comando:

# chmod a+x firewall.sh

Probando el script

Con el parámetro limpio vacío las cadenas y deshabilito el muro de fuego.

$ ./firewall.sh limpio
[Firewall] Limpiando las cadenas.
[Firewall] ADVERTENCIA: SE ACEPTARAN TODAS LAS CONEXIONES.
[Firewall] Script terminado.

Indicando el dispositivo de red (en mi caso es eth1) y la opción confiable.

$ ./firewall.sh eth1 confiable
[Firewall] Vaciando las reglas actuales.
[Firewall] La red es CONFIABLE.
[Firewall] Aceptar toda entrada y salida por loopback.
[Firewall] Aceptar las comunicaciones establecidas.
[Firewall] Aceptar SSH (TCP 22) en eth1
[Firewall] Aceptar HTTP (TCP 80) en eth1
[Firewall] Aceptar Samba (TCP y UDP 137, 138, 139, 445) en eth1
[Firewall] Aceptar rsync (TCP 873) en eth1
[Firewall] ACEPTAR TODO por puertos del 1024 al 65535 en eth1
[Firewall] Aceptar pings.
[Firewall] Script terminado.

Podemos ver el resultado de la ejecución anterior con el comando:

$ sudo iptables -t filter -L -n -v

El cual me entrega lo siguiente:

Chain INPUT (policy DROP 12 packets, 2496 bytes)
 pkts bytes target  prot opt in     out  source     destination
    0     0 ACCEPT  all  --  lo     *    0.0.0.0/0  0.0.0.0/0
  572  601K ACCEPT  all  --  *      *    0.0.0.0/0  0.0.0.0/0    state ESTABLISHED
    0     0 ACCEPT  all  --  *      *    0.0.0.0/0  0.0.0.0/0    state RELATED
    0     0 ACCEPT  tcp  --  eth1   *    0.0.0.0/0  0.0.0.0/0    tcp dpt:22
    0     0 ACCEPT  tcp  --  eth1   *    0.0.0.0/0  0.0.0.0/0    tcp dpt:80
    0     0 ACCEPT  tcp  --  eth1   *    0.0.0.0/0  0.0.0.0/0    tcp dpts:137:139
    3   405 ACCEPT  udp  --  eth1   *    0.0.0.0/0  0.0.0.0/0    udp dpts:137:139
    0     0 ACCEPT  tcp  --  eth1   *    0.0.0.0/0  0.0.0.0/0    tcp dpt:445
    0     0 ACCEPT  udp  --  eth1   *    0.0.0.0/0  0.0.0.0/0    udp dpt:445
    0     0 ACCEPT  tcp  --  eth1   *    0.0.0.0/0  0.0.0.0/0    tcp dpt:873
    0     0 ACCEPT  tcp  --  eth1   *    0.0.0.0/0  0.0.0.0/0    tcp dpts:1024:65535
    0     0 ACCEPT  udp  --  eth1   *    0.0.0.0/0  0.0.0.0/0    udp dpts:1024:65535
    0     0 ACCEPT  icmp --  *      *    0.0.0.0/0  0.0.0.0/0

Chain FORWARD (policy ACCEPT 0 packets, 0 bytes)
 pkts bytes target  prot opt in     out  source     destination

Chain OUTPUT (policy ACCEPT 514 packets, 37799 bytes)
 pkts bytes target  prot opt in     out  source     destination
    0     0 ACCEPT  all  --  *      lo   0.0.0.0/0  0.0.0.0/0

Por último, pruebo la opción insegura.

$ ./firewall.sh eth1 insegura
[Firewall] Vaciando las reglas actuales.
[Firewall] La red es INSEGURA.
[Firewall] Aceptar toda entrada y salida por loopback.
[Firewall] Aceptar las comunicaciones establecidas.
[Firewall] SOLO PERMITIR la salida de paquetes nuevos y establecidos.
[Firewall] Script terminado.

Personalízelo

Por ejemplo, si ejecuta un servidor FTP, agregue las siguientes líneas en la parte confiable del programa:

	echo "$SOY Aceptar conexiones por FTP."
	$CMD -A INPUT -i $INTERFAZ -p tcp --dport 20 -j ACCEPT
	$CMD -A INPUT -i $INTERFAZ -p tcp --dport 21 -j ACCEPT

Si tiene un servidor apache y páginas por https, agregue:

	echo "$SOY Aceptar HTTPS (TCP 443) en $INTERFAZ"
	$CMD -A INPUT -i $INTERFAZ -p tcp --dport 443 -j ACCEPT

Accesos directos para ejecutarlo

Yo he depositado el script firewall.sh en un directorio llamado bin en mi HOME. Los siguientes archivos desktop ejecutarán el script con los parámetros que llevan. Este es para limpiar el muro de fuego:

[Desktop Entry]
Encoding=UTF-8
Name=Firewall Limpio
Comment=Sin firewall, todo permitido
Type=Application
Exec=$HOME/bin/firewall.sh limpio
StartupNotify=false
Terminal=true
TerminalOptions= --noclose
Icon=decrypted

Este para ponerlo en confiable:

[Desktop Entry]
Encoding=UTF-8
Name=Firewall eth1 Confiable
Comment=Firewall eth1 Confiable
Type=Application
Exec=$HOME/bin/firewall.sh eth1 confiable
StartupNotify=false
Terminal=true
TerminalOptions= --noclose
Icon=halfencrypted

Y este es para una red insegura:

[Desktop Entry]
Encoding=UTF-8
Name=Firewall eth1 Insegura
Comment=Firewall eth1 Insegura
Type=Application
Exec=$HOME/bin/firewall.sh eth1 insegura
StartupNotify=false
Terminal=true
TerminalOptions= --noclose
Icon=encrypted

Estos tres archivos desktop los coloco dentro del directorio kde-launcher que está en $HOME/bin. Luego, en KDE agrego un navegador de archivos que apunta a ese directorio para ejecutarlos fácilmente. Así se ven: