# .github/workflows/examples/example-sbom-sign.yml # Example: Generate and sign SBOM with keyless signing # # This example shows how to: # 1. Generate SBOM using Syft # 2. Sign the SBOM with StellaOps # 3. Attach SBOM attestation to container image # # The signed SBOM provides: # - Proof of SBOM generation time # - Binding to CI/CD identity (repo, branch, workflow) # - Transparency log entry for audit name: Generate and Sign SBOM on: push: branches: [main] tags: ['v*'] workflow_dispatch: inputs: image: description: 'Container image to scan (with digest)' required: true type: string env: REGISTRY: ghcr.io IMAGE_NAME: ${{ github.repository }} jobs: generate-sbom: runs-on: ubuntu-latest permissions: contents: read packages: read outputs: sbom-digest: ${{ steps.sbom.outputs.digest }} image-digest: ${{ steps.resolve.outputs.digest }} steps: - name: Checkout uses: actions/checkout@v4 - name: Install Syft uses: anchore/sbom-action/download-syft@v0 - name: Log in to Container Registry uses: docker/login-action@v3 with: registry: ${{ env.REGISTRY }} username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - name: Resolve Image Digest id: resolve run: | if [[ -n "${{ github.event.inputs.image }}" ]]; then IMAGE="${{ github.event.inputs.image }}" else IMAGE="${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.ref_name }}" fi # Resolve to digest if not already if [[ ! "$IMAGE" =~ @sha256: ]]; then DIGEST=$(docker manifest inspect "$IMAGE" -v | jq -r '.Descriptor.digest') IMAGE="${IMAGE%%:*}@${DIGEST}" else DIGEST="${IMAGE##*@}" fi echo "image=${IMAGE}" >> $GITHUB_OUTPUT echo "digest=${DIGEST}" >> $GITHUB_OUTPUT echo "Resolved image: $IMAGE" - name: Generate SBOM id: sbom run: | set -euo pipefail IMAGE="${{ steps.resolve.outputs.image }}" SBOM_FILE="sbom.cdx.json" echo "::group::Generating SBOM for $IMAGE" syft "$IMAGE" \ --output cyclonedx-json="${SBOM_FILE}" \ --source-name "${{ github.repository }}" \ --source-version "${{ github.sha }}" echo "::endgroup::" # Calculate SBOM digest SBOM_DIGEST="sha256:$(sha256sum "${SBOM_FILE}" | cut -d' ' -f1)" echo "digest=${SBOM_DIGEST}" >> $GITHUB_OUTPUT echo "SBOM digest: ${SBOM_DIGEST}" # Store for upload echo "${SBOM_DIGEST}" > sbom-digest.txt - name: Upload SBOM uses: actions/upload-artifact@v4 with: name: sbom path: | sbom.cdx.json sbom-digest.txt if-no-files-found: error sign-sbom: needs: generate-sbom uses: ./.github/workflows/examples/stellaops-sign.yml with: artifact-digest: ${{ needs.generate-sbom.outputs.sbom-digest }} artifact-type: sbom predicate-type: 'https://cyclonedx.org/bom/1.5' push-attestation: true permissions: id-token: write contents: read packages: write attach-to-image: needs: [generate-sbom, sign-sbom] runs-on: ubuntu-latest permissions: packages: write steps: - name: Download SBOM uses: actions/download-artifact@v4 with: name: sbom - name: Install StellaOps CLI uses: stella-ops/setup-cli@v1 - name: Log in to Container Registry uses: docker/login-action@v3 with: registry: ${{ env.REGISTRY }} username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - name: Attach SBOM to Image env: IMAGE_DIGEST: ${{ needs.generate-sbom.outputs.image-digest }} ATTESTATION_DIGEST: ${{ needs.sign-sbom.outputs.attestation-digest }} run: | echo "::group::Attaching SBOM attestation to image" stella attest attach \ --image "${IMAGE_DIGEST}" \ --attestation "${ATTESTATION_DIGEST}" \ --type sbom echo "::endgroup::" - name: Summary run: | cat >> $GITHUB_STEP_SUMMARY << EOF ## SBOM Signed and Attached | Field | Value | |-------|-------| | **Image** | \`${{ needs.generate-sbom.outputs.image-digest }}\` | | **SBOM Digest** | \`${{ needs.generate-sbom.outputs.sbom-digest }}\` | | **Attestation** | \`${{ needs.sign-sbom.outputs.attestation-digest }}\` | | **Rekor UUID** | \`${{ needs.sign-sbom.outputs.rekor-uuid }}\` | ### Verify SBOM \`\`\`bash stella attest verify \\ --artifact "${{ needs.generate-sbom.outputs.sbom-digest }}" \\ --certificate-identity "repo:${{ github.repository }}:ref:${{ github.ref }}" \\ --certificate-oidc-issuer "https://token.actions.githubusercontent.com" \`\`\` ### Download SBOM \`\`\`bash stella sbom download \\ --image "${{ needs.generate-sbom.outputs.image-digest }}" \\ --output sbom.cdx.json \`\`\` EOF