AB#2158 publish measurements (#268)

* cleaned up actions and new measure action to generate, sign and upload measurements
* improve constellation ip fetching to support multiple control nodes
Signed-off-by: Fabian Kammel <fk@edgeless.systems>
This commit is contained in:
Fabian Kammel 2022-07-13 14:04:46 +02:00 committed by GitHub
parent 66eef5bc70
commit 00dfff6840
16 changed files with 203 additions and 60 deletions

View File

@ -1,4 +1,4 @@
name: azure_login name: Azure login
description: "Login to Azure & configure az CLI." description: "Login to Azure & configure az CLI."
inputs: inputs:
azure_credentials: azure_credentials:

View File

@ -1,17 +1,18 @@
name: build name: Build CLI
description: | description: |
Runs cmake & default make target in build folder. Additionally, Sigstore tools Runs cmake and cli make target in build folder. Optionally, Sigstore tools
are used to sign CLI and publish a release when run on v* tag. are used to sign CLI when inputs are provided. A draft release is published
when run on v* tag.
inputs: inputs:
cosign-public-key: cosignPublicKey:
description: 'Cosign public key' description: 'Cosign public key'
required: false required: false
default: '' default: ''
cosign-private-key: cosignPrivateKey:
description: 'Cosign private key' description: 'Cosign private key'
required: false required: false
default: '' default: ''
cosign-password: cosignPassword:
description: 'Password for Cosign private key' description: 'Password for Cosign private key'
required: false required: false
default: '' default: ''
@ -82,10 +83,10 @@ runs:
shell: bash shell: bash
working-directory: build working-directory: build
env: env:
COSIGN_PUBLIC_KEY: ${{ inputs.cosign-public-key }} COSIGN_PUBLIC_KEY: ${{ inputs.cosignPublicKey }}
COSIGN_PRIVATE_KEY: ${{ inputs.cosign-private-key }} COSIGN_PRIVATE_KEY: ${{ inputs.cosignPrivateKey }}
COSIGN_PASSWORD: ${{ inputs.cosign-password }} COSIGN_PASSWORD: ${{ inputs.cosignPassword }}
if: ${{ inputs.cosign-public-key != '' && inputs.cosign-private-key != '' && inputs.cosign-password != '' }} if: ${{ inputs.cosignPublicKey != '' && inputs.cosignPrivateKey != '' && inputs.cosignPassword != '' }}
- name: Release CLI - name: Release CLI
# GitHub endorsed release project. See: https://github.com/actions/create-release # GitHub endorsed release project. See: https://github.com/actions/create-release

View File

@ -1,4 +1,4 @@
name: build micro-service image name: Build micro service
description: Build and upload a container image for a Constellation micro-service description: Build and upload a container image for a Constellation micro-service
inputs: inputs:
name: name:

View File

