Perfil mínimo de firewall en Debian 13: qué permitir y qué bloquear (sin paranoia)

¿Te fue útil?

Los firewalls no suelen fallar de forma escandalosa. Fallan como un saboteador educado: tu despliegue “falla hacia el éxito”, el sistema de monitorización se queda gris y el equipo empieza a culpar al DNS por pura costumbre.

Debian 13 te proporciona toda la tubería necesaria para una postura de firewall limpia, mínima y aburrida. Esta guía trata de dejar pasar el tráfico correcto, bloquear el resto y diagnosticar roturas rápido—sin convertir tu servidor en una pieza de museo que nadie sabe manejar.

Principios: minimalista, stateful y observable

Un “perfil mínimo de firewall” no es “bloquear todo y rezar”. Es una política explícita que coincide con el uso de la máquina, y se implementa con un firewall stateful para no tener que diseñar a mano cada paquete de respuesta como si fuera 1999.

1) Denegar inbound por defecto, permitir established

El tráfico entrante debe rechazarse salvo que haya una razón para aceptarlo. Pero una vez que aceptas una conexión, debes permitir el tráfico de respuesta relacionado. Para eso sirve el seguimiento de estado. Si intentas ser listo y bloquear la mitad del handshake TCP, solo aprenderás nuevas formas de sufrir.

2) Outbound: de “sin paranoia” a “sin sorpresas”

La mayoría de los perfiles mínimos permiten outbound por defecto. Eso está bien para muchos servidores—hasta que tienes que limpiar un proceso comprometido que exfiltra alegremente por 443 a cualquier parte. Un medio sensato es: permitir outbound ampliamente, pero registrar y opcionalmente restringir destinos o puertos “raros” una vez que conozcas el trabajo de la máquina.

3) El registro debe responder una pregunta, no generar arte

Si registras cada paquete bloqueado, tus logs se convertirán en una forma cara de almacenar el clima de botnets. Registra con límites de tasa y solo en los bordes que realmente investigas: nuevos drops entrantes, salidas inesperadas y tipos de ICMP bloqueados que rompen PMTUD.

4) Si no se puede probar, no es política

Un conjunto de reglas que solo existe en la cabeza de alguien es un cuento para dormir. Tu firewall debe ser verificable desde el host y desde la red. Necesitas comandos que confirmen “SSH funciona desde la subred admin”, “la monitorización puede scrapear” y “la resolución DNS no está rota silenciosamente”.

Una cita, porque sigue vigente: “La esperanza no es una estrategia.”John C. Maxwell. La gente de operaciones la repite porque la alternativa es escribir informes de incidentes sobre “suposiciones”.

Broma corta #1: Un firewall es como un portero—si no puede distinguir quién está en la lista de invitados, acabará denegando al DJ y admitiendo al tipo que trae un router.

Hechos interesantes y contexto histórico (breve, útil)

  • Netfilter llegó a Linux 2.4 (era de principios de 2001), reemplazando el antiguo código de filtrado y permitiendo el modelo moderno de conntrack que hace que “established/related” funcione.
  • iptables es una interfaz, no el motor. Los ganchos subyacentes en el kernel son netfilter; las herramientas cambiaron con el tiempo porque la interfaz se volvió enmarañada y difícil de extender.
  • nftables se introdujo para unificar variantes de iptables (iptables, ip6tables, arptables, ebtables) en un solo marco con mejor evaluación de reglas y estructuras de datos.
  • conntrack es estado compartido, no magia. Consume memoria; bajo carga o ataque, una mala configuración puede provocar conexiones caídas que aparentan “flakiness” de red aleatorio.
  • ICMP no es opcional para TCP sano. Bloquear todo ICMP rompe PMTUD, provocando bloqueos en ciertos caminos y MTUs. La gente redescubre esto cada pocos años.
  • Los firewalls stateful no son solo para inbound. Las políticas de salida a menudo dependen del estado para permitir el tráfico de retorno sin abrir puertos entrantes.
  • Debian tiende a “hacer una cosa bien”. No impone un gestor de firewall; obtienes nftables, compatibilidad iptables-nft y eliges cuán opinado será tu envoltorio.
  • Los security groups de la nube cambiaron expectativas. Muchos equipos tratan los firewalls del host como secundarios. Eso funciona hasta que alguien migra una VM a otro perímetro de red o configura mal un security group.

Modelo de amenazas usable

