Un cortafuegos sencillo con UFW
Básico
Los pasos marcados en azul cambian según tu sistema.
En cuanto un servidor tiene IP pública, empiezan las visitas: programas que recorren internet llamando a todos los puertos para ver qué contesta. Un cortafuegos es el portero que decide quién pasa. Y en Ubuntu y Debian, la forma más amable de tenerlo es UFW (Uncomplicated Firewall).
UFW no filtra nada por sí mismo: escribe por ti las reglas del filtrado de paquetes del kernel, que a mano son farragosas. Tú le dices ufw allow ssh y él se encarga del resto.
La idea de fondo es sencilla: cerrarlo todo y abrir solo lo que uses. En este tutorial lo dejarás funcionando así, aprenderás a abrir y cerrar puertas con precisión y verás qué hacer para que siga siendo útil con el paso del tiempo.
Lo que necesitas
Un servidor con Ubuntu o Debian y un usuario con sudo. Si partes de cero, empieza por los primeros pasos de un servidor Linux.
Ubuntu
En Ubuntu, UFW viene de serie (aunque apagado). Si no lo tuvieras, sudo apt install ufw.
Debian
En Debian hay que instalarlo:
sudo apt install ufw
El orden importa. Hasta que no hayas permitido SSH, no actives el cortafuegos: te cerraría la puerta en las narices y no podrías volver a entrar.
Paso 1: que también vigile IPv6
Muchos servidores tienen dirección IPv4 e IPv6. Si el cortafuegos solo filtrara la primera, la segunda sería una entrada sin vigilar. Comprueba que UFW se ocupa de las dos:
sudo grep IPV6 /etc/default/ufw
IPV6=yes
Si aparece no, cámbialo a yes en ese archivo (sudo nano /etc/default/ufw). A partir de ahí, cada regla que crees tendrá su gemela para IPv6, marcada con (v6) en los listados.
Paso 2: cerrar por defecto
Las políticas por defecto se aplican a todo lo que no encaje con ninguna regla. Para un servidor, la combinación sensata es no dejar entrar nada y dejar salir todo:
sudo ufw default deny incoming
sudo ufw default allow outgoing
Así, el servidor puede seguir descargando actualizaciones o consultando otros servicios, pero nadie de fuera puede iniciar una conexión con él. Las excepciones las irás añadiendo tú.
Paso 3: dejar pasar SSH
La primera excepción tiene que ser la que te permite administrar el servidor. Puedes escribirla de tres formas, todas equivalentes:
- Por perfil de aplicación:
sudo ufw allow OpenSSH. Los perfiles los dejan algunos programas al instalarse; los ves consudo ufw app list. - Por nombre de servicio:
sudo ufw allow ssh. UFW sabe qué puerto corresponde a cada servicio conocido. - Por puerto:
sudo ufw allow 22/tcp. Si moviste SSH a otro puerto, pon ese.
Elige una:
sudo ufw allow OpenSSH
Rule added
Rule added (v6)
Mejor aún: con límite
Si cambias allow por limit, SSH sigue abierto, pero una IP que abra seis conexiones o más en treinta segundos queda bloqueada un rato. A un humano no le afecta; a un bot que prueba contraseñas, sí:
sudo ufw limit OpenSSH
Para una defensa más fina, que lea los registros y deje fuera durante horas a quien falla al identificarse, añade luego Fail2Ban.
Paso 4: encenderlo
Repasa lo que has preparado (funciona con el cortafuegos todavía apagado):
sudo ufw show added
Added user rules (see 'ufw status' for running firewall):
ufw limit OpenSSH
Si está la regla de SSH, adelante:
sudo ufw enable
Command may disrupt existing ssh connections. Proceed with operation (y|n)? y
Firewall is active and enabled on system startup
El aviso es normal: como SSH está permitido, tu sesión sigue viva. Responde y. Desde este momento el cortafuegos está activo y se pondrá en marcha solo en cada arranque.
Abrir puertas, con precisión
Ahora toca permitir lo que tu servidor tenga que ofrecer. Cuanto más concreta sea cada regla, mejor.
Servicios habituales
sudo ufw allow http
sudo ufw allow https
Si tienes Nginx o Apache, sus perfiles abren los dos puertos web de una vez: sudo ufw allow 'Nginx Full' o sudo ufw allow 'Apache Full'.
Un rango de puertos
Para rangos, UFW exige que digas el protocolo:
sudo ufw allow 60000:61000/udp
(Ese, por cierto, es el rango que usa Mosh, una alternativa a SSH para conexiones inestables.)
Solo para una dirección
Si un servicio solo lo usas tú desde casa, o solo lo consulta otra máquina tuya, no hace falta abrirlo a todo internet. Con from limitas el origen:
sudo ufw allow from 203.0.113.4 to any port 22 proto tcp
Y con notación CIDR, a toda una red. Por ejemplo, tu red local:
sudo ufw allow from 192.168.1.0/24 to any port 445 proto tcp
Solo por una interfaz
Si el servidor tiene una tarjeta hacia internet y otra hacia una red privada, puedes abrir cada servicio solo por donde corresponde. Mira cómo se llaman las interfaces:
ip -br addr
lo UNKNOWN 127.0.0.1/8 ::1/128
eth0 UP 203.0.113.10/24
eth1 UP 10.0.0.5/24
Y crea la regla para una sola. Por ejemplo, la base de datos (MariaDB o PostgreSQL) solo desde la red privada:
sudo ufw allow in on eth1 to any port 5432 proto tcp
Cerrar puertas
Con la entrada cerrada por defecto, casi nunca necesitarás reglas de bloqueo. Sirven para excepciones dentro de un permiso, como vetar una IP que te está dando guerra:
sudo ufw insert 1 deny from 198.51.100.7
El insert 1 la coloca la primera de la lista. Importa porque UFW revisa las reglas en orden y aplica la primera que encaja: si la pusieras al final, un allow anterior podría dejarla sin efecto.
También puedes cerrar el camino de salida a algo concreto. Por ejemplo, que nada en el servidor pueda enviar correo directamente:
sudo ufw deny out 25/tcp
Ver lo que hay
sudo ufw status verbose
Status: active
Logging: on (low)
Default: deny (incoming), allow (outgoing), disabled (routed)
New profiles: skip
To Action From
-- ------ ----
22/tcp (OpenSSH) LIMIT IN Anywhere
80/tcp ALLOW IN Anywhere
443/tcp ALLOW IN Anywhere
22/tcp (OpenSSH (v6)) LIMIT IN Anywhere (v6)
80/tcp (v6) ALLOW IN Anywhere (v6)
443/tcp (v6) ALLOW IN Anywhere (v6)
Arriba, las políticas por defecto; abajo, tus reglas, cada una con su versión IPv6.
Quitar reglas
La forma más limpia es repetir la regla con delete delante. Así desaparecen a la vez la de IPv4 y la de IPv6:
sudo ufw delete allow http
Rule deleted
Rule deleted (v6)
También puedes borrar por número. Lista las reglas numeradas:
sudo ufw status numbered
To Action From
-- ------ ----
[ 1] 22/tcp (OpenSSH) LIMIT IN Anywhere
[ 2] 443/tcp ALLOW IN Anywhere
[ 3] 22/tcp (OpenSSH (v6)) LIMIT IN Anywhere (v6)
[ 4] 443/tcp (v6) ALLOW IN Anywhere (v6)
Y borra la que quieras con sudo ufw delete 2. Dos detalles: por número solo se va esa línea (su gemela IPv6 sigue ahí), y tras cada borrado los números se recolocan, así que vuelve a listar antes del siguiente.
Apagar o empezar de nuevo
sudo ufw disable apaga el cortafuegos sin borrar nada; sudo ufw enable lo vuelve a encender tal cual estaba.
Si prefieres partir de cero:
sudo ufw reset
Desactiva el cortafuegos y borra todas las reglas (guarda una copia en /etc/ufw por si acaso). Las políticas por defecto no se tocan: revísalas antes de volver a activarlo.
Que siga siendo útil
Un cortafuegos no se configura un día y se olvida. Estos hábitos marcan la diferencia.
Cada puerta abierta, una decisión
Abre lo mínimo para que tus servicios funcionen y, cuando un servicio solo lo necesite una máquina, limítalo a su dirección. Cuantas menos puertas haya, menos hay que vigilar.
Revisión periódica
Lo que abriste para una prueba hace meses quizá siga abierto. Cada cierto tiempo (pon un recordatorio trimestral), mira sudo ufw status numbered y pregúntate de cada regla si el servicio sigue existiendo, si tiene que estar abierta a todo el mundo y si se puede acotar más. Lo que no haga falta, fuera.
Los registros
Con el registro activado, sabrás quién llama a tu puerta:
sudo ufw logging on
Los bloqueos quedan en /var/log/ufw.log o, si tu sistema no tiene rsyslog, en el registro del kernel (sudo journalctl -k | grep UFW). Cada línea [UFW BLOCK] dice de dónde venía el paquete (SRC), a qué puerto iba (DPT) y con qué protocolo (PROTO). Una misma IP tocando muchos puertos distintos en poco tiempo está haciendo un reconocimiento de tu servidor.
Docker va por libre
Si alojas servicios con Docker, ojo: los puertos que publicas con -p no pasan por UFW, porque Docker escribe sus propias reglas por delante. Un contenedor publicado como -p 8080:80 queda accesible desde internet aunque UFW diga lo contrario. Si un contenedor solo debe atenderse desde el propio servidor (por ejemplo, detrás de un proxy inverso), publícalo así: -p 127.0.0.1:8080:80.
Controlar también lo que sale (avanzado)
Dejar salir todo es cómodo, pero si alguien llegara a colarse en tu servidor, podría usarlo para sacar datos o atacar a otros. En servidores delicados puedes invertir la política de salida y permitir solo lo imprescindible:
sudo ufw default deny outgoing
sudo ufw allow out 53
sudo ufw allow out 123/udp
sudo ufw allow out 80/tcp
sudo ufw allow out 443/tcp
Con eso funcionan la resolución de nombres (53), la hora (123) y las descargas y APIs web (80 y 443). Cualquier otro servicio que necesite salir tendrá que tener su regla, así que planifícalo antes de aplicarlo.
Comprobar desde fuera
Lo que de verdad importa es cómo ve tu servidor el resto del mundo. Desde otra máquina, con nmap, mira qué puertos responden:
nmap -Pn -p 22,80,443,3306 tu_servidor
Los que abriste deben salir como open; los demás, filtered o closed.
Hecho
Tu servidor ya solo atiende lo que tú has decidido, y sabes cómo abrir, acotar y cerrar cada puerta. El siguiente paso lógico es Fail2Ban, que añade una defensa activa contra quien insiste en adivinar contraseñas.
