diff --git a/README.md b/README.md index 07c14bd..e554d36 100644 --- a/README.md +++ b/README.md @@ -367,26 +367,31 @@ host with `qemu-system-x86_64` for VM-backed runs. and the static guest validator, verifies checksums plus either the release workflow's Sigstore signature or GitHub build attestations, and installs the validator where `bpfcompat test` finds it automatically. Verification is -fail-closed, so the host needs `cosign` or an authenticated GitHub CLI with -attestation support. On arm64 the CLI installs but the static validator is not -published yet, so artifact-mode VM validation there needs -`make validator-static` or command mode: +fail-closed, so the host needs `cosign` or an authenticated GitHub CLI 2.68.0 +or later (older gh lacks `--source-ref`; see +[verifying releases](docs/verifying-releases.md)). On arm64 the CLI installs +but the static validator is not published yet, so artifact-mode VM validation +there needs `make validator-static` or command mode: ```bash curl -fsSL https://raw.githubusercontent.com/Kernel-Guard/bpfcompat/main/scripts/install.sh | sh ``` -Pin or redirect with `BPFCOMPAT_VERSION`, `BPFCOMPAT_BIN_DIR`, and -`BPFCOMPAT_LIBEXEC_DIR` (see [`scripts/install.sh`](scripts/install.sh)). A -pinned version must be an exact release tag (`vX.Y.Z` or `vX.Y.Z-rc.N`), and its -signature or attestation must come from that tag's release run. A validator -installed outside `/usr/local/libexec/bpfcompat` is not auto-discovered: point -`BPFCOMPAT_VALIDATOR_BIN` at it. +Pin or redirect with `BPFCOMPAT_VERSION`, `BPFCOMPAT_BIN_DIR`, +`BPFCOMPAT_LIBEXEC_DIR`, and `BPFCOMPAT_DOC_DIR` (for releases that publish a +third-party license bundle, it is installed under `//`; +the default directory is `/../share/doc/bpfcompat`; see +[`scripts/install.sh`](scripts/install.sh)). A pinned version must be an exact +release tag (`vX.Y.Z` or `vX.Y.Z-rc.N`), and its signature or attestation must +come from that tag's release run. A validator installed outside +`/usr/local/libexec/bpfcompat` or `/usr/libexec/bpfcompat` is not +auto-discovered: point `BPFCOMPAT_VALIDATOR_BIN` at it. **1. Prebuilt release binary (Linux x86_64 / arm64).** Installs the CLI *and* the static validator (to a location the CLI auto-discovers), checksum- and -attestation-verified. The validator is currently published for amd64 only; on -arm64 use the CLI plus `make validator-static` or command mode: +attestation-verified (the `gh attestation verify` flags below need gh 2.68.0 or +later). The validator is currently published for amd64 only; on arm64 use the +CLI plus `make validator-static` or command mode: ```bash VER=v0.3.7 @@ -396,10 +401,12 @@ curl -fsSLO "$base/bpfcompat-validator-static-linux-amd64" curl -fsSLO "$base/SHA256SUMS" gh attestation verify ./bpfcompat-linux-amd64 \ --repo Kernel-Guard/bpfcompat \ - --signer-workflow Kernel-Guard/bpfcompat/.github/workflows/release-artifacts.yml + --signer-workflow Kernel-Guard/bpfcompat/.github/workflows/release-artifacts.yml \ + --source-ref "refs/tags/$VER" gh attestation verify ./bpfcompat-validator-static-linux-amd64 \ --repo Kernel-Guard/bpfcompat \ - --signer-workflow Kernel-Guard/bpfcompat/.github/workflows/release-artifacts.yml + --signer-workflow Kernel-Guard/bpfcompat/.github/workflows/release-artifacts.yml \ + --source-ref "refs/tags/$VER" sha256sum -c SHA256SUMS --ignore-missing sudo install -m 0755 bpfcompat-linux-amd64 /usr/local/bin/bpfcompat sudo install -D -m 0755 bpfcompat-validator-static-linux-amd64 \ @@ -444,7 +451,7 @@ multi-arch image (amd64 + arm64), published per release: docker run --rm ghcr.io/kernel-guard/bpfcompat:latest version ``` -Tags follow the release without the `v` prefix (`0.3.6`, `0.3`, `latest`). The +Tags follow the release without the `v` prefix (`0.3.7`, `0.3`, `latest`). The image does not bundle QEMU or the validator and therefore is not a standalone VM-validation environment. Use option 0 or the GitHub Action for a kernel matrix. The experimental `serve` command remains present but does not start by @@ -452,8 +459,21 @@ default and is outside the production support boundary. Release candidates use an exact tag such as `0.4.0-rc.1`; they never update the stable minor or `latest` aliases and are not returned by the installer's default latest-release lookup. -Verify provenance with -`cosign verify ghcr.io/kernel-guard/bpfcompat:0.3.7 --certificate-identity-regexp github.com/Kernel-Guard/bpfcompat --certificate-oidc-issuer https://token.actions.githubusercontent.com`. +Verify provenance with the exact signer identity of that release: + +```bash +cosign verify ghcr.io/kernel-guard/bpfcompat:0.3.7 \ + --certificate-identity https://github.com/Kernel-Guard/bpfcompat/.github/workflows/ghcr-image.yml@refs/tags/v0.3.7 \ + --certificate-oidc-issuer https://token.actions.githubusercontent.com +``` + +The signer workflow differs by release: the 0.3.5 to 0.3.7 images (and the +`0.3` and `latest` aliases while they point at 0.3.7) were signed by +`ghcr-image.yml`, while the 0.3.8-rc.1, 0.3.8-rc.2, 0.4.0-rc.2, 0.4.0-rc.3 and +0.4.0-rc.4 images, and later images from the release workflow, are signed by +`release-artifacts.yml` +(`--certificate-identity https://github.com/Kernel-Guard/bpfcompat/.github/workflows/release-artifacts.yml@refs/tags/v`). +Change the identity together with the image tag. ### What a run looks like @@ -813,9 +833,12 @@ Operator guidance: - **Risk scoring:** OpenSSF Scorecard (`scorecard.yml`), published to the public Scorecard API (badge above). - **Signed releases + SLSA provenance:** tag builds produce a CycloneDX SBOM, - cosign keyless (Sigstore OIDC) signatures over `SHA256SUMS` and the SBOM, - plus SLSA provenance attestations for the binaries and container bound to the - producing commit and workflow (`release-artifacts.yml`). Quick check: + cosign keyless (Sigstore OIDC) signatures over `SHA256SUMS` (and, from + v0.4.0-rc.2 and v0.3.8-rc.1, the SBOM; not in v0.3.7), plus SLSA provenance + attestations for the binaries and container bound to the producing commit + and workflow (`release-artifacts.yml`; the 0.3.5 to 0.3.7 images were signed + and attested by `ghcr-image.yml`). Quick check (it checks the repository only; the full + steps also bind the release workflow and tag): ```bash gh attestation verify ./bpfcompat-linux-amd64 --repo Kernel-Guard/bpfcompat diff --git a/action.yml b/action.yml index a12b288..f1e1206 100644 --- a/action.yml +++ b/action.yml @@ -8,7 +8,7 @@ inputs: required: false default: "" artifact: - description: "Local path to a compiled .bpf.o artifact, or a remote OCI reference the CLI pulls (for example ghcr.io/org/gadget:latest, host:port/repo, or an @sha256: digest)." + description: "Local path to a compiled .bpf.o artifact, or a remote OCI reference the CLI pulls (for example ghcr.io/org/gadget:latest, host:port/repo, or an @sha256: digest). Prefix workspace paths whose first directory contains `.` or `:` or is `localhost` with `./` (for example ./.output/minimal.bpf.o); otherwise they are treated as remote references." required: false default: "" command: @@ -80,7 +80,7 @@ inputs: required: false default: "auto" binary: - description: Path to bpfcompat binary. Relative paths are resolved from the caller repository root. + description: Path to bpfcompat binary. Relative paths are resolved from the caller repository root. An executable at this path in the workspace is used instead of the verified prebuilt CLI. required: false default: "./bin/bpfcompat" baseline-report: diff --git a/docs/compatibility-contract.md b/docs/compatibility-contract.md index c657395..95a0ed7 100644 --- a/docs/compatibility-contract.md +++ b/docs/compatibility-contract.md @@ -46,16 +46,18 @@ A static libbpf validator loads the object and optionally attaches it. Provenance: `validator.basename`, `validator.sha256`, `validator.size_bytes` — the same staged bytes are reused for every target in the run. -When the GitHub Action is pinned to a release (a tag, or a commit that maps to -one) and the prebuilt assets verify, the run uses that attested validator, -selected by path and pinned digest: a validator installed elsewhere on the -runner is not used, and other bytes fail closed. A workflow that sets -`BPFCOMPAT_VALIDATOR_BIN` or `BPFCOMPAT_VALIDATOR_SHA256` itself keeps its own -settings, with a warning, and every fallback to a source build while pinned to -a release is a `::warning::` annotation. Neither of those runs is pinned to the -qualified release validator (on a source-build fallback a validator installed -under `/usr/local/libexec` or `/usr/libexec` is still searched first); compare -`validator.sha256` with the release `SHA256SUMS` when it matters. +From v0.4.0-rc.5, when the GitHub Action is pinned to a release (a tag, or a +commit that maps to one) and the prebuilt assets verify, the run uses that +attested validator, selected by path and pinned digest: a validator installed +elsewhere on the runner is not used, and other bytes fail closed. A workflow +that sets `BPFCOMPAT_VALIDATOR_BIN` or `BPFCOMPAT_VALIDATOR_SHA256` itself +keeps its own settings, with a warning, and every fallback to a source build +while pinned to a release is a `::warning::` annotation. Neither of those runs +is pinned to the qualified release validator (on a source-build fallback a +validator installed under `/usr/local/libexec` or `/usr/libexec` is still +searched first); compare `validator.sha256` with the release `SHA256SUMS` when +it matters. With v0.3.7 and v0.4.0-rc.4 that search order applies to every run, +so a validator installed in those directories is used before the verified one. ### The project's own loader (command mode) @@ -77,9 +79,11 @@ Provenance: | `targets[].functional.tests[0].{stdout_tail,stderr_tail}` | bounded diagnostic output | A command-mode target is tied to a kernel by the guest kernel release recorded -before the command starts. If that release cannot be read (or is empty), the -command is not started and the target is `INFRA_ERROR`, never `COMPATIBLE` or -`INCOMPATIBLE`; a required target then fails the run closed. +before the command starts. From v0.4.0-rc.5, if that release cannot be read (or +is empty), the command is not started and the target is `INFRA_ERROR`, never +`COMPATIBLE` or `INCOMPATIBLE`; a required target then fails the run closed. +Earlier releases record the release on a best-effort basis and run the command +even when it cannot be read. Only the invocation bpfcompat was given is recorded. Guest environment contents are not captured, and output is truncated to tails. Do not put secrets in the @@ -271,7 +275,9 @@ Known limitations of the supported path: `localhost/prog.o` are pulled. Spell local files with `./` or an absolute path. Docker Hub images need an explicit `docker.io/` host for tag references; a short digest reference (`name@sha256:…`) is still pulled from - Docker Hub. + Docker Hub. These rules apply from v0.4.0-rc.5; earlier releases treat any + missing path that parses as an image reference as one and try to pull it + (for example `build/prog.bpf.o` from Docker Hub). - **Action `artifact` input.** The Action decides "remote" before looking at the workspace. An input that contains `://` or `@sha256:`, that is `localhost` or starts with `localhost:` or `localhost/`, or whose first @@ -279,7 +285,8 @@ Known limitations of the supported path: unchanged even when a file of that name exists in the workspace. The CLI runs from the Action's own checkout, so such a path is not looked up in the workspace. Dot-directories are caught by this rule too: `.output/minimal.bpf.o` - (the libbpf-bootstrap layout) fails with "no such file", and + (the libbpf-bootstrap layout) fails with "no such file" (from v0.4.0-rc.5; + earlier releases read it as an image on registry host `.output`), and `.output/minimal.o` is read as an image on registry host `.output`. Prefix workspace files with `./` (for example `./.output/minimal.bpf.o` or `./out.v2/prog.o`). diff --git a/docs/production-support-boundary.md b/docs/production-support-boundary.md index e3eb859..80aa44f 100644 --- a/docs/production-support-boundary.md +++ b/docs/production-support-boundary.md @@ -54,9 +54,11 @@ and do not provide evidence that the surface is production-ready. A passing result means the recorded artifact or command loader completed the requested load, attach, or behavior contract on the exact guest image bytes -recorded in that report. Command-mode reports record the invocation digest and -the loader binary's name, size, and SHA-256. Artifact-mode reports record the -same identity fields for the staged libbpf validator. +recorded in that report. Command-mode reports record the invocation digest, +the loader binary's name, size, and SHA-256, and the guest kernel release; from +v0.4.0-rc.5 a command-mode target whose guest kernel release cannot be recorded +is an infrastructure error. Artifact-mode reports record the same identity +fields for the staged libbpf validator. A pass is not a certification of: @@ -76,18 +78,27 @@ Every supported release must: 1. pass unit, race, vet, vulnerability, generated-doc, and release-consistency checks; 2. build candidate binaries once and record their checksums; -3. run the exact candidate CLI and static validator in a pinned-image positive +3. build the static validator in the digest-pinned builder from + `validator/build-inputs.lock` and, when the validator tree is unchanged, + reproduce the lock's recorded validator byte for byte (from v0.4.0-rc.5); +4. for a stable release only, ship exactly the qualified release candidate's + validator bytes (enforced by the release workflow from v0.4.0-rc.5 on; + release candidates do not run this gate); +5. for a stable release only, have every stable-blocking distribution decision + in `distribution/policy.json` recorded (enforced by the release workflow + from v0.4.0-rc.5 on; release candidates do not run this gate); +6. run the exact candidate CLI and static validator in a pinned-image positive VM test; -4. produce the expected classification from an incompatible artifact; -5. execute the exact ARM64 CLI candidate on a native ARM64 runner; -6. build and version-check a candidate multi-architecture container; -7. bind the release inputs and VM results into an attested candidate-evidence - record; -8. publish signed checksums, SBOM, and build provenance; -9. finish as a private draft without an automatic publication path; -10. receive a separate exact-input manual promotion dispatch through the +7. produce the expected classification from an incompatible artifact; +8. execute the exact ARM64 CLI candidate on a native ARM64 runner; +9. build and version-check a candidate multi-architecture container; +10. bind the release inputs and VM results into an attested candidate-evidence + record; +11. publish signed checksums, SBOM, and build provenance; +12. finish as a private draft without an automatic publication path; +13. receive a separate exact-input manual promotion dispatch through the 15-minute `production-release` environment; and -11. re-verify the run, tag, commit, assets, attestations, signatures, channel, +14. re-verify the run, tag, commit, assets, attestations, signatures, channel, image digest, and version before making the draft release public. The release metadata source is [`release.yaml`](../release.yaml). It records diff --git a/docs/quickstart.md b/docs/quickstart.md index 3b77c9f..11dd35a 100644 --- a/docs/quickstart.md +++ b/docs/quickstart.md @@ -67,10 +67,11 @@ Shipping a whole product? Use **suite mode** to gate a collection in one run: The Action is on the [GitHub Marketplace](https://github.com/marketplace/actions/bpfcompat-ebpf-compatibility-gate). Pinning to a release tag — or to the commit SHA a release tag points at — uses -checksum-verified, attested prebuilt binaries -([verifying releases](verifying-releases.md)); otherwise it builds from source, -which needs `libbpf-dev`, `libelf-dev`, `zlib1g-dev`, and `pkg-config` on the -runner. +checksum-verified prebuilt binaries; from v0.4.0-rc.2 and v0.3.8-rc.1 (not in +v0.3.7) the Action also verifies their build attestations +([verifying releases](verifying-releases.md)). +Otherwise it builds from source, which needs `libbpf-dev`, `libelf-dev`, +`zlib1g-dev`, and `pkg-config` on the runner. ## 2. Run it locally (CLI) diff --git a/docs/supply-chain.md b/docs/supply-chain.md index 30caf46..75b2a9d 100644 --- a/docs/supply-chain.md +++ b/docs/supply-chain.md @@ -90,14 +90,15 @@ and the `reproduces=` line from a pinned build, and cut a new release candidate: that changes the shipped validator. `dist/validator-build-inputs.json` is the validator's build record. It is -attested with the candidate provenance and, from the release that introduces the -distribution bundle on, shipped as a release asset listed in `SHA256SUMS`, so it -outlives the workflow run artifact's retention. +attested with the candidate provenance and, from v0.4.0-rc.5 on, shipped as a +release asset listed in `SHA256SUMS`, so it outlives the workflow run +artifact's retention. ### Third-party license bundle and relink kit -The same build produces two more release assets, both listed in `SHA256SUMS`, -attested, and reproducible byte for byte from the same inputs: +From v0.4.0-rc.5 (not in v0.3.7 or v0.4.0-rc.4), the same build produces two +more release assets, both listed in `SHA256SUMS`, attested, and reproducible +byte for byte from the same inputs: - `bpfcompat-third-party-licenses.tar.gz`: the license, notice and patent texts (verbatim) of every Go module linked into the shipped CLIs, taken from each @@ -109,8 +110,9 @@ attested, and reproducible byte for byte from the same inputs: - `bpfcompat-validator-relink-kit-linux-amd64.tar.gz`: the validator's `main.o`, the Makefile recipe split into compile and link commands, the linker's member trace and map, every link input and header with its owning package, the build - record and locks, the validator sources and `relink.sh`. The build refuses - unless relinking `main.o` reproduces the shipped validator byte for byte. + record and locks, the validator's own C sources and `relink.sh`. The build + refuses unless relinking `main.o` reproduces the shipped validator byte for + byte. `distribution/policy.json` records the open distribution decisions. Two of them add assets when decided: `bpfcompat-validator-link-inputs-linux-amd64.tar.gz` @@ -122,19 +124,26 @@ license or states that any license condition is met. The installer and the GitHub Action install the license bundle when the release's `SHA256SUMS` lists it, and the container image carries the Go texts -in `/usr/share/doc/bpfcompat/`. +in `/usr/share/doc/bpfcompat/`. The installer served from `main` already does +this; the GitHub Action and the container image do from v0.4.0-rc.5. ### Verifying a signed release ```bash +VER=v0.3.7 # the exact release tag you downloaded cosign verify-blob \ --certificate SHA256SUMS.crt --signature SHA256SUMS.sig \ - --certificate-identity-regexp '^https://github.com/Kernel-Guard/bpfcompat/.github/workflows/release-artifacts.yml@refs/tags/v' \ + --certificate-identity "https://github.com/Kernel-Guard/bpfcompat/.github/workflows/release-artifacts.yml@refs/tags/$VER" \ --certificate-oidc-issuer https://token.actions.githubusercontent.com \ SHA256SUMS -sha256sum -c SHA256SUMS +sha256sum -c --ignore-missing SHA256SUMS ``` +The identity names the exact tag, as `scripts/install.sh` does, so a signature +made by another tag's release run (for example a prerelease) is refused. The +tag-bound attestation checks are in +[verifying-releases.md](verifying-releases.md). + ## Maintainer-side settings (configure once, in the GitHub UI/API) These are not files in the repo. Track their status here. diff --git a/docs/verifying-releases.md b/docs/verifying-releases.md index 0e6f81a..8f6e11f 100644 --- a/docs/verifying-releases.md +++ b/docs/verifying-releases.md @@ -1,47 +1,61 @@ # Verifying bpfcompat releases Every tagged release of bpfcompat ships with a full supply-chain bundle so you can -prove a binary came from this repository's build workflow before you run it: +prove a binary came from this repository's build workflow before you run it. +Rows that name a version are not in earlier releases: | Artifact | What it is | |---|---| -| `bpfcompat-linux-amd64`, `bpfcompat-validator-static-linux-amd64` | the binaries | +| `bpfcompat-linux-amd64`, `bpfcompat-linux-arm64`, `bpfcompat-validator-static-linux-amd64` | the binaries (the static validator is published for amd64 only) | | `SHA256SUMS` (+ `.sig`, `.crt`) | checksums, cosign-signed (keyless) | | `bpfcompat.sbom.cdx.json` | CycloneDX SBOM | -| `release-candidate-evidence.json` | Tag, commit, workflow run, exact image digest, and positive/negative VM report hashes | -| `bpfcompat-third-party-licenses.tar.gz` | verbatim license/notice/patent texts of the third-party code in the binaries, with `INVENTORY.json` (from the release that introduces it) | -| `bpfcompat-validator-relink-kit-linux-amd64.tar.gz` | the validator's `main.o`, link commands, linker trace and map, inputs with owning packages, sources and `relink.sh` | -| `validator-build-inputs.json` | the validator's build record: builder image digest, package snapshot, package versions, sha256 | +| `release-candidate-evidence.json` | Tag, commit, workflow run, exact image digest, and positive/negative VM report hashes (from v0.4.0-rc.2 and v0.3.8-rc.1; not in v0.3.7) | +| `bpfcompat-third-party-licenses.tar.gz` | verbatim license/notice/patent texts of the third-party code in the binaries, with `INVENTORY.json` (from v0.4.0-rc.5) | +| `bpfcompat-validator-relink-kit-linux-amd64.tar.gz` | the validator's `main.o`, link commands, linker trace and map, inputs with owning packages, the validator's own C sources and `relink.sh` (from v0.4.0-rc.5) | +| `validator-build-inputs.json` | the validator's build record: builder image digest, package snapshot, package versions, sha256 (from v0.4.0-rc.5) | | **SLSA provenance attestation** | provenance bound to the artifact digest, repository, commit, and release workflow | -| **SBOM attestation** | the SBOM bound to the binary's digest | +| **SBOM attestation** | the SBOM bound to the `bpfcompat-linux-amd64` digest (the arm64 CLI and the validator carry SLSA provenance only) | Signing is **keyless** (Sigstore Fulcio + Rekor) via GitHub Actions OIDC — there is no long-lived private key. The signer identity is the release workflow itself. ## 1. Verify build provenance (recommended) -Requires the GitHub CLI (`gh`). This is the strongest check — it proves the binary -was built by *this repo's* release workflow: +Requires the GitHub CLI (`gh`) 2.68.0 or later: `--signer-workflow` was added +in gh 2.51.0 and `--source-ref` in gh 2.68.0. An older gh stops without +verifying anything, with `unknown flag` (before 2.68.0) or +`unknown command "attestation"` (before 2.47.0); upgrade it, or use the cosign +check in section 2. How strictly gh matches these values also depends on its +version: gh 2.97.0 and 2.102.0 fixed published advisories in that matching +(GHSA-mm27-mwq9-fr5g, GHSA-wjmr-j3rp-mh2g, GHSA-4mq3-hpgx-9cx8), so 2.102.0 or +later is recommended. -```bash -gh attestation verify ./bpfcompat-linux-amd64 --repo Kernel-Guard/bpfcompat -``` - -A pass confirms the artifact's digest matches an attestation produced by the -`release-artifacts` workflow on a `v*` tag. To pin the exact workflow identity: +This is the strongest check — it proves the binary was built by *this repo's* +release workflow for the tag you downloaded. Pin the workflow and the exact +tag, as `scripts/install.sh` does: ```bash +VER=v0.3.7 # the exact release tag you downloaded gh attestation verify ./bpfcompat-linux-amd64 \ --repo Kernel-Guard/bpfcompat \ - --signer-workflow Kernel-Guard/bpfcompat/.github/workflows/release-artifacts.yml + --signer-workflow Kernel-Guard/bpfcompat/.github/workflows/release-artifacts.yml \ + --source-ref "refs/tags/$VER" ``` -The promotion evidence is independently attested by the same workflow: +A pass confirms the artifact's digest matches an attestation produced by the +`release-artifacts` workflow on the tag `$VER`. Without `--source-ref`, an +attestation from another tag's release run (for example a prerelease) also +passes, and `gh attestation verify ./bpfcompat-linux-amd64 --repo Kernel-Guard/bpfcompat` +alone accepts an attestation from any workflow in this repository. + +From v0.4.0-rc.2 and v0.3.8-rc.1 (v0.3.7 has no such asset), the promotion +evidence is independently attested by the same workflow: ```bash gh attestation verify ./release-candidate-evidence.json \ --repo Kernel-Guard/bpfcompat \ - --signer-workflow Kernel-Guard/bpfcompat/.github/workflows/release-artifacts.yml + --signer-workflow Kernel-Guard/bpfcompat/.github/workflows/release-artifacts.yml \ + --source-ref "refs/tags/$VER" ``` Offline / air-gapped verification is supported by downloading the attestation @@ -50,24 +64,33 @@ bundle first (`gh attestation download`) and verifying with `--bundle`. ## 2. Verify checksums + cosign signature ```bash -# 1) integrity -sha256sum -c SHA256SUMS +VER=v0.3.7 # the exact release tag you downloaded -# 2) authenticity of SHA256SUMS (keyless cosign) +# 1) integrity of the assets you downloaded +sha256sum -c --ignore-missing SHA256SUMS + +# 2) authenticity of SHA256SUMS (keyless cosign), bound to the exact tag cosign verify-blob SHA256SUMS \ --signature SHA256SUMS.sig \ --certificate SHA256SUMS.crt \ - --certificate-identity-regexp '^https://github.com/Kernel-Guard/bpfcompat/.github/workflows/release-artifacts.yml@refs/tags/v' \ + --certificate-identity "https://github.com/Kernel-Guard/bpfcompat/.github/workflows/release-artifacts.yml@refs/tags/$VER" \ --certificate-oidc-issuer https://token.actions.githubusercontent.com ``` +The identity names the exact tag, as `scripts/install.sh` does, so a signature +made by another tag's release run (for example a prerelease) is refused. + ## 3. Inspect the SBOM ```bash # human-readable component list cat bpfcompat.sbom.cdx.json | jq '.components[].name' | sort -u # or verify the SBOM attestation is bound to the binary -gh attestation verify ./bpfcompat-linux-amd64 --repo Kernel-Guard/bpfcompat --predicate-type https://cyclonedx.org/bom +gh attestation verify ./bpfcompat-linux-amd64 \ + --repo Kernel-Guard/bpfcompat \ + --signer-workflow Kernel-Guard/bpfcompat/.github/workflows/release-artifacts.yml \ + --source-ref "refs/tags/$VER" \ + --predicate-type https://cyclonedx.org/bom ``` ## 4. Third-party license material and the relink kit @@ -90,7 +113,7 @@ cd bpfcompat-validator-relink-kit && sha256sum -c SHA256SUMS `INVENTORY.json` separates observed facts (what was linked, from where, with which texts), recorded distribution decisions (`undecided` until made) and what it does not establish. The bundle is not a statement that any license condition -is met. Releases that predate these assets do not have them; the repository's +is met. Releases before v0.4.0-rc.5 do not have these assets; the repository's `LICENSE`, `NOTICE` and `THIRD_PARTY_NOTICES.md` at that tag remain the reference for those. @@ -102,21 +125,29 @@ reference for those. - **Provenance** — a tamper-evident record of *which commit and workflow* built them. - **Promotion binding** — one attested record connects the release tag and - commit to the tested VM reports and promoted container digest. - -The GitHub Action consumes prebuilt release binaries only after both -`sha256sum -c` and `gh attestation verify` succeed for the CLI and validator. + commit to the tested VM reports and promoted container digest (from + v0.4.0-rc.2 and v0.3.8-rc.1; not in v0.3.7). + +From v0.4.0-rc.2 and v0.3.8-rc.1 (the v0.3.7 Action verifies checksums only), +the GitHub Action consumes prebuilt release binaries only after both +`sha256sum -c` and `gh attestation verify` succeed for the CLI and validator, +and, from v0.4.0-rc.5, for the third-party license bundle when `SHA256SUMS` +lists it. Attestation verification is restricted to `.github/workflows/release-artifacts.yml`; a checksum or attestation mismatch -is a hard failure. When attestation verification is unavailable, the Action -builds from its pinned source instead of trusting unverifiable release bytes. - -After verification the Action runs that validator by path and pinned digest -(`BPFCOMPAT_VALIDATOR_BIN` / `BPFCOMPAT_VALIDATOR_SHA256`), so a validator left -in `/usr/local/libexec` or `/usr/libexec` on a self-hosted runner is not -selected instead. While the Action is pinned to a release, every fallback to a -source build is a `::warning::` annotation: that run does not use the attested -release validator, and the CLI's normal search order (`/usr/local/libexec`, -`/usr/libexec`, then the action checkout) applies to it. A workflow that sets -`BPFCOMPAT_VALIDATOR_BIN` or `BPFCOMPAT_VALIDATOR_SHA256` keeps its own -settings, with a warning. +is a hard failure. When attestation verification is unavailable (no `gh` with +attestation support), the v0.4.0 release candidates' Action builds from its +pinned source instead of trusting unverifiable release bytes; the v0.3.8 +release candidates' Action fails the job instead. + +From v0.4.0-rc.5, after verification the Action runs that validator by path +and pinned digest (`BPFCOMPAT_VALIDATOR_BIN` / `BPFCOMPAT_VALIDATOR_SHA256`), +so a validator left in `/usr/local/libexec` or `/usr/libexec` on a self-hosted +runner is not selected instead. While the Action is pinned to a release, every +fallback to a source build is a `::warning::` annotation: that run does not use +the attested release validator, and the CLI's normal search order +(`/usr/local/libexec`, `/usr/libexec`, then the action checkout) applies to it. +A workflow that sets `BPFCOMPAT_VALIDATOR_BIN` or `BPFCOMPAT_VALIDATOR_SHA256` +keeps its own settings, with a warning. With v0.3.7 and v0.4.0-rc.4 that normal +search order applies to every run, so a validator installed in those +directories is used before the verified one.