Cambiar tema

Guía completa de Docker Secrets: mejores prácticas para gestionar contraseñas y claves API en contenedores

Easton editorial illustration: registry transfer crane

El teléfono vibra sin parar: 24 SMS, todos alertas de anomalías en la base de datos. Me conecto por SSH al servidor y las conexiones a la base de datos están al límite. Alguien está haciendo un escaneo por fuerza bruta.

Reviso los logs y descubro que el atacante usó directamente la contraseña correcta: la de MySQL que escribí hace tres meses en docker-compose.yml. El mes pasado un becario hizo fork del proyecto a su repositorio público para practicar, y así se filtró la contraseña.

Aunque no haya filtración de código, basta con que alguien ejecute docker inspect para ver todas las variables de entorno en texto plano. En este artículo hablamos de cómo gestionar contraseñas en contenedores, qué prácticas peligrosas evitar y qué herramientas protegen de verdad la información sensible.

¿Por qué las variables de entorno no son seguras?

Tres malas prácticas frecuentes

Empiezo contando errores que yo mismo cometí. ¿Te reconoces en alguno?

Primera: escribirlas directamente en el Dockerfile

FROM node:18
ENV DATABASE_PASSWORD=MyS3cr3tP@ssw0rd
ENV API_KEY=sk-1234567890abcdef

Es la peor de todas. Cada instrucción del Dockerfile crea una capa de imagen. Aunque luego uses ENV DATABASE_PASSWORD="" para sobrescribir, la contraseña sigue oculta en las capas históricas. Cualquiera con tu imagen puede sacarla con docker history <nombre-imagen>.

¿No lo crees? Yo tampoco, hasta que un compañero me lo demostró. Bajó una imagen interna de la empresa y con unas pocas órdenes extrajo el Access Key de AWS. Era una clave que dejó hace tres años un compañero que se fue, y nunca la borramos.

Segunda: escribirlas en docker-compose.yml

version: '3.8'
services:
  db:
    image: mysql:8.0
    environment:
      MYSQL_ROOT_PASSWORD: SuperSecretPassword123
      MYSQL_DATABASE: myapp

La usé mucho tiempo. La razón es simple: comodidad. Un docker-compose up -d y listo, desarrollo y depuración muy ágiles.

¿Cuál es el problema? Ese archivo hay que subirlo a Git. Aunque el repositorio sea privado, basta con que alguien gestione mal los permisos — o que alguien haga fork a un repositorio público, como me pasó — y la contraseña queda expuesta.

Tercera: usar archivo .env pero incluirlo en el control de versiones

# archivo .env
DB_PASSWORD=password123
API_SECRET=abcdef123456

Parece más listo: sacas la información sensible a un .env aparte y en docker-compose referencias con ${DB_PASSWORD}.

Pero mucha gente (yo incluido) también sube el .env a Git pensando que «el resto del equipo también tiene que poder ejecutarlo». El resultado es el mismo que escribirlo en docker-compose.

Lo correcto es añadir .env a .gitignore y dar una plantilla .env.example. Pero al principio, ¿quién piensa en todo eso?

Tres riesgos de seguridad de las variables de entorno

Aunque añadas .env a .gitignore y el código no se filtre, las variables de entorno siguen sin ser seguras. ¿Por qué?

Riesgo 1: docker inspect lo muestra todo

Prueba este comando:

docker inspect <ID-contenedor> | grep -A 20 "Env"

Todas las variables de entorno, incluidas las contraseñas, aparecen en texto plano.

¿Qué implica? Cualquiera con acceso al daemon de Docker — operaciones, DevOps, desarrolladores con permisos en el servidor — puede ver tus contraseñas. Sin cracking ni técnicas avanzadas: una sola orden.

En una startup donde trabajé, todos teníamos SSH al servidor de producción por comodidad. Un día un desarrollador frontend curioso por «qué hay en el contenedor» ejecutó docker inspect y la contraseña de la base de datos quedó en pantalla. Por suerte era alguien de confianza. ¿Y si no?

Riesgo 2: los procesos dentro del contenedor también pueden leerlas

Las variables de entorno no solo las ve Docker: todos los procesos del contenedor también. En Linux se guardan en /proc/<PID>/environ.

