feat(sbom): add Helm catalog SBOM+vuln pipeline (CycloneDX)
manifests/helm 카탈로그 전 이미지의 SBOM(CycloneDX)과 취약점 리포트를 생성하는 파이프라인 추가. 컨테이너 내부 trivy/helm 바이너리 직접 호출. - doc/scripts/extract-helm-images.sh: helm template 렌더로 이미지 인벤토리 추출 - doc/scripts/generate-sbom.sh: trivy image --format cyclonedx (이미지 1회 pull) - doc/scripts/scan-sbom.sh: trivy sbom 로 SBOM 기반 오프라인 취약점 스캔 - .github/workflows/sbom.yml: 스케줄(전체)+PR(증분)+수동, 산출물은 아티팩트 - doc/sbom-pipeline.md: 파이프라인 설계·운영 문서(단일) - latest/무태그 digest 고정: mlflow postgresql, vllm-openai(v0.24.0) - .gitignore: 파이프라인 산출물 제외 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,95 @@
|
||||
# Helm 카탈로그 SBOM 생성 파이프라인
|
||||
|
||||
`manifests/helm/` 정적 카탈로그의 **모든 컨테이너 이미지에 대한 SBOM(CycloneDX)과 취약점 리포트**를
|
||||
결정론적으로 생성하는 파이프라인. helm/trivy/python3 가 설치된 리눅스 컨테이너 안에서 실행한다.
|
||||
|
||||
## 왜 필요한가
|
||||
|
||||
GitHub Dependency-Graph SBOM(예: `paasup_dip-catalog_199087.json`)은 pypi 등 소스 매니페스트만 담고
|
||||
**Helm 차트가 참조하는 컨테이너 이미지는 하나도 포함하지 못한다**(Helm 미지원 + 이미지 미스캔). 이 파이프라인이
|
||||
그 공백을 메운다 — `helm template` 렌더로 실제 배포 이미지를 뽑고, 각 이미지의 SBOM 과 취약점을 생성한다.
|
||||
|
||||
## 아키텍처 (SBOM-first)
|
||||
|
||||
이미지를 **한 번만 pull** 해서 SBOM 을 만들고, 취약점은 그 SBOM 에서 오프라인으로 뽑는다.
|
||||
|
||||
```
|
||||
extract-helm-images.sh generate-sbom.sh scan-sbom.sh
|
||||
manifests/helm/** ──helm template──▶ images_final.tsv ──trivy image──▶ sbom/*.cdx.json ──trivy sbom──▶ trivy-summary.md
|
||||
(오프라인·결정론) (이미지 1회 pull, 인증 필요) (CycloneDX) (오프라인·인증 불필요) + trivy-reports/
|
||||
```
|
||||
|
||||
- **레지스트리 pull·인증은 SBOM 생성 단계 1곳으로 집중.** 취약점 스캔은 네트워크·인증 없이 빠르게 반복 가능
|
||||
(CVE DB 갱신 시 재pull 없이 재스캔).
|
||||
- 스캔 대상 이미지는 trivy 가 원격에서 받아 분석 후 폐기하므로 호스트/이미지 스토어에 남지 않는다.
|
||||
|
||||
## 실행 환경 요구사항
|
||||
|
||||
스크립트는 **컨테이너 내부에 설치된 바이너리를 직접 호출**한다. 실행 이미지에 다음이 있어야 한다.
|
||||
|
||||
| 도구 | 사용 스크립트 |
|
||||
|------|--------------|
|
||||
| `helm` (v3) | extract-helm-images.sh |
|
||||
| `trivy` | generate-sbom.sh, scan-sbom.sh |
|
||||
| `python3`, `bash`, `git` | 공통 |
|
||||
|
||||
## 스크립트 (`doc/scripts/`)
|
||||
|
||||
| 스크립트 | 입력 | 출력 |
|
||||
|---------|------|------|
|
||||
| `extract-helm-images.sh` | `manifests/helm/` | `images_final.tsv` (chart⇥version⇥image), `render_status.tsv` |
|
||||
| `generate-sbom.sh` | `images_final.tsv` | `sbom/<img>.cdx.json`, `sbom-index.tsv`, `sbom-gen.log` |
|
||||
| `scan-sbom.sh` | `sbom/` + `sbom-index.tsv` | `trivy-summary.{tsv,md}`, `trivy-reports/`, `trivy-run.log` |
|
||||
|
||||
### 로컬/컨테이너 실행
|
||||
|
||||
```bash
|
||||
# 도구가 설치된 이미지 안에서 (예: <pipeline-image> = helm+trivy+python3 보유 이미지)
|
||||
docker run --rm -v "$PWD:/repo" -w /repo -e TRIVY_CACHE_DIR=/repo/sbom-out/cache <pipeline-image> bash -c '
|
||||
bash doc/scripts/extract-helm-images.sh manifests/helm sbom-out
|
||||
bash doc/scripts/generate-sbom.sh sbom-out/images_final.tsv sbom-out # DOCKER_CONFIG/TRIVY_USERNAME 로 인증
|
||||
bash doc/scripts/scan-sbom.sh sbom-out
|
||||
'
|
||||
```
|
||||
|
||||
주요 환경변수: `PARALLEL`(동시 처리), `LIMIT`(대상 상한, 디버그용), `SEVERITY`(기본 `HIGH,CRITICAL`),
|
||||
`TRIVY_CACHE_DIR`(캐시 디렉토리), `MERGE=1`(generate 시 `catalog.cdx.json` 병합, cyclonedx CLI 필요).
|
||||
사설 레지스트리 인증은 trivy 네이티브: `TRIVY_USERNAME`/`TRIVY_PASSWORD`(단일) 또는 `DOCKER_CONFIG`(다중).
|
||||
|
||||
## CI (`.github/workflows/sbom.yml`)
|
||||
|
||||
- **트리거**: `schedule`(주 1회, 전체) / `pull_request`(`manifests/helm/**` 변경 시, 변경 차트만 증분) / `workflow_dispatch`.
|
||||
- **컨테이너**: `vars.SBOM_PIPELINE_IMAGE` (helm+trivy+python3 보유 이미지)에서 실행.
|
||||
- **시크릿**: `DOCKERHUB_TOKEN`/`DOCKERHUB_USER`(rate limit), `NGC_API_KEY`(nvcr.io), 필요 시 paasup 사설 레지스트리.
|
||||
- **아티팩트**: `sbom/`(+`catalog.cdx.json`), `trivy-summary.*` 업로드 + Job Summary 표시. 산출물은 **커밋하지 않는다**(`.gitignore`).
|
||||
- **스캔 주기 원칙**: 인벤토리·SBOM 은 차트 변경 시에만 바뀌지만 **취약점은 새 CVE 로 계속 변하므로 스케줄 전체 스캔이 필수**.
|
||||
PR 은 변경 차트만(빠른 게이트), 스케줄은 전체.
|
||||
|
||||
## 렌더 함정 (extract 단계, 스크립트에 내장)
|
||||
|
||||
| 플래그 | 이유 |
|
||||
|--------|------|
|
||||
| `--set global.security.allowInsecureImages=true` | `bitnamilegacy/*` 오버라이드가 Bitnami common 의 insecureImages 가드에 걸려 렌더 실패하는 것 방지 |
|
||||
| `--kube-version 1.31.0` | 일부 차트의 `kubeVersion` 상한 제약 회피 (예: rancher `< 1.32`) |
|
||||
|
||||
trivy 병렬 실행 시 공유 캐시 bolt 잠금 충돌을 피하려 워커는 `--cache-backend memory --skip-db-update`,
|
||||
메인은 시작 시 `trivy image --download-db-only` 로 DB 를 1회 워밍한다.
|
||||
|
||||
## 산출물
|
||||
|
||||
- `sbom/<img>.cdx.json` — 이미지별 CycloneDX SBOM (bomFormat CycloneDX). `catalog.cdx.json`(선택) = 전체 병합.
|
||||
- `sbom-index.tsv` — chart⇥version⇥image⇥status⇥sec⇥sbom_file (차트↔이미지↔SBOM 매핑).
|
||||
- `trivy-summary.md` / `.tsv` — 이미지별 CRITICAL/HIGH 집계 (차트 컬럼 포함), 총계·소요시간.
|
||||
- `trivy-reports/<img>.json` — 이미지별 원본 취약점 결과.
|
||||
|
||||
## 운영 노트 — `:latest`/무태그 이미지 digest 고정
|
||||
|
||||
태그가 고정돼 있어도 내용물이 바뀌는 `:latest`/무태그 이미지는 재현성 저해 + 증분 스캔의 사각지대다.
|
||||
정기 전체 스캔 대상에 항상 포함하고, 가능하면 **구체 버전 또는 digest(`@sha256:...`)로 고정**한다.
|
||||
|
||||
적용된 고정(2026-07-08 기준):
|
||||
- `mlflow@1.9.0` postgresql → `bitnamilegacy/postgresql@sha256:42a8200d...` (legacy 에 v18 버전 태그 부재, latest 내용 digest 고정)
|
||||
- `vllm@0.0.11` vllm-openai → `vllm/vllm-openai:v0.24.0`
|
||||
|
||||
digest 고정은 재현성을 보장하지만 자동 보안 패치는 멈춘다. → 정기 스캔으로 새 CVE 를 감지하고 필요 시 digest 를 갱신한다.
|
||||
`bitnamilegacy/*` 는 미유지 이미지이므로 중기적으로 legacy 탈피(대체 이미지)를 검토한다.
|
||||
Reference in New Issue
Block a user