Saltar al contenido
Hugo Méndez
  • Docker
  • Linux
  • Seguridad
  • DevOps

Docker y firewalld: por qué tu puerto "cerrado" sigue abierto (y qué hacer)

Docker publica puertos saltándose las reglas de firewalld y ufw. Cómo comprobarlo, cómo exponer solo lo necesario, el truco de 127.0.0.1 y los errores de Compose con puertos duplicados.

Hugo Méndez26 de mayo de 20262 min de lectura

Es de los sustos más comunes al montar un servidor: configuras el firewall, cierras todo menos 80 y 443, levantas tu docker compose… y resulta que la base de datos o la API quedaron accesibles desde internet. No es un error tuyo en el firewall: es como funciona Docker.

Por qué pasa

Cuando publicas un puerto (ports: - "5432:5432"), Docker agrega sus propias reglas de red para redirigir ese tráfico al contenedor. Ese tráfico se enruta hacia el contenedor antes de pasar por las reglas de entrada que configuraste con firewalld o ufw, así que tu regla de “bloquear” nunca se evalúa.

Compruébalo desde otra máquina, no desde el mismo servidor:

# Desde tu laptop, no desde el servidor
nc -zv IP_DEL_SERVIDOR 5432

Si conecta, ese puerto está expuesto aunque el firewall diga lo contrario.

Regla de oro: publica solo lo que debe ser público

La mayoría de los servicios (base de datos, API interna, Redis) no necesitan publicar puertos. Los contenedores de la misma red de Compose se hablan por nombre de servicio:

services:
  api:
    build: ./backend
    environment:
      MONGO_URI: mongodb://mongo:27017/app   # "mongo" es el nombre del servicio
  mongo:
    image: mongo:7
    # sin "ports": nadie fuera de la red de Docker puede entrar

Si necesitas llegar a un servicio desde el propio servidor (por ejemplo, un Nginx instalado en el host que hace de proxy), publícalo solo en loopback:

services:
  api:
    ports:
      - "127.0.0.1:3000:3000"

Así el puerto existe en localhost del servidor, pero no hacia afuera.

Cuando 127.0.0.1 no funciona

En algunos servidores (lo he visto en distribuciones tipo RHEL con firewalld) publicar en 127.0.0.1 puede dejar el servicio inaccesible incluso desde el propio host. Antes de rendirte, revisa:

  1. ¿Recargaste firewalld después de levantar Docker? firewall-cmd --reload puede borrar las reglas que Docker creó. Reinicia Docker después de recargar el firewall:

    sudo firewall-cmd --reload && sudo systemctl restart docker
  2. ¿Está activo el proxy de usuario de Docker? Si en /etc/docker/daemon.json desactivaste userland-proxy, el tráfico por loopback depende de otras reglas del kernel.

  3. La alternativa más limpia: no publiques nada y mete el proxy (Nginx, Caddy o Traefik) como contenedor en la misma red. Solo el proxy publica 80 y 443.

services:
  proxy:
    image: nginx:stable
    ports: ["80:80", "443:443"]
  api:
    build: ./backend      # sin ports

El otro error: “port is already allocated” con archivos override

Si usas un docker-compose.yml base y un docker-compose.prod.yml encima, los puertos se suman, no se reemplazan. Terminas pidiendo el mismo puerto dos veces y Docker falla con port is already allocated.

Desde Compose v2.24 puedes indicar que el override reemplace la lista:

# docker-compose.prod.yml
services:
  frontend:
    ports: !override
      - "127.0.0.1:8100:80"

También existe !reset para vaciar un valor heredado.

Checklist rápido

  • Ningún servicio interno publica puertos.
  • Lo que va detrás de un proxy en el host se publica en 127.0.0.1.
  • Probaste desde fuera del servidor con nc o nmap.
  • Después de tocar el firewall, reiniciaste Docker.
  • Los overrides de producción usan !override en ports.

Si tu servidor ya está en producción y no estás seguro de qué quedó expuesto, te ayudo a revisarlo.

Volver al blog

Servicios relacionados

¿Tienes un proceso que ya no aguanta más Excel?

Cuéntame qué necesitas. En menos de 24 horas hábiles te respondo con una idea clara de cómo resolverlo y cuánto cuesta.