Un perfil mínimo de firewall no es una lista de verificación de cumplimiento. Es un mapa del tráfico que debería existir. Comienza con aquello que defiendes:

  • Escaneos de Internet y explotación oportunista. Si expones SSH o un puerto web, espera ruido constante y un flujo continuo de malas ideas.
  • Movimiento lateral dentro de tu propia red. Las redes internas no son mágicamente de confianza. Solo son donde los ataques son más silenciosos.
  • Exposición accidental. Un desarrollador enlaza un servidor de depuración a 0.0.0.0, o un paquete abre un puerto que no recordabas haber instalado.
  • Exfiltración de datos y command-and-control. Las reglas de salida son el único lugar donde puedes frenar significativamente esto a nivel de host.
  • Autoinfligidos por errores operativos. La mayoría de incidentes de firewall no son hackers. Eres tú, a las 2am, aplicando “un pequeño cambio”.

Minimal no significa frágil. La meta es un conjunto de reglas suficientemente pequeño para razonar y lo bastante estricto para eliminar sorpresas.

Qué permitir vs qué bloquear (servidor Debian 13 típico)

Inbound: denegar por defecto, luego añadir solo lo que puedas nombrar

En inbound ganas siendo aburrido. Comienza con una postura de denegar por defecto y permite explícitamente:

  • SSH (TCP/22) desde una subred de administración, bastión o rango VPN. Si debes permitir desde cualquier origen, añade limitación de tasa y autenticación fuerte.
  • HTTP/HTTPS (TCP/80, TCP/443) solo si el host es realmente un endpoint web. Muchas máquinas no necesitan estos puertos abiertos.
  • Ingresos de monitorización (varía): p. ej., node exporter (9100), SNMP (161/udp), agentes personalizados. Prefiere “pull desde la subred de monitorización” en lugar de exposición global.
  • Puertos específicos de servicios para bases de datos y brokers solo a las subredes de aplicación que los necesiten. Las bases de datos casi nunca deberían aceptar conexiones desde “cualquier lugar”.
  • ICMP selectivamente: permitir echo-request desde redes de confianza y permitir errores ICMP necesarios para PMTUD (al menos mensajes de “fragmentación necesaria”).

Outbound: empezar permisivo y luego apretar según la realidad

Outbound es donde la gente o no hace nada o se encierra completamente. El enfoque pragmático:

  • Permitir DNS (UDP/TCP 53) solo a tus resolvers, o permitir al resolvedor local si tienes uno. El DNS saliente aleatorio suele ser señal de problemas.
  • Permitir NTP (UDP/123) a tus fuentes de tiempo. La deriva temporal causa fallos de autenticación y logs confusos.
  • Permitir actualizaciones de paquetes (TCP/80/443) al mundo si es necesario, o a tu proxy/mirror corporativo si tienes uno.
  • Permitir egreso de negocio: bases de datos, buses de mensajes, objetos de almacenamiento, APIs. Documenta esto; si no, te “optimizarás” a un outage más tarde.

Qué bloquear sin remordimientos

  • Todo inbound que no esté explícitamente permitido.
  • Todo UDP entrante por defecto a menos que ejecutes un servicio UDP (servidor DNS, NTP, endpoint VPN, receptor syslog, etc.). La exposición UDP invita a rarezas.
  • SMB/NFS/RPC entrantes a menos que el host sea un servidor de archivos y lo hayas acotado firmemente. Estos protocolos funcionan bien dentro del límite correcto.
  • Puertos “de gestión” (API de Docker, kubelet, paneles aleatorios) a menos que puedas decir quién los necesita y desde dónde.

Broma corta #2: Si “abres temporalmente todos los puertos”, tu firewall se vuelve un póster motivacional: aparenta ser protector, pero sobre todo inspira optimismo.

Un perfil minimal de nftables (con justificación)

Debian 13 ejecutará nftables sin problema como tu firewall primario. Mantén el conjunto de reglas legible, coméntalo y resiste la tentación de construir una segunda pila de red dentro del firewall.

Decisiones de diseño

  • Tabla inet única para simetría IPv4+IPv6. Si solo escribes reglas v4, el v6 se convierte en la puerta trasera que no querías instalar.
  • Input default drop, forward default drop. Output default accept (inicialmente), con opciones para apretarlo después.
  • Permitir loopback siempre. Romper el tráfico localhost es una gran manera de inventar nuevos modos de fallo.
  • Permitir established/related temprano por rendimiento y cordura.
  • Permitir ICMP/ICMPv6 esenciales en lugar de “todo” o “nada”.
  • Registrar drops con limitador de tasa. Quieres señal, no un acuario de paquetes.

Ejemplo de conjunto de reglas minimal

cr0x@server:~$ sudo tee /etc/nftables.conf > /dev/null <<'EOF'
#!/usr/sbin/nft -f

flush ruleset

