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
18 changes: 18 additions & 0 deletions .github/workflows/build-custom.yml
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,24 @@ jobs:
bash scripts/build-custom-image.sh
echo "artifact=$(cat out/artifact-url.txt)" >> "$GITHUB_OUTPUT"

# SP-GATE-002: the previously-orphaned verifier is now a GATE. When a signing
# key is configured, every produced image MUST verify against its public key +
# in-toto subject digest, or the build fails. (Unsigned dev builds — no pubkey
# configured — skip this; the signer still records attestation.signed=false.)
- name: Verify image signature + attestation
if: ${{ vars.SOURCEOS_SIGN_PUBKEY != '' }}
env:
SOURCEOS_SIGN_PUBKEY: ${{ vars.SOURCEOS_SIGN_PUBKEY }}
run: |
shopt -s nullglob
imgs=(out/*.iso out/kernel out/initrd)
[ ${#imgs[@]} -gt 0 ] || { echo "SP-GATE-002: a signing key is configured but no image artifacts to verify in out/" >&2; exit 1; }
for f in "${imgs[@]}"; do
Comment on lines +87 to +90
[ -f "$f" ] || continue
echo "SP-GATE-002: verifying $(basename "$f")"
bash scripts/verify-image.sh "$f" "$SOURCEOS_SIGN_PUBKEY"
done
Comment on lines +82 to +94

- name: Mark done
if: success()
run: |
Expand Down
37 changes: 33 additions & 4 deletions scripts/sign-and-provenance.sh
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,17 @@
# yourself with `minisign -W` (see docs/SIGNING_SETUP.md). Verification is
# anyone-can-check with the public key.
#
# Graceful by design: with no SOURCEOS_SIGN_SECRET_KEY the artifact is left
# unsigned but provenance is still emitted (signatureRef omitted). This script
# never fails the build — it always exits 0.
# Attestation state is ALWAYS explicit: the OSImage provenance carries
# `attestation.signed` (true/false) + a reason, so a consumer never has to infer
# "unsigned" from a missing signatureRef (SP-GATE-002 / SP-GATE-003).
#
# Fail-closed, not fail-open:
# * no SOURCEOS_SIGN_SECRET_KEY → unsigned, attestation.signed=false, exit 0
# (graceful for local/dev builds)
# * key present but signing produced none → EXIT NON-ZERO (a key was there and we
# failed to sign — refusing to emit
# unsigned-but-attested provenance)
# * SOURCEOS_REQUIRE_SIGNATURE=1 + unsigned → EXIT NON-ZERO (release contexts mandate it)
#
# Env:
# OUT output dir holding the artifact(s) (required)
Expand Down Expand Up @@ -174,8 +182,14 @@ if [[ "$TARGET" == "iso" ]]; then
SIG_REF=""
[[ "$SIGNED" -eq 1 ]] && SIG_REF="$(ref_for "${PRIMARY_BASE}.minisig")"
OSIMAGE_FILE="$OUT/${PRIMARY_BASE}.osimage.json"
# Explicit attestation state — a consumer reads attestation.signed, never infers
# it from a missing signatureRef. (Feeds the downstream epistemicLevel, SP-GATE-003.)
if [[ "$SIGNED" -eq 1 ]]; then SIGNED_BOOL=true; SIGN_REASON="minisign";
elif [[ -n "${SOURCEOS_SIGN_SECRET_KEY:-}" ]]; then SIGNED_BOOL=false; SIGN_REASON="signing-failed";
else SIGNED_BOOL=false; SIGN_REASON="no-signing-key"; fi
prov="$(jq -n --arg s "$STATEMENT_URN" --arg p "$SLSA_URN" --arg sb "$SBOM_REF" --arg sg "$SIG_REF" \
'{statementRef:$s, slsaPredicateRef:$p}
--argjson signed "$SIGNED_BOOL" --arg reason "$SIGN_REASON" \
'{statementRef:$s, slsaPredicateRef:$p, attestation:{signed:$signed, reason:$reason}}
+ (if $sb != "" then {sbomRef:$sb} else {} end)
+ (if $sg != "" then {signatureRef:$sg} else {} end)')"
jq -n \
Expand Down Expand Up @@ -219,4 +233,19 @@ if [[ "$TARGET" == "iso" ]]; then
fi

log "provenance complete (signed=$SIGNED) for $PRIMARY_BASE"

# ── Fail-closed gate (SP-GATE-002) ─────────────────────────────────────────────
# The old behaviour ("never fails — always exit 0") let an unsigned artifact ship
# provenance that a downstream consumer could mistake for attested. Refuse that:
if [[ "$SIGNED" -ne 1 ]]; then
if [[ -n "${SOURCEOS_SIGN_SECRET_KEY:-}" ]]; then
log "FAIL-CLOSED: a signing key was provided but no artifact was signed (minisign failed)"
exit 4
fi
Comment on lines +237 to +244
if [[ "${SOURCEOS_REQUIRE_SIGNATURE:-0}" = "1" ]]; then
log "FAIL-CLOSED: SOURCEOS_REQUIRE_SIGNATURE=1 but the artifact is unsigned"
exit 4
fi
log "unsigned by design (no key, not required) — attestation.signed=false is explicit in the provenance"
fi
exit 0
Loading