footer-logofooter-logo
Reglas de firewall por defecto sensatas para un servidor LinuxVolver

Reglas de firewall por defecto sensatas para un servidor Linux

Un servidor Linux recién instalado no trae ninguna regla de firewall. Cada listener que arrancas queda accesible al instante desde todo internet. La solución es una base corta y bien entendida: default-deny en la entrada, permitir el tráfico established, y una allow-list explícita para los servicios que realmente ejecutas. Esta guía construye esa base en nftables regla por regla, explica las partes en las que todo el mundo se equivoca (ICMP e IPv6), muestra la misma política en ufw y firewalld, y explica cómo evitar quedarte fuera de tu propio servidor, además de la trampa de la publicación de puertos en Docker.

08 de julio de 2026

por Jesse Schokker

Firewall

nftables

Security

Linux

Loading...

La respuesta primero: la filosofía de las tres reglas

Todo buen firewall de servidor se construye sobre las mismas tres decisiones; en cuanto las interiorizas, el resto es detalle:

  1. Default-deny en la entrada. La política para el tráfico entrante es drop. Todo lo que no esté explícitamente permitido nunca llega a un servicio, incluido el servicio que se te olvidó que tenías corriendo.
  2. Permite el tráfico established y related. Las respuestas a conexiones que tu servidor inició, y la mitad de vuelta de las conexiones que aceptaste, fluyen sin restricción. Esta única regla con estado es lo que hace que default-deny sea viable.
  3. Pon tus servicios explícitamente en la allow-list. SSH, tus puertos web, y nada más, hasta que algo más necesite de verdad ser alcanzable.

El tráfico saliente se queda en default-allow en la mayoría de los servidores; el filtrado de salida tiene usos reales, pero es un paso de hardening opcional, no parte de una configuración por defecto sensata (más sobre esto hacia el final).

Esto importa porque una instalación recién hecha no te da nada de eso. Debian y Ubuntu se distribuyen sin ninguna regla de firewall activa; cada daemon que se enlaza a 0.0.0.0 queda expuesto a internet en el momento en que arranca, y estudios con honeypots muestran que las primeras sondas llegan a los pocos minutos de que una IP se ponga en marcha. Un firewall base es cosa de diez minutos. Aquí está.

El conjunto de reglas base, en nftables

nftables es el framework de firewall nativo en todas las grandes distribuciones actuales (el comando iptables en un sistema moderno es en realidad una capa de compatibilidad sobre nftables), y una sola tabla inet cubre IPv4 e IPv6 a la vez, lo que elimina de raíz el error de firewall más común. Guarda esto como /etc/nftables.conf:

#!/usr/sbin/nft -f
flush ruleset

table inet filter {
  chain input {
    type filter hook input priority filter; policy drop;

    # 1. Loopback is always fine
    iif "lo" accept

    # 2. The stateful core: replies and related traffic
    ct state established,related accept
    ct state invalid drop

    # 3. ICMP — the minimum the internet needs to work
    icmp type { echo-request, destination-unreachable, time-exceeded, parameter-problem } accept
    icmpv6 type { echo-request, destination-unreachable, packet-too-big, time-exceeded, parameter-problem,
                  nd-router-advert, nd-neighbor-solicit, nd-neighbor-advert } accept

    # 4. Your services — the explicit allow-list
    tcp dport 22 accept comment "SSH"
    tcp dport { 80, 443 } accept comment "HTTP/HTTPS"
    udp dport 443 accept comment "HTTP/3 (remove if unused)"

    # 5. Optional: see what you're dropping, without flooding the log
    limit rate 5/minute log prefix "input drop: "
  }

  chain forward {
    type filter hook forward priority filter; policy drop;
  }

  chain output {
    type filter hook output priority filter; policy accept;
  }
}

Aplícalo con nft -f /etc/nftables.conf y hazlo persistente con systemctl enable nftables. (En Debian y Ubuntu, el paquete nftables conecta exactamente este archivo con ese servicio.)

Ahora el razonamiento, regla por regla.

Regla 1: loopback

Un sinfín de componentes de un sistema Linux hablan consigo mismos por 127.0.0.1 y ::1: resolvers, bases de datos, agentes de métricas. Aceptar lo sin condiciones es seguro (el tráfico de loopback nunca llega por el cable), y olvidarlo produce, bajo una política drop, fallos desconcertantes.

Regla 2: el núcleo con estado

ct state established,related accept es la regla que hace la mayor parte del trabajo. El connection tracking (conntrack) recuerda cada flujo que el firewall ha aprobado; esta regla deja pasar el resto de cada conversación aprobada sin reevaluar la allow-list en cada paquete. related cubre protocolos que legítimamente generan flujos secundarios (un error ICMP sobre tu conexión, los canales de datos de FTP).

