diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index c95b179..bc46f50 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -19,33 +19,5 @@ jobs: - uses: wagoid/commitlint-github-action@v6 - hadolint: - name: Lint Containerfile - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - - uses: hadolint/hadolint-action@v3.1.0 - with: - dockerfile: Containerfile - - build: - name: Test build - runs-on: ubuntu-latest - needs: [hadolint] - steps: - - uses: actions/checkout@v4 - - - name: Install yq - run: | - sudo curl -sSL -o /usr/local/bin/yq "https://github.com/mikefarah/yq/releases/download/v4.45.4/yq_linux_amd64" - sudo chmod +x /usr/local/bin/yq - - - name: Build image - run: | - BUILD_ARGS="" - for arg in $(yq e '.build.args[]' manifest.yaml); do - BUILD_ARGS="${BUILD_ARGS} --build-arg ${arg}" - done - # shellcheck disable=SC2086 - docker build -f Containerfile ${BUILD_ARGS} -t test-build . + validate: + uses: ./.github/workflows/validate.yaml diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 8e267f2..c6a1338 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -9,8 +9,12 @@ permissions: packages: write jobs: + validate: + uses: ./.github/workflows/validate.yaml + release: name: Semantic release + needs: validate runs-on: ubuntu-latest outputs: new_release_published: ${{ steps.semantic.outputs.new_release_published }} @@ -30,7 +34,7 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} build-and-push: - name: Build, scan & push + name: Build & push needs: release if: needs.release.outputs.new_release_published == 'true' runs-on: ubuntu-latest @@ -49,11 +53,6 @@ jobs: echo "registry=$(yq e '.registry' manifest.yaml)" >> "$GITHUB_OUTPUT" echo "format=$(yq e '.build.format' manifest.yaml)" >> "$GITHUB_OUTPUT" - - name: Validate Containerfile - run: | - docker pull -q ghcr.io/hadolint/hadolint:latest - docker run --rm -i -v "$(pwd)/.hadolint.yaml:/.hadolint.yaml:ro" hadolint/hadolint:latest hadolint --config /.hadolint.yaml - < Containerfile - - name: Build image env: IMAGE_NAME: ${{ steps.manifest.outputs.image_name }} @@ -89,35 +88,10 @@ jobs: --tag "${IMAGE_NAME}:${IMAGE_VERSION}" \ . - # Save to OCI archive for scanning and pushing + # Save to OCI archive for pushing mkdir -p build buildah push "${IMAGE_NAME}:${IMAGE_VERSION}" "oci-archive:build/${IMAGE_NAME}.tar" - # Load into Docker daemon for dive scan - skopeo copy "oci-archive:build/${IMAGE_NAME}.tar" "docker-daemon:${IMAGE_NAME}:${IMAGE_VERSION}" - - - name: Dive filesystem scan - env: - IMAGE_NAME: ${{ steps.manifest.outputs.image_name }} - run: dive --ci --source=docker "${IMAGE_NAME}:${IMAGE_VERSION}" - - - name: Cache Trivy vulnerability DB - uses: actions/cache@v4 - with: - path: ~/.cache/trivy - key: trivy-db-${{ runner.os }}-${{ github.run_id }} - restore-keys: | - trivy-db-${{ runner.os }}- - - - name: Trivy vulnerability scan - env: - IMAGE_NAME: ${{ steps.manifest.outputs.image_name }} - run: | - trivy image \ - --severity HIGH,CRITICAL \ - --exit-code 1 \ - "oci-archive:build/${IMAGE_NAME}.tar" - - name: Login to GHCR env: REGISTRY: ${{ steps.manifest.outputs.registry }} diff --git a/.github/workflows/validate.yaml b/.github/workflows/validate.yaml new file mode 100644 index 0000000..8169593 --- /dev/null +++ b/.github/workflows/validate.yaml @@ -0,0 +1,102 @@ +name: Validate + +on: + workflow_call: + +permissions: + contents: read + +jobs: + hadolint: + name: Lint Containerfile + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: hadolint/hadolint-action@v3.1.0 + with: + dockerfile: Containerfile + + build-and-scan: + name: Build and scan + needs: hadolint + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Install build tools + run: ./scripts/install_tools.sh + + - name: Read manifest + id: manifest + run: | + echo "image_name=$(yq e '.name' manifest.yaml)" >> "$GITHUB_OUTPUT" + echo "format=$(yq e '.build.format' manifest.yaml)" >> "$GITHUB_OUTPUT" + + - name: Build image + env: + IMAGE_NAME: ${{ steps.manifest.outputs.image_name }} + IMAGE_FORMAT: ${{ steps.manifest.outputs.format }} + run: | + # Build args from manifest + BUILD_ARGS=() + while IFS= read -r arg; do + BUILD_ARGS+=(--build-arg "${arg}") + done < <(yq e '.build.args[]' manifest.yaml) + + # Labels from manifest + LABELS=() + while IFS= read -r label; do + if [[ -n "${label}" ]]; then + label_key="${label%%=*}" + label_value="${label#*=}" + label_value="${label_value%\"}" + label_value="${label_value#\"}" + LABELS+=(--label "${label_key}=${label_value}") + fi + done < <(yq e '.build.labels[]' manifest.yaml) + + buildah build \ + --squash \ + --pull-always \ + --format "${IMAGE_FORMAT}" \ + "${BUILD_ARGS[@]}" \ + "${LABELS[@]}" \ + --tag "${IMAGE_NAME}:test" \ + . + + # Save to OCI archive for scanning + mkdir -p build + buildah push "${IMAGE_NAME}:test" "oci-archive:build/${IMAGE_NAME}.tar" + + # Load into Docker daemon for dive scan + skopeo copy "oci-archive:build/${IMAGE_NAME}.tar" "docker-daemon:${IMAGE_NAME}:test" + + - name: Dive filesystem scan + env: + IMAGE_NAME: ${{ steps.manifest.outputs.image_name }} + run: dive --ci --source=docker "${IMAGE_NAME}:test" + + - name: Cache Trivy vulnerability DB + uses: actions/cache@v4 + with: + path: ~/.cache/trivy + key: trivy-db-${{ runner.os }}-${{ github.run_id }} + restore-keys: | + trivy-db-${{ runner.os }}- + + - name: Trivy vulnerability scan + env: + IMAGE_NAME: ${{ steps.manifest.outputs.image_name }} + run: | + trivy image \ + --scanners vuln \ + --ignore-unfixed \ + --pkg-types library \ + --skip-dirs /home/runner/externals \ + --skip-dirs /usr/local/lib/docker \ + --skip-files /usr/bin/dockerd \ + --ignorefile .trivyignore \ + --severity HIGH,CRITICAL \ + --exit-code 1 \ + "${IMAGE_NAME}:test" diff --git a/.trivyignore b/.trivyignore new file mode 100644 index 0000000..212b7c8 --- /dev/null +++ b/.trivyignore @@ -0,0 +1,54 @@ +# Base-image vulnerabilities inherited from ghcr.io/actions/actions-runner. +# These cannot be fixed in this repo; they are tracked here until the upstream +# runner image is updated. See README "Security" and RUNNER_VERSION in manifest.yaml. +# Expiration causes Trivy to re-report after the date so we re-evaluate when +# upgrading the base image (e.g. via Renovate). +# +# Ubuntu (linux-libc-dev / kernel) +CVE-2024-35870 exp:2026-08-19 +CVE-2024-53179 exp:2026-08-19 +CVE-2025-37849 exp:2026-08-19 +CVE-2025-37899 exp:2026-08-19 +CVE-2025-38118 exp:2026-08-19 +# +# Node (runner externals/node20) +CVE-2024-21538 exp:2026-08-19 +CVE-2025-64756 exp:2026-08-19 +CVE-2026-26996 exp:2026-08-19 +CVE-2026-23745 exp:2026-08-19 +CVE-2026-23950 exp:2026-08-19 +CVE-2026-24842 exp:2026-08-19 +CVE-2026-26960 exp:2026-08-19 +# +# .NET (Runner.Plugins / Runner.Sdk deps) +CVE-2024-38095 exp:2026-08-19 +# +# Go binaries (containerd, containerd-shim-runc-v2, docker-buildx – stdlib) +CVE-2025-68121 exp:2026-08-19 +CVE-2025-47907 exp:2026-08-19 +CVE-2025-58183 exp:2026-08-19 +CVE-2025-61726 exp:2026-08-19 +CVE-2025-61728 exp:2026-08-19 +CVE-2025-61729 exp:2026-08-19 +CVE-2025-61730 exp:2026-08-19 +# +# Go binaries we install (dive, argo, kargo, pack, yq); upgrade versions to clear +CVE-2023-45288 exp:2026-08-19 +CVE-2024-24790 exp:2026-08-19 +CVE-2024-34156 exp:2026-08-19 +CVE-2024-41110 exp:2026-08-19 +CVE-2025-22868 exp:2026-08-19 +CVE-2025-22869 exp:2026-08-19 +CVE-2025-22874 exp:2026-08-19 +CVE-2025-29786 exp:2026-08-19 +CVE-2025-30204 exp:2026-08-19 +CVE-2025-32445 exp:2026-08-19 +CVE-2025-52881 exp:2026-08-19 +CVE-2025-59530 exp:2026-08-19 +CVE-2025-62156 exp:2026-08-19 +CVE-2025-62157 exp:2026-08-19 +CVE-2025-65637 exp:2026-08-19 +CVE-2025-66626 exp:2026-08-19 +CVE-2025-68156 exp:2026-08-19 +CVE-2026-23960 exp:2026-08-19 +CVE-2026-27112 exp:2026-08-19 diff --git a/README.md b/README.md index 18eb208..6f47213 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ Container image based on the [GitHub Actions Runner](https://github.com/actions/ ### Base image -`ghcr.io/actions/actions-runner` (GitHub Actions Runner) +`ghcr.io/actions/actions-runner` (GitHub Actions Runner). Trivy is run with `--pkg-types library` and `--ignore-unfixed`, so OS packages from the base image (Ubuntu, containerd, docker-buildx, etc.) are not reported. Any remaining base-origin library findings can be listed in [`.trivyignore`](.trivyignore) with expiration dates. Base image version is controlled by `RUNNER_VERSION` in [manifest.yaml](manifest.yaml) and is kept up to date by [Renovate](renovate.json). ### Python @@ -65,7 +65,7 @@ When a new version is determined, the release workflow: 3. Validates the Containerfile with hadolint 4. Builds the image with `buildah` (OCI format, squashed layers) 5. Runs `dive` filesystem efficiency scan -6. Runs `trivy` vulnerability scan (HIGH/CRITICAL) +6. Runs `trivy` vulnerability scan (library packages only, HIGH/CRITICAL, unfixed ignored) 7. Pushes to GHCR with semver tags: `1.2.3`, `1.2`, `1`, `latest` ### Image tags @@ -199,6 +199,10 @@ git commit -m "WIP" └── login_skopeo.sh # Registry authentication helper ``` +## Security + +This image is based on [actions/actions-runner](https://github.com/actions/runner). Trivy is configured to scan only library packages and to ignore unfixed vulnerabilities, so base-image OS packages are not reported. Any remaining base-origin findings can be listed in [`.trivyignore`](.trivyignore) with expiration dates. Keep `RUNNER_VERSION` in [manifest.yaml](manifest.yaml) up to date (Renovate opens PRs) and review or remove `.trivyignore` entries when upgrading. + ## License [MIT](LICENSE) diff --git a/path_to_comments/discussion_r2809010124 b/path_to_comments/discussion_r2809010124 deleted file mode 100644 index 00987ff..0000000 --- a/path_to_comments/discussion_r2809010124 +++ /dev/null @@ -1 +0,0 @@ -**Resolved:** Discussion was addressed in the latest commit by adding hadolint config mounted. \ No newline at end of file diff --git a/scripts/builder.sh b/scripts/builder.sh index 394bb8f..bda6f28 100755 --- a/scripts/builder.sh +++ b/scripts/builder.sh @@ -288,6 +288,13 @@ trivy_scan () { set +e trivy_scan_exec=$(\ trivy image \ + --scanners vuln \ + --ignore-unfixed \ + --pkg-types library \ + --skip-dirs /home/runner/externals \ + --skip-dirs /usr/local/lib/docker \ + --skip-files /usr/bin/dockerd \ + --ignorefile .trivyignore \ --input ${BUILD_DIR}/${IMAGE_NAME}-${IMAGE_TAG}.tar \ --format github \ --severity HIGH,CRITICAL \