feat(policy): verify MRTD via ITA CSP RIM instead of pinned values (#49) #60
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
| # Generates and validates the composite attestation policy, uploads it to ITA, | |
| # and publishes signed release assets and provenance. | |
| # | |
| # generate: builds and validates policies. It authenticates to GCP/GHCR to | |
| # measure images but does not upload to ITA or create a GitHub release. | |
| # publish: only on push to main (or explicit dispatch), gated by the `release` | |
| # environment, with access to ITA and Sigstore credentials. | |
| name: Release Policy | |
| on: | |
| push: | |
| branches: [main] | |
| paths: | |
| - "attestation-policy/policy-manifest.yaml" | |
| - "attestation-policy/initdata/**" | |
| - ".github/actions/generate-policy/**" | |
| workflow_dispatch: | |
| inputs: | |
| version: | |
| description: "Release version (empty = next v0.0.1aN)" | |
| required: false | |
| type: string | |
| reason: | |
| description: "Reason for this release" | |
| required: false | |
| type: string | |
| publish: | |
| description: "Upload to ITA and create the GitHub release (main only)" | |
| required: false | |
| type: boolean | |
| default: false | |
| permissions: | |
| contents: read | |
| concurrency: | |
| group: production-ita-policy-release | |
| cancel-in-progress: false | |
| jobs: | |
| generate: | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| packages: read | |
| id-token: write # GCP workload identity federation | |
| outputs: | |
| version: ${{ steps.version.outputs.version }} | |
| ita-policy-file: ${{ steps.generate.outputs.ita-policy-file }} | |
| trustee-cpu-policy-file: ${{ steps.generate.outputs.trustee-cpu-policy-file }} | |
| trustee-gpu-policy-file: ${{ steps.generate.outputs.trustee-gpu-policy-file }} | |
| env: | |
| # The single managed ITA policy every deployed initdata pins. The "-a" | |
| # suffix is historical, from a retired blue/green slot pair; the name is | |
| # sent on every update, so changing it renames the live policy in ITA. | |
| ITA_POLICY_ID: cbeedffa-e224-4664-b6b4-573fcd4133d3 | |
| ITA_POLICY_NAME: integritee-policy-a | |
| REASON: ${{ inputs.reason || '' }} | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 | |
| - name: Resolve release version | |
| id: version | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| REQUESTED_VERSION: ${{ inputs.version || '' }} | |
| run: >- | |
| python3 .github/workflows/release-policy/manage.py resolve-version | |
| --requested "$REQUESTED_VERSION" | |
| - name: Install Python | |
| uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 | |
| with: | |
| python-version: "3.12" | |
| - name: Install Python dependencies | |
| run: pip install pyyaml requests | |
| - name: Authenticate to GCP | |
| uses: google-github-actions/auth@7c6bc770dae815cd3e89ee6cdf493a5fab2cc093 # v3.0.0 | |
| with: | |
| workload_identity_provider: ${{ secrets.GCP_WORKLOAD_IDENTITY_PROVIDER }} | |
| service_account: ${{ secrets.GCP_SERVICE_ACCOUNT }} | |
| - name: Configure Docker for Artifact Registry | |
| run: gcloud auth configure-docker us-central1-docker.pkg.dev --quiet | |
| - name: Mint GitHub App token | |
| id: app-token | |
| uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0 | |
| with: | |
| client-id: ${{ secrets.GH_APP_CLIENT_ID }} | |
| private-key: ${{ secrets.GH_APP_PRIVATE_KEY }} | |
| owner: cohere-ai | |
| repositories: cohere-cc-baselines,blobheart | |
| - name: Install ORAS | |
| uses: oras-project/setup-oras@1d808f7d7f6995cc68b7bf507bfe5c5446e1dc9d # v2.0.1 | |
| - name: Log in to GHCR | |
| run: echo "${{ github.token }}" | oras login ghcr.io -u ${{ github.actor }} --password-stdin | |
| # ---------------------------------------------------------------- | |
| # Initialize predicate with workflow-level context | |
| # ---------------------------------------------------------------- | |
| - name: Initialize predicate | |
| env: | |
| VERSION: ${{ steps.version.outputs.version }} | |
| run: >- | |
| python3 .github/workflows/release-policy/manage.py init-predicate | |
| --version "$VERSION" | |
| --output predicate.json | |
| # ---------------------------------------------------------------- | |
| # Generate policy (appends targets + cvm_measure_version to predicate) | |
| # ---------------------------------------------------------------- | |
| - name: Generate policy | |
| id: generate | |
| uses: ./.github/actions/generate-policy | |
| with: | |
| manifest-file: attestation-policy/policy-manifest.yaml | |
| github-token: ${{ steps.app-token.outputs.token }} | |
| output-artifacts-dir: artifacts | |
| policy-types: ita trustee | |
| predicate-file: predicate.json | |
| # ---------------------------------------------------------------- | |
| # Validate manifest | |
| # ---------------------------------------------------------------- | |
| - name: Validate policy manifest | |
| uses: ./.github/actions/validate-manifest | |
| with: | |
| manifest-file: attestation-policy/policy-manifest.yaml | |
| # ---------------------------------------------------------------- | |
| # Hand off to publish, and leave the same files behind for inspection | |
| # ---------------------------------------------------------------- | |
| - name: Upload workflow artifacts | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | |
| with: | |
| name: attestation-release-assets | |
| path: | | |
| ${{ steps.generate.outputs.ita-policy-file }} | |
| ${{ steps.generate.outputs.trustee-cpu-policy-file }} | |
| ${{ steps.generate.outputs.trustee-gpu-policy-file }} | |
| predicate.json | |
| publish: | |
| needs: generate | |
| if: github.ref == 'refs/heads/main' && (github.event_name == 'push' || inputs.publish) | |
| runs-on: ubuntu-latest | |
| environment: release | |
| permissions: | |
| contents: read # the release itself is created with the App token below | |
| attestations: write | |
| id-token: write # Sigstore | |
| env: | |
| ITA_POLICY_ID: cbeedffa-e224-4664-b6b4-573fcd4133d3 | |
| ITA_POLICY_NAME: integritee-policy-a | |
| REASON: ${{ inputs.reason || '' }} | |
| steps: | |
| # Pin to the commit generate ran against, so a push landing mid-run | |
| # cannot pair the downloaded policy with a different manifest. | |
| - name: Checkout | |
| uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 | |
| with: | |
| ref: ${{ github.sha }} | |
| - name: Install Python | |
| uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 | |
| with: | |
| python-version: "3.12" | |
| - name: Install Python dependencies | |
| run: pip install requests | |
| - name: Download generated policy | |
| uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 | |
| with: | |
| name: attestation-release-assets | |
| - name: Mint Integritee release token | |
| id: release-token | |
| uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0 | |
| with: | |
| client-id: ${{ secrets.CC_POLICY_APP_CLIENT_ID }} | |
| private-key: ${{ secrets.CC_POLICY_APP_PRIVATE_KEY }} | |
| owner: cohere-ai | |
| repositories: integritee | |
| permission-contents: write | |
| # ---------------------------------------------------------------- | |
| # Upload to ITA, then read it back to prove the stored policy is exactly | |
| # what generate produced | |
| # ---------------------------------------------------------------- | |
| - name: Upload policy to ITA | |
| id: upload | |
| uses: ./.github/actions/upload-ita-policy | |
| with: | |
| policy-file: ${{ needs.generate.outputs.ita-policy-file }} | |
| policy-id: ${{ env.ITA_POLICY_ID }} | |
| policy-name: ${{ env.ITA_POLICY_NAME }} | |
| ita-api-key: ${{ secrets.ITA_ADMIN_API_KEY }} | |
| ita-api-url: ${{ secrets.ITA_API_URL }} | |
| predicate-file: predicate.json | |
| - name: Fetch uploaded policy from ITA | |
| id: fetch | |
| uses: ./.github/actions/fetch-ita-policy | |
| with: | |
| policy-id: ${{ env.ITA_POLICY_ID }} | |
| ita-api-key: ${{ secrets.ITA_ADMIN_API_KEY }} | |
| ita-api-url: ${{ secrets.ITA_API_URL }} | |
| - name: Verify uploaded policy | |
| run: diff "${{ needs.generate.outputs.ita-policy-file }}" "${{ steps.fetch.outputs.policy-file }}" | |
| # ---------------------------------------------------------------- | |
| # Attest policies with Sigstore | |
| # ---------------------------------------------------------------- | |
| # One bundle with several subjects, so a single Rekor entry covers every | |
| # released policy. The manifest is named too: its content is already in | |
| # the predicate, but a subject gives it a digest a consumer can check | |
| # directly instead of diffing a download against the predicate. | |
| - name: Attest policies with Sigstore | |
| id: attest | |
| uses: actions/attest@59d89421af93a897026c735860bf21b6eb4f7b26 # v4.1.0 | |
| with: | |
| subject-path: | | |
| ${{ needs.generate.outputs.ita-policy-file }} | |
| ${{ needs.generate.outputs.trustee-cpu-policy-file }} | |
| ${{ needs.generate.outputs.trustee-gpu-policy-file }} | |
| attestation-policy/policy-manifest.yaml | |
| predicate-type: https://cohere.com/attestation-policy/v1 | |
| predicate-path: predicate.json | |
| # ---------------------------------------------------------------- | |
| # Create GitHub Release | |
| # ---------------------------------------------------------------- | |
| - name: Prepare release assets | |
| env: | |
| BUNDLE_PATH: ${{ steps.attest.outputs.bundle-path }} | |
| POLICY_ID: ${{ env.ITA_POLICY_ID }} | |
| run: >- | |
| python3 .github/workflows/release-policy/manage.py prepare-assets | |
| --policy "${{ needs.generate.outputs.ita-policy-file }}" | |
| "${{ needs.generate.outputs.trustee-cpu-policy-file }}" | |
| "${{ needs.generate.outputs.trustee-gpu-policy-file }}" | |
| --manifest attestation-policy/policy-manifest.yaml | |
| --predicate predicate.json | |
| --bundle "$BUNDLE_PATH" | |
| --output-dir release-assets | |
| --release-notes release-body.md | |
| --policy-id "$POLICY_ID" | |
| --reason "$REASON" | |
| - name: Create release | |
| env: | |
| GH_TOKEN: ${{ steps.release-token.outputs.token }} | |
| VERSION: ${{ needs.generate.outputs.version }} | |
| run: >- | |
| gh release create "$VERSION" | |
| --title "Attestation Policy $VERSION" | |
| --notes-file release-body.md | |
| release-assets/ita_policy.rego | |
| release-assets/trustee_policy_cpu.rego | |
| release-assets/trustee_policy_gpu.rego | |
| release-assets/policy-manifest.yaml | |
| release-assets/policy-manifest-bundle.tar.gz | |
| release-assets/predicate.json | |
| release-assets/attestation-bundle.sigstore.json |