-
Notifications
You must be signed in to change notification settings - Fork 540
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #163 from arnogeurts/ssh-secret-backend-ca
Add SSH secret backend CA resource
- Loading branch information
Showing
5 changed files
with
328 additions
and
0 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
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,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 | ||
} |
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,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), | ||
) | ||
} |
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,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. |
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