Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
115 changes: 96 additions & 19 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Fieldnote-Echo/Kokoro-FastAPI — Hardened GHCR Release
# Replaces upstream's multi-arch bake workflow with a GPU-only, amd64-only
# build that includes supply-chain hardening (pinned actions, Sigstore
# attestation, Trivy scan, CycloneDX SBOM).
# build that includes supply-chain hardening (pinned actions, egress
# enforcement, Sigstore attestation, gating Trivy scan, attested SBOM).
#
# Incorporates from upstream's reworked release workflow: concurrency group,
# duplicate-release guard, ubuntu-24.04 runner, and the new multi-stage
Expand All @@ -22,7 +22,7 @@ permissions:
packages: write # GHCR push
id-token: write # Sigstore OIDC
security-events: write # SARIF upload
attestations: write # Build provenance
attestations: write # Build provenance + SBOM attestation

concurrency:
group: release-${{ github.ref_name }}
Expand All @@ -33,16 +33,48 @@ jobs:
runs-on: ubuntu-24.04
env:
IMAGE: ghcr.io/fieldnote-echo/kokoro-fastapi-gpu
SCAN_TAG: ghcr.io/fieldnote-echo/kokoro-fastapi-gpu:ci-scan # local-only, never pushed
steps:
# ── Runner hardening ──────────────────────────────────────────────
# egress-policy: block — the runner may only reach the endpoints below.
# Derived from this workflow's known egress (GitHub/GHCR, PyPI, PyTorch,
# NVIDIA NGC, Hugging Face model download, Sigstore, Trivy DB). If a
# release fails with a "Blocked" egress line, add the reported host here;
# harden-runner fails closed and names the exact host.
- name: Harden runner
uses: step-security/harden-runner@5ef0c079ce82195b2a36a210272d6b661572d83e # v2.14.2
with:
egress-policy: audit
egress-policy: block
allowed-endpoints: >
Comment on lines +47 to +48

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Allow the Dockerfile's build-time downloads

When this workflow runs the Buildx steps under egress-policy: block, the Dockerfile still performs outbound apt-get update and curl https://astral.sh/uv/install.sh during the image build (docker/gpu/Dockerfile.optimized lines 15-18 and 45-47). This allow-list does not include the Ubuntu/NVIDIA apt hosts or astral.sh, so those deterministic build-time requests are denied before the Trivy gate or push step and the release job cannot publish an image.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Allow the UniDic dictionary download host

Even after the previously reported apt and Astral hosts are allowed, the image build still fails under this allow-list when docker/gpu/Dockerfile.optimized lines 77–79 execute python -m unidic download: UniDic fetches its dictionary from cotonoha-dic.s3-ap-northeast-1.amazonaws.com, which is not permitted here. Add that endpoint so the default INCLUDE_JAPANESE=true build can complete.

Useful? React with 👍 / 👎.

github.com:443
api.github.com:443
objects.githubusercontent.com:443
codeload.github.com:443
release-assets.githubusercontent.com:443
ghcr.io:443
pkg-containers.githubusercontent.com:443
actions-results-receiver-production.githubapp.com:443
productionresultssa0.blob.core.windows.net:443
pypi.org:443
files.pythonhosted.org:443
download.pytorch.org:443
nvcr.io:443
huggingface.co:443
cdn-lfs.huggingface.co:443
cdn-lfs-us-1.hf.co:443
fulcio.sigstore.dev:443
rekor.sigstore.dev:443
tuf-repo-cdn.sigstore.dev:443
mirror.gcr.io:443
registry-1.docker.io:443
auth.docker.io:443
production.cloudflare.docker.com:443
Comment on lines +47 to +71

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

1. Egress block breaks build 🐞 Bug ☼ Reliability

The release workflow enables egress-policy: block but the allow-list omits endpoints required by
docker/gpu/Dockerfile.optimized (Ubuntu APT repositories and astral.sh), so the Docker build
will be network-blocked and the job will fail before Trivy gating/push when layers aren’t already
satisfied by cache.
Agent Prompt
## Issue description
The release workflow uses `step-security/harden-runner` with `egress-policy: block`, but the `allowed-endpoints` list is missing outbound destinations used during the Docker build (APT mirror hosts and `astral.sh` for installing `uv`). This will cause the `docker/build-push-action` build to fail with blocked egress on a fresh runner or when cache layers are invalidated.

## Issue Context
`docker/gpu/Dockerfile.optimized` performs:
- `apt-get update` / `apt-get install` (requires Ubuntu APT mirrors; typically `archive.ubuntu.com` and `security.ubuntu.com`, often over port 80)
- `curl -LsSf https://astral.sh/uv/install.sh | sh` (requires `astral.sh:443`, and potentially follow-on downloads)

## Fix Focus Areas
- .github/workflows/release.yml[44-71]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


# ── Checkout ──────────────────────────────────────────────────────
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false # don't leave the token in .git/config for later steps

# ── Guard: don't re-publish an existing release ───────────────────
- name: Check release does not exist
Expand Down Expand Up @@ -84,10 +116,60 @@ jobs:
type=semver,pattern={{major}}.{{minor}}
type=sha,prefix=