@ -1,5 +1,6 @@
name: constellation_create name: Constellation create
description: "Create a new Constellation cluster." description: |
Create a new Constellation cluster using latest CoreOS image.
inputs: inputs:
workerNodesCount: workerNodesCount:
description: "Number of worker nodes to spawn." description: "Number of worker nodes to spawn."
@ -40,18 +41,21 @@ runs:
constellation config generate ${{ inputs.cloudProvider }} constellation config generate ${{ inputs.cloudProvider }}
shell: bash shell: bash
- name: Set latest Azure image - name: Set latest image
run: | run: |
case $CSP in
azure)
LATEST_AZURE_IMAGE=$(az sig image-version list --resource-group constellation-images --gallery-name Constellation --gallery-image-definition constellation-coreos --query "sort_by([], &publishingProfile.publishedDate)[].id" -o table | tail -n 1) LATEST_AZURE_IMAGE=$(az sig image-version list --resource-group constellation-images --gallery-name Constellation --gallery-image-definition constellation-coreos --query "sort_by([], &publishingProfile.publishedDate)[].id" -o table | tail -n 1)
yq eval -i "(.provider.azure.image) = \"${LATEST_AZURE_IMAGE}\"" constellation-conf.yaml yq eval -i "(.provider.azure.image) = \"${LATEST_AZURE_IMAGE}\"" constellation-conf.yaml
shell: bash ;;
if: ${{ inputs.cloudProvider == 'azure' }} gcp)
- name: Set latest GCP image
run: |
LATEST_GCP_IMAGE_TIMESTAMP=$(gcloud compute images list --filter="name~'constellation-coreos-\d{10}'" --sort-by=creationTimestamp --project constellation-images --format="table(name)" | tail -n 1 | cut -d '-' -f3) LATEST_GCP_IMAGE_TIMESTAMP=$(gcloud compute images list --filter="name~'constellation-coreos-\d{10}'" --sort-by=creationTimestamp --project constellation-images --format="table(name)" | tail -n 1 | cut -d '-' -f3)
yq eval -i "(.provider.gcp.image) = \"projects/constellation-images/global/images/constellation-coreos-${LATEST_GCP_IMAGE_TIMESTAMP}\"" constellation-conf.yaml yq eval -i "(.provider.gcp.image) = \"projects/constellation-images/global/images/constellation-coreos-${LATEST_GCP_IMAGE_TIMESTAMP}\"" constellation-conf.yaml
;;
esac
shell: bash shell: bash
if: ${{ inputs.cloudProvider == 'gcp' }} env:
CSP: ${{ inputs.cloudProvider }}
- name: Constellation create - name: Constellation create
run: | run: |
@ -66,34 +70,12 @@ runs:
path: constellation-state.json path: constellation-state.json
if: ${{ always() && !env.ACT }} if: ${{ always() && !env.ACT }}
- name: Read Coordinator IP (Azure)
run: |
echo CONSTELL_IP=$(jq -r .azurecoordinators[].PublicIP constellation-state.json) >> $GITHUB_ENV
shell: bash
if: ${{ inputs.cloudProvider == 'azure' }}
- name: Read Coordinator IP (GCP)
run: |
echo CONSTELL_IP=$(jq -r .gcpcoordinators[].PublicIP constellation-state.json) >> $GITHUB_ENV
shell: bash
if: ${{ inputs.cloudProvider == 'gcp' }}
- name: Constellation init - name: Constellation init
run: | run: |
if [ ${{ inputs.autoscale }} = true ]; then autoscale=--autoscale; fi if [ ${{ inputs.autoscale }} = true ]; then autoscale=--autoscale; fi
constellation init ${autoscale} constellation init ${autoscale}
shell: bash shell: bash
- name: Fetch PCRs
run: |
pcr-reader --constell-ip ${{ env.CONSTELL_IP }} -o measurements.go
shell: bash
- name: Upload measurements
uses: actions/upload-artifact@v3
with:
name: measurements.go
path: measurements.go
if: ${{ !env.ACT }}
- name: Configure VPN connection - name: Configure VPN connection
run: wg-quick up ./wg0.conf run: wg-quick up ./wg0.conf
shell: bash shell: bash

View File

@ -1,4 +1,4 @@
name: constellation_destroy name: Constellation destroy
description: "Destroy a running Constellation cluster." description: "Destroy a running Constellation cluster."
runs: runs:
using: 'composite' using: 'composite'

View File