table inet filter {
  set admin_v4 {
    type ipv4_addr
    flags interval
    elements = { 192.0.2.0/24, 198.51.100.10 }
  }

  chain input {
    type filter hook input priority 0; policy drop;

    # Always allow loopback
    iif "lo" accept

    # Allow established/related traffic
    ct state established,related accept

    # Drop invalid packets early
    ct state invalid drop

    # ICMP/ICMPv6: keep the network healthy
    ip protocol icmp icmp type { echo-request, echo-reply, destination-unreachable, time-exceeded, parameter-problem } accept
    ip6 nexthdr icmpv6 icmpv6 type { echo-request, echo-reply, destination-unreachable, time-exceeded, packet-too-big, parameter-problem } accept

    # SSH from admin networks only
    ip saddr @admin_v4 tcp dport 22 ct state new accept

    # Example: HTTPS if this host is a web endpoint
    tcp dport 443 ct state new accept

    # Log & drop everything else (rate-limited)
    limit rate 10/second burst 20 packets log prefix "nft-in-drop " level info
    drop
  }

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

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

    # Optional: tighten later once you know your egress
    # Example patterns shown in the tasks section.
  }
}
EOF

Esto es intencionalmente simple. No intenta resolver DDoS. No microgestiona cada conexión de salida. Evita la exposición accidental, reduce el radio de daño de “ups instalé un demonio” y hace que el acceso entrante sea auditable.

Dos notas operativas que evitan dolor:

  • IPv6 es real. Si tu host tiene IPv6 habilitado (frecuente), pero tu firewall solo cubre IPv4, tienes una bypass no intencional.
  • No olvides la persistencia. Un conjunto de reglas aplicado manualmente a las 15:00 y desaparecido tras un reinicio no es un firewall; es una pieza de arte performativo.

Tareas prácticas (comandos, salidas, decisiones)

Estas son las cosas que realmente haces en una caja Debian 13 cuando quieres un firewall mínimo que no genere tickets. Cada tarea incluye: comando, qué significa la salida y la decisión que tomas.

Task 1: Confirmar qué backend de firewall estás usando

cr0x@server:~$ sudo update-alternatives --display iptables
iptables - auto mode
  link best version is /usr/sbin/iptables-nft
  link currently points to /usr/sbin/iptables-nft
  link iptables is /usr/sbin/iptables
/usr/sbin/iptables-legacy - priority 10
/usr/sbin/iptables-nft - priority 20

Qué significa: Estás usando el backend nftables vía la compatibilidad de iptables. Está bien, pero mezclar reglas iptables puras y conjuntos de nftables puede volverse confuso rápido.

Decisión: Prefiere reglas nativas de nftables en /etc/nftables.conf. Si está en uso iptables-legacy, planifica una ventana de migración.

Task 2: Ver si nftables está activo y persistente

cr0x@server:~$ systemctl status nftables
● nftables.service - nftables
     Loaded: loaded (/lib/systemd/system/nftables.service; enabled)
     Active: active (exited) since Mon 2025-12-30 09:11:10 UTC; 2min ago
       Docs: man:nft(8)

Qué significa: El servicio está habilitado y ha cargado tu conjunto de reglas. “active (exited)” es normal; carga reglas y luego sale.

Decisión: Si está deshabilitado/inactivo, actívalo tras validar reglas: sudo systemctl enable --now nftables.

Task 3: Listar el ruleset activo (trust, but verify)

cr0x@server:~$ sudo nft list ruleset
table inet filter {
	set admin_v4 {
		type ipv4_addr
		flags interval
		elements = { 192.0.2.0/24, 198.51.100.10 }
	}

	chain input {
		type filter hook input priority filter; policy drop;
		iif "lo" accept
		ct state established,related accept
		ct state invalid drop
		ip protocol icmp icmp type { echo-request, echo-reply, destination-unreachable, time-exceeded, parameter-problem } accept
		ip6 nexthdr icmpv6 icmpv6 type { echo-request, echo-reply, destination-unreachable, time-exceeded, packet-too-big, parameter-problem } accept
		ip saddr @admin_v4 tcp dport 22 ct state new accept
		tcp dport 443 ct state new accept
		limit rate 10/second burst 20 packets log prefix "nft-in-drop " level info
		drop
	}
}

Qué significa: El kernel tiene exactamente lo que creías que cargaste. Esa es la verdad de referencia.

Decisión: Si ves tablas/cadenas inesperadas, tienes otra herramienta escribiendo reglas (agente de nube, plataforma de contenedores, scripts antiguos). Decide quién posee el firewall.

Task 4: Validar la sintaxis de la configuración antes de aplicar (evitar quedarte fuera)

cr0x@server:~$ sudo nft -c -f /etc/nftables.conf

Qué significa: No ver salida es bueno; la comprobación de sintaxis pasó. Si imprime un error, te dirá número de línea y token.

