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
82 changes: 71 additions & 11 deletions .github/workflows/_build-and-publish-image.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ on:

permissions:
contents: write
id-token: write
attestations: write

jobs:
build:
Expand Down Expand Up @@ -148,6 +150,50 @@ jobs:
- name: Show build artifacts
run: ls -lh build/

- name: Generate SHA256SUMS
run: |
set -euo pipefail
shopt -s nullglob
cd build
efi=("${ARTIFACT_PREFIX}"_*.efi) sbom=("${ARTIFACT_PREFIX}"_*.sbom.cdx.json)
((${#efi[@]} == 1 && ${#sbom[@]} == 1))
sha256sum "${ARTIFACT_PREFIX}"_*.{efi,raw,tar.gz,qcow2,sbom.cdx.json} \
portable_measurements.json >SHA256SUMS
printf 'EFI_FILE=build/%s\nSBOM_FILE=build/%s\n' "${efi[0]}" "${sbom[0]}" >>"$GITHUB_ENV"

- name: Attest build provenance
id: attest-provenance
uses: actions/attest@508db95dd578ae2727ebd6217d5ba78e4fbda05d # v4.2.1
with:
subject-checksums: build/SHA256SUMS

- name: Export provenance bundle
env:
BUNDLE_PATH: ${{ steps.attest-provenance.outputs.bundle-path }}
run: cp "${BUNDLE_PATH}" build/provenance.sigstore.json

- name: Attest SBOM
id: attest-sbom
uses: actions/attest@508db95dd578ae2727ebd6217d5ba78e4fbda05d # v4.2.1
with:
subject-path: ${{ env.EFI_FILE }}
sbom-path: ${{ env.SBOM_FILE }}

- name: Export SBOM bundle
env:
BUNDLE_PATH: ${{ steps.attest-sbom.outputs.bundle-path }}
run: cp "${BUNDLE_PATH}" build/sbom.sigstore.json

- name: Verify attestations
env:
GH_TOKEN: ${{ github.token }}
run: |
gh attestation verify "${EFI_FILE}" --repo "${GITHUB_REPOSITORY}" \
--bundle build/provenance.sigstore.json
gh attestation verify "${EFI_FILE}" --repo "${GITHUB_REPOSITORY}" \
--bundle build/sbom.sigstore.json \
--predicate-type https://cyclonedx.org/bom

- name: Install rclone
run: |
set -euo pipefail
Expand Down Expand Up @@ -178,11 +224,13 @@ jobs:
acl = private
use_data_integrity_protections = false
EOF
# Upload only the image artifacts and measurements.
rclone copy -P --retries 3 --retries-sleep 20s --error-on-no-transfer \
--s3-upload-concurrency=8 --transfers=8 \
--include "${ARTIFACT_PREFIX}_*.{efi,tar.gz,qcow2}" \
--include "${ARTIFACT_PREFIX}_*.{efi,raw,tar.gz,qcow2}" \
--include "${ARTIFACT_PREFIX}_*.sbom.cdx.json" \
--include "portable_measurements.json" \
--include "SHA256SUMS" \
--include "*.sigstore.json" \
build "r2:${R2_BUCKET}/${R2_PATH_PREFIX}/${VERSION_DIR}/"

- name: Create draft GitHub Release
Expand All @@ -207,18 +255,21 @@ jobs:
echo "| 🔖 **Commit** | [\`${COMMIT_SHORT}\`](${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/commit/${HEAD_SHA}) |"
echo "| 📅 **Built** | ${BUILT} |"
echo ""
echo "### 💿 Images"
echo "### 💿 Artifacts"
echo ""
echo "| Target | File | Size |"
echo "|---|---|--:|"
for f in build/"${ARTIFACT_PREFIX}"_*.efi build/"${ARTIFACT_PREFIX}"_*.tar.gz build/"${ARTIFACT_PREFIX}"_*.qcow2; do
for f in build/"${ARTIFACT_PREFIX}"_*.efi build/"${ARTIFACT_PREFIX}"_*.raw build/"${ARTIFACT_PREFIX}"_*.tar.gz build/"${ARTIFACT_PREFIX}"_*.qcow2 \
build/"${ARTIFACT_PREFIX}"_*.sbom.cdx.json; do
n="$(basename "$f")"
sz="$(du -h "$f" | cut -f1)"
case "$n" in
*.efi) t="🐧 UKI (EFI)" ;;
*.tar.gz) t="☁️ GCP disk" ;;
*.qcow2) t="🧪 QEMU qcow2" ;;
*) t="📄 artifact" ;;
*.efi) t="🐧 UKI (EFI)" ;;
*.raw) t="💽 Raw disk" ;;
*.tar.gz) t="☁️ GCP disk" ;;
*.qcow2) t="🧪 QEMU qcow2" ;;
*.cdx.json) t="🧾 SBOM (CycloneDX)" ;;
*) t="📄 artifact" ;;
esac
echo "| ${t} | [\`${n}\`](${R2_PUBLIC_BASE_URL}/${R2_PATH_PREFIX}/${VERSION_DIR}/${n}) | ${sz} |"
done
Expand All @@ -228,6 +279,15 @@ jobs:
echo ">"
echo "> 📖 Details: [attestation walkthrough](https://github.com/flashbots/flashbots-images/blob/main/modules/flashbox/flashbox-l1/readme.md#attestation-walkthrough) · [attested-tls-proxy](https://github.com/flashbots/attested-tls-proxy)"
echo ""
echo "### 🔏 Verify build and SBOM"
echo ""
echo "Checksums and Sigstore verification bundles are attached."
echo ""
echo '```bash'
echo "gh attestation verify <artifact> --repo ${GITHUB_REPOSITORY} --bundle provenance.sigstore.json"
echo "gh attestation verify <the .efi> --repo ${GITHUB_REPOSITORY} --bundle sbom.sigstore.json --predicate-type https://cyclonedx.org/bom"
echo '```'
echo ""
echo "### 🔁 Reproduce the image hashes"
echo ""
echo "1. Clone and check out this release:"
Expand Down Expand Up @@ -256,10 +316,10 @@ jobs:
echo ' ```'
} > release-notes.md