Si alguna dependencia de tu aplicación tiene una vulnerabilidad y alguien inyecta código, puede leer las variables de entorno y obtener la contraseña.

El año pasado una librería de Node.js tuvo un backdoor que leía credenciales AWS en variables de entorno y las enviaba a internet. Proyectos afectados: decenas de miles.

Riesgo 3: pueden filtrarse en los logs

Este es más sutil.

Muchas aplicaciones imprimen la configuración al arrancar, o en errores vuelcan variables de entorno a los logs. Los archivos de log suelen tener permisos flojos e incluso van a sistemas centralizados (ELK, por ejemplo).

Conocí un equipo que centralizaba todos los logs de contenedores en un clúster Elasticsearch. En una auditoría de seguridad encontraron cientos de contraseñas de base de datos en los logs… Los desarrolladores acostumbrados a console.log(process.env) en depuración y luego se olvidaban de borrarlo.

Peor aún: una vez que la contraseña entra en los logs, es difícil borrarla por completo — copias de seguridad, archivos, réplicas en muchos sitios.

Casos reales

En internet hay muchos incidentes de este tipo; muchas empresas no los hacen públicos. Te cuento algunos que conozco:

Caso 1: filtración en repositorios públicos de GitHub

En 2023 un análisis mostró que más de un millón de repositorios públicos en GitHub contenían claves API, contraseñas de base de datos y otra información sensible. No es que los desarrolladores sean tontos: a menudo es un descuido — configuración de prueba que no se borró, o un .env subido por error.

Un amigo que hace machine learning: su equipo publicó código de entrenamiento en GitHub y subió también credenciales AWS. En 24 horas alguien usó esas credenciales para levantar más de diez instancias GPU y minar. La factura superó los veinte mil dólares.

Caso 2: historial de pipelines CI/CD

Las herramientas CI/CD (Jenkins, GitLab CI, etc.) suelen conservar logs de variables de entorno en el historial de builds. Sin buena ofuscación, esos logs son un almacén de contraseñas.

En un proyecto con GitLab CI para despliegue automático, un compañero que se fue dejó permisos a un equipo externo. Alguien revisó logs de builds antiguos y obtuvo credenciales de la base de datos de producción. Por suerte querían ayudar y nos avisaron. ¿Y si no?

Caso 3: escaneo de imágenes en Docker Hub

Un informe de seguridad de Alibaba escaneó imágenes Docker públicas y encontró que el 76 % tenía vulnerabilidades, muchas con credenciales hardcodeadas.

Algunas empresas suben imágenes internas a Docker Hub público pensando que «nadie va a revisar la imagen de nuestra pequeña empresa». Pero hay crawlers y escáneres automatizados dedicados a eso. Tu imagen puede escanearse en minutos.

No digo esto para asustar, sino para que veas que estos problemas ocurren de verdad y son más comunes de lo que imaginas. Lo bueno: hay formas de evitarlos.

¿Qué es Docker Secrets y cómo usarlo?

Cómo funciona Docker Secrets

Bien, vamos a la solución.

Docker Secrets es el mecanismo oficial de Docker para gestionar claves. En resumen: la clave deja de ser una cadena en texto plano y pasa a ser un archivo cifrado.

El flujo es así:

  1. Creas un secret; Docker lo almacena cifrado en el log Raft del clúster
  2. Cuando el contenedor lo necesita, Docker lo transmite por TLS cifrado
  3. El secret se monta como archivo en /run/secrets/ dentro del contenedor
  4. Ese directorio es tmpfs — sistema de archivos solo en memoria, sin escribir en disco
  5. Al detener el contenedor, el secret se borra de la memoria

Lo clave: el secret no aparece en variables de entorno, no sale en docker inspect, no se empaqueta en la imagen con docker commit.

¿Suena abstracto? La primera vez que leí la documentación de Docker también me perdí. Prueba en la práctica y se entiende.

Una limitación: Docker Secrets nativo solo funciona en modo Swarm. Al principio me frustró — desarrollo local en una sola máquina, ¿por qué no puedo usarlo?

