Files
service-catalog/.claude/skills/sbom-cve-gate/SKILL.md
T
wbsong111 9557f1fccb sbom.yml 수동 실행이 실제로 필요한 경우를 문서화한다 (#55)
이번 세션에서 워크플로 수동 실행을 두 번 잘못 썼다: (1) 이미 연 PR에 대고
workflow_dispatch를 또 돌려 pull_request 자동 트리거와 완전히 중복시켰다,
(2) catalog-tag-update.yml이 GITHUB_TOKEN으로 force-push한 브랜치에서
--chart 스코프를 빠뜨려 카탈로그 전체(58차트)를 스캔했다.

sbom-cve-gate SKILL.md에 "gh workflow run은 언제 실제로 필요한가" 절을 추가해
정리했다 — PR이 이미 그 경로를 건드리면 pull_request 트리거가 자동으로 돈다
(gh pr checks로 먼저 확인). 수동 실행이 실제로 필요한 건 PR이 아직 없거나,
GITHUB_TOKEN이 force-push해 재귀 방지로 pull_request 이벤트가 안 뜨는 경우뿐이다.
수동 실행 시엔 -f chart=<차트명>으로 반드시 스코프를 좁힌다.

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-27 14:46:38 +09:00

4.7 KiB

name, description
name description
sbom-cve-gate 카탈로그 이미지의 SBOM 생성·CVE 스캔·게이트 판정을 실행하거나 결과를 해석할 때 사용한다. "SBOM 만들어줘", "취약점 스캔 돌려줘", "CVE 게이트 확인", "이 이미지 CRITICAL 몇 개야", "예외 등록" 같은 요청이 해당한다. scripts/pipeline/(extract-helm-images.sh, generate-sbom.sh, scan-sbom.sh, cve-gate.py)과 sbom.yml 워크플로를 다룬다.

SBOM·CVE 게이트 실행

extract-helm-images.sh → generate-sbom.sh → scan-sbom.sh(+CoverageProbe) → cve-gate.py

실행 경로 2가지

기본은 GitHub 워크플로다 — 파이프라인은 도구가 설치된 컨테이너(vars.SBOM_PIPELINE_IMAGE) 안에서 돈다.

gh workflow run helm-catalog-sbom --repo <org>/dip-catalog -f limit=3   # 빠른 검증
gh workflow run helm-catalog-sbom --repo <org>/dip-catalog              # 전체(limit=0)
gh run watch --repo <org>/dip-catalog

로컬에서 돌릴 때도 같은 컨테이너를 쓴다(정확한 docker run 명령은 doc/sbom-pipeline.md "로컬/컨테이너 실행" 참고). 레지스트리 인증이 필요하다 — TRIVY_USERNAME/TRIVY_PASSWORD(단일) 또는 DOCKER_CONFIG(다중). 빠른 검증은 generate 단계에 LIMIT=3.

gh workflow run은 언제 실제로 필요한가

sbom.ymlpull_request(경로 manifests/helm/**)로 이미 자동 트리거된다. PR이 이미 열려 있고 그 PR이 이 경로를 건드린다면 수동 실행은 중복이다 — 열자마자 자동으로 이미 돌았을 가능성이 높다. gh pr checks <번호>로 먼저 확인한다.

수동 실행(workflow_dispatch)이 실제로 필요한 경우는 둘뿐이다:

  1. 아직 PR이 없다 — 브랜치에 push만 했거나, PR을 열기 전에 미리 확인하고 싶을 때.
  2. PR의 브랜치가 워크플로 자신의 GITHUB_TOKEN으로 force-push됐다 — 예: catalog-tag-update.yml이 고정 브랜치에 매번 force-push하는 경우. GitHub은 기본 GITHUB_TOKEN으로 일어난 push에 대해 재귀 방지로 pull_request 이벤트를 발생시키지 않는다 — 이때는 gh pr checks가 빈 목록을 보여준다(실측: PR #50).

수동 실행할 땐 반드시 -f chart=<차트명>으로 스코프를 좁힌다. 빠뜨리면 전체 카탈로그(58차트·180+이미지)를 스캔해 SBOM 생성 단계에서만 10분 넘게 걸린다(실측). PR이 여러 차트를 건드리면 차트별로 나눠 여러 번 돌리거나, limit으로 상한을 둔다.

결과 해석 시 반드시 볼 것

CoverageProbe를 먼저 본다. findings 0건이 "진짜 0건"인지 "스캐너가 그 배포판을 모르는 것"인지 구분하는 유일한 수단이다.

의미
ok 데이터 있음 — 0건은 진짜 0건
none 데이터 없음 → 게이트가 차단한다(거짓 clean)
n/a OS 패키지 없음(distroless 등)

게이트는 고유 CVE 단위로 집계하고 max(벤더 등급, NVD 등급) 를 실효 등급으로 쓴다 — 벤더가 하향 평가한 CVE를 놓치지 않기 위함이다(.claude/pitfalls.md "스캐너 결과를 그대로 믿지 말 것"). 승인 예외는 doc/cve-exceptions.json(근거·만료일 필수, .trivyignore 안 씀).

현재 상태 — 게이트는 warn-only다

sbom.ymlcve-gate.py--warn-only로 호출한다. 게이트가 실패해도 워크플로/PR을 막지 않는다. 45+ 카탈로그 차트가 아직 이 게이트로 트리아지된 적이 없어, 강제 전환 전에 전체 스캔 1회로 현황 파악이 선행돼야 한다.

최신 미결 사항·전환 판단 근거는 MEMORY.md를 본다 — 이 skill에 중복 기록하지 않는다.

차단 CVE 대응 우선순위

무료 치환(태그 교체 · 이미지 좌표 교체) → 자체 빌드(hardened-containers) → 예외 승인

"베이스 OS 교체"라는 독립 레버는 없다 — 실제로 해보면 항상 자체 빌드로 귀결된다. 레버 판단 상세는 cve-remediation skill이 갖는다. 자체 빌드로 가야 한다면 별도 레포 hardened-containers에서 한다 — 그 레포의 docs/image-authoring/README.md가 절차 단일 출처다. 이 레포는 이미지를 빌드하지 않는다. 판정 로직 상세는 scripts/pipeline/cve-gate.py의 모듈 docstring을 1차 출처로 본다.

참고