terraform: add Terraform module for Azure (#2566)

* add Azure Terraform module

* add maa-patching command to cli

* refactor release process

* factor out image fetching to own action

* add CI

* generate

* fix some unnecessary changes

Signed-off-by: Moritz Sanft <58110325+msanft@users.noreply.github.com>

* use `constellation maa-patch` in ci

* insecure flag when using debug image

Signed-off-by: Moritz Sanft <58110325+msanft@users.noreply.github.com>

* only update maa url if existing

Signed-off-by: Moritz Sanft <58110325+msanft@users.noreply.github.com>

* make node group zone optional on aws and gcp

Signed-off-by: Moritz Sanft <58110325+msanft@users.noreply.github.com>

* [remove] register updated workflow

Signed-off-by: Moritz Sanft <58110325+msanft@users.noreply.github.com>

* Revert "[remove] register updated workflow"

This reverts commit e70b9515b7eabbcbe0d41fa1296c48750cd02ace.

* create MAA

Signed-off-by: Moritz Sanft <58110325+msanft@users.noreply.github.com>

* make maa-patching only run on azure

Signed-off-by: Moritz Sanft <58110325+msanft@users.noreply.github.com>

* add comment

Signed-off-by: Moritz Sanft <58110325+msanft@users.noreply.github.com>

* require node group zone for GCP and AWS

* remove unnecessary bazel action

* stamp version to correct file

* refer to `maa-patch` command in docs

* run Azure test in weekly e2e

* comment / naming improvements

* remove sa_account resource

* disable spellcheck ot use "URL"

* `create_maa` variable

* don't write maa url to config

Signed-off-by: Moritz Sanft <58110325+msanft@users.noreply.github.com>

* default to nightly image

* use input ref and stream

* fix command check

* don't set region in weekly e2e call

* patch maa if url is not empty

Signed-off-by: Moritz Sanft <58110325+msanft@users.noreply.github.com>

* remove `create_maa` variable

* remove binaries

Signed-off-by: Moritz Sanft <58110325+msanft@users.noreply.github.com>

* remove undefined input

* replace invalid attestation URL error message

Co-authored-by: Thomas Tendyck <51411342+thomasten@users.noreply.github.com>

* fix punctuation

Co-authored-by: Thomas Tendyck <51411342+thomasten@users.noreply.github.com>

* skip hidden commands in clidocgen

Signed-off-by: Moritz Sanft <58110325+msanft@users.noreply.github.com>

* enable spellcheck before code block

* move spellcheck trigger out of info block

Signed-off-by: Moritz Sanft <58110325+msanft@users.noreply.github.com>

* fix workflow dependencies

* let image default to CLI version

---------

Signed-off-by: Moritz Sanft <58110325+msanft@users.noreply.github.com>
Co-authored-by: Thomas Tendyck <51411342+thomasten@users.noreply.github.com>
This commit is contained in:
Moritz Sanft 2023-11-13 18:46:20 +01:00 committed by GitHub
parent e8f0c58558
commit 8e4feb7e2a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
34 changed files with 778 additions and 248 deletions

View File

@ -0,0 +1,69 @@
name: Find latest image
description: 'Find the latest image reference for a given ref/stream.'
inputs:
git-ref:
description: 'Git ref to checkout.'
imageVersion:
description: 'Image version to use. If set, no image will be searched for and the specified image will be returned.'
ref:
description: 'The ref the image was built on. (e.g. "main")'
default: 'main'
stream:
description: 'The publication stream of the image. (e.g. "debug")'
default: 'debug'
outputs:
image:
description: "Image reference to be used in the cluster."
value: ${{ steps.find-latest-image.outputs.output }}${{ steps.check-input.outputs.image }}
isDebugImage:
description: "Whether the image is a debug image."
value: ${{ steps.isDebugImage.outputs.isDebugImage }}
runs:
using: 'composite'
steps:
- name: Checkout head
if: inputs.imageVersion == '' && inputs.git-ref == 'head'
uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0
with:
ref: ${{ !github.event.pull_request.head.repo.fork && github.head_ref || '' }}
- name: Checkout ref
if: inputs.imageVersion == '' && inputs.git-ref != 'head'
uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0
with:
ref: ${{ inputs.git-ref }}
- name: Login to AWS
if: inputs.imageVersion == ''
uses: aws-actions/configure-aws-credentials@010d0da01d0b5a38af31e9c3470dbfdabdecca3a # v4.0.1
with:
role-to-assume: arn:aws:iam::795746500882:role/GithubConstellationVersionsAPIRead
aws-region: eu-central-1
- name: Find latest image
id: find-latest-image
if: inputs.imageVersion == ''
uses: ./.github/actions/versionsapi
with:
command: latest
ref: ${{ inputs.ref }}
stream: ${{ inputs.stream }}
- name: Is debug image?
id: isDebugImage
shell: bash
run: |
case "${{ inputs.imageVersion }}" in
"")
echo "isDebugImage=true" | tee -a "$GITHUB_OUTPUT"
;;
*"/stream/debug/"*)
echo "isDebugImage=true" | tee -a "$GITHUB_OUTPUT"
;;
*)
echo "isDebugImage=false" | tee -a "$GITHUB_OUTPUT"
;;
esac

View File

@ -84,7 +84,7 @@ runs:
working-directory: ${{ github.workspace }}/e2e-infra working-directory: ${{ github.workspace }}/e2e-infra
if: inputs.cloudProvider == 'azure' if: inputs.cloudProvider == 'azure'
run: | run: |
bazel run //hack/maa-patch:maa-patch $(terraform output attestationURL | jq -r) ./constellation maa-patch $(terraform output attestationURL | jq -r)
- name: Write outputs to state file - name: Write outputs to state file
shell: bash shell: bash

View File

@ -1,5 +1,10 @@
name: Upload Terraform module name: Upload Terraform module
description: "Upload the Terraform module as an artifact." description: "Upload the Terraform module as an artifact."
inputs:
version:
description: "The Constellation CLI version to use in the Terraform Module. Leave empty when a built CLI is already present when applying the module."
runs: runs:
using: "composite" using: "composite"
steps: steps:
@ -9,15 +14,23 @@ runs:
cp -r terraform terraform-module cp -r terraform terraform-module
find terraform-module -name "*.go" -type f -delete find terraform-module -name "*.go" -type f -delete
find terraform-module -name "*.bazel" -type f -delete find terraform-module -name "*.bazel" -type f -delete
- name: Stamp version
shell: bash
run: |
sed -i "s/@@CONSTELLATION_VERSION@@/${{ inputs.version }}/g" terraform-module/constellation-cluster/variables.tf
- name: Zip terraform dir - name: Zip terraform dir
shell: bash shell: bash
run: | run: |
zip -r terraform-module.zip terraform-module zip -r terraform-module.zip terraform-module
- name: Upload artifact - name: Upload artifact
uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3 uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3
with: with:
name: terraform-module name: terraform-module
path: terraform-module.zip path: terraform-module.zip
- name: Cleanup Terraform module dir - name: Cleanup Terraform module dir
shell: bash shell: bash
run: | run: |

View File

