diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 1719ab8c..a990ad5b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -58,6 +58,9 @@ jobs: name: Build ${{ matrix.package }} (${{ matrix.build.target }}) permissions: contents: read + # Required by actions/attest for keyless Sigstore signing. + id-token: write + attestations: write needs: [parse-tag] runs-on: ${{ matrix.build.os }} strategy: @@ -93,13 +96,63 @@ jobs: - name: Configure git auth run: git config --global url."https://x-access-token:${{ secrets.GH_PAT }}@github.com/".insteadOf "https://github.com/" - name: Build release binary - run: cargo build --release --target ${{ matrix.build.target }} -p ${{ env.PACKAGE }} + run: cargo build --locked --release --target ${{ matrix.build.target }} -p ${{ env.PACKAGE }} - run: strip target/${{ matrix.build.target }}/release/${{ env.PACKAGE }} - run: mv target/${{ matrix.build.target }}/release/${{ env.PACKAGE }} ${{ env.PACKAGE }}-${{ matrix.build.suffix }} + + - name: Generate sha256 + shell: bash + run: | + set -euo pipefail + BIN="${PACKAGE}-${{ matrix.build.suffix }}" + if command -v sha256sum >/dev/null 2>&1; then + sha256sum "$BIN" > "$BIN.sha256" + else + shasum -a 256 "$BIN" > "$BIN.sha256" + fi + cat "$BIN.sha256" + + - name: Generate SBOM (SPDX-JSON) + uses: anchore/sbom-action@e22c389904149dbc22b58101806040fa8d37a610 # v0.24.0 + with: + file: ${{ env.PACKAGE }}-${{ matrix.build.suffix }} + format: spdx-json + output-file: ${{ env.PACKAGE }}-${{ matrix.build.suffix }}.spdx.json + upload-artifact: false + upload-release-assets: false + + - name: Attest SBOM + uses: actions/attest@59d89421af93a897026c735860bf21b6eb4f7b26 # v4.1.0 + with: + subject-path: ${{ env.PACKAGE }}-${{ matrix.build.suffix }} + sbom-path: ${{ env.PACKAGE }}-${{ matrix.build.suffix }}.spdx.json + + - name: Attest build provenance (SLSA v1) + uses: actions/attest@59d89421af93a897026c735860bf21b6eb4f7b26 # v4.1.0 + with: + subject-path: ${{ env.PACKAGE }}-${{ matrix.build.suffix }} + + - name: Install cosign + uses: sigstore/cosign-installer@cad07c2e89fa2edd6e2d7bab4c1aa38e53f76003 # v4.1.1 + + - name: Sign binary with cosign (keyless) + shell: bash + run: | + set -euo pipefail + BIN="${PACKAGE}-${{ matrix.build.suffix }}" + cosign sign-blob --yes \ + --bundle "${BIN}.sigstore.json" \ + "$BIN" + - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: ${{ env.PACKAGE }}-${{ matrix.build.suffix }} - path: ${{ env.PACKAGE }}-${{ matrix.build.suffix }} + path: | + ${{ env.PACKAGE }}-${{ matrix.build.suffix }} + ${{ env.PACKAGE }}-${{ matrix.build.suffix }}.sha256 + ${{ env.PACKAGE }}-${{ matrix.build.suffix }}.spdx.json + ${{ env.PACKAGE }}-${{ matrix.build.suffix }}.sigstore.json + if-no-files-found: error publish: name: Publish ${{ matrix.package }} diff --git a/README.md b/README.md index a807c67b..40de695b 100644 --- a/README.md +++ b/README.md @@ -120,7 +120,7 @@ The Minimum Supported Rust Version (MSRV) is specified in [`Cargo.toml`](Cargo.t ## Security -See [SECURITY.md](SECURITY.md) for vulnerability reporting. +See [SECURITY.md](SECURITY.md) for vulnerability reporting and for instructions on [verifying release binaries](SECURITY.md#verifying-releases) (SLSA provenance, SBOM, Sigstore signatures, and SHA-256 checksums). ## License diff --git a/SECURITY.md b/SECURITY.md index 08a6f7df..197c20df 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -4,3 +4,52 @@ If you believe you've found a security vulnerability, please do not report it via GitHub issues. Instead, email `security@tempo.xyz` and we will acknowledge your report within 5 days and provide a more detailed follow-up within 10 days. + +## Verifying Releases + +Each binary ships with three sidecars: `.sha256` (checksum), `.sigstore.json` +(cosign keyless bundle), and `.spdx.json` (SBOM). GitHub also stores SLSA v1 +provenance and SBOM attestations against the binary's digest. Signing identity +is the OIDC token of [`build.yml`](./.github/workflows/build.yml). + +```bash +TAG=v1.0.0 +PKG=tempo-wallet +BIN=${PKG}-linux-amd64 +``` + +### From GitHub Releases + +```bash +gh release download "$TAG" --repo tempoxyz/wallet \ + -p "$BIN" -p "$BIN.sha256" -p "$BIN.sigstore.json" -p "$BIN.spdx.json" + +sha256sum -c "$BIN.sha256" + +gh attestation verify "$BIN" --repo tempoxyz/wallet \ + --signer-workflow tempoxyz/wallet/.github/workflows/build.yml \ + --source-ref "refs/tags/${TAG}" \ + --predicate-type https://slsa.dev/provenance/v1 + +gh attestation verify "$BIN" --repo tempoxyz/wallet \ + --signer-workflow tempoxyz/wallet/.github/workflows/build.yml \ + --source-ref "refs/tags/${TAG}" \ + --predicate-type https://spdx.dev/Document/v2.3 +``` + +### From cli.tempo.xyz (offline-friendly) + +```bash +BASE=https://cli.tempo.xyz/extensions/${PKG} +curl -fsSL -O "${BASE}/${BIN}" +curl -fsSL -O "${BASE}/${BIN}.sha256" +curl -fsSL -O "${BASE}/${BIN}.sigstore.json" + +sha256sum -c "${BIN}.sha256" + +cosign verify-blob \ + --bundle "${BIN}.sigstore.json" \ + --certificate-identity "https://github.com/tempoxyz/wallet/.github/workflows/build.yml@refs/tags/${TAG}" \ + --certificate-oidc-issuer 'https://token.actions.githubusercontent.com' \ + "$BIN" +``` diff --git a/crates/tempo-sign/src/sign.rs b/crates/tempo-sign/src/sign.rs index 5122c977..620aafcc 100644 --- a/crates/tempo-sign/src/sign.rs +++ b/crates/tempo-sign/src/sign.rs @@ -10,7 +10,7 @@ use sha2::{Digest, Sha256}; use crate::error::SignError; /// Skip non-binary artifacts when building a release manifest. -pub const SKIP_EXTENSIONS: &[&str] = &[".json", ".md", ".sh", ".txt", ".py"]; +pub const SKIP_EXTENSIONS: &[&str] = &[".json", ".md", ".sh", ".txt", ".py", ".sha256"]; /// Compute SHA-256 for a file. pub fn sha256_file(path: &Path) -> Result {