Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] binary_authorization_attestor KMS support #4078

Merged
merged 1 commit into from
Jul 26, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 32 additions & 9 deletions google/bootstrap_utils_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,30 +11,43 @@ import (
)

var SharedKeyRing = "tftest-shared-keyring-1"
var SharedCyptoKey = "tftest-shared-key-1"
var SharedCryptoKey = map[string]string{
"ENCRYPT_DECRYPT": "tftest-shared-key-1",
"ASYMMETRIC_SIGN": "tftest-shared-sign-key-1",
"ASYMMETRIC_DECRYPT": "tftest-shared-decrypt-key-1",
}

type bootstrappedKMS struct {
*cloudkms.KeyRing
*cloudkms.CryptoKey
}

// BootstrapKMSKey returns a KMS key in the "global" location.
// See BootstrapKMSKeyInLocation.
func BootstrapKMSKey(t *testing.T) bootstrappedKMS {
return BootstrapKMSKeyInLocation(t, "global")
}

func BootstrapKMSKeyInLocation(t *testing.T, locationID string) bootstrappedKMS {
return BootstrapKMSKeyWithPurposeInLocation(t, "ENCRYPT_DECRYPT", locationID)
}

// BootstrapKMSKeyWithPurpose returns a KMS key in the "global" location.
// See BootstrapKMSKeyWithPurposeInLocation.
func BootstrapKMSKeyWithPurpose(t *testing.T, purpose string) bootstrappedKMS {
return BootstrapKMSKeyWithPurposeInLocation(t, purpose, "global")
}