Luego descubrí que docker-compose también soporta file secrets (menos potente, pero usable). En producción con clúster, Swarm o Kubernetes van bien.

Tutorial paso a paso: MySQL + WordPress

Un ejemplo completo. Supongamos que desplegamos un blog WordPress y hay que proteger la contraseña root de MySQL y la del usuario.

Paso 1: crear archivos de secret

Escribe las contraseñas en archivos locales (no los subas a Git):

echo "MyRootPassword123" > db_root_password.txt
echo "MyUserPassword456" > db_password.txt

Paso 2: inicializar Swarm (si aún no lo hiciste)

docker swarm init

Sí, solo esa orden. Si Swarm no está activo, ejecútala. Puedes usar Swarm en una sola máquina, no hace falta un clúster de varios nodos.

Paso 3: crear Docker secrets

docker secret create mysql_root_password db_root_password.txt
docker secret create mysql_password db_password.txt

Después de crearlos, borra de inmediato los archivos locales con contraseñas:

rm db_root_password.txt db_password.txt

Importante. Una vez que Docker leyó y almacenó cifrado, el texto plano local debe desaparecer.

Comprueba que los secrets existen:

docker secret ls

Verás algo así:

ID                          NAME                  CREATED         UPDATED
abc123...                   mysql_root_password   5 seconds ago   5 seconds ago
def456...                   mysql_password        3 seconds ago   3 seconds ago

Nota: no ves el contenido del secret, solo nombre y metadatos. Ese es el punto: una vez creado, ni el administrador obtiene el texto plano.

Paso 4: crear docker-compose.yml

version: '3.8'

secrets:
  mysql_root_password:
    external: true
  mysql_password:
    external: true

services:
  db:
    image: mysql:8.0
    secrets:
      - mysql_root_password
      - mysql_password
    environment:
      MYSQL_ROOT_PASSWORD_FILE: /run/secrets/mysql_root_password
      MYSQL_PASSWORD_FILE: /run/secrets/mysql_password
      MYSQL_USER: wordpress
      MYSQL_DATABASE: wordpress
    volumes:
      - db_data:/var/lib/mysql

  wordpress:
    image: wordpress:latest
    depends_on:
      - db
    ports:
      - "8080:80"
    secrets:
      - mysql_password
    environment:
      WORDPRESS_DB_HOST: db:3306
      WORDPRESS_DB_USER: wordpress
      WORDPRESS_DB_PASSWORD_FILE: /run/secrets/mysql_password
      WORDPRESS_DB_NAME: wordpress

volumes:
  db_data:

Puntos clave:

  1. secrets a nivel raíz: declara qué secrets usas; external: true indica que ya existen y no los crea compose
  2. services.db.secrets: qué secrets usa este servicio
  3. MYSQL_ROOT_PASSWORD_FILE: la imagen oficial de MySQL soporta variables con sufijo _FILE y lee la contraseña del archivo

Ese sufijo _FILE es importante: no todas las imágenes lo soportan. Si no, lee el archivo en tu script de arranque:

# ejemplo entrypoint.sh
export DB_PASSWORD=$(cat /run/secrets/db_password)
# luego arranca tu aplicación

Paso 5: desplegar

docker stack deploy -c docker-compose.yml myapp

Aquí se usa docker stack deploy, no docker-compose up. En modo Swarm hay que usar stack.

Paso 6: verificar

Entra al contenedor MySQL y comprueba el montaje:

docker exec -it <ID-contenedor> sh
ls -la /run/secrets/

Verás:

total 8
-r--r--r-- 1 root root 18 Dec 18 10:00 mysql_password
-r--r--r-- 1 root root 18 Dec 18 10:00 mysql_root_password

Prueba a leer:

cat /run/secrets/mysql_root_password

Aparece la contraseña. Pero lo importante: solo existe en memoria, no se escribe en disco ni aparece en variables de entorno.

Comprueba que docker inspect no muestra la contraseña:

docker inspect <ID-contenedor> | grep -i password

Solo verás rutas como MYSQL_ROOT_PASSWORD_FILE=/run/secrets/mysql_root_password, no la contraseña en sí.

Esa es la ventaja de Docker Secrets: la aplicación dentro del contenedor puede leerla, pero desde fuera no hay texto plano.

