OS Image Build pipeline: prepare lookup tables and additional artifacts (#560)

This commit is contained in:
Malte Poll 2022-11-16 15:45:10 +01:00 committed by GitHub
parent ca4764c466
commit cdaf1fc476
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 366 additions and 55 deletions

View File

@ -36,9 +36,9 @@ outputs:
awsImagePath:
description: "AWS image path"
value: ${{ steps.aws.outputs.imagePath }}
awsAmiOutput:
description: "AWS ami output path"
value: ${{ steps.aws.outputs.amiOutput }}
awsJsonOutput:
description: "AWS ami json output path"
value: ${{ steps.aws.outputs.jsonOutput }}
awsImageFilename:
description: "AWS raw image filename"
value: ${{ steps.aws.outputs.imageFilename }}
@ -72,6 +72,9 @@ outputs:
azureImagePath:
description: "Azure image path"
value: ${{ steps.azure.outputs.imagePath }}
azureJsonOutput:
description: "Azure image json output path"
value: ${{ steps.azure.outputs.jsonOutput }}
azureSecurityType:
description: "Azure security type"
value: ${{ steps.azure.outputs.securityType }}
@ -108,6 +111,9 @@ outputs:
gcpImagePath:
description: "GCP image path"
value: ${{ steps.gcp.outputs.imagePath }}
gcpJsonOutput:
description: "GCP image json output path"
value: ${{ steps.gcp.outputs.jsonOutput }}
gcpImageName:
description: "GCP image name"
value: ${{ steps.gcp.outputs.imageName }}
@ -117,6 +123,18 @@ outputs:
gcpImageFamily:
description: "GCP image family"
value: ${{ steps.gcp.outputs.imageFamily }}
qemuJsonOutput:
description: "QEMU image json output path"
value: ${{ steps.qemu.outputs.jsonOutput }}
qemuBucket:
description: "QEMU S3 bucket"
value: ${{ steps.qemu.outputs.bucket }}
qemuBaseUrl:
description: "QEMU raw image base URL"
value: ${{ steps.qemu.outputs.baseUrl }}
qemuImagePath:
description: "QEMU image path"
value: ${{ steps.qemu.outputs.imagePath }}
runs:
using: "composite"
@ -142,7 +160,7 @@ runs:
echo "bucket=constellation-images" >> $GITHUB_OUTPUT
echo "efivarsPath=${basePath}/mkosi.output.aws/fedora~36/efivars.bin" >> $GITHUB_OUTPUT
echo "imagePath=${basePath}/mkosi.output.aws/fedora~36/image.raw" >> $GITHUB_OUTPUT
echo "amiOutput=${basePath}/mkosi.output.aws/fedora~36/ami.json" >> $GITHUB_OUTPUT
echo "jsonOutput=${basePath}/mkosi.output.aws/fedora~36/image-upload.json" >> $GITHUB_OUTPUT
echo "imageFilename=image-$(date +%s).raw" >> $GITHUB_OUTPUT
if [ "${imageType}" = release ]
then
@ -172,6 +190,7 @@ runs:
semver: ${{ steps.version.outputs.semanticVersion }}
pseudover: ${{ steps.version.outputs.pseudoVersion }}
branchName: ${{ steps.version.outputs.branchName }}
uploadVariant: ${{ inputs.uploadVariant }}
run: |
echo "resourceGroupName=constellation-images" >> $GITHUB_OUTPUT
echo "region=northeurope" >> $GITHUB_OUTPUT
@ -181,6 +200,7 @@ runs:
echo "publisher=edgelesssys" >> $GITHUB_OUTPUT
echo "rawImagePath=${basePath}/mkosi.output.azure/fedora~36/image.raw" >> $GITHUB_OUTPUT
echo "imagePath=${basePath}/mkosi.output.azure/fedora~36/image.vhd" >> $GITHUB_OUTPUT
echo "jsonOutput=${basePath}/mkosi.output.azure/fedora~36/image-upload${uploadVariant}.json" >> $GITHUB_OUTPUT
# TODO: set default security type to "ConfidentialVM" once replication is possible
securityType=${{ inputs.uploadVariant }}
if [ -z "${securityType}" ]; then
@ -234,6 +254,7 @@ runs:
echo "region=europe-west3" >> $GITHUB_OUTPUT
echo "rawImagePath=${basePath}/mkosi.output.gcp/fedora~36/image.raw" >> $GITHUB_OUTPUT
echo "imagePath=${basePath}/mkosi.output.gcp/fedora~36/image.tar.gz" >> $GITHUB_OUTPUT
echo "jsonOutput=${basePath}/mkosi.output.gcp/fedora~36/image-upload.json" >> $GITHUB_OUTPUT
if [ "${imageType}" = release ]
then
echo "imageName=constellation-${imageVersion//./-}" >> $GITHUB_OUTPUT
@ -249,3 +270,15 @@ runs:
echo "imageFilename=constellation-${timestamp}.tar.gz" >> $GITHUB_OUTPUT
echo "imageFamily=constellation-${branchName}" >> $GITHUB_OUTPUT
fi
- name: Configure QEMU input variables
id: qemu
if: ${{ inputs.csp == 'qemu' }}
shell: bash
env:
basePath: ${{ inputs.basePath }}
run: |
echo "bucket=cdn-confidential-cloud-backend" >> $GITHUB_OUTPUT
echo "baseUrl=https://cdn.confidential.cloud" >> $GITHUB_OUTPUT
echo "imagePath=${basePath}/mkosi.output.qemu/fedora~36/image.raw" >> $GITHUB_OUTPUT
echo "jsonOutput=${basePath}/mkosi.output.qemu/fedora~36/image-upload.json" >> $GITHUB_OUTPUT

View File

@ -67,6 +67,7 @@ jobs:
outputs:
imageType: ${{ steps.image-type.outputs.imageType }}
pkiSet: ${{ steps.pki-set.outputs.pkiSet }}
imageVersionUid: ${{ steps.image-version-uid.outputs.imageVersionUid }}
steps:
- name: Checkout
@ -103,6 +104,18 @@ jobs:
echo "pkiSet=pki_testing" >> "$GITHUB_OUTPUT"
fi
- name: Determine image version uid
id: image-version-uid
shell: bash
run: |
if [ "${{ steps.image-type.outputs.imageType }}" = "release" ]; then
echo "imageVersionUid=${{ github.event.inputs.imageVersion }}" >> "$GITHUB_OUTPUT"
elif [ "${{ steps.image-type.outputs.imageType }}" = "debug" ]; then
echo "imageVersionUid=debug-${{ steps.version.outputs.pseudoVersion }}" >> "$GITHUB_OUTPUT"
else
echo "imageVersionUid=${{ steps.version.outputs.branchName }}-${{ steps.version.outputs.pseudoVersion }}" >> "$GITHUB_OUTPUT"
fi
make-os-image:
name: "Build OS using mkosi"
needs: [build-settings, build-dependencies]
@ -250,7 +263,7 @@ jobs:
strategy:
fail-fast: false
matrix:
csp: [aws, azure, gcp]
csp: [aws, azure, gcp, qemu]
upload-variant: [""]
include:
- csp: azure
@ -291,7 +304,10 @@ jobs:
- name: Login to AWS
uses: aws-actions/configure-aws-credentials@67fbcbb121271f7775d2e7715933280b06314838 # tag=v1.7.0
if: ${{ matrix.csp == 'aws' || matrix.csp == 'azure' }}
# on AWS, login is required to upload the image as AMI
# on Azure, login is done to download the VMGS from S3
# on QEMU, login is done to upload the image to S3
if: ${{ matrix.csp == 'aws' || matrix.csp == 'azure' || matrix.csp == 'qemu' }}
with:
role-to-assume: arn:aws:iam::795746500882:role/GitHubConstellationImagePipeline
aws-region: eu-central-1
@ -334,14 +350,14 @@ jobs:
run: |
echo "::group::Upload AWS image"
secure-boot/aws/create_uefivars.sh "${AWS_EFIVARS_PATH}"
upload/upload_aws.sh "${AWS_AMI_OUTPUT}"
echo -e "Uploaded AWS image: \`\`\`$(jq < "${AWS_AMI_OUTPUT}")\`\`\`" >> "$GITHUB_STEP_SUMMARY"
upload/upload_aws.sh
echo -e "Uploaded AWS image: \n\n\`\`\`\n$(jq < "${AWS_JSON_OUTPUT}")\n\`\`\`\n" >> "$GITHUB_STEP_SUMMARY"
echo "::endgroup::"
working-directory: ${{ github.workspace }}/image
if: ${{ matrix.csp == 'aws' }}
env:
PKI: ${{ github.workspace }}/image/pki
AWS_AMI_OUTPUT: ${{ steps.vars.outputs.awsAmiOutput }}
AWS_JSON_OUTPUT: ${{ steps.vars.outputs.awsJsonOutput }}
AWS_BUCKET: ${{ steps.vars.outputs.awsBucket }}
AWS_EFIVARS_PATH: ${{ steps.vars.outputs.awsEfivarsPath }}
AWS_IMAGE_FILENAME: ${{ steps.vars.outputs.awsImageFilename }}
@ -357,12 +373,13 @@ jobs:
echo "::group::Upload GCP image"
upload/pack.sh gcp "${GCP_RAW_IMAGE_PATH}" "${GCP_IMAGE_PATH}"
upload/upload_gcp.sh
echo -e "Uploaded GCP image: \`projects/${GCP_PROJECT}/global/images/${GCP_IMAGE_NAME}\`" >> "$GITHUB_STEP_SUMMARY"
echo -e "Uploaded GCP image: \n\n\`\`\`\n$(jq < "${GCP_JSON_OUTPUT}")\n\`\`\`\n" >> "$GITHUB_STEP_SUMMARY"
echo "::endgroup::"
working-directory: ${{ github.workspace }}/image
if: ${{ matrix.csp == 'gcp' }}
env:
PKI: ${{ github.workspace }}/image/pki
GCP_JSON_OUTPUT: ${{ steps.vars.outputs.gcpJsonOutput }}
GCP_BUCKET: ${{ steps.vars.outputs.gcpBucket }}
GCP_IMAGE_FAMILY: ${{ steps.vars.outputs.gcpImageFamily }}
GCP_IMAGE_FILENAME: ${{ steps.vars.outputs.gcpImageFilename }}
@ -378,12 +395,13 @@ jobs:
echo "::group::Upload Azure image"
upload/pack.sh azure "${AZURE_RAW_IMAGE_PATH}" "${AZURE_IMAGE_PATH}"
upload/upload_azure.sh -g --disk-name "${AZURE_DISK_NAME}" "${AZURE_VMGS_PATH}"
echo -e "Uploaded Azure ${AZURE_SECURITY_TYPE} image: \`/subscriptions/0d202bbb-4fa7-4af8-8125-58c269a05435/resourceGroups/${AZURE_RESOURCE_GROUP_NAME^^}/providers/Microsoft.Compute/galleries/${AZURE_GALLERY_NAME}/images/${AZURE_IMAGE_DEFINITION}/versions/${AZURE_IMAGE_VERSION}\`" >> "$GITHUB_STEP_SUMMARY"
echo -e "Uploaded Azure ${AZURE_SECURITY_TYPE} image: \n\n\`\`\`\n$(jq < "${AZURE_JSON_OUTPUT}")\n\`\`\`\n" >> "$GITHUB_STEP_SUMMARY"
echo "::endgroup::"
working-directory: ${{ github.workspace }}/image
if: ${{ matrix.csp == 'azure' }}
env:
PKI: ${{ github.workspace }}/image/pki
AZURE_JSON_OUTPUT: ${{ steps.vars.outputs.azureJsonOutput }}
AZURE_DISK_NAME: ${{ steps.vars.outputs.azureDiskName }}
AZURE_GALLERY_NAME: ${{ steps.vars.outputs.azureGalleryName }}
AZURE_IMAGE_DEFINITION: ${{ steps.vars.outputs.azureImageDefinition }}
@ -400,9 +418,34 @@ jobs:
AZURE_SKU: ${{ steps.vars.outputs.azureSku }}
AZURE_VMGS_PATH: ${{ steps.vars.outputs.azureVmgsPath }}
- name: Upload QEMU image
shell: bash
if: ${{ matrix.csp == 'qemu' }}
run: |
echo "::group::Upload QEMU image"
upload/upload_qemu.sh
echo -e "Uploaded QEMU image: \n\n\`\`\`\n$(jq < "${QEMU_JSON_OUTPUT}")\n\`\`\`\n" >> "$GITHUB_STEP_SUMMARY"
echo "::endgroup::"
working-directory: ${{ github.workspace }}/image
env:
QEMU_JSON_OUTPUT: ${{ steps.vars.outputs.qemuJsonOutput }}
QEMU_BUCKET: ${{ steps.vars.outputs.qemuBucket }}
QEMU_BASE_URL: ${{ steps.vars.outputs.qemuBaseUrl }}
QEMU_IMAGE_PATH: ${{ steps.vars.outputs.qemuImagePath }}
IMAGE_VERSION_UID: ${{ needs.build-settings.outputs.imageVersionUid }}
- name: Upload image lookup table as artifact
uses: actions/upload-artifact@83fd05a356d7e2593de66fc9913b3002723633cb # tag=v3.1.1
with:
name: lookup-table
path: ${{ github.workspace }}/image/mkosi.output.*/*/image-upload*.json
calculate-pcrs:
name: "Calculate PCRs"
needs: [make-os-image]
needs: [build-settings, make-os-image]
permissions:
id-token: write
contents: read
runs-on: ubuntu-22.04
strategy:
fail-fast: false
@ -414,6 +457,12 @@ jobs:
with:
ref: ${{ github.head_ref }}
- 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: Download OS image artifact
uses: actions/download-artifact@9782bd6a9848b53b110e712e20e42d89988822b7 # tag=v3.0.1
with:
@ -436,7 +485,7 @@ jobs:
./precalculate_pcr_9.sh ${{ github.workspace }}/image.raw ${{ github.workspace }}/pcr-9-${{ matrix.csp }}.json
} >> "$GITHUB_STEP_SUMMARY"
cp pcr-stable.json ${{ github.workspace }}/
jq --sort-keys -s '.[0] * .[1] * .[2] * .[3]' ${{ github.workspace }}/pcr-* > ${{ github.workspace }}/pcrs-${{ matrix.csp }}.json
jq -sSc '.[0] * .[1] * .[2] * .[3]' ${{ github.workspace }}/pcr-* > ${{ github.workspace }}/pcrs-${{ matrix.csp }}.json
echo "::endgroup::"
working-directory: ${{ github.workspace }}/image/measured-boot
@ -446,11 +495,28 @@ jobs:
name: pcrs
path: pcrs-${{ matrix.csp }}.json
- name: Upload expected PCRs to S3
shell: bash
run: |
aws s3 cp \
"pcrs-${{ matrix.csp }}.json" \
"s3://cdn-confidential-cloud-backend/v1/measurements/${{ needs.build-settings.outputs.imageVersionUid }}/${{ matrix.csp }}/measurements.image.json" \
--no-progress
generate-sbom:
name: "Generate SBOM"
needs: [build-dependencies, make-os-image]
needs: [build-settings, build-dependencies, make-os-image]
permissions:
id-token: write
contents: read
runs-on: ubuntu-22.04
steps:
- 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: Install squashfs tools
run: |
echo "::group::Install squashfs tools"
@ -465,6 +531,13 @@ jobs:
# since the images only differ in the ESP partition
name: parts-qemu
- name: Download manifest
uses: actions/download-artifact@9782bd6a9848b53b110e712e20e42d89988822b7 # tag=v3.0.1
with:
# downloading / using only the QEMU manifest is fine
# since the images only differ in the ESP partition
name: manifest-qemu
- name: Unpack squashfs
run: |
echo "::group::Unpack squashfs"
@ -475,18 +548,21 @@ jobs:
with:
path: image.root.tree
artifact-name: sbom.spdx.json
output-file: sbom.spdx.json
format: spdx-json
- uses: anchore/sbom-action@06e109483e6aa305a2b2395eabae554e51530e1d # tag=v0.13.1
with:
path: image.root.tree
artifact-name: sbom.cyclonedx.json
output-file: sbom.cyclonedx.json
format: cyclonedx-json
- uses: anchore/sbom-action@06e109483e6aa305a2b2395eabae554e51530e1d # tag=v0.13.1
with:
path: image.root.tree
artifact-name: sbom.syft.json
output-file: sbom.syft.json
format: syft-json
- name: Combine hashes
@ -529,3 +605,55 @@ jobs:
EOF
cat SHA256SUMS
echo -e "SHA256SUMS:\n\`\`\`\n$(cat SHA256SUMS)\n\`\`\`" >> "$GITHUB_STEP_SUMMARY"
- name: Upload SBOMs to S3
shell: bash
run: |
sboms='sbom.spdx.json sbom.cyclonedx.json sbom.syft.json'
manifests='image.raw.manifest image.raw.changelog'
hashes='SHA256SUMS'
for file in ${sboms} ${manifests} ${hashes}; do
aws s3 cp \
"${file}" \
"s3://cdn-confidential-cloud-backend/v1/sbom/${{ needs.build-settings.outputs.imageVersionUid }}/${file}" \
--no-progress
done
upload-image-lookup-table:
name: "Upload image lookup table"
runs-on: ubuntu-22.04
needs: [build-settings, upload-os-image]
permissions:
id-token: write
contents: read
steps:
- name: Download image lookup table
uses: actions/download-artifact@v2
with:
name: lookup-table
- name: Combine lookup tables for CSPs
shell: bash
run: |
echo '{}' > intermediate.json
for lut in mkosi.output.*/*/image-upload*.json; do
jq -scS '.[0] * .[1]' intermediate.json "${lut}" > lookup-table.json
cp lookup-table.json intermediate.json
done
rm -f intermediate.json
mv lookup-table.json "${{ needs.build-settings.outputs.imageVersionUid }}.json"
- 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 lookup table to S3
shell: bash
run: |
aws s3 cp \
"${{ needs.build-settings.outputs.imageVersionUid }}.json" \
"s3://cdn-confidential-cloud-backend/v1/images/${{ needs.build-settings.outputs.imageVersionUid }}.json" \
--no-progress
echo -e "- [Lookup table](https://cdn.confidential.cloud/v1/images/${{ needs.build-settings.outputs.imageVersionUid }}.json)" >> "$GITHUB_STEP_SUMMARY"

2
image/.gitignore vendored
View File

@ -3,3 +3,5 @@ mkosi.extra
pki
image.*
mkosi.output.*
pki_*/*.key
pki_*/*.vmgs

View File

@ -5,6 +5,7 @@ BOOTSTRAPPER_BINARY ?= $(BASE_PATH)/../build/bootstrapper
DISK_MAPPER_BINARY ?= $(BASE_PATH)/../build/disk-mapper
PKI ?= $(BASE_PATH)/pki
MKOSI_EXTRA ?= $(BASE_PATH)/mkosi.extra
IMAGE_VERSION ?= v0.0.0
-include $(CURDIR)/config.mk
csps := aws qemu gcp azure
certs := $(PKI)/PK.cer $(PKI)/KEK.cer $(PKI)/db.cer
@ -16,7 +17,7 @@ all: $(csps)
$(csps): %: mkosi.output.%/fedora~36/image.raw
mkosi.output.%/fedora~36/image.raw: mkosi.files/mkosi.%.conf inject-bins inject-certs
mkosi --config mkosi.files/mkosi.$*.conf build
mkosi --config mkosi.files/mkosi.$*.conf --image-version=$(IMAGE_VERSION) build
secure-boot/signed-shim.sh $@
@if [ -n $(SUDO_UID) ] && [ -n $(SUDO_GID) ]; then \
chown -R $(SUDO_UID):$(SUDO_GID) mkosi.output.$*; \

View File

@ -84,7 +84,7 @@ The generated images are partially signed by Microsoft ([shim loader](https://gi
For QEMU and Azure, you can pre-generate the NVRAM variables for secure boot. This is not necessary for GCP, as you can specify secure boot parameters via the GCP API on image creation.
<details>
<summary>libvirt / QEMU / KVM</summary>
<summary><a id="qemu-secure-boot">libvirt / QEMU / KVM</a></summary>
```sh
secure-boot/generate_nvram_vars.sh mkosi.output.qemu/fedora~36/image.raw
@ -112,6 +112,7 @@ export AZURE_SNAPSHOT_NAME=${AZURE_DISK_NAME}
export AZURE_RAW_IMAGE_PATH=${PWD}/mkosi.output.azure/fedora~36/image.raw
export AZURE_IMAGE_PATH=${PWD}/mkosi.output.azure/fedora~36/image.vhd
export AZURE_VMGS_FILENAME=${AZURE_SECURITY_TYPE}.vmgs
export AZURE_JSON_OUTPUT=${PWD}/mkosi.output.azure/fedora~36/image-upload.json
export BLOBS_DIR=${PWD}/blobs
upload/pack.sh azure "${AZURE_RAW_IMAGE_PATH}" "${AZURE_IMAGE_PATH}"
upload/upload_azure.sh --disk-name "${AZURE_DISK_NAME}-setup-secure-boot" ""
@ -158,9 +159,9 @@ export AWS_BUCKET=constellation-images
export AWS_EFIVARS_PATH=${PWD}/mkosi.output.aws/fedora~36/efivars.bin
export AWS_IMAGE_PATH=${PWD}/mkosi.output.aws/fedora~36/image.raw
export AWS_IMAGE_FILENAME=image-$(date +%s).raw
export AWS_AMI_OUTPUT=${PWD}/mkosi.output.aws/fedora~36/ami.txt
export AWS_JSON_OUTPUT=${PWD}/mkosi.output.aws/fedora~36/image-upload.json
secure-boot/aws/create_uefivars.sh "${AWS_EFIVARS_PATH}"
upload/upload_aws.sh "${AWS_AMI_OUTPUT}"
upload/upload_aws.sh
```
</details>
@ -187,6 +188,7 @@ export GCP_BUCKET=constellation-images
export GCP_RAW_IMAGE_PATH=${PWD}/mkosi.output.gcp/fedora~36/image.raw
export GCP_IMAGE_FILENAME=$(date +%s).tar.gz
export GCP_IMAGE_PATH=${PWD}/mkosi.output.gcp/fedora~36/image.tar.gz
export GCP_JSON_OUTPUT=${PWD}/mkosi.output.gcp/fedora~36/image-upload.json
upload/pack.sh gcp ${GCP_RAW_IMAGE_PATH} ${GCP_IMAGE_PATH}
upload/upload_gcp.sh
```
@ -228,8 +230,27 @@ export AZURE_PUBLISHER=edgelesssys
export AZURE_DISK_NAME=constellation-$(date +%s)
export AZURE_RAW_IMAGE_PATH=${PWD}/mkosi.output.azure/fedora~36/image.raw
export AZURE_IMAGE_PATH=${PWD}/mkosi.output.azure/fedora~36/image.vhd
export AZURE_JSON_OUTPUT=${PWD}/mkosi.output.azure/fedora~36/image-upload.json
upload/pack.sh azure "${AZURE_RAW_IMAGE_PATH}" "${AZURE_IMAGE_PATH}"
upload/upload_azure.sh -g --disk-name "${AZURE_DISK_NAME}" "${AZURE_VMGS_PATH}"
```
</details>
<details>
<summary>QEMU</summary>
- Install `aws` cli (see [here](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html))
- Login to AWS (see [here](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-quickstart.html))
```sh
# set these variables
export IMAGE_VERSION_UID= # e.g. "test123" or "v2.1.0"
export QEMU_BUCKET=cdn-confidential-cloud-backend
export QEMU_BASE_URL="https://cdn.confidential.cloud"
export QEMU_IMAGE_PATH=${PWD}/mkosi.output.qemu/fedora~36/image.raw
export QEMU_JSON_OUTPUT=${PWD}/mkosi.output.qemu/fedora~36/image-upload.json
upload/upload_qemu.sh
```
</details>

View File

@ -1,6 +1,8 @@
{
"pcr11": "0000000000000000000000000000000000000000000000000000000000000000",
"pcr12": "0000000000000000000000000000000000000000000000000000000000000000",
"pcr13": "0000000000000000000000000000000000000000000000000000000000000000",
"pcr15": "0000000000000000000000000000000000000000000000000000000000000000"
"measurements": {
"11": "0000000000000000000000000000000000000000000000000000000000000000",
"12": "0000000000000000000000000000000000000000000000000000000000000000",
"13": "0000000000000000000000000000000000000000000000000000000000000000",
"15": "0000000000000000000000000000000000000000000000000000000000000000"
}
}

View File

@ -22,21 +22,23 @@ write_output() {
local out="$1"
cat > "${out}" << EOF
{
"pcr4": "${expected_pcr_4}",
"efistages": [
{
"name": "shim",
"sha256": "${shim_authentihash}"
},
{
"name": "systemd-boot",
"sha256": "${sd_boot_authentihash}"
},
{
"name": "uki",
"sha256": "${uki_authentihash}"
}
]
"measurements": {
"4": "${expected_pcr_4}"
},
"efistages": [
{
"name": "shim",
"sha256": "${shim_authentihash}"
},
{
"name": "systemd-boot",
"sha256": "${sd_boot_authentihash}"
},
{
"name": "uki",
"sha256": "${uki_authentihash}"
}
]
}
EOF
}

View File

@ -7,7 +7,7 @@
# PCR[8] contains the hash of the kernel command line and is measured by systemd-boot.
# This value is deprecated and will be moved to PCR[12] in the future.
# This script may produce wrong results after 2023 (when the kernel command line is only measured in PCR[12]).
# Usage: precalculate_pcr_8.sh <path to image> <path to output file>
# Usage: precalculate_pcr_8.sh <path to image> <path to output file> <csp>
set -euo pipefail
shopt -s inherit_errexit
@ -34,8 +34,11 @@ write_output() {
local out="$1"
cat > "${out}" << EOF
{
"pcr8": "${expected_pcr_8}",
"cmdline": "${cmdline}"
"measurements": {
"8": "${expected_pcr_8}"
},
"cmdline": "${cmdline}",
"cmdline-sha256": "${cmdline_hash}"
}
EOF
}

View File

@ -27,8 +27,10 @@ write_output() {
local out="$1"
cat > "${out}" << EOF
{
"pcr9": "${expected_pcr_9}",
"initrd": "${initrd_hash}"
"measurements": {
"9": "${expected_pcr_9}"
},
"initrd-sha256": "${initrd_hash}"
}
EOF
}

View File

@ -17,6 +17,8 @@ SecureBootKey=pki/db.key
SecureBootCertificate=pki/db.crt
# TODO: Wait for systemd 252 to bring systemd-measure
# Measure=yes
ImageId=constellation
Output=image.raw
[Host]
QemuHeadless=yes

View File

@ -20,3 +20,7 @@ mkdir -p /etc/{cni,kubernetes}
mv /etc/issue.d /usr/lib/issue.d || true
rm -f /etc/issue
rm -f /etc/issue.net
# update /etc/os-release
echo "IMAGE_ID=\"${IMAGE_ID}\"" >> /etc/os-release
echo "IMAGE_VERSION=\"${IMAGE_VERSION}\"" >> /etc/os-release

View File

@ -13,7 +13,6 @@ fi
CONTAINERS_JSON=$(mktemp /tmp/containers-XXXXXXXXXXXXXX.json)
declare -A AMI_FOR_REGION
AMI_OUTPUT=$1
import_status() {
local import_task_id=$1
@ -160,8 +159,17 @@ for region in ${AWS_REPLICATION_REGIONS}; do
tag_ami_with_backing_snapshot "${AMI_FOR_REGION[${region}]}" "${region}"
make_ami_public "${AMI_FOR_REGION[${region}]}" "${region}"
done
echo -n "{\"${AWS_REGION}\": \"${AMI_FOR_REGION[${AWS_REGION}]}\"" > "${AMI_OUTPUT}"
json=$(jq -ncS \
--arg region "${AWS_REGION}" \
--arg ami "${AMI_FOR_REGION[${AWS_REGION}]}" \
'{"aws":{($region): $ami}}')
for region in ${AWS_REPLICATION_REGIONS}; do
echo -n ", \"${region}\": \"${AMI_FOR_REGION[${region}]}\"" >> "${AMI_OUTPUT}"
json=$(jq -ncS \
--argjson json "${json}" \
--arg region "${region}" \
--arg ami "${AMI_FOR_REGION[${region}]}" \
'$json * {"aws": {($region): $ami}}')
done
echo "}" >> "${AMI_OUTPUT}"
echo "${json}" > "${AWS_JSON_OUTPUT}"

View File

@ -41,10 +41,13 @@ set -- "${POSITIONAL_ARGS[@]}" # restore positional parameters
if [[ ${AZURE_SECURITY_TYPE} == "ConfidentialVM" ]]; then
AZURE_DISK_SECURITY_TYPE=ConfidentialVM_VMGuestStateOnlyEncryptedWithPlatformKey
AZURE_SIG_VERSION_ENCRYPTION_TYPE=EncryptedVMGuestStateOnlyWithPmk
security_type_short_name="cvm"
elif [[ ${AZURE_SECURITY_TYPE} == "ConfidentialVMSupported" ]]; then
AZURE_DISK_SECURITY_TYPE=""
security_type_short_name="cvm"
elif [[ ${AZURE_SECURITY_TYPE} == "TrustedLaunch" ]]; then
AZURE_DISK_SECURITY_TYPE=TrustedLaunch
security_type_short_name="trustedlaunch"
else
echo "Unknown security type: ${AZURE_SECURITY_TYPE}"
exit 1
@ -188,6 +191,43 @@ create_sig_version() {
${SOURCE}
}
get_image_version_reference() {
local is_community_gallery
is_community_gallery=$(az sig show --gallery-name "${AZURE_GALLERY_NAME}" \
--resource-group "${AZURE_RESOURCE_GROUP_NAME}" \
--query 'sharingProfile.communityGalleryInfo.communityGalleryEnabled' \
-o tsv)
if [[ ${is_community_gallery} == "true" ]]; then
get_community_image_version_reference
return
fi
get_unshared_image_version_reference
}
get_community_image_version_reference() {
local communityGalleryName
communityGalleryName=$(az sig show --gallery-name "${AZURE_GALLERY_NAME}" \
--resource-group "${AZURE_RESOURCE_GROUP_NAME}" \
--query 'sharingProfile.communityGalleryInfo.publicNames[0]' \
-o tsv)
az sig image-version show-community \
--public-gallery-name "${communityGalleryName}" \
--gallery-image-definition "${AZURE_IMAGE_DEFINITION}" \
--gallery-image-version "${AZURE_IMAGE_VERSION}" \
--location "${AZURE_REGION}" \
--query 'uniqueId' \
-o tsv
}
get_unshared_image_version_reference() {
az sig image-version show \
--resource-group "${AZURE_RESOURCE_GROUP_NAME}" \
--gallery-name "${AZURE_GALLERY_NAME}" \
--gallery-image-definition "${AZURE_IMAGE_DEFINITION}" \
--gallery-image-version "${AZURE_IMAGE_VERSION}" \
--query id --output tsv
}
create_disk
if [[ ${CREATE_SIG_VERSION} == "YES" ]]; then
@ -196,3 +236,10 @@ if [[ ${CREATE_SIG_VERSION} == "YES" ]]; then
delete_image
delete_disk
fi
image_reference=$(get_image_version_reference)
json=$(jq -ncS \
--arg security_type "${security_type_short_name}" \
--arg image_reference "${image_reference}" \
'{"azure": {($security_type): $image_reference}}')
echo -n "${json}" > "${AZURE_JSON_OUTPUT}"

View File

@ -31,3 +31,11 @@ gcloud compute images add-iam-policy-binding "${GCP_IMAGE_NAME}" \
--member='allAuthenticatedUsers' \
--role='roles/compute.imageUser'
gsutil rm "gs://${GCP_BUCKET}/${GCP_IMAGE_FILENAME}"
image_reference=$(gcloud compute images describe "${GCP_IMAGE_NAME}" \
--project "${GCP_PROJECT}" \
'--format=value(selfLink.scope(v1))')
json=$(jq -ncS \
--arg image_reference "${image_reference}" \
'{"gcp": {"sev-es": $image_reference}}')
echo -n "${json}" > "${GCP_JSON_OUTPUT}"

21
image/upload/upload_qemu.sh Executable file
View File

@ -0,0 +1,21 @@
#!/usr/bin/env bash
# Copyright (c) Edgeless Systems GmbH
#
# SPDX-License-Identifier: AGPL-3.0-only
set -euo pipefail
shopt -s inherit_errexit
if [[ -z ${CONFIG_FILE-} ]] && [[ -f ${CONFIG_FILE-} ]]; then
# shellcheck source=/dev/null
. "${CONFIG_FILE}"
fi
aws s3 cp "${QEMU_IMAGE_PATH}" "s3://${QEMU_BUCKET}/v1/raw/${IMAGE_VERSION_UID}/qemu/image.raw" --no-progress
image_url="${QEMU_BASE_URL}/v1/raw/${IMAGE_VERSION_UID}/qemu/image.raw"
json=$(jq -ncS \
--arg image_url "${image_url}" \
'{"qemu": {"default": $image_url}}')
echo -n "${json}" > "${QEMU_JSON_OUTPUT}"

View File

@ -42,12 +42,13 @@ The build pipeline takes as inputs:
- a version number that is one of
- a release version number (e.g. `v2.2.0`) for release images
- a pseudo-version number (e.g. `v2.3.0-pre-debug`) for development images
- a pseudo-version number (e.g. `v2.3.0-pre-branch-name`) for branch images
- a pseudo-version number (e.g. `debug-v2.3.0-pre.0.`) for development images
- a pseudo-version number (e.g. `branch-name-v2.3.0-pre.0.`) for branch images
- a commit hash of the Constellation monorepo that is used to build the images (e.g. `cc0de5c68d41f31dd0b284d574f137e0b0ad106b`)
- a commit timestamp of the Constellation monorepo that is used to build the images (e.g. `20221115082220`)
To identify images belonging to one invocation of the build pipeline, the pipeline uses a unique identifier for the set of images, referred to as `image version uid`.
This is either the release version number (e.g. `v2.2.0`) or a pseudo version that combines the version number and the commit hash (e.g. `v2.3.0-pre-debug-cc0de5c68d41f31dd0b284d574f137e0b0ad106b`).
This is either the release version number (e.g. `v2.2.0`) or a pseudo version that combines the version number, commit timestamp and the commit hash (e.g. `debug-v2.3.0-pre.0.20221115082220-cc0de5c68d41f31dd0b284d574f137e0b0ad106b`).
The build pipeline produces as outputs:
@ -59,12 +60,34 @@ The build pipeline produces as outputs:
The lookup table is uploaded to S3 and is used to identify the images that belong to a given `image version uid`.
Measurements are uploaded to S3 and can be looked up for each cloud service provider and `image version uid`.
## Image API
The build pipeline produces artifacts that are uploaded to S3 and can be accessed via HTTP.
The artifacts are organized in a directory structure that allows to look up the artifacts for a given `image version uid`.
Where applicable, the API uses the following CSP names:
- `aws` for Amazon Web Services
- `azure` for Microsoft Azure
- `gcp` for Google Cloud Platform
- `qemu` for QEMU
The following HTTP endpoints are available:
- `GET /v1/images/<image version uid>.json` returns the lookup table for the given `image version uid`.
- `GET /v1/measurements/<image version uid>/<csp>/` contains files with measurements and signatures for the given `image version uid` and CSP.
- `measurements.json` contains the final measurements for the given `image version uid` and CSP.
- `measurements.json.sig` returns the signature of the measurements file.
- `measurements.image.json` returns the measurements generated statically from the image.
- `GET /v1/raw/<image version uid>/<csp>/image.raw` returns the raw image for the given `image version uid` and CSP.
- `GET /v1/sbom/<image version uid>/` contains SBOM files for the given `image version uid`. The exact formats and file names are TBD.
## Image lookup table
The image lookup table is a JSON file that maps the `image version uid` to the CSP-specific image references. It uses the `image version uid` as file name.
```
s3://<BUCKET-NAME>/images/<IMAGE-VERSION-UID>.json
s3://<BUCKET-NAME>/v1/images/<IMAGE-VERSION-UID>.json
```
```json
@ -80,8 +103,10 @@ s3://<BUCKET-NAME>/images/<IMAGE-VERSION-UID>.json
},
"gcp": {
"sev-es": "gcp-image-123"
},
"qemu": {
"default": "https://cdn.confidential.cloud/v1/raw/v2.2.0/qemu/image.raw"
}
"qemu": "https://cdn.edgeless.systems/.../qemu.raw"
}
```
@ -101,8 +126,9 @@ The format of the image measurements is described in the [secure software distri
The image measurements are stored in a folder structure in S3 that is organized by CSP and `image version uid`.
```
s3://<BUCKET-NAME>/measurements/<CSP>/<IMAGE-VERSION-UID>/measurements.yaml
s3://<BUCKET-NAME>/measurements/<CSP>/<IMAGE-VERSION-UID>/measurements.yaml.sig
s3://<BUCKET-NAME>/v1/measurements/<CSP>/<IMAGE-VERSION-UID>/measurements.json
s3://<BUCKET-NAME>/v1/measurements/<CSP>/<IMAGE-VERSION-UID>/measurements.json.sig
s3://<BUCKET-NAME>/v1/measurements/<CSP>/<IMAGE-VERSION-UID>/measurements.image.json
```
## CLI image discovery
@ -117,11 +143,12 @@ The `image` field is independent of the CSP and is a used to discover the CSP-sp
The CLI can find a CSP- and region specific image reference by looking up the `image version uid` in the following order:
- if a local file `<IMAGE-VERSION-UID>.json` exists, use the lookup table in that file
- otherwise, load the image lookup table from a well known URL (e.g. `https://cdn.confidential.cloud/images/<IMAGE-VERSION-UID>.json`) and use the lookup table in that file
- otherwise, load the image lookup table from a well known URL (e.g. `https://cdn.confidential.cloud/v1/images/<IMAGE-VERSION-UID>.json`) and use the lookup table in that file
- choose the CSP-specific image reference for the current region and security type:
- On AWS, use the AMI ID for the current region (e.g. `.aws.us-east-1`)
- On Azure, use the image ID for the security type (CVM or Trusted Launch) (e.g. `.azure.cvm`)
- On GCP, use the only image ID (e.g. `.gcp`)
- On GCP, use the only image ID (e.g. `.gcp.sev-es`)
- On QEMU, use the only image ID (e.g. `.qemu.default`)
This allows customers to upload images to their own cloud subscription and use them with the CLI by providing the image lookup table as a local file.