Files
git.stella-ops.org/.gitea/workflows/templates/dsse-attest-verify-check.yml
2026-02-19 22:10:54 +02:00

126 lines
4.5 KiB
YAML

# =============================================================================
# dsse-attest-verify-check.yml
# Sprint: SPRINT_20260219_011 (CIAP-02)
# Description: Signs SBOM with DSSE, verifies attestation, validates Rekor proof
# =============================================================================
#
# This workflow creates a DSSE attestation for an SBOM, verifies it, and
# optionally validates the Rekor transparency log inclusion proof.
#
# Supports both keyless (Fulcio/OIDC) and keyed (cosign key) signing modes.
#
# =============================================================================
name: DSSE Attest + Verify + Rekor Check
on:
workflow_call:
inputs:
subject_ref:
description: 'OCI image reference (registry/repo@sha256:...)'
required: true
type: string
predicate_path:
description: 'Path to the DSSE predicate JSON file'
required: true
type: string
signing_mode:
description: 'Signing mode: keyless (Fulcio/OIDC) or key (cosign key)'
required: false
type: string
default: 'keyless'
public_key_path:
description: 'Path to cosign public key PEM (required for key mode)'
required: false
type: string
predicate_type:
description: 'Predicate type URI for the attestation'
required: false
type: string
default: 'https://cyclonedx.org/bom'
skip_rekor:
description: 'Skip Rekor transparency log (for air-gapped environments)'
required: false
type: boolean
default: false
jobs:
attest-and-verify:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
id-token: write # For OIDC-based keyless signing
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Install cosign
uses: sigstore/cosign-installer@v3
- name: Sign attestation
id: sign
env:
COSIGN_EXPERIMENTAL: '1'
run: |
SIGN_FLAGS="--predicate ${{ inputs.predicate_path }}"
SIGN_FLAGS="${SIGN_FLAGS} --type ${{ inputs.predicate_type }}"
if [ "${{ inputs.signing_mode }}" = "key" ]; then
# Keyed signing
SIGN_FLAGS="${SIGN_FLAGS} --key ${{ inputs.public_key_path }}"
fi
if [ "${{ inputs.skip_rekor }}" = "true" ]; then
SIGN_FLAGS="${SIGN_FLAGS} --tlog-upload=false"
fi
cosign attest ${SIGN_FLAGS} "${{ inputs.subject_ref }}"
echo "### Attestation Signed" >> $GITHUB_STEP_SUMMARY
echo "- Subject: \`${{ inputs.subject_ref }}\`" >> $GITHUB_STEP_SUMMARY
echo "- Mode: ${{ inputs.signing_mode }}" >> $GITHUB_STEP_SUMMARY
echo "- Predicate type: \`${{ inputs.predicate_type }}\`" >> $GITHUB_STEP_SUMMARY
- name: Verify attestation
id: verify
run: |
VERIFY_FLAGS="--type ${{ inputs.predicate_type }}"
if [ "${{ inputs.signing_mode }}" = "key" ]; then
VERIFY_FLAGS="${VERIFY_FLAGS} --key ${{ inputs.public_key_path }}"
else
# Keyless: verify against Sigstore trust root
VERIFY_FLAGS="${VERIFY_FLAGS} --certificate-identity-regexp '.*'"
VERIFY_FLAGS="${VERIFY_FLAGS} --certificate-oidc-issuer-regexp '.*'"
fi
cosign verify-attestation ${VERIFY_FLAGS} "${{ inputs.subject_ref }}"
if [ $? -eq 0 ]; then
echo "Attestation verification: PASS" >> $GITHUB_STEP_SUMMARY
else
echo "Attestation verification: FAIL" >> $GITHUB_STEP_SUMMARY
exit 1
fi
- name: Validate Rekor inclusion proof
if: inputs.skip_rekor != true
run: |
# Fetch the Rekor entry for our attestation
DIGEST=$(sha256sum "${{ inputs.predicate_path }}" | cut -d' ' -f1)
# Use rekor-cli to search and verify
if command -v rekor-cli &> /dev/null; then
ENTRY=$(rekor-cli search --sha "sha256:${DIGEST}" 2>/dev/null | head -1)
if [ -n "${ENTRY}" ]; then
rekor-cli verify --artifact "${{ inputs.predicate_path }}" --entry "${ENTRY}"
echo "Rekor inclusion proof: PASS (entry: ${ENTRY})" >> $GITHUB_STEP_SUMMARY
else
echo "Rekor entry not found (may be pending)" >> $GITHUB_STEP_SUMMARY
fi
else
echo "rekor-cli not available, skipping Rekor verification" >> $GITHUB_STEP_SUMMARY
fi