/**
* BootstrapKMSKeyWithLocation will return a KMS key in a particular location
* that can be used in tests that are testing KMS integration with other resources.
* BootstrapKMSKeyWithPurposeInLocation will return a KMS key in a
* particular location with the given purpose that can be used
* in tests that are testing KMS integration with other resources.
*
* This will either return an existing key or create one if it hasn't been created
* in the project yet. The motivation is because keyrings don't get deleted and we
* don't want a linear growth of disabled keyrings in a project. We also don't want
* to incur the overhead of creating a new project for each test that needs to use
* a KMS key.
**/
func BootstrapKMSKeyInLocation(t *testing.T, locationID string) bootstrappedKMS {
func BootstrapKMSKeyWithPurposeInLocation(t *testing.T, purpose, locationID string) bootstrappedKMS {
if v := os.Getenv("TF_ACC"); v == "" {
log.Println("Acceptance tests and bootstrapping skipped unless env 'TF_ACC' set")

Expand All @@ -49,7 +62,7 @@ func BootstrapKMSKeyInLocation(t *testing.T, locationID string) bootstrappedKMS
keyRingParent := fmt.Sprintf("projects/%s/locations/%s", projectID, locationID)
keyRingName := fmt.Sprintf("%s/keyRings/%s", keyRingParent, SharedKeyRing)
keyParent := fmt.Sprintf("projects/%s/locations/%s/keyRings/%s", projectID, locationID, SharedKeyRing)
keyName := fmt.Sprintf("%s/cryptoKeys/%s", keyParent, SharedCyptoKey)
keyName := fmt.Sprintf("%s/cryptoKeys/%s", keyParent, SharedCryptoKey[purpose])

config := &Config{
Credentials: getTestCredsFromEnv(),
Expand Down Expand Up @@ -87,12 +100,22 @@ func BootstrapKMSKeyInLocation(t *testing.T, locationID string) bootstrappedKMS
cryptoKey, err := kmsClient.Projects.Locations.KeyRings.CryptoKeys.Get(keyName).Do()
if err != nil {
if isGoogleApiErrorWithCode(err, 404) {
algos := map[string]string{
"ENCRYPT_DECRYPT": "GOOGLE_SYMMETRIC_ENCRYPTION",
"ASYMMETRIC_SIGN": "RSA_SIGN_PKCS1_4096_SHA512",
"ASYMMETRIC_DECRYPT": "RSA_DECRYPT_OAEP_4096_SHA512",
}
template := cloudkms.CryptoKeyVersionTemplate{
Algorithm: algos[purpose],
}

newKey := cloudkms.CryptoKey{
Purpose: "ENCRYPT_DECRYPT",
Purpose: purpose,
VersionTemplate: &template,
}

cryptoKey, err = kmsClient.Projects.Locations.KeyRings.CryptoKeys.Create(keyParent, &newKey).
CryptoKeyId(SharedCyptoKey).Do()
CryptoKeyId(SharedCryptoKey[purpose]).Do()
if err != nil {
t.Errorf("Unable to bootstrap KMS key. Cannot create new CryptoKey: %s", err)
}
Expand Down
157 changes: 157 additions & 0 deletions google/data_source_google_kms_crypto_key_version.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
package google

import (
"fmt"
"log"
"strconv"
"strings"

"github.com/hashicorp/terraform/helper/schema"
)

func dataSourceGoogleKmsCryptoKeyVersion() *schema.Resource {
return &schema.Resource{
Read: dataSourceGoogleKmsCryptoKeyVersionRead,
Schema: map[string]*schema.Schema{
"crypto_key": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"version": {
Type: schema.TypeInt,
Optional: true,
Default: 1,
},
"algorithm": {
Type: schema.TypeString,
Computed: true,
},
"protection_level": {
Type: schema.TypeString,
Computed: true,
},
"state": {
Type: schema.TypeString,
Computed: true,
},
"public_key": {
Type: schema.TypeList,
Optional: true,
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"algorithm": {
Type: schema.TypeString,
Computed: true,
},
"pem": {
Type: schema.TypeString,
Computed: true,
},
},
},
},
},
}
}

func dataSourceGoogleKmsCryptoKeyVersionRead(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)

url, err := replaceVars(d, config, "{{KmsBasePath}}{{crypto_key}}/cryptoKeyVersions/{{version}}")
if err != nil {
return err
}

log.Printf("[DEBUG] Getting attributes for CryptoKeyVersion: %#v", url)
res, err := sendRequest(config, "GET", url, nil)
if err != nil {
return handleNotFoundError(err, d, fmt.Sprintf("KmsCryptoKeyVersion %q", d.Id()))
}

if err := d.Set("version", flattenKmsCryptoKeyVersionVersion(res["name"], d)); err != nil {
return fmt.Errorf("Error reading CryptoKeyVersion: %s", err)
}
if err := d.Set("state", flattenKmsCryptoKeyVersionState(res["state"], d)); err != nil {
return fmt.Errorf("Error reading CryptoKeyVersion: %s", err)
}
if err := d.Set("protection_level", flattenKmsCryptoKeyVersionProtectionLevel(res["protectionLevel"], d)); err != nil {
return fmt.Errorf("Error reading CryptoKeyVersion: %s", err)
}
if err := d.Set("algorithm", flattenKmsCryptoKeyVersionAlgorithm(res["algorithm"], d)); err != nil {
return fmt.Errorf("Error reading CryptoKeyVersion: %s", err)
}

url, err = replaceVars(d, config, "{{KmsBasePath}}{{crypto_key}}")
if err != nil {
return err
}

log.Printf("[DEBUG] Getting purpose of CryptoKey: %#v", url)
res, err = sendRequest(config, "GET", url, nil)
if err != nil {
return handleNotFoundError(err, d, fmt.Sprintf("KmsCryptoKey %q", d.Id()))
}

if res["purpose"] == "ASYMMETRIC_SIGN" || res["purpose"] == "ASYMMETRIC_DECRYPT" {
url, err = replaceVars(d, config, "{{KmsBasePath}}{{crypto_key}}/cryptoKeyVersions/{{version}}/publicKey")
if err != nil {
return err
}
log.Printf("[DEBUG] Getting public key of CryptoKeyVersion: %#v", url)
res, _ = sendRequest(config, "GET", url, nil)

if err := d.Set("public_key", flattenKmsCryptoKeyVersionPublicKey(res, d)); err != nil {
return fmt.Errorf("Error reading CryptoKeyVersion public key: %s", err)
}
}
d.SetId(fmt.Sprintf("//cloudkms.googleapis.com/%s/cryptoKeyVersions/%d", d.Get("crypto_key"), d.Get("version")))

return nil
}

func flattenKmsCryptoKeyVersionVersion(v interface{}, d *schema.ResourceData) interface{} {
parts := strings.Split(v.(string), "/")
version := parts[len(parts)-1]
// Handles the string fixed64 format
if intVal, err := strconv.ParseInt(version, 10, 64); err == nil {
return intVal
} // let terraform core handle it if we can't convert the string to an int.
return v
}

func flattenKmsCryptoKeyVersionState(v interface{}, d *schema.ResourceData) interface{} {
return v
}

func flattenKmsCryptoKeyVersionProtectionLevel(v interface{}, d *schema.ResourceData) interface{} {
return v
}

func flattenKmsCryptoKeyVersionAlgorithm(v interface{}, d *schema.ResourceData) interface{} {
return v
}

func flattenKmsCryptoKeyVersionPublicKey(v interface{}, d *schema.ResourceData) interface{} {
if v == nil {
return nil
}
original := v.(map[string]interface{})
if len(original) == 0 {
return nil
}
transformed := make(map[string]interface{})
transformed["pem"] =
flattenKmsCryptoKeyVersionPublicKeyPem(original["pem"], d)
transformed["algorithm"] =
flattenKmsCryptoKeyVersionPublicKeyAlgorithm(original["algorithm"], d)
return []interface{}{transformed}
}
func flattenKmsCryptoKeyVersionPublicKeyPem(v interface{}, d *schema.ResourceData) interface{} {
return v
}

func flattenKmsCryptoKeyVersionPublicKeyAlgorithm(v interface{}, d *schema.ResourceData) interface{} {
return v
}
47 changes: 47 additions & 0 deletions google/data_source_google_kms_crypto_key_version_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package google

import (
"fmt"
"testing"

"github.com/hashicorp/terraform/helper/resource"
)

func TestAccDataSourceGoogleKmsCryptoKeyVersion_basic(t *testing.T) {
asymSignKey := BootstrapKMSKeyWithPurpose(t, "ASYMMETRIC_SIGN")
asymDecrKey := BootstrapKMSKeyWithPurpose(t, "ASYMMETRIC_DECRYPT")
symKey := BootstrapKMSKey(t)

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
Steps: []resource.TestStep{
{
Config: testAccDataSourceGoogleKmsCryptoKeyVersion_basic(asymSignKey.CryptoKey.Name),
Check: resource.TestCheckResourceAttr("data.google_kms_crypto_key_version.version", "version", "1"),
},
// Asymmetric keys should have a public key
{
Config: testAccDataSourceGoogleKmsCryptoKeyVersion_basic(asymSignKey.CryptoKey.Name),
Check: resource.TestCheckResourceAttr("data.google_kms_crypto_key_version.version", "public_key.#", "1"),
},
{
Config: testAccDataSourceGoogleKmsCryptoKeyVersion_basic(asymDecrKey.CryptoKey.Name),
Check: resource.TestCheckResourceAttr("data.google_kms_crypto_key_version.version", "public_key.#", "1"),
},
// Symmetric key should have no public key
{
Config: testAccDataSourceGoogleKmsCryptoKeyVersion_basic(symKey.CryptoKey.Name),
Check: resource.TestCheckResourceAttr("data.google_kms_crypto_key_version.version", "public_key.#", "0"),
},
},
})
}

func testAccDataSourceGoogleKmsCryptoKeyVersion_basic(kmsKey string) string {
return fmt.Sprintf(`
data "google_kms_crypto_key_version" "version" {
crypto_key = "%s"
}
`, kmsKey)
}
1 change: 1 addition & 0 deletions google/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,7 @@ func Provider() terraform.ResourceProvider {
"google_kms_secret": dataSourceGoogleKmsSecret(),
"google_kms_key_ring": dataSourceGoogleKmsKeyRing(),
"google_kms_crypto_key": dataSourceGoogleKmsCryptoKey(),
"google_kms_crypto_key_version": dataSourceGoogleKmsCryptoKeyVersion(),
"google_folder": dataSourceGoogleFolder(),
"google_folder_organization_policy": dataSourceGoogleFolderOrganizationPolicy(),
"google_netblock_ip_ranges": dataSourceGoogleNetblockIpRanges(),
Expand Down
Loading