Skip to content

Commit

Permalink
Merge pull request #163 from arnogeurts/ssh-secret-backend-ca
Browse files Browse the repository at this point in the history
Add SSH secret backend CA resource
  • Loading branch information
Becca Petrin authored Oct 5, 2018
2 parents 7e5de10 + d3c85b4 commit 04893e7
Show file tree
Hide file tree
Showing 5 changed files with 328 additions and 0 deletions.
1 change: 1 addition & 0 deletions vault/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ func Provider() terraform.ResourceProvider {
"vault_policy": policyResource(),
"vault_mount": mountResource(),
"vault_audit": auditResource(),
"vault_ssh_secret_backend_ca": sshSecretBackendCAResource(),
},
}
}
Expand Down
135 changes: 135 additions & 0 deletions vault/resource_ssh_secret_backend_ca.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
package vault

import (
"fmt"
"github.com/hashicorp/terraform/helper/schema"
"github.com/hashicorp/vault/api"
"log"
"strings"
)

func sshSecretBackendCAResource() *schema.Resource {
return &schema.Resource{
Create: sshSecretBackendCACreate,
Read: sshSecretBackendCARead,
Delete: sshSecretBackendCADelete,
Exists: sshSecretBackendCAExists,
Importer: &schema.ResourceImporter{
State: schema.ImportStatePassthrough,
},

Schema: map[string]*schema.Schema{
"backend": {
Type: schema.TypeString,
Optional: true,
Default: "ssh",
ForceNew: true,
Description: "The path of the SSH Secret Backend where the CA should be configured",
// standardise on no beginning or trailing slashes
StateFunc: func(v interface{}) string {
return strings.Trim(v.(string), "/")
},
},
"generate_signing_key": {
Type: schema.TypeBool,
Optional: true,
ForceNew: true,
Description: "Whether Vault should generate the signing key pair internally.",
},
"private_key": {
Type: schema.TypeString,
Optional: true,
ForceNew: true,
Sensitive: true,
Computed: true,
Description: "Private key part the SSH CA key pair; required if generate_signing_key is false.",
},
"public_key": {
Type: schema.TypeString,
Optional: true,
ForceNew: true,
Computed: true,
Description: "Public key part the SSH CA key pair; required if generate_signing_key is false.",
},
},
}
}

func sshSecretBackendCACreate(d *schema.ResourceData, meta interface{}) error {
client := meta.(*api.Client)
backend := d.Get("backend").(string)

data := make(map[string]interface{})
if generateSigningKey, ok := d.Get("generate_signing_key").(bool); ok {
data["generate_signing_key"] = generateSigningKey
}
if privateKey, ok := d.Get("private_key").(string); ok {
data["private_key"] = privateKey
}
if publicKey, ok := d.Get("public_key").(string); ok {
data["public_key"] = publicKey
}

log.Printf("[DEBUG] Writing CA information on SSH backend %q", backend)
_, err := client.Logical().Write(backend+"/config/ca", data)
if err != nil {
return fmt.Errorf("Error writing CA information for backend %q: %s", backend, err)
}
log.Printf("[DEBUG] Written CA information on SSH backend %q", backend)

d.SetId(backend)
return sshSecretBackendCARead(d, meta)
}

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

backend := d.Id()

log.Printf("[DEBUG] Reading CA information from SSH backend %q", backend)
secret, err := client.Logical().Read(backend + "/config/ca")
if err != nil {
return fmt.Errorf("Error reading CA information from SSH backend %q: %s", backend, err)
}
log.Printf("[DEBUG] Read CA information from SSH backend %q", backend)
if secret == nil {
log.Printf("[WARN] CA information not found in SSH backend %q, removing from state", backend)
d.SetId("")
return nil
}
d.Set("public_key", secret.Data["public_key"])
d.Set("backend", backend)

// the API doesn't return private_key and generate_signing_key
// So... if they drift, they drift.

return nil
}

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

backend := d.Id()
log.Printf("[DEBUG] Deleting CA configuration for SSH backend %q", backend)
_, err := client.Logical().Delete(backend + "/config/ca")
if err != nil {
return fmt.Errorf("Error deleting CA configuration for SSH backend %q: %s", backend, err)
}
log.Printf("[DEBUG] Deleted CA configuration for SSH backend %q", backend)

return nil
}

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

backend := d.Id()
log.Printf("[DEBUG] Checking if CA information exists for backend %q ", backend)
secret, err := client.Logical().Read(backend + "/config/ca")
if err != nil {
return true, fmt.Errorf("Error checking if CA information exists for backend %q: %s", backend, err)
}
log.Printf("[DEBUG] Checked if CA information exists for backend %q", backend)

return secret != nil, nil
}
144 changes: 144 additions & 0 deletions vault/resource_ssh_secret_backend_ca_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
package vault

import (
"fmt"
"testing"

"github.com/hashicorp/terraform/helper/acctest"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
"github.com/hashicorp/vault/api"
)

