Los secretos de build son credenciales que una construcción necesita para acceder a recursos privados, pero que no deben formar parte de la imagen, sus capas, metadata, logs ni cache exportada.
temporalmente
Texto
secreto autorizado
→ mount temporal durante un RUN
→ acceso a recurso privado
→ mount desaparece
→ artefacto final sin credencial
BuildKit ofrece secret mounts y SSH mounts para separar la credencial del resultado. Esta separación reduce exposición, pero no garantiza seguridad por sí sola: el comando que consume el secreto todavía puede copiarlo, imprimirlo o incorporarlo al artefacto.
ARG NPM_TOKEN
RUN npm config set //registry.npmjs.org/:_authToken "$NPM_TOKEN" \
&& npm ci
Aunque ARG no quede disponible como variable normal de runtime, puede aparecer en historial, logs, metadata de build o comandos. También puede afectar caches exportadas.
Más grave:
docker
COPY .npmrc /root/.npmrc
RUN npm ci
RUN rm /root/.npmrc
Borrar el archivo en una capa posterior no lo elimina de la capa anterior. El token puede recuperarse inspeccionando blobs.
ENV es todavía menos apropiado:
docker
ENV NPM_TOKEN=secret
Persiste como metadata de la imagen y puede verse con docker image inspect.
# syntax=docker/dockerfile:1
FROM node:22-slim AS build
WORKDIR /app
COPY package.json package-lock.json ./
RUN --mount=type=secret,id=npmrc,target=/root/.npmrc,required=true \
npm ci
Según el flujo y tooling, el secret puede suministrarse desde una variable del entorno del cliente. La idea sigue siendo la misma: el valor se entrega mediante el canal de secrets del builder, no como build arg ordinario.
Hace que el build falle claramente si el secreto no fue suministrado. Sin esta opción, el comando puede fallar después con un error ambiguo de autenticación.
# syntax=docker/dockerfile:1
FROM alpine:3.22 AS source
RUN apk add --no-cache git openssh-client
RUN --mount=type=ssh \
git clone git@github.com:organization/private-repository.git /src
Build:
Bash
docker build --ssh default -t private-build .
El builder utiliza el agente SSH disponible sin copiar la private key a la imagen.
Los cache mounts no son secretos, pero suelen aparecer junto a ellos:
docker
RUN --mount=type=cache,target=/root/.npm,sharing=locked \
--mount=type=secret,id=npmrc,target=/root/.npmrc \
npm ci
Aquí existen dos fronteras distintas:
el secret autoriza la descarga;
el cache conserva paquetes descargados para acelerar futuros builds.
Debes comprobar que el package manager no escriba credenciales dentro del cache. Una cache exportada a un registry o backend remoto puede tener un alcance mucho mayor que el builder local.
Cambiar el valor de un secret mount no siempre invalida automáticamente una operación cacheada. El secreto no forma parte ordinaria de la clave porque incluirlo filtraría información y reduciría utilidad.
Si una credencial nueva cambia el resultado esperado, utiliza un input no sensible para controlar invalidación:
docker
ARG PRIVATE_REGISTRY_GENERATION=1
RUN --mount=type=secret,id=npmrc,target=/root/.npmrc \
echo "$PRIVATE_REGISTRY_GENERATION" >/dev/null \
&& npm ci
No pongas el token como valor del argumento. Usa una versión o generación pública.
Un runtime secret no debería incorporarse durante build. La misma imagen debe poder ejecutarse en distintos ambientes y recibir el secreto en runtime mediante secret manager, file mount u otro canal controlado.
Utiliza herramientas de análisis de imágenes y escaneo de secretos capaces de revisar blobs individuales. Ninguna herramienta garantiza encontrar todos los formatos; combina prevención y revisión.
Una etapa anterior no llega al runtime automáticamente, pero un output puede copiar el secreto y la cache/build system puede conservar contenido. Multi-stage no reemplaza secret mounts.
Optimización de imágenes, donde se evalúa tamaño, compatibilidad, superficie de ataque y operabilidad sin reducir el problema a elegir la base más pequeña.