mirror of
https://github.com/cozystack/cozystack.git
synced 2026-11-09 14:02:56 +09:00
No release carried a signature or an SBOM, so users could verify none of the ISO, disk images, kernel, initramfs, manifests and cozypkg binaries they download, and the OpenSSF Scorecard Signed-Releases check and CLOMonitor's signed_releases and sbom checks failed. hack/upload-assets.sh now hands its asset list to the new hack/sign-release-assets.sh before the first upload. That script writes cozystack-source-sbom.spdx.json with syft over the committed tree and the cozypkg binaries, cozystack-checksums.txt with the SHA-256 of every asset and the SBOM, and cozystack-checksums.txt.sigstore.json with a keyless cosign signature over the checksums. The SBOM does not describe the container images or the Talos boot images, which is what its name says; image SBOMs are #2937. The script verifies the bundle against the identities of tags.yaml on a v* tag and promote-rc.yaml on main before returning, so a bad signature fails the job with nothing uploaded, and hack/upload-assets.bats pins the upload list and that order. The two jobs that call make upload_assets get id-token: write, cosign v3.1.3 from the SHA-pinned cosign-installer, and syft from its release tarball checked against a committed SHA-256, because anchore/sbom-action/download-syft runs an install script fetched at run time; hack/ci-tool-installs-contract.bats now covers both. docs/release.md describes the files, what the SBOM covers, the minimum cosign version to verify a download with, what the certificate identity proves, and that promote-rc.yaml is dispatched from main only. Refs #4759 Assisted-by: LLM Signed-off-by: Marian Koreniuk <moriarti@cp.if.ua>
108 lines
3.9 KiB
Bash
Executable File
108 lines
3.9 KiB
Bash
Executable File
#!/bin/bash
|
|
# Write the SBOM, the checksums and the keyless signature of a release.
|
|
#
|
|
# Usage: hack/sign-release-assets.sh <version> <asset>...
|
|
#
|
|
# Every asset is a file in one directory (_out/assets in a release job), and the
|
|
# three files below are written next to them:
|
|
#
|
|
# cozystack-source-sbom.spdx.json SPDX SBOM, written by syft, of the
|
|
# committed source tree and of the
|
|
# cozypkg binaries in the cozypkg-*.tar.gz
|
|
# assets. It does not describe the
|
|
# container images or the Talos ISO and
|
|
# disk images, so its name says "source".
|
|
# CLOMonitor's sbom check looks for
|
|
# "sbom" in a release asset's name.
|
|
# cozystack-checksums.txt SHA-256 of every asset and of the SBOM,
|
|
# in sha256sum format.
|
|
# cozystack-checksums.txt.sigstore.json Sigstore bundle of a keyless cosign
|
|
# signature over the checksums, made with
|
|
# the job's GitHub OIDC identity.
|
|
#
|
|
# One signature covers every asset through the checksums. The asset list comes
|
|
# from the caller, so the checksums name exactly what hack/upload-assets.sh
|
|
# uploads, and never one of these three files: promote-rc.yaml downloads the rc's
|
|
# copies along with its other assets, and a stable release gets its own.
|
|
#
|
|
# The bundle is verified before the script returns, with the same certificate
|
|
# identity users are told to check in docs/release.md, so a release whose
|
|
# signature that check would reject fails here instead of being uploaded.
|
|
#
|
|
# Needs syft and cosign on PATH, and `id-token: write` in the job.
|
|
set -euo pipefail
|
|
|
|
SBOM=cozystack-source-sbom.spdx.json
|
|
CHECKSUMS=cozystack-checksums.txt
|
|
BUNDLE=cozystack-checksums.txt.sigstore.json
|
|
|
|
# The workflows that publish releases, at the refs they run from: tags.yaml for a
|
|
# pre-release, on a v* tag push, and promote-rc.yaml for a stable one, dispatched
|
|
# from main only. A run from any other ref signs, but fails the check below.
|
|
# docs/release.md shows users the same pattern.
|
|
IDENTITY_REGEXP='^https://github\.com/cozystack/cozystack/\.github/workflows/(tags\.yaml@refs/tags/v|promote-rc\.yaml@refs/heads/main$)'
|
|
OIDC_ISSUER=https://token.actions.githubusercontent.com
|
|
|
|
if [ "$#" -lt 2 ]; then
|
|
echo "usage: $0 <version> <asset>..." >&2
|
|
exit 2
|
|
fi
|
|
version=$1
|
|
shift
|
|
|
|
dir=$(dirname "$1")
|
|
names=()
|
|
for asset in "$@"; do
|
|
if [ ! -f "$asset" ]; then
|
|
echo "$0: asset not found: $asset" >&2
|
|
exit 1
|
|
fi
|
|
if [ "$(dirname "$asset")" != "$dir" ]; then
|
|
echo "$0: $asset is not in $dir, where the other assets are" >&2
|
|
exit 1
|
|
fi
|
|
name=$(basename "$asset")
|
|
case $name in
|
|
"$SBOM" | "$CHECKSUMS" | "$BUNDLE")
|
|
echo "$0: $name is written by this script and cannot be an input" >&2
|
|
exit 1
|
|
;;
|
|
esac
|
|
names+=("$name")
|
|
done
|
|
|
|
stage=$(mktemp -d)
|
|
trap 'rm -rf "$stage"' EXIT
|
|
|
|
# The tree being released, as committed: git archive leaves out _out/, the
|
|
# promote job's .release-tooling checkout and anything else the runner left in
|
|
# the work tree.
|
|
mkdir "$stage/source"
|
|
git archive HEAD | tar -x -C "$stage/source"
|
|
for name in "${names[@]}"; do
|
|
case $name in
|
|
cozypkg-*.tar.gz)
|
|
mkdir "$stage/${name%.tar.gz}"
|
|
tar -xzf "$dir/$name" -C "$stage/${name%.tar.gz}"
|
|
;;
|
|
esac
|
|
done
|
|
|
|
syft scan "dir:$stage" \
|
|
--quiet \
|
|
--source-name cozystack \
|
|
--source-version "$version" \
|
|
--output "spdx-json=$dir/$SBOM"
|
|
|
|
(cd "$dir" && sha256sum -- "${names[@]}" "$SBOM") > "$dir/$CHECKSUMS"
|
|
|
|
cosign sign-blob --yes \
|
|
--bundle "$dir/$BUNDLE" \
|
|
"$dir/$CHECKSUMS"
|
|
|
|
cosign verify-blob \
|
|
--bundle "$dir/$BUNDLE" \
|
|
--certificate-identity-regexp "$IDENTITY_REGEXP" \
|
|
--certificate-oidc-issuer "$OIDC_ISSUER" \
|
|
"$dir/$CHECKSUMS"
|