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
if: inputs.cloudProvider == 'azure'
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
shell: bash

View File

@ -1,5 +1,10 @@
name: Upload Terraform module
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:
using: "composite"
steps:
@ -9,15 +14,23 @@ runs:
cp -r terraform terraform-module
find terraform-module -name "*.go" -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
shell: bash
run: |
zip -r terraform-module.zip terraform-module
- name: Upload artifact
uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3
with:
name: terraform-module
path: terraform-module.zip
- name: Cleanup Terraform module dir
shell: bash
run: |

View File

@ -6,32 +6,28 @@ on:
ref:
type: string
description: "Git ref to checkout"
required: false
cloudProvider:
description: "Which cloud provider to use."
type: choice
options:
- "aws"
- "azure"
- "gcp"
required: true
regionZone:
description: "Region or zone to create the cluster in. Leave empty for default region/zone."
type: string
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
required: true
cliVersion:
description: "Constellation CLI version to use. Empty value means build from source."
type: string
default: ""
required: false
workflow_call:
inputs:
ref:
type: string
description: "Git ref to checkout"
required: false
cloudProvider:
description: "Which cloud provider to use."
type: string
@ -40,16 +36,14 @@ on:
description: "Which zone to use."
type: string
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
required: true
cliVersion:
description: "Constellation CLI version to use. Empty value means build from source."
type: string
default: ""
required: false
jobs:
build:
tf-module-test:
runs-on: ubuntu-22.04
permissions:
id-token: write
@ -62,16 +56,30 @@ jobs:
with:
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
with:
version: ${{ inputs.cliVersion }}
- name: Download Terraform module
uses: actions/download-artifact@v3
uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2
with:
name: 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
id: create-prefix
@ -83,29 +91,62 @@ jobs:
- name: Create AWS Terraform variable input file
if: inputs.cloudProvider == 'aws'
working-directory: ./terraform-module/aws-constellation
working-directory: ${{ github.workspace }}/terraform-module/aws-constellation
shell: bash
run: |
cat > terraform.tfvars <<EOF
name = "${{ steps.create-prefix.outputs.prefix }}"
image = "${{ inputs.image }}"
image = "${{ steps.find-latest-image.outputs.image }}"
zone = "${{ inputs.regionZone || 'us-east-2c' }}"
name_prefix = "${{ steps.create-prefix.outputs.prefix }}"
debug = ${{ steps.find-latest-image.outputs.isDebugImage }}
node_groups = {
control_plane_default = {
role = "control-plane"
zone = "${{ inputs.regionZone || 'us-east-2c' }}"
instance_type = "m6a.xlarge"
disk_size = 30
disk_type = "gp3"
initial_count = 2
zone = "${{ inputs.regionZone || 'us-east-2c' }}"
},
worker_default = {
role = "worker"
zone = "${{ inputs.regionZone || 'us-east-2c' }}"
instance_type = "m6a.xlarge"
disk_size = 30
disk_type = "gp3"
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
@ -113,36 +154,39 @@ jobs:
- name: Create GCP Terraform variable input file
if: inputs.cloudProvider == 'gcp'
working-directory: ./terraform-module/gcp-constellation
working-directory: ${{ github.workspace }}/terraform-module/gcp-constellation
shell: bash
run: |
cat > terraform.tfvars <<EOF
name = "${{ steps.create-prefix.outputs.prefix }}"
project = "${{ secrets.GCP_E2E_PROJECT }}"
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' }}"
debug = ${{ steps.find-latest-image.outputs.isDebugImage }}
node_groups = {
control_plane_default = {
role = "control-plane"
zone = "${{ inputs.regionZone || 'europe-west3-b' }}"
instance_type = "n2d-standard-4"
disk_size = 30
disk_type = "pd-ssd"
initial_count = 2
zone = "${{ inputs.regionZone || 'europe-west3-b' }}"
},
worker_default = {
role = "worker"
zone = "${{ inputs.regionZone || 'europe-west3-b' }}"
instance_type = "n2d-standard-4"
disk_size = 30
disk_type = "pd-ssd"
initial_count = 2
zone = "${{ inputs.regionZone || 'europe-west3-b' }}"
}
}
EOF
cat terraform.tfvars
- name: Install dependencies (Terraform)
shell: bash
run: |
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
@ -178,7 +222,7 @@ jobs:
shell: bash
run: |
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
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
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)
if: inputs.cloudProvider == 'gcp'
uses: ./.github/actions/login_gcp
@ -199,22 +249,25 @@ jobs:
- name: Apply Terraform Cluster
id: apply_terraform
working-directory: ./terraform-module/${{ inputs.cloudProvider }}-constellation
working-directory: ${{ github.workspace }}/terraform-module/${{ inputs.cloudProvider }}-constellation
shell: bash
run: |
cp ../../constellation .
terraform init
terraform apply -var-file=terraform.tfvars -auto-approve
- 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'
working-directory: ./terraform-module/${{ inputs.cloudProvider }}-constellation
working-directory: ${{ github.workspace }}/terraform-module/${{ inputs.cloudProvider }}-constellation
shell: bash
run: |
terraform init
terraform destroy -var-file=terraform.tfvars -auto-approve
- name: Verify cleanup
working-directory: ./terraform-module/${{ inputs.cloudProvider }}-constellation
working-directory: ${{ github.workspace }}/terraform-module/${{ inputs.cloudProvider }}-constellation
shell: bash
run: |
if [ -f constellation-mastersecret.json ] || [ -f constellation-conf.yaml ]; then
echo "Files constellation-mastersecret.json or constellation-conf.yaml still exist"

View File

@ -376,20 +376,14 @@ jobs:
max-parallel: 5
matrix:
include:
- test: "GCP"
provider: "gcp"
regionZone: "europe-west2-a"
- test: "AWS"
provider: "aws"
regionZone: "us-east-2c"
- provider: "gcp"
- provider: "aws"
- provider: "azure"
permissions:
id-token: write
contents: read
packages: write
needs: [find-latest-image]
secrets: inherit
uses: ./.github/workflows/e2e-test-tf-module.yml
with:
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
contents: read
outputs:
image: ${{ steps.find-latest-image.outputs.output }}${{ steps.check-input.outputs.image }}
isDebugImage: ${{ steps.isDebugImage.outputs.isDebugImage }}
image: ${{ steps.find-latest-image.outputs.image }}
isDebugImage: ${{ steps.find-latest-image.outputs.isDebugImage }}
steps:
- name: Check input
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
- name: Get Latest Image
id: find-latest-image
if: inputs.imageVersion == ''
uses: ./.github/actions/versionsapi
uses: ./.github/actions/find_latest_image
with:
command: latest
git-ref: ${{ inputs.git-ref }}
imageVersion: ${{ inputs.imageVersion }}
ref: main
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:
runs-on: ${{ inputs.runner }}
permissions:

View File

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

View File

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

View File

@ -26,6 +26,7 @@ go_library(
"iamupgradeapply.go",
"init.go",
"log.go",
"maapatch.go",
"mini.go",
"minidown.go",
"miniup.go",
@ -78,6 +79,7 @@ go_library(
"//internal/kms/uri",
"//internal/license",
"//internal/logger",
"//internal/maa",
"//internal/retry",
"//internal/semver",
"//internal/sigstore",
@ -129,6 +131,7 @@ go_test(
"iamdestroy_test.go",
"iamupgradeapply_test.go",
"init_test.go",
"maapatch_test.go",
"recover_test.go",
"spinner_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}
```
### Update CLI version for Terraform module
Update the `version` inside `terraform/constellation-cluster/install-constellation.sh` to the new release.
### Patch release
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
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
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
git clone --branch <VERSION> https://github.com/edgelesssys/constellation
cd constellation/hack/maa-patch
go run . $(terraform output attestationURL | jq -r)
constellation maa-patch <URL>
```
:::
<!-- 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.
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{}
body := &bytes.Buffer{}
for _, c := range allSubCommands(rootCmd) {
if c.Hidden {
continue
}
name := c.Name()
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" {
provisioner "local-exec" {
command = <<EOT
../constellation-cluster/install-yq.sh
../common/install-yq.sh
EOT
}
triggers = {
@ -21,7 +21,7 @@ resource "null_resource" "ensure_yq" {
}
module "fetch_image" {
source = "../fetch-image"
source = "../common/fetch-image"
csp = "aws"
attestation_variant = var.enable_snp ? "aws-sev-snp" : "aws-nitro-tpm"
region = local.region
@ -47,6 +47,7 @@ module "aws" {
module "constellation" {
source = "../constellation-cluster"
csp = "aws"
debug = var.debug
name = var.name
image = var.image
microservice_version = var.microservice_version

View File

@ -5,7 +5,7 @@ variable "name" {
variable "image" {
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" {

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
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"
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
}
resource "null_resource" "fetch_image" {
provisioner "local-exec" {
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
if [[ -f ./constellation ]]; then # needed to allow using devbuilds
if [[ -f ./constellation ]]; then
echo "constellation CLI is already available."
exit 0
fi
version="v2.13.0"
os=$(uname -s)
arch=$(uname -m)
version=$1
url=""
echo "Fetching constellation ${version}"
if [[ ${os} == "Darwin" ]]; 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
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
elif [[ ${os} == "Linux" ]]; 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
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
echo "Fetching constellation ${version}"
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
else
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" {
provisioner "local-exec" {
command = <<EOT
${path.module}/install-constellation.sh
${path.module}/install-constellation.sh ${var.constellation_version}
EOT
}
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" {
count = var.gcp_config != null ? 1 : 0
provisioner "local-exec" {
command = <<EOT
echo ${var.gcp_config.serviceAccountKey} | base64 -d > "${local.gcp_sa_file_path}"
EOT
}
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.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 '.infrastructure.gcp.projectID = "${var.gcp_config.project}"' -i constellation-state.yaml
./yq eval '.infrastructure.gcp.ipCidrPod = "${var.gcp_config.ipCidrPod}"' -i constellation-state.yaml
EOT
@ -99,21 +125,21 @@ resource "null_resource" "config" {
command = <<EOT
./yq eval '.name = "${var.name}"' -i constellation-conf.yaml
if [ "${var.image}" != "" ]; then
./yq eval '.image = "${var.image}"' -i constellation-conf.yaml
./yq eval '.image = "${var.image}"' -i constellation-conf.yaml
fi
if [ "${var.kubernetes_version}" != "" ]; then
./yq eval '.kubernetesVersion = "${var.kubernetes_version}"' -i constellation-conf.yaml
./yq eval '.kubernetesVersion = "${var.kubernetes_version}"' -i constellation-conf.yaml
fi
if [ "${var.microservice_version}" != "" ]; then
./yq eval '.microserviceVersion = "${var.microservice_version}"' -i constellation-conf.yaml
./yq eval '.microserviceVersion = "${var.microservice_version}"' -i constellation-conf.yaml
fi
${local.yq_node_groups}
./constellation config fetch-measurements
./constellation config fetch-measurements ${var.debug == true ? "--insecure" : ""}
EOT
}
depends_on = [
null_resource.aws_config, null_resource.gcp_config
null_resource.aws_config, null_resource.gcp_config, null_resource.azure_config
]
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" {
type = string
description = "The cloud service provider to use."
validation {
condition = var.csp == "aws" || var.csp == "gcp"
error_message = "The CSP must be one of {aws|gcp}."
condition = var.csp == "aws" || var.csp == "gcp" || var.csp == "azure"
error_message = "The cloud service provider to use."
}
}
@ -14,7 +26,8 @@ variable "node_groups" {
instance_type = string
disk_size = number
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."
validation {
@ -58,7 +71,6 @@ variable "apiServerCertSANs" {
description = "List of additional SANs (Subject Alternative Names) for the Kubernetes API server certificate."
}
variable "aws_config" {
type = object({
region = string
@ -70,6 +82,23 @@ variable "aws_config" {
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" {
type = object({
region = string
@ -82,15 +111,6 @@ variable "gcp_config" {
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" {
type = string
description = "Kubernetes version."
@ -100,3 +120,9 @@ variable "microservice_version" {
type = string
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" {
provisioner "local-exec" {
command = <<EOT
../constellation-cluster/install-yq.sh
../common/install-yq.sh
EOT
}
triggers = {
@ -23,7 +23,7 @@ resource "null_resource" "ensure_yq" {
}
module "fetch_image" {
source = "../fetch-image"
source = "../common/fetch-image"
csp = "gcp"
attestation_variant = "gcp-sev-es"
image = var.image
@ -46,6 +46,7 @@ module "gcp" {
module "constellation" {
source = "../constellation-cluster"
csp = "gcp"
debug = var.debug
name = var.name
image = var.image
microservice_version = var.microservice_version

View File

@ -15,8 +15,8 @@ variable "service_account_id" {
variable "image" {
type = string
description = "Node image reference or semantical release version."
} # is required resolve the AMI
description = "Node image reference or semantic release version."
}
variable "microservice_version" {
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"
value = azurerm_user_assigned_identity.identity_uami.id
}
output "base_resource_group" {
value = azurerm_resource_group.base_resource_group.name
}