Skip to content
Draft
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
63 changes: 43 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 `<BPFCOMPAT_DOC_DIR>/<tag>/`;
the default directory is `<BIN_DIR>/../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
Expand All @@ -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 \
Expand Down Expand Up @@ -444,16 +451,29 @@ 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
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<version>`).
Change the identity together with the image tag.

### What a run looks like

Expand Down Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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:
Expand Down
37 changes: 22 additions & 15 deletions docs/compatibility-contract.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand All @@ -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
Expand Down Expand Up @@ -271,15 +275,18 @@ 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
component contains `.` or `:` and is followed by `/`, is passed to the CLI
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`).
Expand Down
37 changes: 24 additions & 13 deletions docs/production-support-boundary.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:

Expand All @@ -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
Expand Down
9 changes: 5 additions & 4 deletions docs/quickstart.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
29 changes: 19 additions & 10 deletions docs/supply-chain.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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`
Expand All @@ -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.
Expand Down
Loading
Loading