Limitaciones de Docker Secrets y alternativas

Docker Secrets no es perfecto. Principales limitaciones:

Limitación 1: requiere modo Swarm

Lo que más molesta. Para desarrollo local en una sola máquina necesitas docker swarm init antes de usar secrets. Aunque Swarm en un solo nodo funciona, parece matar moscas a cañonazos.

Y una vez en Swarm, hay que usar docker stack deploy en lugar del familiar docker-compose up. Cambian los hábitos.

Limitación 2: el secret no se puede modificar

Una vez creado, el contenido es de solo lectura. Para cambiar la contraseña hay que borrar y recrear:

docker secret rm mysql_password
echo "NewPassword789" | docker secret create mysql_password -

Y actualizar todos los servicios que lo usan:

docker service update --secret-rm mysql_password --secret-add mysql_password myapp_db

Más engorroso que cambiar una variable de entorno y reiniciar el contenedor.

Limitación 3: no sirve para contenedores sueltos

Con docker run directo no puedes usar Docker Secrets. Tiene que ser servicio creado con docker service create o desplegado con docker stack.

Poco práctico para pruebas rápidas. A veces solo quiero levantar un contenedor de base de datos y me toca todo el flujo Swarm.

Alternativa para desarrollo local: docker-compose file secrets

docker-compose tiene un atajo: secrets tipo file. No requiere Swarm; monta un archivo local como secret.

Configuración aproximada:

version: '3.8'

secrets:
  db_password:
    file: ./secrets/db_password.txt

services:
  db:
    image: mysql:8.0
    secrets:
      - db_password
    environment:
      MYSQL_ROOT_PASSWORD_FILE: /run/secrets/db_password

Crea el archivo local:

mkdir secrets
echo "MyPassword123" > secrets/db_password.txt

Importante: añade secrets/ a .gitignore.

Más seguro que variables de entorno: la contraseña no sale en docker inspect ni se empaqueta en la imagen. Pero no iguala a Docker Secrets real porque:

  • El archivo sigue en texto plano en disco local
  • No hay transmisión cifrada
  • No hay gestión centralizada

Para desarrollo local suele bastar. En producción, Swarm o Kubernetes de verdad.

Trucos avanzados

Con lo básico dominado, algunos consejos útiles.

Truco 1: ruta de montaje personalizada

Por defecto el secret va en /run/secrets/<secret_name>, pero puedes personalizar ruta y nombre:

services:
  myapp:
    image: myapp:latest
    secrets:
      - source: db_password
        target: /app/config/database.pwd
        mode: 0400

mode: 0400 fija permisos (solo lectura del owner), más seguro.

Truco 2: varios entornos

Desarrollo, pruebas y producción con contraseñas distintas pero el mismo nombre de secret:

# producción
docker secret create db_password prod_password.txt

# pruebas (otro clúster Swarm)
docker secret create db_password test_password.txt

El código solo lee /run/secrets/db_password, sin importar el valor. Mismo código, distintos clústeres por entorno.

Truco 3: rotación de secrets

Cambiar contraseñas periódicamente es buena práctica. La rotación en Docker es un poco enrevesada pero manejable:

# 1. Crear nueva contraseña
echo "NewPassword" | docker secret create db_password_v2 -

# 2. Actualizar servicio: añadir nuevo secret, quitar el viejo
docker service update \
  --secret-rm db_password \
  --secret-add source=db_password_v2,target=/run/secrets/db_password \
  myapp_db

# 3. Tras confirmar que el servicio va bien, borrar el secret viejo
docker secret rm db_password

El truco: el nuevo secret se llama db_password_v2, pero con target la ruta de montaje sigue siendo /run/secrets/db_password. La aplicación no cambia.

Truco 4: crear secret desde stdin

¿No quieres rastro en disco? Crea desde entrada estándar:

echo "MySecretPassword" | docker secret create db_password -

El - final indica lectura desde stdin. Ni archivo temporal.

O más seguro, entrada manual (no queda en el historial del shell):

docker secret create db_password -
# pega la contraseña y Ctrl+D para terminar

Comparación con otras herramientas de gestión de claves