Decisión: Nunca apliques configs de firewall sin validar por SSH a menos que tengas una consola fuera de banda o una red de seguridad.

Task 5: Encontrar qué servicios están escuchando (deja de adivinar)

cr0x@server:~$ sudo ss -lntu
Netid State  Recv-Q Send-Q Local Address:Port  Peer Address:Port Process
tcp   LISTEN 0      4096   0.0.0.0:22         0.0.0.0:*
tcp   LISTEN 0      4096   0.0.0.0:443        0.0.0.0:*
udp   UNCONN 0      0      127.0.0.53:53      0.0.0.0:*

Qué significa: Solo SSH y HTTPS están expuestos en todas las interfaces; DNS está enlazado al resolvedor local stub. Buena línea base.

Decisión: Si ves un puerto sorpresa escuchando en 0.0.0.0 o :::, ciérralo en el servicio (preferible) o ponle un firewall explícito hasta entenderlo.

Task 6: Identificar qué proceso posee un puerto

cr0x@server:~$ sudo ss -lntup 'sport = :443'
Netid State  Recv-Q Send-Q Local Address:Port Peer Address:Port Process
tcp   LISTEN 0      4096   0.0.0.0:443       0.0.0.0:*     users:(("nginx",pid=1187,fd=6))

Qué significa: nginx es el listener. Eso es útil al decidir si abrir 443 en absoluto.

Decisión: Si no es un servicio que quieres expuesto a Internet, corrige la dirección de bind del servicio o desactívalo.

Task 7: Comprobar ruta predeterminada e interfaces (las reglas son sensibles a la interfaz)

cr0x@server:~$ ip -br addr
lo               UNKNOWN        127.0.0.1/8 ::1/128
ens3             UP             203.0.113.20/24 2001:db8:1234::20/64

cr0x@server:~$ ip route
default via 203.0.113.1 dev ens3
203.0.113.0/24 dev ens3 proto kernel scope link src 203.0.113.20

Qué significa: ens3 es la interfaz real; también tienes IPv6. Si tu firewall no cubre inet, te falta la mitad de la historia.

Decisión: Usa table inet salvo que tengas una razón deliberada para no hacerlo.

Task 8: Confirmar que no estás reenviando paquetes por accidente

cr0x@server:~$ sysctl net.ipv4.ip_forward net.ipv6.conf.all.forwarding
net.ipv4.ip_forward = 0
net.ipv6.conf.all.forwarding = 0

Qué significa: Este host no actúa como router. Bueno para servidores típicos.

Decisión: Si el forwarding está activado sin querer, desactívalo o asegúrate de que tu cadena forward tenga una política explícita y segura.

Task 9: Observar drops en vivo (demuestra que el firewall es el problema)

cr0x@server:~$ sudo journalctl -f -k | grep -E 'nft-in-drop|IN='
Dec 30 09:15:22 server kernel: nft-in-drop IN=ens3 OUT= MAC=... SRC=198.51.100.77 DST=203.0.113.20 LEN=60 TOS=0x00 PREC=0x00 TTL=49 ID=4242 DF PROTO=TCP SPT=51234 DPT=22 WINDOW=64240 SYN

Qué significa: Alguien en 198.51.100.77 intentó enviar un SYN a SSH y fue bloqueado por tus reglas (no está en admin_v4).

Decisión: Si es acceso administrativo legítimo, añade la IP/rango fuente a la allowlist. Si no, ignora y mantén los logs con límite de tasa.

Task 10: Verificar que el estado de conntrack funciona (las reglas stateful dependen de ello)

cr0x@server:~$ sudo conntrack -S
cpu=0 found=412 new=37 invalid=2 ignore=0 delete=12 delete_list=12 insert=37 insert_failed=0 drop=0 early_drop=0 error=0 search_restart=0

Qué significa: conntrack funciona; drop/early_drop bajos es sano.

Decisión: Si ves muchos drop o early_drop, podrías estar agotando la tabla conntrack bajo carga o ataque. Considera ajuste y reducir exposición.

Task 11: Comprobar capacidad de la tabla conntrack (culpable clásico de fallos aleatorios)

cr0x@server:~$ sysctl net.netfilter.nf_conntrack_count net.netfilter.nf_conntrack_max
net.netfilter.nf_conntrack_count = 1832
net.netfilter.nf_conntrack_max = 262144

Qué significa: Estás lejos de la capacidad. Si el count se acerca al max, el seguimiento de estado se convierte en cuello de botella.

Decisión: Si estás cerca del máximo en carga normal, aumenta nf_conntrack_max y asegura memoria disponible; también reduce exposición entrante innecesaria.

Task 12: Confirmar que DNS funciona con tu postura de firewall

