Skip to content

feat(policy): verify MRTD via ITA CSP RIM instead of pinned values (#49) #60

feat(policy): verify MRTD via ITA CSP RIM instead of pinned values (#49)

feat(policy): verify MRTD via ITA CSP RIM instead of pinned values (#49) #60

# 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