@ -0,0 +1,114 @@
name: Constellation measure
description: |
Create measurements of a Constellation cluster and print to stdout.
Optionally sign and/or upload to S3, if corresponding inputs are provided.
inputs:
cloudProvider:
description: "Either 'gcp' or 'azure'."
required: true
cosignPublicKey:
description: 'Cosign public key'
required: false
default: ''
cosignPrivateKey:
description: 'Cosign private key'
required: false
default: ''
cosignPassword:
description: 'Password for Cosign private key'
required: false
default: ''
awsAccessKeyID:
description: 'AWS access key ID to upload measurements'
required: false
default: ''
awsSecretAccessKey:
description: 'AWS secret access key to upload measurements'
required: false
default: ''
awsDefaultRegion:
description: 'AWS region of S3 bucket to upload measurements'
required: false
default: ''
awsBucketName:
description: 'S3 bucket name to upload measurements to'
required: false
default: ''
runs:
using: 'composite'
steps:
# Check /docs/secure_software_distribution.md#sign-measurements
# for why we ignore certain measurement values.
- name: Fetch PCRs
run: |
case $CSP in
azure)
FIRST_NODE=$(jq -r ".azurecoordinators | keys | first" constellation-state.json)
CONSTELL_IP=$(jq -r ".azurecoordinators.\"${FIRST_NODE}\".PublicIP" constellation-state.json)
pcr-reader --constell-ip ${CONSTELL_IP} -format yaml > measurements.yaml
yq e 'del(.[0,6,10,11,12,13,14,15,16,17,18,19,20,21,22,23])' -i measurements.yaml
;;
gcp)
FIRST_NODE=$(jq -r ".gcpcoordinators | keys | first" constellation-state.json)
CONSTELL_IP=$(jq -r ".gcpcoordinators.\"${FIRST_NODE}\".PublicIP" constellation-state.json)
pcr-reader --constell-ip ${CONSTELL_IP} -format yaml > measurements.yaml
yq e 'del(.[11,12,13,14,15,16,17,18,19,20,21,22,23])' -i measurements.yaml
;;
esac
cat measurements.yaml
shell: bash
env:
CSP: ${{ inputs.cloudProvider }}
# TODO: Replace with https://github.com/sigstore/sigstore-installer/tree/initial
# once it has the functionality
- name: Install Cosign
uses: sigstore/cosign-installer@main
if: ${{ inputs.cosignPublicKey != '' && inputs.cosignPrivateKey != '' && inputs.cosignPassword != '' }}
- name: Install Rekor
run: |
curl -LO https://github.com/sigstore/rekor/releases/download/v0.9.0/rekor-cli-linux-amd64
sudo install rekor-cli-linux-amd64 /usr/local/bin/rekor-cli
shell: bash
if: ${{ inputs.cosignPublicKey != '' && inputs.cosignPrivateKey != '' && inputs.cosignPassword != '' }}
- name: Sign measurements
run: |
set -e
set -o pipefail
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 measurements.yaml > measurements.yaml.sig
# Verify - As documentation & check
# Local Signature (input: artifact, key, signature)
cosign verify-blob --key cosign.pub --signature measurements.yaml.sig measurements.yaml
# Transparency Log Signature (input: artifact, key)
uuid=$(rekor-cli search --artifact measurements.yaml | 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) measurements.yaml
shell: bash
env:
COSIGN_PUBLIC_KEY: ${{ inputs.cosignPublicKey }}
COSIGN_PRIVATE_KEY: ${{ inputs.cosignPrivateKey }}
COSIGN_PASSWORD: ${{ inputs.cosignPassword }}
if: ${{ inputs.cosignPublicKey != '' && inputs.cosignPrivateKey != '' && inputs.cosignPassword != '' }}
- name: Install AWS CLI
run: sudo apt-get update && sudo apt-get -y install awscli
shell: bash
if: ${{ inputs.awsAccessKeyID != '' && inputs.awsSecretAccessKey != '' && inputs.awsDefaultRegion != '' && inputs.awsBucketName != '' }}
- name: Upload to S3
run: |
IMAGE=$(yq e ".provider.${CSP}.image" constellation-conf.yaml)
S3_PATH=s3://${PUBLIC_BUCKET_NAME}/${IMAGE}
aws s3 cp measurements.yaml ${S3_PATH}/measurements.yaml
if test -f measurements.yaml.sig; then
aws s3 cp measurements.yaml.sig ${S3_PATH}/measurements.yaml.sig
fi
shell: bash
env:
AWS_ACCESS_KEY_ID: ${{ inputs.awsAccessKeyID }}
AWS_SECRET_ACCESS_KEY: ${{ inputs.awsSecretAccessKey }}
AWS_DEFAULT_REGION: ${{ inputs.awsDefaultRegion }}
PUBLIC_BUCKET_NAME: ${{ inputs.awsBucketName }}
CSP: ${{ inputs.cloudProvider }}
if: ${{ inputs.awsAccessKeyID != '' && inputs.awsSecretAccessKey != '' && inputs.awsDefaultRegion != '' && inputs.awsBucketName != '' }}

View File

