From 25e05e0ba2dc74c88af380a3adc03f863550ae83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EC=98=81=EC=B0=BD?= Date: Mon, 20 Jul 2026 17:14:51 +0900 Subject: [PATCH] Create cve-edge-post.yml --- .github/workflows/cve-edge-post.yml | 137 ++++++++++++++++++++++++++++ 1 file changed, 137 insertions(+) create mode 100644 .github/workflows/cve-edge-post.yml diff --git a/.github/workflows/cve-edge-post.yml b/.github/workflows/cve-edge-post.yml new file mode 100644 index 0000000..4c85178 --- /dev/null +++ b/.github/workflows/cve-edge-post.yml @@ -0,0 +1,137 @@ +name: helm-catalog-cve-json + +# manifests/helm 카탈로그의 컨테이너 이미지 취약점을 스캔해 단일 JSON 요약으로 출력한다. +# sbom.yml 과 동일한 SBOM+trivy 파이프라인(doc/scripts/*.sh)을 재사용하고, +# trivy-reports/*.json(이미지별 원본 스캔 결과)을 jq 로 집계해 다음 포맷을 만든다. +# +# {"low": 0, "high": 0, "medium": 0, "critical": 0, +# "scanned_at": "2026-07-13T06:19:44Z", "summary": "CVE-xxxx-xxxxx, ..."} +# +# summary 는 발견된 CRITICAL CVE ID 전체를 콤마로 나열한다(중복 제거). +# 생성된 JSON 은 POST https://edge.gke.paasup.io/api/v1/cve-scans 로 전송한다. +# (X-CVE-API-Key 헤더 필요 — Repo Secret CVE_API_KEY, SSL 검증은 --insecure 로 스킵) +# +# 실행 컨테이너: sbom.yml 과 동일하게 vars.SBOM_PIPELINE_IMAGE (helm+trivy+python3+bash+git) 사용. + +on: + workflow_dispatch: + inputs: + limit: + description: '대상 이미지 수 상한 (0=전체). 테스트 시 예: 3' + required: false + default: '0' + +permissions: + contents: read + +jobs: + cve-json: + runs-on: ubuntu-latest + timeout-minutes: 120 + container: + image: ${{ vars.SBOM_PIPELINE_IMAGE }} + env: + OUT_DIR: ${{ github.workspace }}/sbom-out + TRIVY_CACHE_DIR: ${{ github.workspace }}/sbom-out/cache + PARALLEL: '3' + steps: + - name: Preflight — 도구 확인 + run: | + set -e + for t in helm trivy python3 bash git jq; do + command -v "$t" >/dev/null || { echo "::error::컨테이너에 $t 없음 (vars.SBOM_PIPELINE_IMAGE 확인)"; exit 1; } + done + helm version --short; trivy --version | head -1 + + - uses: actions/checkout@v4 + + - name: git safe.directory + run: git config --global --add safe.directory "$GITHUB_WORKSPACE" + + # 사설 레지스트리 인증: 시크릿으로 docker config.json 을 만들어 trivy 가 읽게 한다. + - name: 레지스트리 인증 구성 + env: + DOCKERHUB_USER: ${{ secrets.DOCKERHUB_USER }} + DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }} + NGC_API_KEY: ${{ secrets.NGC_API_KEY }} + run: | + mkdir -p "$OUT_DIR/.docker" + python3 - <<'PY' + import os, json, base64 + auths={} + def add(reg,user,secret): + if user and secret: + auths[reg]={"auth":base64.b64encode(f"{user}:{secret}".encode()).decode()} + add("https://index.docker.io/v1/", os.environ.get("DOCKERHUB_USER"), os.environ.get("DOCKERHUB_TOKEN")) + add("docker.getcollate.io", os.environ.get("DOCKERHUB_USER"), os.environ.get("DOCKERHUB_TOKEN")) + add("nvcr.io", "$oauthtoken", os.environ.get("NGC_API_KEY")) + out=os.path.join(os.environ["OUT_DIR"],".docker","config.json") + json.dump({"auths":auths}, open(out,"w")) + print("configured registries:", list(auths.keys()) or "(none)") + PY + echo "DOCKER_CONFIG=$OUT_DIR/.docker" >> "$GITHUB_ENV" + + - name: 이미지 인벤토리 추출 + run: bash doc/scripts/extract-helm-images.sh "$GITHUB_WORKSPACE/manifests/helm" "$OUT_DIR" + + - name: 스캔 대상 결정 (전체) + run: cp "$OUT_DIR/images_final.tsv" "$OUT_DIR/images_scan.tsv" + + - name: SBOM 생성 (CycloneDX) + env: + LIMIT: ${{ github.event.inputs.limit || '0' }} + run: | + if [ -s "$OUT_DIR/images_scan.tsv" ]; then + bash doc/scripts/generate-sbom.sh "$OUT_DIR/images_scan.tsv" "$OUT_DIR" + else + echo "대상 이미지 없음 — SBOM 생성 생략"; mkdir -p "$OUT_DIR/sbom" + fi + + - name: 취약점 스캔 (SBOM 입력, 오프라인) + run: | + if ls "$OUT_DIR/sbom/"*.cdx.json >/dev/null 2>&1; then + bash doc/scripts/scan-sbom.sh "$OUT_DIR" + else + echo "SBOM 없음 — 스캔 생략"; mkdir -p "$OUT_DIR/trivy-reports" + fi + + # trivy-reports/*.json (이미지별 원본 Trivy 결과)을 jq 로 병합 집계. + - name: CVE JSON 생성 + run: | + SCANNED_AT="$(date -u +%Y-%m-%dT%H:%M:%SZ)" + REPORTS_DIR="$OUT_DIR/trivy-reports" + + if ls "$REPORTS_DIR"/*.json >/dev/null 2>&1; then + RESULT="$(jq -s --arg scanned_at "$SCANNED_AT" ' + [.[] | .Results[]? | .Vulnerabilities[]? ] as $vulns + | { + low: ([$vulns[] | select(.Severity=="LOW")] | length), + high: ([$vulns[] | select(.Severity=="HIGH")] | length), + medium: ([$vulns[] | select(.Severity=="MEDIUM")] | length), + critical: ([$vulns[] | select(.Severity=="CRITICAL")] | length), + scanned_at: $scanned_at, + summary: ([$vulns[] | select(.Severity=="CRITICAL") | .VulnerabilityID] | unique | join(", ")) + } + ' "$REPORTS_DIR"/*.json)" + else + RESULT="$(jq -n --arg scanned_at "$SCANNED_AT" \ + '{low:0, high:0, medium:0, critical:0, scanned_at: $scanned_at, summary: ""}')" + fi + + echo "$RESULT" | tee "$OUT_DIR/cve-summary.json" + echo "$RESULT" >> "$GITHUB_STEP_SUMMARY" + + - name: CVE 스캔 결과 전송 + if: always() + env: + CVE_API_KEY: ${{ secrets.CVE_API_KEY }} + run: | + if [ ! -f "$OUT_DIR/cve-summary.json" ]; then + echo "::warning::$OUT_DIR/cve-summary.json 없음 — 전송 생략" + exit 0 + fi + curl --fail-with-body --silent --show-error --insecure \ + --request POST "https://edge.gke.paasup.io/api/v1/cve-scans" \ + --header "Content-Type: application/json" \ + --header "X-CVE-API-Key: $CVE_API_KEY" \ + --data @"$OUT_DIR/cve-summary.json"