Cuando creamos imágenes de Docker para aplicaciones Node.js, especialmente para proyectos que usan TypeScript, es recomendable usar las características de multi-stage build de Docker. Esto asegura que obtenemos una imagen de producción con solo los archivos necesarios para ejecutar la aplicación en la etapa final. 🚀
🛠️ Instalación de Dependencias en la Etapa de Build
Durante la etapa de build, normalmente debemos instalar todas las dependencias del proyecto para que el proceso de compilación sea exitoso. La mayoría de los paquetes están disponibles en Internet, pero es común que las organizaciones utilicen paquetes privados en NPM o GitHub. Estos paquetes proporcionan funcionalidad interna o herramientas necesarias para la aplicación.
⚠️ Riesgo de Exposición de Tokens y Secretos
Una práctica común es proporcionar valores en tiempo de build, como la versión de la aplicación, usando el flag --build-arg <varname>=<value>
en el comando docker build
. Sin embargo, esta técnica no es recomendable para valores sensibles, ya que cualquier persona puede inspeccionar las capas de una imagen de Docker y ver su contenido utilizando herramientas como Dive
.
La documentación oficial de Docker desaconseja el uso de argumentos de build para este caso, ya que estos valores también son visibles con el comando docker history
.
🔐 Usando Docker Secrets para Mayor Seguridad
Afortunadamente, Docker ha introducido la funcionalidad de build secrets, que nos permite evitar estos problemas y garantizar que valores sensibles no estén disponibles en la imagen final.
A continuación se muestra un Dockerfile para una aplicación sencilla en TypeScript:
FROM node:20-alpine as base
# Etapa para construir la aplicación
FROM base as builder
WORKDIR /app
COPY package*json tsconfig.json src ./
# Instalación de todas las dependencias
RUN npm ci
# Construcción de la aplicación
RUN npm run build
# Eliminación de paquetes de desarrollo innecesarios
RUN npm prune --production
# Etapa para la imagen final
FROM base as runner
WORKDIR /app
ENV NODE_ENV="production"
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 hono
# Copiar dependencias de producción y los outputs de la compilación desde la etapa builder
COPY --from=builder --chown=hono:nodejs /app/node_modules /app/node_modules
COPY --from=builder --chown=hono:nodejs /app/dist /app/dist
COPY --from=builder --chown=hono:nodejs /app/package.json /app/package.json
USER hono
EXPOSE 3000
CMD ["node", "/app/dist/index.js"]
Si tenemos un paquete privado en el proyecto, podríamos encontrarnos con un error como este al ejecutar docker build
:
3.577 npm notice
3.578 npm ERR! code E401
3.578 npm ERR! 401 Unauthorized - GET https://npm.pkg.github.com/download/@org-name/tsconfig/3.0.0/afc9686569ec81268d8d51c3d3137d4d715a6094 - no autenticado: El usuario no puede ser autenticado con el token proporcionado.
🔑 Solución: Usando Secrets para Autenticación
Para resolver este problema, podemos actualizar la etapa de build en el Dockerfile para crear un archivo .npmrc
y usar secretos para pasar el token de autenticación del registro de manera segura.
FROM node:20-alpine as base
# Etapa para construir la aplicación
FROM base as builder
# Crear un archivo .npmrc para configurar el registro privado
RUN cat <<EOF > $HOME/.npmrc
@org-name:registry=https://npm.pkg.github.com
//npm.pkg.github.com/:_authToken=\${GITHUB_TOKEN}
EOF
WORKDIR /app
COPY package*json tsconfig.json src ./
# Usar el tipo de montaje secrets para pasar de forma segura el token durante la instalación
RUN --mount=type=secret,id=GITHUB_TOKEN,required=true \
GITHUB_TOKEN=$(cat /run/secrets/GITHUB_TOKEN) \
npm ci
# Construcción de la aplicación
RUN npm run build
# Eliminación de paquetes de desarrollo innecesarios
RUN npm prune --production
🛡️ Comando para Construir la Imagen con Secretos
Para construir la imagen, primero debemos establecer la variable de entorno necesaria para el secreto y pasarla al comando docker build
:
export GITHUB_TOKEN=<personal-access-token>
docker build -t secure-app-image --secret id=GITHUB_TOKEN .
Aspectos Importantes para Tener en Cuenta
- Archivo
.npmrc
específico del proyecto: Este archivo es mencionado en la documentación de NPM, donde la CLI de NPM reemplazará el valor con el contenido de la variable de entornoGITHUB_TOKEN
. - Token disponible para la instalación de dependencias: El token
GITHUB_TOKEN
estará disponible en las variables de entorno durante la construcción de la imagen y la ejecución del comandonpm ci
. Esto se logra usando el tipo de montaje de secretos en el comandoRUN
.
Conclusión 🎯
Con la funcionalidad build secrets de Docker, ahora tenemos una forma segura de pasar tokens y valores sensibles al proceso de construcción de Docker sin que queden almacenados en la imagen. Así, podemos estar seguros de que nuestros secretos no serán expuestos.