From 3eed6fd5084ba8c34174313c898563b7823e97d1 Mon Sep 17 00:00:00 2001 From: poida Date: Tue, 18 Oct 2016 23:30:38 +1100 Subject: [PATCH 1/4] Added KMS CMK support to EBS builder Added the 'kms_key_id' parameter. This supports supplying a customer master key (CMK) when encrypting the EBS volume. The parameter is optional and only takes effect when 'encrypted' is true. When 'encrypted' is true but 'kms_key_id' is missing the 'aws/ebs' key will be used. --- builder/amazon/common/ami_config.go | 1 + builder/amazon/ebs/step_encrypted_ami.go | 9 +++++++++ website/source/docs/builders/amazon-ebs.html.md | 2 ++ 3 files changed, 12 insertions(+) diff --git a/builder/amazon/common/ami_config.go b/builder/amazon/common/ami_config.go index c59653863..fa442cb5c 100644 --- a/builder/amazon/common/ami_config.go +++ b/builder/amazon/common/ami_config.go @@ -21,6 +21,7 @@ type AMIConfig struct { AMIForceDeregister bool `mapstructure:"force_deregister"` AMIForceDeleteSnapshot bool `mapstructure:"force_delete_snapshot"` AMIEncryptBootVolume bool `mapstructure:"encrypt_boot"` + AMIKmsKeyId string `mapstructure:"kms_key_id"` SnapshotTags map[string]string `mapstructure:"snapshot_tags"` } diff --git a/builder/amazon/ebs/step_encrypted_ami.go b/builder/amazon/ebs/step_encrypted_ami.go index f51b944bc..8ab84e8e1 100644 --- a/builder/amazon/ebs/step_encrypted_ami.go +++ b/builder/amazon/ebs/step_encrypted_ami.go @@ -18,9 +18,13 @@ func (s *stepCreateEncryptedAMICopy) Run(state multistep.StateBag) multistep.Ste config := state.Get("config").(Config) ec2conn := state.Get("ec2").(*ec2.EC2) ui := state.Get("ui").(packer.Ui) + kmsKeyId := config.AMIConfig.AMIKmsKeyId // Encrypt boot not set, so skip step if !config.AMIConfig.AMIEncryptBootVolume { + if kmsKeyId != "" { + ui.Say(fmt.Sprintf("Ignoring KMS Key ID: %s, encrypted=false", kmsKeyId)) + } return multistep.ActionContinue } @@ -36,11 +40,16 @@ func (s *stepCreateEncryptedAMICopy) Run(state multistep.StateBag) multistep.Ste ui.Say(fmt.Sprintf("Copying AMI: %s(%s)", region, id)) + if kmsKeyId != "" { + ui.Say(fmt.Sprintf("Encypting with KMS Key ID: %s", kmsKeyId)) + } + copyOpts := &ec2.CopyImageInput{ Name: &config.AMIName, // Try to overwrite existing AMI SourceImageId: aws.String(id), SourceRegion: aws.String(region), Encrypted: aws.Bool(true), + KmsKeyId: aws.String(kmsKeyId), } copyResp, err := ec2conn.CopyImage(copyOpts) diff --git a/website/source/docs/builders/amazon-ebs.html.md b/website/source/docs/builders/amazon-ebs.html.md index 92e366e0e..0a656c19f 100644 --- a/website/source/docs/builders/amazon-ebs.html.md +++ b/website/source/docs/builders/amazon-ebs.html.md @@ -77,6 +77,8 @@ builder. - `encrypted` (boolean) - Indicates whether to encrypt the volume or not + - `kms_key_id` (string) - The ID of the KMS key to use for volume encryption + - `iops` (integer) - The number of I/O operations per second (IOPS) that the volume supports. See the documentation on [IOPs](https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_EbsBlockDevice.html) From 7ea17e1630d195adba5b7dd03e91d7318d1eec56 Mon Sep 17 00:00:00 2001 From: poida Date: Fri, 2 Dec 2016 21:30:14 +1100 Subject: [PATCH 2/4] Allow custom encrypted AMIs to be shared When using a custom KMS key to encrypt the boot volume of an AMI, packer should allow it to be shared with other users. --- builder/amazon/common/ami_config.go | 4 ++-- builder/amazon/common/ami_config_test.go | 11 +++++++++-- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/builder/amazon/common/ami_config.go b/builder/amazon/common/ami_config.go index fa442cb5c..8914b1934 100644 --- a/builder/amazon/common/ami_config.go +++ b/builder/amazon/common/ami_config.go @@ -58,8 +58,8 @@ func (c *AMIConfig) Prepare(ctx *interpolate.Context) []error { c.AMIRegions = regions } - if len(c.AMIUsers) > 0 && c.AMIEncryptBootVolume { - errs = append(errs, fmt.Errorf("Cannot share AMI with encrypted boot volume")) + if len(c.AMIUsers) > 0 && len(c.AMIKmsKeyId) == 0 && c.AMIEncryptBootVolume { + errs = append(errs, fmt.Errorf("Cannot share AMI with encrypted boot volume unless key is specified with kms_key_id")) } if len(errs) > 0 { diff --git a/builder/amazon/common/ami_config_test.go b/builder/amazon/common/ami_config_test.go index 9d52cfc3b..df9533724 100644 --- a/builder/amazon/common/ami_config_test.go +++ b/builder/amazon/common/ami_config_test.go @@ -59,11 +59,18 @@ func TestAMIConfigPrepare_regions(t *testing.T) { } -func TestAMIConfigPrepare_EncryptBoot(t *testing.T) { +func TestAMIConfigPrepare_Share_EncryptedBoot(t *testing.T) { c := testAMIConfig() c.AMIUsers = []string{"testAccountID"} c.AMIEncryptBootVolume = true + + c.AMIKmsKeyId = "" if err := c.Prepare(nil); err == nil { - t.Fatal("should have error") + t.Fatal("shouldn't be able to share ami with encrypted boot volume unless the kms_key_id param is provided") + } + + c.AMIKmsKeyId = "89c3fb9a-de87-4f2a-aedc-fddc5138193c" + if err := c.Prepare(nil); err != nil { + t.Fatal("should be able to share ami with encrypted boot volume if the kms_key_id param is provided") } } From 8b13b750976be88c54252720cf452666fa6433f2 Mon Sep 17 00:00:00 2001 From: poida Date: Fri, 2 Dec 2016 21:51:10 +1100 Subject: [PATCH 3/4] Updated documentation and error messages The docs for kms_key_id needed to be next to encrypt_boot. Shortened some of the kms_key_id error messages. --- builder/amazon/common/ami_config.go | 2 +- builder/amazon/common/ami_config_test.go | 4 ++-- website/source/docs/builders/amazon-ebs.html.md | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/builder/amazon/common/ami_config.go b/builder/amazon/common/ami_config.go index 8914b1934..d8674418c 100644 --- a/builder/amazon/common/ami_config.go +++ b/builder/amazon/common/ami_config.go @@ -59,7 +59,7 @@ func (c *AMIConfig) Prepare(ctx *interpolate.Context) []error { } if len(c.AMIUsers) > 0 && len(c.AMIKmsKeyId) == 0 && c.AMIEncryptBootVolume { - errs = append(errs, fmt.Errorf("Cannot share AMI with encrypted boot volume unless key is specified with kms_key_id")) + errs = append(errs, fmt.Errorf("Cannot share AMI with encrypted boot volume unless kms_key_id is provided")) } if len(errs) > 0 { diff --git a/builder/amazon/common/ami_config_test.go b/builder/amazon/common/ami_config_test.go index df9533724..f2ee31e3d 100644 --- a/builder/amazon/common/ami_config_test.go +++ b/builder/amazon/common/ami_config_test.go @@ -66,11 +66,11 @@ func TestAMIConfigPrepare_Share_EncryptedBoot(t *testing.T) { c.AMIKmsKeyId = "" if err := c.Prepare(nil); err == nil { - t.Fatal("shouldn't be able to share ami with encrypted boot volume unless the kms_key_id param is provided") + t.Fatal("shouldn't be able to share ami with encrypted boot volume unless kms_key_id is provided") } c.AMIKmsKeyId = "89c3fb9a-de87-4f2a-aedc-fddc5138193c" if err := c.Prepare(nil); err != nil { - t.Fatal("should be able to share ami with encrypted boot volume if the kms_key_id param is provided") + t.Fatal("should be able to share ami with encrypted boot volume if kms_key_id is provided") } } diff --git a/website/source/docs/builders/amazon-ebs.html.md b/website/source/docs/builders/amazon-ebs.html.md index 0a656c19f..00615e2eb 100644 --- a/website/source/docs/builders/amazon-ebs.html.md +++ b/website/source/docs/builders/amazon-ebs.html.md @@ -77,8 +77,6 @@ builder. - `encrypted` (boolean) - Indicates whether to encrypt the volume or not - - `kms_key_id` (string) - The ID of the KMS key to use for volume encryption - - `iops` (integer) - The number of I/O operations per second (IOPS) that the volume supports. See the documentation on [IOPs](https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_EbsBlockDevice.html) @@ -164,6 +162,8 @@ builder. AMI with an encrypted boot volume (discarding the initial unencrypted AMI in the process). Default `false`. +- `kms_key_id` (string) - The ID of the KMS key to use for boot volume encryption. + - `iam_instance_profile` (string) - The name of an [IAM instance profile](https://docs.aws.amazon.com/IAM/latest/UserGuide/instance-profiles.html) to launch the EC2 instance with. From d7a7d8c85222b22ba8d140c31d2ded056d14d5cf Mon Sep 17 00:00:00 2001 From: Rickard von Essen Date: Sun, 4 Dec 2016 15:14:53 +0100 Subject: [PATCH 4/4] It's not possible to share encrypted AMI's --- builder/amazon/common/ami_config.go | 4 ++-- builder/amazon/common/ami_config_test.go | 6 +++--- builder/amazon/ebs/step_encrypted_ami.go | 3 ++- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/builder/amazon/common/ami_config.go b/builder/amazon/common/ami_config.go index d8674418c..fa442cb5c 100644 --- a/builder/amazon/common/ami_config.go +++ b/builder/amazon/common/ami_config.go @@ -58,8 +58,8 @@ func (c *AMIConfig) Prepare(ctx *interpolate.Context) []error { c.AMIRegions = regions } - if len(c.AMIUsers) > 0 && len(c.AMIKmsKeyId) == 0 && c.AMIEncryptBootVolume { - errs = append(errs, fmt.Errorf("Cannot share AMI with encrypted boot volume unless kms_key_id is provided")) + if len(c.AMIUsers) > 0 && c.AMIEncryptBootVolume { + errs = append(errs, fmt.Errorf("Cannot share AMI with encrypted boot volume")) } if len(errs) > 0 { diff --git a/builder/amazon/common/ami_config_test.go b/builder/amazon/common/ami_config_test.go index f2ee31e3d..ecb88bcb4 100644 --- a/builder/amazon/common/ami_config_test.go +++ b/builder/amazon/common/ami_config_test.go @@ -66,11 +66,11 @@ func TestAMIConfigPrepare_Share_EncryptedBoot(t *testing.T) { c.AMIKmsKeyId = "" if err := c.Prepare(nil); err == nil { - t.Fatal("shouldn't be able to share ami with encrypted boot volume unless kms_key_id is provided") + t.Fatal("shouldn't be able to share ami with encrypted boot volume") } c.AMIKmsKeyId = "89c3fb9a-de87-4f2a-aedc-fddc5138193c" - if err := c.Prepare(nil); err != nil { - t.Fatal("should be able to share ami with encrypted boot volume if kms_key_id is provided") + if err := c.Prepare(nil); err == nil { + t.Fatal("shouldn't be able to share ami with encrypted boot volume") } } diff --git a/builder/amazon/ebs/step_encrypted_ami.go b/builder/amazon/ebs/step_encrypted_ami.go index 8ab84e8e1..5721def98 100644 --- a/builder/amazon/ebs/step_encrypted_ami.go +++ b/builder/amazon/ebs/step_encrypted_ami.go @@ -2,6 +2,7 @@ package ebs import ( "fmt" + "log" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/ec2" @@ -23,7 +24,7 @@ func (s *stepCreateEncryptedAMICopy) Run(state multistep.StateBag) multistep.Ste // Encrypt boot not set, so skip step if !config.AMIConfig.AMIEncryptBootVolume { if kmsKeyId != "" { - ui.Say(fmt.Sprintf("Ignoring KMS Key ID: %s, encrypted=false", kmsKeyId)) + log.Printf(fmt.Sprintf("Ignoring KMS Key ID: %s, encrypted=false", kmsKeyId)) } return multistep.ActionContinue }