본문으로 건너뛰기
개발 머꼬
개발 노트Docker
hohyeon.dev18

빌드 인자로 넘긴 토큰이 이미지 히스토리에 그대로 남아 있던 이유

  • #Docker
  • #Engineering Note
  • #보안

문제 발생

사설 레지스트리에서 패키지를 받으려고 토큰을 빌드 인자로 넘겼습니다.

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은 빌드 클라이언트로부터 비밀을 받아 그 빌드 명령이 실행되는 동안에만 빌드 컨테이너 안에서 일시적으로 사용할 수 있게 하고, 그래서 비밀이 최종 이미지에 지속되지 않습니다.

해결 방안

  1. secret 마운트로 바꿉니다.
RUN --mount=type=secret,id=npm_token \
    NPM_TOKEN="$(cat /run/secrets/npm_token)" pnpm install
docker build --secret id=npm_token,env=NPM_TOKEN .
  1. ARG는 비밀이 아닌 것에만 씁니다. 빌드 시점 버전, 커밋 SHA, 빌드 대상 플랫폼 같은 값입니다.

  2. 런타임 비밀은 이미지에 넣지 않습니다. 애플리케이션이 실행 중에 쓰는 값은 docker run -e나 compose의 env_file, 오케스트레이터의 시크릿으로 주입합니다 — 이미지는 여러 환경에서 공유되는 산출물입니다.

  3. 이미 유출된 토큰은 회수합니다. 이미지를 다시 만드는 것으로는 부족합니다. 그 이미지를 pull한 사람이 있다면 값은 이미 나갔습니다 — 폐기하고 새로 발급하는 것이 유일한 대응입니다.

  4. .dockerignore.npmrc·.env를 애초에 컨텍스트에서 뺍니다. 실수로 COPY . .에 딸려 들어가는 경로를 막습니다.

  5. CI에서 확인합니다. 빌드된 이미지에 docker history나 파일 스캔을 돌려 비밀 패턴이 없는지 점검하면, 이 실수가 배포 전에 걸립니다.

공식 문서

마지막 수정

좋아요북마크

댓글0

아직 댓글이 없어요. 첫 의견을 편하게 남겨 보세요.