@ -6,32 +6,28 @@ on:
ref: ref:
type: string type: string
description: "Git ref to checkout" description: "Git ref to checkout"
required: false
cloudProvider: cloudProvider:
description: "Which cloud provider to use." description: "Which cloud provider to use."
type: choice type: choice
options: options:
- "aws" - "aws"
- "azure"
- "gcp" - "gcp"
required: true required: true
regionZone: regionZone:
description: "Region or zone to create the cluster in. Leave empty for default region/zone." description: "Region or zone to create the cluster in. Leave empty for default region/zone."
type: string type: string
image: image:
description: "Node image version of the cluster." description: "OS Image version used in the cluster's VMs, as specified in the Constellation config. If not set, the latest nightly image from main is used."
type: string type: string
required: true
cliVersion: cliVersion:
description: "Constellation CLI version to use. Empty value means build from source." description: "Constellation CLI version to use. Empty value means build from source."
type: string type: string
default: ""
required: false
workflow_call: workflow_call:
inputs: inputs:
ref: ref:
type: string type: string
description: "Git ref to checkout" description: "Git ref to checkout"
required: false
cloudProvider: cloudProvider:
description: "Which cloud provider to use." description: "Which cloud provider to use."
type: string type: string
@ -40,16 +36,14 @@ on:
description: "Which zone to use." description: "Which zone to use."
type: string type: string
image: image:
description: "Node image reference which is compatible with the current dev release version." description: "OS Image version used in the cluster's VMs, as specified in the Constellation config. If not set, the latest nightly image from main is used."
type: string type: string
required: true
cliVersion: cliVersion:
description: "Constellation CLI version to use. Empty value means build from source." description: "Constellation CLI version to use. Empty value means build from source."
type: string type: string
default: ""
required: false
jobs: jobs:
build: tf-module-test:
runs-on: ubuntu-22.04 runs-on: ubuntu-22.04
permissions: permissions:
id-token: write id-token: write
@ -62,16 +56,30 @@ jobs:
with: with:
ref: ${{ inputs.ref || github.head_ref }} ref: ${{ inputs.ref || github.head_ref }}
- name: Upload module - name: Get Latest Image
id: find-latest-image
uses: ./.github/actions/find_latest_image
with:
git-ref: ${{ inputs.ref }}
imageVersion: ${{ inputs.image }}
ref: main
stream: nightly
- name: Upload Terraform module
uses: ./.github/actions/upload_terraform_module uses: ./.github/actions/upload_terraform_module
with:
version: ${{ inputs.cliVersion }}
- name: Download Terraform module - name: Download Terraform module
uses: actions/download-artifact@v3 uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2
with: with:
name: terraform-module name: terraform-module
- name: Unzip Terraform module - name: Unzip Terraform module
run: unzip terraform-module.zip shell: bash
run: |
unzip terraform-module.zip -d ${{ github.workspace }}
rm terraform-module.zip
- name: Create resource prefix - name: Create resource prefix
id: create-prefix id: create-prefix
@ -83,29 +91,62 @@ jobs:
- name: Create AWS Terraform variable input file - name: Create AWS Terraform variable input file
if: inputs.cloudProvider == 'aws' if: inputs.cloudProvider == 'aws'
working-directory: ./terraform-module/aws-constellation working-directory: ${{ github.workspace }}/terraform-module/aws-constellation
shell: bash
run: | run: |
cat > terraform.tfvars <<EOF cat > terraform.tfvars <<EOF
name = "${{ steps.create-prefix.outputs.prefix }}" name = "${{ steps.create-prefix.outputs.prefix }}"
image = "${{ inputs.image }}" image = "${{ steps.find-latest-image.outputs.image }}"
zone = "${{ inputs.regionZone || 'us-east-2c' }}" zone = "${{ inputs.regionZone || 'us-east-2c' }}"
name_prefix = "${{ steps.create-prefix.outputs.prefix }}" name_prefix = "${{ steps.create-prefix.outputs.prefix }}"
debug = ${{ steps.find-latest-image.outputs.isDebugImage }}
node_groups = { node_groups = {
control_plane_default = { control_plane_default = {
role = "control-plane" role = "control-plane"
zone = "${{ inputs.regionZone || 'us-east-2c' }}"
instance_type = "m6a.xlarge" instance_type = "m6a.xlarge"
disk_size = 30 disk_size = 30
disk_type = "gp3" disk_type = "gp3"
initial_count = 2 initial_count = 2
zone = "${{ inputs.regionZone || 'us-east-2c' }}"
}, },
worker_default = { worker_default = {
role = "worker" role = "worker"
zone = "${{ inputs.regionZone || 'us-east-2c' }}"
instance_type = "m6a.xlarge" instance_type = "m6a.xlarge"
disk_size = 30 disk_size = 30
disk_type = "gp3" disk_type = "gp3"
initial_count = 2 initial_count = 2
zone = "${{ inputs.regionZone || 'us-east-2c' }}"
}
}
EOF
cat terraform.tfvars
- name: Create Azure Terraform variable input file
if: inputs.cloudProvider == 'azure'
working-directory: ${{ github.workspace }}/terraform-module/azure-constellation
shell: bash
run: |
cat > terraform.tfvars <<EOF
name = "${{ steps.create-prefix.outputs.prefix }}"
image = "${{ steps.find-latest-image.outputs.image }}"
location = "${{ inputs.regionZone || 'northeurope' }}"
service_principal_name = "${{ steps.create-prefix.outputs.prefix }}-sp"
resource_group_name = "${{ steps.create-prefix.outputs.prefix }}-rg"
debug = ${{ steps.find-latest-image.outputs.isDebugImage }}
node_groups = {
control_plane_default = {
role = "control-plane"
instance_type = "Standard_DC4as_v5"
disk_size = 30
disk_type = "Premium_LRS"
initial_count = 2
},
worker_default = {
role = "worker"
instance_type = "Standard_DC4as_v5"
disk_size = 30
disk_type = "Premium_LRS"
initial_count = 2
} }
} }
EOF EOF
@ -113,36 +154,39 @@ jobs:
- name: Create GCP Terraform variable input file - name: Create GCP Terraform variable input file
if: inputs.cloudProvider == 'gcp' if: inputs.cloudProvider == 'gcp'
working-directory: ./terraform-module/gcp-constellation working-directory: ${{ github.workspace }}/terraform-module/gcp-constellation
shell: bash
run: | run: |
cat > terraform.tfvars <<EOF cat > terraform.tfvars <<EOF
name = "${{ steps.create-prefix.outputs.prefix }}" name = "${{ steps.create-prefix.outputs.prefix }}"
project = "${{ secrets.GCP_E2E_PROJECT }}" project = "${{ secrets.GCP_E2E_PROJECT }}"
service_account_id = "${{ steps.create-prefix.outputs.prefix }}-sa" service_account_id = "${{ steps.create-prefix.outputs.prefix }}-sa"
image = "${{ inputs.image }}" image = "${{ steps.find-latest-image.outputs.image }}"
zone = "${{ inputs.regionZone || 'europe-west3-b' }}" zone = "${{ inputs.regionZone || 'europe-west3-b' }}"
debug = ${{ steps.find-latest-image.outputs.isDebugImage }}
node_groups = { node_groups = {
control_plane_default = { control_plane_default = {
role = "control-plane" role = "control-plane"
zone = "${{ inputs.regionZone || 'europe-west3-b' }}"
instance_type = "n2d-standard-4" instance_type = "n2d-standard-4"
disk_size = 30 disk_size = 30
disk_type = "pd-ssd" disk_type = "pd-ssd"
initial_count = 2 initial_count = 2
zone = "${{ inputs.regionZone || 'europe-west3-b' }}"
}, },
worker_default = { worker_default = {
role = "worker" role = "worker"
zone = "${{ inputs.regionZone || 'europe-west3-b' }}"
instance_type = "n2d-standard-4" instance_type = "n2d-standard-4"
disk_size = 30 disk_size = 30
disk_type = "pd-ssd" disk_type = "pd-ssd"
initial_count = 2 initial_count = 2
zone = "${{ inputs.regionZone || 'europe-west3-b' }}"
} }
} }
EOF EOF
cat terraform.tfvars cat terraform.tfvars
- name: Install dependencies (Terraform) - name: Install dependencies (Terraform)
shell: bash
run: | run: |
sudo apt update && sudo apt install gpg sudo apt update && sudo apt install gpg
wget -O- https://apt.releases.hashicorp.com/gpg | sudo gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg wget -O- https://apt.releases.hashicorp.com/gpg | sudo gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg
@ -178,7 +222,7 @@ jobs:
shell: bash shell: bash
run: | run: |
curl -fsSL -o constellation https://github.com/edgelesssys/constellation/releases/download/${{ inputs.cliVersion }}/constellation-linux-amd64 curl -fsSL -o constellation https://github.com/edgelesssys/constellation/releases/download/${{ inputs.cliVersion }}/constellation-linux-amd64
chmod u+x ./constellation chmod u+x constellation
./constellation version ./constellation version
sudo sh -c 'echo "127.0.0.1 license.confidential.cloud" >> /etc/hosts' sudo sh -c 'echo "127.0.0.1 license.confidential.cloud" >> /etc/hosts'
@ -191,6 +235,12 @@ jobs:
# extend token expiry to 6 hours to ensure constellation can terminate # extend token expiry to 6 hours to ensure constellation can terminate
role-duration-seconds: 21600 role-duration-seconds: 21600
- name: Login to Azure (IAM + Cluster service principal)
if: inputs.cloudProvider == 'azure'
uses: ./.github/actions/login_azure
with:
azure_credentials: ${{ secrets.AZURE_E2E_TF_CREDENTIALS }}
- name: Login to GCP (IAM + Cluster service account) - name: Login to GCP (IAM + Cluster service account)
if: inputs.cloudProvider == 'gcp' if: inputs.cloudProvider == 'gcp'
uses: ./.github/actions/login_gcp uses: ./.github/actions/login_gcp
@ -199,7 +249,8 @@ jobs:
- name: Apply Terraform Cluster - name: Apply Terraform Cluster
id: apply_terraform id: apply_terraform
working-directory: ./terraform-module/${{ inputs.cloudProvider }}-constellation working-directory: ${{ github.workspace }}/terraform-module/${{ inputs.cloudProvider }}-constellation
shell: bash
run: | run: |
cp ../../constellation . cp ../../constellation .
terraform init terraform init
@ -208,13 +259,15 @@ jobs:
- name: Destroy Terraform Cluster - name: Destroy Terraform Cluster
# outcome is part of the steps context (https://docs.github.com/en/actions/learn-github-actions/contexts#steps-context) # outcome is part of the steps context (https://docs.github.com/en/actions/learn-github-actions/contexts#steps-context)
if: always() && steps.apply_terraform.outcome != 'skipped' if: always() && steps.apply_terraform.outcome != 'skipped'
working-directory: ./terraform-module/${{ inputs.cloudProvider }}-constellation working-directory: ${{ github.workspace }}/terraform-module/${{ inputs.cloudProvider }}-constellation
shell: bash
run: | run: |
terraform init terraform init
terraform destroy -var-file=terraform.tfvars -auto-approve terraform destroy -var-file=terraform.tfvars -auto-approve
- name: Verify cleanup - name: Verify cleanup
working-directory: ./terraform-module/${{ inputs.cloudProvider }}-constellation working-directory: ${{ github.workspace }}/terraform-module/${{ inputs.cloudProvider }}-constellation
shell: bash
run: | run: |
if [ -f constellation-mastersecret.json ] || [ -f constellation-conf.yaml ]; then if [ -f constellation-mastersecret.json ] || [ -f constellation-conf.yaml ]; then
echo "Files constellation-mastersecret.json or constellation-conf.yaml still exist" echo "Files constellation-mastersecret.json or constellation-conf.yaml still exist"

