파이프라인 보안
파이프라인 보안 (Pipeline security)
이 문서는 Free, Premium, Ultimate 티어에서 사용할 수 있고, GitLab.com, GitLab Self-Managed, GitLab Dedicated 모두에서 활용할 수 있어요.
출처: 문서
본문
시크릿 관리 (Secrets Management)
시크릿 관리는 개발자가 민감한 데이터를 엄격한 접근 제어가 적용된 안전한 환경에 안전하게 저장하는 데 사용하는 시스템이에요. **시크릿(secret)**은 기밀로 유지해야 하는 민감한 자격 증명이에요. 시크릿의 예로는:
- 비밀번호
- SSH 키
- 접근 토큰
- 노출되면 조직에 해가 될 수 있는 기타 모든 유형의 자격 증명
시크릿이 노출되면 어떤 일이 생기는지, 왜 안전한 보관이 중요한지가 이 주제의 핵심이에요.
시크릿 저장
시크릿 관리 제공자
가장 민감하고 가장 엄격한 정책이 적용되는 시크릿은 시크릿 매니저에 저장해야 해요. 시크릿 매니저 솔루션을 사용하면 시크릿이 GitLab 인스턴스 외부에 저장돼요. 여러 제공자가 이 서비스를 제공하며, HashiCorp Vault, Azure Key Vault, Google Cloud Secret Manager 등이 있어요.
특정 외부 시크릿 관리 제공자에 대한 GitLab 네이티브 통합을 사용해 CI/CD 파이프라인에서 필요할 때 그 시크릿을 가져올 수 있어요.
CI/CD 변수
CI/CD 변수는 CI/CD 파이프라인에서 데이터를 저장하고 재사용하는 편리한 방법이지만, 시크릿 관리 제공자보다는 덜 안전해요. 변수 값은:
- GitLab 프로젝트, 그룹 또는 인스턴스 설정에 저장돼요. 설정에 접근할 수 있는 사용자는 숨겨지지 않은 변수 값에 접근할 수 있어요.
- 재정의될 수 있어서 어떤 값이 사용되었는지 파악하기 어려워요.
- 우발적인 파이프라인 잘못된 구성으로 노출될 수 있어요.
변수에 저장하기 적합한 정보는 노출되어도 악용 위험이 없는(비민감한) 데이터여야 해요.
민감한 데이터는 시크릿 관리 솔루션에 저장해야 해요. 시크릿 관리 솔루션이 없어 민감한 데이터를 CI/CD 변수에 저장해야 한다면 항상:
CI/CD 파이프라인에 매개변수 전달하기
CI/CD 파이프라인에 매개변수를 전달할 때는 파이프라인 변수 대신 CI/CD inputs을 사용해요.
Inputs가 제공하는 것:
- 파이프라인 생성 시 타입 안전 검증.
- 명시적인 매개변수 계약.
- 보안을 강화하는 범위 제한 가용성.
파이프라인 변수는 타입 검증이 없고, 사전 정의 변수를 재정의해 예기치 않은 동작을 일으킬 수 있으며, 민감한 시크릿과 같은 권한 범위를 공유하므로, inputs를 구현할 때 파이프라인 변수 비활성화를 고려해 보안 취약점을 방지해요.
파이프라인 무결성 (Pipeline Integrity)
파이프라인 무결성을 보장하는 핵심 보안 원칙은 다음과 같아요.
- 공급망 보안(Supply Chain Security): 자산은 신뢰할 수 있는 소스에서 얻고 무결성을 검증해야 해요.
- 재현성(Reproducibility): 파이프라인은 같은 입력을 사용하면 일관된 결과를 내야 해요.
- 감사 가능성(Auditability): 모든 파이프라인 의존성은 추적 가능하고 출처(provenance)를 검증할 수 있어야 해요.
- 버전 제어(Version Control): 파이프라인 의존성의 변경은 추적되고 제어되어야 해요.
Docker 이미지
클라이언트 측 무결성 검증을 보장하려면 Docker 이미지에 항상 SHA 다이제스트를 사용해요. 예를 들어:
-
Node:
- 사용:
image: node@sha256:0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef - 대신에:
image: node:latest
- 사용:
-
Python:
- 사용:
image: python@sha256:9876543210abcdef9876543210abcdef9876543210abcdef9876543210abcdef - 대신에:
image: python:3.9
- 사용:
특정 태그가 있는 이미지의 SHA 다이제스트는 다음으로 찾을 수 있어요.
docker pull node:18.17.1
docker images --digests node:18.17.1
이미지 무결성을 보호하는 컨테이너 레지스트리에서 가져오는 것을 선호해요.
- 보호된 컨테이너 저장소를 사용해 누가 컨테이너 저장소의 컨테이너 이미지를 변경할 수 있는지 제한해요.
- 보호된 태그를 사용해 누가 컨테이너 태그를 푸시하고 삭제할 수 있는지 제어해요.
가능하면 컨테이너 참조에 변수를 사용하지 마세요. 변수는 악성 이미지를 가리키도록 수정될 수 있기 때문이에요. 예를 들어:
-
선호:
image: my-registry.example.com/node:18.17.1
-
대신에:
image: ${CUSTOM_REGISTRY}/node:latestimage: node:${VERSION}
패키지 의존성
잡에서 패키지 의존성을 고정(lock down)해야 해요. 잠금 파일에 정의된 정확한 버전을 사용해요.
-
npm:
- 사용:
npm ci - 대신에:
npm install
- 사용:
-
yarn:
- 사용:
yarn install --frozen-lockfile - 대신에:
yarn install
- 사용:
-
Python:
- 사용:
pip install -r requirements.txt --require-hashespip install -r requirements.lock
- 대신에:
pip install -r requirements.txt
- 사용:
-
Go:
go.sum의 정확한 버전 사용:go mod verifygo mod download
- 대신에:
go get ./...
예를 들어, CI/CD 잡에서:
javascript-job:
script:
- npm ci
셸 명령과 스크립트
잡에서 도구를 설치할 때는 항상 정확한 버전을 지정하고 검증해요. 예를 들어, Terraform 잡에서:
terraform_job:
script:
# Download specific version
- |
wget https://releases.hashicorp.com/terraform/1.5.7/terraform_1.5.7_linux_amd64.zip
# IMPORTANT: Always verify checksums
echo "c0ed7bc32ee52ae255af9982c8c88a7a4c610485cf1d55feeb037eab75fa082c terraform_1.5.7_linux_amd64.zip" | sha256sum -c
unzip terraform_1.5.7_linux_amd64.zip
mv terraform /usr/local/bin/
# Use the installed version
- terraform init
- terraform plan
버전 관리 도구
가능하면 버전 관리자를 사용해요.
node_build:
script:
# Use nvm to install and use a specific Node version
- |
nvm install 16.15.1
nvm use 16.15.1
- node --version # Verify version
- npm ci
- npm run build
포함된 구성
파이프라인에 구성이나 CI/CD 컴포넌트를 추가하기 위해 [include 키워드](/ci/yaml/#include)를 사용할 때는 가능하면 특정 ref를 사용해요. 예를 들어:
include:
- project: 'my-group/my-project'
ref: 8b0c8b318857c8211c15c6643b0894345a238c4e # Pin to a specific commit
file: '/templates/build.yml'
- project: 'my-group/security'
ref: v2.1.0 # Pin to a protected tag
file: '/templates/scan.yml'
- component: 'my-group/security-scans' # Pin to a specific version
version: '1.2.3'
버전 없는 include는 피해요.
include:
- project: 'my-group/my-project' # Unsafe
file: '/templates/build.yml'
- component: 'my-group/security-scans' # Unsafe
- remote: 'https://example.com/security-scan.yml' # Unsafe
원격 파일을 include하는 대신, 파일을 다운로드해 저장소에 저장해요. 그런 다음 로컬 복사본을 include하면 돼요.
include:
- local: '/ci/security-scan.yml' # Verified and stored in the repository
관련 주제
- CIS Docker Benchmarks
- Google Cloud: 안전한 배포 파이프라인 설계
더 알아보기
파이프라인 보안에서 가장 중요한 건 시크릿을 어디에 저장하느냐예요. GitLab 네이티브 외부 시크릿 제공자 통합은 시크릿 문서에서, OIDC 기반 인증은 ID 토큰 인증 문서에서 확인할 수 있어요.