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
24 changes: 21 additions & 3 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,11 @@ jobs:
-v "$cache:/root/.cache/trivy" \
"$TRIVY_IMAGE" image --skip-db-update --offline-scan \
--scanners vuln,secret --severity UNKNOWN,LOW,MEDIUM,HIGH,CRITICAL \
--exit-code 1 "$RUNTIME_IMAGE"
--format json \
--output "/release/resource-scheduler-${RELEASE_TAG#v}-security.json" "$RUNTIME_IMAGE"
bash scripts/assess-runtime-vulnerabilities \
"$release_dir/resource-scheduler-${RELEASE_TAG#v}-security.json" \
"$release_dir" "$RUNTIME_IMAGE"
docker run --rm --network none \
-v /var/run/docker.sock:/var/run/docker.sock \
-v "$PWD/$release_dir:/release" \
Expand All @@ -108,7 +112,11 @@ jobs:
sha256sum \
"resource-scheduler-${RELEASE_TAG#v}-linux-amd64.tar.xz" \
"resource-scheduler-${RELEASE_TAG#v}-image.cdx.json" \
"resource-scheduler-${RELEASE_TAG#v}-security.json" \
"resource-scheduler-${RELEASE_TAG#v}-ubuntu-packages.tsv" \
runtime-risk-summary.txt \
runtime-vendor-unfixed.tsv \
runtime-vendor-unfixed.openvex.json \
> SHA256SUMS
)

Expand Down Expand Up @@ -146,10 +154,14 @@ jobs:
set -euo pipefail
gh release create "$RELEASE_TAG" --repo "$GITHUB_REPOSITORY" --verify-tag \
--title "Resource Scheduler ${RELEASE_TAG#v}" \
--notes 'Maintained resource scheduler runtime published from a pure numeric version, with tests, vulnerability gate, SBOM, package inventory, checksums, and build provenance.' \
--notes 'Maintained resource scheduler runtime published from a pure numeric version, with tests, SBOM, package inventory, full vulnerability evidence, an explicit register of vendor-unfixed Low/Medium findings, checksums, and build provenance.' \
"dist/release/resource-scheduler-${RELEASE_TAG#v}-linux-amd64.tar.xz" \
"dist/release/resource-scheduler-${RELEASE_TAG#v}-image.cdx.json" \
"dist/release/resource-scheduler-${RELEASE_TAG#v}-security.json" \
"dist/release/resource-scheduler-${RELEASE_TAG#v}-ubuntu-packages.tsv" \
dist/release/runtime-risk-summary.txt \
dist/release/runtime-vendor-unfixed.tsv \
dist/release/runtime-vendor-unfixed.openvex.json \
dist/release/SHA256SUMS

- name: Clean run-owned resources
Expand All @@ -158,9 +170,15 @@ jobs:
run: |
set +e
docker logout ghcr.io >/dev/null 2>&1
owner="$(id -u):$(id -g)"
cache="$RUNNER_TEMP/resource-scheduler-release-trivy-cache"
if [ -d "$cache" ] && docker image inspect "$TRIVY_IMAGE" >/dev/null 2>&1; then
docker run --rm --volume "$cache:/target" --entrypoint chown \
"$TRIVY_IMAGE" -R "$owner" /target >/dev/null 2>&1
fi
for image in "${RUNTIME_IMAGE:-}" "$DAPPER_IMAGE" "$TRIVY_IMAGE"; do
if [ -n "$image" ] && docker image inspect "$image" >/dev/null 2>&1; then
docker image rm -f "$image" >/dev/null 2>&1
fi
done
rm -rf -- bin dist "$RUNNER_TEMP/resource-scheduler-release-trivy-cache"
rm -rf -- bin dist "$cache"
14 changes: 11 additions & 3 deletions .github/workflows/supply-chain.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ jobs:
runs-on: ubuntu-24.04
timeout-minutes: 90
env:
CANDIDATE_VERSION: v0.8.17
CANDIDATE_VERSION: v0.8.18
DAPPER_IMAGE: local/resource-scheduler-dapper:${{ github.sha }}
TRIVY_IMAGE: aquasec/trivy:0.74.0@sha256:62b1e65e8869bc4b4c6aa4fa2b21595256c7c2f6018a9d9ad61caf87187c1969

