비공개 패키지를 받으려고 토큰을 ARG나 ENV로 넘기거나, .npmrc를 COPY했다가 빌드 후 지우면 안전할 것 같지만 아닙니다. 도커 이미지는 레이어 단위로 쌓이고, 한 번 들어간 파일이나 빌드 인자는 뒤 레이어에서 지워도 앞 레이어에 그대로 남습니다. 누구나 docker history나 레이어 추출로 꺼내볼 수 있습니다.
왜 새는지 직접 확인
Docker
docker build --build-arg NPM_TOKEN=secret123 -t app .
docker history --no-trunc app | grep NPM_TOKEN
--build-arg로 넘긴 값은 history에 평문으로 보입니다. RUN rm .npmrc로 지워도 그 파일을 만든 앞 레이어가 살아 있어 docker save 후 tar를 풀면 그대로 나옵니다.
해결 — BuildKit --secret
BuildKit의 --secret은 시크릿을 특정 RUN 단계에만 메모리로 마운트하고, 최종 이미지나 레이어에 전혀 남기지 않습니다.
Dockerfile
# syntax=docker/dockerfile:1
FROM node:20-slim
WORKDIR /app
COPY package*.json ./
RUN --mount=type=secret,id=npmtoken \
NPM_TOKEN=$(cat /run/secrets/npmtoken) npm ci
COPY . .
빌드 시 파일이나 환경변수에서 시크릿을 주입합니다.
Docker
# 파일에서
docker build --secret id=npmtoken,src=$HOME/.npm_token -t app .
# 환경변수에서
NPM_TOKEN=secret123 docker build --secret id=npmtoken,env=NPM_TOKEN -t app .
/run/secrets/npmtoken은 해당 RUN이 끝나면 사라지며, history에도 레이어에도 흔적이 없습니다.
방식별 비교
| 방식 | 레이어에 남나 | 평가 |
|---|---|---|
ENV TOKEN=... | 남음 (영구) | 절대 금지 |
ARG TOKEN + --build-arg | history에 평문 | 금지 |
COPY .npmrc 후 RUN rm | 앞 레이어에 남음 | 금지 |
RUN --mount=type=secret | 안 남음 | 권장 |
체크리스트
로컬 터미널
# BuildKit 활성화 (Docker 23+ 기본, 구버전은 명시)
export DOCKER_BUILDKIT=1
# 빌드 후 누출 점검 — 아무것도 안 나와야 정상
docker history --no-trunc app | grep -i -E 'token|secret|password'
docker save app -o app.tar # tar 풀어서 .npmrc 등 잔존 확인
Dockerfile 첫 줄의 # syntax=docker/dockerfile:1을 빠뜨리면 --mount 문법이 인식되지 않으니 반드시 넣습니다.
레이어에 실제로 시크릿이 남는지 docker history로 확인하고 --secret으로 막아 보는 실습은 도커 트랙에서 회원가입 없이 무료로 할 수 있습니다.