ct state invalid drop descarta paquetes que no pertenecen a ningún flujo conocido y tampoco pueden iniciar uno nuevo válido: ACKs perdidos fuera de ventana, basura de escáneres en mitad de una conexión, restos de ataques. Descartarlos pronto es higiene gratis.

Una advertencia honesta: conntrack mismo es estado, y el estado se puede agotar; eso es exactamente lo que buscan los ataques DDoS por familia de protocolo. Para un servidor normal, los valores por defecto más las SYN cookies bastan; si ejecutas un servicio con una tasa de conexiones muy alta, subir net.netfilter.nf_conntrack_max tiene su sitio en tu lista de ajustes.

Regla 3: ICMP bien hecho

El mito de firewall más persistente es: "bloquea el ping, bloquea ICMP, es más seguro". No lo es; rompe el plano de control de internet:

  • destination-unreachable (y dentro de él, el fragmentation-needed de IPv4) es cómo funciona Path MTU Discovery. Descártalo y las conexiones que pasan por cualquier ruta con MTU menor (VPN, túneles, algún tránsito) se quedan colgadas misteriosamente en transferencias grandes, el clásico bug de "SSH funciona pero SCP se atasca".
  • packet-too-big es el equivalente en IPv6, y IPv6 es más estricto: los routers nunca fragmentan en IPv6, así que PMTUD es obligatorio. Descartar ICMPv6 en bloque rompe IPv6, punto.
  • El neighbour discovery (nd-neighbor-solicit, nd-neighbor-advert, nd-router-advert) es el ARP de IPv6. Descártalo y tu servidor pierde su gateway por defecto en cuanto caduca la neighbour cache.
  • echo-request (ping) merece la pena permitirlo en un servidor: no cuesta prácticamente nada (tu dirección es igualmente descubrible por escáneres) y vas a querer hacer ping a tu propia máquina mientras depuras. Si te preocupan las inundaciones de ping, limita su tasa (icmp type echo-request limit rate 10/second accept) en lugar de descartarlo.

La base de arriba permite exactamente los mensajes de control que los protocolos necesitan, y nada más: sin timestamps, sin redirects, sin router solicitations que no necesites en la entrada.

Regla 4: la allow-list

Esta sección debería ser un inventario preciso de para qué sirve el servidor. Dos hábitos la mantienen honesta:

  • Añade un comment a cada regla. Dentro de seis meses, tcp dport 8472 accept comment "flannel VXLAN" es documentación; un número de puerto pelado es arqueología.
  • Cuando un servicio es interno, dilo en la regla. Si PostgreSQL solo debe ser alcanzable desde tus servidores de aplicación en una red privada, codifícalo: ip saddr 10.0.0.0/24 tcp dport 5432 accept, no un allow pelado al puerto 5432. (Las direcciones aquí y más abajo son rangos de documentación; sustitúyelas por las tuyas.)

Regla 5: registrar los descartes

Registrar todos los descartes en un servidor expuesto a internet es una máquina de llenar el disco; el escaneo de fondo es constante. La muestra limit rate 5/minute te da suficiente en journalctl -k para notar patrones (y depurar esos momentos de "por qué no puedo llegar a mi nuevo servicio": si ves el descarte registrado, el firewall es tu problema) sin el ruido.

La mitad olvidada: la paridad IPv6

El modo de fallo histórico: un conjunto de reglas iptables cuidado para IPv4, mientras ip6tables nunca se tocaba, dejando cada servicio completamente abierto por IPv6 en máquinas dual-stack. Los estudios de aquella época encontraban regularmente exposición v6 que la gente no sabía que tenía.

La base de arriba es inmune por construcción: una tabla nftables de tipo inet aplica cada regla a ambas familias a la vez, y las reglas ICMPv6 le dan a v6 exactamente los mensajes de control que necesita. Si usas ufw o firewalld en su lugar, ambos también gestionan v6 junto a v4 por defecto. La regla general sobrevive a cualquier elección de herramienta: cada decisión de firewall que tomes debe valer para las dos familias de direcciones. Si tu servidor tiene un registro AAAA, prueba tus servicios por v6 después de aplicar las reglas, no solo por v4.

Limitar la tasa de SSH (y dónde encaja fail2ban)

