Ir directamente al contenido

Ajustes de cookies

Elija por tipo qué cookies permite. Puede cambiar su elección en cualquier momento en Ajustes de cookies, al pie de cada página. Declaración de privacidad

Necesarias

Siempre activadas

Necesarias para iniciar sesión, conservar su carrito y recordar sus preferencias. Siempre están activadas.

¿Qué cookies?
  • weboke_sessie, weboke_klant, weboke_remember: su sesión y mantener la sesión iniciada
  • weboke_basket: su carrito
  • wkBtwIncl: mostrar los precios con o sin IVA
  • wok_cookies: su elección de cookies, 12 meses

Mide qué anuncio lleva a una visita o a un pedido, para no pagar por anuncios que no sirven de nada.

¿Qué cookies?
  • Google Ads a través de Google Tag Manager (_gcl_au, _gcl_aw), hasta 90 días
  • OpenAI Ads, anuncios en ChatGPT
  • wok_aff: a través de qué socio llegó a nosotros (programa de afiliados), 60 días

4,6

País e idioma

Cada país tiene su propio sitio, con los precios y el IVA de ese país.

Ver

Módulo 9 · 9.3

Docker compose y el firewall

Módulo 9: Docker y docker compose · Tiempo de lectura: 12 minutos

Objetivo de aprendizaje: Después de esta lección podrá describir servicios en un archivo compose, iniciarlos, actualizarlos y detenerlos, y sabrá explicar por qué los puertos publicados por Docker esquivan ufw y cómo evitarlo.

Conceptos clave: compose.yaml, services, image, ports, volumes, environment, .env, restart, networks, depends_on, healthcheck, docker compose up -d, pull, logs, down, version: obsoleto, Docker y ufw, DOCKER-USER, publicar en 127.0.0.1, secretos, docker.sock

¿Por qué compose?

Con docker run (lección 9.2) inicia un único contenedor con una larga línea de opciones. Una aplicación real suele constar de varios contenedores: un programa, una base de datos, un proxy inverso. Recordar esas líneas y volver a teclearlas después de un reinicio o una actualización acaba saliendo mal algún día.

Docker Compose describe todos los contenedores de una aplicación en un solo archivo: compose.yaml. Usted coloca ese archivo en una carpeta propia, por ejemplo /srv/status/compose.yaml, y lo gestiona todo con docker compose. El archivo es a la vez su documentación: qué se ejecuta, qué imagen, qué puertos, dónde están los datos. Incluya esa carpeta en su copia de seguridad (lección 8.1) y podrá restaurar la aplicación en un servidor nuevo en pocos minutos.

El complemento se llama docker compose (con un espacio, versión 2). El antiguo programa independiente docker-compose (con guion) ha sido sustituido; todavía lo verá en guías antiguas. También el campo version: "3.8" al principio de los archivos antiguos está obsoleto: Compose utiliza siempre el esquema más reciente y con version: solo muestra una advertencia.

Leer un archivo compose

Un ejemplo con un programa y una base de datos PostgreSQL:

# /srv/notities/compose.yaml
services:
  app:
    image: ghcr.io/example/notities:1.4
    restart: unless-stopped
    ports:
      - "127.0.0.1:8081:8080"
    environment:
      DB_HOST: db
      DB_USER: notities
      DB_PASSWORD: ${DB_PASSWORD}
    depends_on:
      db:
        condition: service_healthy

  db:
    image: postgres:17
    restart: unless-stopped
    environment:
      POSTGRES_USER: notities
      POSTGRES_PASSWORD: ${DB_PASSWORD}
    volumes:
      - db-data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U notities"]
      interval: 10s
      retries: 5

volumes:
  db-data:

Paso a paso:

  • services: cada servicio se convierte en un contenedor. El nombre (app, db) es también el nombre de host con el que los contenedores se encuentran entre sí. Compose crea una red propia para la aplicación, de modo que app se conecta a la base de datos en db:5432.
  • image: siempre con una etiqueta de versión (lección 9.1).
  • restart: lo que hace Docker cuando el contenedor se detiene o el servidor se reinicia. unless-stopped lo vuelve a iniciar, salvo que usted lo haya detenido a propósito. Sin política de reinicio, su aplicación no vuelve tras un reinicio (lección 4.2).
  • ports: solo la aplicación publica un puerto, y únicamente en 127.0.0.1. La base de datos no publica nada: la aplicación llega a ella a través de la red interna. Una base de datos con ports: - "5432:5432" es uno de los errores más frecuentes (véase más abajo).
  • environment: ajustes para el programa. La contraseña no aparece literalmente, sino como ${DB_PASSWORD}.
  • volumes: el volumen con nombre db-data conserva la base de datos cuando se sustituye el contenedor.
  • depends_on con healthcheck: la aplicación solo arranca cuando la base de datos está realmente lista, no simplemente iniciada.

Secretos en un archivo .env

Compose lee variables como ${DB_PASSWORD} de un archivo .env en la misma carpeta:

# /srv/notities/.env
DB_PASSWORD=Kies-hier-een-lang-willekeurig-wachtwoord
anna@web1:/srv/notities$ sudo chmod 600 .env

Así la contraseña no figura en el archivo compose que quizá guarde en Git o pegue en un foro para pedir ayuda. Añada .env a .gitignore. Genere la contraseña con un gestor de contraseñas o con openssl rand -base64 24.

Uso diario

Todos los comandos se ejecutan en la carpeta que contiene el archivo compose:

anna@web1:/srv/notities$ sudo docker compose config     # comprobar el archivo, con las variables sustituidas
anna@web1:/srv/notities$ sudo docker compose up -d      # iniciar todo (o actualizar según el archivo)
anna@web1:/srv/notities$ sudo docker compose ps         # estado por servicio
anna@web1:/srv/notities$ sudo docker compose logs -f app
anna@web1:/srv/notities$ sudo docker compose pull       # descargar las imágenes nuevas
anna@web1:/srv/notities$ sudo docker compose up -d      # sustituir los contenedores por las imágenes nuevas
anna@web1:/srv/notities$ sudo docker compose down       # detener y eliminar los contenedores

down deja sus volúmenes intactos. down -v elimina también los volúmenes de la aplicación, y con ellos su base de datos. Escríbalo solo si de verdad quiere empezar de cero.

docker compose config es el equivalente de nginx -t y sshd -t: muestra cómo entiende Compose su archivo, con todas las variables sustituidas, antes de iniciar nada. Tenga cuidado de no pegar esa salida en ningún sitio, porque contiene las contraseñas.

Docker esquiva ufw

Llegamos al punto de seguridad más importante de este módulo. En la lección 5.2 configuró ufw como "denegar todo, excepto SSH, 80 y 443". Cabría esperar que un contenedor con ports: - "5432:5432" no fuera accesible. No es así.

La documentación de Docker lo dice literalmente: Docker y ufw usan las reglas del firewall de una manera incompatible entre sí. Cuando usted publica un puerto, Docker añade sus propias reglas al firewall del kernel. Esas reglas redirigen el tráfico al contenedor (traducción de direcciones, DNAT) antes de que se apliquen las reglas de ufw. El paquete, por tanto, rodea ufw. ufw status no muestra nada especial, mientras el puerto es accesible desde todo internet. Lo mismo ocurre con firewalld.

Además, sin dirección Docker publica en 0.0.0.0 y [::], es decir, en IPv4 e IPv6 (lección 9.1).