View File

@ -376,20 +376,14 @@ jobs:
max-parallel: 5 max-parallel: 5
matrix: matrix:
include: include:
- test: "GCP" - provider: "gcp"
provider: "gcp" - provider: "aws"
regionZone: "europe-west2-a" - provider: "azure"
- test: "AWS"
provider: "aws"
regionZone: "us-east-2c"
permissions: permissions:
id-token: write id-token: write
contents: read contents: read
packages: write packages: write
needs: [find-latest-image]
secrets: inherit secrets: inherit
uses: ./.github/workflows/e2e-test-tf-module.yml uses: ./.github/workflows/e2e-test-tf-module.yml
with: with:
cloudProvider: "${{ matrix.provider }}" cloudProvider: "${{ matrix.provider }}"
regionZone: "${{ matrix.regionZone }}"
image: ${{ needs.find-latest-image.outputs.image-main-nightly }}

View File

@ -151,64 +151,18 @@ jobs:
id-token: write id-token: write
contents: read contents: read
outputs: outputs:
image: ${{ steps.find-latest-image.outputs.output }}${{ steps.check-input.outputs.image }} image: ${{ steps.find-latest-image.outputs.image }}
isDebugImage: ${{ steps.isDebugImage.outputs.isDebugImage }} isDebugImage: ${{ steps.find-latest-image.outputs.isDebugImage }}
steps: steps:
- name: Check input - name: Get Latest Image
id: check-input
shell: bash
run: |
if [[ -z "${{ inputs.imageVersion }}" ]]; then
echo "Using latest debug image from main."
exit 0
else
echo "image=${{ inputs.imageVersion }}" | tee -a "$GITHUB_OUTPUT"
fi
- name: Checkout head
if: inputs.imageVersion == '' && inputs.git-ref == 'head'
uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0
with:
ref: ${{ !github.event.pull_request.head.repo.fork && github.head_ref || '' }}
- name: Checkout ref
if: inputs.imageVersion == '' && inputs.git-ref != 'head'
uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0
with:
ref: ${{ inputs.git-ref }}
- name: Login to AWS
if: inputs.imageVersion == ''
uses: aws-actions/configure-aws-credentials@010d0da01d0b5a38af31e9c3470dbfdabdecca3a # v4.0.1
with:
role-to-assume: arn:aws:iam::795746500882:role/GithubConstellationVersionsAPIRead
aws-region: eu-central-1
- name: Find latest image
id: find-latest-image id: find-latest-image
if: inputs.imageVersion == '' uses: ./.github/actions/find_latest_image
uses: ./.github/actions/versionsapi
with: with:
command: latest git-ref: ${{ inputs.git-ref }}
imageVersion: ${{ inputs.imageVersion }}
ref: main ref: main
stream: debug stream: debug
- name: Is debug image?
id: isDebugImage
shell: bash
run: |
case "${{ inputs.imageVersion }}" in
"")
echo "isDebugImage=true" | tee -a "$GITHUB_OUTPUT"
;;
*"/stream/debug/"*)
echo "isDebugImage=true" | tee -a "$GITHUB_OUTPUT"
;;
*)
echo "isDebugImage=false" | tee -a "$GITHUB_OUTPUT"
;;
esac
e2e-test-manual: e2e-test-manual:
runs-on: ${{ inputs.runner }} runs-on: ${{ inputs.runner }}
permissions: permissions:

View File

@ -76,9 +76,6 @@ jobs:
with: with:
ref: ${{ inputs.ref || github.head_ref }} ref: ${{ inputs.ref || github.head_ref }}
- name: Upload Terraform module
uses: ./.github/actions/upload_terraform_module
- name: Setup bazel - name: Setup bazel
uses: ./.github/actions/setup_bazel_nix uses: ./.github/actions/setup_bazel_nix
with: with:
@ -112,6 +109,20 @@ jobs:
build/constellation-${{ matrix.os }}-${{ matrix.arch }}.exe build/constellation-${{ matrix.os }}-${{ matrix.arch }}.exe
build/constellation-${{ matrix.os }}-${{ matrix.arch }}.exe.sig build/constellation-${{ matrix.os }}-${{ matrix.arch }}.exe.sig
upload-terraform-module:
runs-on: ubuntu-22.04
steps:
- name: Checkout
id: checkout
uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0
with:
ref: ${{ inputs.ref || github.head_ref }}
- name: Upload Terraform module
uses: ./.github/actions/upload_terraform_module
with:
version: ${{ inputs.versionName }}
push-containers: push-containers:
runs-on: ubuntu-22.04 runs-on: ubuntu-22.04
if: inputs.pushContainers if: inputs.pushContainers
@ -148,6 +159,7 @@ jobs:
needs: needs:
- build-cli - build-cli
- signed-sbom - signed-sbom
- upload-terraform-module
outputs: outputs:
provenance-subjects: ${{ steps.provenance-subjects.outputs.provenance-subjects }} provenance-subjects: ${{ steps.provenance-subjects.outputs.provenance-subjects }}
steps: steps:
@ -278,6 +290,7 @@ jobs:
needs: needs:
- build-cli - build-cli
- provenance - provenance
- upload-terraform-module
steps: steps:
- name: Download CLI binaries darwin-amd64 - name: Download CLI binaries darwin-amd64
uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2 uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2
@ -356,6 +369,7 @@ jobs:
- build-cli - build-cli
- provenance - provenance
- signed-sbom - signed-sbom
- upload-terraform-module
steps: steps:
- name: Write cosign public key - name: Write cosign public key
run: echo "$COSIGN_PUBLIC_KEY" > cosign.pub run: echo "$COSIGN_PUBLIC_KEY" > cosign.pub

View File

@ -61,6 +61,7 @@ func NewRootCmd() *cobra.Command {
rootCmd.AddCommand(cmd.NewIAMCmd()) rootCmd.AddCommand(cmd.NewIAMCmd())
rootCmd.AddCommand(cmd.NewVersionCmd()) rootCmd.AddCommand(cmd.NewVersionCmd())
rootCmd.AddCommand(cmd.NewInitCmd()) rootCmd.AddCommand(cmd.NewInitCmd())
rootCmd.AddCommand(cmd.NewMaaPatchCmd())
return rootCmd return rootCmd
} }

View File