cr0x@server:~$ resolvectl status
Global
       Protocols: -LLMNR -mDNS -DNSOverTLS DNSSEC=no/unsupported
resolv.conf mode: stub
Current DNS Server: 192.0.2.53
       DNS Servers: 192.0.2.53 192.0.2.54

cr0x@server:~$ dig +short debian.org
151.101.2.132

Qué significa: El resolvedor del sistema usa tu DNS interno, y las consultas tienen éxito.

Decisión: Si DNS falla al apretar reglas de salida, permite UDP/TCP 53 a los resolvers listados (no “a cualquiera”).

Task 13: Probar accesibilidad entrante desde el propio host (chequeo de cordura)

cr0x@server:~$ nc -vz 127.0.0.1 22
Connection to 127.0.0.1 22 port [tcp/ssh] succeeded!

cr0x@server:~$ nc -vz 127.0.0.1 443
Connection to 127.0.0.1 443 port [tcp/https] succeeded!

Qué significa: Los servicios están arriba localmente. Si el acceso remoto falla, es territorio de firewall/routing/security-group, no del demonio.

Decisión: No “fixees” nginx cuando el problema es un SYN bloqueado.

Task 14: Aplicar reglas de forma segura con guardia de rollback automática

cr0x@server:~$ sudo bash -c 'nft -c -f /etc/nftables.conf && (sleep 20; nft flush ruleset) & nft -f /etc/nftables.conf'

Qué significa: Esto aplica las reglas, pero programa un flush en 20 segundos. Si te quedas fuera, las reglas se autodestruyen.

Decisión: Usa una guardia así cuando edites por SSH. Si todo sigue funcionando, cancela el rollback matando el proceso sleep en background (o reaplica correctamente y reinicia nftables).

Task 15: Hacer las reglas persistentes y gestionadas por systemd

cr0x@server:~$ sudo systemctl enable --now nftables
Synchronizing state of nftables.service with SysV service script with /lib/systemd/systemd-sysv-install.
Executing: /lib/systemd/systemd-sysv-install enable nftables

Qué significa: Las reglas cargarán al arranque; has pasado de “estado runtime hecho a mano” a un servicio gestionado.

Decisión: A partir de aquí, los cambios pasan por gestión de configuración o al menos control de versiones.

Task 16: Apretar salida sin romper actualizaciones (ejemplo)

Aquí es donde los equipos se asustan y no hacen nada. Un patrón viable: mantén la política de salida en accept, pero permite explícitamente los puertos “necesarios” y registra el resto durante un tiempo antes de cambiar a drop.

cr0x@server:~$ sudo nft add rule inet filter output ct state established,related accept
cr0x@server:~$ sudo nft add rule inet filter output oif "lo" accept
cr0x@server:~$ sudo nft add rule inet filter output udp dport 53 ip daddr { 192.0.2.53, 192.0.2.54 } accept
cr0x@server:~$ sudo nft add rule inet filter output tcp dport 53 ip daddr { 192.0.2.53, 192.0.2.54 } accept
cr0x@server:~$ sudo nft add rule inet filter output udp dport 123 ip daddr 192.0.2.123 accept
cr0x@server:~$ sudo nft add rule inet filter output tcp dport { 80, 443 } accept

Qué significa: Has enumerado DNS, NTP y egress web. Si más tarde cambias la política de salida a drop, estas reglas mantienen la caja funcional.

Decisión: Recopila qué más necesitas (backends de métricas, endpoints de object storage, relays de correo) antes de imponer drop.

Tres micro-historias corporativas desde el frente

Incidente: una suposición equivocada (IPv6 “no estaba en uso”)

Un equipo gestionaba una flota Debian tras un balanceador de carga en la nube. Escribieron un firewall IPv4 limpio: denegar inbound por defecto, permitir 443, permitir SSH desde rango VPN. Se felicitaron y siguieron adelante. La ventana de cambios terminó temprano, lo cual siempre es sospechoso.

Una semana después, seguridad marcó tráfico entrante que no debería haber sido posible. No porque el firewall estuviera “roto”, sino porque era irrelevante. Los hosts tenían direcciones IPv6 globales asignadas automáticamente y las reglas solo cubrían IPv4. El servicio era accesible por IPv6 directamente, eludiendo la ruta del balanceador y la perímetro de política previsto.

No fue un ataque exótico. Fue Internet haciendo lo suyo: escanear cualquier cosa con una dirección ruteable. Los logs eran escasos porque registraban solo drops IPv4. Mientras tanto, el equipo de aplicación perseguía fantasmas en la configuración del balanceador.