Matriz de cuatro herramientas

Docker Secrets no es la única opción. Según escala e infraestructura, puedes necesitar otras. Tabla comparativa:

HerramientaEscenarioVentaja principalPrincipal inconvenienteDificultad
Docker SecretsClúster Docker SwarmSoporte nativo, costo cero, configuración simpleSolo Swarm, funciones básicas, no multiplataforma⭐ Baja
Kubernetes SecretsProducción K8sNativo del ecosistema K8s, ligado al ciclo de vida del Podetcd sin cifrado por defecto, funciones básicas, requiere configuración extra⭐⭐ Media
HashiCorp VaultEmpresa, multinube, cumplimiento estrictoMáxima potencia, claves dinámicas, permisos granulares, auditoría, varios backendsArquitectura compleja, despliegue propio, curva de aprendizaje alta⭐⭐⭐ Alta
AWS Secrets ManagerEntorno 100 % AWS, RDS/LambdaIntegración profunda con AWS, rotación automática RDS, servicio gestionadoAtado a AWS, difícil multinube, costo por uso⭐⭐ Media

En resumen:

  • Proyecto pequeño, Docker Swarm → Docker Secrets basta, gratis y simple
  • Producción Kubernetes → K8s Secrets + External Secrets Operator hacia Vault externo
  • Gran empresa, multinube, alta seguridad → Vault, inversión inicial, beneficio a largo plazo
  • Todo en AWS → Secrets Manager, muy cómodo con RDS y Lambda

No hay mejor herramienta, solo la más adecuada. Mi camino: al principio proyectos pequeños con docker-compose file secrets; al crecer el equipo, Docker Swarm + Docker Secrets; ahora la empresa en K8s evaluando Vault.

Cada migración tiene costo, pero la seguridad mejora paso a paso.

Recomendaciones de elección

¿Cómo elegir? Algunas referencias:

Si eres…

Desarrollador individual / proyecto pequeño:

  • Desarrollo local: docker-compose file secrets basta
  • VPS con varios servicios: Docker Swarm + Docker Secrets
  • Presupuesto ajustado: no metas Vault, es demasiado

Startup (10-50 personas):

  • Con Docker: Docker Secrets
  • Con K8s: K8s Secrets + External Secrets Operator (evolución gradual hacia Vault)
  • Todo en AWS: Secrets Manager directo

Gran empresa:

  • Multinube: Vault casi obligatorio
  • Auditoría y cumplimiento: Vault
  • Equipo de seguridad dedicado: Vault
  • Presupuesto amplio: Vault

Árbol de decisión simplificado:

¿Usas K8s?
 └─ Sí → K8s Secrets; funciones avanzadas → Vault
 └─ No → ¿Docker Swarm?
      └─ Sí → Docker Secrets
      └─ No → ¿En AWS?
           └─ Sí → AWS Secrets Manager
           └─ No → docker-compose file secrets (dev) / Vault (prod)

Casos reales

Dos caminos que conozco:

Caso 1: evolución de una startup

Un amigo con SaaS: al principio 5 personas, todo en Docker en un VPS. docker-compose + file secrets, archivos de contraseña en el servidor, gestión manual.

Seis meses después, ronda ángel, 15 personas, microservicios. Pasaron a Docker Swarm y Docker Secrets: gestión centralizada en el clúster, sin copiar archivos en cada servidor.

Un año más, ronda A y migración a K8s (contrataron DevOps): K8s Secrets. AWS RDS y S3 con Secrets Manager; External Secrets Operator sincroniza a K8s.

Ahora evalúan Vault unificado por estrategia multinube (backup en GCP); Secrets Manager atado a AWS les limita.

La arquitectura de seguridad evoluciona con el negocio; no hace falta la solución más compleja desde el día uno.

Caso 2: empresa tradicional de un salto

Otro amigo en un banco, containerización el año pasado. Sector financiero, cumplimiento estricto: Vault desde el inicio.

Curva de aprendizaje alta, pero:

  • Auditoría completa de cada acceso a claves
  • Claves dinámicas (credenciales temporales de BD que expiran solas)
  • Integración con LDAP existente

