1Por qué importa
Todo el material práctico de este curso viaja en archivos docker-compose.yml.
Si no entiendes qué hace cada línea, el laboratorio se convierte en una caja negra que
unas veces funciona y otras no, y aprender seguridad con una caja negra debajo es
aprender supersticiones.
Pero hay una razón mucho más grande que el curso. Docker y Compose son, hoy, la forma en que la mayoría del software se despliega y se prueba. Las mismas tres primitivas que vas a usar para montar tu laboratorio —redes, volúmenes y variables de entorno— son las que deciden, en producción, si un fallo en una aplicación se queda en esa aplicación o se lleva por delante el resto de la máquina. Cada línea de un compose es una decisión de frontera de confianza, aunque nadie la llame así en la revisión de código.
Y hay una tercera razón, incómoda: Docker está lleno de comportamientos por defecto
cómodos y poco seguros. Publicar en todas las interfaces, dar salida a internet a todo,
ejecutar como root dentro del contenedor, montar rutas del anfitrión sin
aviso. Ninguno es un fallo del producto: son decisiones de usabilidad tomadas cuando el
caso principal era “que esto arranque”. Saber cuáles son y cómo revertirlos es la mitad
del trabajo.
No estás aprendiendo Docker. Estás aprendiendo a leer un archivo de despliegue y
responder: si un atacante consigue ejecutar comandos dentro de este contenedor,
¿qué alcanza? Esa pregunta se responde con tres lecturas —networks,
volumes, environment— y con nada más.
2Conceptos
- Imagen: sistema de archivos de solo lectura, en capas, identificado por una etiqueta o por un digest.
- Contenedor: un proceso del anfitrión con vistas restringidas del sistema y una capa de escritura efímera encima de la imagen.
namespace: mecanismo del núcleo que hace que un proceso vea su propia lista de procesos, su propia red o su propio sistema de archivos.cgroup: mecanismo que limita cuánta memoria o CPU puede consumir. Limita recursos, no visibilidad.- Volumen nombrado: almacenamiento gestionado por Docker, fuera del contenedor y fuera de tu árbol de directorios.
- Montaje de enlace: una ruta de tu máquina expuesta dentro del contenedor. Es una puerta en las dos direcciones.
tmpfs: montaje en memoria que desaparece al parar el contenedor. No toca tu disco.- Variable de entorno: par nombre-valor visible para el proceso, para todos sus hijos y para cualquiera que pueda inspeccionar el contenedor.
3Explicación profunda
Qué aísla un contenedor, y qué no
Un contenedor no es una máquina. Es un proceso normal de tu sistema al que el núcleo le
ha dado vistas propias de algunas cosas. El namespace de procesos hace que
solo vea los suyos; el de red, que tenga su propia pila e interfaces; el de montajes,
que vea otro árbol de directorios. Los cgroup le ponen techo a la memoria
y a la CPU. Y ya.
Lo que no hay es separación de núcleo. Tu núcleo y el del contenedor son el mismo, con las mismas llamadas al sistema y los mismos fallos. Un exploit de escalada en el núcleo ejecutado dentro de un contenedor se ejecuta contra tu máquina. Por eso el material realmente destructivo de este curso vive en una máquina virtual, que es la lección siguiente, y no en un contenedor.
Esta distinción tiene una consecuencia práctica que conviene fijar ya: un contenedor es una frontera buena contra accidentes y una frontera mediana contra un atacante. Contiene un proceso mal configurado, un binario que escribe donde no debe o una dependencia con un fallo de aplicación. No contiene, por sí solo, a alguien que trae un exploit de núcleo o al que le has montado el socket de Docker.
Redes: tres opciones y dos de ellas queman una frontera
Compose te da, en la práctica, tres modos de conectar un servicio, y no son variantes del mismo mecanismo: son tres niveles de aislamiento distintos.
| Modo | Qué ve el contenedor | Cuándo usarlo |
|---|---|---|
bridge con internal: true | Solo a sus vecinos de red. Sin salida. | Por defecto en todo laboratorio. Es la opción segura. |
bridge normal | A sus vecinos y a internet, con NAT. | Solo la pieza de borde, y solo si de verdad necesita salir. |
network_mode: host | La pila de red de tu máquina, entera. | Casi nunca. Elimina por completo el aislamiento de red. |
El tercero merece un párrafo propio porque aparece en muchas respuestas de foro como
solución rápida a “no consigo que se conecten”. Con network_mode: host el
contenedor no tiene red propia: usa la tuya. Todo lo que escuche dentro escucha
en tus interfaces, la sintaxis ports deja de aplicarse —se ignora— y con
ella desaparece la protección de 127.0.0.1:. Un servicio vulnerable en
modo anfitrión está expuesto exactamente igual que si lo hubieras instalado a mano en
tu equipo.
Dentro de una red definida por ti, Compose activa el resolutor interno: el nombre del
servicio resuelve al contenedor. Ojo con un matiz que confunde a mucha gente: en la red
bridge por defecto de Docker —la que usas cuando no declaras redes— ese
resolutor por nombre no funciona igual. Declarar tus redes no es cosmética;
cambia el comportamiento.
Volúmenes: dónde acaba viviendo lo que el laboratorio escribe
Los tres tipos de montaje resuelven problemas distintos y tienen riesgos distintos. Elegir mal no da un error: da un laboratorio que deja restos en tu disco o, peor, uno que puede escribir en tus archivos.
# 1) volumen nombrado: lo gestiona Docker, vive fuera de tu arbol volumes: - db_datos:/var/lib/postgresql/data # 2) montaje de enlace: una ruta TUYA dentro del contenedor volumes: - ./app:/usr/share/nginx/html:ro # :ro = el contenedor no puede escribir # 3) tmpfs: en memoria, desaparece al parar. No toca el disco. tmpfs: - /tmp
El montaje de enlace es el peligroso, y no por sutil: lo que montas, lo entregas.
Si la aplicación vulnerable tiene montado un directorio tuyo con permiso de escritura,
el ejercicio de escritura arbitraria de archivos que vas a hacer en la fase web deja de
ser un ejercicio en el momento en que la ruta apunte al montaje. Por eso en los
laboratorios del curso los montajes de enlace son siempre de rutas del propio
laboratorio y casi siempre con :ro.
Hay un caso especial que merece nombre propio: montar el socket de Docker
(/var/run/docker.sock) dentro de un contenedor. Se ve constantemente en
ejemplos de herramientas de monitorización. Es equivalente a dar acceso de
administrador a tu máquina, porque quien habla con ese socket puede crear un contenedor
privilegiado que monte la raíz del anfitrión. No es una escalada de privilegios: es un
atajo documentado. En un laboratorio nunca hace falta.
Y el detalle que arruina más sesiones de práctica: docker compose down sin
-v conserva los volúmenes nombrados. Crees que reiniciaste el
laboratorio y en realidad arrastras el estado del ataque anterior. Cuando un ejercicio
“ya no reproduce”, esta es la primera sospechosa.
Variables de entorno: cómodas, reproducibles y no secretas
Compose ofrece varias vías para inyectar configuración, y conviene tener clara la precedencia porque cuando dos se contradicen no hay ningún aviso:
| Origen | Para qué sirve | Prioridad |
|---|---|---|
environment: en el servicio | Valor fijo para ese contenedor. | La más alta de las declaradas en el archivo. |
env_file: | Un archivo con muchos valores; se pasan al contenedor. | Por debajo de environment. |
Archivo .env del directorio | Sustituir ${VARIABLES} dentro del propio compose. | Es otra cosa: interpola el YAML, no llega solo al contenedor. |
| Entorno de tu terminal | Sustitución en el compose y valores de environment sin valor. | Gana sobre .env en la interpolación. |
La confusión clásica es creer que .env y env_file son lo
mismo. El primero rellena huecos ${ASI} del propio YAML; el segundo define
variables dentro del contenedor. Un laboratorio que mezcla ambos sin saberlo produce
exactamente el peor error posible: funciona en tu máquina y no en la siguiente.
Ahora la parte de seguridad, que es la importante. Una variable de entorno no es un secreto, y hay tres formas de leerla que no requieren nada especial:
# 1) desde el anfitrion, sin entrar al contenedor docker inspect lab00-db --format '{{json .Config.Env}}' # 2) desde dentro, en cualquier proceso: el entorno esta en /proc cat /proc/1/environ | tr '\0' '\n' # 3) por herencia: cualquier proceso hijo lo recibe, incluido # el que se ejecuta por una inyeccion de comandos env
Añade a eso que las variables suelen acabar en volcados de error, en trazas de depuración y en los informes de fallos de muchas librerías. En este curso las contraseñas de laboratorio se escriben en claro a propósito, para que se vean y para que en la fase de gestión de secretos tengas un antes y un después concreto que comparar. Lo que no se puede es escribirlas en claro y llamarlo seguro.
4Ejemplo sencillo
# Las cuatro preguntas que responde cualquier compose networks ¿con quien puede hablar? -> alcance ports ¿quien puede hablarle? -> exposicion volumes ¿que puede leer y escribir? -> datos environment ¿que sabe? -> credenciales # Y las cuatro respuestas seguras por defecto en un laboratorio networks una sola red, con internal: true ports ninguno, o uno con 127.0.0.1: delante volumes solo rutas del laboratorio, con :ro donde se pueda environment nada que no puedas publicar en el repositorio
5Ejemplo técnico
El archivo que escribes no es el que se ejecuta. Compose expande formas cortas,
interpola variables y aplica valores por defecto. docker compose config
imprime el resultado real, y esa es la única versión que merece la pena auditar:
El campo host_ip es la comprobación que buscas. Si falta, o si vale
0.0.0.0, el servicio está publicado en todas las interfaces. Un verificador
automático es media docena de líneas sobre esta salida, y es infinitamente más fiable
que buscar texto en el archivo escrito a mano, donde la misma cosa se puede expresar de
tres formas distintas.
Prueba también la otra mitad: pídele a Docker el entorno real de un contenedor y compáralo con lo que creías haber puesto. Es habitual descubrir variables heredadas de la imagen base que nadie declaró.
6Diagrama
Ver el mismo diagrama como código Mermaid (editable)
flowchart TB
P[ports] --> NS
V[volumes de enlace] --> NS
E[environment] --> NS
NS[namespaces + cgroups] --> K[nucleo compartido]
7Código
Un servicio escrito con las cuatro decisiones explícitas. Cada comentario responde una de las cuatro preguntas de la sección 4:
services: app: image: nginx:1.27-alpine # etiqueta fija, no "latest" # alcance: una sola red, sin salida networks: - lab_interna # exposicion: ningun puerto. Se llega por el proxy o no se llega. # datos: solo rutas del laboratorio, y de solo lectura volumes: - ./app:/usr/share/nginx/html:ro tmpfs: - /tmp # lo temporal no toca tu disco # credenciales: nada que no puedas publicar environment: APP_ENTORNO: laboratorio # endurecimiento barato y casi siempre olvidado read_only: true # raiz del contenedor inmutable security_opt: - no-new-privileges:true # nada de escalar por setuid cap_drop: - ALL # sin capacidades del nucleo
Las tres últimas claves no aparecen en casi ningún tutorial y cuestan una línea cada
una. read_only convierte la raíz del contenedor en inmutable, así que un
payload que quiera dejar un archivo se queda sin sitio.
no-new-privileges impide que un binario con bit setuid gane
privilegios. cap_drop: ALL quita las capacidades del núcleo que casi ningún
servicio necesita. Ninguna de las tres arregla un fallo de aplicación; las tres
encarecen mucho lo que viene después del fallo.
8Qué puede salir mal
Etiqueta móvil. latest hace que el laboratorio de hoy no sea el
de mañana. Cuando un ejercicio deje de reproducirse no vas a poder distinguir tu
error de un cambio en la imagen. Para reproducibilidad de verdad, fija el digest.
Creer que down reinicia. Sin -v, los volúmenes
nombrados sobreviven. El “estado limpio” que crees tener arrastra los datos que
escribiste atacando.
Montar de más. Un montaje de enlace con permiso de escritura sobre una ruta tuya convierte cualquier escritura arbitraria del contenedor en escritura en tu disco. El caso extremo es el socket de Docker, que es acceso de administrador al anfitrión disfrazado de volumen.
Usar network_mode: host para salir del paso. Elimina el
aislamiento de red completo y hace que ports se ignore en silencio,
con lo que la protección de 127.0.0.1: desaparece sin que nada avise.
Tratar environment como un almacén de secretos. Es legible con
docker inspect, desde /proc y por cualquier proceso hijo,
incluido el que abre una inyección de comandos.
9Cómo lo abusaría un atacante
Supón que alguien consigue ejecutar un comando dentro de un contenedor, por la vía
que sea. Lo primero que hace no es buscar un exploit exótico: hace inventario, y ese
inventario son exactamente las primitivas de esta lección. env para las
credenciales, mount para ver qué hay montado, y un vistazo a la red
para saber a quién puede hablar.
Las variables de entorno suelen dar el mejor botín por unidad de esfuerzo. En un despliegue típico contienen la cadena de conexión a la base de datos, alguna clave de API y a veces el token de un servicio de nube. No hay que romper nada: están ahí porque alguien decidió que era la forma cómoda de configurar.
Los montajes son el segundo objetivo. Un montaje de escritura hacia una ruta del anfitrión es una vía directa: escribir en un directorio de tareas programadas, en un script que se ejecuta periódicamente o en la configuración de un servicio da ejecución fuera del contenedor sin tocar el núcleo. Y si lo montado es el socket de Docker, el trabajo se acaba ahí: se pide un contenedor privilegiado con la raíz del anfitrión dentro y ya está.
Lo tercero es la red, y es lo que más subestima la gente. Un contenedor
comprometido en una red con salida sirve para exfiltrar y para atacar a terceros
desde tu conexión. En una red con internal: true, ninguna de las dos
cosas está disponible, y eso convierte una intrusión completa en una molestia local.
10Cómo defenderlo
- Audita la forma expandida, no el archivo escrito:
docker compose configes la verdad y hace explícito elhost_ip. - Etiquetas fijas, y digest cuando la reproducibilidad importe. Un entorno que no se reproduce no se puede investigar.
- Volúmenes nombrados por defecto; montajes de enlace solo del propio laboratorio y con
:ro;tmpfspara lo temporal. - Nunca el socket de Docker dentro de un contenedor. Si una herramienta lo pide, la respuesta correcta es cambiar de herramienta o darle un intermediario con permisos mínimos.
- Las tres líneas baratas en cada servicio:
read_only,no-new-privileges,cap_drop: ALL. Quita las que rompan algo, y anota por qué. - Trata
environmentcomo público. Para lo que no puede serlo, la solución es un gestor de secretos, y llega en su propia fase del curso. - Reinicia con
down -vsiempre que quieras estado limpio, y comprueba condocker volume lsque no quedó nada.
11Laboratorio
Levanta el laboratorio y recorre las tres primitivas de la lección:
cd cursos/seguridad/labs/lab-00-base
docker compose up -d
docker compose config | head -40
Primero, la contraseña de la base de datos. Está en claro en el compose a propósito: compruébalo desde los dos lados de la frontera.
Segundo, los volúmenes. Comprueba que down sin -v no limpia:
docker compose exec db sh -c "touch /var/lib/postgresql/data/marca_de_ayer"
docker compose down
docker compose up -d
docker compose exec db ls /var/lib/postgresql/data/marca_de_ayer
El archivo sigue ahí. Ese es el estado fantasma que arruina la reproducibilidad
de un ejercicio. Repite el ciclo con down -v y verifica que
desaparece. Cuando termines:
docker compose down -v
docker volume ls
12Ejercicios
Clasifica seis montajes
Para cada uno di el tipo (volumen nombrado, montaje de enlace o tmpfs)
y si te parece aceptable en un laboratorio:
(1) db_datos:/var/lib/postgresql/data;
(2) ./app:/usr/share/nginx/html:ro;
(3) /var/run/docker.sock:/var/run/docker.sock;
(4) ~/:/host;
(5) /tmp como tmpfs;
(6) ../../:/repo.
(1) volumen nombrado, correcto. (2) montaje de enlace de solo lectura sobre una
ruta del laboratorio, correcto. (3) montaje de enlace del socket de Docker:
inaceptable, equivale a acceso de administrador al anfitrión. (4) montaje de tu
directorio personal con escritura: inaceptable, entrega tus claves y tu
historial. (5) tmpfs, correcto y además deseable. (6) montaje del
repositorio entero: mala idea, sale del directorio del laboratorio y suele
arrastrar el directorio de control de versiones.
Clave: los aceptables son 1, 2 y 5. La regla que se evalúa: un montaje de enlace apunta a una ruta del laboratorio, con :ro salvo justificación escrita.
Endurece un servicio sin romperlo
Copia el lab-00-base a un directorio propio y añade al servicio
app las tres líneas de endurecimiento de la sección 7
(read_only, no-new-privileges, cap_drop).
Levántalo. Algo va a fallar. Diagnostica qué, arréglalo sin quitar ninguna de las
tres, y explica en una frase por qué el arreglo es mejor que la marcha atrás.
Con la raíz inmutable, el servidor no puede escribir sus archivos temporales ni
su fichero de identificador de proceso. El arreglo no es quitar
read_only, sino declarar montajes en memoria para esas rutas
concretas:
read_only: true tmpfs: - /tmp - /var/cache/nginx - /var/run
Por qué es mejor: la marcha atrás devuelve el sistema de archivos entero a escritura, mientras que esto concede exactamente las tres rutas necesarias, y además en memoria, así que nada de lo que se escriba sobrevive al reinicio.
Clave: el patrón se llama enumerar lo necesario en vez de permitir todo, y es el mismo que vas a aplicar a permisos, a reglas de firewall y a políticas de identidad durante el resto del curso.
Reproducibilidad de verdad
Investiga la diferencia entre referenciar una imagen por etiqueta y por digest. Después convierte tu copia del laboratorio a digests, documenta cómo los obtuviste, y explica qué garantía te da eso exactamente y cuál no te da. Termina respondiendo: si una imagen fijada por digest tiene una vulnerabilidad nueva, ¿en qué te ayuda haberla fijado?
imagen@sha256:..., y docker image inspect te da el valor que necesitas.El digest identifica un contenido exacto: la misma referencia da siempre los mismos bytes, y si alguien reemplaza la imagen detrás de la etiqueta, tu despliegue no se entera porque ya no depende de la etiqueta. Lo que no te da es seguridad del contenido: fijar el digest de una imagen con un fallo garantiza que seguirás teniendo ese fallo, exactamente igual, para siempre.
En qué ayuda entonces: en que sabes qué tienes. Un inventario de digests se puede cruzar con avisos de vulnerabilidades y decidir la actualización a propósito, en un cambio revisable, en vez de que la imagen cambie sola bajo tus pies. La reproducibilidad no es lo contrario de actualizar: es lo que hace que actualizar sea una decisión y no un accidente.
Clave: la frase que se evalúa es “fijar no es parchear”. Si tu respuesta sugiere que el digest te protege de vulnerabilidades, vuelve a leer el segundo párrafo.
Red Team · saquea el entorno desde dentro
Ponte en el papel del atacante que acaba de conseguir ejecución dentro del
contenedor app. Sin salir de él, y solo con lo que hay en la imagen,
reúne: todas las variables de entorno, la lista de montajes con sus permisos, y la
lista de vecinos alcanzables. Escribe el informe en diez líneas y marca cuál de los
tres hallazgos convertirías primero en un ataque.
Entorno: env o leer /proc/1/environ. Montajes:
cat /proc/mounts, donde se ve el modo de cada uno. Vecinos:
resolver los nombres de servicio y probar los puertos habituales. En el
lab-00-base el resultado es pobre a propósito —la app apenas tiene
entorno— y el hallazgo valioso está un salto más allá: el nombre
db resuelve, y la contraseña vive en el entorno de ese
contenedor.
Qué convertir primero en ataque: el vecino. El entorno propio y los montajes dicen lo que ya tienes; el vecino dice a dónde puedes ir. Ese salto —de ejecución en un servicio a acceso al almacén de datos— es el movimiento lateral, y es el patrón que más veces vas a repetir en el curso.
Clave: el orden correcto es entorno, montajes, vecinos, y la prioridad es siempre lo que amplía el alcance. Si empezaste buscando exploits de núcleo, te saltaste el inventario.
Blue Team · el inventario deja rastro, ¿dónde?
Repite el saqueo del ejercicio anterior mientras observas
docker compose logs -f y los eventos del propio Docker. Responde:
¿qué parte del inventario es visible para el defensor y qué parte es
completamente silenciosa? Después propón dónde habría que instrumentar para que
dejara de serlo, y qué coste tendría.
docker events en otra terminal te enseña qué considera Docker un acontecimiento digno de registrar.
Visible: la creación del contenedor, los arranques y paradas, y las ejecuciones
lanzadas desde fuera con exec. Silencioso: absolutamente todo lo
que ocurre dentro del contenedor. Leer el entorno, listar montajes y
resolver nombres no genera un solo evento en Docker ni una línea en los logs de
aplicación.
La instrumentación que lo hace visible vive en el núcleo, no en el motor de contenedores: recolección de llamadas al sistema con reglas de detección. El coste es real y hay que decirlo: un componente privilegiado más en cada máquina, volumen de eventos alto y una fase de ajuste larga para que las alertas no se ignoren a la semana.
Clave: la conclusión evaluable es que el motor de contenedores no es una fuente de detección de lo que pasa dentro. Guárdala para la fase de detección y respuesta.
Architecture Challenge · una plantilla de compose para toda la organización
Diseña la plantilla base que usarían todos los equipos de una empresa para levantar entornos de desarrollo. Decide qué valores impones, cuáles sugieres y cuáles dejas libres; cómo se comprueba el cumplimiento; qué haces con el equipo que necesita legítimamente una excepción; y cómo evitas que la plantilla se quede obsoleta y todo el mundo la copie mal.
Una propuesta razonable: un archivo base distribuido como paquete versionado que
cada equipo combina con el suyo, en vez de copiarlo. Impuestos y verificados en
el pipeline: dirección obligatoria en los puertos publicados, prohibición del
socket de Docker, prohibición de network_mode: host, imágenes por
digest. Sugeridos con aviso pero no bloqueantes: read_only,
cap_drop, límites de recursos, porque rompen servicios legítimos y
un control que bloquea de más se termina desactivando entero.
Excepciones: con nombre, con fecha de caducidad y declaradas en el propio repositorio del equipo, para que se puedan contar. Una excepción que no se puede contar es una política que no existe.
Clave: no hay una única respuesta. Se evalúa que distingas qué bloquea de qué avisa, que la base se herede en vez de copiarse, y que las excepciones caduquen.
13Preguntas de comprensión
Pones una contraseña en environment:. ¿Dónde queda legible?
Las variables de entorno se propagan hacia abajo por diseño. Esa comodidad es exactamente lo que las inhabilita como mecanismo de secretos.
¿Cuál es la diferencia relevante, en seguridad, entre un volumen nombrado y un montaje de enlace?
Lo que montas, lo entregas. El volumen nombrado vive fuera de tu árbol de directorios; el enlace es una puerta directa a tus archivos.
Ejecutas docker compose down y vuelves a levantar. ¿En qué estado está la base de datos?
Es la causa más frecuente de un ejercicio que “ya no reproduce”: el estado del ataque anterior sigue en el volumen.
¿Qué aísla exactamente un contenedor respecto a tu máquina?
Un contenedor contiene bien los accidentes y contiene a medias a un atacante. Para lo destructivo hace falta separación de núcleo, es decir, una máquina virtual.
14Reto adicional
Construye el laboratorio mínimo reproducible
Escribe desde cero un docker-compose.yml con dos servicios —un
servidor web y un cliente— que cumpla todo esto a la vez: ningún puerto publicado,
red interna, imágenes por digest, raíz de solo lectura, sin capacidades, y una
variable de entorno que el cliente lea para saber a quién consultar. Añade un
LEEME.txt con las comprobaciones que demuestran cada propiedad.
exec; eso es una propiedad del diseño, no una limitación que haya que sortear.
El orden que funciona: primero los dos servicios en una red con
internal: true y sin ports; después
cap_drop: ALL y no-new-privileges, que rara vez
rompen nada; después read_only con los tmpfs
necesarios; y al final el paso a digests, que es mecánico. El cliente resuelve
al servidor por nombre de servicio, tomado de una variable de entorno.
El LEEME.txt es la mitad del ejercicio y es donde se ve si lo
entendiste: una comprobación por propiedad, cada una con el resultado que se
espera. Sin puertos, la comprobación es que la lista de puertos sale vacía; sin
salida, que una petición al exterior falla; raíz inmutable, que un
touch en la raíz da error.
Clave: el entregable que importa es el conjunto de comprobaciones. Un laboratorio cuyas propiedades no se pueden verificar con un comando es una declaración de intenciones, no un entorno.
15Resumen
- Un contenedor es un proceso con vistas propias (
namespace) y límites de consumo (cgroup). Comparte tu núcleo, y eso marca el límite de lo que puede contener. - Las tres primitivas que perforan el aislamiento son las que tú escribes:
ports, montajes de enlace yenvironment. network_mode: hostelimina la red propia y hace queportsse ignore en silencio: adiós a la protección de127.0.0.1:.- Volumen nombrado por defecto, enlace solo del laboratorio y con
:ro,tmpfspara lo temporal. El socket de Docker, nunca. - Una variable de entorno se lee desde fuera, desde
/procy por herencia. Es configuración, no un secreto. downsin-vconserva los volúmenes: el estado fantasma es la primera sospecha cuando un ejercicio deja de reproducirse.- Audita
docker compose config, no el archivo escrito: la forma expandida hace explícito lo que la corta esconde.
docker inspect desde el anfitrión, /proc/<pid>/environ desde dentro, y por herencia en cualquier proceso hijo./var/run/docker.sock es tan grave?.env y env_file?.env rellena los ${huecos} del propio compose; env_file define variables dentro del contenedor. Confundirlos da entornos que solo funcionan en una máquina.16Mis notas
Estas notas se guardan solo en este navegador. No se envían a ningún servidor. Expórtalas desde el panel de progreso si cambias de máquina.
17Recursos
Los comentarios se cargan solo si los pides, desde GitHub Discussions. No se carga nada de terceros mientras no pulses.