diff --git a/builder/amazon/common/ami_config.go b/builder/amazon/common/ami_config.go index a03c3a789..9d75aa5fc 100644 --- a/builder/amazon/common/ami_config.go +++ b/builder/amazon/common/ami_config.go @@ -3,6 +3,7 @@ package common import ( "fmt" "log" + "regexp" "github.com/hashicorp/packer/template/interpolate" ) @@ -62,6 +63,23 @@ func (c *AMIConfig) Prepare(accessConfig *AccessConfig, ctx *interpolate.Context errs = append(errs, fmt.Errorf("Cannot share AMI with encrypted boot volume")) } + var kmsKeys []string + if len(c.AMIKmsKeyId) > 0 { + kmsKeys = append(kmsKeys, c.AMIKmsKeyId) + } + if len(c.AMIRegionKMSKeyIDs) > 0 { + for _, kmsKey := range c.AMIRegionKMSKeyIDs { + if len(kmsKey) == 0 { + kmsKeys = append(kmsKeys, c.AMIKmsKeyId) + } + } + } + for _, kmsKey := range kmsKeys { + if !validateKmsKey(kmsKey) { + errs = append(errs, fmt.Errorf("%s is not a valid KMS Key Id.", kmsKey)) + } + } + if len(c.SnapshotUsers) > 0 { if len(c.AMIKmsKeyId) == 0 && c.AMIEncryptBootVolume { errs = append(errs, fmt.Errorf("Cannot share snapshot encrypted with default KMS key")) @@ -128,3 +146,23 @@ func (c *AMIConfig) prepareRegions(accessConfig *AccessConfig) (errs []error) { } return errs } + +// See https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_CopyImage.html +func validateKmsKey(kmsKey string) (valid bool) { + kmsKeyIdPattern := `[a-f0-9-]+$` + aliasPattern := `alias/[a-zA-Z0-9:/_-]+$` + kmsArnStartPattern := `^arn:aws:kms:([a-z]{2}-(gov-)?[a-z]+-\d{1})?:(\d{12}):` + if regexp.MustCompile(fmt.Sprintf("^%s", kmsKeyIdPattern)).MatchString(kmsKey) { + return true + } + if regexp.MustCompile(fmt.Sprintf("^%s", aliasPattern)).MatchString(kmsKey) { + return true + } + if regexp.MustCompile(fmt.Sprintf("%skey/%s", kmsArnStartPattern, kmsKeyIdPattern)).MatchString(kmsKey) { + return true + } + if regexp.MustCompile(fmt.Sprintf("%s%s", kmsArnStartPattern, aliasPattern)).MatchString(kmsKey) { + return true + } + return false +} diff --git a/builder/amazon/common/ami_config_test.go b/builder/amazon/common/ami_config_test.go index f7fce7411..ec1fda91a 100644 --- a/builder/amazon/common/ami_config_test.go +++ b/builder/amazon/common/ami_config_test.go @@ -176,6 +176,41 @@ func TestAMIConfigPrepare_Share_EncryptedBoot(t *testing.T) { } } +func TestAMIConfigPrepare_ValidateKmsKey(t *testing.T) { + c := testAMIConfig() + c.AMIEncryptBootVolume = true + + accessConf := testAccessConfig() + + validCases := []string{ + "abcd1234-e567-890f-a12b-a123b4cd56ef", + "alias/foo/bar", + "arn:aws:kms:us-east-1:012345678910:key/abcd1234-a123-456a-a12b-a123b4cd56ef", + "arn:aws:kms:us-east-1:012345678910:alias/foo/bar", + } + for _, validCase := range validCases { + c.AMIKmsKeyId = validCase + if err := c.Prepare(accessConf, nil); err != nil { + t.Fatalf("%s should not have failed KMS key validation", validCase) + } + } + + invalidCases := []string{ + "ABCD1234-e567-890f-a12b-a123b4cd56ef", + "ghij1234-e567-890f-a12b-a123b4cd56ef", + "ghij1234+e567_890f-a12b-a123b4cd56ef", + "foo/bar", + "arn:aws:kms:us-east-1:012345678910:foo/bar", + } + for _, invalidCase := range invalidCases { + c.AMIKmsKeyId = invalidCase + if err := c.Prepare(accessConf, nil); err == nil { + t.Fatalf("%s should have failed KMS key validation", invalidCase) + } + } + +} + func TestAMINameValidation(t *testing.T) { c := testAMIConfig()