Dos personas-mes en despliegue y formación; problema resuelto de una vez, mantenimiento bajo después.

Contextos distintos, elecciones distintas. Vault puede no compensar en una pyme; Docker Secrets puede quedarse corto en una gran empresa.

Checklist de mejores prácticas para producción

Medidas de seguridad inmediatas

Uses o no Docker Secrets, haz esto ya:

✅ Revisar historial de Git en busca de información sensible

# buscar posibles contraseñas
git log -p -S 'password' -S 'secret' -S 'api_key'

# comprobar si .env se subió alguna vez
git log --all --full-history -- .env

Si encuentras algo, limpia el historial con git filter-repo o BFG Repo-Cleaner. Luego cambia de inmediato todas las contraseñas filtradas.

✅ Configurar .gitignore

# .gitignore
.env
*.env
secrets/
db_password.txt
*_password.txt
*_secret.txt
*.pem
*.key

No confíes en la suerte; añádelo.

✅ Activar GitHub Secret Scanning

En GitHub: Settings → Security → Secret scanning alerts. Si subes una clave por error, GitHub avisa.

La versión gratuita cubre repos públicos; privados requieren Pro/Team/Enterprise. Los públicos son los que más lo necesitan.

✅ Rotar credenciales que puedan haberse filtrado

Inventario:

  • Contraseñas de base de datos
  • Claves API (AWS/OpenAI/Stripe, etc.)
  • JWT secret
  • OAuth client secrets
  • Claves privadas SSL

Cambia todo lo que puedas. Tras una filtración, aún hay tiempo de reaccionar.

Pasos para implementar Docker Secrets

¿Vas con Docker Secrets? Este orden:

Paso 1: inventario de información sensible

Lista de qué es sensible:

✓ Contraseña root MySQL
✓ Contraseña de aplicación en BD
✓ Contraseña Redis
✓ Clave de firma JWT
✓ Claves API de terceros (pago/SMS, etc.)
✓ Clave privada de certificado SSL
✓ OAuth client secret

Paso 2: elegir herramienta

Según la comparación: Docker Secrets, K8s Secrets o Vault. Elige una; puedes migrar después.

Paso 3: crear secrets

# Docker Swarm
docker swarm init
docker secret create db_password <(echo "contraseña")
docker secret create api_key <(echo "clave")

