공개 컨테이너 이미지 한 장이 저장소 관리자 권한으로 이어질 때 운영팀이 점검할 배포 비밀 (Baseten)

공개 Harbor 이미지의 빌드 이력에서 유효한 GitHub 토큰이 발견된 Baseten 사례를 바탕으로, 운영팀이 점검할 레지스트리·이미지·권한 관리 순서를 정리합니다.

공개 레지스트리에서 이미지 설정과 빌드 이력의 토큰 노출로 이어지는 네 단계
원문 기반 한글 재구성. 출처: Strix 공개 보고

공개 컨테이너 이미지가 곧바로 문제라는 뜻은 아니다. 익명으로 받는 대상이 이미지 파일에만 그친다고 보면 안 된다. 이미지 설정과 빌드 이력까지 내려받을 수 있으면, 오래전에 넣은 배포용 비밀이 저장소 관리자 권한으로 이어질 수 있다.

보안 기업 Strix는 2026년 9월 1일 공개한 보고에서 Baseten의 Harbor 레지스트리 한 프로젝트가 익명 pull을 허용했고, 그 안의 baseten/baseten-app 이미지 설정의 빌드 이력에서 GitHub personal access token(PAT)을 찾았다고 밝혔다. Strix의 읽기 전용 확인에 따르면 이 토큰은 basetenbot 계정으로 유효했고, 주요 제품 저장소와 클러스터를 다루는 GitOps 저장소 등에 관리자·push 권한이 있었다. Baseten은 제보 뒤 레지스트리 프로젝트를 잠그고 토큰을 교체했다고 Strix는 전했다.

공개된 것은 레이어 파일만이 아니었다

컨테이너 이미지는 파일 시스템 레이어만으로 구성되지 않는다. manifest가 가리키는 image config에는 환경 설정과 함께 빌드 단계의 이력이 담길 수 있다. Strix는 공개 Harbor 프로젝트에서 저장소 목록을 열람하고 익명 pull 토큰으로 manifest와 blob을 받은 뒤, 이 config의 history[].created_by를 검사했다. 토큰은 애플리케이션 파일이 아니라 빌드 명령이 기록된 곳에 남아 있었다.

공개 레지스트리의 익명 pull 권한이 이미지 설정과 빌드 이력의 토큰 노출로 이어지는 네 단계
원문 기반 한글 재구성. 공개 이미지의 설정과 빌드 이력까지 읽히는 경로. 출처: Strix 공개 보고

이 차이 때문에 마지막 레이어에서 비밀 파일을 삭제해도 해결되지 않는다. 이전 단계의 레이어 또는 config history에 값이 남아 있으면, 이미지를 받은 사람은 과거 상태를 읽을 수 있다. 이번 사례에서 해당 build step은 2023년 3월 3일에 실행됐고, Strix가 토큰을 확인한 시점은 2026년 7월이었다.

ARG에 넣은 토큰이 왜 빌드 이력에 남는가

비공개 의존성을 내려받기 위해 ARG GITHUB_TOKEN을 선언하고 RUN 명령에서 URL이나 환경 변수로 확장하는 방식은 편해 보인다. Docker 공식 문서는 build argument와 environment variable에 비밀을 넣지 말라고 안내한다. 이런 값은 이미지의 메타데이터나 이력에 남을 수 있기 때문이다.

Strix가 제시한 패턴도 여기에 해당한다. 빌드 인수로 받은 토큰을 Git 인증 URL에 넣어 private dependency를 받는 과정에서, 실제 토큰 값이 RUN 명령의 이력에 기록됐다는 것이다. 여기에 git config --global로 인증 URL을 저장하면, 빌드 이력 문제를 피해도 Git 설정 파일이라는 두 번째 흔적이 생긴다.

토큰을 ARG 또는 ENV로 전달하는 방식과 BuildKit secret mount를 작업 중에만 쓰는 방식의 비교
원문 기반 한글 재구성. Docker는 비밀을 ARG·ENV에 넣는 대신 BuildKit secret mount 사용을 안내한다. 출처: Docker 공식 문서

대체 경로는 BuildKit secret mount다. 빌드 명령이 실행되는 동안에만 비밀을 mount하고, 최종 레이어와 image config에 쓰지 않도록 한다. 이 방식도 인증 정보를 전역 설정이나 복사 대상 디렉터리에 남기면 효과가 줄어든다. 비밀 전달 방식과 빌드 산출물 검사를 함께 바꿔야 한다.

토큰을 바꾸는 일과 이미지를 고치는 일은 순서가 다르다

공개 이미지에서 비밀이 발견되면 Dockerfile을 수정해 새 태그를 배포하는 작업부터 시작하기 쉽다. 그러나 과거 이미지를 내려받은 사람이 있을 수 있고, 레지스트리 캐시나 이전 태그가 남아 있을 수 있다. 먼저 해당 토큰을 폐기하고, 그 계정이 접근할 수 있는 조직·저장소·배포 시스템의 권한을 다시 확인해야 한다. 새 이미지는 이미 유출된 토큰을 무효화하지 못한다.

그 뒤에는 공개 범위를 실제 익명 요청으로 확인한다. 프로젝트가 public인지라는 설정값만 보지 말고 repository 목록, manifest, blob을 인증 없이 조회·pull할 수 있는지 점검한다. 현재 태그만 검사하면 오래된 이미지가 빠진다. 태그와 digest를 포괄해 레이어 파일, image config, build history를 모두 secret scanner 대상으로 넣는 편이 안전하다.

공개 레지스트리 범위 확인, 과거 태그 검사, 비밀 폐기와 권한 축소, 배포 관문 적용으로 이어지는 운영팀 점검 순서
원문 기반 한글 재구성. 공개 이미지에서 자격증명이 발견됐을 때의 점검 순서. 출처: Strix 공개 보고

배포 관문에는 config 검사도 넣어야 한다

이미지 스캔이 파일 시스템 레이어만 읽는다면 이번처럼 build history에 남은 값은 놓친다. CI에서는 secret mount 사용 여부를 검사하고, 빌드가 끝난 뒤에는 image config와 history까지 검사 범위에 넣는다. 레지스트리에서는 public 프로젝트 추가와 접근 정책 변경을 감시한다. 배포 비밀의 수명도 따로 관리해야 한다. 장기 PAT보다 업무·저장소·기간을 좁힌 토큰을 쓰면, 한 이미지가 노출됐을 때 퍼지는 범위를 줄일 수 있다.

이 사례가 운영팀에 남기는 질문은 간단하다. “우리 공개 이미지는 무엇을 실행하는가”만 보지 말고 “누구나 내려받은 config와 과거 history에서 어떤 자격증명을 재구성할 수 있는가”까지 확인해야 한다. 레지스트리 공개 설정, build secret 전달, Git 인증 설정, 토큰 권한은 배포 파이프라인에서 한 묶음으로 점검할 대상이다.

출처: We wanted to use Baseten for inference. We ended up with admin access to Baseten GitHub repos, Strix, 2026-09-01