From d827f4f757181f3400900f5ca9b62106d204a401 Mon Sep 17 00:00:00 2001 From: Svetlin Zamfirov Date: Mon, 30 Mar 2020 15:05:16 +0200 Subject: [PATCH 1/3] Adding custom shielded images support (GCP) Adds `enable_secure_boot`, `enable_vtpm` and `enable_integrity_monitoring` config options to enable building of custom Shielded GCP Compute images. Feedback on this is more than welcome as this is my first attempt in contributing to anything Packer related. Packer is great for us to build custom images on top of GCP but we would like to enhance that to support Shielded VM images. This will allow us to have more secure and trusted images which our team(s) will be using. --- builder/googlecompute/config.go | 25 ++++++++++++++++ builder/googlecompute/config.hcl2spec.go | 6 ++++ builder/googlecompute/driver.go | 3 ++ builder/googlecompute/driver_gce.go | 29 +++++++++++++++---- builder/googlecompute/image.go | 24 +++++++++++---- builder/googlecompute/step_create_instance.go | 10 +++++++ .../googlecompute/Config-not-required.mdx | 15 +++++++++- 7 files changed, 99 insertions(+), 13 deletions(-) diff --git a/builder/googlecompute/config.go b/builder/googlecompute/config.go index d47e7ece8..f47c68d9d 100644 --- a/builder/googlecompute/config.go +++ b/builder/googlecompute/config.go @@ -57,6 +57,22 @@ type Config struct { // Type of disk used to back your instance, like pd-ssd or pd-standard. // Defaults to pd-standard. DiskType string `mapstructure:"disk_type" required:"false"` + // Shielded VM offers verifiable integrity of your Compute Engine VM instances, + // so you can be confident your instances haven't been compromised by boot- + // or kernel-level malware or rootkits. Shielded VM's verifiable integrity + // is achieved through the use of: + // * Secure Boot - helps ensure that the system only runs authentic software + // by verifying the digital signature of all boot components, and halting + // the boot process if signature verification fails. + EnableSecureBoot bool `mapstructure:"enable_secure_boot" required:"false"` + // * virtual trusted platform module (vTPM)-enabled Measured Boot - A vTPM + // is a virtualized trusted platform module, which is a specialized computer + // chip you can use to protect objects, like keys and certificates, that + // you use to authenticate access to your system. + EnableVtpm bool `mapstructure:"enable_vtpm" required:"false"` + // * Integrity monitoring - Integrity monitoring helps you understand and + // make decisions about the state of your VM instances. + EnableIntegrityMonitoring bool `mapstructure:"enable_integrity_monitoring" required:"false"` // The unique name of the resulting image. Defaults to // `packer-{{timestamp}}`. ImageName string `mapstructure:"image_name" required:"false"` @@ -222,6 +238,15 @@ func (c *Config) Prepare(raws ...interface{}) ([]string, error) { c.DiskType = "pd-standard" } + // Disabling the vTPM also disables integrity monitoring, because integrity + // monitoring relies on data gathered by Measured Boot. + if !c.EnableVtpm { + if c.EnableIntegrityMonitoring { + errs = packer.MultiErrorAppend(errs, + errors.New("You cannot enable Integrity Monitoring when vTPM is disabled.")) + } + } + if c.ImageDescription == "" { c.ImageDescription = "Created by Packer" } diff --git a/builder/googlecompute/config.hcl2spec.go b/builder/googlecompute/config.hcl2spec.go index 5acbf53c8..896fde42f 100644 --- a/builder/googlecompute/config.hcl2spec.go +++ b/builder/googlecompute/config.hcl2spec.go @@ -66,6 +66,9 @@ type FlatConfig struct { DiskName *string `mapstructure:"disk_name" required:"false" cty:"disk_name"` DiskSizeGb *int64 `mapstructure:"disk_size" required:"false" cty:"disk_size"` DiskType *string `mapstructure:"disk_type" required:"false" cty:"disk_type"` + EnableSecureBoot *bool `mapstructure:"enable_secure_boot" required:"false" cty:"enable_secure_boot"` + EnableVtpm *bool `mapstructure:"enable_vtpm" required:"false" cty:"enable_vtpm"` + EnableIntegrityMonitoring *bool `mapstructure:"enable_integrity_monitoring" required:"false" cty:"enable_integrity_monitoring"` ImageName *string `mapstructure:"image_name" required:"false" cty:"image_name"` ImageDescription *string `mapstructure:"image_description" required:"false" cty:"image_description"` ImageEncryptionKey *FlatCustomerEncryptionKey `mapstructure:"image_encryption_key" required:"false" cty:"image_encryption_key"` @@ -167,6 +170,9 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { "disk_name": &hcldec.AttrSpec{Name: "disk_name", Type: cty.String, Required: false}, "disk_size": &hcldec.AttrSpec{Name: "disk_size", Type: cty.Number, Required: false}, "disk_type": &hcldec.AttrSpec{Name: "disk_type", Type: cty.String, Required: false}, + "enable_secure_boot": &hcldec.AttrSpec{Name: "enable_secure_boot", Type: cty.Bool, Required: false}, + "enable_vtpm": &hcldec.AttrSpec{Name: "enable_vtpm", Type: cty.Bool, Required: false}, + "enable_integrity_monitoring": &hcldec.AttrSpec{Name: "enable_integrity_monitoring", Type: cty.Bool, Required: false}, "image_name": &hcldec.AttrSpec{Name: "image_name", Type: cty.String, Required: false}, "image_description": &hcldec.AttrSpec{Name: "image_description", Type: cty.String, Required: false}, "image_encryption_key": &hcldec.BlockSpec{TypeName: "image_encryption_key", Nested: hcldec.ObjectSpec((*FlatCustomerEncryptionKey)(nil).HCL2Spec())}, diff --git a/builder/googlecompute/driver.go b/builder/googlecompute/driver.go index e7d34e10d..0de581bb0 100644 --- a/builder/googlecompute/driver.go +++ b/builder/googlecompute/driver.go @@ -72,6 +72,9 @@ type InstanceConfig struct { DisableDefaultServiceAccount bool DiskSizeGb int64 DiskType string + EnableSecureBoot bool + EnableVtpm bool + EnableIntegrityMonitoring bool Image *Image Labels map[string]string MachineType string diff --git a/builder/googlecompute/driver_gce.go b/builder/googlecompute/driver_gce.go index ea68b6d65..f98f964c4 100644 --- a/builder/googlecompute/driver_gce.go +++ b/builder/googlecompute/driver_gce.go @@ -271,11 +271,12 @@ func (d *driverGCE) GetImageFromProject(project, name string, fromFamily bool) ( return nil, fmt.Errorf("Image, %s, could not be found in project: %s", name, project) } else { return &Image{ - Licenses: image.Licenses, - Name: image.Name, - ProjectId: project, - SelfLink: image.SelfLink, - SizeGb: image.DiskSizeGb, + GuestOsFeatures: image.GuestOsFeatures, + Licenses: image.Licenses, + Name: image.Name, + ProjectId: project, + SelfLink: image.SelfLink, + SizeGb: image.DiskSizeGb, }, nil } } @@ -466,7 +467,23 @@ func (d *driverGCE) RunInstance(c *InstanceConfig) (<-chan error, error) { }, } - d.ui.Message("Requesting instance creation...") + // Shielded VMs configuration. If the user has set at least one of the + // options, the shielded VM configuration will reflect that. If they + // don't set any of the options the settings will default to the ones + // of the source compute image which is used for creating the virtual + // machine. + shieldedInstanceConfig := &compute.ShieldedInstanceConfig{ + EnableSecureBoot: c.EnableSecureBoot, + EnableVtpm: c.EnableVtpm, + EnableIntegrityMonitoring: c.EnableIntegrityMonitoring, + } + shieldedUiMessage := "" + if c.EnableSecureBoot || c.EnableVtpm || c.EnableIntegrityMonitoring { + instance.ShieldedInstanceConfig = shieldedInstanceConfig + shieldedUiMessage = " Shielded VM" + } + + d.ui.Message(fmt.Sprintf("Requesting%s instance creation...", shieldedUiMessage)) op, err := d.service.Instances.Insert(d.projectId, zone.Name, &instance).Do() if err != nil { return nil, err diff --git a/builder/googlecompute/image.go b/builder/googlecompute/image.go index fae69db75..46fac69b8 100644 --- a/builder/googlecompute/image.go +++ b/builder/googlecompute/image.go @@ -2,15 +2,18 @@ package googlecompute import ( "strings" + + compute "google.golang.org/api/compute/v1" ) type Image struct { - Labels map[string]string - Licenses []string - Name string - ProjectId string - SelfLink string - SizeGb int64 + GuestOsFeatures []*compute.GuestOsFeature + Labels map[string]string + Licenses []string + Name string + ProjectId string + SelfLink string + SizeGb int64 } func (i *Image) IsWindows() bool { @@ -21,3 +24,12 @@ func (i *Image) IsWindows() bool { } return false } + +func (i *Image) IsSecureBootCompatible() bool { + for _, osFeature := range i.GuestOsFeatures { + if osFeature.Type == "SECURE_BOOT" { + return true + } + } + return false +} diff --git a/builder/googlecompute/step_create_instance.go b/builder/googlecompute/step_create_instance.go index 9421a2420..80733beb2 100644 --- a/builder/googlecompute/step_create_instance.go +++ b/builder/googlecompute/step_create_instance.go @@ -107,6 +107,13 @@ func (s *StepCreateInstance) Run(ctx context.Context, state multistep.StateBag) return multistep.ActionHalt } + if c.EnableSecureBoot && !sourceImage.IsSecureBootCompatible() { + err := fmt.Errorf("Image: %s is not secure boot compatible. Please set 'enable_secure_boot' to false or choose another source image.", sourceImage.Name) + state.Put("error", err) + ui.Error(err.Error()) + return multistep.ActionHalt + } + ui.Say(fmt.Sprintf("Using image: %s", sourceImage.Name)) if sourceImage.IsWindows() && c.Comm.Type == "winrm" && c.Comm.WinRMPassword == "" { @@ -133,6 +140,9 @@ func (s *StepCreateInstance) Run(ctx context.Context, state multistep.StateBag) DisableDefaultServiceAccount: c.DisableDefaultServiceAccount, DiskSizeGb: c.DiskSizeGb, DiskType: c.DiskType, + EnableSecureBoot: c.EnableSecureBoot, + EnableVtpm: c.EnableVtpm, + EnableIntegrityMonitoring: c.EnableIntegrityMonitoring, Image: sourceImage, Labels: c.Labels, MachineType: c.MachineType, diff --git a/website/pages/partials/builder/googlecompute/Config-not-required.mdx b/website/pages/partials/builder/googlecompute/Config-not-required.mdx index 4dcd04533..996d9dd19 100644 --- a/website/pages/partials/builder/googlecompute/Config-not-required.mdx +++ b/website/pages/partials/builder/googlecompute/Config-not-required.mdx @@ -25,6 +25,19 @@ - `disk_type` (string) - Type of disk used to back your instance, like pd-ssd or pd-standard. Defaults to pd-standard. +- `enable_secure_boot` (bool) - Enable Secure Boot on a Shielded VM instance. Secure Boot helps ensure that the system + only runs authentic software by verifying the digital signature of all boot components, and halting the boot process + if signature verification fails. + [Details](https://cloud.google.com/security/shielded-cloud/shielded-vm#secure-boot) + +- `enable_vtpm` (bool) - Enable virtualized trusted platform module on a Shielded VM instance. + [Details](https://cloud.google.com/security/shielded-cloud/shielded-vm#vtpm) + +- `enable_integrity_monitoring` (bool) - Enable integrity monitoring on a Shielded VM instance. Integrity + monitoring helps you understand and make decisions about the state of your VM instances. Note: requires + enable_vtpm to be set to true. + [Details](https://cloud.google.com/security/shielded-cloud/shielded-vm#integrity-monitoring) + - `image_name` (string) - The unique name of the resulting image. Defaults to `packer-{{timestamp}}`. @@ -137,4 +150,4 @@ instance. For more information, see the Vault docs: https://www.vaultproject.io/docs/commands/#environment-variables Example:`"vault_gcp_oauth_engine": "gcp/token/my-project-editor",` - \ No newline at end of file + From 31f0b0201aafb6f6da520001c7d8daf818884474 Mon Sep 17 00:00:00 2001 From: Svetlin Zamfirov Date: Mon, 30 Mar 2020 15:42:58 +0200 Subject: [PATCH 2/3] Remove changes from auto-generated code --- .../builder/googlecompute/Config-not-required.mdx | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/website/pages/partials/builder/googlecompute/Config-not-required.mdx b/website/pages/partials/builder/googlecompute/Config-not-required.mdx index 996d9dd19..4dcd04533 100644 --- a/website/pages/partials/builder/googlecompute/Config-not-required.mdx +++ b/website/pages/partials/builder/googlecompute/Config-not-required.mdx @@ -25,19 +25,6 @@ - `disk_type` (string) - Type of disk used to back your instance, like pd-ssd or pd-standard. Defaults to pd-standard. -- `enable_secure_boot` (bool) - Enable Secure Boot on a Shielded VM instance. Secure Boot helps ensure that the system - only runs authentic software by verifying the digital signature of all boot components, and halting the boot process - if signature verification fails. - [Details](https://cloud.google.com/security/shielded-cloud/shielded-vm#secure-boot) - -- `enable_vtpm` (bool) - Enable virtualized trusted platform module on a Shielded VM instance. - [Details](https://cloud.google.com/security/shielded-cloud/shielded-vm#vtpm) - -- `enable_integrity_monitoring` (bool) - Enable integrity monitoring on a Shielded VM instance. Integrity - monitoring helps you understand and make decisions about the state of your VM instances. Note: requires - enable_vtpm to be set to true. - [Details](https://cloud.google.com/security/shielded-cloud/shielded-vm#integrity-monitoring) - - `image_name` (string) - The unique name of the resulting image. Defaults to `packer-{{timestamp}}`. @@ -150,4 +137,4 @@ instance. For more information, see the Vault docs: https://www.vaultproject.io/docs/commands/#environment-variables Example:`"vault_gcp_oauth_engine": "gcp/token/my-project-editor",` - + \ No newline at end of file From b706f02408be8306bcc59a57da19e075a59f81d1 Mon Sep 17 00:00:00 2001 From: Svetlin Zamfirov Date: Mon, 30 Mar 2020 16:10:51 +0200 Subject: [PATCH 3/3] Fixing auto-generated code errors --- builder/googlecompute/config.go | 25 +++++++++---------- .../googlecompute/Config-not-required.mdx | 15 +++++++++++ 2 files changed, 27 insertions(+), 13 deletions(-) diff --git a/builder/googlecompute/config.go b/builder/googlecompute/config.go index f47c68d9d..abe8e4531 100644 --- a/builder/googlecompute/config.go +++ b/builder/googlecompute/config.go @@ -57,21 +57,20 @@ type Config struct { // Type of disk used to back your instance, like pd-ssd or pd-standard. // Defaults to pd-standard. DiskType string `mapstructure:"disk_type" required:"false"` - // Shielded VM offers verifiable integrity of your Compute Engine VM instances, - // so you can be confident your instances haven't been compromised by boot- - // or kernel-level malware or rootkits. Shielded VM's verifiable integrity - // is achieved through the use of: - // * Secure Boot - helps ensure that the system only runs authentic software - // by verifying the digital signature of all boot components, and halting - // the boot process if signature verification fails. + // Create a Shielded VM image with Secure Boot enabled. It helps ensure that + // the system only runs authentic software by verifying the digital signature + // of all boot components, and halting the boot process if signature verification + // fails. [Details](https://cloud.google.com/security/shielded-cloud/shielded-vm) EnableSecureBoot bool `mapstructure:"enable_secure_boot" required:"false"` - // * virtual trusted platform module (vTPM)-enabled Measured Boot - A vTPM - // is a virtualized trusted platform module, which is a specialized computer - // chip you can use to protect objects, like keys and certificates, that - // you use to authenticate access to your system. + // Create a Shielded VM image with virtual trusted platform module + // Measured Boot enabled. A vTPM is a virtualized trusted platform module, + // which is a specialized computer chip you can use to protect objects, + // like keys and certificates, that you use to authenticate access to your + // system. [Details](https://cloud.google.com/security/shielded-cloud/shielded-vm) EnableVtpm bool `mapstructure:"enable_vtpm" required:"false"` - // * Integrity monitoring - Integrity monitoring helps you understand and - // make decisions about the state of your VM instances. + // Integrity monitoring helps you understand and make decisions about the + // state of your VM instances. Note: integrity monitoring relies on having + // vTPM enabled. [Details](https://cloud.google.com/security/shielded-cloud/shielded-vm) EnableIntegrityMonitoring bool `mapstructure:"enable_integrity_monitoring" required:"false"` // The unique name of the resulting image. Defaults to // `packer-{{timestamp}}`. diff --git a/website/pages/partials/builder/googlecompute/Config-not-required.mdx b/website/pages/partials/builder/googlecompute/Config-not-required.mdx index 4dcd04533..6a7204c1d 100644 --- a/website/pages/partials/builder/googlecompute/Config-not-required.mdx +++ b/website/pages/partials/builder/googlecompute/Config-not-required.mdx @@ -25,6 +25,21 @@ - `disk_type` (string) - Type of disk used to back your instance, like pd-ssd or pd-standard. Defaults to pd-standard. +- `enable_secure_boot` (bool) - Create a Shielded VM image with Secure Boot enabled. It helps ensure that + the system only runs authentic software by verifying the digital signature + of all boot components, and halting the boot process if signature verification + fails. [Details](https://cloud.google.com/security/shielded-cloud/shielded-vm) + +- `enable_vtpm` (bool) - Create a Shielded VM image with virtual trusted platform module + Measured Boot enabled. A vTPM is a virtualized trusted platform module, + which is a specialized computer chip you can use to protect objects, + like keys and certificates, that you use to authenticate access to your + system. [Details](https://cloud.google.com/security/shielded-cloud/shielded-vm) + +- `enable_integrity_monitoring` (bool) - Integrity monitoring helps you understand and make decisions about the + state of your VM instances. Note: integrity monitoring relies on having + vTPM enabled. [Details](https://cloud.google.com/security/shielded-cloud/shielded-vm) + - `image_name` (string) - The unique name of the resulting image. Defaults to `packer-{{timestamp}}`.