@ -1,4 +1,4 @@
name: e2e_test name: E2E test
description: "Run Constellation e2e test." description: "Run Constellation e2e test."
inputs: inputs:
workerNodesCount: workerNodesCount:
@ -26,8 +26,29 @@ inputs:
description: 'Which tests should be run? Check README for guidance!' description: 'Which tests should be run? Check README for guidance!'
required: true required: true
msTeamsWebhook: msTeamsWebhook:
description: 'WebHook used to notify of failure' description: 'WebHook used to notify of failure.'
required: true required: true
cosignPublicKey:
description: 'Cosign public key to sign measurements.'
required: false
cosignPrivateKey:
description: 'Cosign private key to sign measurements.'
required: false
cosignPassword:
description: 'Cosign password for private key.'
required: false
awsAccessKeyID:
description: 'AWS access key ID to upload measurements.'
required: false
awsSecretAccessKey:
description: 'AWS secrets access key to upload measurements.'
required: false
awsDefaultRegion:
description: 'AWS region of S3 bucket. to upload measurements.'
required: false
awsBucketName:
description: 'AWS S3 bucket name to upload measurements.'
required: false
runs: runs:
using: 'composite' using: 'composite'
steps: steps:
@ -53,6 +74,17 @@ runs:
workerNodesCount: ${{ inputs.workerNodesCount }} workerNodesCount: ${{ inputs.workerNodesCount }}
controlNodesCount: ${{ inputs.controlNodesCount }} controlNodesCount: ${{ inputs.controlNodesCount }}
machineType: ${{ inputs.machineType }} machineType: ${{ inputs.machineType }}
- name: Measure cluster
uses: ./.github/actions/constellation_measure
with:
cloudProvider: ${{ inputs.cloudProvider }}
cosignPublicKey: ${{ inputs.cosignPublicKey }}
cosignPrivateKey: ${{ inputs.cosignPrivateKey }}
cosignPassword: ${{ inputs.cosignPassword }}
awsAccessKeyID: ${{ inputs.awsAccessKeyID }}
awsSecretAccessKey: ${{ inputs.awsSecretAccessKey }}
awsDefaultRegion: ${{ inputs.awsDefaultRegion }}
awsBucketName: ${{ inputs.awsBucketName }}
- name: Run e2e tests - name: Run e2e tests
uses: ./.github/actions/sonobuoy uses: ./.github/actions/sonobuoy
with: with:

View File

@ -1,4 +1,4 @@
name: gcp_login name: GCP login
description: "Login to GCP & configure gcloud CLI." description: "Login to GCP & configure gcloud CLI."
inputs: inputs:
gcp_service_account_json: gcp_service_account_json:

View File

@ -30,7 +30,7 @@ jobs:
- name: Build and upload access-manager container image - name: Build and upload access-manager container image
id: build-and-upload id: build-and-upload
uses: ./.github/actions/build_micro-service uses: ./.github/actions/build_micro_service
with: with:
name: access-manager name: access-manager
projectVersion: "0.0.0" projectVersion: "0.0.0"

View File

@ -32,7 +32,7 @@ jobs:
- name: Build and upload activation-service container image - name: Build and upload activation-service container image
id: build-and-upload id: build-and-upload
uses: ./.github/actions/build_micro-service uses: ./.github/actions/build_micro_service
with: with:
name: activation-service name: activation-service
projectVersion: "0.0.0" projectVersion: "0.0.0"

View File

@ -24,6 +24,6 @@ jobs:
- name: Build cli - name: Build cli
uses: ./.github/actions/build_cli uses: ./.github/actions/build_cli
with: with:
cosign-public-key: ${{ secrets.COSIGN_PUBLIC_KEY }} cosignPublicKey: ${{ secrets.COSIGN_PUBLIC_KEY }}
cosign-private-key: ${{ secrets.COSIGN_PRIVATE_KEY }} cosignPrivateKey: ${{ secrets.COSIGN_PRIVATE_KEY }}
cosign-password: ${{ secrets.COSIGN_PASSWORD }} cosignPassword: ${{ secrets.COSIGN_PASSWORD }}

View File

@ -31,7 +31,7 @@ jobs:
- name: Build and upload KMS server container image - name: Build and upload KMS server container image
id: build-and-upload id: build-and-upload
uses: ./.github/actions/build_micro-service uses: ./.github/actions/build_micro_service
with: with:
name: kmsserver name: kmsserver
projectVersion: '0.0.0' projectVersion: '0.0.0'

View File

@ -50,7 +50,7 @@ jobs:
- name: Build and upload activation-service container image - name: Build and upload activation-service container image
id: build-and-upload id: build-and-upload
uses: ./.github/actions/build_micro-service uses: ./.github/actions/build_micro_service
with: with:
name: ${{ inputs.microService }} name: ${{ inputs.microService }}
projectVersion: ${{ inputs.version }} projectVersion: ${{ inputs.version }}

View File

@ -23,7 +23,7 @@ jobs:
- name: Build and upload verification-service container image - name: Build and upload verification-service container image
id: build-and-upload id: build-and-upload
uses: ./.github/actions/build_micro-service uses: ./.github/actions/build_micro_service
with: with:
name: verification-service name: verification-service
projectVersion: '0.0.0' projectVersion: '0.0.0'