func TestAccSSHSecretBackendCA_basic(t *testing.T) {
backend := "ssh-" + acctest.RandString(10)

resource.Test(t, resource.TestCase{
Providers: testProviders,
PreCheck: func() { testAccPreCheck(t) },
CheckDestroy: testAccCheckSSHSecretBackendCADestroy,
Steps: []resource.TestStep{
{
Config: testAccSSHSecretBackendCAConfigGenerated(backend),
Check: testAccSSHSecretBackendCACheck(backend),
},
},
})
}

func TestAccSSHSecretBackendCA_provided(t *testing.T) {
backend := "ssh-" + acctest.RandString(10)

resource.Test(t, resource.TestCase{
Providers: testProviders,
PreCheck: func() { testAccPreCheck(t) },
CheckDestroy: testAccCheckSSHSecretBackendCADestroy,
Steps: []resource.TestStep{
{
Config: testAccSSHSecretBackendCAConfigProvided(backend),
Check: testAccSSHSecretBackendCACheck(backend),
},
},
})
}

func TestAccSSHSecretBackend_import(t *testing.T) {
backend := "ssh-" + acctest.RandString(10)
resource.Test(t, resource.TestCase{
Providers: testProviders,
PreCheck: func() { testAccPreCheck(t) },
Steps: []resource.TestStep{
{
Config: testAccSSHSecretBackendCAConfigGenerated(backend),
Check: testAccSSHSecretBackendCACheck(backend),
},
{
ResourceName: "vault_ssh_secret_backend_ca.test",
ImportState: true,
// state cannot be verified since generate_signing_key and private_key are not returned by the API
ImportStateVerify: false,
},
},
})
}

func testAccCheckSSHSecretBackendCADestroy(s *terraform.State) error {
client := testProvider.Meta().(*api.Client)

for _, rs := range s.RootModule().Resources {
if rs.Type != "vault_ssh_secret_backend_ca" {
continue
}
backend := rs.Primary.ID
secret, err := client.Logical().Read(backend + "/config/ca")
if err != nil {
return err
}
if secret != nil {
return fmt.Errorf("CA information still exists for backend %q", rs.Primary.ID)
}
}
return nil
}

func testAccSSHSecretBackendCAConfigGenerated(backend string) string {
return fmt.Sprintf(`
resource "vault_mount" "test" {
type = "ssh"
path = "%s"
}
resource "vault_ssh_secret_backend_ca" "test" {
backend = "${vault_mount.test.path}"
generate_signing_key = true
}`, backend)
}