Tres formas de hacerlo bien, de la más sencilla a la más compleja:

  1. No publique nada que no deba ser público. Las bases de datos, las cachés y los servicios internos no llevan ports:. Los contenedores de la misma aplicación compose se comunican a través de la red interna.
  2. Publique en loopback. "127.0.0.1:8081:8080". Así solo el propio servidor puede acceder, y usted coloca delante un proxy inverso (nginx o Caddy, módulo 7) que escucha en el 443. Docker también puede tener esto como valor predeterminado: un ajuste del daemon hace que todos los puertos publicados sin dirección queden en 127.0.0.1.
  3. Reglas en la cadena DOCKER-USER. Docker crea una cadena vacía DOCKER-USER que se recorre antes de sus propias reglas. Ahí puede añadir sus propias reglas, por ejemplo "solo esta dirección IP puede acceder a los puertos publicados". Es un trabajo avanzado con iptables o nftables, y es fácil equivocarse. Para la mayoría de los servidores pequeños bastan las opciones 1 y 2.

Compruébelo siempre desde fuera, como en la lección 5.1: nc -vz -w 3 <ip> 5432 desde su portátil, o la comprobación del curso del ejercicio Z5.1 y Z10.4.

Otras dos cosas que no debe hacer

  • No monte /var/run/docker.sock en un contenedor, a menos que sepa exactamente por qué. Un contenedor con acceso al socket de Docker puede iniciar contenedores por su cuenta, incluso uno con todo el disco del host (lección 9.2). Algunas herramientas (paneles, actualizadores automáticos) lo piden; en la práctica, eso equivale a dar root a ese programa.
  • No use privileged: true ni network_mode: host solo porque lo diga una guía. Ambos eliminan en gran parte el aislamiento del contenedor. Utilícelos únicamente si el autor explica por qué son necesarios.

Resumen

  • Un archivo compose (compose.yaml) describe todos los contenedores de una aplicación; es a la vez documentación y debe formar parte de su copia de seguridad.
  • Use etiquetas de versión, una política de reinicio, volúmenes con nombre, un healthcheck cuando sea posible, y secretos en un .env con permisos 600.
  • docker compose config, up -d, ps, logs, pull, down; down -v elimina también los volúmenes.
  • Los puertos publicados esquivan ufw y firewalld. Publique solo lo que deba ser público y, en otro caso, en 127.0.0.1 con un proxy inverso delante.
  • Nada de docker.sock, privileged ni network_mode: host sin una buena razón.

Cuestionario

1. ufw está configurado como "denegar todo excepto 22, 80 y 443". Su archivo compose tiene ports: - "5432:5432" en la base de datos. ¿Es accesible la base de datos desde internet?



2. ¿Cómo llega el contenedor app a la base de datos de la misma aplicación compose si la base de datos no publica ningún puerto?



3. Después de reiniciar el servidor, sus contenedores ya no se están ejecutando. ¿Qué falta probablemente en el archivo compose?



4. ¿Cuál es la diferencia entre docker compose down y docker compose down -v?



5. Un programa de panel pide en su guía - /var/run/docker.sock:/var/run/docker.sock. ¿Qué significa eso para la seguridad?



Ejercicio

Ejercicio Z9.3: Revisar un archivo compose. El archivo de ejemplo compose-nakijken.txt contiene (como archivo de texto) el archivo compose de una pequeña tienda online con una aplicación, una base de datos, una caché y una herramienta de administración. Contiene seis errores tratados en esta lección o en las lecciones 9.1 y 9.2. Indique para cada bloque de líneas numerado si es correcto o incorrecto, y elija para cada error la corrección adecuada. El programa compara sus respuestas con la clave fija. Si tiene su propio archivo compose, revíselo después siguiendo los mismos puntos.

Ejercicio Z9.3: Revisar un archivo compose

Archivo de este ejercicio: compose-nakijken.txt





















No guardamos lo que escriba ni lo que encontremos. Su navegador recuerda sus respuestas y si ha superado el ejercicio. Si ha iniciado sesión, en Mis cursos solo guardamos que el ejercicio estaba (casi) correcto; allí también puede eliminarlo.

Para saber más

Un momento…

Configure sus dominios

Configure para cada dominio los servidores de nombres, la vinculación a su hosting y quién será el titular.

Cargando sus dominios…