Skip to content

Commit

Permalink
binary_authorization_attestor KMS support
Browse files Browse the repository at this point in the history
Signed-off-by: Modular Magician <[email protected]>
  • Loading branch information
drebes authored and modular-magician committed Jul 24, 2019
1 parent cb7b92a commit 48a1318
Show file tree
Hide file tree
Showing 7 changed files with 428 additions and 16 deletions.
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
}
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
86 changes: 84 additions & 2 deletions google/resource_binary_authorization_attestor.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,15 +61,32 @@ func resourceBinaryAuthorizationAttestor() *schema.Resource {
Schema: map[string]*schema.Schema{
"ascii_armored_pgp_public_key": {
Type: schema.TypeString,
Required: true,
Optional: true,
},
"comment": {
Type: schema.TypeString,
Optional: true,
},
"id": {
Type: schema.TypeString,
Computed: true,
Optional: true,
},
"pkix_public_key": {
Type: schema.TypeList,
Optional: true,
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"public_key_pem": {
Type: schema.TypeString,
Optional: true,
},
"signature_algorithm": {
Type: schema.TypeString,
Optional: true,
},
},
},
},
},
},
Expand Down Expand Up @@ -320,6 +337,7 @@ func flattenBinaryAuthorizationAttestorAttestationAuthorityNotePublicKeys(v inte
"comment": flattenBinaryAuthorizationAttestorAttestationAuthorityNotePublicKeysComment(original["comment"], d),
"id": flattenBinaryAuthorizationAttestorAttestationAuthorityNotePublicKeysId(original["id"], d),
"ascii_armored_pgp_public_key": flattenBinaryAuthorizationAttestorAttestationAuthorityNotePublicKeysAsciiArmoredPgpPublicKey(original["asciiArmoredPgpPublicKey"], d),
"pkix_public_key": flattenBinaryAuthorizationAttestorAttestationAuthorityNotePublicKeysPkixPublicKey(original["pkixPublicKey"], d),
})
}
return transformed
Expand All @@ -336,6 +354,29 @@ func flattenBinaryAuthorizationAttestorAttestationAuthorityNotePublicKeysAsciiAr
return v
}

func flattenBinaryAuthorizationAttestorAttestationAuthorityNotePublicKeysPkixPublicKey(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["public_key_pem"] =
flattenBinaryAuthorizationAttestorAttestationAuthorityNotePublicKeysPkixPublicKeyPublicKeyPem(original["publicKeyPem"], d)
transformed["signature_algorithm"] =
flattenBinaryAuthorizationAttestorAttestationAuthorityNotePublicKeysPkixPublicKeySignatureAlgorithm(original["signatureAlgorithm"], d)
return []interface{}{transformed}
}
func flattenBinaryAuthorizationAttestorAttestationAuthorityNotePublicKeysPkixPublicKeyPublicKeyPem(v interface{}, d *schema.ResourceData) interface{} {
return v
}

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

func flattenBinaryAuthorizationAttestorAttestationAuthorityNoteDelegationServiceAccountEmail(v interface{}, d *schema.ResourceData) interface{} {
return v
}
Expand Down Expand Up @@ -426,6 +467,13 @@ func expandBinaryAuthorizationAttestorAttestationAuthorityNotePublicKeys(v inter
transformed["asciiArmoredPgpPublicKey"] = transformedAsciiArmoredPgpPublicKey
}

transformedPkixPublicKey, err := expandBinaryAuthorizationAttestorAttestationAuthorityNotePublicKeysPkixPublicKey(original["pkix_public_key"], d, config)
if err != nil {
return nil, err
} else if val := reflect.ValueOf(transformedPkixPublicKey); val.IsValid() && !isEmptyValue(val) {
transformed["pkixPublicKey"] = transformedPkixPublicKey
}

req = append(req, transformed)
}
return req, nil
Expand All @@ -443,6 +491,40 @@ func expandBinaryAuthorizationAttestorAttestationAuthorityNotePublicKeysAsciiArm
return v, nil
}

func expandBinaryAuthorizationAttestorAttestationAuthorityNotePublicKeysPkixPublicKey(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
l := v.([]interface{})
if len(l) == 0 || l[0] == nil {
return nil, nil
}
raw := l[0]
original := raw.(map[string]interface{})
transformed := make(map[string]interface{})

transformedPublicKeyPem, err := expandBinaryAuthorizationAttestorAttestationAuthorityNotePublicKeysPkixPublicKeyPublicKeyPem(original["public_key_pem"], d, config)
if err != nil {
return nil, err
} else if val := reflect.ValueOf(transformedPublicKeyPem); val.IsValid() && !isEmptyValue(val) {
transformed["publicKeyPem"] = transformedPublicKeyPem
}

transformedSignatureAlgorithm, err := expandBinaryAuthorizationAttestorAttestationAuthorityNotePublicKeysPkixPublicKeySignatureAlgorithm(original["signature_algorithm"], d, config)
if err != nil {
return nil, err
} else if val := reflect.ValueOf(transformedSignatureAlgorithm); val.IsValid() && !isEmptyValue(val) {
transformed["signatureAlgorithm"] = transformedSignatureAlgorithm
}

return transformed, nil
}

func expandBinaryAuthorizationAttestorAttestationAuthorityNotePublicKeysPkixPublicKeyPublicKeyPem(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
return v, nil
}

func expandBinaryAuthorizationAttestorAttestationAuthorityNotePublicKeysPkixPublicKeySignatureAlgorithm(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
return v, nil
}

func expandBinaryAuthorizationAttestorAttestationAuthorityNoteDelegationServiceAccountEmail(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
return v, nil
}
Expand Down
Loading

0 comments on commit 48a1318

Please sign in to comment.