Expand Down Expand Up @@ -53,6 +53,7 @@ jobs:
scripts/build \
scripts/entry \
scripts/package \
scripts/assess-runtime-vulnerabilities \
scripts/check-build-downloads \
scripts/check-migration-policy \
.github/workflows/release.yml \
Expand Down Expand Up @@ -248,6 +249,11 @@ jobs:
runtime_secrets="$(jq '[.Results[]?.Secrets[]?] | length' evidence/runtime-security.json)"
runtime_critical="$(jq '[.Results[]?.Vulnerabilities[]? | select(.Severity == "CRITICAL")] | length' evidence/runtime-security.json)"
runtime_high="$(jq '[.Results[]?.Vulnerabilities[]? | select(.Severity == "HIGH")] | length' evidence/runtime-security.json)"
runtime_vulnerabilities="$(jq '[.Results[]?.Vulnerabilities[]?] | length' evidence/runtime-security.json)"
bash scripts/assess-runtime-vulnerabilities \
evidence/runtime-security.json evidence "$RUNTIME_IMAGE"
runtime_vendor_unfixed="$(sed -n 's/^runtime_vendor_unfixed_low_medium=//p' evidence/runtime-risk-summary.txt)"
runtime_actionable="$(sed -n 's/^runtime_actionable=//p' evidence/runtime-risk-summary.txt)"
dapper_secrets="$(jq '[.Results[]?.Secrets[]?] | length' evidence/dapper-security.json)"
dapper_unreviewed="$(jq '[.Results[]?.Vulnerabilities[]? |
select(.Severity == "CRITICAL" or .Severity == "HIGH") |
Expand Down Expand Up @@ -285,11 +291,13 @@ jobs:
}
]
}' > evidence/dapper.openvex.json
printf 'source_secrets=%s\nruntime_secrets=%s\nruntime_critical=%s\nruntime_high=%s\ndapper_secrets=%s\ndapper_unreviewed=%s\n' \
"$source_secrets" "$runtime_secrets" "$runtime_critical" "$runtime_high" \
printf 'source_secrets=%s\nruntime_secrets=%s\nruntime_vulnerabilities=%s\nruntime_vendor_unfixed=%s\nruntime_actionable=%s\nruntime_critical=%s\nruntime_high=%s\ndapper_secrets=%s\ndapper_unreviewed=%s\n' \
"$source_secrets" "$runtime_secrets" "$runtime_vulnerabilities" "$runtime_vendor_unfixed" "$runtime_actionable" "$runtime_critical" "$runtime_high" \
"$dapper_secrets" "$dapper_unreviewed" | tee evidence/security-summary.txt
test "$source_secrets" -eq 0
test "$runtime_secrets" -eq 0
test "$runtime_actionable" -eq 0
test "$runtime_vulnerabilities" -eq "$runtime_vendor_unfixed"
test "$runtime_critical" -eq 0
test "$runtime_high" -eq 0
test "$dapper_secrets" -eq 0
Expand Down
1 change: 1 addition & 0 deletions Dockerfile.dapper
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ RUN set -eux; \
"${snapshot_uri}" > /etc/apt/sources.list.d/pasturestack-snapshot.sources; \
printf 'Acquire::Retries "5";\nAcquire::http::Timeout "30";\nAcquire::https::Timeout "30";\nAcquire::https::CaInfo "/etc/ssl/certs/ca-certificates.crt";\nAcquire::https::Verify-Peer "true";\nAcquire::https::Verify-Host "true";\nAcquire::http::Pipeline-Depth "0";\nAcquire::AllowInsecureRepositories "false";\nAPT::Get::AllowUnauthenticated "false";\n' > /etc/apt/apt.conf.d/80pasturestack-retries; \
apt-get update && \
DEBIAN_FRONTEND=noninteractive apt-get upgrade -y --no-install-recommends && \
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
bash="${UBUNTU_APT_BASH_VERSION}" \
ca-certificates="${UBUNTU_APT_CA_CERTIFICATES_VERSION}" \
Expand Down
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ PastureStack is an independent community effort to preserve, audit, and moderniz

