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
20 changes: 17 additions & 3 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,23 @@ jobs:
exit 1
fi

- name: Install Cosign
uses: sigstore/cosign-installer@v4.1.0

# Sign the in-bundle checksums.txt and ship the signature inside the
# bundle so the release-mode installer can verify authenticity offline
# (issue #15). checksums.txt already vouches for every bundled file, so one
# signature covers the extracted payload.
- name: Sign bundle checksums for offline verification
env:
ARCHIVE_BASE: ${{ steps.meta.outputs.archive_base }}
run: |
set -euo pipefail
bundle="dist/${ARCHIVE_BASE}"
cosign sign-blob --yes \
--bundle "${bundle}/checksums.txt.cosign.bundle" \
"${bundle}/checksums.txt"
Comment on lines +244 to +248

- name: Create archives and checksums
env:
ARCHIVE_BASE: ${{ steps.meta.outputs.archive_base }}
Expand Down Expand Up @@ -270,9 +287,6 @@ jobs:
(cd "${bundle}" && sha256sum --check checksums.txt)
done

- name: Install Cosign
uses: sigstore/cosign-installer@v4.1.0

# Keyless detached signatures for the release archives and checksums
# (issue #15). Each .cosign.bundle is portable and verifiable offline with
# `cosign verify-blob` against the workflow identity and OIDC issuer.
Expand Down
31 changes: 31 additions & 0 deletions install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,37 @@ verify_release_bundle() {
die "checksum verification requires sha256sum or shasum on PATH"
fi
log "Release bundle checksums verified."

verify_release_signature
}

# Verify the bundle's authenticity signature when possible (issue #15). The
# release pipeline ships a keyless cosign signature of checksums.txt inside the
# bundle; because checksums.txt already vouches for every bundled file,
# verifying it authenticates the whole payload. Gated: a host without cosign, or
# a bundle without a signature, still installs on checksums alone. When cosign
# and the signature are both present, a bad signature is fatal.
verify_release_signature() {
_sig="${ROOT_DIR}/checksums.txt.cosign.bundle"
if [ ! -f "${_sig}" ]; then
log "No bundle signature present; skipping signature check (checksums verified)."
return 0
fi
if ! command -v cosign >/dev/null 2>&1; then
log "cosign not found; skipping signature check (checksums verified)."
log "Install cosign to cryptographically verify bundle authenticity."
return 0
fi
_identity="${UBEROS_SIGN_IDENTITY_REGEXP:-https://github.com/jmservera/UbeROS/.github/workflows/release.yml@.*}"
_issuer="${UBEROS_SIGN_OIDC_ISSUER:-https://token.actions.githubusercontent.com}"
log "Verifying release bundle signature with cosign..."
cosign verify-blob \
--bundle "${_sig}" \
--certificate-identity-regexp "${_identity}" \
--certificate-oidc-issuer "${_issuer}" \
"${ROOT_DIR}/checksums.txt" >/dev/null 2>&1 \
|| die "release bundle signature verification failed; the bundle is not authentic or was modified"
log "Release bundle signature verified."
}

# True when the installer may prompt: not forced off and stdin is a terminal.
Expand Down
54 changes: 54 additions & 0 deletions scripts/validate-wave3.sh
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,60 @@ expect_failure 'checksum verification failed' env PATH="${FAKE_BIN}:${PATH}" \
[ ! -s "${DOCKER_LOG}" ] || fail "tampered bundle reached Docker before rejection"
pass "tampered release bundle is rejected before Docker runs"

# --- Release signature verification (issue #15) -----------------------------
printf '\n== release signature verification (issue #15) ==\n'
SIG_ROOT="${SANDBOX}/release-sig"
SIG_WS="${SANDBOX}/release-sig-ws"
mkdir -p "${SIG_ROOT}"
cp install.sh .env.template compose.override.gpu.yaml \
compose.override.intel.yaml compose.override.wsl.yaml "${SIG_ROOT}/"
printf 'services: {}\n' > "${SIG_ROOT}/compose.release.yaml"
printf '0.4.0-beta\n' > "${SIG_ROOT}/VERSION"
(
cd "${SIG_ROOT}"
find . -type f ! -name checksums.txt -print0 \
| sort -z \
| xargs -0 sha256sum > checksums.txt
)
# Ship a signature file so the installer attempts verification.
printf 'stub-signature\n' > "${SIG_ROOT}/checksums.txt.cosign.bundle"

# A cosign that succeeds: the signed bundle installs and reaches Docker.
cat > "${FAKE_BIN}/cosign" <<EOF
#!/bin/sh
exit 0
EOF
chmod +x "${FAKE_BIN}/cosign"
: > "${DOCKER_LOG}"
PATH="${FAKE_BIN}:${PATH}" sh "${SIG_ROOT}/install.sh" -y \
--workspace "${SIG_WS}" --no-migrate >/dev/null \
|| fail "signed release bundle failed to install with a valid signature"
grep -q 'compose .* pull' "${DOCKER_LOG}" \
|| fail "signed release bundle did not reach the pull step"
pass "signed release bundle with a valid signature installs"
Comment on lines +371 to +383

# A cosign that fails: verification is fail-closed before Docker.
cat > "${FAKE_BIN}/cosign" <<EOF
#!/bin/sh
exit 1
EOF
chmod +x "${FAKE_BIN}/cosign"
: > "${DOCKER_LOG}"
expect_failure 'signature verification failed' env PATH="${FAKE_BIN}:${PATH}" \
sh "${SIG_ROOT}/install.sh" -y --workspace "${SIG_WS}" --no-migrate
[ ! -s "${DOCKER_LOG}" ] || fail "bad-signature bundle reached Docker before rejection"
pass "release bundle with an invalid signature is rejected before Docker runs"

# Without cosign the signed bundle still installs on verified checksums alone.
rm -f "${FAKE_BIN}/cosign"
: > "${DOCKER_LOG}"
PATH="${FAKE_BIN}:${PATH}" sh "${SIG_ROOT}/install.sh" -y \
--workspace "${SIG_WS}" --no-migrate >/dev/null \
|| fail "signed bundle failed to install when cosign is absent"
grep -q 'compose .* pull' "${DOCKER_LOG}" \
|| fail "cosign-absent install did not reach the pull step"
pass "signed bundle installs on checksums alone when cosign is absent"

# --- Learning-package selection (PR-14) -------------------------------------
printf '\n== learning-package selection (FR-H1, FR-H4, FR-H5) ==\n'
PACKAGE_ROOT="${SANDBOX}/packages"
Expand Down
Loading