# docker-compose file secrets
mkdir secrets
echo "contraseña" > secrets/db_password.txt
echo "clave" > secrets/api_key.txt
chmod 600 secrets/*

Paso 4: modificar configuración

Cambia environment por secrets en docker-compose.yml:

# antes
services:
  app:
    environment:
      DB_PASSWORD: "hardcoded_password"  # ❌

# después
services:
  app:
    secrets:
      - db_password
    environment:
      DB_PASSWORD_FILE: /run/secrets/db_password  # ✅

Paso 5: modificar código de aplicación (si hace falta)

Si tu framework no soporta sufijo _FILE, lee el archivo en código:

// ejemplo Node.js
const fs = require('fs');
const dbPassword = process.env.DB_PASSWORD_FILE
  ? fs.readFileSync(process.env.DB_PASSWORD_FILE, 'utf8').trim()
  : process.env.DB_PASSWORD;

Paso 6: probar

En entorno de pruebas, confirma:

  • El servicio arranca
  • Lee la contraseña correctamente
  • docker inspect no muestra texto plano
  • La funcionalidad es correcta

Paso 7: despliegue a producción

Si todo va bien, despliega. Como seguro, conserva temporalmente variables de entorno antiguas como respaldo; bórralas cuando el nuevo esquema sea estable.

Mantenimiento continuo de seguridad

Docker Secrets no es «configurar y olvidar»:

Rotar claves periódicamente (cada 90 días recomendado)

Calendario cada trimestre. Tras una baja, cambia las contraseñas relacionadas.

Monitorizar logs de acceso a claves

Con Vault, revisa audit logs y patrones anómalos.

Docker Secrets y K8s Secrets no tienen auditoría por defecto; para eso hace falta otras herramientas (Falco, por ejemplo).

Principio de mínimo privilegio

No des a todos los servicios acceso a todos los secrets:

services:
  frontend:
    secrets:
      - api_key  # el frontend solo necesita API key

  backend:
    secrets:
      - db_password  # el backend necesita BD
      - api_key

Herramientas de escaneo de claves

Integra en CI/CD:

Un pre-commit hook evita muchos envíos accidentales.

Cinco trampas frecuentes

1. No uses ARG para información sensible

# ❌ incorrecto
ARG DB_PASSWORD=secret
ENV DATABASE_URL=postgres://user:${DB_PASSWORD}@db/myapp

ARG queda en el historial de build; docker history lo muestra.

2. No imprimas secrets en stdout

# ❌ incorrecto
password = open('/run/secrets/db_password').read()
print(f"Using password: {password}")  # ¡va a docker logs!

Los sistemas de logs capturan stdout. Una vez en logs, difícil de borrar.

3. No reutilices el mismo secret entre entornos

Separa contraseñas de desarrollo, pruebas y producción. Si filtra pruebas, no arrastre producción.

4. No copies el secret a otra ruta dentro del contenedor

# ❌ incorrecto
cp /run/secrets/db_password /app/config/password.txt

/run/secrets es tmpfs en memoria; al parar el contenedor desaparece. Copiar a otra ruta puede escribir en disco y empeorar la seguridad.

Lee directamente desde /run/secrets.

5. No olvides expiración y rotación

Con claves dinámicas con expiración (Vault), configura renovación o rotación automática. No esperes a que expire a las 3 de la madrugada y caigan todos los servicios.

Conclusión

En pocas palabras: no pongas contraseñas donde puedan verse.

Las variables de entorno parecen cómodas, pero docker inspect las expone. ENV en Dockerfile es peor: quedan para siempre en las capas. Muchos incidentes vienen de estos atajos «por comodidad».

Docker Secrets no es perfecto — requiere Swarm, no se modifica, incómodo en desarrollo local. Pero resuelve lo esencial: almacenamiento cifrado, transmisión cifrada, fuera de variables de entorno e imágenes. En clúster Swarm es la opción más directa.

Con Kubernetes: K8s Secrets; necesidades empresariales: Vault; todo en AWS: Secrets Manager. No hay mejor herramienta, solo la adecuada.

Lo importante es actuar. Hoy mismo comprueba:

  • ¿Hay contraseñas en tu historial de Git?
  • ¿.env está en .gitignore?
  • ¿docker inspect muestra tus contraseñas?

Si respondes sí / no / sí, toca dedicar tiempo a corregirlo. No hace falta Vault de golpe; pasar de variables de entorno a file secrets ya mejora la situación.

La seguridad es un proceso continuo, no una configuración única. Rotar contraseñas, vigilar accesos anómalos, escanear código: estos hábitos importan más que la herramienta.

Compártelo con tu equipo. La gestión de claves no es de una sola persona; es responsabilidad colectiva. Estándares comunes y revisiones periódicas reducen el riesgo de verdad.

Que no te despierten más alertas a las 3 de la madrugada.


Recursos de referencia:

Flujo completo de gestión segura con Docker Secrets

Gestiona contraseñas de contenedores y claves API de forma segura, compara Docker/K8s/Vault y ofrece un checklist completo para producción

⏱️ Estimated time: 1 hr

  1. 1

    Step 1: Entender el problema de seguridad y las malas prácticas

    Problema de seguridad:
    • El atacante usó directamente la contraseña correcta: la de MySQL escrita en docker-compose.yml
    • El mes pasado un becario hizo fork del proyecto a su repositorio público para practicar
    • La filtración de contraseñas provocó un escaneo por fuerza bruta de la base de datos

    Malas prácticas:
    • Escribir contraseñas directamente en archivos de configuración
    • Creer que las variables de entorno son seguras
    • Incluso decir 'mi repositorio es privado, no pasa nada'
    • Todo esto son riesgos de seguridad

    Errores frecuentes:
    • Contraseña en el Dockerfile
    • Contraseña en docker-compose.yml
    • Contraseña en variables de entorno pero subida a Git
    • Usar archivo .env sin añadirlo a .gitignore
  2. 2

    Step 2: Gestionar claves con Docker Secrets

    Solución con Docker Secrets:
    • Crear secretos con docker secret create: docker secret create mysql_password -
    • Configurar secrets en docker-compose.yml: secrets: - mysql_password
    • Acceder dentro del contenedor por /run/secrets/: cat /run/secrets/mysql_password
    • Los secretos se almacenan cifrados en Docker Swarm

    Crear secretos:
    • echo "your-password" | docker secret create mysql_password -
    • Referenciar en docker-compose.yml: secrets: - mysql_password
    • Leer dentro del contenedor: cat /run/secrets/mysql_password
  3. 3

    Step 3: Comparación de otras soluciones y mejores prácticas

    Comparación de otras soluciones:
    • Docker Secrets (adecuado para Docker Swarm, almacenamiento cifrado)
    • Kubernetes Secrets (entorno K8s, codificación Base64)
    • HashiCorp Vault (gestión empresarial, claves dinámicas)
    • AWS Secrets Manager (entorno AWS, integración con servicios AWS)
    • Archivo de variables de entorno (.env, no recomendado en producción)

    Mejores prácticas:
    • En producción, usar siempre Secrets para información sensible
    • Rotar claves periódicamente
    • Usar herramientas de escaneo (trufflehog) para revisar repositorios Git
    • Configurar control de acceso para evitar filtraciones
    • No codificar contraseñas en el código

FAQ

¿Por qué no escribir contraseñas en el Dockerfile o docker-compose.yml?
Problema de seguridad: el atacante usó directamente la contraseña correcta — la de MySQL escrita en docker-compose.yml. El mes pasado un becario hizo fork del proyecto a su repositorio público para practicar, y la filtración provocó un escaneo por fuerza bruta de la base de datos.

Malas prácticas:
• Escribir contraseñas directamente en archivos de configuración
• Creer que las variables de entorno son seguras
• Incluso decir 'mi repositorio es privado, no pasa nada'
• Todo esto son riesgos de seguridad

Errores frecuentes:
• Contraseña en el Dockerfile
• Contraseña en docker-compose.yml
• Contraseña en variables de entorno pero subida a Git
• Usar archivo .env sin añadirlo a .gitignore
¿Cómo gestionar claves con Docker Secrets?
Solución con Docker Secrets:
• Crear secretos con docker secret create (docker secret create mysql_password -)
• Configurar secrets en docker-compose.yml (secrets: - mysql_password)
• Acceder dentro del contenedor por /run/secrets/ (cat /run/secrets/mysql_password)
• Los secretos se almacenan cifrados en Docker Swarm

Crear secretos:
• echo "your-password" | docker secret create mysql_password -
• Referenciar en docker-compose.yml: secrets: - mysql_password
• Leer dentro del contenedor: cat /run/secrets/mysql_password
¿En qué se diferencia Docker Secrets de otras soluciones de gestión de claves?
Comparación de otras soluciones:
• Docker Secrets (adecuado para Docker Swarm, almacenamiento cifrado)
• Kubernetes Secrets (entorno K8s, codificación Base64)
• HashiCorp Vault (gestión empresarial, claves dinámicas)
• AWS Secrets Manager (entorno AWS, integración con servicios AWS)
• Archivo de variables de entorno (.env, no recomendado en producción)

Recomendaciones:
• Entorno Docker Swarm: Docker Secrets
• Entorno K8s: Kubernetes Secrets
• Necesidades empresariales: Vault
• Entorno AWS: Secrets Manager
¿Cuáles son las mejores prácticas de gestión de claves en producción?
Mejores prácticas:
• En producción, usar siempre Secrets para información sensible
• Rotar claves periódicamente
• Usar herramientas de escaneo (trufflehog) para revisar repositorios Git
• Configurar control de acceso para evitar filtraciones
• No codificar contraseñas en el código

Revisiones periódicas:
• Escanear repositorios Git con trufflehog
• Comprobar si hay filtraciones de claves
• Configurar CI/CD con escaneo automático
• Rotar claves de inmediato si se detecta una filtración

19 min de lectura · Publicado el: 18 dic 2025 · Actualizado el: 21 ago 2026

Comentarios

Inicia sesión con GitHub para dejar un comentario

Easton BlogEaston Blog