mirror of
https://github.com/edgelesssys/constellation.git
synced 2024-12-28 08:59:34 -05:00
ci: refactor image measurement generation (#1152)
* Merge measurements.image.json and measurements.json into latter. * Use static (known) measurement values for the ones we cannot precompute. Signed-off-by: Fabian Kammel <fk@edgeless.systems>
This commit is contained in:
parent
e01ddc08c2
commit
4c5ab7c5e9
2
.github/actions/e2e_verify/action.yml
vendored
2
.github/actions/e2e_verify/action.yml
vendored
@ -20,7 +20,7 @@ runs:
|
||||
- name: Fetch & write measurements
|
||||
shell: bash
|
||||
run: |
|
||||
MEASUREMENTS=$(curl -fsSL https://cdn.confidential.cloud/constellation/v1/${{ inputs.osImage }}/image/csp/${{ inputs.cloudProvider }}/measurements.image.json | jq '.measurements' -r)
|
||||
MEASUREMENTS=$(curl -fsSL https://cdn.confidential.cloud/constellation/v1/${{ inputs.osImage }}/image/csp/${{ inputs.cloudProvider }}/measurements.json | jq '.measurements' -r)
|
||||
for key in $(echo $MEASUREMENTS | jq 'keys[]' -r); do
|
||||
echo Updating $key to $(echo $MEASUREMENTS | jq ".\"$key\"" -r)
|
||||
yq -i ".provider.${{ inputs.cloudProvider }}.measurements.[$key] = $(echo $MEASUREMENTS | jq ".\"$key\"")" constellation-conf.yaml
|
||||
|
147
.github/workflows/build-os-image.yml
vendored
147
.github/workflows/build-os-image.yml
vendored
@ -581,18 +581,163 @@ jobs:
|
||||
jq -sSc '.[0] * .[1] * .[2] * .[3]' ${{ github.workspace }}/pcr-* > ${{ github.workspace }}/pcrs-${{ matrix.csp }}.json
|
||||
echo "::endgroup::"
|
||||
|
||||
- name: Add static PCRs
|
||||
run: |
|
||||
OUTFLAGS="-I 0 -o json -i"
|
||||
PCRFILE="${{ github.workspace }}/pcrs-${{ matrix.csp }}.json"
|
||||
|
||||
case ${{ matrix.csp }} in
|
||||
aws)
|
||||
yq e '.csp = "AWS" |
|
||||
.image = "${{ needs.build-settings.outputs.imageNameShort }}" |
|
||||
.measurements.0.warnOnly = true |
|
||||
.measurements.0.expected = "737f767a12f54e70eecbc8684011323ae2fe2dd9f90785577969d7a2013e8c12" |
|
||||
.measurements.2.warnOnly = true |
|
||||
.measurements.2.expected = "3d458cfe55cc03ea1f443f1562beec8df51c75e14a9fcf9a7234a13f198e7969" |
|
||||
.measurements.3.warnOnly = true |
|
||||
.measurements.3.expected = "3d458cfe55cc03ea1f443f1562beec8df51c75e14a9fcf9a7234a13f198e7969" |
|
||||
.measurements.4.warnOnly = false |
|
||||
.measurements.5.warnOnly = true |
|
||||
.measurements.5.expected = "73d13bf0a9dc9a201a337150a0a4adb06e6b2206eefaeb4fbb0fe5dac6202fd3" |
|
||||
.measurements.6.warnOnly = true |
|
||||
.measurements.6.expected = "3d458cfe55cc03ea1f443f1562beec8df51c75e14a9fcf9a7234a13f198e7969" |
|
||||
.measurements.7.warnOnly = true |
|
||||
.measurements.7.expected = "120e498db2a224bd512b6efc9b0234f843e10bf061eb7a76ecca5509a2238901" |
|
||||
.measurements.8.warnOnly = false |
|
||||
.measurements.9.warnOnly = false |
|
||||
.measurements.11.warnOnly = false |
|
||||
.measurements.12.warnOnly = false |
|
||||
.measurements.13.warnOnly = false |
|
||||
.measurements.14.warnOnly = true |
|
||||
.measurements.14.expected = "d7c4cc7ff7933022f013e03bdee875b91720b5b86cf1753cad830f95e791926f" |
|
||||
.measurements.15.warnOnly = false' \
|
||||
"${OUTFLAGS}" "${PCRFILE}"
|
||||
;;
|
||||
azure)
|
||||
yq e '.csp = "Azure" |
|
||||
.image = "${{ needs.build-settings.outputs.imageNameShort }}" |
|
||||
.measurements.1.warnOnly = true |
|
||||
.measurements.1.expected = "3d458cfe55cc03ea1f443f1562beec8df51c75e14a9fcf9a7234a13f198e7969" |
|
||||
.measurements.2.warnOnly = true |
|
||||
.measurements.2.expected = "3d458cfe55cc03ea1f443f1562beec8df51c75e14a9fcf9a7234a13f198e7969" |
|
||||
.measurements.3.warnOnly = true |
|
||||
.measurements.3.expected = "3d458cfe55cc03ea1f443f1562beec8df51c75e14a9fcf9a7234a13f198e7969" |
|
||||
.measurements.4.warnOnly = false |
|
||||
.measurements.5.warnOnly = true |
|
||||
.measurements.5.expected = "b731e1a1c70e65e43a4ed54d668b7e585163d9c42f1ed4096174451ff0ba62f2" |
|
||||
.measurements.7.warnOnly = true |
|
||||
.measurements.7.expected = "346547a8ce5957af27e552427d6b9e6d9cb502f0156e9155380451eea1b3f0ed" |
|
||||
.measurements.8.warnOnly = false |
|
||||
.measurements.9.warnOnly = false |
|
||||
.measurements.11.warnOnly = false |
|
||||
.measurements.12.warnOnly = false |
|
||||
.measurements.13.warnOnly = false |
|
||||
.measurements.14.warnOnly = true |
|
||||
.measurements.14.expected = "d7c4cc7ff7933022f013e03bdee875b91720b5b86cf1753cad830f95e791926f" |
|
||||
.measurements.15.warnOnly = false' \
|
||||
"${OUTFLAGS}" "${PCRFILE}"
|
||||
;;
|
||||
gcp)
|
||||
yq e '.csp = "GCP" |
|
||||
.image = "${{ needs.build-settings.outputs.imageNameShort }}" |
|
||||
.measurements.0.warnOnly = false |
|
||||
.measurements.0.expected = "0f35c214608d93c7a6e68ae7359b4a8be5a0e99eea9107ece427c4dea4e439cf" |
|
||||
.measurements.1.warnOnly = true |
|
||||
.measurements.1.expected = "745f2fb4235e4647aa0ad5ace781cd929eb68c28870e7dd5d1a1535854325e56" |
|
||||
.measurements.2.warnOnly = true |
|
||||
.measurements.2.expected = "3d458cfe55cc03ea1f443f1562beec8df51c75e14a9fcf9a7234a13f198e7969" |
|
||||
.measurements.3.warnOnly = true |
|
||||
.measurements.3.expected = "3d458cfe55cc03ea1f443f1562beec8df51c75e14a9fcf9a7234a13f198e7969" |
|
||||
.measurements.4.warnOnly = false |
|
||||
.measurements.5.warnOnly = true |
|
||||
.measurements.5.expected = "9d5f8c2e0464c69552e593be32a4b089f7e77f4ed12d60cdcd94120b6ccaccf8" |
|
||||
.measurements.6.warnOnly = true |
|
||||
.measurements.6.expected = "3d458cfe55cc03ea1f443f1562beec8df51c75e14a9fcf9a7234a13f198e7969" |
|
||||
.measurements.7.warnOnly = true |
|
||||
.measurements.7.expected = "b1e9b305325c51b93da58cbf7f92512d8eebfa01143e4d8844e40e062e9b6cd5" |
|
||||
.measurements.8.warnOnly = false |
|
||||
.measurements.9.warnOnly = false |
|
||||
.measurements.10.warnOnly = true |
|
||||
.measurements.10.expected = "7f96fbc55e1d2a0de46e5d44658c06ef102d1198703efa69f2ea6b5aa1c9a176" |
|
||||
.measurements.11.warnOnly = false |
|
||||
.measurements.12.warnOnly = false |
|
||||
.measurements.13.warnOnly = false |
|
||||
.measurements.14.warnOnly = true |
|
||||
.measurements.14.expected = "d7c4cc7ff7933022f013e03bdee875b91720b5b86cf1753cad830f95e791926f" |
|
||||
.measurements.15.warnOnly = false' \
|
||||
"${OUTFLAGS}" "${PCRFILE}"
|
||||
;;
|
||||
qemu)
|
||||
yq e '.csp = "QEMU" |
|
||||
.image = "${{ needs.build-settings.outputs.imageNameShort }}" |
|
||||
.measurements.4.warnOnly = false |
|
||||
.measurements.8.warnOnly = false |
|
||||
.measurements.9.warnOnly = false |
|
||||
.measurements.11.warnOnly = false |
|
||||
.measurements.12.warnOnly = false |
|
||||
.measurements.13.warnOnly = false |
|
||||
.measurements.15.warnOnly = false' \
|
||||
"${OUTFLAGS}" "${PCRFILE}"
|
||||
;;
|
||||
*)
|
||||
echo "Unknown CSP: ${{ matrix.csp }}"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
- name: Upload expected PCRs as artifact
|
||||
uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2
|
||||
with:
|
||||
name: pcrs
|
||||
path: pcrs-${{ matrix.csp }}.json
|
||||
|
||||
- name: Install Cosign
|
||||
uses: sigstore/cosign-installer@9becc617647dfa20ae7b1151972e9b3a2c338a2b # tag=v2.8.1
|
||||
|
||||
- name: Install Rekor
|
||||
shell: bash
|
||||
run: |
|
||||
curl -fsSLO https://github.com/sigstore/rekor/releases/download/v0.12.0/rekor-cli-linux-amd64
|
||||
sudo install rekor-cli-linux-amd64 /usr/local/bin/rekor-cli
|
||||
rm rekor-cli-linux-amd64
|
||||
|
||||
- name: Sign measurements
|
||||
shell: bash
|
||||
env:
|
||||
COSIGN_PUBLIC_KEY: ${{ inputs.isRelease && secrets.COSIGN_PUBLIC_KEY || secrets.COSIGN_DEV_PUBLIC_KEY }}
|
||||
COSIGN_PRIVATE_KEY: ${{ inputs.isRelease && secrets.COSIGN_PRIVATE_KEY || secrets.COSIGN_DEV_PRIVATE_KEY }}
|
||||
COSIGN_PASSWORD: ${{ inputs.isRelease && secrets.COSIGN_PASSWORD || secrets.COSIGN_DEV_PASSWORD }}
|
||||
run: |
|
||||
echo "${COSIGN_PUBLIC_KEY}" > cosign.pub
|
||||
# Enabling experimental mode also publishes signature to Rekor
|
||||
COSIGN_EXPERIMENTAL=1 cosign sign-blob --key env://COSIGN_PRIVATE_KEY \
|
||||
"${{ github.workspace }}/pcrs-${{ matrix.csp }}.json" > "${{ github.workspace }}/pcrs-${{ matrix.csp }}.json.sig"
|
||||
# Verify - As documentation & check
|
||||
# Local Signature (input: artifact, key, signature)
|
||||
cosign verify-blob --key cosign.pub \
|
||||
--signature "${{ github.workspace }}/pcrs-${{ matrix.csp }}.json.sig" \
|
||||
"${{ github.workspace }}/pcrs-${{ matrix.csp }}.json"
|
||||
# Transparency Log Signature (input: artifact, key)
|
||||
uuid=$(rekor-cli search --artifact "${{ github.workspace }}/pcrs-${{ matrix.csp }}.json" | tail -n 1)
|
||||
sig=$(rekor-cli get --uuid="${uuid}" --format=json | jq -r .Body.HashedRekordObj.signature.content)
|
||||
cosign verify-blob --key cosign.pub --signature <(echo "${sig}") "${{ github.workspace }}/pcrs-${{ matrix.csp }}.json"
|
||||
|
||||
- name: Upload signature of expected PCRs as artifact
|
||||
uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2
|
||||
with:
|
||||
name: pcrs-sig
|
||||
path: pcrs-${{ matrix.csp }}.json.sig
|
||||
|
||||
- name: Upload expected PCRs to S3
|
||||
shell: bash
|
||||
run: |
|
||||
aws s3 cp \
|
||||
"pcrs-${{ matrix.csp }}.json" \
|
||||
"s3://cdn-constellation-backend/${{needs.build-settings.outputs.imageApiBasePath}}/csp/${{ matrix.csp }}/measurements.image.json" \
|
||||
"s3://cdn-constellation-backend/${{needs.build-settings.outputs.imageApiBasePath}}/csp/${{ matrix.csp }}/measurements.json" \
|
||||
--no-progress
|
||||
aws s3 cp \
|
||||
"pcrs-${{ matrix.csp }}.json" \
|
||||
"s3://cdn-constellation-backend/${{needs.build-settings.outputs.imageApiBasePath}}/csp/${{ matrix.csp }}/measurements.json.sig" \
|
||||
--no-progress
|
||||
|
||||
generate-sbom:
|
||||
|
391
.github/workflows/generate-measurements.yml
vendored
391
.github/workflows/generate-measurements.yml
vendored
@ -1,391 +0,0 @@
|
||||
name: Generate and Upload Measurements
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
osImage:
|
||||
description: "Full name of OS image (CSP independent image version UID)."
|
||||
type: string
|
||||
required: true
|
||||
isDebugImage:
|
||||
description: "Is OS image a debug image?"
|
||||
type: boolean
|
||||
required: true
|
||||
signMeasurements:
|
||||
description: "Sign and upload the measurements?"
|
||||
type: boolean
|
||||
required: true
|
||||
isRelease:
|
||||
description: "Is this a release?"
|
||||
type: boolean
|
||||
default: false
|
||||
required: false
|
||||
ref:
|
||||
type: string
|
||||
description: "Git ref to checkout"
|
||||
required: false
|
||||
workflow_call:
|
||||
inputs:
|
||||
osImage:
|
||||
description: "Full name of OS image (CSP independent image version UID)."
|
||||
type: string
|
||||
required: true
|
||||
isDebugImage:
|
||||
description: "Is OS image a debug image?"
|
||||
type: boolean
|
||||
required: true
|
||||
signMeasurements:
|
||||
description: "Sign and upload the measurements?"
|
||||
type: boolean
|
||||
required: true
|
||||
isRelease:
|
||||
description: "Is this a release?"
|
||||
type: boolean
|
||||
ref:
|
||||
type: string
|
||||
description: "Git ref to checkout"
|
||||
required: false
|
||||
|
||||
jobs:
|
||||
calculate-measurements-on-csp:
|
||||
name: "Calculate Measurements on CSP"
|
||||
runs-on: ubuntu-22.04
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
provider: ["aws", "azure", "gcp", "qemu"]
|
||||
permissions:
|
||||
id-token: write
|
||||
contents: read
|
||||
env:
|
||||
ARM_CLIENT_ID: ${{ secrets.AZURE_E2E_CLIENT_ID }}
|
||||
ARM_CLIENT_SECRET: ${{ secrets.AZURE_E2E_CLIENT_SECRET }}
|
||||
ARM_SUBSCRIPTION_ID: ${{ secrets.AZURE_E2E_SUBSCRIPTION_ID }}
|
||||
ARM_TENANT_ID: ${{ secrets.AZURE_E2E_TENANT_ID }}
|
||||
outputs:
|
||||
ref: ${{ steps.extract.outputs.ref }}
|
||||
stream: ${{ steps.extract.outputs.stream }}
|
||||
version: ${{ steps.extract.outputs.version }}
|
||||
steps:
|
||||
- name: Check out repository
|
||||
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
|
||||
with:
|
||||
ref: ${{ inputs.ref || github.head_ref }}
|
||||
|
||||
- name: Extract ref, stream and version
|
||||
id: extract
|
||||
uses: ./.github/actions/shortname
|
||||
with:
|
||||
shortname: ${{ inputs.osImage }}
|
||||
|
||||
- name: Check if image definition from build pipeline exists
|
||||
shell: bash
|
||||
run: |
|
||||
wget -O /dev/null "https://cdn.confidential.cloud/constellation/v1/ref/${{ steps.extract.outputs.ref }}/stream/${{ steps.extract.outputs.stream }}/${{ steps.extract.outputs.version }}/image/info.json"
|
||||
|
||||
- name: Setup Go environment
|
||||
uses: actions/setup-go@6edd4406fa81c3da01a34fa6f6343087c207a568 # v3.5.0
|
||||
with:
|
||||
go-version: "1.19.5"
|
||||
|
||||
- name: Build hack/pcr-reader
|
||||
shell: bash
|
||||
working-directory: hack/pcr-reader
|
||||
run: |
|
||||
go build .
|
||||
pwd >> "$GITHUB_PATH"
|
||||
|
||||
- name: Login to Azure
|
||||
if: matrix.provider == 'azure'
|
||||
uses: ./.github/actions/login_azure
|
||||
with:
|
||||
azure_credentials: ${{ secrets.AZURE_E2E_CREDENTIALS }}
|
||||
|
||||
- name: Create Azure resource group
|
||||
id: az_resource_group_gen
|
||||
if: matrix.provider == 'azure'
|
||||
shell: bash
|
||||
run: |
|
||||
uuid=$(cat /proc/sys/kernel/random/uuid)
|
||||
name=e2e-test-${uuid%%-*}
|
||||
az group create --location northeurope --name "$name" --tags e2e
|
||||
echo "res_group_name=$name" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Create Cluster in E2E Test environment
|
||||
id: create_cluster
|
||||
if: matrix.provider != 'qemu'
|
||||
uses: ./.github/actions/e2e_test
|
||||
with:
|
||||
workerNodesCount: 1
|
||||
controlNodesCount: 1
|
||||
cloudProvider: ${{ matrix.provider }}
|
||||
gcpProject: ${{ secrets.GCP_E2E_PROJECT }}
|
||||
gcp_service_account: "constellation-e2e@constellation-331613.iam.gserviceaccount.com"
|
||||
gcpClusterServiceAccountKey: ${{ secrets.GCP_CLUSTER_SERVICE_ACCOUNT }}
|
||||
azureSubscription: ${{ secrets.AZURE_E2E_SUBSCRIPTION_ID }}
|
||||
azureTenant: ${{ secrets.AZURE_E2E_TENANT_ID }}
|
||||
azureClientID: ${{ secrets.AZURE_E2E_CLIENT_ID }}
|
||||
azureClientSecret: ${{ secrets.AZURE_E2E_CLIENT_SECRET }}
|
||||
azureUserAssignedIdentity: ${{ secrets.AZURE_E2E_USER_ASSIGNED_IDENTITY }}
|
||||
azureResourceGroup: ${{ steps.az_resource_group_gen.outputs.res_group_name }}
|
||||
osImage: ${{ inputs.osImage }}
|
||||
isDebugImage: ${{ inputs.isDebugImage }}
|
||||
test: "nop"
|
||||
|
||||
- name: Fetch PCRs from running cluster
|
||||
if: matrix.provider != 'qemu'
|
||||
shell: bash
|
||||
env:
|
||||
CSP: ${{ matrix.provider }}
|
||||
run: |
|
||||
KUBECONFIG="${PWD}/constellation-admin.conf" kubectl rollout status ds/verification-service -n kube-system --timeout=3m
|
||||
CONSTELL_IP=$(jq -r ".ip" constellation-id.json)
|
||||
mkdir -p "${{ github.workspace }}/generated-measurements"
|
||||
pcr-reader -constell-ip "${CONSTELL_IP}" -format json -metadata -csp "${{ matrix.provider }}" -image "${{ inputs.osImage }}" > "${{ github.workspace }}/generated-measurements/measurements.json"
|
||||
echo "All PCRs of current boot:"
|
||||
cat "${{ github.workspace }}/generated-measurements/measurements.json"
|
||||
case ${CSP} in
|
||||
aws)
|
||||
yq e 'del(.measurements.[1,10,16,17,18,19,20,21,22,23])' -i "${{ github.workspace }}/generated-measurements/measurements.json"
|
||||
yq '.measurements.4.warnOnly = false |
|
||||
.measurements.8.warnOnly = false |
|
||||
.measurements.9.warnOnly = false |
|
||||
.measurements.11.warnOnly = false |
|
||||
.measurements.12.warnOnly = false |
|
||||
.measurements.13.warnOnly = false |
|
||||
.measurements.15.warnOnly = false |
|
||||
.measurements.15.expected = "0000000000000000000000000000000000000000000000000000000000000000"' \
|
||||
-I 0 -o json -i "${{ github.workspace }}/generated-measurements/measurements.json"
|
||||
;;
|
||||
azure)
|
||||
yq e 'del(.measurements.[0,6,10,16,17,18,19,20,21,22,23])' -I 0 -o json -i "${{ github.workspace }}/generated-measurements/measurements.json"
|
||||
yq '.measurements.4.warnOnly = false |
|
||||
.measurements.8.warnOnly = false |
|
||||
.measurements.9.warnOnly = false |
|
||||
.measurements.11.warnOnly = false |
|
||||
.measurements.12.warnOnly = false |
|
||||
.measurements.13.warnOnly = false |
|
||||
.measurements.15.warnOnly = false |
|
||||
.measurements.15.expected = "0000000000000000000000000000000000000000000000000000000000000000"' \
|
||||
-I 0 -o json -i "${{ github.workspace }}/generated-measurements/measurements.json"
|
||||
;;
|
||||
gcp)
|
||||
yq e 'del(.measurements.[16,17,18,19,20,21,22,23])' -I 0 -o json -i "${{ github.workspace }}/generated-measurements/measurements.json"
|
||||
yq '.measurements.0.warnOnly = false |
|
||||
.measurements.4.warnOnly = false |
|
||||
.measurements.8.warnOnly = false |
|
||||
.measurements.9.warnOnly = false |
|
||||
.measurements.11.warnOnly = false |
|
||||
.measurements.12.warnOnly = false |
|
||||
.measurements.13.warnOnly = false |
|
||||
.measurements.15.warnOnly = false |
|
||||
.measurements.15.expected = "0000000000000000000000000000000000000000000000000000000000000000"' \
|
||||
-I 0 -o json -i "${{ github.workspace }}/generated-measurements/measurements.json"
|
||||
;;
|
||||
*)
|
||||
echo "CSP case check failed!"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
echo "PCRs to be published after removing known variable ones:"
|
||||
cat "${{ github.workspace }}/generated-measurements/measurements.json"
|
||||
mv "${{ github.workspace }}/generated-measurements/measurements.json" "${{ github.workspace }}/generated-measurements/measurements-${{ matrix.provider }}.json"
|
||||
|
||||
- name: Set PCRs for QEMU
|
||||
if: matrix.provider == 'qemu'
|
||||
shell: bash
|
||||
env:
|
||||
ref: ${{ steps.extract.outputs.ref }}
|
||||
stream: ${{ steps.extract.outputs.stream }}
|
||||
version: ${{ steps.extract.outputs.version }}
|
||||
run: |
|
||||
path="constellation/v1/ref/${ref}/stream/${stream}/${version}/image/csp/${{ matrix.provider }}/measurements.image.json"
|
||||
mkdir -p "${{ github.workspace }}/generated-measurements"
|
||||
wget -O ${{ github.workspace }}/generated-measurements/measurements.image.json "https://cdn.confidential.cloud/${path}"
|
||||
jq '.measurements' < ${{ github.workspace }}/generated-measurements/measurements.image.json | jq '{"measurements": .}' > ${{ github.workspace }}/generated-measurements/measurements.json
|
||||
cat "${{ github.workspace }}/generated-measurements/measurements.json"
|
||||
yq '
|
||||
.csp = "QEMU" |
|
||||
.image = "${{ steps.extract.outputs.version }}" |
|
||||
.measurements.4.warnOnly = false |
|
||||
.measurements.8.warnOnly = false |
|
||||
.measurements.9.warnOnly = false |
|
||||
.measurements.11.warnOnly = false |
|
||||
.measurements.12.warnOnly = false |
|
||||
.measurements.13.warnOnly = false |
|
||||
.measurements.15.warnOnly = false |
|
||||
.measurements.15.expected = "0000000000000000000000000000000000000000000000000000000000000000"' \
|
||||
-I 0 -o json -i "${{ github.workspace }}/generated-measurements/measurements.json"
|
||||
mv "${{ github.workspace }}/generated-measurements/measurements.json" "${{ github.workspace }}/generated-measurements/measurements-${{ matrix.provider }}.json"
|
||||
|
||||
- name: Upload measurements as artifact
|
||||
uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2
|
||||
with:
|
||||
name: measurements-${{ matrix.provider }}.json
|
||||
path: "${{ github.workspace }}/generated-measurements"
|
||||
|
||||
- name: Always terminate cluster
|
||||
if: always() && matrix.provider != 'qemu'
|
||||
continue-on-error: true
|
||||
uses: ./.github/actions/constellation_destroy
|
||||
with:
|
||||
kubeconfig: ${{ steps.create_cluster.outputs.kubeconfig }}
|
||||
|
||||
- name: Always destroy Azure resource group
|
||||
if: always() && matrix.provider == 'azure'
|
||||
shell: bash
|
||||
run: |
|
||||
az group delete \
|
||||
--name ${{ steps.az_resource_group_gen.outputs.res_group_name }} \
|
||||
--force-deletion-types Microsoft.Compute/virtualMachineScaleSets \
|
||||
--force-deletion-types Microsoft.Compute/virtualMachines \
|
||||
--no-wait \
|
||||
--yes
|
||||
|
||||
validate-measurements:
|
||||
name: "Validate Measurements"
|
||||
needs: [calculate-measurements-on-csp]
|
||||
runs-on: ubuntu-22.04
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
provider: ["aws", "azure", "gcp", "qemu"]
|
||||
steps:
|
||||
- name: Check out repository
|
||||
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
|
||||
with:
|
||||
ref: ${{ inputs.ref || github.head_ref }}
|
||||
|
||||
- name: Setup Go environment
|
||||
uses: actions/setup-go@6edd4406fa81c3da01a34fa6f6343087c207a568 # v3.5.0
|
||||
with:
|
||||
go-version: "1.19.5"
|
||||
|
||||
- name: Build hack/pcr-compare
|
||||
shell: bash
|
||||
working-directory: hack/pcr-compare
|
||||
run: |
|
||||
go build .
|
||||
pwd >> "$GITHUB_PATH"
|
||||
|
||||
- name: Download calculated measurements from artifact
|
||||
uses: actions/download-artifact@9782bd6a9848b53b110e712e20e42d89988822b7 # tag=v3.1.1
|
||||
with:
|
||||
name: measurements-${{ matrix.provider }}.json
|
||||
path: "${{ github.workspace }}/generated-measurements"
|
||||
|
||||
- name: Download expected measurements from build pipeline for image
|
||||
shell: bash
|
||||
env:
|
||||
ref: ${{ needs.calculate-measurements-on-csp.outputs.ref }}
|
||||
stream: ${{ needs.calculate-measurements-on-csp.outputs.stream }}
|
||||
version: ${{ needs.calculate-measurements-on-csp.outputs.version }}
|
||||
run: |
|
||||
path="constellation/v1/ref/${ref}/stream/${stream}/${version}/image/csp/${{ matrix.provider }}/measurements.image.json"
|
||||
mkdir -p ${{ github.workspace }}/expected-measurements
|
||||
wget -O ${{ github.workspace }}/expected-measurements/measurements.image.json "https://cdn.confidential.cloud/${path}"
|
||||
cat ${{ github.workspace }}/expected-measurements/measurements.image.json
|
||||
|
||||
- name: Check if expected measurements == actual measurements from running cluster
|
||||
shell: bash
|
||||
run: |
|
||||
pcr-compare ${{ github.workspace }}/expected-measurements/measurements.image.json ${{ github.workspace }}/generated-measurements/measurements-${{ matrix.provider }}.json
|
||||
|
||||
sign-measurements:
|
||||
name: "Sign Measurements"
|
||||
if: inputs.signMeasurements
|
||||
needs: [calculate-measurements-on-csp, validate-measurements]
|
||||
runs-on: ubuntu-22.04
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
provider: ["aws", "azure", "gcp", "qemu"]
|
||||
permissions:
|
||||
id-token: write
|
||||
contents: read
|
||||
steps:
|
||||
- name: Install Cosign
|
||||
uses: sigstore/cosign-installer@9becc617647dfa20ae7b1151972e9b3a2c338a2b # tag=v2.8.1
|
||||
|
||||
- name: Install Rekor
|
||||
shell: bash
|
||||
run: |
|
||||
curl -fsSLO https://github.com/sigstore/rekor/releases/download/v0.12.0/rekor-cli-linux-amd64
|
||||
sudo install rekor-cli-linux-amd64 /usr/local/bin/rekor-cli
|
||||
rm rekor-cli-linux-amd64
|
||||
|
||||
- name: Download measurements from artifact
|
||||
uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2
|
||||
with:
|
||||
name: measurements-${{ matrix.provider }}.json
|
||||
path: "${{ github.workspace }}/generated-measurements"
|
||||
|
||||
- name: Sign measurements
|
||||
shell: bash
|
||||
env:
|
||||
COSIGN_PUBLIC_KEY: ${{ inputs.isRelease && secrets.COSIGN_PUBLIC_KEY || secrets.COSIGN_DEV_PUBLIC_KEY }}
|
||||
COSIGN_PRIVATE_KEY: ${{ inputs.isRelease && secrets.COSIGN_PRIVATE_KEY || secrets.COSIGN_DEV_PRIVATE_KEY }}
|
||||
COSIGN_PASSWORD: ${{ inputs.isRelease && secrets.COSIGN_PASSWORD || secrets.COSIGN_DEV_PASSWORD }}
|
||||
run: |
|
||||
echo "${COSIGN_PUBLIC_KEY}" > cosign.pub
|
||||
# Enabling experimental mode also publishes signature to Rekor
|
||||
COSIGN_EXPERIMENTAL=1 cosign sign-blob --key env://COSIGN_PRIVATE_KEY "${{ github.workspace }}/generated-measurements/measurements-${{ matrix.provider }}.json" > "${{ github.workspace }}/generated-measurements/measurements-${{ matrix.provider }}.json.sig"
|
||||
# Verify - As documentation & check
|
||||
# Local Signature (input: artifact, key, signature)
|
||||
cosign verify-blob --key cosign.pub --signature "${{ github.workspace }}/generated-measurements/measurements-${{ matrix.provider }}.json.sig" "${{ github.workspace }}/generated-measurements/measurements-${{ matrix.provider }}.json"
|
||||
# Transparency Log Signature (input: artifact, key)
|
||||
uuid=$(rekor-cli search --artifact "${{ github.workspace }}/generated-measurements/measurements-${{ matrix.provider }}.json" | tail -n 1)
|
||||
sig=$(rekor-cli get --uuid="${uuid}" --format=json | jq -r .Body.HashedRekordObj.signature.content)
|
||||
cosign verify-blob --key cosign.pub --signature <(echo "${sig}") "${{ github.workspace }}/generated-measurements/measurements-${{ matrix.provider }}.json"
|
||||
|
||||
- name: Upload signed measurements as artifact
|
||||
uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2
|
||||
with:
|
||||
name: measurements-${{ matrix.provider }}.json.sig
|
||||
path: "${{ github.workspace }}/generated-measurements"
|
||||
|
||||
publish-measurements:
|
||||
name: "Publish Measurements"
|
||||
if: inputs.signMeasurements
|
||||
needs:
|
||||
[calculate-measurements-on-csp, validate-measurements, sign-measurements]
|
||||
runs-on: ubuntu-22.04
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
provider: ["aws", "azure", "gcp", "qemu"]
|
||||
permissions:
|
||||
id-token: write
|
||||
contents: read
|
||||
steps:
|
||||
- name: Download measurements from artifact
|
||||
uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2
|
||||
with:
|
||||
name: measurements-${{ matrix.provider }}.json
|
||||
path: "${{ github.workspace }}/generated-measurements"
|
||||
|
||||
- name: Download signature from artifact
|
||||
uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2
|
||||
with:
|
||||
name: measurements-${{ matrix.provider }}.json.sig
|
||||
path: "${{ github.workspace }}/generated-measurements"
|
||||
|
||||
- name: Login to AWS
|
||||
uses: aws-actions/configure-aws-credentials@67fbcbb121271f7775d2e7715933280b06314838 # tag=v1.7.0
|
||||
with:
|
||||
role-to-assume: arn:aws:iam::795746500882:role/GitHubConstellationImagePipeline
|
||||
aws-region: eu-central-1
|
||||
|
||||
- name: Upload to S3
|
||||
shell: bash
|
||||
env:
|
||||
IMAGE_UID: ${{ inputs.osImage }}
|
||||
PUBLIC_BUCKET_NAME: ${{ secrets.PUBLIC_BUCKET_NAME }}
|
||||
CSP: ${{ matrix.provider }}
|
||||
ref: ${{ needs.calculate-measurements-on-csp.outputs.ref }}
|
||||
stream: ${{ needs.calculate-measurements-on-csp.outputs.stream }}
|
||||
version: ${{ needs.calculate-measurements-on-csp.outputs.version }}
|
||||
run: |
|
||||
S3_PATH=s3://cdn-constellation-backend/constellation/v1/ref/${ref}/stream/${stream}/${version}/image/csp/${{ matrix.provider }}
|
||||
aws s3 cp "${{ github.workspace }}/generated-measurements/measurements-${{ matrix.provider }}.json" "${S3_PATH}/measurements.json"
|
||||
aws s3 cp "${{ github.workspace }}/generated-measurements/measurements-${{ matrix.provider }}.json.sig" "${S3_PATH}/measurements.json.sig"
|
17
.github/workflows/release.yml
vendored
17
.github/workflows/release.yml
vendored
@ -255,24 +255,9 @@ jobs:
|
||||
stream: "stable"
|
||||
ref: ${{ needs.verify-inputs.outputs.RELEASE_BRANCH }}
|
||||
|
||||
generate-measurements:
|
||||
name: Generate OS image measurements
|
||||
needs: [verify-inputs, os-image]
|
||||
uses: ./.github/workflows/generate-measurements.yml
|
||||
permissions:
|
||||
id-token: write
|
||||
contents: read
|
||||
secrets: inherit
|
||||
with:
|
||||
osImage: ${{ inputs.version }}
|
||||
isDebugImage: false
|
||||
signMeasurements: true
|
||||
isRelease: true
|
||||
ref: ${{ needs.verify-inputs.outputs.RELEASE_BRANCH }}
|
||||
|
||||
update-hardcoded-measurements:
|
||||
name: Update hardcoded measurements (in the CLI)
|
||||
needs: [verify-inputs, generate-measurements]
|
||||
needs: [verify-inputs]
|
||||
permissions:
|
||||
contents: write
|
||||
runs-on: ubuntu-22.04
|
||||
|
@ -31,7 +31,6 @@ There may be more API groups in the future (e.g. `cli`)
|
||||
- [`/constellation/v1/ref/<ref>/stream/<stream>/<version>/image/sbom.<format>.json`](image-api.md)
|
||||
- [`/constellation/v1/ref/<ref>/stream/<stream>/<version>/image/csp/<csp>/measurements.json`](image-api.md)
|
||||
- [`/constellation/v1/ref/<ref>/stream/<stream>/<version>/image/csp/<csp>/measurements.json.sig`](image-api.md)
|
||||
- [`/constellation/v1/ref/<ref>/stream/<stream>/<version>/image/csp/<csp>/measurements.image.json`](image-api.md)
|
||||
- [`/constellation/v1/ref/<ref>/stream/<stream>/<version>/image/csp/<csp>/image.raw`](image-api.md)
|
||||
|
||||
## API path identifiers `ref`, `stream` and `version`
|
||||
|
@ -75,9 +75,8 @@ The following HTTP endpoints are available:
|
||||
- [`info.json` returns the lookup table for the given image version.](#image-lookup-table)
|
||||
- `sbom.<format>.json` contains SBOM files for the given image version. The exact formats and file names are TBD.
|
||||
- `GET /constellation/v1/ref/<REF>/stream/<STREAM>/<VERSION>/csp/<csp>/` contains files with measurements and signatures for the given image version and CSP.
|
||||
- `measurements.json` contains the final measurements for the given image version and CSP.
|
||||
- `measurements.json` contains the measurements for the given image version and CSP.
|
||||
- `measurements.json.sig` returns the signature of the measurements file.
|
||||
- `measurements.image.json` returns the measurements generated statically from the image.
|
||||
- `image.raw` returns the raw image for the given image version and CSP.
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user