La solución no fue heroica. Movieron el ruleset a una tabla inet, permitieron explícitamente lo que querían para v6 y bloquearon el resto. Luego auditaron el direccionamiento: o mantienen IPv6 y lo administran, o lo deshabilitan intencionalmente. La lección real no fue “IPv6 da miedo”. Fue “si no escribiste una regla para ello, no lo controlas”.

Optimización que salió mal (bloquear ICMP para “reducir ruido”)

Un equipo de plataforma cansado de “tormentas de ping” y logs ICMP propuso una política ordenada: bloquear todo ICMP entrante y saliente. Menos ruido, menos argumentos sobre “superficie de ataque”, más tiempo para cosas importantes—como reuniones.

Por un tiempo pareció funcionar. Luego algunos clientes empezaron a reportar timeouts intermitentes en respuestas grandes. No todos los clientes. No todos los caminos. Lo suficiente para ser un dolor rotatorio. La primera respuesta fue culpar a la aplicación. La segunda, culpar a la red. La tercera fue aumentar los timeouts, que es una forma clásica de ocultar un problema hasta que se vuelve imposible ignorarlo.

El problema real: path MTU discovery. Ciertas rutas requerían manejo de fragmentación y los servidores necesitaban recibir “packet too big” (IPv6) o mensajes relacionados (IPv4). Con ICMP bloqueado, las conexiones se quedaban estancadas con paquetes que se perdían silenciosamente. Las peticiones pequeñas funcionaban. Las grandes morían lentamente.

Revirtieron el bloqueo absoluto de ICMP y lo reemplazaron por “permitir lo esencial, rate-limit a echo”. El incidente no fue costoso en hardware ni soporte, pero sí en atención. El fallo no fue optimizar; fue optimizar lo equivocado.

Práctica aburrida pero correcta que salvó el día (despliegue por etapas + drops registrados)

Una empresa con un equipo SRE medianamente sensato quería firewalls en cientos de servidores Debian. La frase clave es “medianamente sensato”: no intentaron hacerlo perfecto de una vez. Escribieron una política mínima inbound-deny, luego la desplegaron en modo observación primero con logs de drops limitados por tasa y un dashboard que resumía puertos bloqueados por subred.

La semana uno fue aburrida. Los bots golpearon SSH; el firewall los bloqueó. Faltaban unas pocas IPs admin legítimas en la allowlist; las añadieron. Unos checks de monitorización heredados seguían scrapeando desde redes antiguas; migraron esos checks. Nadie entró en pánico porque el rollout fue por etapas y tenían forma de ver qué se bloqueaba.

La semana dos fue donde el trabajo “aburrido” dio fruto. Durante un incidente separado—una mala configuración de aplicación que causó floods de conexiones de salida—el equipo pudo demostrar rápidamente que no era un evento entrante. Los logs del firewall estaban tranquilos. La tabla conntrack estaba estable. La red estaba bien. Se concentraron en la capa correcta y lo resolvieron más rápido.

Este es el truco operacional: un firewall mínimo más observabilidad, desplegado gradualmente, es menos emocionante que un rediseño grandioso. También funciona.

Guion de diagnóstico rápido

Cuando “la red está rota”, quieres una secuencia que estreche la búsqueda en minutos, no una hora de paseo por las opiniones de todos.

Primero: ¿el servicio realmente está escuchando?

  • Ejecuta ss -lntu. Si nada escucha en el puerto esperado, deja de culpar al firewall.
  • Ejecuta systemctl status <service>. Si se cayó o está ligado a localhost, el firewall es un observador.

Segundo: ¿llega tráfico al host?

  • Comprueba interfaz/dirección: ip -br addr.
  • Desde un cliente remoto, intenta una conexión TCP. En el servidor, observa con tcpdump en la interfaz. Si los SYN nunca llegan, es routing/security group/NACL aguas arriba.

Tercero: ¿lo está bloqueando el firewall?

  • Revisa logs: journalctl -k | grep nft-in-drop.
  • Lista reglas y contadores (si los usas): nft list ruleset. Si no cuentas, empieza; los contadores convierten adivinanzas en matemáticas.

Cuarto: si es “intermitente”, revisa conntrack e ICMP

  • conntrack -S y sysctl net.netfilter.nf_conntrack_count para síntomas de capacidad.
  • Revisa la política ICMP. Si bloqueaste packet-too-big/time-exceeded, te compraste un outage raro.

Quinto: valida dependencias de salida

  • DNS: resolvectl status y dig.
  • Tiempo: timedatectl y el estado del cliente NTP.
  • Actualizaciones/proxies: intenta apt-get update y observa drops.

Errores comunes: síntoma → causa raíz → arreglo

1) “SSH funcionaba antes, ahora estoy bloqueado”

Síntoma: SSH hace timeout tras aplicar reglas.