@ -26,6 +26,7 @@ go_library(
"iamupgradeapply.go", "iamupgradeapply.go",
"init.go", "init.go",
"log.go", "log.go",
"maapatch.go",
"mini.go", "mini.go",
"minidown.go", "minidown.go",
"miniup.go", "miniup.go",
@ -78,6 +79,7 @@ go_library(
"//internal/kms/uri", "//internal/kms/uri",
"//internal/license", "//internal/license",
"//internal/logger", "//internal/logger",
"//internal/maa",
"//internal/retry", "//internal/retry",
"//internal/semver", "//internal/semver",
"//internal/sigstore", "//internal/sigstore",
@ -129,6 +131,7 @@ go_test(
"iamdestroy_test.go", "iamdestroy_test.go",
"iamupgradeapply_test.go", "iamupgradeapply_test.go",
"init_test.go", "init_test.go",
"maapatch_test.go",
"recover_test.go", "recover_test.go",
"spinner_test.go", "spinner_test.go",
"status_test.go", "status_test.go",

View File

@ -0,0 +1,71 @@
/*
Copyright (c) Edgeless Systems GmbH
SPDX-License-Identifier: AGPL-3.0-only
*/
package cmd
import (
"context"
"fmt"
"net/url"
"github.com/edgelesssys/constellation/v2/internal/maa"
"github.com/spf13/cobra"
)
// NewMaaPatchCmd returns a new cobra.Command for the maa-patch command.
func NewMaaPatchCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "maa-patch <attestation-url>",
Short: "Patch the MAA's attestation policy",
Long: "Patch the MAA's attestation policy.",
Args: cobra.MatchAll(
cobra.ExactArgs(1),
func(cmd *cobra.Command, args []string) error {
if _, err := url.Parse(args[0]); err != nil {
return fmt.Errorf("argument %s is not a valid URL: %w", args[0], err)
}
return nil
},
),
RunE: runPatchMAA,
Hidden: true, // we don't want to show this command to the user directly.
}
return cmd
}
type maaPatchCmd struct {
log debugLog
patcher patcher
}
func runPatchMAA(cmd *cobra.Command, args []string) error {
log, err := newCLILogger(cmd)
if err != nil {
return fmt.Errorf("creating logger: %w", err)
}
defer log.Sync()
p := maa.NewAzurePolicyPatcher()
c := &maaPatchCmd{log: log, patcher: p}
return c.patchMAA(cmd, args[0])
}
func (c *maaPatchCmd) patchMAA(cmd *cobra.Command, attestationURL string) error {
c.log.Debugf("Using attestation URL %s", attestationURL)
if err := c.patcher.Patch(cmd.Context(), attestationURL); err != nil {
return fmt.Errorf("patching MAA attestation policy: %w", err)
}
return nil
}
type patcher interface {
Patch(ctx context.Context, attestationURL string) error
}

View File

@ -0,0 +1,59 @@
/*
Copyright (c) Edgeless Systems GmbH
SPDX-License-Identifier: AGPL-3.0-only
*/
package cmd
import (
"context"
"testing"
"github.com/edgelesssys/constellation/v2/internal/logger"
"github.com/spf13/cobra"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestMAAPatch(t *testing.T) {
testCases := map[string]struct {
attestationURL string
patcher *stubPolicyPatcher
wantErr bool
}{
"success": {
attestationURL: "https://example.com",
patcher: &stubPolicyPatcher{},
wantErr: false,
},
"patch error": {
attestationURL: "https://example.com",
patcher: &stubPolicyPatcher{patchErr: assert.AnError},
wantErr: true,
},
}
for name, tc := range testCases {
t.Run(name, func(t *testing.T) {
require := require.New(t)
c := &maaPatchCmd{log: logger.NewTest(t), patcher: tc.patcher}
err := c.patchMAA(&cobra.Command{}, tc.attestationURL)
if tc.wantErr {
require.Error(err)
} else {
require.NoError(err)
}
})
}
}
type stubPolicyPatcher struct {
patchErr error
}
// Patch implements the patcher interface.
func (p *stubPolicyPatcher) Patch(context.Context, string) error {
return p.patchErr
}

View File

@ -30,10 +30,6 @@ Releases should be performed using [the automated release pipeline](https://gith
git push origin ${working_branch} git push origin ${working_branch}
``` ```
### Update CLI version for Terraform module
Update the `version` inside `terraform/constellation-cluster/install-constellation.sh` to the new release.
### Patch release ### Patch release
1. `cherry-pick` (only) the required commits from `main` 1. `cherry-pick` (only) the required commits from `main`

View File

@ -249,19 +249,21 @@ To self-manage the infrastructure of your cluster, download the Terraform files
They contain a minimum configuration for the resources necessary to run a Constellation cluster on the corresponding CSP. From this base, you can now add, edit, or substitute resources per your own requirements with the infrastructure They contain a minimum configuration for the resources necessary to run a Constellation cluster on the corresponding CSP. From this base, you can now add, edit, or substitute resources per your own requirements with the infrastructure
management tooling of your choice. You need to keep the essential functionality of the base configuration in order for your cluster to function correctly. management tooling of your choice. You need to keep the essential functionality of the base configuration in order for your cluster to function correctly.
<!-- vale off -->
:::info :::info
On Azure, if the enforcement policy is set to `MAAFallback` in `constellation-config.yaml`, a manual update to the MAA provider's policy is necessary. On Azure, if the enforcement policy is set to `MAAFallback` in `constellation-config.yaml`, a manual update to the MAA provider's policy is necessary.
You can apply the update with the following commands, where `<VERSION>` is the version of Constellation that should be set up. (e.g. `v2.12.0`) You can apply the update with the following command after creating the infrastructure, with `<URL>` being the URL of the MAA provider (i.e., `$(terraform output attestationURL | jq -r)`, when using the minimal Terraform configuration).
```bash ```bash
git clone --branch <VERSION> https://github.com/edgelesssys/constellation constellation maa-patch <URL>
cd constellation/hack/maa-patch
go run . $(terraform output attestationURL | jq -r)
``` ```
::: :::
<!-- vale on -->
Make sure all necessary resources are created, e.g., through checking your CSP's portal and retrieve the necessary values, aligned with the outputs (specified in `outputs.tf`) of the base configuration. Make sure all necessary resources are created, e.g., through checking your CSP's portal and retrieve the necessary values, aligned with the outputs (specified in `outputs.tf`) of the base configuration.
Fill these outputs into the corresponding fields of the `constellation-state.yaml` file. For example, fill the IP or DNS name your cluster can be reached at into the `.Infrastructure.ClusterEndpoint` field. Fill these outputs into the corresponding fields of the `constellation-state.yaml` file. For example, fill the IP or DNS name your cluster can be reached at into the `.Infrastructure.ClusterEndpoint` field.

View File

@ -28,6 +28,9 @@ func main() {
cmdList := &bytes.Buffer{} cmdList := &bytes.Buffer{}
body := &bytes.Buffer{} body := &bytes.Buffer{}
for _, c := range allSubCommands(rootCmd) { for _, c := range allSubCommands(rootCmd) {
if c.Hidden {
continue
}
name := c.Name() name := c.Name()
fullName, level := determineFullNameAndLevel(c) fullName, level := determineFullNameAndLevel(c)

View File

@ -1,22 +0,0 @@
load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library")
load("//bazel/go:go_test.bzl", "go_test")
go_library(
name = "maa-patch_lib",
srcs = ["main.go"],
importpath = "github.com/edgelesssys/constellation/v2/hack/maa-patch",
visibility = ["//visibility:private"],
deps = ["//internal/maa"],
)
go_binary(
name = "maa-patch",
embed = [":maa-patch_lib"],
visibility = ["//visibility:public"],
)
go_test(
name = "maa-patch_test",
srcs = ["main_test.go"],
embed = [":maa-patch_lib"],
)

View File

@ -1,33 +0,0 @@
/*
Copyright (c) Edgeless Systems GmbH
SPDX-License-Identifier: AGPL-3.0-only
*/
package main
import (
"context"
"fmt"
"net/url"
"os"
"github.com/edgelesssys/constellation/v2/internal/maa"
)
func main() {
if len(os.Args) != 2 {
fmt.Fprintf(os.Stderr, "Usage: %s <attestation-url>\n", os.Args[0])
os.Exit(1)
}
attestationURL := os.Args[1]
if _, err := url.Parse(attestationURL); err != nil {
fmt.Fprintf(os.Stderr, "Invalid attestation URL: %s\n", err)
os.Exit(1)
}
p := maa.NewAzurePolicyPatcher()
if err := p.Patch(context.Background(), attestationURL); err != nil {
panic(err)
}
}

View File

@ -1,13 +0,0 @@
/*
Copyright (c) Edgeless Systems GmbH
SPDX-License-Identifier: AGPL-3.0-only
*/
package main
import "testing"
func TestNop(t *testing.T) {
t.Skip("This is a nop-test to catch build-time errors in this package.")
}

View File

@ -12,7 +12,7 @@ module "aws_iam" {
resource "null_resource" "ensure_yq" { resource "null_resource" "ensure_yq" {
provisioner "local-exec" { provisioner "local-exec" {
command = <<EOT command = <<EOT
../constellation-cluster/install-yq.sh ../common/install-yq.sh
EOT EOT
} }
triggers = { triggers = {
@ -21,7 +21,7 @@ resource "null_resource" "ensure_yq" {
} }
module "fetch_image" { module "fetch_image" {
source = "../fetch-image" source = "../common/fetch-image"
csp = "aws" csp = "aws"
attestation_variant = var.enable_snp ? "aws-sev-snp" : "aws-nitro-tpm" attestation_variant = var.enable_snp ? "aws-sev-snp" : "aws-nitro-tpm"
region = local.region region = local.region
@ -47,6 +47,7 @@ module "aws" {
module "constellation" { module "constellation" {
source = "../constellation-cluster" source = "../constellation-cluster"
csp = "aws" csp = "aws"
debug = var.debug
name = var.name name = var.name
image = var.image image = var.image
microservice_version = var.microservice_version microservice_version = var.microservice_version

View File

@ -5,7 +5,7 @@ variable "name" {
variable "image" { variable "image" {
type = string type = string
description = "Node image reference or semantical release version. The field is required to resolve the AMI." description = "Node image reference or semantic release version."
} }
variable "microservice_version" { variable "microservice_version" {

View File

@ -0,0 +1,143 @@
# This file is maintained automatically by "terraform init".
# Manual edits may be lost in future updates.
provider "registry.terraform.io/hashicorp/azuread" {
version = "2.43.0"
constraints = "2.43.0"
hashes = [
"h1:/wPCaaEC7By9zWMxYmPMiLNu+zuYuFUyl5mkhCwwi8w=",
"h1:83hGDTWccRJXsKGg1VeYJkBeHwE2cCTRKFCZud4iWQo=",
"h1:bp9HeofaEJDiWtyLMwIEYVgxP5yoMs/dQhjCYsbXU34=",
"h1:jmvCGhwc+jUip0Hy4PI1ZiO/11vdQ3TTp3YaBTKFGiQ=",
"h1:tU/kGFohqNia+uVFT1ujYKZRH2lvEP73LUhQDJtO1w4=",
"zh:1c3e89cf19118fc07d7b04257251fc9897e722c16e0a0df7b07fcd261f8c12e7",
"zh:2225e2e97ccba4ed1d84f1d430f1ebd837943fe187e57f24f1763172dda61556",
"zh:24708cb09411a766ff397e05cae49058ca38edc718db303a7faef9823402737d",
"zh:3a61167ff58d585abd56233731a8fd649c7c04272bd5b878f963883496e19192",
"zh:433f557634b5e663caaeb68c504c7771c186eba7ecf5d4030437956bc6599ecb",
"zh:5e8cc3b3bcc22d217cf588c821ce091c7d40f0815aecc1addde5355c17cb381d",
"zh:7b008c376097cd60259d43f58fcb33fee56fe9aebb4a94ed7958868ee501d7d0",
"zh:908907fd38537583ea60dccbf73055ae1a2963acc399be4f8e9a6616a9a537db",
"zh:966586cfd850606bab7dd2242c5b9e35d3a7178f64eaac0b44dea54c104c8169",
"zh:a624286401913d3ec44b4825e2c5ae38ac94fb4950aeed8f4b91d09c898f8cce",
"zh:b5171a4463fd0d9b0ce2a08605499b6d99fe93d6fc3f4143e9a26201065cc90a",
"zh:cdcfeeb9db4dbdc6f1fb5644453b37dbd0025b4f3127e9ff348f1e62d66b493e",
]
}
provider "registry.terraform.io/hashicorp/azurerm" {
version = "3.74.0"
constraints = "3.74.0"
hashes = [
"h1:4b15khHtc5OkIVEFg0W5QRwf/ov1WVQkXVdSiAcTCS8=",
"h1:ETVZfmulZQ435+lgFCkZRpfVOLyAxfDOwbPXFg3aLLQ=",
"h1:H3diAufZ5VDQKsQNYykVRaFTOUJ4gjFiT2VLYi574+w=",
"h1:LEdK8BxNSNiBQbtcJhQZKMMHDjmPpUsvDpr3Mzs93Tg=",
"h1:VfBB00BE0wvFiod7BlL+Cn6r2599MEi94hnAQ277ux8=",
"zh:0424c70152f949da1ec52ba96d20e5fd32fd22d9bd9203ce045d5f6aab3d20fc",
"zh:16dbf581d10f8e7937185bcdcceb4f91d08c919e452fb8da7580071288c8c397",
"zh:3019103bc2c3b4e185f5c65696c349697644c968f5c085af5505fed6d01c4241",
"zh:49bb56ebaed6653fdb913c2b2bb74fc8b5399e7258d1e89084f72c44ea1130dd",
"zh:85547666517f899d88620bd23a000a8f43c7dc93587c350eb1ea17bcb3e645c7",
"zh:8bed8b646ff1822d8764de68b56b71e5dd971a4b77eba80d47f400a530800bea",
"zh:8bfa6c70c004ba05ebce47f74f49ce872c28a68a18bb71b281a9681bcbbdbfa1",
"zh:a2ae9e38fda0695fb8aa810e4f1ce4b104bfda651a87923b307bb1728680d8b6",
"zh:beac1efe32f99072c892095f5ff46e40d6852b66679a03bc3acbe1b90fb1f653",
"zh:d8a6ca20e49ebe7ea5688d91233d571e2c2ccc3e41000c39a7d7031df209ea8e",
"zh:f569b65999264a9416862bca5cd2a6177d94ccb0424f3a4ef424428912b9cb3c",
"zh:f937b5fdf49b072c0347408d0a1c5a5d822dae1a23252915930e5a82d1d8ce8b",
]
}
provider "registry.terraform.io/hashicorp/local" {
version = "2.4.0"
hashes = [
"h1:7RnIbO3CFakblTJs7o0mUiY44dc9xGYsLhSNFSNS1Ds=",
"h1:Bs7LAkV/iQTLv72j+cTMrvx2U3KyXrcVHaGbdns1NcE=",
"h1:OMqURhlP2IgLEgUCzSlaKXyb/IbnKForgDSjZF/NY4Y=",
"h1:R97FTYETo88sT2VHfMgkPU3lzCsZLunPftjSI5vfKe8=",
"h1:ZUEYUmm2t4vxwzxy1BvN1wL6SDWrDxfH7pxtzX8c6d0=",
"zh:53604cd29cb92538668fe09565c739358dc53ca56f9f11312b9d7de81e48fab9",
"zh:66a46e9c508716a1c98efbf793092f03d50049fa4a83cd6b2251e9a06aca2acf",
"zh:70a6f6a852dd83768d0778ce9817d81d4b3f073fab8fa570bff92dcb0824f732",
"zh:78d5eefdd9e494defcb3c68d282b8f96630502cac21d1ea161f53cfe9bb483b3",
"zh:82a803f2f484c8b766e2e9c32343e9c89b91997b9f8d2697f9f3837f62926b35",
"zh:9708a4e40d6cc4b8afd1352e5186e6e1502f6ae599867c120967aebe9d90ed04",
"zh:973f65ce0d67c585f4ec250c1e634c9b22d9c4288b484ee2a871d7fa1e317406",
"zh:c8fa0f98f9316e4cfef082aa9b785ba16e36ff754d6aba8b456dab9500e671c6",
"zh:cfa5342a5f5188b20db246c73ac823918c189468e1382cb3c48a9c0c08fc5bf7",
"zh:e0e2b477c7e899c63b06b38cd8684a893d834d6d0b5e9b033cedc06dd7ffe9e2",
"zh:f62d7d05ea1ee566f732505200ab38d94315a4add27947a60afa29860822d3fc",
"zh:fa7ce69dde358e172bd719014ad637634bbdabc49363104f4fca759b4b73f2ce",
]
}
provider "registry.terraform.io/hashicorp/null" {
version = "3.2.1"
hashes = [
"h1:FbGfc+muBsC17Ohy5g806iuI1hQc4SIexpYCrQHQd8w=",
"h1:tSj1mL6OQ8ILGqR2mDu7OYYYWf+hoir0pf9KAQ8IzO8=",
"h1:vUW21lLLsKlxtBf0QF7LKJreKxs0CM7YXGzqW1N/ODY=",
"h1:wqgRvlyVIbkCeCQs+5jj6zVuQL0KDxZZtNofGqqlSdI=",
"h1:ydA0/SNRVB1o95btfshvYsmxA+jZFRZcvKzZSB+4S1M=",
"zh:58ed64389620cc7b82f01332e27723856422820cfd302e304b5f6c3436fb9840",
"zh:62a5cc82c3b2ddef7ef3a6f2fedb7b9b3deff4ab7b414938b08e51d6e8be87cb",
"zh:63cff4de03af983175a7e37e52d4bd89d990be256b16b5c7f919aff5ad485aa5",
"zh:74cb22c6700e48486b7cabefa10b33b801dfcab56f1a6ac9b6624531f3d36ea3",
"zh:78d5eefdd9e494defcb3c68d282b8f96630502cac21d1ea161f53cfe9bb483b3",
"zh:79e553aff77f1cfa9012a2218b8238dd672ea5e1b2924775ac9ac24d2a75c238",
"zh:a1e06ddda0b5ac48f7e7c7d59e1ab5a4073bbcf876c73c0299e4610ed53859dc",
"zh:c37a97090f1a82222925d45d84483b2aa702ef7ab66532af6cbcfb567818b970",
"zh:e4453fbebf90c53ca3323a92e7ca0f9961427d2f0ce0d2b65523cc04d5d999c2",
"zh:e80a746921946d8b6761e77305b752ad188da60688cfd2059322875d363be5f5",
"zh:fbdb892d9822ed0e4cb60f2fedbdbb556e4da0d88d3b942ae963ed6ff091e48f",
"zh:fca01a623d90d0cad0843102f9b8b9fe0d3ff8244593bd817f126582b52dd694",
]
}
provider "registry.terraform.io/hashicorp/random" {
version = "3.5.1"
constraints = "3.5.1"
hashes = [
"h1:3hjTP5tQBspPcFAJlfafnWrNrKnr7J4Cp0qB9jbqf30=",
"h1:6FVyQ/aG6tawPam6B+oFjgdidKd83uG9n7dOSQ66HBA=",
"h1:IL9mSatmwov+e0+++YX2V6uel+dV6bn+fC/cnGDK3Ck=",
"h1:VSnd9ZIPyfKHOObuQCaKfnjIHRtR7qTw19Rz8tJxm+k=",
"h1:sZ7MTSD4FLekNN2wSNFGpM+5slfvpm5A/NLVZiB7CO0=",
"zh:04e3fbd610cb52c1017d282531364b9c53ef72b6bc533acb2a90671957324a64",
"zh:119197103301ebaf7efb91df8f0b6e0dd31e6ff943d231af35ee1831c599188d",
"zh:4d2b219d09abf3b1bb4df93d399ed156cadd61f44ad3baf5cf2954df2fba0831",
"zh:6130bdde527587bbe2dcaa7150363e96dbc5250ea20154176d82bc69df5d4ce3",
"zh:6cc326cd4000f724d3086ee05587e7710f032f94fc9af35e96a386a1c6f2214f",
"zh:78d5eefdd9e494defcb3c68d282b8f96630502cac21d1ea161f53cfe9bb483b3",
"zh:b6d88e1d28cf2dfa24e9fdcc3efc77adcdc1c3c3b5c7ce503a423efbdd6de57b",
"zh:ba74c592622ecbcef9dc2a4d81ed321c4e44cddf7da799faa324da9bf52a22b2",
"zh:c7c5cde98fe4ef1143bd1b3ec5dc04baf0d4cc3ca2c5c7d40d17c0e9b2076865",
"zh:dac4bad52c940cd0dfc27893507c1e92393846b024c5a9db159a93c534a3da03",
"zh:de8febe2a2acd9ac454b844a4106ed295ae9520ef54dc8ed2faf29f12716b602",
"zh:eab0d0495e7e711cca367f7d4df6e322e6c562fc52151ec931176115b83ed014",
]
}
provider "registry.terraform.io/hashicorp/tls" {
version = "4.0.4"
hashes = [
"h1:GZcFizg5ZT2VrpwvxGBHQ/hO9r6g0vYdQqx3bFD3anY=",
"h1:Wd3RqmQW60k2QWPN4sK5CtjGuO1d+CRNXgC+D4rKtXc=",
"h1:bNsvpX5EGuVxgGRXBQVLXlmq40PdoLp8Rfuh1ZmV7yY=",
"h1:pe9vq86dZZKCm+8k1RhzARwENslF3SXb9ErHbQfgjXU=",
"h1:rKKMyIEBZwR+8j6Tx3PwqBrStuH+J+pxcbCR5XN8WAw=",
"zh:23671ed83e1fcf79745534841e10291bbf34046b27d6e68a5d0aab77206f4a55",
"zh:45292421211ffd9e8e3eb3655677700e3c5047f71d8f7650d2ce30242335f848",
"zh:59fedb519f4433c0fdb1d58b27c210b27415fddd0cd73c5312530b4309c088be",
"zh:5a8eec2409a9ff7cd0758a9d818c74bcba92a240e6c5e54b99df68fff312bbd5",
"zh:5e6a4b39f3171f53292ab88058a59e64825f2b842760a4869e64dc1dc093d1fe",
"zh:810547d0bf9311d21c81cc306126d3547e7bd3f194fc295836acf164b9f8424e",
"zh:824a5f3617624243bed0259d7dd37d76017097dc3193dac669be342b90b2ab48",
"zh:9361ccc7048be5dcbc2fafe2d8216939765b3160bd52734f7a9fd917a39ecbd8",
"zh:aa02ea625aaf672e649296bce7580f62d724268189fe9ad7c1b36bb0fa12fa60",
"zh:c71b4cd40d6ec7815dfeefd57d88bc592c0c42f5e5858dcc88245d371b4b8b1e",
"zh:dabcd52f36b43d250a3d71ad7abfa07b5622c69068d989e60b79b2bb4f220316",
"zh:f569b65999264a9416862bca5cd2a6177d94ccb0424f3a4ef424428912b9cb3c",
]
}

View File

@ -0,0 +1,67 @@
resource "null_resource" "ensure_yq" {
provisioner "local-exec" {
command = <<EOT
../common/install-yq.sh
EOT
}
triggers = {
always_run = timestamp()
}
}
module "fetch_image" {
source = "../common/fetch-image"
csp = "azure"
attestation_variant = "azure-sev-snp"
image = var.image
depends_on = [null_resource.ensure_yq]
}
module "azure_iam" {
source = "../infrastructure/iam/azure"
region = var.location
service_principal_name = var.service_principal_name
resource_group_name = var.resource_group_name
}
module "azure" {
source = "../infrastructure/azure"
name = var.name
user_assigned_identity = module.azure_iam.uami_id
node_groups = var.node_groups
location = var.location
image_id = module.fetch_image.image
debug = var.debug
resource_group = module.azure_iam.base_resource_group
create_maa = var.create_maa
}
module "constellation" {
source = "../constellation-cluster"
csp = "azure"
debug = var.debug
name = var.name
image = var.image
microservice_version = var.microservice_version
kubernetes_version = var.kubernetes_version
uid = module.azure.uid
clusterEndpoint = module.azure.out_of_cluster_endpoint
inClusterEndpoint = module.azure.in_cluster_endpoint
initSecretHash = module.azure.initSecret
ipCidrNode = module.azure.ip_cidr_nodes
apiServerCertSANs = module.azure.api_server_cert_sans
node_groups = var.node_groups
azure_config = {
subscription = module.azure_iam.subscription_id
tenant = module.azure_iam.tenant_id
location = var.location
resourceGroup = module.azure.resource_group
userAssignedIdentity = module.azure_iam.uami_id
deployCSIDriver = var.deploy_csi_driver
secureBoot = var.secure_boot
maaURL = module.azure.attestationURL
networkSecurityGroupName = module.azure.network_security_group_name
loadBalancerName = module.azure.loadbalancer_name
}
depends_on = [null_resource.ensure_yq]
}

View File

@ -0,0 +1,88 @@
variable "name" {
type = string
description = "Name of the Constellation cluster."
}
variable "image" {
type = string
description = "Node image reference or semantical release version."
}
variable "microservice_version" {
type = string
description = "Microservice version. When not set, the latest default version will be used."
default = ""
}
variable "kubernetes_version" {
type = string
description = "Kubernetes version. When not set, the latest default version will be used."
default = ""
}
variable "debug" {
type = bool
default = false
description = "DON'T USE IN PRODUCTION: Enable debug mode and allow the use of debug images."
}
variable "custom_endpoint" {
type = string
default = ""
description = "Custom endpoint (DNS Name) to use for the Constellation API server. If not set, the default endpoint will be used."
}
variable "internal_load_balancer" {
type = bool
default = false
description = "Use an internal load balancer."
}
variable "node_groups" {
type = map(object({
role = string
initial_count = optional(number)
instance_type = string
disk_size = number
disk_type = string
zones = optional(list(string))
}))
description = "A map of node group names to node group configurations."
validation {
condition = can([for group in var.node_groups : group.role == "control-plane" || group.role == "worker"])
error_message = "The role has to be 'control-plane' or 'worker'."
}
}
variable "service_principal_name" {
type = string
description = "Name of the service principal used to create the cluster."
}
variable "resource_group_name" {
type = string
description = "Name of the resource group the cluster's resources are created in."
}
variable "location" {
type = string
description = "Azure datacenter region the cluster will be deployed in."
}
variable "deploy_csi_driver" {
type = bool
default = true
description = "Deploy the Azure Disk CSI driver with on-node encryption into the cluster."
}
variable "secure_boot" {
type = bool
default = false
description = "Enable secure boot for VMs. If enabled, the OS image has to include a virtual machine guest state (VMGS) blob."
}
variable "create_maa" {
type = bool
default = true
description = "Create an MAA for attestation."
}

View File

@ -5,9 +5,16 @@ locals {
fetch_image_command = <<EOT fetch_image_command = <<EOT
curl -s https://cdn.confidential.cloud/constellation/v2/${local.image_ref}/image/info.json | \ curl -s https://cdn.confidential.cloud/constellation/v2/${local.image_ref}/image/info.json | \
./yq eval '.list[] | select(.csp == "${var.csp}" and .attestationVariant == "${var.attestation_variant}"${local.region_filter}) | .reference' - | tr -d '\n' > "image.txt" ./yq eval '.list[] | select(.csp == "${var.csp}" and .attestationVariant == "${var.attestation_variant}"${local.region_filter}) | .reference' - | tr -d '\n' > "image.txt"
if [ '${var.csp}' = 'azure' ]; then
sed -i 's/CommunityGalleries/communityGalleries/g' image.txt
sed -i 's/Images/images/g' image.txt
sed -i 's/Versions/versions/g' image.txt
fi
EOT EOT
} }
resource "null_resource" "fetch_image" { resource "null_resource" "fetch_image" {
provisioner "local-exec" { provisioner "local-exec" {
command = local.fetch_image_command command = local.fetch_image_command

43
terraform/common/install-yq.sh Executable file
View File

@ -0,0 +1,43 @@
#!/usr/bin/env bash
version="v4.35.2"
if [[ -f ./yq ]] && ./yq --version | grep -q "${version}"; then
echo "yq is already available and up to date."
exit 0
fi
if [[ -f ./yq ]]; then
echo "yq is already available but not at the required version. Replacing with ${version}."
rm -f yq
fi
echo "Fetching yq ${version}"
os=$(uname -s)
arch=$(uname -m)
url=""
if [[ ${os} == "Darwin" ]]; then
if [[ ${arch} == "arm64" ]]; then
url="https://github.com/mikefarah/yq/releases/download/${version}/yq_darwin_arm64"
elif [[ ${arch} == "x86_64" ]]; then
url="https://github.com/mikefarah/yq/releases/download/${version}/yq_darwin_amd64"
fi
elif [[ ${os} == "Linux" ]]; then
if [[ ${arch} == "x86_64" ]]; then
url="https://github.com/mikefarah/yq/releases/download/${version}/yq_linux_amd64"
elif [[ ${arch} == "arm64" ]]; then
url="https://github.com/mikefarah/yq/releases/download/${version}/yq_linux_arm64"
fi
fi
if [[ -z ${url} ]]; then
echo "os \"${os}\" and/or architecture \"${arch}\" is not supported."
exit 1
else
echo "Downloading yq from ${url}"
curl -o yq -L "${url}"
chmod +x ./yq
./yq --version
if ! ./yq --version | grep -q "${version}"; then # check that yq was installed correctly
echo "Version is incorrect"
exit 1
fi
fi

View File

@ -1,29 +1,32 @@
#!/usr/bin/env bash #!/usr/bin/env bash
if [[ -f ./constellation ]]; then # needed to allow using devbuilds if [[ -f ./constellation ]]; then
echo "constellation CLI is already available." echo "constellation CLI is already available."
exit 0 exit 0
fi fi
version="v2.13.0"
os=$(uname -s) os=$(uname -s)
arch=$(uname -m) arch=$(uname -m)
version=$1
url="" url=""
echo "Fetching constellation ${version}"
if [[ ${os} == "Darwin" ]]; then if [[ ${os} == "Darwin" ]]; then
if [[ ${arch} == "arm64" ]]; then if [[ ${arch} == "arm64" ]]; then
url="https://github.com/edgelesssys/constellation/releases/download/${version}/constellation-darwin-arm64" url="https://github.com/edgelesssys/constellation/releases/${version}/download/constellation-darwin-arm64"
elif [[ ${arch} == "x86_64" ]]; then elif [[ ${arch} == "x86_64" ]]; then
url="https://github.com/edgelesssys/constellation/releases/download/${version}/constellation-darwin-amd64" url="https://github.com/edgelesssys/constellation/releases/${version}/download/constellation-darwin-amd64"
fi fi
elif [[ ${os} == "Linux" ]]; then elif [[ ${os} == "Linux" ]]; then
if [[ ${arch} == "x86_64" ]]; then if [[ ${arch} == "x86_64" ]]; then
url="https://github.com/edgelesssys/constellation/releases/download/${version}/constellation-linux-amd64" url="https://github.com/edgelesssys/constellation/releases/${version}/download/constellation-linux-amd64"
elif [[ ${arch} == "arm64" ]]; then elif [[ ${arch} == "arm64" ]]; then
url="https://github.com/edgelesssys/constellation/releases/download/${version}/constellation-linux-arm64" url="https://github.com/edgelesssys/constellation/releases/${version}/download/constellation-linux-arm64"
fi fi
fi fi
echo "Fetching constellation ${version}"
if [[ -z ${url} ]]; then if [[ -z ${url} ]]; then
echo "OS \"${os}\" and/or architecture \"${arch}\" is not supported." echo "os \"${os}\" and/or architecture \"${arch}\" is not supported."
exit 1 exit 1
else else
curl -o constellation -L "${url}" curl -o constellation -L "${url}"

View File

@ -1,43 +0,0 @@
#!/usr/bin/env bash
VERSION="v4.35.2"
if [[ -f ./yq ]] && ./yq --version | grep -q "${VERSION}"; then
echo "yq is already available and up to date."
exit 0
fi
if [[ -f ./yq ]]; then
echo "yq is already available but not at the required version. Replacing with ${VERSION}."
rm -f yq
fi
echo "Fetching yq ${VERSION}"
OS=$(uname -s)
ARCH=$(uname -m)
URL=""
if [[ ${OS} == "Darwin" ]]; then
if [[ ${ARCH} == "arm64" ]]; then
URL="https://github.com/mikefarah/yq/releases/download/${VERSION}/yq_darwin_arm64"
elif [[ ${ARCH} == "x86_64" ]]; then
URL="https://github.com/mikefarah/yq/releases/download/${VERSION}/yq_darwin_amd64"
fi
elif [[ ${OS} == "Linux" ]]; then
if [[ ${ARCH} == "x86_64" ]]; then
URL="https://github.com/mikefarah/yq/releases/download/${VERSION}/yq_linux_amd64"
elif [[ ${ARCH} == "arm64" ]]; then
URL="https://github.com/mikefarah/yq/releases/download/${VERSION}/yq_linux_arm64"
fi
fi
if [[ -z ${URL} ]]; then
echo "OS \"${OS}\" and/or architecture \"${ARCH}\" is not supported."
exit 1
else
echo "Downloading yq from ${URL}"
curl -o yq -L "${URL}"
chmod +x ./yq
./yq --version
if ! ./yq --version | grep -q "${VERSION}"; then # check that yq was installed correctly
echo "Version is incorrect"
exit 1
fi
fi

View File

@ -15,7 +15,7 @@ locals {
resource "null_resource" "ensure_cli" { resource "null_resource" "ensure_cli" {
provisioner "local-exec" { provisioner "local-exec" {
command = <<EOT command = <<EOT
${path.module}/install-constellation.sh ${path.module}/install-constellation.sh ${var.constellation_version}
EOT EOT
} }
triggers = { triggers = {
@ -53,14 +53,41 @@ resource "null_resource" "aws_config" {
] ]
} }
resource "null_resource" "azure_config" {
count = var.azure_config != null ? 1 : 0
provisioner "local-exec" {
command = <<EOT
./yq eval '.provider.azure.subscription = "${var.azure_config.subscription}"' -i constellation-conf.yaml
./yq eval '.provider.azure.tenant = "${var.azure_config.tenant}"' -i constellation-conf.yaml
./yq eval '.provider.azure.location = "${var.azure_config.location}"' -i constellation-conf.yaml
./yq eval '.provider.azure.resourceGroup = "${var.azure_config.resourceGroup}"' -i constellation-conf.yaml
./yq eval '.provider.azure.userAssignedIdentity = "${var.azure_config.userAssignedIdentity}"' -i constellation-conf.yaml
./yq eval '.provider.azure.deployCSIDriver = ${var.azure_config.deployCSIDriver}' -i constellation-conf.yaml
./yq eval '.provider.azure.secureBoot = ${var.azure_config.secureBoot}' -i constellation-conf.yaml
./yq eval '.infrastructure.azure.resourceGroup = "${var.azure_config.resourceGroup}"' -i constellation-state.yaml
./yq eval '.infrastructure.azure.subscriptionID = "${var.azure_config.subscription}"' -i constellation-state.yaml
./yq eval '.infrastructure.azure.networkSecurityGroupName = "${var.azure_config.networkSecurityGroupName}"' -i constellation-state.yaml
./yq eval '.infrastructure.azure.loadBalancerName = "${var.azure_config.loadBalancerName}"' -i constellation-state.yaml
./yq eval '.infrastructure.azure.userAssignedIdentity = "${var.azure_config.userAssignedIdentity}"' -i constellation-state.yaml
if [ '${var.azure_config.maaURL}' != '' ]; then
./yq eval '.infrastructure.azure.attestationURL = "${var.azure_config.maaURL}"' -i constellation-state.yaml
./constellation maa-patch ${var.azure_config.maaURL}
fi
EOT
}
triggers = {
always_run = timestamp()
}
depends_on = [
terraform_data.config_generate
]
}
resource "null_resource" "service_account_file" { resource "null_resource" "service_account_file" {
count = var.gcp_config != null ? 1 : 0 count = var.gcp_config != null ? 1 : 0
provisioner "local-exec" { provisioner "local-exec" {
command = <<EOT command = <<EOT
echo ${var.gcp_config.serviceAccountKey} | base64 -d > "${local.gcp_sa_file_path}" echo ${var.gcp_config.serviceAccountKey} | base64 -d > "${local.gcp_sa_file_path}"
EOT EOT
} }
provisioner "local-exec" { provisioner "local-exec" {
@ -81,7 +108,6 @@ resource "null_resource" "gcp_config" {
./yq eval '.provider.gcp.region = "${var.gcp_config.region}"' -i constellation-conf.yaml ./yq eval '.provider.gcp.region = "${var.gcp_config.region}"' -i constellation-conf.yaml
./yq eval '.provider.gcp.zone = "${var.gcp_config.zone}"' -i constellation-conf.yaml ./yq eval '.provider.gcp.zone = "${var.gcp_config.zone}"' -i constellation-conf.yaml
./yq eval '.provider.gcp.serviceAccountKeyPath = "${local.gcp_sa_file_path}"' -i constellation-conf.yaml ./yq eval '.provider.gcp.serviceAccountKeyPath = "${local.gcp_sa_file_path}"' -i constellation-conf.yaml
./yq eval '.infrastructure.gcp.projectID = "${var.gcp_config.project}"' -i constellation-state.yaml ./yq eval '.infrastructure.gcp.projectID = "${var.gcp_config.project}"' -i constellation-state.yaml
./yq eval '.infrastructure.gcp.ipCidrPod = "${var.gcp_config.ipCidrPod}"' -i constellation-state.yaml ./yq eval '.infrastructure.gcp.ipCidrPod = "${var.gcp_config.ipCidrPod}"' -i constellation-state.yaml
EOT EOT
@ -108,12 +134,12 @@ resource "null_resource" "config" {
./yq eval '.microserviceVersion = "${var.microservice_version}"' -i constellation-conf.yaml ./yq eval '.microserviceVersion = "${var.microservice_version}"' -i constellation-conf.yaml
fi fi
${local.yq_node_groups} ${local.yq_node_groups}
./constellation config fetch-measurements ./constellation config fetch-measurements ${var.debug == true ? "--insecure" : ""}
EOT EOT
} }
depends_on = [ depends_on = [
null_resource.aws_config, null_resource.gcp_config null_resource.aws_config, null_resource.gcp_config, null_resource.azure_config
] ]
triggers = { triggers = {

View File

@ -1,9 +1,21 @@
variable "constellation_version" {
type = string
description = "Constellation CLI version to use."
default = "@@CONSTELLATION_VERSION@@"
}
variable "image" {
type = string
description = "The node image reference or semantic release version."
default = "@@CONSTELLATION_VERSION@@"
}
variable "csp" { variable "csp" {
type = string type = string
description = "The cloud service provider to use." description = "The cloud service provider to use."
validation { validation {
condition = var.csp == "aws" || var.csp == "gcp" condition = var.csp == "aws" || var.csp == "gcp" || var.csp == "azure"
error_message = "The CSP must be one of {aws|gcp}." error_message = "The cloud service provider to use."
} }
} }
@ -14,7 +26,8 @@ variable "node_groups" {
instance_type = string instance_type = string
disk_size = number disk_size = number
disk_type = string disk_type = string
zone = string zone = optional(string, "") # For AWS, GCP
zones = optional(list(string), []) # For Azure
})) }))
description = "A map of node group names to node group configurations." description = "A map of node group names to node group configurations."
validation { validation {
@ -58,7 +71,6 @@ variable "apiServerCertSANs" {
description = "List of additional SANs (Subject Alternative Names) for the Kubernetes API server certificate." description = "List of additional SANs (Subject Alternative Names) for the Kubernetes API server certificate."
} }
variable "aws_config" { variable "aws_config" {
type = object({ type = object({
region = string region = string
@ -70,6 +82,23 @@ variable "aws_config" {
default = null default = null
} }
variable "azure_config" {
type = object({
subscription = string
tenant = string
location = string
resourceGroup = string
userAssignedIdentity = string
deployCSIDriver = bool
secureBoot = bool
maaURL = string
networkSecurityGroupName = string
loadBalancerName = string
})
description = "The cluster config for Azure."
default = null
}
variable "gcp_config" { variable "gcp_config" {
type = object({ type = object({
region = string region = string
@ -82,15 +111,6 @@ variable "gcp_config" {
default = null default = null
} }
variable "image" {
type = string
description = "The node image reference or semantical release version."
validation {
condition = length(var.image) > 0
error_message = "The image variable must not be empty."
}
}
variable "kubernetes_version" { variable "kubernetes_version" {
type = string type = string
description = "Kubernetes version." description = "Kubernetes version."
@ -100,3 +120,9 @@ variable "microservice_version" {
type = string type = string
description = "Microservice version." description = "Microservice version."
} }
variable "debug" {
type = bool
default = false
description = "DON'T USE IN PRODUCTION: Enable debug mode and allow the use of debug images."
}

View File

@ -14,7 +14,7 @@ module "gcp_iam" {
resource "null_resource" "ensure_yq" { resource "null_resource" "ensure_yq" {
provisioner "local-exec" { provisioner "local-exec" {
command = <<EOT command = <<EOT
../constellation-cluster/install-yq.sh ../common/install-yq.sh
EOT EOT
} }
triggers = { triggers = {
@ -23,7 +23,7 @@ resource "null_resource" "ensure_yq" {
} }
module "fetch_image" { module "fetch_image" {
source = "../fetch-image" source = "../common/fetch-image"
csp = "gcp" csp = "gcp"
attestation_variant = "gcp-sev-es" attestation_variant = "gcp-sev-es"
image = var.image image = var.image
@ -46,6 +46,7 @@ module "gcp" {
module "constellation" { module "constellation" {
source = "../constellation-cluster" source = "../constellation-cluster"
csp = "gcp" csp = "gcp"
debug = var.debug
name = var.name name = var.name
image = var.image image = var.image
microservice_version = var.microservice_version microservice_version = var.microservice_version

View File

@ -15,8 +15,8 @@ variable "service_account_id" {
variable "image" { variable "image" {
type = string type = string
description = "Node image reference or semantical release version." description = "Node image reference or semantic release version."
} # is required resolve the AMI }
variable "microservice_version" { variable "microservice_version" {
type = string type = string

View File

@ -10,3 +10,7 @@ output "uami_id" {
description = "Outputs the id in the format: /$ID/resourceGroups/$RG/providers/Microsoft.ManagedIdentity/userAssignedIdentities/$NAME. Not to be confused with the client_id" description = "Outputs the id in the format: /$ID/resourceGroups/$RG/providers/Microsoft.ManagedIdentity/userAssignedIdentities/$NAME. Not to be confused with the client_id"
value = azurerm_user_assigned_identity.identity_uami.id value = azurerm_user_assigned_identity.identity_uami.id
} }
output "base_resource_group" {
value = azurerm_resource_group.base_resource_group.name
}