diff --git a/.github/scripts/collect-ubuntu-backport-evidence.sh b/.github/scripts/collect-ubuntu-backport-evidence.sh new file mode 100644 index 0000000..6896641 --- /dev/null +++ b/.github/scripts/collect-ubuntu-backport-evidence.sh @@ -0,0 +1,34 @@ +#!/usr/bin/env bash +# Only read the exact consumer and immutable bundle; execute its guard on the runner. +set -euo pipefail +test "$#" -eq 3 +image=$1 +sbom=$2 +evidence=$3 +bundle_image='ghcr.io/pasturestack/ubuntu-security-backports:v0.1.0@sha256:2742a3d429e4e14bc587f6dedc51ded4246cf90bbaedcd86c67d0885e32a6f2a' +bundle_digest=${bundle_image##*@} +[[ "$bundle_digest" =~ ^sha256:[0-9a-f]{64}$ ]] +test -s "$sbom" +mkdir -p "$evidence" +scratch=$(mktemp -d) +consumer= +bundle= +cleanup() { + if [[ -n "$consumer" ]]; then docker rm "$consumer" >/dev/null 2>&1 || true; fi + if [[ -n "$bundle" ]]; then docker rm "$bundle" >/dev/null 2>&1 || true; fi + rm -rf -- "$scratch" +} +trap cleanup EXIT +consumer=$(docker create --entrypoint /bin/true "$image") +docker cp "$consumer:/usr/share/pasturestack/security/ubuntu-backports/." "$evidence/" +docker rm "$consumer" >/dev/null +consumer= +bundle=$(docker create "$bundle_image" /artifact-export-only) +docker cp "$bundle:/out/verify-consumer-evidence.py" "$scratch/verify-consumer-evidence.py" +docker cp "$bundle:/out/SHA256SUMS" "$scratch/bundle-SHA256SUMS" +docker rm "$bundle" >/dev/null +bundle= +python3 -B "$scratch/verify-consumer-evidence.py" \ + --evidence-dir "$evidence" --sbom "$sbom" \ + --expected-bundle-digest "$bundle_digest" \ + --bundle-checksums "$scratch/bundle-SHA256SUMS" diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 368e5c3..902ef25 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -77,6 +77,24 @@ jobs: -v "$RUNNER_TEMP/trivy-cache:/root/.cache/trivy" \ "$TRIVY_IMAGE" image --scanners vuln,secret \ --exit-code 0 \ + --format json --output /evidence/runtime-security-raw.json "$image" + docker run --rm \ + -v /var/run/docker.sock:/var/run/docker.sock \ + -v "$PWD/evidence:/evidence" \ + -v "$RUNNER_TEMP/trivy-cache:/root/.cache/trivy" \ + "$TRIVY_IMAGE" image --format cyclonedx \ + --output /evidence/runtime.cdx.json "$image" + backports="$RUNNER_TEMP/pod-pause-image-ubuntu-backports" + bash .github/scripts/collect-ubuntu-backport-evidence.sh \ + "$image" "evidence/runtime.cdx.json" "$backports" \ + > "evidence/ubuntu-backports-verification.json" + tar -C "$backports" -czf "evidence/ubuntu-backports.tar.gz" . + docker run --rm -v "$backports:/backports:ro" \ + -v /var/run/docker.sock:/var/run/docker.sock \ + -v "$PWD/evidence:/evidence" \ + -v "$RUNNER_TEMP/trivy-cache:/root/.cache/trivy" \ + "$TRIVY_IMAGE" image --vex /backports/openvex.json --skip-db-update --scanners vuln,secret \ + --exit-code 0 \ --format json --output /evidence/runtime-security.json "$image" jq -e '[.Results[]?.Vulnerabilities[]? | select(.Severity == "CRITICAL" or .Severity == "HIGH")] | length == 0' \ evidence/runtime-security.json >/dev/null @@ -84,15 +102,9 @@ jobs: python3 .github/scripts/enforce-runtime-vulnerabilities.py \ --phase release \ --repository "$GITHUB_REPOSITORY" - docker run --rm \ - -v /var/run/docker.sock:/var/run/docker.sock \ - -v "$PWD/evidence:/evidence" \ - -v "$RUNNER_TEMP/trivy-cache:/root/.cache/trivy" \ - "$TRIVY_IMAGE" image --format cyclonedx \ - --output /evidence/runtime.cdx.json "$image" jq -e '.bomFormat == "CycloneDX" and (.components | length > 0)' \ evidence/runtime.cdx.json >/dev/null - sha256sum evidence/runtime-security.json evidence/runtime.cdx.json \ + sha256sum evidence/runtime-security-raw.json evidence/ubuntu-backports-verification.json evidence/ubuntu-backports.tar.gz evidence/runtime-security.json evidence/runtime.cdx.json \ > evidence/SHA256SUMS - name: Publish immutable runtime image @@ -131,7 +143,8 @@ jobs: gh release create "$GITHUB_REF_NAME" --repo "$GITHUB_REPOSITORY" --verify-tag \ --title "Pod Pause Image $GITHUB_REF_NAME" \ --notes 'Pure numeric PastureStack pod-infrastructure image release with signal smoke tests, vulnerability scan, SBOM, checksums, and provenance.' \ - evidence/runtime-security.json evidence/runtime.cdx.json evidence/SHA256SUMS + evidence/runtime-security-raw.json evidence/runtime-security.json evidence/runtime.cdx.json \ + evidence/ubuntu-backports-verification.json evidence/ubuntu-backports.tar.gz evidence/SHA256SUMS - name: Clean run-owned resources if: always() diff --git a/.github/workflows/security-release-gate.yml b/.github/workflows/security-release-gate.yml index 8fa2783..0bd9b76 100644 --- a/.github/workflows/security-release-gate.yml +++ b/.github/workflows/security-release-gate.yml @@ -136,14 +136,28 @@ jobs: --volume "$PWD/evidence:/evidence" \ --volume "$trivy_cache:/root/.cache/trivy" \ "$TRIVY_IMAGE" image \ - --format cyclonedx \ - --output /evidence/image.cdx.json \ + --scanners vuln \ + --format json \ + --output /evidence/image-vulnerabilities-raw.json \ "$CANDIDATE_IMAGE" docker run --rm \ --volume /var/run/docker.sock:/var/run/docker.sock \ --volume "$PWD/evidence:/evidence" \ --volume "$trivy_cache:/root/.cache/trivy" \ "$TRIVY_IMAGE" image \ + --format cyclonedx \ + --output /evidence/image.cdx.json \ + "$CANDIDATE_IMAGE" + backports="$RUNNER_TEMP/pod-pause-image-ubuntu-backports" + bash .github/scripts/collect-ubuntu-backport-evidence.sh \ + "$CANDIDATE_IMAGE" "evidence/image.cdx.json" "$backports" \ + > "evidence/ubuntu-backports-verification.json" + tar -C "$backports" -czf "evidence/ubuntu-backports.tar.gz" . + docker run --rm -v "$backports:/backports:ro" \ + --volume /var/run/docker.sock:/var/run/docker.sock \ + --volume "$PWD/evidence:/evidence" \ + --volume "$trivy_cache:/root/.cache/trivy" \ + "$TRIVY_IMAGE" image --vex /backports/openvex.json --skip-db-update \ --scanners vuln \ --format json \ --output /evidence/image-vulnerabilities.json \ diff --git a/Dockerfile b/Dockerfile index a946e75..07cb6d2 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,8 @@ +# syntax=docker/dockerfile:1.7 ARG UBUNTU_IMAGE=ubuntu:26.04@sha256:2260313b31c8c011cd2eebe728008efac1b3982be73eb71348ea2648d2c0e09b +ARG UBUNTU_SECURITY_BACKPORTS_IMAGE=ghcr.io/pasturestack/ubuntu-security-backports:v0.1.0@sha256:2742a3d429e4e14bc587f6dedc51ded4246cf90bbaedcd86c67d0885e32a6f2a + FROM ${UBUNTU_IMAGE} AS snapshot-ca-bootstrap ADD --checksum=sha256:5b6326f1932cfd7be57130561dc78192f09ed9f58bfbe6606ed0c50510c3b6cd \ @@ -84,9 +87,12 @@ RUN gcc -O2 -Wall -Wextra -Werror -D_FORTIFY_SOURCE=3 -fstack-protector-strong - -Wl,-z,relro -Wl,-z,now -Wl,--build-id=none -o /out-pause pause.c \ && strip /out-pause +FROM ${UBUNTU_SECURITY_BACKPORTS_IMAGE} AS ubuntu_security_backports + FROM ${UBUNTU_IMAGE} +ARG UBUNTU_SECURITY_BACKPORTS_IMAGE -ARG IMAGE_VERSION=v3.0.5 +ARG IMAGE_VERSION=v3.0.6 ARG SOURCE_REVISION=unknown ARG UBUNTU_IMAGE @@ -120,5 +126,24 @@ COPY --from=build /out-pause /pause COPY --from=build /usr/share/pasturestack/manifests/ /usr/share/pasturestack/manifests/ COPY LICENSE /usr/share/licenses/pod-pause-image/LICENSE +# Keep the build-only installer and package archives outside the final image. +RUN --mount=type=bind,from=ubuntu_security_backports,source=/out,target=/opt/pasturestack/ubuntu-security-backports,readonly \ + set -eux; \ + evidence=/usr/share/pasturestack/security/ubuntu-backports; \ + mkdir -p "$evidence"; \ + /opt/pasturestack/ubuntu-security-backports/install-existing-backports --install-in-docker-build > "$evidence/install.json"; \ + /opt/pasturestack/ubuntu-security-backports/install-existing-backports --verify-installed > "$evidence/verify.json"; \ + for file in manifest.json qualification.json source-inputs.lock.json openvex.json; do \ + cp "/opt/pasturestack/ubuntu-security-backports/$file" "$evidence/$file"; \ + done; \ + printf '%s\n' "$UBUNTU_SECURITY_BACKPORTS_IMAGE" > "$evidence/bundle-image.txt"; \ + (cd "$evidence" && sha256sum manifest.json qualification.json source-inputs.lock.json openvex.json install.json verify.json bundle-image.txt > SHA256SUMS); \ + { \ + printf 'record\tname\tarchitecture\tversion\n'; \ + printf 'metadata\tschema\t-\tpasturestack.dpkg-manifest/v1\n'; \ + printf 'metadata\tbase_image\t-\t%s\n' "$UBUNTU_IMAGE"; \ + dpkg-query -W -f='package\t${binary:Package}\t${Architecture}\t${Version}\n' | LC_ALL=C sort; \ + } > /usr/share/pasturestack/manifests/runtime-ubuntu-packages.tsv + USER 65532:65532 ENTRYPOINT ["/pause"] diff --git a/README.md b/README.md index f3747d8..d7ada54 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,9 @@ # PastureStack Pod Pause Image +Linux runtime **`v3.0.6`** consumes verified Ubuntu security backports without adding packages or changing the existing coreutils provider. + +The runtime recipe applies the qualified Ubuntu 26.04 backport bundle only to already-installed packages and preserves the selected coreutils provider. The immutable bundle digest and final-image checks are pending. Bundle manifests, qualification, exact-version OpenVEX, source inputs and install/verify receipts are retained under `/usr/share/pasturestack/security/ubuntu-backports/`; raw scan evidence and the existing full-severity release policy remain required. + A small pod-infrastructure image that keeps a container alive, reaps orphaned child processes, and exits cleanly when it receives `SIGTERM` or `SIGINT`. PastureStack is an independent community effort to preserve, audit, and modernize the Rancher 1.6 ecosystem. It is not affiliated with or endorsed by Rancher Labs or SUSE. @@ -8,10 +12,10 @@ PastureStack is an independent community effort to preserve, audit, and moderniz ## Maintained image -The reviewed `linux/amd64` runtime uses the pure numeric coordinate: +After publication, the candidate `linux/amd64` runtime uses the pure numeric coordinate: ```text -ghcr.io/pasturestack/pod-pause-image:v3.0.5 +ghcr.io/pasturestack/pod-pause-image:v3.0.6 ``` Product identity and provenance are carried by the package name, OCI labels, @@ -21,7 +25,7 @@ numeric successor after its release and integration gates pass. ## Build -Version 3.0.5 installs the SHA256-pinned official Ubuntu CA security update in +The recipe installs the SHA256-pinned official Ubuntu CA security update in the build stage after the locked snapshot. Both stages apply the existing-package security micro-updates in `ubuntu-security-packages.lock` from the signed 2026-10-23 Ubuntu 26.04 snapshot, without adding or removing packages. This @@ -33,9 +37,9 @@ signal-handling, privilege or pod-sandbox behavior. ```sh docker build --pull \ - --build-arg IMAGE_VERSION=v3.0.5 \ + --build-arg IMAGE_VERSION=v3.0.6 \ --build-arg SOURCE_REVISION="$(git rev-parse HEAD)" \ - -t local/pasturestack/pod-pause-image:v3.0.5 . + -t local/pasturestack/pod-pause-image:v3.0.6 . ``` The runtime base is pinned to the reviewed Ubuntu 26.04 `linux/amd64` manifest. The build stage uses the HTTPS Ubuntu snapshot and exact direct-package versions recorded in [`ubuntu-apt.lock`](ubuntu-apt.lock). The final image carries the resolved builder toolchain, builder package, and runtime package manifests under `/usr/share/pasturestack/manifests/`; CI also records builder and runtime image inspections, CycloneDX SBOMs, vulnerability reports, and signal-handling smoke tests as a 30-day review artifact. @@ -45,7 +49,7 @@ The release gate checks the complete runtime vulnerability report. Any available ## Smoke test ```sh -docker run -d --name pod-pause-poc local/pasturestack/pod-pause-image:v3.0.5 +docker run -d --name pod-pause-poc local/pasturestack/pod-pause-image:v3.0.6 docker inspect --format '{{.State.Running}} {{.Config.User}}' pod-pause-poc docker stop --time 5 pod-pause-poc docker inspect --format '{{.State.ExitCode}}' pod-pause-poc diff --git a/scripts/validate b/scripts/validate index b75c14e..776fb0d 100644 --- a/scripts/validate +++ b/scripts/validate @@ -45,7 +45,7 @@ grep -F 'builder-ubuntu-packages.tsv' Dockerfile >/dev/null grep -F 'builder-toolchain.tsv' Dockerfile >/dev/null grep -F 'runtime-ubuntu-packages.tsv' Dockerfile >/dev/null grep -F 'rm -f /usr/bin/pebble' Dockerfile >/dev/null -grep -F 'ARG IMAGE_VERSION=v3.0.5' Dockerfile >/dev/null +grep -F 'ARG IMAGE_VERSION=v3.0.6' Dockerfile >/dev/null grep -F 'ADD --checksum=sha256:5b6326f1932cfd7be57130561dc78192f09ed9f58bfbe6606ed0c50510c3b6cd' Dockerfile >/dev/null grep -F 'https://archive.ubuntu.com/ubuntu/pool/main/c/ca-certificates/ca-certificates_20260816ubuntu0.26.04.1_all.deb' Dockerfile >/dev/null test "$(grep -Fc 'dpkg -i /tmp/ca-certificates.deb;' Dockerfile)" -eq 1