-
Notifications
You must be signed in to change notification settings - Fork 264
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add terraform datasource "google_kms_secret_ciphertext"
Signed-off-by: Modular Magician <[email protected]>
- Loading branch information
1 parent
0091a5d
commit 4f389d3
Showing
5 changed files
with
280 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
package google | ||
|
||
import ( | ||
"google.golang.org/api/cloudkms/v1" | ||
|
||
"encoding/base64" | ||
"fmt" | ||
"github.com/hashicorp/terraform/helper/schema" | ||
"log" | ||
"time" | ||
) | ||
|
||
func dataSourceGoogleKmsSecretCiphertext() *schema.Resource { | ||
return &schema.Resource{ | ||
Read: dataSourceGoogleKmsSecretCiphertextRead, | ||
Schema: map[string]*schema.Schema{ | ||
"crypto_key": { | ||
Type: schema.TypeString, | ||
Required: true, | ||
}, | ||
"ciphertext": { | ||
Type: schema.TypeString, | ||
Computed: true, | ||
}, | ||
"plaintext": { | ||
Type: schema.TypeString, | ||
Required: true, | ||
Sensitive: true, | ||
}, | ||
}, | ||
} | ||
} | ||
|
||
func dataSourceGoogleKmsSecretCiphertextRead(d *schema.ResourceData, meta interface{}) error { | ||
config := meta.(*Config) | ||
|
||
cryptoKeyId, err := parseKmsCryptoKeyId(d.Get("crypto_key").(string), config) | ||
|
||
if err != nil { | ||
return err | ||
} | ||
|
||
plaintext := base64.StdEncoding.EncodeToString([]byte(d.Get("plaintext").(string))) | ||
|
||
kmsEncryptRequest := &cloudkms.EncryptRequest{ | ||
Plaintext: plaintext, | ||
} | ||
|
||
encryptResponse, err := config.clientKms.Projects.Locations.KeyRings.CryptoKeys.Encrypt(cryptoKeyId.cryptoKeyId(), kmsEncryptRequest).Do() | ||
|
||
if err != nil { | ||
return fmt.Errorf("Error encrypting plaintext: %s", err) | ||
} | ||
|
||
log.Printf("[INFO] Successfully encrypted plaintext") | ||
|
||
d.Set("ciphertext", encryptResponse.Ciphertext) | ||
d.SetId(time.Now().UTC().String()) | ||
|
||
return nil | ||
} |
113 changes: 113 additions & 0 deletions
113
google-beta/data_source_google_kms_secret_ciphertext_test.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
package google | ||
|
||
import ( | ||
"encoding/base64" | ||
"fmt" | ||
"log" | ||
"testing" | ||
|
||
"github.com/hashicorp/terraform/helper/acctest" | ||
"github.com/hashicorp/terraform/helper/resource" | ||
"github.com/hashicorp/terraform/terraform" | ||
"google.golang.org/api/cloudkms/v1" | ||
) | ||
|
||
func TestAccKmsSecretCiphertext_basic(t *testing.T) { | ||
t.Parallel() | ||
|
||
projectOrg := getTestOrgFromEnv(t) | ||
projectBillingAccount := getTestBillingAccountFromEnv(t) | ||
|
||
projectId := "terraform-" + acctest.RandString(10) | ||
keyRingName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) | ||
cryptoKeyName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) | ||
|
||
plaintext := fmt.Sprintf("secret-%s", acctest.RandString(10)) | ||
|
||
// The first test creates resources needed to encrypt plaintext and produce ciphertext | ||
resource.Test(t, resource.TestCase{ | ||
PreCheck: func() { testAccPreCheck(t) }, | ||
Providers: testAccProviders, | ||
Steps: []resource.TestStep{ | ||
{ | ||
Config: testGoogleKmsCryptoKey_basic(projectId, projectOrg, projectBillingAccount, keyRingName, cryptoKeyName), | ||
Check: func(s *terraform.State) error { | ||
cryptoKeyId, err := getCryptoKeyId(s, "google_kms_crypto_key.crypto_key") | ||
|
||
if err != nil { | ||
return err | ||
} | ||
|
||
// The second test asserts that the data source created a ciphertext that can be decrypted to the correct plaintext | ||
resource.Test(t, resource.TestCase{ | ||
PreCheck: func() { testAccPreCheck(t) }, | ||
Providers: testAccProviders, | ||
Steps: []resource.TestStep{ | ||
{ | ||
Config: testGoogleKmsSecretCiphertext_datasource(cryptoKeyId.terraformId(), plaintext), | ||
Check: func(s *terraform.State) error { | ||
plaintext, err := testAccDecryptSecretDataWithCryptoKey(s, cryptoKeyId, "data.google_kms_secret_ciphertext.acceptance") | ||
|
||
if err != nil { | ||
return err | ||
} | ||
|
||
return resource.TestCheckResourceAttr("data.google_kms_secret_ciphertext.acceptance", "plaintext", plaintext)(s) | ||
}, | ||
}, | ||
}, | ||
}) | ||
|
||
return nil | ||
}, | ||
}, | ||
}, | ||
}) | ||
} | ||
|
||
func getCryptoKeyId(s *terraform.State, cryptoKeyResourceName string) (*kmsCryptoKeyId, error) { | ||
config := testAccProvider.Meta().(*Config) | ||
rs, ok := s.RootModule().Resources[cryptoKeyResourceName] | ||
if !ok { | ||
return nil, fmt.Errorf("Resource not found: %s", cryptoKeyResourceName) | ||
} | ||
|
||
return parseKmsCryptoKeyId(rs.Primary.Attributes["id"], config) | ||
} | ||
|
||
func testAccDecryptSecretDataWithCryptoKey(s *terraform.State, cryptoKeyId *kmsCryptoKeyId, secretCiphertextResourceName string) (string, error) { | ||
config := testAccProvider.Meta().(*Config) | ||
rs, ok := s.RootModule().Resources[secretCiphertextResourceName] | ||
if !ok { | ||
return "", fmt.Errorf("Resource not found: %s", secretCiphertextResourceName) | ||
} | ||
ciphertext, ok := rs.Primary.Attributes["ciphertext"] | ||
if !ok { | ||
return "", fmt.Errorf("Attribute 'ciphertext' not found in resource '%s'", secretCiphertextResourceName) | ||
} | ||
|
||
kmsDecryptRequest := &cloudkms.DecryptRequest{ | ||
Ciphertext: ciphertext, | ||
} | ||
|
||
decryptResponse, err := config.clientKms.Projects.Locations.KeyRings.CryptoKeys.Decrypt(cryptoKeyId.cryptoKeyId(), kmsDecryptRequest).Do() | ||
|
||
if err != nil { | ||
return "", fmt.Errorf("Error decrypting ciphertext: %s", err) | ||
} | ||
|
||
plaintext := base64.StdEncoding.EncodeToString([]byte(decryptResponse.Plaintext)) | ||
|
||
log.Printf("[INFO] Successfully decrypted ciphertext and got plaintext: %s", plaintext) | ||
|
||
return plaintext, nil | ||
} | ||
|
||
func testGoogleKmsSecretCiphertext_datasource(cryptoKeyTerraformId, plaintext string) string { | ||
return fmt.Sprintf(` | ||
data "google_kms_secret_ciphertext" "acceptance" { | ||
crypto_key = "%s" | ||
plaintext = "%s" | ||
} | ||
`, cryptoKeyTerraformId, plaintext) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
--- | ||
layout: "google" | ||
page_title: "Google: google_kms_secret_ciphertext" | ||
sidebar_current: "docs-google-kms-secret-ciphertext" | ||
description: |- | ||
Encrypts secret data with Google Cloud KMS and provides access to the ciphertext | ||
--- | ||
|
||
# google\_kms\_secret\_ciphertext | ||
|
||
This data source allows you to encrypt data with Google Cloud KMS and use the | ||
ciphertext within your resource definitions. | ||
|
||
For more information see | ||
[the official documentation](https://cloud.google.com/kms/docs/encrypt-decrypt). | ||
|
||
~> **NOTE**: Using this data source will allow you to conceal secret data within your | ||
resource definitions, but it does not take care of protecting that data in the | ||
logging output, plan output, or state output. Please take care to secure your secret | ||
data outside of resource definitions. | ||
|
||
## Example Usage | ||
|
||
First, create a KMS KeyRing and CryptoKey using the resource definitions: | ||
|
||
```hcl | ||
resource "google_kms_key_ring" "my_key_ring" { | ||
project = "my-project" | ||
name = "my-key-ring" | ||
location = "us-central1" | ||
} | ||
resource "google_kms_crypto_key" "my_crypto_key" { | ||
name = "my-crypto-key" | ||
key_ring = "${google_kms_key_ring.my_key_ring.id}" | ||
} | ||
``` | ||
|
||
Next, encrypt some sensitive information and use the encrypted data in your resource definitions: | ||
|
||
```hcl | ||
data "google_kms_secret_ciphertext" "my_password" { | ||
crypto_key = "${google_kms_crypto_key.my_crypto_key.id}" | ||
plaintext = "my-secret-password" | ||
} | ||
resource "google_compute_instance" "instance" { | ||
name = "test" | ||
machine_type = "n1-standard-1" | ||
zone = "us-central1-a" | ||
boot_disk { | ||
initialize_params { | ||
image = "debian-cloud/debian-9" | ||
} | ||
} | ||
network_interface { | ||
network = "default" | ||
access_config { | ||
} | ||
} | ||
metadata = { | ||
password = "${data.google_kms_secret_ciphertext.my_password.ciphertext}" | ||
} | ||
} | ||
``` | ||
|
||
The resulting instance can then access the encrypted password from its metadata | ||
and decrypt it, e.g. using the [Cloud SDK](https://cloud.google.com/sdk/gcloud/reference/kms/decrypt)): | ||
```bash | ||
$ curl -H "Metadata-Flavor: Google" http://metadata.google.internal/computeMetadata/v1/instance/attributes/password \ | ||
> | base64 -d | gcloud kms decrypt \ | ||
> --project my-project \ | ||
> --location us-central1 \ | ||
> --keyring my-key-ring \ | ||
> --key my-crypto-key \ | ||
> --plaintext-file - \ | ||
> --ciphertext-file - \ | ||
my-secret-password | ||
``` | ||
|
||
## Argument Reference | ||
|
||
The following arguments are supported: | ||
|
||
* `plaintext` (Required) - The plaintext to be encrypted | ||
* `crypto_key` (Required) - The id of the CryptoKey that will be used to | ||
encrypt the provided plaintext. This is represented by the format | ||
`{projectId}/{location}/{keyRingName}/{cryptoKeyName}`. | ||
|
||
## Attributes Reference | ||
|
||
The following attribute is exported: | ||
|
||
* `ciphertext` - Contains the result of encrypting the provided plaintext, encoded in base64. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters