Skip to content

Commit 333c2f0

Browse files
authored
Fix bug with CSEK where the key stored in state might be associated with the wrong disk (#327)
* Fix bug with CSEK where the key stored in state might be associated with the wrong disk * preserve original order of attached disks * use the disk index to figure out the raw key
1 parent 5ee8447 commit 333c2f0

File tree

2 files changed

+154
-31
lines changed

2 files changed

+154
-31
lines changed

google/resource_compute_instance.go

+30-14
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,19 @@
11
package google
22

33
import (
4+
"crypto/sha256"
5+
"encoding/base64"
46
"fmt"
57
"log"
68
"strings"
79

10+
"regexp"
11+
812
"github.com/hashicorp/terraform/helper/schema"
913
"github.com/hashicorp/terraform/helper/validation"
1014
computeBeta "google.golang.org/api/compute/v0.beta"
1115
"google.golang.org/api/compute/v1"
1216
"google.golang.org/api/googleapi"
13-
"regexp"
1417
)
1518

1619
var InstanceBaseApiVersion = v1
@@ -1083,16 +1086,15 @@ func resourceComputeInstanceRead(d *schema.ResourceData, meta interface{}) error
10831086
return fmt.Errorf("Expected %d disks, API returned %d", expectedDisks, len(instance.Disks))
10841087
}
10851088

1086-
attachedDiskSources := make(map[string]struct{}, attachedDisksCount)
1089+
attachedDiskSources := make(map[string]int, attachedDisksCount)
10871090
for i := 0; i < attachedDisksCount; i++ {
1088-
attachedDiskSources[d.Get(fmt.Sprintf("attached_disk.%d.source", i)).(string)] = struct{}{}
1091+
attachedDiskSources[d.Get(fmt.Sprintf("attached_disk.%d.source", i)).(string)] = i
10891092
}
10901093

