diff --git a/builder/googlecompute/config.go b/builder/googlecompute/config.go index d47e7ece8..abe8e4531 100644 --- a/builder/googlecompute/config.go +++ b/builder/googlecompute/config.go @@ -57,6 +57,21 @@ 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"` + // 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"` + // 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 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}}`. ImageName string `mapstructure:"image_name" required:"false"` @@ -222,6 +237,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..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}}`.