# Attach the measurements and the .efi UKI; the larger tar.gz/qcow2
# artifacts stay in R2 and are linked in the release body.
assets=()
for f in build/portable_measurements.json build/"${ARTIFACT_PREFIX}"_*.efi; do
for f in build/portable_measurements.json build/SHA256SUMS \
build/provenance.sigstore.json build/sbom.sigstore.json \
build/"${ARTIFACT_PREFIX}"_*.efi; do
[ -e "$f" ] && assets+=("$f")
done

Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/flashbox-l1.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ jobs:
release:
permissions:
contents: write
id-token: write
attestations: write
uses: ./.github/workflows/_build-and-publish-image.yaml
with:
image-id: flashbox-l1
Expand Down
1 change: 1 addition & 0 deletions flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@
qemu-utils
parted
jq
syft
reprepro
systemd
bash
Expand Down
2 changes: 1 addition & 1 deletion modules/flashbox/flashbox-l1/mkosi.build
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ LIGHTHOUSE_BUILD_CMD="
export SOURCE_DATE_EPOCH=\$(git log -1 --pretty=%ct)
export RUSTFLAGS='-C link-arg=-Wl,--build-id=none -C metadata= --remap-path-prefix \$(pwd)=. -L /usr/lib/x86_64-linux-gnu -l z -l zstd -l snappy'