**Upstream:** [`rancher/scheduler`](https://github.com/rancher/scheduler). This GitHub fork retains the upstream Git history, authorship, dates, and license notices. PastureStack maintenance is consolidated into one commit after the preserved upstream boundary.

The current public release and Catalog image are `v0.8.17`.
The current release candidate and next Catalog image are `v0.8.18`.

## Runtime image

Expand All @@ -24,17 +24,17 @@ The native command is `resource-scheduler`. A `scheduler` executable symlink rem

## Build and test

The reviewed build uses Go 1.27.0 to compile Docker CLI 29.7.2 and Docker Buildx 0.36.1 from checksum-pinned source. A small checksum-locked Buildx patch removes its sole compiled dependency on the legacy Docker module; the two security-sensitive Buildx modules are then pinned to `github.com/moby/go-archive` 0.3.0 and `golang.org/x/mod` 0.40.0, with the final binary metadata checked in CI. The Ubuntu base image is digest-pinned; direct packages are version-pinned in `ubuntu-apt.lock` against the fixed `20260909T000000Z` Canonical snapshot, and each built image records the complete resolved `dpkg` inventory. BuildKit receives the source commit time through `SOURCE_DATE_EPOCH`, and CI rejects differing binary hashes or image IDs across clean rebuilds.
The reviewed build uses Go 1.27.0 to compile Docker CLI 29.7.2 and Docker Buildx 0.36.1 from checksum-pinned source. A small checksum-locked Buildx patch removes its sole compiled dependency on the legacy Docker module; the two security-sensitive Buildx modules are then pinned to `github.com/moby/go-archive` 0.3.0 and `golang.org/x/mod` 0.40.0, with the final binary metadata checked in CI. The Ubuntu base image is digest-pinned; all inherited base packages are updated from the fixed `20260909T000000Z` Canonical snapshot before the direct packages pinned in `ubuntu-apt.lock` are installed, and each built image records the complete resolved `dpkg` inventory. BuildKit receives the source commit time through `SOURCE_DATE_EPOCH`, and CI rejects differing binary hashes or image IDs across clean rebuilds.

The dependency graph is declared in `go.mod`, checksum-bound by `go.sum`, and committed in the standard module-aware `vendor` tree for reproducible offline builds. Security CI produces short-lived source and runtime CycloneDX SBOMs, runs binary reachability analysis, and blocks runtime Critical or High vulnerabilities and detected secrets.
The dependency graph is declared in `go.mod`, checksum-bound by `go.sum`, and committed in the standard module-aware `vendor` tree for reproducible offline builds. Security CI produces short-lived source and runtime CycloneDX SBOMs, runs binary reachability analysis, and blocks detected secrets, every High/Critical runtime finding, and any finding for which Ubuntu has published a fixed package. Low/Medium findings without an available Ubuntu fix remain visible as `under_investigation` in the release risk register and OpenVEX artifact; they are not represented as resolved and are re-evaluated when the vendor, base digest, or vulnerability data changes.

```bash
make test
make validate
bash scripts/check-build-downloads
go list -mod=vendor ./...
bash scripts/check-migration-policy
VERSION_OVERRIDE=v0.8.17 IMAGE_NAMESPACE=pasturestack make package
VERSION_OVERRIDE=v0.8.18 IMAGE_NAMESPACE=pasturestack make package
```

CI validates source and dependency locks, tests and reproducible builds, and generates short-lived security evidence. Releases are published only from an annotated, pure numeric SemVer tag that resolves to the reviewed commit.
Expand Down
9 changes: 9 additions & 0 deletions RELEASE_NOTES.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
# Release Notes

## 0.8.18

- Update every runtime package available from the fixed Ubuntu snapshot.
- Preserve the full all-severity runtime scan and explicitly register Low/Medium
findings for which Ubuntu has not published a fixed package; keep them marked
`under_investigation` instead of weakening the scan or claiming resolution.
- Block release whenever a fixed package is available or a High/Critical
runtime finding remains.

## 0.8.17

- Update the checksum-pinned Go, Docker CLI, Buildx, gRPC, and Ubuntu package
Expand Down
3 changes: 2 additions & 1 deletion SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ Resource Scheduler receives scoped control-plane credentials, consumes host and
- Apply the checksum-recorded Buildx patch and reject the build if the resulting binary still records the legacy Docker module.
- Keep the runtime base image digest-pinned. Resolve exact direct-package versions from the dated Canonical snapshot in `ubuntu-apt.lock`, and retain the complete resolved `dpkg` inventory in each build and runtime image.
- Verify that `go.mod`, `go.sum`, and `vendor/modules.txt` agree by compiling and testing with `-mod=vendor`.
- Run unit tests, race tests, `go vet`, formatting checks, build-policy checks, migration-policy checks, secret scanning, an SBOM inventory, and High/Critical vulnerability scanning before publishing.
- Run unit tests, race tests, `go vet`, formatting checks, build-policy checks, migration-policy checks, secret scanning, an SBOM inventory, and all-severity vulnerability scanning before publishing.
- Block every runtime High/Critical finding and every finding with a vendor-published fixed version. Preserve vendor-unfixed Low/Medium findings in the release scan, risk register, and OpenVEX as `under_investigation`; never label them fixed or not affected.
- A High or Critical finding may be classified as not affected only when CI proves it is an unfixed `linux-libc-dev` header finding in the disposable builder, emits exact-package OpenVEX evidence that expires on 2026-09-15, and proves the package is absent from the runtime image. Any fixed or different builder finding remains blocking.
- Publish a new immutable version when source or dependencies change; do not replace an existing release digest.

Expand Down
1 change: 1 addition & 0 deletions package/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ RUN set -eux; \
"${snapshot_uri}" > /etc/apt/sources.list.d/pasturestack-snapshot.sources; \
printf 'Acquire::Retries "5";\nAcquire::http::Timeout "30";\nAcquire::https::Timeout "30";\nAcquire::https::CaInfo "/etc/ssl/certs/ca-certificates.crt";\nAcquire::https::Verify-Peer "true";\nAcquire::https::Verify-Host "true";\nAcquire::http::Pipeline-Depth "0";\nAcquire::AllowInsecureRepositories "false";\nAPT::Get::AllowUnauthenticated "false";\n' > /etc/apt/apt.conf.d/80pasturestack-retries; \
apt-get update; \
DEBIAN_FRONTEND=noninteractive apt-get upgrade -y --no-install-recommends; \
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
bash="${UBUNTU_APT_BASH_VERSION}" \
ca-certificates="${UBUNTU_APT_CA_CERTIFICATES_VERSION}" \
Expand Down
58 changes: 58 additions & 0 deletions scripts/assess-runtime-vulnerabilities
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
#!/bin/bash
set -euo pipefail

report=${1:?usage: assess-runtime-vulnerabilities REPORT_JSON OUTPUT_DIR SUBJECT}
output_dir=${2:?usage: assess-runtime-vulnerabilities REPORT_JSON OUTPUT_DIR SUBJECT}
subject=${3:?usage: assess-runtime-vulnerabilities REPORT_JSON OUTPUT_DIR SUBJECT}

mkdir -p "${output_dir}"

total=$(jq '[.Results[]?.Vulnerabilities[]?] | length' "${report}")
secrets=$(jq '[.Results[]?.Secrets[]?] | length' "${report}")
pending=$(jq '[.Results[]?.Vulnerabilities[]? |
select((.Severity == "LOW" or .Severity == "MEDIUM") and ((.FixedVersion // "") == ""))] | length' "${report}")
actionable=$(jq '[.Results[]?.Vulnerabilities[]? |
select((.Severity != "LOW" and .Severity != "MEDIUM") or ((.FixedVersion // "") != ""))] | length' "${report}")

jq -r '.Results[]?.Vulnerabilities[]? |
select((.Severity == "LOW" or .Severity == "MEDIUM") and ((.FixedVersion // "") == "")) |
[.VulnerabilityID, .PkgName, .InstalledVersion, (.FixedVersion // ""), .Severity, (.PkgIdentifier.PURL // "")] | @tsv' \
"${report}" | LC_ALL=C sort -u > "${output_dir}/runtime-vendor-unfixed.tsv"

jq -n \
--slurpfile report "${report}" \
--arg subject "${subject}" \
--arg timestamp "$(date -u +'%Y-%m-%dT%H:%M:%SZ')" \
'{
"@context":"https://openvex.dev/ns/v0.2.0",
"@id":"https://github.com/PastureStack/resource-scheduler/security/openvex/runtime-vendor-unfixed",
author:"PastureStack Security",
timestamp:$timestamp,
version:1,
"x-pasturestack-policy":"Pending vendor fix; re-evaluate when Ubuntu publishes a fixed package, the base digest changes, or vulnerability intelligence changes.",
statements:[
$report[0].Results[]?.Vulnerabilities[]?
| select((.Severity == "LOW" or .Severity == "MEDIUM") and ((.FixedVersion // "") == ""))
| {
vulnerability:{name:.VulnerabilityID},
products:[{"@id":$subject}],
status:"under_investigation",
impact_statement:("Ubuntu has not published a fixed version for " + .PkgName + " " + .InstalledVersion + "; the finding remains registered and is not represented as resolved.")
}
]
}' > "${output_dir}/runtime-vendor-unfixed.openvex.json"

printf 'runtime_secrets=%s\nruntime_vulnerabilities=%s\nruntime_vendor_unfixed_low_medium=%s\nruntime_actionable=%s\n' \
"${secrets}" "${total}" "${pending}" "${actionable}" | tee "${output_dir}/runtime-risk-summary.txt"

test "${secrets}" -eq 0
test "${total}" -eq "${pending}"
test "${actionable}" -eq 0
test "$(jq '.statements | length' "${output_dir}/runtime-vendor-unfixed.openvex.json")" -eq "${pending}"
jq -e 'all(.statements[];
.status == "under_investigation" and
(.products | length) == 1 and
(.impact_statement | contains("not represented as resolved")))' \
"${output_dir}/runtime-vendor-unfixed.openvex.json" >/dev/null

echo "RESOURCE_SCHEDULER_RUNTIME_RISK_OK secrets=0 total=${total} vendor_unfixed=${pending} actionable=0"
Loading