diff --git a/README.md b/README.md index 60e5a87..14b7423 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,8 @@ Reusable Gitea Actions composite workflows for noctrl infrastructure automation. Builds and optionally pushes OCI container images to `registry.noctrl.eu` using Podman with isolated storage context. +Since `v10`, the built image is scanned with Trivy before the push and the action fails on fixable HIGH/CRITICAL vulnerabilities (tune with `scan-severity`, opt out with `scan: "false"`). Requires the `trivy` binary on the runner (present in `noctrl/gitea-runner` since `v9`). + **Location:** [`./podman-build-publish`](./podman-build-publish) **Use in workflows:** diff --git a/podman-build-publish/action.yaml b/podman-build-publish/action.yaml index e06b4a8..b8b1b76 100644 --- a/podman-build-publish/action.yaml +++ b/podman-build-publish/action.yaml @@ -37,6 +37,20 @@ inputs: Set to "false" for build-only verification workflows. required: false default: "true" + scan: + description: | + Whether to scan the built image with Trivy before pushing. The step + fails — blocking the push — when vulnerabilities at or above the + configured severities with an available fix are found (unfixed CVEs + are ignored: nothing actionable, and Debian/Alpine bases always carry + some). Requires the trivy binary on the runner. Set to "false" only + with a documented reason. + required: false + default: "true" + scan-severity: + description: Comma-separated Trivy severities that fail the scan. + required: false + default: HIGH,CRITICAL runs: using: composite @@ -141,6 +155,45 @@ runs: build_cmd+=("${{ inputs.context }}") "${build_cmd[@]}" + - if: ${{ inputs.scan != 'false' }} + id: scan + shell: bash + run: | + set -euo pipefail + + : "${PODMAN_STORAGE_DRIVER:=vfs}" + : "${PODMAN_STORAGE_OPT:=}" + + podman_args=( + --root "${RUNNER_TEMP}/podman-root" + --runroot "${RUNNER_TEMP}/podman-runroot" + --storage-driver "${PODMAN_STORAGE_DRIVER}" + ) + + if [[ -n "${PODMAN_STORAGE_OPT}" ]]; then + podman_args+=(--storage-opt "${PODMAN_STORAGE_OPT}") + fi + + image_base="registry.noctrl.eu/${{ inputs.image-name }}" + first_tag="$(printf '%s\n' "${{ inputs.tags }}" | tr ', ' '\n\n' | sed '/^$/d' | head -n1)" + + # Trivy cannot read podman's storage under a custom --root, so scan + # an exported archive of the image instead. + archive="${RUNNER_TEMP}/trivy-scan-image.tar" + rm -f "${archive}" + podman "${podman_args[@]}" save --format oci-archive -o "${archive}" "${image_base}:${first_tag}" + + echo "Scanning ${image_base}:${first_tag} (fail on: ${{ inputs.scan-severity }}, fixed only)" + trivy image \ + --input "${archive}" \ + --scanners vuln \ + --severity "${{ inputs.scan-severity }}" \ + --ignore-unfixed \ + --exit-code 1 \ + --no-progress + + rm -f "${archive}" + - if: ${{ inputs.push != 'false' }} id: push shell: bash