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鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add vault_transit_encrypt and vault_transit_decrypt #872

Merged
merged 2 commits into from
Oct 2, 2020
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
69 changes: 69 additions & 0 deletions vault/data_source_transit_decrypt.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package vault

import (
"encoding/base64"
"fmt"
"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
"github.com/hashicorp/vault/api"
)

func transitDecryptDataSource() *schema.Resource {
return &schema.Resource{
Read: transitDecryptDataSourceRead,

Schema: map[string]*schema.Schema{
"key": {
Type: schema.TypeString,
Required: true,
Description: "Name of the decryption key to use.",
},
"backend": {
Type: schema.TypeString,
Required: true,
Description: "The Transit secret backend the key belongs to.",
},
"plaintext": {
Type: schema.TypeString,
Computed: true,
Description: "Decrypted plain text",
Sensitive: true,
},
"context": {
Type: schema.TypeString,
Optional: true,
Description: "Specifies the context for key derivation",
},
"ciphertext": {
Type: schema.TypeString,
Required: true,
Description: "Transit encrypted cipher text.",
},
},
}
}

func transitDecryptDataSourceRead(d *schema.ResourceData, meta interface{}) error {
client := meta.(*api.Client)

backend := d.Get("backend").(string)
key := d.Get("key").(string)
ciphertext := d.Get("ciphertext").(string)

context := base64.StdEncoding.EncodeToString([]byte(d.Get("context").(string)))
payload := map[string]interface{}{
"ciphertext": ciphertext,
"context": context,
}

decryptedData, err := client.Logical().Write(backend+"/decrypt/"+key, payload)
if err != nil {
return fmt.Errorf("issue encrypting with key: %s", err)
}

plaintext, _ := base64.StdEncoding.DecodeString(decryptedData.Data["plaintext"].(string))

d.SetId(base64.StdEncoding.EncodeToString([]byte(ciphertext)))
d.Set("plaintext", string(plaintext))

return nil
}
67 changes: 67 additions & 0 deletions vault/data_source_transit_decrypt_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package vault

import (
"fmt"
"testing"

"github.com/hashicorp/terraform-plugin-sdk/helper/resource"
"github.com/hashicorp/terraform-plugin-sdk/terraform"
)

func TestDataSourceTransitDecrypt(t *testing.T) {
resource.Test(t, resource.TestCase{
Providers: testProviders,
PreCheck: func() { testAccPreCheck(t) },
Steps: []resource.TestStep{
{
Config: testDataSourceTransitDecrypt_config,
Check: testDataSourceTransitDecrypt_check,
},
},
})

}

var testDataSourceTransitDecrypt_config = `
resource "vault_mount" "test" {
path = "transit"
type = "transit"
description = "This is an example mount"
}

resource "vault_transit_secret_backend_key" "test" {
name = "test"
backend = vault_mount.test.path
deletion_allowed = true
}

data "vault_transit_encrypt" "test" {
backend = vault_mount.test.path
key = vault_transit_secret_backend_key.test.name
plaintext = "foo"
}

data "vault_transit_decrypt" "test" {
backend = vault_mount.test.path
key = vault_transit_secret_backend_key.test.name
ciphertext = data.vault_transit_encrypt.test.ciphertext
}
`

func testDataSourceTransitDecrypt_check(s *terraform.State) error {
resourceState := s.Modules[0].Resources["data.vault_transit_decrypt.test"]
if resourceState == nil {
return fmt.Errorf("resource not found in state %v", s.Modules[0].Resources)
}

iState := resourceState.Primary
if iState == nil {
return fmt.Errorf("resource has no primary instance")
}

if got, want := iState.Attributes["plaintext"], "foo"; got != want {
return fmt.Errorf("Decrypted plaintext %s; did not match encrypted plaintext 'foo'", iState.Attributes["plaintext"])
}

return nil
}
77 changes: 77 additions & 0 deletions vault/data_source_transit_encrypt.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package vault

import (
"encoding/base64"
"fmt"
"github.com/hashicorp/terraform-plugin-sdk/helper/schema"

"github.com/hashicorp/vault/api"
)

func transitEncryptDataSource() *schema.Resource {
return &schema.Resource{
Read: transitEncryptDataSourceRead,

Schema: map[string]*schema.Schema{
"key": {
Type: schema.TypeString,
Required: true,
Description: "Name of the encryption key to use.",
},
"backend": {
Type: schema.TypeString,
Required: true,
Description: "The Transit secret backend the key belongs to.",
},
"plaintext": {
Type: schema.TypeString,
Required: true,
Description: "Map of strings read from Vault.",
Sensitive: true,
},
"context": {
Type: schema.TypeString,
Optional: true,
Description: "Specifies the context for key derivation",
},
"key_version": {
Type: schema.TypeInt,
Optional: true,
Description: "The version of the key to use for encryption",
},
"ciphertext": {
Type: schema.TypeString,
Computed: true,
Description: "Transit encrypted cipher text.",
},
},
}
}

func transitEncryptDataSourceRead(d *schema.ResourceData, meta interface{}) error {
client := meta.(*api.Client)

backend := d.Get("backend").(string)
key := d.Get("key").(string)
keyVersion := d.Get("key_version").(int)

plaintext := base64.StdEncoding.EncodeToString([]byte(d.Get("plaintext").(string)))
context := base64.StdEncoding.EncodeToString([]byte(d.Get("context").(string)))
payload := map[string]interface{}{
"plaintext": plaintext,
"context": context,
"key_version": keyVersion,
}

encryptedData, err := client.Logical().Write(backend+"/encrypt/"+key, payload)
if err != nil {
return fmt.Errorf("issue encrypting with key: %s", err)
}

cipherText := encryptedData.Data["ciphertext"]

d.SetId(base64.StdEncoding.EncodeToString([]byte(cipherText.(string))))
d.Set("ciphertext", cipherText)

return nil
}
67 changes: 67 additions & 0 deletions vault/data_source_transit_encrypt_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package vault

