빌드 인자로 넘긴 토큰이 이미지 히스토리에 그대로 남아 있던 이유
문제 발생
사설 레지스트리에서 패키지를 받으려고 토큰을 빌드 인자로 넘겼습니다.
ARG NPM_TOKEN
RUN echo "//registry.example.com/:_authToken=${NPM_TOKEN}" > .npmrc && pnpm install이미지를 레지스트리에 올린 뒤 확인해보니 토큰이 그대로 보였습니다.
$ docker history --no-trunc my-image | grep NPM_TOKEN.npmrc를 지우는 RUN rm .npmrc를 뒤에 넣었는데도 앞 레이어에 남아 있었습니다.
원인 분석
이미지는 레이어의 누적이고, 지운다고 사라지지 않습니다. 앞 레이어에 쓰인 파일은 뒤 레이어에서 삭제해도 이미지 안에 그대로 있습니다. 빌드 인자도 마찬가지로 메타데이터에 남습니다.
Docker 문서가 이유를 한 문장으로 정리합니다 — 빌드 인자와 환경변수는 최종 이미지에 지속되기 때문에 빌드에 비밀을 전달하는 데 적절하지 않습니다.
대신 제공되는 것이 secret 마운트입니다 — RUN --mount=type=secret은 빌드 클라이언트로부터 비밀을 받아 그 빌드 명령이 실행되는 동안에만 빌드 컨테이너 안에서 일시적으로 사용할 수 있게 하고, 그래서 비밀이 최종 이미지에 지속되지 않습니다.
해결 방안
- secret 마운트로 바꿉니다.
RUN --mount=type=secret,id=npm_token \
NPM_TOKEN="$(cat /run/secrets/npm_token)" pnpm installdocker build --secret id=npm_token,env=NPM_TOKEN .-
ARG는 비밀이 아닌 것에만 씁니다. 빌드 시점 버전, 커밋 SHA, 빌드 대상 플랫폼 같은 값입니다.
-
런타임 비밀은 이미지에 넣지 않습니다. 애플리케이션이 실행 중에 쓰는 값은
docker run -e나 compose의env_file, 오케스트레이터의 시크릿으로 주입합니다 — 이미지는 여러 환경에서 공유되는 산출물입니다. -
이미 유출된 토큰은 회수합니다. 이미지를 다시 만드는 것으로는 부족합니다. 그 이미지를 pull한 사람이 있다면 값은 이미 나갔습니다 — 폐기하고 새로 발급하는 것이 유일한 대응입니다.
-
.dockerignore로.npmrc·.env를 애초에 컨텍스트에서 뺍니다. 실수로COPY . .에 딸려 들어가는 경로를 막습니다. -
CI에서 확인합니다. 빌드된 이미지에
docker history나 파일 스캔을 돌려 비밀 패턴이 없는지 점검하면, 이 실수가 배포 전에 걸립니다.
댓글0
댓글을 남기려면 로그인이 필요해요. 로그인
아직 댓글이 없어요. 첫 의견을 편하게 남겨 보세요.