Skip to content
Merged
Show file tree
Hide file tree
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
51 changes: 41 additions & 10 deletions .github/workflows/security-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,20 +43,20 @@ jobs:
run: |
set -euo pipefail
mkdir -p dist/security-runtime
docker image inspect local/system-image-preloader:v0.3.2 > dist/security-runtime/images.json
container=$(docker create --entrypoint /bin/true local/system-image-preloader:v0.3.2)
docker image inspect local/system-image-preloader:v0.3.3 > dist/security-runtime/images.json
container=$(docker create --entrypoint /bin/true local/system-image-preloader:v0.3.3)
trap 'docker rm -f "$container" >/dev/null 2>&1 || true' EXIT
docker cp "$container:/licenses/third-party/security/." dist/security-runtime/
docker cp "$container:/usr/local/bin/docker" dist/security-runtime/docker-cli
docker run --rm --network none --entrypoint sha256sum \
local/system-image-preloader:v0.3.2 /usr/local/bin/docker \
local/system-image-preloader:v0.3.3 /usr/local/bin/docker \
> dist/security-runtime/docker-sha256.txt
python3 -B tests/docker_manifest_digest_proof.py \
> dist/security-runtime/docker-manifest-digest-proof.json
rm dist/security-runtime/docker-cli
docker rm "$container" >/dev/null
trap - EXIT
docker save local/system-image-preloader:v0.3.2 | gzip -n > dist/security-runtime/images.tar.gz
docker save local/system-image-preloader:v0.3.3 | gzip -n > dist/security-runtime/images.tar.gz
printf '%s\n' "$GITHUB_SHA" > dist/security-runtime/source-revision.txt
(cd dist/security-runtime && sha256sum images.json images.tar.gz source-revision.txt > SHA256SUMS)
- name: Scan the exact runtime and generate its SBOM
Expand All @@ -65,15 +65,46 @@ jobs:
set -euo pipefail
scanner=aquasec/trivy:0.74.0@sha256:62b1e65e8869bc4b4c6aa4fa2b21595256c7c2f6018a9d9ad61caf87187c1969
docker pull "$scanner"
for runtime in local/system-image-preloader:v0.3.2; do
cache="$RUNNER_TEMP/ubuntu-backports-trivy-cache"
mkdir -p "$cache"
docker run --rm -v "$cache:/root/.cache/trivy" "$scanner" image --download-db-only
for runtime in local/system-image-preloader:v0.3.3; do
name=${runtime##*/}; name=${name%%:*}
docker save "$runtime" --output "$RUNNER_TEMP/$name.tar"
docker run --rm -v "$RUNNER_TEMP/$name.tar:/image.tar:ro" -v "$PWD/dist/security-runtime:/evidence" "$scanner" image --input /image.tar --scanners vuln,secret --list-all-pkgs --format json --output "/evidence/$name-security.json"
docker run --rm -v "$RUNNER_TEMP/$name.tar:/image.tar:ro" -v "$PWD/dist/security-runtime:/evidence" "$scanner" image --input /image.tar --format cyclonedx --output "/evidence/$name-sbom.cdx.json"
docker run --rm -v "$cache:/root/.cache/trivy" -v "$RUNNER_TEMP/$name.tar:/image.tar:ro" -v "$PWD/dist/security-runtime:/evidence" "$scanner" image --skip-db-update --input /image.tar --scanners vuln,secret --severity UNKNOWN,LOW,MEDIUM,HIGH,CRITICAL --list-all-pkgs --format json --output "/evidence/$name-security.json"
docker run --rm -v "$cache:/root/.cache/trivy" -v "$RUNNER_TEMP/$name.tar:/image.tar:ro" -v "$PWD/dist/security-runtime:/evidence" "$scanner" image --skip-db-update --input /image.tar --format cyclonedx --output "/evidence/$name-sbom.cdx.json"
mkdir -p dist/security-runtime
consumer_container=$(docker create "$runtime" /bin/true)
if docker cp "$consumer_container:/usr/share/pasturestack/security/ubuntu-backports" dist/security-runtime/ubuntu-backports; then
docker rm "$consumer_container" >/dev/null
else
docker rm "$consumer_container" >/dev/null
exit 1
fi
(cd dist/security-runtime/ubuntu-backports && sha256sum --check SHA256SUMS)
bundle_image='ghcr.io/pasturestack/ubuntu-security-backports:v0.1.0@sha256:2742a3d429e4e14bc587f6dedc51ded4246cf90bbaedcd86c67d0885e32a6f2a'
mkdir -p dist/security-runtime/bundle-tools
bundle_container=$(docker create "$bundle_image" unused)
if docker cp "$bundle_container:/out/verify-consumer-evidence.py" dist/security-runtime/bundle-tools/verify-consumer-evidence.py && \
docker cp "$bundle_container:/out/SHA256SUMS" dist/security-runtime/bundle-tools/SHA256SUMS; then
docker rm "$bundle_container" >/dev/null
else
docker rm "$bundle_container" >/dev/null
exit 1
fi
(cd dist/security-runtime/bundle-tools && \
test "$(grep -Ec '^[0-9a-f]{64} verify-consumer-evidence\.py$' SHA256SUMS)" -eq 1 && \
grep -E '^[0-9a-f]{64} verify-consumer-evidence\.py$' SHA256SUMS | sha256sum --check -)
python3 dist/security-runtime/bundle-tools/verify-consumer-evidence.py \
--evidence-dir dist/security-runtime/ubuntu-backports --sbom "dist/security-runtime/$name-sbom.cdx.json" \
--bundle-checksums dist/security-runtime/bundle-tools/SHA256SUMS \
--expected-bundle-digest 'sha256:2742a3d429e4e14bc587f6dedc51ded4246cf90bbaedcd86c67d0885e32a6f2a' > dist/security-runtime/ubuntu-backports-validation.json
docker run --rm -v "$cache:/root/.cache/trivy" -v "$RUNNER_TEMP/$name.tar:/image.tar:ro" -v "$PWD/dist/security-runtime:/evidence" "$scanner" image --skip-db-update --input /image.tar --scanners vuln,secret --severity UNKNOWN,LOW,MEDIUM,HIGH,CRITICAL --list-all-pkgs --vex /evidence/ubuntu-backports/openvex.json --format json --output "/evidence/$name-security-backports.json"
tar -cf dist/security-runtime/ubuntu-backports-evidence.tar -C dist/security-runtime ubuntu-backports ubuntu-backports-validation.json
done
python3 -B scripts/review-runtime-security.py
python3 -B scripts/enforce-runtime-vulnerabilities.py --phase runtime
(cd dist/security-runtime && sha256sum -- *.json *.txt images.tar.gz > SHA256SUMS)
(cd dist/security-runtime && sha256sum -- *.json *.txt *.tar images.tar.gz > SHA256SUMS)
- name: Upload verified runtime and checksums
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
Expand All @@ -94,7 +125,7 @@ jobs:
packages: write
env:
GH_TOKEN: ${{ github.token }}
RELEASE_TAG: v0.3.2
RELEASE_TAG: v0.3.3
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
Expand All @@ -113,7 +144,7 @@ jobs:
printf '%s' "$GH_TOKEN" | docker login ghcr.io -u "$GITHUB_ACTOR" --password-stdin
trap 'docker logout ghcr.io >/dev/null 2>&1 || true' EXIT
: > dist/security-runtime/registry-images.jsonl
local_image='local/system-image-preloader:v0.3.2'
local_image='local/system-image-preloader:v0.3.3'
remote_image="ghcr.io/pasturestack/system-image-preloader:$RELEASE_TAG"
if docker manifest inspect "$remote_image" >/dev/null 2>"$RUNNER_TEMP/manifest-error"; then
echo "Refusing to replace an existing immutable image tag: $remote_image" >&2; exit 1
Expand Down
17 changes: 17 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
# Modified by PastureStack contributors for independent maintenance and rebranding.

ARG UBUNTU_IMAGE=ubuntu:26.04@sha256:2260313b31c8c011cd2eebe728008efac1b3982be73eb71348ea2648d2c0e09b
FROM ghcr.io/pasturestack/ubuntu-security-backports:v0.1.0@sha256:2742a3d429e4e14bc587f6dedc51ded4246cf90bbaedcd86c67d0885e32a6f2a AS ubuntu_security_backports
FROM ${UBUNTU_IMAGE} AS tool-builder

ARG GO_VERSION=1.27.3
Expand Down Expand Up @@ -182,4 +183,20 @@ LABEL org.opencontainers.image.title="system-image-preloader" \
org.opencontainers.image.version="${IMAGE_VERSION}" \
org.opencontainers.image.revision="${IMAGE_REVISION}"

# Install qualified backports only for packages already in this runtime.
# The immutable read-only build mount adds no installer or interpreter to it.
RUN --mount=from=ubuntu_security_backports,source=/out,target=/opt/pasturestack/ubuntu-security-backports,ro \
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 document in manifest.json qualification.json openvex.json source-inputs.lock.json; do \
cp "/opt/pasturestack/ubuntu-security-backports/${document}" "${evidence}/${document}"; \
done; \
printf '%s\n' 'ghcr.io/pasturestack/ubuntu-security-backports:v0.1.0@sha256:2742a3d429e4e14bc587f6dedc51ded4246cf90bbaedcd86c67d0885e32a6f2a' > "${evidence}/bundle-image.txt"; \
(cd "${evidence}" && sha256sum manifest.json qualification.json openvex.json source-inputs.lock.json install.json verify.json bundle-image.txt > SHA256SUMS); \
dpkg-query -W -f='${binary:Package}\t${Version}\n' | LC_ALL=C sort > /usr/share/pasturestack/ubuntu-security-packages.tsv; \
rm -f /var/cache/ldconfig/aux-cache

ENTRYPOINT ["/usr/local/bin/system-image-preloader"]
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Created by PastureStack contributors for repeatable validation.

VERSION ?= v0.3.2
VERSION ?= v0.3.3
REVISION ?= $(shell git rev-parse HEAD)
IMAGE_REPOSITORY ?= local/system-image-preloader
IMAGE ?= $(IMAGE_REPOSITORY):$(VERSION)
Expand Down
14 changes: 9 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# PastureStack System Image Preloader

Linux runtime **`v0.3.3`** integrates shared Ubuntu 26.04 security backports for already-installed packages while preserving the selected coreutils provider.

The image retains bundle manifests, source inputs and installation receipts under `/usr/share/pasturestack/security/ubuntu-backports/`.

PastureStack System Image Preloader discovers the images required by legacy-compatible system workloads and asks the local Docker daemon to pull images that are not already present.

The runtime is a Bash executable rather than a Go module. Its public package identity is the repository, executable, container image, and OCI metadata name `system-image-preloader`.
Expand All @@ -10,9 +14,9 @@ PastureStack is an independent community effort to preserve, audit, and moderniz

This repository is a derivative maintenance project. It preserves the upstream Git history, authorship, and Apache License 2.0 terms. See [ORIGIN.md](ORIGIN.md) for source attribution and [COMPATIBILITY.md](COMPATIBILITY.md) for the legacy contracts that intentionally retain upstream identifiers.

## Reviewed release
## Runtime and build contract

The `v0.3.2` release provides:
The maintained recipe provides:

- a self-contained public build based on a digest-pinned Ubuntu image;
- the `system-image-preloader` executable and image name;
Expand Down Expand Up @@ -73,7 +77,7 @@ restores the image-presence state. It never performs a broad image prune.
Use the immutable semantic tag:

```text
ghcr.io/pasturestack/system-image-preloader:v0.3.2
ghcr.io/pasturestack/system-image-preloader:v0.3.3
```

The deployment definition must mount `/var/run/docker.sock`, run one instance
Expand Down Expand Up @@ -117,13 +121,13 @@ The repository remains under its existing Apache License 2.0 file. The runtime i

This project is a background service with no user interface or runtime localization subsystem. It therefore does not add locale files. User-facing dashboards that manage this service must provide Traditional Chinese through their own locale framework.

## October 2026 security maintenance
## Security inputs

Source-built yq and gomplate select `golang.org/x/net v0.60.0` for the October HTTP/2 fixes, with x/text 0.42.0, go-git 5.19.2 and grpc-go 1.83.2 where applicable. Their upstream feature versions stay unchanged. The official Docker CLI 29.9.2 is pinned by archive and executable SHA-256 and carries the vendor's patched Go 1.26.10 compiler; its exact source is commit `a82e528901260c8df6073311379eac65f92db204`. CI exercises these exact helpers and scans the actual final runtime.

Maintained Go builds select the patched Go 1.27.3 compiler without raising the existing Go language requirement. Ubuntu build/runtime stages apply the checksum-pinned `ca-certificates 20260816ubuntu0.26.04.1` security package after their frozen APT baseline; the final trust store and installed version are verified. Release artifacts must pass the repository's existing checks before publication.

The 0.3.2 rebuild pins gomplate's affected OpenTelemetry SDK to official 1.45.0
Gomplate selects official OpenTelemetry SDK 1.45.0
(CVE-2026-81870) and adopts the checksum-pinned official Docker CLI 29.9.2,
within the existing 29.x series. The daemon and gomplate 5.1.0 are unchanged.
Every scan severity is retained. Any available fix or unreviewed
Expand Down
2 changes: 1 addition & 1 deletion scripts/integration-smoke
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
set -Eeuo pipefail

PROJECT_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
IMAGE="${IMAGE:-local/system-image-preloader:v0.3.2}"
IMAGE="${IMAGE:-local/system-image-preloader:v0.3.3}"
TEST_PULL_IMAGE="${TEST_PULL_IMAGE:-ghcr.io/pasturestack/container-cron:v0.6.0}"
PLATFORM_VERSION="${PLATFORM_VERSION:-v1.6.281}"
ACCESS_KEY="integration-access"
Expand Down
2 changes: 1 addition & 1 deletion scripts/review-runtime-security.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ def review(report, dependencies, gomplate_info, docker_sha256, image_id):


def main():
report = json.loads((EVIDENCE / 'system-image-preloader-security.json').read_text())
report = json.loads((EVIDENCE / 'system-image-preloader-security-backports.json').read_text())
images = json.loads((EVIDENCE / 'images.json').read_text())
if len(images) != 1 or report.get('Metadata', {}).get('ImageID') != images[0]['Id']:
raise ValueError('scan does not identify the exact built image')
Expand Down
Loading