-
Notifications
You must be signed in to change notification settings - Fork 552
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add a vault_aws_auth_backend_cert resource that can manage the certificates configured on an AWS auth backend.
- Loading branch information
1 parent
388dbf7
commit 7e59fbe
Showing
310 changed files
with
141,953 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
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,180 @@ | ||
package vault | ||
|
||
import ( | ||
"encoding/base64" | ||
"fmt" | ||
"log" | ||
"regexp" | ||
"strings" | ||
|
||
"github.com/hashicorp/terraform/helper/schema" | ||
"github.com/hashicorp/terraform/helper/validation" | ||
"github.com/hashicorp/vault/api" | ||
) | ||
|
||
var ( | ||
awsAuthBackendCertBackendFromPathRegex = regexp.MustCompile("^auth/(.+)/config/certificate/.+$") | ||
awsAuthBackendCertNameFromPathRegex = regexp.MustCompile("^auth/.+/config/certificate/(.+$)") | ||
) | ||
|
||
func awsAuthBackendCertResource() *schema.Resource { | ||
return &schema.Resource{ | ||
Create: awsAuthBackendCertCreate, | ||
Read: awsAuthBackendCertRead, | ||
Delete: awsAuthBackendCertDelete, | ||
Exists: awsAuthBackendCertExists, | ||
Importer: &schema.ResourceImporter{ | ||
State: schema.ImportStatePassthrough, | ||
}, | ||
|
||
Schema: map[string]*schema.Schema{ | ||
"cert_name": { | ||
Type: schema.TypeString, | ||
Required: true, | ||
Description: "Name of the certificate to configure.", | ||
ForceNew: true, | ||
}, | ||
"aws_public_cert": { | ||
Type: schema.TypeString, | ||
Required: true, | ||
Description: "Base64 encoded AWS Public key required to verify PKCS7 signature of the EC2 instance metadata.", | ||
ForceNew: true, | ||
/*DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool { | ||
return base64.StdEncoding.EncodeToString([]byte(old)) == new || | ||
base64.RawStdEncoding.EncodeToString([]byte(old)) == new || | ||
base64.URLEncoding.EncodeToString([]byte(old)) == new | ||
},*/ | ||
}, | ||
"type": { | ||
Type: schema.TypeString, | ||
Optional: true, | ||
Description: "The type of document that can be verified using the certificate. Must be either \"pkcs7\" or \"identity\".", | ||
ForceNew: true, | ||
Default: "pkcs7", | ||
ValidateFunc: validation.StringInSlice([]string{"pkcs7", "identity"}, false), | ||
}, | ||
"backend": { | ||
Type: schema.TypeString, | ||
Optional: true, | ||
Description: "Unique name of the auth backend to configure.", | ||
ForceNew: true, | ||
Default: "aws", | ||
// standardise on no beginning or trailing slashes | ||
StateFunc: func(v interface{}) string { | ||
return strings.Trim(v.(string), "/") | ||
}, | ||
}, | ||
}, | ||
} | ||
} | ||
|
||
func awsAuthBackendCertCreate(d *schema.ResourceData, meta interface{}) error { | ||
client := meta.(*api.Client) | ||
|
||
backend := d.Get("backend").(string) | ||
certType := d.Get("type").(string) | ||
publicCert := d.Get("aws_public_cert").(string) | ||
name := d.Get("cert_name").(string) | ||
|
||
path := awsAuthBackendCertPath(backend, name) | ||
|
||
_, err := client.Logical().Write(path, map[string]interface{}{ | ||
"aws_public_cert": publicCert, | ||
"type": certType, | ||
}) | ||
|
||
d.SetId(path) | ||
|
||
if err != nil { | ||
d.SetId("") | ||
return fmt.Errorf("Error configuring AWS auth backend cert %q: %s", path, err) | ||
} | ||
|
||
return awsAuthBackendCertRead(d, meta) | ||
} | ||
|
||
func awsAuthBackendCertRead(d *schema.ResourceData, meta interface{}) error { | ||
client := meta.(*api.Client) | ||
|
||
path := d.Id() | ||
|
||
backend, err := awsAuthBackendCertBackendFromPath(path) | ||
if err != nil { | ||
return fmt.Errorf("Invalid path %q for AWS auth backend cert: %s", path, err) | ||
} | ||
|
||
name, err := awsAuthBackendCertNameFromPath(path) | ||
if err != nil { | ||
return fmt.Errorf("Invalid path %q for AWS auth backend cert: %s", err) | ||
} | ||
|
||
resp, err := client.Logical().Read(path) | ||
if err != nil { | ||
return fmt.Errorf("Error reading AWS auth backend cert %q: %s", path, err) | ||
} | ||
if resp == nil { | ||
log.Printf("[WARN] AWS auth backend cert %q not found, removing it from state", path) | ||
d.SetId("") | ||
} | ||
|
||
// the cert response gets back as undecoded base64 | ||
// to keep it simple for people referencing the cert body | ||
// and make sure they get what they expect, we turn it back | ||
// into base64 before putting it in the state | ||
d.Set("aws_public_cert", base64.RawStdEncoding.EncodeToString([]byte(resp.Data["aws_public_cert"].(string)))) | ||
d.Set("type", resp.Data["type"]) | ||
d.Set("backend", backend) | ||
d.Set("cert_name", name) | ||
|
||
return nil | ||
} | ||
|
||
func awsAuthBackendCertDelete(d *schema.ResourceData, meta interface{}) error { | ||
client := meta.(*api.Client) | ||
path := d.Id() | ||
|
||
_, err := client.Logical().Delete(path) | ||
if err != nil { | ||
return fmt.Errorf("Error deleting AWS auth backend cert %q: %s", path, err) | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func awsAuthBackendCertExists(d *schema.ResourceData, meta interface{}) (bool, error) { | ||
client := meta.(*api.Client) | ||
|
||
path := d.Id() | ||
|
||
resp, err := client.Logical().Read(path) | ||
if err != nil { | ||
return true, fmt.Errorf("Error checking for existence of AWS auth backend cert %q: %s", path, err) | ||
} | ||
return resp != nil, nil | ||
} | ||
|
||
func awsAuthBackendCertPath(backend, name string) string { | ||
return "auth/" + strings.Trim(backend, "/") + "/config/certificate/" + strings.Trim(name, "/") | ||
} | ||
|
||
func awsAuthBackendCertNameFromPath(path string) (string, error) { | ||
if !awsAuthBackendCertNameFromPathRegex.MatchString(path) { | ||
return "", fmt.Errorf("no name found") | ||
} | ||
res := awsAuthBackendCertNameFromPathRegex.FindStringSubmatch(path) | ||
if len(res) != 2 { | ||
return "", fmt.Errorf("unexpected number of matches (%d) for name", len(res)) | ||
} | ||
return res[1], nil | ||
} | ||
|
||
func awsAuthBackendCertBackendFromPath(path string) (string, error) { | ||
if !awsAuthBackendCertBackendFromPathRegex.MatchString(path) { | ||
return "", fmt.Errorf("no backend found") | ||
} | ||
res := awsAuthBackendCertBackendFromPathRegex.FindStringSubmatch(path) | ||
if len(res) != 2 { | ||
return "", fmt.Errorf("unexpected number of matches (%d) for backend", len(res)) | ||
} | ||
return res[1], 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,129 @@ | ||
package vault | ||
|
||
import ( | ||
"encoding/base64" | ||
"fmt" | ||
"testing" | ||
|
||
"github.com/hashicorp/terraform/helper/acctest" | ||
"github.com/hashicorp/terraform/helper/resource" | ||
"github.com/hashicorp/terraform/terraform" | ||
"github.com/hashicorp/vault/api" | ||
) | ||
|
||
const testAWSPublicCert = "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUM3VENDQXEwQ0NRQ1d1a2paNVY0YVp6QUpCZ2NxaGtqT09BUURNRnd4Q3pBSkJnTlZCQVlUQWxWVE1Sa3cKRndZRFZRUUlFeEJYWVhOb2FXNW5kRzl1SUZOMFlYUmxNUkF3RGdZRFZRUUhFd2RUWldGMGRHeGxNU0F3SGdZRApWUVFLRXhkQmJXRjZiMjRnVjJWaUlGTmxjblpwWTJWeklFeE1RekFlRncweE1qQXhNRFV4TWpVMk1USmFGdzB6Ck9EQXhNRFV4TWpVMk1USmFNRnd4Q3pBSkJnTlZCQVlUQWxWVE1Sa3dGd1lEVlFRSUV4QlhZWE5vYVc1bmRHOXUKSUZOMFlYUmxNUkF3RGdZRFZRUUhFd2RUWldGMGRHeGxNU0F3SGdZRFZRUUtFeGRCYldGNmIyNGdWMlZpSUZObApjblpwWTJWeklFeE1RekNDQWJjd2dnRXNCZ2NxaGtqT09BUUJNSUlCSHdLQmdRQ2prdmNTMmJiMVZRNHl0LzVlCmloNU9PNmtLL24xTHpsbHI3RDhad3RRUDhmT0VwcDVFMm5nK0Q2VWQxWjFnWWlwcjU4S2ozbnNzU05wSTZiWDMKVnlJUXpLN3dMY2xuZC9Zb3pxTk5tZ0l5WmVjTjdFZ2xLOUlUSEpMUCt4OEZ0VXB0M1FieVlYSmRtVk1lZ042UApodmlZdDVKSC9uWWw0aGgzUGExSEpkc2tnUUlWQUxWSjNFUjExK0tvNHRQNm53dkh3aDYrRVJZUkFvR0JBSTFqCmsrdGtxTVZIdUFGY3ZBR0tvY1Rnc2pKZW02LzVxb216SnVLRG1iSk51OVF4dzNyQW90WGF1OFFlK01CY0psL1UKaGh5MUtIVnBDR2w5ZnVlUTJzNklMMENhTy9idXljVTFDaVlRazQwS05IQ2NIZk5pWmJkbHgxRTlycFVwN2JuRgpsUmEydjFudE1YM2NhUlZEZGJ0UEVXbWR4U0NZc1lGRGs0bVpyT0xCQTRHRUFBS0JnRWJtZXZlNWY4TElFL0dmCk1ObVA5Q001ZW92UU9HeDVobzhXcUQrYVRlYnMrazJ0bjkyQkJQcWVacXBXUmE1UC8ranJkS21sMXF4NGxsSFcKTVhyczNJZ0liNitoVUlCK1M4ZHo4L21tTzBicHI3NlJvWlZDWFlhYjJDWmVkRnV0N3FjM1dVSDkrRVVBSDVtdwp2U2VEQ09VTVlRUjdSOUxJTll3b3VISXppcVFZTUFrR0J5cUdTTTQ0QkFNREx3QXdMQUlVV1hCbGs0MHhUd1N3CjdIWDMyTXhYWXJ1c2U5QUNGQk5HbWRYMlpCclZOR3JOOU4yZjZST2swazlLCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K" | ||
|
||
func TestAccAWSAuthBackendCert_import(t *testing.T) { | ||
backend := acctest.RandomWithPrefix("aws") | ||
name := acctest.RandomWithPrefix("test-cert") | ||
resource.Test(t, resource.TestCase{ | ||
PreCheck: func() { testAccPreCheck(t) }, | ||
Providers: testProviders, | ||
CheckDestroy: testAccCheckAWSAuthBackendCertDestroy, | ||
Steps: []resource.TestStep{ | ||
{ | ||
Config: testAccAWSAuthBackendCertConfig_basic(backend, name), | ||
Check: testAccAWSAuthBackendCertCheck_attrs(backend, name), | ||
}, | ||
{ | ||
ResourceName: "vault_aws_auth_backend_cert.cert", | ||
ImportState: true, | ||
ImportStateVerify: true, | ||
}, | ||
}, | ||
}) | ||
} | ||
|
||
func TestAccAWSAuthBackendCert_basic(t *testing.T) { | ||
backend := acctest.RandomWithPrefix("aws") | ||
name := acctest.RandomWithPrefix("test-cert") | ||
resource.Test(t, resource.TestCase{ | ||
PreCheck: func() { testAccPreCheck(t) }, | ||
Providers: testProviders, | ||
CheckDestroy: testAccCheckAWSAuthBackendCertDestroy, | ||
Steps: []resource.TestStep{ | ||
{ | ||
Config: testAccAWSAuthBackendCertConfig_basic(backend, name), | ||
Check: testAccAWSAuthBackendCertCheck_attrs(backend, name), | ||
}, | ||
}, | ||
}) | ||
} | ||
|
||
func testAccCheckAWSAuthBackendCertDestroy(s *terraform.State) error { | ||
client := testProvider.Meta().(*api.Client) | ||
for _, rs := range s.RootModule().Resources { | ||
if rs.Type != "vault_aws_auth_backend_cert" { | ||
continue | ||
} | ||
secret, err := client.Logical().Read(rs.Primary.ID) | ||
if err != nil { | ||
return fmt.Errorf("Error checking for AWS Auth Backend certificate %q: %s", rs.Primary.ID, err) | ||
} | ||
if secret != nil { | ||
return fmt.Errorf("AWS auth backend certificate %q still exists", rs.Primary.ID) | ||
} | ||
} | ||
return nil | ||
} | ||
|
||
func testAccAWSAuthBackendCertConfig_basic(backend, name string) string { | ||
return fmt.Sprintf(` | ||
resource "vault_auth_backend" "aws" { | ||
path = "%s" | ||
type = "aws" | ||
description = "Test auth backend for AWS backend certificates" | ||
} | ||
resource "vault_aws_auth_backend_cert" "cert" { | ||
backend = "${vault_auth_backend.aws.path}" | ||
cert_name = "%s" | ||
aws_public_cert = "%s" | ||
type = "pkcs7" | ||
}`, backend, name, testAWSPublicCert) | ||
} | ||
|
||
func testAccAWSAuthBackendCertCheck_attrs(backend, name string) resource.TestCheckFunc { | ||
return func(s *terraform.State) error { | ||
resourceState := s.Modules[0].Resources["vault_aws_auth_backend_cert.cert"] | ||
if resourceState == nil { | ||
return fmt.Errorf("resource not found in state") | ||
} | ||
|
||
instanceState := resourceState.Primary | ||
if instanceState == nil { | ||
return fmt.Errorf("resource has no primary instance") | ||
} | ||
|
||
endpoint := instanceState.ID | ||
|
||
if endpoint != "auth/"+backend+"/config/certificate/"+name { | ||
return fmt.Errorf("expected ID to be %q, got %q", "auth/"+backend+"/config/certificate/"+name, endpoint) | ||
} | ||
|
||
client := testProvider.Meta().(*api.Client) | ||
resp, err := client.Logical().Read(endpoint) | ||
if err != nil { | ||
return fmt.Errorf("error reading back AWS auth certificate from %q: %s", endpoint, err) | ||
} | ||
if resp == nil { | ||
return fmt.Errorf("AWS auth certificate not configured at %q", endpoint) | ||
} | ||
|
||
// the read function re-encodes the response in state to match the config | ||
resp.Data["aws_public_cert"] = base64.RawStdEncoding.EncodeToString([]byte(resp.Data["aws_public_cert"].(string))) | ||
attrs := map[string]string{ | ||
"aws_public_cert": "aws_public_cert", | ||
"type": "type", | ||
} | ||
for stateAttr, apiAttr := range attrs { | ||
if resp.Data[apiAttr] == nil && instanceState.Attributes[stateAttr] == "" { | ||
continue | ||
} | ||
if resp.Data[apiAttr] != instanceState.Attributes[stateAttr] { | ||
return fmt.Errorf("Expected %s (%s) of %q to be %q, got %q", apiAttr, stateAttr, endpoint, instanceState.Attributes[stateAttr], resp.Data[apiAttr]) | ||
} | ||
} | ||
return nil | ||
} | ||
} |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
Oops, something went wrong.