
Guardar secretos en Docker: un ENV borrado puede seguir dentro de la imagen

Para proteger credenciales en Docker, usa un montaje de secretos de BuildKit durante la compilación y archivos de secretos durante la ejecución. La guía de secretos de compilación de Docker advierte que ARG y ENV son inadecuados para entregar credenciales: sus valores pueden persistir en la imagen.
Si un Dockerfile establece una contraseña con ENV, borrarla o sustituirla en una instrucción posterior no elimina necesariamente el valor anterior del historial. Para decidir el mecanismo adecuado, identifica cuándo se necesita la credencial: solo durante una instrucción de compilación, al iniciar un contenedor con Compose o mientras funciona un servicio de Swarm.
Qué permanece cuando se borra un ENV
ENV modifica la configuración de la imagen. Una instrucción posterior que asigne un valor vacío puede hacer que la configuración final parezca limpia, mientras el historial conserva la instrucción que contenía el valor original. Ejecutar «RUN unset NOMBRE» tampoco deshace un ENV definido en el Dockerfile: ese comando afecta al proceso de la instrucción RUN, no elimina la configuración heredada por los contenedores.
ARG plantea otro riesgo. Aunque su valor no se convierte automáticamente en una variable del contenedor final, puede aparecer en el historial o en metadatos de la compilación. Tampoco sirve copiar un archivo de credenciales a la imagen y borrarlo en una instrucción posterior: la capa que añadió el archivo puede seguir dentro del archivo de imagen. Si una credencial real ya entró en una imagen distribuida, corresponde rotarla y reconstruir la imagen sin introducir ese valor.
BuildKit: entregar el secreto solo a la instrucción que lo usa
El montaje de BuildKit pone un secreto a disposición de una instrucción RUN durante su ejecución, sin incorporarlo por sí mismo a la imagen final. Como ejemplo condicional con una cadena ficticia, guarda «MARCADOR_DE_PRUEBA» en «../credenciales/api_token.txt», fuera del contexto de construcción. El Dockerfile mínimo contiene «FROM alpine» y «RUN --mount=type=secret,id=api_token,required=true test -s /run/secrets/api_token».
Construye la imagen con «docker build --secret id=api_token,src=../credenciales/api_token.txt -t demo-secrets .». El identificador api_token relaciona el archivo proporcionado al comando con el montaje solicitado en RUN. La opción required=true hace fallar esa instrucción si falta el secreto; test comprueba que el archivo contiene datos sin imprimirlos. En una compilación real, sustituye test por el cliente que necesita autenticarse y haz que lea la ruta montada.
El alcance de la protección termina donde comienza la salida del programa. Si el cliente escribe la credencial en un registro, un archivo de configuración o un artefacto que después se copia, ese resultado sí puede acabar en la imagen. Evita imprimir el valor en los registros de compilación y revisa los archivos que produce la instrucción antes de distribuir la imagen.
Compose: montar un archivo en el contenedor autorizado
Para ejecutar un servicio con Compose, declara el secreto en el nivel superior de compose.yaml y concédelo al servicio mediante secrets. La documentación de secretos de Compose indica que, en contenedores Linux, entrega cada secreto como un archivo montado bajo /run/secrets; solo los servicios a los que se concede acceso reciben ese montaje.
Un ejemplo mínimo, usando un archivo local ficticio «./api_token.txt», expresa la configuración en estas dos líneas YAML: «services: {app: {image: alpine, command: [sleep, infinity], secrets: [api_token]}}» y «secrets: {api_token: {file: ./api_token.txt}}». Tras «docker compose up -d», ejecuta «docker compose exec -T app test -s /run/secrets/api_token». Un resultado satisfactorio confirma que el servicio puede leer un archivo con contenido, sin mostrar la cadena.
Compose monta el archivo de origen; ese archivo sigue requiriendo protección en el equipo donde se ejecuta Compose y debe quedar fuera del repositorio y del contexto de construcción. Un archivo .env usado para rellenar una variable de entorno del servicio no ofrece la misma separación: el valor pasa a la configuración del contenedor. La aplicación debe poder leer la ruta del secreto. Algunas imágenes admiten una variable con sufijo _FILE para indicar esa ruta; en tal caso, la variable contiene «/run/secrets/api_token», no la contraseña.
Swarm: un secreto gestionado para tareas de servicio
Swarm tiene otro ámbito de almacenamiento y distribución. La guía de secretos de Swarm explica que cifra los secretos durante el transporte y en el registro Raft, y los entrega a las tareas de los servicios autorizados. En contenedores Linux, la ruta predeterminada es /run/secrets/nombre; el montaje de la tarea utiliza un sistema de archivos en memoria.
Con un swarm inicializado, crea un secreto de prueba mediante «docker secret create api_token ./api_token.txt» y asígnalo con «docker service create --name app --secret api_token alpine sleep 3600». La tarea recibe «/run/secrets/api_token», aunque alpine no utilizará su contenido por sí sola: la aplicación real tiene que abrir el archivo o aceptar una opción que apunte a él. Compose también puede leer un secreto externo de Swarm, pero declarar un archivo local para «docker compose up» no convierte ese archivo en un secreto almacenado por Swarm.
La distinción importa al gestionar el ciclo de vida. Un secreto de Swarm se concede a un servicio y puede retirarse de sus tareas mediante una actualización del servicio; el archivo local que usa Compose se administra en el equipo de origen. En ambos casos, el nombre del secreto y la ruta de montaje pueden ser visibles sin revelar por ello el contenido, de modo que la aplicación debe tratar el archivo montado como la credencial efectiva.
Comprobar imagen, historial, capas y configuración
Haz estas comprobaciones con «MARCADOR_DE_PRUEBA» u otra cadena ficticia reconocible. Para la imagen de BuildKit, consulta la configuración con «docker image inspect demo-secrets --format '{{json .Config.Env}}'» y el historial con «docker history --no-trunc demo-secrets». Busca la cadena en ambas salidas: la ausencia en Config.Env no descarta que una instrucción anterior la haya dejado en el historial.
Para revisar el contenido exportado, ejecuta «docker save -o imagen.tar demo-secrets», extrae el archivo en un directorio de auditoría y busca allí la cadena de prueba, incluidos los archivos de capas y los metadatos. Una coincidencia revela dónde se incorporó el valor; una búsqueda de texto sin coincidencias no prueba por sí sola que ninguna herramienta lo haya guardado transformado o comprimido. Revisa también los registros y artefactos generados durante la compilación.
En Compose, obtén el ID con «docker compose ps -q app» y consulta «docker inspect ID_DEL_CONTENEDOR --format '{{json .Config.Env}}'». El valor secreto no debería figurar en esa configuración; la prueba con «test -s /run/secrets/api_token» verifica por separado que el montaje está disponible. Para Swarm, localiza una tarea activa con «docker service ps app» y aplica las mismas dos comprobaciones a su contenedor en el nodo donde se ejecuta. Así distingues el acceso autorizado al archivo de una credencial incrustada en la imagen o en las variables del contenedor.
Lee también:
Artículos relacionados


Están buscando secretos en servidores Vite: parchear puede no ser suficiente

uv o pip: instalar ocho veces más rápido exige cambiar cómo bloqueas dependencias

Tu agente de código hereda tus permisos: cómo recortarlos antes de ejecutar

Un llms.txt puede instalar el paquete equivocado: 120 archivos lo permitían

Cómo aislar agentes de IA sin falsear una prueba de ciberseguridad
Suscríbete a nuestro boletín
Reciba las últimas noticias sobre Web3, IA y criptomonedas directamente en su bandeja de entrada.