import (
"fmt"
"testing"

"github.com/hashicorp/terraform-plugin-sdk/helper/resource"
"github.com/hashicorp/terraform-plugin-sdk/terraform"
)

func TestDataSourceTransitEncrypt(t *testing.T) {
resource.Test(t, resource.TestCase{
Providers: testProviders,
PreCheck: func() { testAccPreCheck(t) },
Steps: []resource.TestStep{
{
Config: testDataSourceTransitEncrypt_config,
Check: testDataSourceTransitEncrypt_check,
},
},
})

}

var testDataSourceTransitEncrypt_config = `
resource "vault_mount" "test" {
path = "transit"
type = "transit"
description = "This is an example mount"
}

resource "vault_transit_secret_backend_key" "test" {
name = "test"
backend = vault_mount.test.path
deletion_allowed = true
}

data "vault_transit_encrypt" "test" {
backend = vault_mount.test.path
key = vault_transit_secret_backend_key.test.name
plaintext = "foo"
}

data "vault_transit_decrypt" "test" {
backend = vault_mount.test.path
key = vault_transit_secret_backend_key.test.name
ciphertext = data.vault_transit_encrypt.test.ciphertext
}
`

func testDataSourceTransitEncrypt_check(s *terraform.State) error {
resourceState := s.Modules[0].Resources["data.vault_transit_decrypt.test"]
if resourceState == nil {
return fmt.Errorf("resource not found in state %v", s.Modules[0].Resources)
}

iState := resourceState.Primary
if iState == nil {
return fmt.Errorf("resource has no primary instance")
}

if got, want := iState.Attributes["plaintext"], "foo"; got != want {
return fmt.Errorf("Encrypted ciphertext %s; did not decrypt to plaintext %s", iState.Attributes["ciphertext"], iState.Attributes["plaintext"])
}

return nil
}
8 changes: 8 additions & 0 deletions vault/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,14 @@ var (
Resource: authBackendDataSource(),
PathInventory: []string{"/sys/auth"},
},
"vault_transit_encrypt": {
Resource: transitEncryptDataSource(),
PathInventory: []string{"/transit/encrypt/{name}"},
},
"vault_transit_decrypt": {
Resource: transitDecryptDataSource(),
PathInventory: []string{"/transit/decrypt/{name}"},
},
}

ResourceRegistry = map[string]*Description{
Expand Down
37 changes: 37 additions & 0 deletions website/docs/d/transit_decrypt.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
---
layout: "vault"
page_title: "Vault: vault_transit_decrypt data source"
sidebar_current: "docs-vault-datasource-transit-decrypt"
description: |-
Decrypt ciphertext using a Vault Transit encryption key.
---

# vault\_transit\_decrypt

This is a data source which can be used to decrypt ciphertext using a Vault Transit key.

## Example Usage

```hcl
data "vault_transit_decrypt" "test" {
backend = "transit"
key = "test"
ciphertext = "vault:v1:S3GtnJ5GUNCWV+/pdL9+g1Feu/nzAv+RlmTmE91Tu0rBkeIU8MEb2nSspC/1IQ=="
}
```

## Argument Reference

Each document configuration may have one or more `rule` blocks, which each accept the following arguments:

* `key` - (Required) Specifies the name of the transit key to decrypt against.

* `backend` - (Required) The path the transit secret backend is mounted at, with no leading or trailing `/`.

* `ciphertext` - (Required) Ciphertext to be decoded.

* `context` - (Optional) Context for key derivation. This is required if key derivation is enabled for this key.

## Attributes Reference

* `plaintext` - Decrypted plaintext returned from Vault
50 changes: 50 additions & 0 deletions website/docs/d/transit_encrypt.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
---
layout: "vault"
page_title: "Vault: vault_transit_encrypt data source"
sidebar_current: "docs-vault-datasource-transit-encrypt"
description: |-
Encrypts plaintext using a Vault Transit encryption key.
---

# vault\_transit\_encrypt

This is a data source which can be used to encrypt plaintext using a Vault Transit key.

## Example Usage

```hcl
resource "vault_mount" "test" {
path = "transit"
type = "transit"
description = "This is an example mount"
}

resource "vault_transit_secret_backend_key" "test" {
backend = "${vault_mount.test.path}"
name = "test"
}

data "vault_transit_encrypt" "test" {
backend = "${vault_mount.test.path}"
key = "${vault_transit_secret_backend_key.test.name}"
plaintext = "foobar"
}
```

## Argument Reference

Each document configuration may have one or more `rule` blocks, which each accept the following arguments:

* `key` - (Required) Specifies the name of the transit key to encrypt against.

* `backend` - (Required) The path the transit secret backend is mounted at, with no leading or trailing `/`.

* `plaintext` - (Required) Plaintext to be encoded.

* `context` - (Optional) Context for key derivation. This is required if key derivation is enabled for this key.

* `key_version` - (Optional) The version of the key to use for encryption. If not set, uses the latest version. Must be greater than or equal to the key's `min_encryption_version`, if set.

## Attributes Reference

* `ciphertext` - Encrypted ciphertext returned from Vault