# ── Build & push ──────────────────────────────────────────────────
# ── Build for scanning (load locally, DO NOT push yet) ────────────
# CUDA base images are pinned by digest, mirroring docker-bake.hcl's
# gpu-amd64 target (CUDA 12.6.3). Keep these in sync with the bake file;
# refresh with: docker buildx imagetools inspect nvcr.io/nvidia/cuda:<tag>
- name: Build image (load for scan)
id: build_scan
uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 # v6.19.2
with:
context: .
file: docker/gpu/Dockerfile.optimized
platforms: linux/amd64
load: true
push: false
build-args: |
CUDA_VERSION=12.6.3
CUDA_BUILDER_IMAGE=nvcr.io/nvidia/cuda:12.6.3-cudnn-devel-ubuntu24.04@sha256:50efab398f76258daa91ceebb33b6467e40217c67ea44fb5a2cebc6be7d9cce3
CUDA_RUNTIME_IMAGE=nvcr.io/nvidia/cuda:12.6.3-cudnn-runtime-ubuntu24.04@sha256:8aef630a54bc5c5146ae5ce68e6af5caa3df0fb690bb91544175c91f307e4356
tags: ${{ env.SCAN_TAG }}
cache-from: type=gha
cache-to: type=gha,mode=max

# ── Trivy: GATE the release (fail on fixable CRITICAL) ────────────
# Runs BEFORE push so a vulnerable image is never published. ignore-unfixed
# avoids blocking on CVEs in the CUDA/OS base that have no available fix;
# gate on CRITICAL only (HIGH is reported below but non-blocking, since
# CUDA base layers routinely carry unactionable HIGH advisories).
- name: Trivy scan (gate)
uses: aquasecurity/trivy-action@c1824fd6edce30d7ab345a9989de00bbd46ef284 # 0.34.0
with:
image-ref: ${{ env.SCAN_TAG }}
format: table
severity: CRITICAL
ignore-unfixed: true
exit-code: "1"

# ── Trivy: full report to the Security tab (non-blocking) ─────────
- name: Trivy scan (SARIF report)
if: always() && steps.build_scan.outcome == 'success'
Comment on lines +155 to +156

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Make the SARIF reporting path non-blocking

When either this second Trivy invocation or the following SARIF upload fails because of a transient tool, database, or code-scanning ingestion error, the step still marks the job failed; the later Build and push step has the implicit success() condition and is therefore skipped. This makes the supposedly non-blocking report an additional release gate even when the preceding CRITICAL scan passed, so the report and upload steps need failure handling such as continue-on-error.

Useful? React with 👍 / 👎.

uses: aquasecurity/trivy-action@c1824fd6edce30d7ab345a9989de00bbd46ef284 # 0.34.0
with:
image-ref: ${{ env.SCAN_TAG }}
format: sarif
output: trivy-results.sarif
severity: CRITICAL,HIGH

- name: Upload Trivy SARIF
if: always() && hashFiles('trivy-results.sarif') != ''
uses: github/codeql-action/upload-sarif@9e907b5e64f6b83e7804b09294d44122997950d6 # v4.32.3
with:
sarif_file: trivy-results.sarif

# ── Push (only reached if the gate scan passed) ───────────────────
# Reuses the layers built above via the GHA cache, so this is a fast
# cache hit rather than a second full build.
- name: Build and push
id: build
uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 # v6.19.2
Expand All @@ -113,20 +195,6 @@ jobs:
subject-digest: ${{ steps.build.outputs.digest }}
push-to-registry: true

# ── Trivy container scan ──────────────────────────────────────────
- name: Trivy vulnerability scan
uses: aquasecurity/trivy-action@c1824fd6edce30d7ab345a9989de00bbd46ef284 # 0.34.0
with:
image-ref: ${{ env.IMAGE }}@${{ steps.build.outputs.digest }}
format: sarif
output: trivy-results.sarif
severity: CRITICAL,HIGH

- name: Upload Trivy SARIF
uses: github/codeql-action/upload-sarif@9e907b5e64f6b83e7804b09294d44122997950d6 # v4.32.3
with:
sarif_file: trivy-results.sarif

# ── CycloneDX SBOM ───────────────────────────────────────────────
- name: Generate SBOM
uses: anchore/sbom-action@28d71544de8eaf1b958d335707167c5f783590ad # v0.22.2
Expand All @@ -135,6 +203,15 @@ jobs:
format: cyclonedx-json

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Generate SPDX JSON for the SBOM attestation

actions/attest-sbom requires the supplied SBOM to use SPDX JSON, but this step writes a CycloneDX JSON document and passes it directly as sbom-path. The attestation action therefore cannot create the SBOM attestation, leaving the job failed after the image has been pushed and preventing the subsequent GitHub Release; generate an SPDX JSON file for this action, or use an attestation mechanism that supports CycloneDX.

Useful? React with 👍 / 👎.

output-file: sbom.cdx.json

# ── Attest the SBOM to the registry (verifiable, like provenance) ──
- name: Attest SBOM
uses: actions/attest-sbom@4651f806c01d8637787e274ac3bdf724ef169f34 # v3.0.0
with:
subject-name: ${{ env.IMAGE }}
subject-digest: ${{ steps.build.outputs.digest }}
sbom-path: sbom.cdx.json
push-to-registry: true

# ── GitHub Release ────────────────────────────────────────────────
- name: Create GitHub Release
if: startsWith(github.ref, 'refs/tags/')
Expand Down