CI/CD
GitHub Actions 캐시: 작동 방식과 숨기는 것
GitHub Actions 캐시는 워크플로 실행 사이에 파일을 저장하므로, 작업이 이미 했던 다운로드를 건너뛸 수 있습니다. 빌드가 빨라집니다. 동시에 패키지가 가져오기 없이 빌드에 들어온다는 뜻이기도 하며, 빌드에 대한 대부분의 기록은 이를 보지 못합니다.
업데이트
캐시의 작동 방식
actions/cache 액션은 작업이 끝날 때 일련의 경로를 키 아래에 저장하고, 이후 작업이 시작될 때 이를
복원합니다. 일반적인 npm 설정에서는 패키지 관리자의 다운로드 캐시를 캐시하고, 잠금 파일을 키로 사용합니다.
- uses: actions/cache@v4
with:
path: ~/.npm
key: npm-${{ runner.os }}-${{ hashFiles('package-lock.json') }}
restore-keys: |
npm-${{ runner.os }}-
- run: npm ci
복원할 때 액션은 먼저 key와 정확히 일치하는 항목을 찾고, 다음으로 부분 일치를, 그다음 각
restore-keys 접두사를 차례로 찾습니다. 여러 캐시가 접두사와 일치하면 가장 최근에 생성된 캐시를
복원합니다. 이 대체 동작 덕분에 잠금 파일이 바뀐 뒤에도 빌드가 빠르게 유지되지만, 작업이 이전 잠금 파일용으로 만들어진
캐시에서 시작할 수 있다는 뜻이기도 합니다.
이 액션이 아예 필요 없는 경우도 많습니다. setup-node, setup-python, setup-java,
setup-go, setup-ruby, setup-dotnet에는 캐싱이 내장되어 있습니다. Node.js의
경우 setup-node는 npm, Yarn, pnpm의 전역 패키지 데이터를 캐시하며 node_modules는 캐시하지
않습니다. 또한 package.json이 npm을 packageManager로 선언하면 npm 캐싱을 자동으로 켭니다.
범위와 한도
| 규칙 | GitHub의 동작 |
|---|---|
| 실행이 복원할 수 있는 캐시 | 자체 브랜치와 기본 브랜치, 풀 리퀘스트는 베이스 브랜치도 포함 |
| 풀 리퀘스트가 쓸 수 있는 캐시 | 기본 브랜치가 아니라 해당 병합 ref 범위의 캐시만 |
| 스토리지 | 기본적으로 리포지토리당 10 GB, 조직 및 엔터프라이즈 소유자가 늘릴 수 있음 |
| 제거 | 7일 넘게 접근하지 않은 항목, 그리고 스토리지가 가득 차면 가장 오래전에 사용된 항목 |
| 시크릿 | 캐시하지 마세요. 풀 리퀘스트 접근 권한이 있는 누구나 캐시 내용을 읽을 수 있습니다 |
캐시가 SBOM에서 숨기는 것
캐시는 패키지 레지스트리를 건너뛰는 지름길입니다. 그것이 캐시의 목적이며, 동시에 빌드가 무엇을 사용했는지에 대한 모든 기록을 혼란스럽게 만드는 이유이기도 합니다.
- 다운로드가 없으면 흔적도 없습니다. 캐시에서 복원된 패키지는 레지스트리에 요청하지 않고 설치됩니다. 빌드의 네트워크 트래픽으로 만든 기록은 캐시를 추적하지 않는 한 이 패키지를 놓칩니다.
-
새 키 아래의 오래된 내용.
restore-keys부분 일치는 이전 잠금 파일용으로 만들어진 캐시를 복원합니다. 이후 설치 단계가 바뀐 것을 추가하거나 교체하지만, 작업의 출발점은 현재 잠금 파일이 설명하는 것과 다릅니다. - 종속성 그 이상. 캐시에는 빌드 도구, 컴파일러, 생성된 파일도 들어 있으며, 이는 어떤 애플리케이션 매니페스트에도 나열되지 않습니다.
- 스캔으로는 알 수 없습니다. 잠금 파일 기반 SBOM은 작업이 캐시에서 정확히 그것을 설치했는지와 관계없이 잠금 파일에 적힌 내용을 설명합니다.
이것이 더 큰 문제와 어떻게 연결되는지는 잠금 파일이 출시된 것의 기록이 아닌 이유와 전이적 종속성을 참고하세요.
캐시 포이즈닝
캐시는 리포지토리의 워크플로 간에 공유되므로, 워크플로 사이를 잇는 통로가 됩니다. 2024년 5월, 보안 연구자 Adnan
Khan은 취약한 pull_request_target 워크플로나 손상된 종속성을 통해 기본 브랜치 컨텍스트에서 코드를 실행할
수 있는 사람은 누구나 캐시를 복원하는 워크플로의 파일을 덮어쓸 수 있음을 보여 주었습니다. 실행 중인 작업의 캐시 토큰을
가진 공격자는 리포지토리 캐시를 가득 채워 GitHub가 정상 항목을 제거하게 만든 뒤, 같은 키로 오염된 항목을 쓸 수
있습니다. 이를 복원하는 릴리스 워크플로는 자신의 시크릿을 가진 채 공격자의 파일을 실행하게 됩니다.
위험을 줄이려면 다음과 같이 하세요.
- 릴리스는 깨끗한 상태에서 빌드하거나, 릴리스 워크플로만 쓰는 캐시에서 빌드하세요.
- 신뢰할 수 없는 풀 리퀘스트 코드를 기본 브랜치 컨텍스트에서 실행하지 마세요.
- 시크릿과 토큰을 캐시 경로에 두지 마세요.
- 캐시가 복원한 것을 다운로드와 똑같이 면밀히 검토해야 하는 빌드 입력으로 다루세요.
CRACI가 캐시를 다루는 방식
CRACI는 GitHub Actions 러너이므로 actions/cache와 setup-* 캐시가 지금과 똑같이 작동하며,
컨테이너 레이어 캐싱도 함께 제공됩니다. 캐싱은 더 빠른 하드웨어, 더 짧은 대기 시간과 함께 CRACI가 GitHub 호스팅
러너보다 빌드를 약 2배 빠르게 실행하는 이유 중 하나이기도 합니다.
달라지는 것은 기록입니다. CRACI의 패키지를 인식하는 프록시는 작업이 가져오는 패키지를 기록하며, 증거는 캐시와 함께 전달됩니다. 작업이 캐시를 복원하면 CRACI는 그 캐시에 대해 알고 있는 종속성을 해당 작업의 SBOM으로 이어 갑니다.
- 캐시별 완전성. 모든 작업과 마찬가지로 모든 캐시에도 완전성 상태가 있습니다. 불완전한 캐시를 복원한 작업은 불완전한 SBOM을 받고, 그 작업이 만든 캐시도 그 상태를 이어받으므로 빈틈이 소리 없이 완전한 기록으로 바뀌는 일은 없습니다.
- 아카이브로 추측하지 않습니다. CRACI는 캐시 아카이브 안의 패키지 매니페스트를 읽지 않습니다. 캐시를 만든 작업을 기록했기 때문에 캐시에 무엇이 들어 있는지 압니다.
-
생성 작업당 캐시 하나를 유지하고, 생성 작업의 이그레스 정책을
default: deny로 설정하면 그 증거를 신뢰할 수 있게 유지할 수 있습니다. 자세한 내용은 캐시 및 SBOM 완전성 가이드에 있습니다.
기록 방식은 빌드 시점 SBOM을 참고하시거나, 데모를 예약해 캐시를 사용하는 워크플로 하나를 CRACI에서 실행해 보세요.
GitHub Actions 캐시: 자주 묻는 질문
GitHub는 Actions 캐시를 얼마나 오래 보관하나요?
GitHub는 7일 넘게 접근하지 않은 캐시 항목을 제거합니다. 리포지토리가 스토리지 한도(기본 10 GB)를 넘으면 마지막 접근 순서에 따라 가장 오래된 캐시부터 제거합니다.
setup-node는 node_modules를 캐시하나요?
아니요. actions/setup-node의 cache 입력은 node_modules가 아니라 npm, Yarn 또는 pnpm 패키지 관리자의 전역 패키지 데이터를 캐시합니다. 설치 단계는 여전히 실행되지만, 패키지를 내려받는 대신 복원된 캐시에서 읽습니다.
풀 리퀘스트가 main 브랜치의 캐시를 읽을 수 있나요?
네. 워크플로 실행은 자체 브랜치와 기본 브랜치의 캐시를 복원할 수 있으며, 풀 리퀘스트는 베이스 브랜치의 캐시도 복원할 수 있습니다. 풀 리퀘스트에서 만든 캐시는 해당 병합 ref 범위로 한정되므로 기본 브랜치 범위에 쓸 수 없습니다.
릴리스 워크플로에서 Actions 캐시를 사용해도 안전한가요?
장단점이 있습니다. 2024년에 발표된 연구에 따르면, 기본 브랜치 워크플로에서 코드를 실행할 수 있는 사람은 누구나 릴리스 워크플로를 포함해 다른 워크플로가 복원하는 캐시를 오염시킬 수 있습니다. 릴리스를 게시하거나 배포 시크릿을 보유한 워크플로는 깨끗한 상태나 자신만 쓰는 캐시에서 빌드하는 편이 더 안전합니다.
복원된 캐시가 SBOM을 바꾸나요?
그럴 수 있습니다. 캐시에서 복원된 패키지는 다운로드 없이 설치되므로, 네트워크 트래픽으로 만든 기록은 캐시를 추적하지 않는 한 이를 놓칩니다. 그리고 잠금 파일 스캔으로는 캐시에 정확히 그 버전들이 들어 있었는지 알 수 없습니다.
캐시는 그대로, SBOM은 완전하게
워크플로를 CRACI에서 실행하세요. 캐시는 계속 빌드 속도를 높이고, 캐시가 담고 있는 종속성은 각 작업의 기록된 SBOM에 그대로 남습니다.
데모 예약