El puerto 22 va a recibir intentos de fuerza bruta para siempre; con autenticación solo por clave, esos intentos son ruido, pero limitarlos a bajo coste sigue mereciendo la pena. nftables puede limitar la tasa por origen de forma nativa con un set dinámico. Sustituye la regla SSH simple por:

    set ssh_ratelimit {
      type ipv4_addr
      flags dynamic
      timeout 10m
    }
    set ssh_ratelimit6 {
      type ipv6_addr
      flags dynamic
      timeout 10m
    }

    tcp dport 22 ct state new add @ssh_ratelimit { ip saddr limit rate 4/minute } accept comment "SSH v4, per-IP throttle"
    tcp dport 22 ct state new add @ssh_ratelimit6 { ip6 saddr limit rate 4/minute } accept comment "SSH v6, per-IP throttle"

Cada dirección de origen recibe cuatro conexiones SSH nuevas por minuto; la quinta se descarta en silencio hasta que su tasa baja. Esto es ciego a la intención (no distingue un login fallido de uno exitoso), por lo que fail2ban sigue siendo un complemento necesario: lee el log de autenticación y bloquea direcciones que de verdad fallan al autenticarse. Limitar la tasa en el firewall, bloquear por evidencia con fail2ban, y exigir claves; la combinación acaba con el problema de ruido en SSH.

La misma base en ufw y firewalld

Si la convención de tu distribución es un frontend, usa el frontend; mezclar reglas escritas a mano con las reglas de un frontend es exactamente donde los firewalls se vuelven arqueología. La misma política:

ufw (el frontend por defecto de Ubuntu; por debajo maneja iptables, que acaba en nftables vía la capa de compatibilidad):

ufw default deny incoming
ufw default allow outgoing
ufw limit 22/tcp comment 'SSH, rate-limited'
ufw allow 80/tcp
ufw allow 443
ufw enable

ufw limit aplica un límite antifuerza bruta incorporado (seis conexiones en treinta segundos por origen). ufw gestiona por ti loopback, conntrack y valores por defecto de ICMP sensatos.

firewalld (el valor por defecto de la familia RHEL, basado en nftables desde 2018):

firewall-cmd --set-default-zone=public
firewall-cmd --permanent --zone=public --add-service=ssh
firewall-cmd --permanent --zone=public --add-service=http
firewall-cmd --permanent --zone=public --add-service=https
firewall-cmd --reload

firewalld piensa en zonas y servicios en lugar de reglas; la zona public es default-deny con los servicios listados abiertos, e ICMP se gestiona con sensatez de fábrica.

Cómo no quedarte fuera de tu propio servidor

Los cambios de firewall en un servidor remoto son la caída autoinfligida clásica. Los hábitos que los vuelven aburridos:

  • Comprueba la sintaxis antes de aplicar: nft -c -f /etc/nftables.conf valida el archivo sin cargarlo.
  • Programa un rollback automático antes de cambios arriesgados, y cancélalo en cuanto confirmes que sigues teniendo acceso:
# revert to the known-good ruleset in 3 minutes unless cancelled
systemd-run --on-active=3m --unit=fw-rollback nft -f /etc/nftables.known-good
# ...apply your change, confirm SSH still works, then cancel the rollback:
systemctl stop fw-rollback.timer

Y mantén una segunda sesión SSH abierta durante todo el proceso; guarda el nuevo conjunto de reglas como known-good solo después de que una conexión nueva tenga éxito.

  • Nunca pruebes un cambio de política default-drop sin acceso out-of-band. En un servidor dedicado eso significa acceso a consola KVM-over-IP/IPMI (incluido en nuestros servidores), que convierte un bloqueo de un incidente en un arreglo de dos minutos.
  • El orden importa menos en nftables de lo que temes, pero la regla accept-established debe preceder a tus drops, y todo el archivo se aplica de forma atómica con nft -f, así que un conjunto de reglas cargado a medias no puede dejarte tirado como sí podían históricamente los comandos iptables secuenciales.

Docker se salta ufw y tu cadena input

Si ejecutas Docker con puertos publicados (-p 8080:80), debes saber esto: Docker programa directamente las reglas de NAT y forwarding del kernel, y los puertos publicados se saltan ufw y tu cadena input por completo. Tu firewall cuidadosamente configurado en default-deny no se aplica a los contenedores publicados con -p; el tráfico se redirige antes de que tu cadena input llegue siquiera a verlo. Esto es un comportamiento documentado y de toda la vida, no un bug que se vaya a arreglar en la próxima versión (el backend nativo de nftables de Docker Engine 29 es experimental y no cambia la semántica).

Las soluciones aceptadas:

  • Enlaza los puertos que no quieras públicos a localhost o a una dirección privada: -p 127.0.0.1:8080:80 o -p 10.0.0.5:5432:5432.
  • Pon las restricciones en la cadena DOCKER-USER, que Docker garantiza evaluar antes que sus propias reglas: ese es el gancho soportado para "ponerle firewall a mis contenedores".
  • Trata cada -p con un puerto pelado como si "esto ya está en internet", porque lo está.