Causa raíz: Permitiste SSH solo desde una subred que no incluye tu IP actual, o olvidaste las vías de acceso SSH por IPv6.

Arreglo: Usa una consola fuera de banda o guardia de rollback. Añade tu rango fuente admin a un nft set; aplica mediante nft -c primero. Asegura que las reglas estén en table inet para manejar v6.

2) “HTTPS está abierto pero los clientes se cuelgan en respuestas grandes”

Síntoma: Peticiones pequeñas tienen éxito; descargas grandes se cuelgan o se resetean impredeciblemente.

Causa raíz: Mensajes ICMP “packet too big” (IPv6) o PMTUD relacionados bloqueados; problemas de fragmentación se quedan en agujeros negros.

Arreglo: Permite tipos esenciales de ICMP/ICMPv6, no solo echo. Mantén logs con limitación de tasa.

3) “La monitorización de repente no ve nada”

Síntoma: Fallan scrapes de métricas o agentes no pueden conectar tras cambios de firewall.

Causa raíz: Cambiaron IPs de origen de monitorización (nuevos collectors, NAT, nueva subred) y no estaban en las allowlists.

Arreglo: Usa nft sets para subredes de monitorización y mantenlas actualizadas. Prefiere permitir desde redes de monitorización, no “any”.

4) “DNS falla aleatoriamente solo en este host”

Síntoma: Algunas resoluciones hacen timeout; otras funcionan.

Causa raíz: UDP/53 saliente permitido, pero TCP/53 bloqueado; respuestas DNS grandes o fallback DNSSEC requieren TCP.

Arreglo: Permite tanto UDP como TCP 53 a tus resolvers.

5) “Las conexiones son inestables bajo carga”

Síntoma: Fallos aleatorios de conexiones salientes o entrantes durante picos.

Causa raíz: Agotamiento de la tabla conntrack o churn elevado; el firewall stateful no puede asignar entradas.

Arreglo: Ajusta nf_conntrack_max y considera reducir la exposición o usar SYN proxy upstream. También verifica que no estés haciendo seguimiento innecesario (p. ej., para interfaces solo locales).

6) “Permití el puerto 443, pero sigue bloqueado”

Síntoma: Clientes remotos no pueden conectar; la conexión local funciona.

Causa raíz: Security group/NACL aguas arriba lo bloquea, o el servicio está ligado a localhost, o estás probando IPv6 mientras solo permites IPv4.

Arreglo: Confirma la dirección de bind con ss -lntup. Confirma que los paquetes llegan con tcpdump. Usa reglas inet para paridad v4/v6.

7) “Docker/Kubernetes cambian mi firewall sin avisar”

Síntoma: El ruleset difiere de lo que hay en /etc/nftables.conf; aparecen reglas accept inesperadas.

Causa raíz: La herramienta de contenedores manipula netfilter para implementar NAT/forwarding.

Arreglo: Decide propiedad: permite a la plataforma gestionar tablas/cadenas específicas, o aisla tu política de host en una tabla dedicada y evita sobrescribir cadenas de la plataforma. Valida tras desplegar.

Listas de verificación / plan paso a paso

Checklist A: Política mínima inbound para un servidor típico

  1. Inventario de listeners: ejecuta ss -lntu. Si un servicio no debe ser público, arréglalo en el servicio primero (bind a localhost o desactivar).
  2. Decidir camino de acceso admin: VPN/bastión/subnet. Escríbelo. De verdad.
  3. Escribir reglas nftables en una tabla inet: accept loopback, accept established/related, drop invalid.
  4. Permitir SSH solo desde redes admin: usa un set para que los cambios no requieran reescribir reglas.
  5. Permitir solo los puertos de servicio requeridos: 443 para web, quizá nada más.
  6. Permitir tipos esenciales de ICMP/ICMPv6: mantener PMTUD funcional.
  7. Limitar por tasa los logs de drops: de lo contrario acabarás apagando el logging.
  8. Validar sintaxis: nft -c -f /etc/nftables.conf.
  9. Aplicar con guardia de rollback si es remoto: programa un flush si estás nervioso.
  10. Habilitar servicio nftables: persistente al reinicio.

Checklist B: Postura sensata de outbound sin autoboicot

  1. Mide primero: registra outbound inesperado durante una semana antes de imponer drop.
  2. Permite DNS a los resolvers: UDP/TCP 53 a servidores conocidos.
  3. Permite NTP a las fuentes de tiempo: no dejes que la deriva temporal invente fallos de autenticación.
  4. Permite actualizaciones: TCP 80/443 a proxy o mirror; de lo contrario siempre “lo abrirás temporalmente”.
  5. Permite dependencias de negocio: bases de datos, colas, endpoints de object storage.
  6. Solo entonces considera policy output drop: y mantén una ruta de emergencia break-glass.

