From e9e5a59251534857f25998acfe6b12632295cef0 Mon Sep 17 00:00:00 2001 From: sternik <147771+sternik@users.noreply.github.com> Date: Mon, 15 Jul 2024 15:48:29 +0200 Subject: [PATCH] feat: add support for Secure Boot UEFI keys in Azure images (#70) --- docs/index.md | 2 +- docs/resources/template.md | 33 ++++++++++++ examples/provider/provider.tf | 2 +- .../imagefactory_template/resource.tf | 24 +++++++++ imagefactory/imagetemplate/schema.go | 18 +++++++ imagefactory/imagetemplate/structures.go | 16 ++++++ pkg/graphql/graphql.go | 50 ++++++++++++++----- pkg/graphql/schema.graphql | 36 ++++++++++++- 8 files changed, 166 insertions(+), 15 deletions(-) diff --git a/docs/index.md b/docs/index.md index 569aefa..acb858b 100644 --- a/docs/index.md +++ b/docs/index.md @@ -34,7 +34,7 @@ terraform { required_providers { imagefactory = { source = "nordcloud/imagefactory" - version = "1.9.1" + version = "1.10.0" } } } diff --git a/docs/resources/template.md b/docs/resources/template.md index ad12247..e715526 100644 --- a/docs/resources/template.md +++ b/docs/resources/template.md @@ -144,6 +144,30 @@ resource "imagefactory_template" "azure_template" { } } +# AZURE Template - additional signatures on Gen2 images + +resource "imagefactory_variable" "uefi_key" { + name = "UEFI_KEY" + value = "MIIDQTCCAimgAwIBAgIQDd70KTXzSXuUqRAfm+RzqzANBgkqhkiG9w0BAQsFADAj...." +} + +resource "imagefactory_template" "azure_template" { + name = "Ubuntu1804" + description = "Ubuntu 18.04 on Azure" + cloud_provider = "AZURE" + distribution_id = data.imagefactory_distribution.ubuntu18.id + config { + azure { + trusted_launch = true + additional_signatures { + variable_name = imagefactory_variable.uefi_key.name + # If the variable is not defined in the template, the value can be set directly + # variable_value = "UEFI_KEY" + } + } + } +} + output "azure_template" { value = imagefactory_template.azure_template } @@ -308,6 +332,7 @@ Required: Optional: - `additional_data_disks` (Block List, Max: 10) (see [below for nested schema](#nestedblock--config--azure--additional_data_disks)) +- `additional_signatures` (Block List) Additional UEFI keys that are used to validate the boot loader. This feature allows you to bind UEFI keys for driver/kernel modules that are signed by using a private key that's owned by third-party vendors. (see [below for nested schema](#nestedblock--config--azure--additional_signatures)) - `eol_date_option` (Boolean) Default value is set to true - `exclude_from_latest` (Boolean) - `replica_regions` (List of String) @@ -322,6 +347,14 @@ Required: - `size` (Number) Data disk size between 1 and 10 GB. + +### Nested Schema for `config.azure.additional_signatures` + +Required: + +- `variable_name` (String) The name of the Customer Variable that is used to store the UEFI key. + + ### Nested Schema for `config.azure.vm_image_definition` diff --git a/examples/provider/provider.tf b/examples/provider/provider.tf index 417b591..ed04e64 100644 --- a/examples/provider/provider.tf +++ b/examples/provider/provider.tf @@ -7,7 +7,7 @@ terraform { required_providers { imagefactory = { source = "nordcloud/imagefactory" - version = "1.9.1" + version = "1.10.0" } } } diff --git a/examples/resources/imagefactory_template/resource.tf b/examples/resources/imagefactory_template/resource.tf index 0df6f2a..bed48f5 100644 --- a/examples/resources/imagefactory_template/resource.tf +++ b/examples/resources/imagefactory_template/resource.tf @@ -129,6 +129,30 @@ resource "imagefactory_template" "azure_template" { } } +# AZURE Template - additional signatures on Gen2 images + +resource "imagefactory_variable" "uefi_key" { + name = "UEFI_KEY" + value = "MIIDQTCCAimgAwIBAgIQDd70KTXzSXuUqRAfm+RzqzANBgkqhkiG9w0BAQsFADAj...." +} + +resource "imagefactory_template" "azure_template" { + name = "Ubuntu1804" + description = "Ubuntu 18.04 on Azure" + cloud_provider = "AZURE" + distribution_id = data.imagefactory_distribution.ubuntu18.id + config { + azure { + trusted_launch = true + additional_signatures { + variable_name = imagefactory_variable.uefi_key.name + # If the variable is not defined in the template, the value can be set directly + # variable_value = "UEFI_KEY" + } + } + } +} + output "azure_template" { value = imagefactory_template.azure_template } diff --git a/imagefactory/imagetemplate/schema.go b/imagefactory/imagetemplate/schema.go index f698e40..210a0bd 100644 --- a/imagefactory/imagetemplate/schema.go +++ b/imagefactory/imagetemplate/schema.go @@ -128,6 +128,16 @@ var additionalDataDisksResource = &schema.Resource{ }, } +var additionalSignaturesResource = &schema.Resource{ + Schema: map[string]*schema.Schema{ + "variable_name": { + Type: schema.TypeString, + Required: true, + Description: "The name of the Customer Variable that is used to store the UEFI key.", + }, + }, +} + var azureTemplateConfigResource = &schema.Resource{ Schema: map[string]*schema.Schema{ "exclude_from_latest": { @@ -162,6 +172,14 @@ var azureTemplateConfigResource = &schema.Resource{ Type: schema.TypeBool, Optional: true, }, + "additional_signatures": { + Type: schema.TypeList, + Optional: true, + Elem: additionalSignaturesResource, + Description: "Additional UEFI keys that are used to validate the boot loader. " + + "This feature allows you to bind UEFI keys for driver/kernel modules that " + + "are signed by using a private key that's owned by third-party vendors.", + }, }, } diff --git a/imagefactory/imagetemplate/structures.go b/imagefactory/imagetemplate/structures.go index 8fc3bcc..88e2727 100644 --- a/imagefactory/imagetemplate/structures.go +++ b/imagefactory/imagetemplate/structures.go @@ -121,6 +121,18 @@ func expandAdditionalDataDisks(in []interface{}) *[]graphql.NewAdditionalDataDis return &out } +func expandAdditionalSignatures(in []interface{}) *[]graphql.NewUefiKey { + out := []graphql.NewUefiKey{} + for i := range in { + m := in[i].(map[string]interface{}) + out = append(out, graphql.NewUefiKey{ + VariableName: graphql.String(m["variable_name"].(string)), + }) + } + + return &out +} + func expandTemplateAzureConfig(in []interface{}) *graphql.NewTemplateAZUREConfig { if len(in) == 0 { return nil @@ -149,6 +161,10 @@ func expandTemplateAzureConfig(in []interface{}) *graphql.NewTemplateAZUREConfig out.AdditionalDataDisks = expandAdditionalDataDisks(m["additional_data_disks"].([]interface{})) } + if m["additional_signatures"] != nil { + out.AdditionalSignatures = expandAdditionalSignatures(m["additional_signatures"].([]interface{})) + } + return out } diff --git a/pkg/graphql/graphql.go b/pkg/graphql/graphql.go index bbd231e..80f6d6c 100644 --- a/pkg/graphql/graphql.go +++ b/pkg/graphql/graphql.go @@ -3083,6 +3083,13 @@ const ( DistributionAttributePROVIDER DistributionAttribute = "PROVIDER" ) +type HyperVGeneration string + +const ( + HyperVGenerationHYPERVGENERATIONV1 HyperVGeneration = "HYPERV_GENERATION_V1" + HyperVGenerationHYPERVGENERATIONV2 HyperVGeneration = "HYPERV_GENERATION_V2" +) + type ImageAttribute string const ( @@ -3676,12 +3683,13 @@ type NewTemplateAWSConfig struct { } type NewTemplateAZUREConfig struct { - AdditionalDataDisks *[]NewAdditionalDataDisks `json:"additionalDataDisks,omitempty"` - EolDateOption *Boolean `json:"eolDateOption,omitempty"` - ExcludeFromLatest *Boolean `json:"excludeFromLatest,omitempty"` - ReplicaRegions *[]String `json:"replicaRegions,omitempty"` - TrustedLaunch *Boolean `json:"trustedLaunch,omitempty"` - VmImageDefinition *NewVMImageDefinition `json:"vmImageDefinition,omitempty"` + AdditionalDataDisks *[]NewAdditionalDataDisks `json:"additionalDataDisks,omitempty"` + AdditionalSignatures *[]NewUefiKey `json:"additionalSignatures,omitempty"` + EolDateOption *Boolean `json:"eolDateOption,omitempty"` + ExcludeFromLatest *Boolean `json:"excludeFromLatest,omitempty"` + ReplicaRegions *[]String `json:"replicaRegions,omitempty"` + TrustedLaunch *Boolean `json:"trustedLaunch,omitempty"` + VmImageDefinition *NewVMImageDefinition `json:"vmImageDefinition,omitempty"` } type NewTemplateComponent struct { @@ -3713,6 +3721,10 @@ type NewTemplateExoscaleConfig struct { Zone *String `json:"zone,omitempty"` } +type NewUefiKey struct { + VariableName String `json:"variableName"` +} + type NewVMImageDefinition struct { Name String `json:"name"` Offer String `json:"offer"` @@ -3800,6 +3812,10 @@ type TemplatesSort struct { // Objects // +type AZUREConfig struct { + HypervGeneration *HyperVGeneration `json:"hypervGeneration,omitempty"` +} + type Account struct { Alias *String `json:"alias,omitempty"` ChangeDetails ChangeDetails `json:"changeDetails"` @@ -3969,6 +3985,10 @@ type ComponentResults struct { Results *[]Component `json:"results,omitempty"` } +type Config struct { + Azure *AZUREConfig `json:"azure,omitempty"` +} + type Contact struct { Email String `json:"email"` Name String `json:"name"` @@ -4012,6 +4032,7 @@ type CustomerStats struct { type Distribution struct { ComplianceScore *ComplianceScore `json:"complianceScore,omitempty"` + Config *Config `json:"config,omitempty"` CreatedAt String `json:"createdAt"` Deprecated *Boolean `json:"deprecated,omitempty"` Description *String `json:"description,omitempty"` @@ -4262,12 +4283,13 @@ type TemplateAWSConfig struct { } type TemplateAZUREConfig struct { - AdditionalDataDisks *[]AdditionalDataDisks `json:"additionalDataDisks,omitempty"` - EolDateOption *Boolean `json:"eolDateOption,omitempty"` - ExcludeFromLatest *Boolean `json:"excludeFromLatest,omitempty"` - ReplicaRegions *[]String `json:"replicaRegions,omitempty"` - TrustedLaunch *Boolean `json:"trustedLaunch,omitempty"` - VmImageDefinition *VMImageDefinition `json:"vmImageDefinition,omitempty"` + AdditionalDataDisks *[]AdditionalDataDisks `json:"additionalDataDisks,omitempty"` + AdditionalSignatures *[]UefiKey `json:"additionalSignatures,omitempty"` + EolDateOption *Boolean `json:"eolDateOption,omitempty"` + ExcludeFromLatest *Boolean `json:"excludeFromLatest,omitempty"` + ReplicaRegions *[]String `json:"replicaRegions,omitempty"` + TrustedLaunch *Boolean `json:"trustedLaunch,omitempty"` + VmImageDefinition *VMImageDefinition `json:"vmImageDefinition,omitempty"` } type TemplateComponent struct { @@ -4313,6 +4335,10 @@ type TemplateState struct { Status BuildStatus `json:"status"` } +type UefiKey struct { + VariableName String `json:"variableName"` +} + type VMImageDefinition struct { Name String `json:"name"` Offer String `json:"offer"` diff --git a/pkg/graphql/schema.graphql b/pkg/graphql/schema.graphql index c07776b..d309959 100644 --- a/pkg/graphql/schema.graphql +++ b/pkg/graphql/schema.graphql @@ -606,7 +606,7 @@ type CustomerStats { } -# Copyright 2021-2023 Nordcloud Oy or its affiliates. All Rights Reserved. +# Copyright 2021-2024 Nordcloud Oy or its affiliates. All Rights Reserved. """ Distribution defines the cloud image that can be created by the ImageFactory. @@ -631,6 +631,7 @@ type Distribution { osEolDate: String complianceScore: ComplianceScore deprecated: Boolean + config: Config } type ComplianceScore { @@ -644,6 +645,19 @@ type DistributionResults { count: Int! } +type Config { + azure: AZUREConfig +} + +enum HyperVGeneration { + HYPERV_GENERATION_V1 + HYPERV_GENERATION_V2 +} + +type AZUREConfig { + hypervGeneration: HyperVGeneration +} + # Copyright 2022-2024 Nordcloud Oy or its affiliates. All Rights Reserved. @@ -1010,6 +1024,10 @@ type AdditionalDataDisks { size: Int! } +type UefiKey { + variableName: String! +} + type TemplateAZUREConfig { replicaRegions: [String] excludeFromLatest: Boolean @@ -1017,6 +1035,7 @@ type TemplateAZUREConfig { eolDateOption: Boolean additionalDataDisks: [AdditionalDataDisks!] trustedLaunch: Boolean + additionalSignatures: [UefiKey!] } type TemplateExoscaleConfig { @@ -1150,6 +1169,13 @@ input NewVMImageDefinition { sku: String! } +input NewUefiKey { + """ + variableName is the name of the Customer Variable that is used to store the UEFI key. + """ + variableName: String! +} + input NewAdditionalDataDisks { """ size is in GB, from 1G to 10G. @@ -1172,6 +1198,14 @@ input NewTemplateAZUREConfig { """ trustedLaunch: Boolean + """ + `additionalSignatures` defines additional UEFI keys that are used to validate the boot loader. + + This feature allows you to bind unified extensible firmware interface (UEFI) keys for driver/kernel modules + that are signed by using a private key that's owned by third-party vendors. + """ + additionalSignatures: [NewUefiKey!] + """ `additionalDataDisks` defines extra data disks attached to the image with a limit of 10.