Filtrado de salida: cuándo merece la pena

Una política de salida default-accept es lo correcto por defecto: los servidores hablan legítimamente hacia fuera con mirrors de paquetes, DNS, NTP, APIs y monitorización. El filtrado de salida justifica su complejidad en situaciones concretas: regímenes de cumplimiento normativo que exigen control de egress, servidores que procesan entradas no confiables donde quieres frenar reverse shells y la exfiltración de datos, y máquinas de propósito único cuyo perfil de tráfico completo se puede conocer (un servidor de base de datos no tiene ningún motivo para abrir conexiones a hosts arbitrarios de internet).

Si lo adoptas, hazlo igual que con la entrada: enumera lo que el servidor necesita (DNS a tus resolvers, 80/443 a mirrors, tu endpoint de monitorización), permítelo, pon el resto en default-drop, y registra los descartes durante una semana antes de aplicarlo de verdad; el registro te enseñará qué se te olvidó.

Preguntas frecuentes

¿Debería usar nftables directamente, o mejor ufw o firewalld?

Sigue la convención de tu distribución y las costumbres de tu flota. En Debian, nftables nativo es limpio y de primera clase; en Ubuntu, ufw es el camino más trillado; en RHEL/AlmaLinux/Rocky, firewalld es la interfaz soportada y la documentación lo da por hecho. Las tres acaban produciendo reglas nftables en el kernel. El único antipatrón real es mezclar capas: añadir reglas nft a mano en una máquina que gestiona firewalld provoca reglas que desaparecen al recargar.

¿Es bloquear el ping una buena medida de seguridad?

No. La existencia de tu servidor no queda oculta por descartar los echo requests (los escáneres encuentran hosts activos por muchos otros medios), y pierdes una herramienta de diagnóstico básica que tú mismo vas a necesitar. Peor aún: quien se propone "bloquear el ping" suele bloquear ICMP entero de paso y rompe Path MTU Discovery y el neighbour discovery de IPv6. Permite echo-request (con límite de tasa si quieres) y los mensajes de error/control; descarta el resto.

¿Me protegen estas reglas de ataques DDoS?

En parte, y es importante precisar en qué parte. Un conjunto de reglas default-deny reduce la superficie de ataque (el tráfico basura dirigido a puertos cerrados muere barato) y los límites de tasa amortiguan pequeñas inundaciones de conexiones. Pero ninguna regla en el host ayuda una vez que un ataque satura tu enlace de subida; los paquetes ya han atravesado el cable detrás del cual se ejecutan tus reglas. Los ataques volumétricos se detienen aguas arriba o no se detienen en absoluto, por lo que nuestra red pone mitigación DDoS permanente y un firewall perimetral self-service delante de tu puerto.

Apliqué un firewall y algo se rompió. ¿Cómo lo depuro?

Revisa primero el log de descartes: journalctl -k | grep "input drop" (con la regla de logging de la base). Si el tráfico roto aparece ahí, te falta una regla allow. Anota el puerto y el protocolo de la línea del log. Si no aparece, el firewall no es tu problema; revisa el binding del servicio (ss -tlnp) y, en hosts alcanzables por IPv6, confirma que probaste la misma familia de direcciones que está fallando.

Despliegue en Serverside

En nuestros servidores dedicados, el conjunto de reglas en el host de esta guía es solo una de dos capas de firewall: cada servidor recibe también un firewall self-service en el borde de la red, de modo que el tráfico hacia puertos que nunca sirves se descarta aguas arriba antes de tocar tu máquina, junto con mitigación DDoS permanente en nuestra red ASN 55285. Y si alguna vez un cambio de firewall te llega a dejar fuera, el acceso a consola KVM-over-IP incluido hace que la recuperación tarde minutos, no un ticket de soporte. El aprovisionamiento tarda menos de un minuto en Ubuntu, Debian, AlmaLinux/Rocky, RHEL y más.

Lo siguiente en esta serie: elegir tu herramienta de reglas en iptables frente a nftables, el panorama de amenazas más amplio en ataques DDoS explicados, y cómo identificar qué está golpeando realmente tu servidor en capturar y analizar un DDoS con Wireshark.

Jesse Schokker

Escrito por

Jesse Schokker

Co-founder & CTO, Serverside.com

Jesse is the co-founder and CTO of Serverside.com, where he leads the engineering behind the company's bare-metal cloud: from the ASN 55285 backbone to the core automation that drives day-to-day operation. He writes about dedicated servers, operating systems, and running production workloads on bare metal.