View File

@ -27,3 +27,10 @@ jobs:
# TODO: Remove E2E_SKIP once AB#2174 is resolved # TODO: Remove E2E_SKIP once AB#2174 is resolved
sonobuoyTestSuiteCmd: '--plugin e2e --plugin-env e2e.E2E_FOCUS="\[Conformance\]" --plugin-env e2e.E2E_SKIP="for service with type clusterIP|HostPort validates that there is no conflict between pods with same hostPort but different hostIP and protocol" --plugin https://raw.githubusercontent.com/vmware-tanzu/sonobuoy-plugins/master/cis-benchmarks/kube-bench-plugin.yaml --plugin https://raw.githubusercontent.com/vmware-tanzu/sonobuoy-plugins/master/cis-benchmarks/kube-bench-master-plugin.yaml' sonobuoyTestSuiteCmd: '--plugin e2e --plugin-env e2e.E2E_FOCUS="\[Conformance\]" --plugin-env e2e.E2E_SKIP="for service with type clusterIP|HostPort validates that there is no conflict between pods with same hostPort but different hostIP and protocol" --plugin https://raw.githubusercontent.com/vmware-tanzu/sonobuoy-plugins/master/cis-benchmarks/kube-bench-plugin.yaml --plugin https://raw.githubusercontent.com/vmware-tanzu/sonobuoy-plugins/master/cis-benchmarks/kube-bench-master-plugin.yaml'
msTeamsWebhook: ${{ secrets.MS_TEAMS_WEBHOOK_URI }} msTeamsWebhook: ${{ secrets.MS_TEAMS_WEBHOOK_URI }}
cosignPublicKey: ${{ secrets.COSIGN_PUBLIC_KEY }}
cosignPrivateKey: ${{ secrets.COSIGN_PRIVATE_KEY }}
cosignPassword: ${{ secrets.COSIGN_PASSWORD }}
awsAccessKeyID: ${{ secrets.AWS_ACCESS_KEY_ID }}
awsSecretAccessKey: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
awsDefaultRegion: ${{ secrets.AWS_DEFAULT_REGION }}
awsBucketName: ${{ secrets.PUBLIC_BUCKET_NAME }}

View File

@ -27,3 +27,10 @@ jobs:
# TODO: Remove E2E_SKIP once AB#2174 is resolved # TODO: Remove E2E_SKIP once AB#2174 is resolved
sonobuoyTestSuiteCmd: '--plugin e2e --plugin-env e2e.E2E_FOCUS="\[Conformance\]" --plugin-env e2e.E2E_SKIP="for service with type clusterIP|HostPort validates that there is no conflict between pods with same hostPort but different hostIP and protocol" --plugin https://raw.githubusercontent.com/vmware-tanzu/sonobuoy-plugins/master/cis-benchmarks/kube-bench-plugin.yaml --plugin https://raw.githubusercontent.com/vmware-tanzu/sonobuoy-plugins/master/cis-benchmarks/kube-bench-master-plugin.yaml' sonobuoyTestSuiteCmd: '--plugin e2e --plugin-env e2e.E2E_FOCUS="\[Conformance\]" --plugin-env e2e.E2E_SKIP="for service with type clusterIP|HostPort validates that there is no conflict between pods with same hostPort but different hostIP and protocol" --plugin https://raw.githubusercontent.com/vmware-tanzu/sonobuoy-plugins/master/cis-benchmarks/kube-bench-plugin.yaml --plugin https://raw.githubusercontent.com/vmware-tanzu/sonobuoy-plugins/master/cis-benchmarks/kube-bench-master-plugin.yaml'
msTeamsWebhook: ${{ secrets.MS_TEAMS_WEBHOOK_URI }} msTeamsWebhook: ${{ secrets.MS_TEAMS_WEBHOOK_URI }}
cosignPublicKey: ${{ secrets.COSIGN_PUBLIC_KEY }}
cosignPrivateKey: ${{ secrets.COSIGN_PRIVATE_KEY }}
cosignPassword: ${{ secrets.COSIGN_PASSWORD }}
awsAccessKeyID: ${{ secrets.AWS_ACCESS_KEY_ID }}
awsSecretAccessKey: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
awsDefaultRegion: ${{ secrets.AWS_DEFAULT_REGION }}
awsBucketName: ${{ secrets.PUBLIC_BUCKET_NAME }}