cargo build --bin lighthouse \
cargo auditable build --bin lighthouse \
--features gnosis,slasher-lmdb,slasher-mdbx,slasher-redb,jemalloc-unprefixed \
--profile release \
--locked \
Expand Down
11 changes: 9 additions & 2 deletions scripts/build_rust_package.sh
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ build_rust_package() {

# Clone the repository
local build_dir="$BUILDROOT/build/$package"
local source_url=$git_url
mkdir -p "$build_dir"
set +x # don't leak github token into logs
echo "Cloning ${git_url}"
Expand All @@ -38,14 +39,20 @@ build_rust_package() {
local git_describe=$( git -C "$build_dir" describe --always --long --tags )
printf "${git_describe#$package/}" > "$BUILDDIR/$package.git"

mkdir -p "${ARTIFACTDIR:?}/sbom"
local repository=${source_url#*://*/}
local vcs_url=$(printf 'git+%s@%s' "$source_url" "$(git -C "$build_dir" rev-parse HEAD)" | jq -sRr @uri)
printf 'pkg:generic/%s/%s@%s?arch=%s&vcs_url=%s\n' "${repository%/*}" "$package" "$version" "$DISTRIBUTION_ARCHITECTURE" "$vcs_url" \
> "$ARTIFACTDIR/sbom/$package.sbom"

# If binary is cached, skip compilation
if [ -n "${extra_features}" ]; then
local cached_binary="$BUILDDIR/${package}-${git_describe#${package}/}-${extra_features}/${package}"
else
local cached_binary="$BUILDDIR/${package}-${git_describe#${package}/}/${package}"
fi
local cached_binary="${cached_binary//,/-}"
if [ -f "$cached_binary" ]; then
if [ -f "$cached_binary" ] && grep -aFq .dep-v0 "$cached_binary"; then
echo "Using cached binary for $package version $version"
if [ -n "${extra_features}" ]; then
echo "| \`$package\` | \`$version\` (\`$git_describe\`, features: ${extra_features}) | reused from cache | \`$( du -sh $cached_binary | cut -f1 )\` | |" ">> $BUILDDIR/manifest.md"
Expand Down Expand Up @@ -83,7 +90,7 @@ build_rust_package() {
CARGO_TERM_COLOR='never'
cd '/build/$package'
cargo fetch
cargo build --release --frozen ${extra_features:+--features $extra_features} --package $cargo_package
cargo auditable build --release --frozen ${extra_features:+--features $extra_features} --package $cargo_package
"
local seconds=$(( $( date +%s ) - ts ))
local duration=$( printf "%dm%ds" $(( seconds / 60 )) $(( seconds % 60 )) )
Expand Down
7 changes: 7 additions & 0 deletions scripts/make_git_package.sh
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ make_git_package() {

# Clone the repository
local build_dir="$BUILDROOT/build/$package"
local source_url=$git_url
set +x # don't leak github token into logs
echo "Cloning ${git_url}"
if [ -f "$BUILDDIR/.ghtoken" ]; then
Expand All @@ -36,6 +37,12 @@ make_git_package() {
local git_describe=$( git -C "$build_dir" describe --always --long --tags )
printf "${git_describe#$package/}" > "$BUILDDIR/$package.git"

mkdir -p "${ARTIFACTDIR:?}/sbom"
local repository=${source_url#*://*/}
local vcs_url=$(printf 'git+%s@%s' "$source_url" "$(git -C "$build_dir" rev-parse HEAD)" | jq -sRr @uri)
printf 'pkg:generic/%s/%s@%s?arch=%s&vcs_url=%s\n' "${repository%/*}" "$package" "$version" "$DISTRIBUTION_ARCHITECTURE" "$vcs_url" \
> "$ARTIFACTDIR/sbom/$package.sbom"

local env_hash=$(
{
# We hash the build_cmd into the cache dir to catch
Expand Down
1 change: 1 addition & 0 deletions shared/mkosi.conf
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ BuildPackages=build-essential
git
curl
cmake
cargo-auditable
pkg-config
clang
flex
Expand Down
39 changes: 39 additions & 0 deletions shared/mkosi.postinst.d/95-prepare-sbom.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
#!/usr/bin/env bash
# Record package metadata before it's removed by CleanPackageMetadata
set -euo pipefail
shopt -s nullglob

stage="${OUTPUTDIR:?}/.sbom-root"

# Copy dpkg metadata
mkdir -p "$stage/var/lib/dpkg" "$stage/etc" "$stage/usr/lib/sbom"
cp "${BUILDROOT:?}/var/lib/dpkg/status" "$stage/var/lib/dpkg/status"
cp -L "$BUILDROOT/etc/os-release" "$stage/etc/os-release"
if [[ -d "$BUILDROOT/usr/share/doc" ]]; then
(cd "$BUILDROOT" && find usr/share/doc -maxdepth 2 -name copyright \
-exec cp --parents -t "$stage" {} +)
fi

# Create SBOM metadata for deb packages installed manually
debroot=$(mktemp -d "${PACKAGEDIR:?}/.sbom.XXXXXX")
trap 'rm -r "$debroot"' EXIT
for deb in "${PACKAGEDIR:?}"/*.deb; do
package=$(dpkg-deb -f "$deb" Package)
if [[ $(dpkg-query --admindir="$stage/var/lib/dpkg" -W -f='${Status} ${Version}' \
"$package" 2>/dev/null) == "install ok installed $(dpkg-deb -f "$deb" Version)" ]]; then
ln "$deb" "$debroot/"
sed -i "/^Package: $package$/,/^$/d" "$stage/var/lib/dpkg/status"
fi
done
SYFT_CHECK_FOR_APP_UPDATE=false SYFT_FILE_METADATA_SELECTION=none \
syft -q scan "dir:$debroot" --override-default-catalogers deb-archive-cataloger \
-o syft-json >"$stage/usr/lib/sbom/local-debs.syft.json"
SYFT_CHECK_FOR_APP_UPDATE=false SYFT_FILE_METADATA_SELECTION=none \
syft -q scan "dir:$BUILDROOT" --override-default-catalogers \
go-module-binary-cataloger,cargo-auditable-binary-cataloger \
-o syft-json >"$stage/usr/lib/sbom/binaries.syft.json"

# Add metadata for packages installed from source
if [[ -d "${ARTIFACTDIR:?}/sbom" ]]; then
cp -r "$ARTIFACTDIR/sbom/." "$stage/usr/lib/sbom"
fi
29 changes: 29 additions & 0 deletions shared/mkosi.postoutput.d/95-gen-sbom.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#!/usr/bin/env bash
set -euo pipefail

root="$OUTPUTDIR/.sbom-root"
out="$OUTPUTDIR/${IMAGE_ID}_${IMAGE_VERSION}"

# Bind the SBOM to the primary image artifact
image="$out.efi"
[[ -f "$out.raw" ]] && image="$out.raw"
sha=$(sha256sum "$image" | cut -d' ' -f1)

export SYFT_CHECK_FOR_APP_UPDATE=false
export SYFT_FILE_METADATA_SELECTION=none
syft scan "dir:$root" \
--select-catalogers +sbom-cataloger \
--source-name "$IMAGE_ID" \
--source-version "$IMAGE_VERSION" \
-o cyclonedx-json |
jq --arg file "${image##*/}" --arg sha "$sha" '
del(.serialNumber)
| del(.components[].properties[]? | select(.name == "syft:cpe23"))
| .metadata.timestamp = "1970-01-01T00:00:00Z"
| .metadata.component += {
"bom-ref": "root", "type": "operating-system",
hashes: [{alg: "SHA-256", content: $sha}],
properties: [{name: "sbom:image-file", value: $file}]}' \
>"$out.sbom.cdx.json"

rm -r "$root"