Checklist C: Gestión de cambios que no creará un incidente

  1. Desplegar por etapas: canary en un pequeño porcentaje de hosts.
  2. Tener un camino fuera de banda: consola, serial o al menos un temporizador de rollback.
  3. Usar contadores y logs: de lo contrario depurarás por sensaciones.
  4. Registrar la intención: un comentario por regla es más barato que un postmortem.
  5. Probar desde los lugares correctos: red admin, red de monitorización y una red hostil externa (o al menos una subred distinta).

FAQ

1) ¿Debo usar nftables directamente o una herramienta wrapper?

Si gestionas servidores de producción y quieres comportamiento predecible, usa nftables directamente. Los wrappers pueden ser útiles, pero a menudo ocultan las reglas reales y complican la depuración.

2) ¿“Denegar inbound por defecto” es suficiente seguridad?

Es una base sólida, no un programa de seguridad completo. Reduce la exposición accidental y limita escaneos oportunistas. Aun necesitas parcheo, autenticación sensata, principio de menor privilegio y monitorización.

3) ¿Necesito firewallizar el tráfico saliente?

No siempre el primer día. Pero deberías entender tus dependencias de salida y registrar el egress inusual. Reglas de salida estrictas son más valiosas cuando el rol del host es estable.

4) ¿Por qué permitir ICMP en absoluto?

Porque las redes usan ICMP para corrección operativa, no solo para “ping”. PMTUD y señales de error dependen de él. Bloquea echo si hace falta, pero no rompas packet-too-big y similares.

5) ¿Cuál es el conjunto mínimo inbound para un servidor sin cabeza?

Loopback, established/related, ICMP/ICMPv6 esenciales y SSH desde una red admin. Eso es todo salvo que el host sirva tráfico.

6) ¿Cómo evito bloquearme por SSH?

Valida con nft -c, aplica con un temporizador de rollback y conserva una opción de consola fuera de banda. Además, no pruebes reglas nuevas por primera vez en el único host que no puedes permitirte reiniciar.

7) ¿Qué hacer con IPv6—deshabilitarlo o firewallizarlo?

Firewallízalo. Deshabilitar IPv6 puede ser aceptable en algunos entornos, pero suele ser un juego de Whac-A-Mole con herramientas y valores por defecto. Si el host tiene IPv6, tu política debe cubrirlo.

8) ¿Debo abrir puertos de base de datos al mundo si la contraseña es fuerte?

No. La autenticación fuerte es buena; la exposición innecesaria sigue siendo mala. Limita puertos DB a subredes de aplicación o redes privadas. Si necesitas acceso remoto, usa bastión o VPN.

9) ¿Puedo confiar en security groups de la nube en lugar de firewalls de host?

Puedes, hasta que no puedas. Los firewalls de host son una red de seguridad local y una guía contra listeners accidentales. Usa ambos cuando sea posible; al menos usa firewalls de host en sistemas de alto valor.

10) ¿Cuánto logging debería activar para paquetes bloqueados?

Suficiente para diagnosticar: logs limitados por tasa para nuevos drops entrantes y opcionalmente salidas inesperadas. Si tus logs se convierten en un diario de botnet, dejarás de leerlos—y entonces son inútiles.

Próximos pasos que no arruinarán tu fin de semana

Haz tres cosas en este orden y obtendrás el 80% del valor con el 20% del riesgo:

  1. Inventario de listeners con ss -lntu y apaga todo lo que no deba estar expuesto. Esto reduce la cantidad de política de firewall que necesitas incluso.
  2. Desplegar el perfil minimal inbound de nftables (tabla inet, loopback, established/related, ICMP/ICMPv6 esenciales, SSH desde subred admin, puertos de servicio que realmente sirves). Habilita persistencia con systemd.
  3. Añadir observabilidad: logs de drops limitados por tasa y un hábito de diagnóstico rápido (estado de escucha → llegada de paquete → decisión del firewall → conntrack/ICMP).

Una vez estable, puedes decidir hasta dónde llegar con restricciones de salida. Empieza registrando y allowlisteando DNS/NTP/actualizaciones. Apreta solo cuando hayas observado tráfico real. “Sin paranoia” no significa “sin política”. Significa que tu política está basada en lo que hacen tus servidores, no en lo que imagina tu ansiedad.

← Anterior
MySQL vs PostgreSQL en incidentes de disco lleno: quién se recupera más limpio y rápido
Siguiente →
Ubuntu 24.04: Docker + UFW = puertos abiertos sorpresa — Cierra la brecha sin romper contenedores

Deja un comentario