func testAccSSHSecretBackendCAConfigProvided(backend string) string {
return fmt.Sprintf(`
resource "vault_mount" "test" {
type = "ssh"
path = "%s"
}
resource "vault_ssh_secret_backend_ca" "test" {
backend = "${vault_mount.test.path}"
private_key = <<EOF
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEAu/5/sDSqVMV6USjgPkGcHM9X3ENtgMU4AFrKAMCV85qbGhgR
w9zJruvIxT695/0kLH7UqeEfMxlY4XBuHkWRuU4Djd5cALelHJ8zmG+ZlaRcrQjV
RM0Pvn2D3BQsTSWIIWSzLmLuaYOGBMrrgBlGDrVLW88pksYiPTr4BdxqK/TzOwLK
YjwlT+XV3HQLxr6a7+SHk3//PWqQhhIZs+uaOSsg5xSBuUx6EGJIqSWBUiBhB6PJ
5ndGVDRZkiSmul6lp/4WcuvAkXiKqHCRCnNAcBAAhFUEnL0JqQ9g5QmIwEGc+L4t
g3v4Qi+IUwlk8LRkkrcEgAjxi04JO2XXBPzlGQIDAQABAoIBAA6Dw9ATAOOyq5MA
mO+1mRwQVjRHcHj0wTIl0Frmg61fToJhQV3h+iBrTAEOqxLyVIyq7jh/jS0g09/0
Ekx8CphIEbYuaOQVScY/9HfchfsryYwClpTNUF3gywF+/TynnS8W207FjKrQ4NQV
5sDpMqOIE91uzULr0VDw8J1jOz9RdEuFL1SkUwrAH8Ki1DePrU3Bag+tHel3g/0u
DLYsw//cIQ66vUxW0JIHh0IB8WQlYC/IuE+GmLcHbfFpyRFRfrHqy/F0aLACQWRt
bCePdD953b3x9sCvrftOhkD/ar0RPInWgjSJ3yycsa5eIQQrXzgA0QDN3A7pHeqP
czUZk9ECgYEA7SmGtNGfIYsPdZ0T5X87CtzkMi0sNNHkgxegh5BzCxs4eExrPhQz
mfH2OZhmJv3cuMQWkCnRoW8JA/fUtBxN/nJcw8PeCdH2OgQxu2LfdiOXE35E4Jhh
+4GUSTd8/Qgg1QuSRAAWRhDcRUrUljiYQqJVf7/xGkp1vCs7RKSe4ycCgYEAyu0v
cY0www6K93tZU7EoP9zUoW+tkDVrEDq8WkXMorci+p9TdRH3o/jGp/sIcHv90oA3
nhnLEhf1DLTWMmjp/+DC0pHk7cWiOATWLda/6rt3pmfSyWxAaN4yXLX2zd7IQ2t/
5OgspE5FYnJ2AJay7inaSJBkF125f6wgKVFaHb8CgYEAqIHZ4X4Th/zLVjDuYyDc
baJ3TSOFhl4f8/kEqW28IAcOP4Nkq24lH9uorFGZO1kiy/Efav0boo1HJZegfPyj
egf922a+y9FwFtbGEzN0PPear1IHVGFRNSdjmgYf+5Ub5uPa4BADw3LVXzKFC9tY
a/f1sdhKUfjX4IQDD4m8Dv8CgYBvfz0HLiWxtwbiDfM5yegslsB55yu9RayK4Urm
at2SNf/RJsOrWnDvtlwopgSwEWCYTXzBsLhkO6eYELB0SDLyNeO14RWhE2sbToUD
8K/IYLLQStGFfKYzOIsBZ7WwzgzJBoLiGjOVH7B99BgkIKk1tOdL4ZItSIEIxmFx
clKKbwKBgG5E3n8E+miHJ/5WqZ3tCpiEAwn1Mwj5XFZnJ9LUq7fmHwr3IP3PkyF4
FfAMjrELscc0jJu32AbLZiI6/mwztMu9cN16owccnN3BwBd5JCqdGv8Lxi93rsha
DYdgQ3utnZSfVvB0VpmW0YBysI/vLa0+6b58ubme1Ko93AdJsP0e
-----END RSA PRIVATE KEY-----
EOF
public_key = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC7/n+wNKpUxXpRKOA+QZwcz1fcQ22AxTgAWsoAwJXzmpsaGBHD3Mmu68jFPr3n/SQsftSp4R8zGVjhcG4eRZG5TgON3lwAt6UcnzOYb5mVpFytCNVEzQ++fYPcFCxNJYghZLMuYu5pg4YEyuuAGUYOtUtbzymSxiI9OvgF3Gor9PM7AspiPCVP5dXcdAvGvprv5IeTf/89apCGEhmz65o5KyDnFIG5THoQYkipJYFSIGEHo8nmd0ZUNFmSJKa6XqWn/hZy68CReIqocJEKc0BwEACEVQScvQmpD2DlCYjAQZz4vi2De/hCL4hTCWTwtGSStwSACPGLTgk7ZdcE/OUZ [email protected]"
}`, backend)
}

func testAccSSHSecretBackendCACheck(backend string) resource.TestCheckFunc {
return resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttrSet("vault_ssh_secret_backend_ca.test", "public_key"),
resource.TestCheckResourceAttr("vault_ssh_secret_backend_ca.test", "backend", backend),
)
}
44 changes: 44 additions & 0 deletions website/docs/r/ssh_secret_backend_ca.html.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
---
layout: "vault"
page_title: "Vault: vault_ssh_secret_backend_ca resource"
sidebar_current: "docs-vault-resource-ssh-secret-backend-ca"
description: |-
Managing CA information in an SSH secret backend in Vault
---

# vault\_ssh\_secret\_backend\_ca

Provides a resource to manage CA information in an SSH secret backend
[SSH secret backend within Vault](https://www.vaultproject.io/docs/secrets/ssh/index.html).

## Example Usage

```hcl
resource "vault_mount" "example" {
type = "ssh"
}
resource "vault_ssh_secret_backend_ca" "foo" {
backend = "${vault_mount.example.path}"
}
```

## Argument Reference

The following arguments are supported:

* `backend` - (Optional) The path where the SSH secret backend is mounted. Defaults to 'ssh'

* `generate_signing_key` - (Optional) Whether Vault should generate the signing key pair internally. Defaults to true

* `public_key` - (Optional) The public key part the SSH CA key pair; required if generate_signing_key is false.

* `private_key` - (Optional) The private key part the SSH CA key pair; required if generate_signing_key is false.

~> **Important** Because Vault does not support reading the private_key back from the API, Terraform cannot detect
and correct drift on `private_key`. Changing the values, however, _will_ overwrite the previously stored values.


## Attributes Reference

No additional attributes are exposed by this resource.
4 changes: 4 additions & 0 deletions website/vault.erb
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,10 @@
<li<%= sidebar_current("docs-vault-resource-token-auth-backend-role") %>>
<a href="/docs/providers/vault/r/token_auth_backend_role.html">vault_token_auth_backend_role</a>
</li>
<li<%= sidebar_current("docs-vault-resource-ssh-secret-backend-ca") %>>
<a href="/docs/providers/vault/r/ssh_secret_backend_ca.html">ssh_secret_backend_ca</a>
</li>

</ul>
</li>

Expand Down

0 comments on commit 04893e7

Please sign in to comment.