Verify Enclave #33
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Verify Enclave | |
| on: | |
| schedule: | |
| - cron: '0 8 * * *' | |
| workflow_dispatch: | |
| permissions: | |
| contents: read | |
| id-token: write # Mint OIDC tokens (custom webhook + Sigstore signing). | |
| attestations: write # Publish verify-result attestation to GitHub's attestations API. | |
| jobs: | |
| verify: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-go@v5 | |
| with: | |
| go-version: stable | |
| - name: Install enclave CLI | |
| run: go install github.com/ArkLabsHQ/introspector-enclave/cli/cmd/enclave@latest | |
| - name: Fetch deployment manifest | |
| id: manifest | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| run: | | |
| gh release download -p deployment.json || { | |
| echo "No deployment manifest found. Run the deploy workflow first." | |
| exit 1 | |
| } | |
| base_url=$(jq -r '.base_url // empty' deployment.json) | |
| pcr0=$(jq -r '.pcr0' deployment.json) | |
| pcr1=$(jq -r '.pcr1' deployment.json) | |
| pcr2=$(jq -r '.pcr2' deployment.json) | |
| echo "base_url=${base_url}" >> "$GITHUB_OUTPUT" | |
| echo "pcr0=${pcr0}" >> "$GITHUB_OUTPUT" | |
| echo "pcr1=${pcr1}" >> "$GITHUB_OUTPUT" | |
| echo "pcr2=${pcr2}" >> "$GITHUB_OUTPUT" | |
| - name: Verify attestation | |
| id: verify | |
| if: steps.manifest.outputs.base_url != '' || vars.ENCLAVE_BASE_URL != '' | |
| env: | |
| BASE_URL: ${{ steps.manifest.outputs.base_url || vars.ENCLAVE_BASE_URL }} | |
| PCR0: ${{ steps.manifest.outputs.pcr0 }} | |
| run: | | |
| output=$(enclave verify \ | |
| --base-url "${BASE_URL}" \ | |
| --expected-pcr0 "${PCR0}" \ | |
| --wait 60 2>&1) && status="pass" || status="fail" | |
| echo "status=${status}" >> "$GITHUB_OUTPUT" | |
| echo "output<<EOF" >> "$GITHUB_OUTPUT" | |
| echo "${output}" >> "$GITHUB_OUTPUT" | |
| echo "EOF" >> "$GITHUB_OUTPUT" | |
| { | |
| echo "## Enclave Verification" | |
| echo "- **Status:** ${status}" | |
| echo "- **PCR0:** ${PCR0}" | |
| echo '```' | |
| echo "${output}" | |
| echo '```' | |
| } >> "$GITHUB_STEP_SUMMARY" | |
| - name: Build attestation predicate | |
| if: steps.verify.outcome != 'skipped' | |
| env: | |
| STATUS: ${{ steps.verify.outputs.status }} | |
| PCR0: ${{ steps.manifest.outputs.pcr0 }} | |
| PCR1: ${{ steps.manifest.outputs.pcr1 }} | |
| PCR2: ${{ steps.manifest.outputs.pcr2 }} | |
| BASE_URL: ${{ steps.manifest.outputs.base_url || vars.ENCLAVE_BASE_URL }} | |
| VERIFIER: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }} | |
| run: | | |
| jq -n \ | |
| --arg status "$STATUS" \ | |
| --arg pcr0 "$PCR0" \ | |
| --arg pcr1 "$PCR1" \ | |
| --arg pcr2 "$PCR2" \ | |
| --arg base_url "$BASE_URL" \ | |
| --arg verifier "$VERIFIER" \ | |
| --argjson verifiedAt "$(date -u +%s)" \ | |
| '{ | |
| status: $status, | |
| expectedPcr0: $pcr0, | |
| expectedPcr1: $pcr1, | |
| expectedPcr2: $pcr2, | |
| baseUrl: $base_url, | |
| verifier: $verifier, | |
| verifiedAt: $verifiedAt | |
| }' > predicate.json | |
| # Anchor the attestation to image.eif from the eif-latest release. | |
| # actions/attest@v2 computes sha256(image.eif) as the subject digest, which | |
| # composes with `gh attestation verify image.eif --predicate-type ...`. | |
| # The PCR0/1/2 values still live in the predicate envelope above; this | |
| # subject just gives auditors a verifiable anchor — the same file the | |
| # build-provenance attestation in release-eif.yml is also anchored to. | |
| - name: Download EIF for attestation subject | |
| if: steps.verify.outcome != 'skipped' | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| run: gh release download eif-latest -p image.eif --clobber | |
| - name: Attest enclave verification result | |
| if: steps.verify.outcome != 'skipped' | |
| uses: actions/attest@v2 | |
| with: | |
| subject-path: image.eif | |
| predicate-type: https://github.com/BitspendPayment/MPCWallet/predicates/enclave-verify/v1 | |
| predicate-path: predicate.json | |
| - name: Mint OIDC token | |
| id: oidc | |
| if: always() && steps.verify.outcome != 'skipped' | |
| run: | | |
| TOKEN=$(curl -sLS \ | |
| -H "Authorization: bearer $ACTIONS_ID_TOKEN_REQUEST_TOKEN" \ | |
| "$ACTIONS_ID_TOKEN_REQUEST_URL&audience=attestation-api" \ | |
| | jq -r .value) | |
| echo "::add-mask::$TOKEN" | |
| echo "token=$TOKEN" >> "$GITHUB_OUTPUT" | |
| - name: Publish to attestation API | |
| if: always() && steps.verify.outcome != 'skipped' | |
| env: | |
| API_URL: ${{ vars.ATTESTATION_API_URL }} | |
| OIDC_TOKEN: ${{ steps.oidc.outputs.token }} | |
| STATUS: ${{ steps.verify.outputs.status || 'unknown' }} | |
| VERIFY_OUTPUT: ${{ steps.verify.outputs.output || '' }} | |
| PCR0: ${{ steps.manifest.outputs.pcr0 }} | |
| PCR1: ${{ steps.manifest.outputs.pcr1 }} | |
| PCR2: ${{ steps.manifest.outputs.pcr2 }} | |
| BASE_URL: ${{ steps.manifest.outputs.base_url || vars.ENCLAVE_BASE_URL }} | |
| run: | | |
| payload=$(jq -n \ | |
| --arg status "$STATUS" \ | |
| --arg pcr0 "$PCR0" \ | |
| --arg pcr1 "$PCR1" \ | |
| --arg pcr2 "$PCR2" \ | |
| --arg verify_output "$VERIFY_OUTPUT" \ | |
| --arg commit_sha "$GITHUB_SHA" \ | |
| --arg run_id "$GITHUB_RUN_ID" \ | |
| --arg base_url "$BASE_URL" \ | |
| --argjson verified_at "$(date -u +%s)" \ | |
| '{ | |
| status: $status, | |
| pcr0: $pcr0, | |
| pcr1: $pcr1, | |
| pcr2: $pcr2, | |
| verify_output: $verify_output, | |
| commit_sha: $commit_sha, | |
| run_id: $run_id, | |
| base_url: $base_url, | |
| verified_at: $verified_at | |
| }') | |
| curl --fail-with-body -sSL --retry 3 -X POST "$API_URL/api/attestations/webhook" \ | |
| -H "Authorization: Bearer $OIDC_TOKEN" \ | |
| -H "Content-Type: application/json" \ | |
| -d "$payload" |