10911094
dIndex := 0
1092-
adIndex := 0
10931095
sIndex := 0
10941096
disks := make([]map[string]interface{}, 0, disksCount)
1095-
attachedDisks := make([]map[string]interface{}, 0, attachedDisksCount)
1097+
attachedDisks := make([]map[string]interface{}, attachedDisksCount)
10961098
scratchDisks := make([]map[string]interface{}, 0, scratchDisksCount)
10971099
for _, disk := range instance.Disks {
10981100
if _, ok := d.GetOk("boot_disk"); ok && disk.Boot {
@@ -1115,24 +1117,29 @@ func resourceComputeInstanceRead(d *schema.ResourceData, meta interface{}) error
11151117
"device_name": d.Get(fmt.Sprintf("disk.%d.device_name", dIndex)),
11161118
"disk_encryption_key_raw": d.Get(fmt.Sprintf("disk.%d.disk_encryption_key_raw", dIndex)),
11171119
}
1118-
if disk.DiskEncryptionKey != nil && disk.DiskEncryptionKey.Sha256 != "" {
1119-
di["disk_encryption_key_sha256"] = disk.DiskEncryptionKey.Sha256
1120+
if d.Get(fmt.Sprintf("disk.%d.disk_encryption_key_raw", dIndex)) != "" {
1121+
sha, err := hash256(d.Get(fmt.Sprintf("disk.%d.disk_encryption_key_raw", dIndex)).(string))
1122+
if err != nil {
1123+
return err
1124+
}
1125+
di["disk_encryption_key_sha256"] = sha
11201126
}
11211127
disks = append(disks, di)
11221128
dIndex++
11231129
} else {
1130+
adIndex := attachedDiskSources[disk.Source]
11241131
di := map[string]interface{}{
1125-
"source": disk.Source,
1126-
"device_name": disk.DeviceName,
1127-
"disk_encryption_key_raw": d.Get(fmt.Sprintf("attached_disk.%d.disk_encryption_key_raw", adIndex)),
1132+
"source": disk.Source,
1133+
"device_name": disk.DeviceName,
11281134
}
1129-
if disk.DiskEncryptionKey != nil && disk.DiskEncryptionKey.Sha256 != "" {
1130-
di["disk_encryption_key_sha256"] = disk.DiskEncryptionKey.Sha256
1135+
if key := disk.DiskEncryptionKey; key != nil {
1136+
di["disk_encryption_key_raw"] = d.Get(fmt.Sprintf("attached_disk.%d.disk_encryption_key_raw", adIndex))
1137+
di["disk_encryption_key_sha256"] = key.Sha256
11311138
}
1132-
attachedDisks = append(attachedDisks, di)
1133-
adIndex++
1139+
attachedDisks[adIndex] = di
11341140
}
11351141
}
1142+
11361143
d.Set("disk", disks)
11371144
d.Set("attached_disk", attachedDisks)
11381145
d.Set("scratch_disk", scratchDisks)
@@ -1614,6 +1621,15 @@ func getProjectFromSubnetworkLink(subnetwork string) string {
16141621
return r.FindStringSubmatch(subnetwork)[1]
16151622
}
16161623

1624+
func hash256(raw string) (string, error) {
1625+
decoded, err := base64.StdEncoding.DecodeString(raw)
1626+
if err != nil {
1627+
return "", err
1628+
}
1629+
h := sha256.Sum256(decoded)
1630+
return base64.StdEncoding.EncodeToString(h[:]), nil
1631+
}
1632+
16171633
func createAcceleratorPartialUrl(zone, accelerator string) string {
16181634
return fmt.Sprintf("zones/%s/acceleratorTypes/%s", zone, accelerator)
16191635
}

google/resource_compute_instance_test.go

+124-17
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"fmt"
55
"os"
66
"regexp"
7+
"strconv"
78
"strings"
89
"testing"
910

@@ -225,21 +226,34 @@ func TestAccComputeInstance_deprecated_disksWithAutodelete(t *testing.T) {
225226
func TestAccComputeInstance_diskEncryption(t *testing.T) {
226227
var instance compute.Instance
227228
var instanceName = fmt.Sprintf("instance-test-%s", acctest.RandString(10))
228-
var diskName = fmt.Sprintf("instance-testd-%s", acctest.RandString(10))
229+
bootEncryptionKey := "SGVsbG8gZnJvbSBHb29nbGUgQ2xvdWQgUGxhdGZvcm0="
230+
bootEncryptionKeyHash := "esTuF7d4eatX4cnc4JsiEiaI+Rff78JgPhA/v1zxX9E="
231+
diskNameToEncryptionKey := map[string]*compute.CustomerEncryptionKey{
232+
fmt.Sprintf("instance-testd-%s", acctest.RandString(10)): {
233+
RawKey: "Ym9vdDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI=",
234+
Sha256: "awJ7p57H+uVZ9axhJjl1D3lfC2MgA/wnt/z88Ltfvss=",
235+
},
236+
fmt.Sprintf("instance-testd-%s", acctest.RandString(10)): {
237+
RawKey: "c2Vjb25kNzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI=",
238+
Sha256: "7TpIwUdtCOJpq2m+3nt8GFgppu6a2Xsj1t0Gexk13Yc=",
239+
},
240+
fmt.Sprintf("instance-testd-%s", acctest.RandString(10)): {
241+
RawKey: "dGhpcmQ2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI=",
242+
Sha256: "b3pvaS7BjDbCKeLPPTx7yXBuQtxyMobCHN1QJR43xeM=",
243+
},
244+
}
229245

230246
resource.Test(t, resource.TestCase{
231247
PreCheck: func() { testAccPreCheck(t) },
232248
Providers: testAccProviders,
233249
CheckDestroy: testAccCheckComputeInstanceDestroy,
234250
Steps: []resource.TestStep{
235251
resource.TestStep{
236-
Config: testAccComputeInstance_disks_encryption(diskName, instanceName),
252+
Config: testAccComputeInstance_disks_encryption(bootEncryptionKey, diskNameToEncryptionKey, instanceName),
237253
Check: resource.ComposeTestCheckFunc(
238254
testAccCheckComputeInstanceExists(
239255
"google_compute_instance.foobar", &instance),
240-
testAccCheckComputeInstanceDisk(&instance, instanceName, true, true),
241-
testAccCheckComputeInstanceDisk(&instance, diskName, true, false),
242-
testAccCheckComputeInstanceDiskEncryptionKey("google_compute_instance.foobar", &instance),
256+
testAccCheckComputeInstanceDiskEncryptionKey("google_compute_instance.foobar", &instance, bootEncryptionKeyHash, diskNameToEncryptionKey),
243257
),
244258
},
245259
},
@@ -982,24 +996,66 @@ func testAccCheckComputeInstanceScratchDisk(instance *compute.Instance, interfac
982996
}
983997
}
984998

985-
func testAccCheckComputeInstanceDiskEncryptionKey(n string, instance *compute.Instance) resource.TestCheckFunc {
999+
func testAccCheckComputeInstanceDiskEncryptionKey(n string, instance *compute.Instance, bootDiskEncryptionKey string, diskNameToEncryptionKey map[string]*compute.CustomerEncryptionKey) resource.TestCheckFunc {
9861000
return func(s *terraform.State) error {
9871001
rs, ok := s.RootModule().Resources[n]
9881002
if !ok {
9891003
return fmt.Errorf("Not found: %s", n)
9901004
}
9911005

9921006
for i, disk := range instance.Disks {
993-
attr := rs.Primary.Attributes[fmt.Sprintf("disk.%d.disk_encryption_key_sha256", i)]
994-
if attr == "" && disk.Boot {
995-
attr = rs.Primary.Attributes["boot_disk.0.disk_encryption_key_sha256"]
1007+
if disk.Boot {
1008+
attr := rs.Primary.Attributes["boot_disk.0.disk_encryption_key_sha256"]
1009+
if attr == "" {
1010+
attr = rs.Primary.Attributes[fmt.Sprintf("disk.%d.disk_encryption_key_sha256", i)]
1011+
}
1012+
if attr != bootDiskEncryptionKey {
1013+
return fmt.Errorf("Boot disk has wrong encryption key in state.\nExpected: %s\nActual: %s", bootDiskEncryptionKey, attr)
1014+
}
1015+
if disk.DiskEncryptionKey == nil && attr != "" {
1016+
return fmt.Errorf("Disk %d has mismatched encryption key.\nTF State: %+v\nGCP State: <empty>", i, attr)
1017+
}
1018+
if disk.DiskEncryptionKey != nil && attr != disk.DiskEncryptionKey.Sha256 {
1019+
return fmt.Errorf("Disk %d has mismatched encryption key.\nTF State: %+v\nGCP State: %+v",
1020+
i, attr, disk.DiskEncryptionKey.Sha256)
1021+
}
1022+
} else {
1023+
if disk.DiskEncryptionKey != nil {
1024+
sourceUrl := strings.Split(disk.Source, "/")
1025+
expectedKey := diskNameToEncryptionKey[sourceUrl[len(sourceUrl)-1]].Sha256
1026+
if disk.DiskEncryptionKey.Sha256 != expectedKey {
1027+
return fmt.Errorf("Disk %d has unexpected encryption key in GCP.\nExpected: %s\nActual: %s", i, expectedKey, disk.DiskEncryptionKey.Sha256)
1028+
}
1029+
}
9961030
}
997-
if disk.DiskEncryptionKey == nil && attr != "" {
998-
return fmt.Errorf("Disk %d has mismatched encryption key.\nTF State: %+v\nGCP State: <empty>", i, attr)
1031+
}
1032+
1033+
numDisks, err := strconv.Atoi(rs.Primary.Attributes["disk.#"])
1034+
if err != nil {
1035+
return fmt.Errorf("Error converting value of disk.#")
1036+
}
1037+
for i := 0; i < numDisks; i++ {
1038+
diskName := rs.Primary.Attributes[fmt.Sprintf("disk.%d.disk", i)]
1039+
encryptionKey := rs.Primary.Attributes[fmt.Sprintf("disk.%d.disk_encryption_key_sha256", i)]
1040+
expectedEncryptionKey := diskNameToEncryptionKey[diskName].Sha256
1041+
if encryptionKey != expectedEncryptionKey {
1042+
return fmt.Errorf("Disk %d has unexpected encryption key in state.\nExpected: %s\nActual: %s", i, expectedEncryptionKey, encryptionKey)
9991043
}
1000-
if disk.DiskEncryptionKey != nil && attr != disk.DiskEncryptionKey.Sha256 {
1001-
return fmt.Errorf("Disk %d has mismatched encryption key.\nTF State: %+v\nGCP State: %+v",
1002-
i, attr, disk.DiskEncryptionKey.Sha256)
1044+
}
1045+
1046+
numAttachedDisks, err := strconv.Atoi(rs.Primary.Attributes["attached_disk.#"])
1047+
if err != nil {
1048+
return fmt.Errorf("Error converting value of attached_disk.#")
1049+
}
1050+
for i := 0; i < numAttachedDisks; i++ {
1051+
diskSourceUrl := strings.Split(rs.Primary.Attributes[fmt.Sprintf("attached_disk.%d.source", i)], "/")
1052+
diskName := diskSourceUrl[len(diskSourceUrl)-1]
1053+
encryptionKey := rs.Primary.Attributes[fmt.Sprintf("attached_disk.%d.disk_encryption_key_sha256", i)]
1054+
if key, ok := diskNameToEncryptionKey[diskName]; ok {
1055+
expectedEncryptionKey := key.Sha256
1056+
if encryptionKey != expectedEncryptionKey {
1057+
return fmt.Errorf("Attached disk %d has unexpected encryption key in state.\nExpected: %s\nActual: %s", i, expectedEncryptionKey, encryptionKey)
1058+
}
10031059
}
10041060
}
10051061
return nil
@@ -1452,13 +1508,44 @@ resource "google_compute_instance" "foobar" {
14521508
`, disk, instance, autodelete)
14531509
}
14541510

1455-
func testAccComputeInstance_disks_encryption(disk, instance string) string {
1511+
func testAccComputeInstance_disks_encryption(bootEncryptionKey string, diskNameToEncryptionKey map[string]*compute.CustomerEncryptionKey, instance string) string {
1512+
diskNames := []string{}
1513+
for k, _ := range diskNameToEncryptionKey {
1514+
diskNames = append(diskNames, k)
1515+
}
14561516
return fmt.Sprintf(`
14571517
resource "google_compute_disk" "foobar" {
14581518
name = "%s"
14591519
size = 10
14601520
type = "pd-ssd"
14611521
zone = "us-central1-a"
1522+
1523+
disk_encryption_key_raw = "%s"
1524+
}
1525+
1526+
resource "google_compute_disk" "foobar2" {
1527+
name = "%s"
1528+
size = 10
1529+
type = "pd-ssd"
1530+
zone = "us-central1-a"
1531+
1532+
disk_encryption_key_raw = "%s"
1533+
}
1534+
1535+
resource "google_compute_disk" "foobar3" {
1536+
name = "%s"
1537+
size = 10
1538+
type = "pd-ssd"
1539+
zone = "us-central1-a"
1540+
1541+
disk_encryption_key_raw = "%s"
1542+
}
1543+
1544+
resource "google_compute_disk" "foobar4" {
1545+
name = "%s"
1546+
size = 10
1547+
type = "pd-ssd"
1548+
zone = "us-central1-a"
14621549
}
14631550
14641551
resource "google_compute_instance" "foobar" {
@@ -1470,11 +1557,26 @@ resource "google_compute_instance" "foobar" {
14701557
initialize_params{
14711558
image = "debian-8-jessie-v20160803"
14721559
}
1473-
disk_encryption_key_raw = "SGVsbG8gZnJvbSBHb29nbGUgQ2xvdWQgUGxhdGZvcm0="
1560+
disk_encryption_key_raw = "%s"
14741561
}
14751562
14761563
disk {
14771564
disk = "${google_compute_disk.foobar.name}"
1565+
disk_encryption_key_raw = "%s"
1566+
}
1567+
1568+
attached_disk {
1569+
source = "${google_compute_disk.foobar2.self_link}"
1570+
disk_encryption_key_raw = "%s"
1571+
}
1572+
1573+
attached_disk {
1574+
source = "${google_compute_disk.foobar4.self_link}"
1575+
}
1576+
1577+
attached_disk {
1578+
source = "${google_compute_disk.foobar3.self_link}"
1579+
disk_encryption_key_raw = "%s"
14781580
}
14791581
14801582
network_interface {
@@ -1485,7 +1587,12 @@ resource "google_compute_instance" "foobar" {
14851587
foo = "bar"
14861588
}
14871589
}
1488-
`, disk, instance)
1590+
`, diskNames[0], diskNameToEncryptionKey[diskNames[0]].RawKey,
1591+
diskNames[1], diskNameToEncryptionKey[diskNames[1]].RawKey,
1592+
diskNames[2], diskNameToEncryptionKey[diskNames[2]].RawKey,
1593+
"instance-testd-"+acctest.RandString(10),
1594+
instance, bootEncryptionKey,
1595+
diskNameToEncryptionKey[diskNames[0]].RawKey, diskNameToEncryptionKey[diskNames[1]].RawKey, diskNameToEncryptionKey[diskNames[2]].RawKey)
14891596
}
14901597

14911598
func testAccComputeInstance_attachedDisk(disk, instance string) string {

0 commit comments

Comments
 (0)