From 30d5a57e9be63c0cce5b30c6597fa5d872d3d1d5 Mon Sep 17 00:00:00 2001 From: tmanninger Date: Mon, 19 Dec 2022 08:11:47 +0100 Subject: [PATCH 1/3] Add new resource vault_cert_auth_backend_config --- vault/provider.go | 4 + vault/resource_cert_auth_backend_config.go | 135 +++++++++++++++++ .../resource_cert_auth_backend_config_test.go | 142 ++++++++++++++++++ 3 files changed, 281 insertions(+) create mode 100644 vault/resource_cert_auth_backend_config.go create mode 100644 vault/resource_cert_auth_backend_config_test.go diff --git a/vault/provider.go b/vault/provider.go index 1984b29be..ff914843d 100644 --- a/vault/provider.go +++ b/vault/provider.go @@ -494,6 +494,10 @@ var ( Resource: UpdateSchemaResource(certAuthBackendRoleResource()), PathInventory: []string{"/auth/cert/certs/{name}"}, }, + "vault_cert_auth_backend_config": { + Resource: UpdateSchemaResource(certAuthBackendConfigResource()), + PathInventory: []string{"/auth/cert/config"}, + }, "vault_generic_endpoint": { Resource: UpdateSchemaResource(genericEndpointResource("vault_generic_endpoint")), PathInventory: []string{GenericPath}, diff --git a/vault/resource_cert_auth_backend_config.go b/vault/resource_cert_auth_backend_config.go new file mode 100644 index 000000000..c56854bba --- /dev/null +++ b/vault/resource_cert_auth_backend_config.go @@ -0,0 +1,135 @@ +package vault + +import ( + "fmt" + "log" + "strings" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + + "github.com/hashicorp/terraform-provider-vault/internal/provider" +) + +func certAuthBackendConfigResource() *schema.Resource { + return &schema.Resource{ + Create: certAuthBackendWrite, + Read: ReadWrapper(certAuthBackendRead), + Update: certAuthBackendWrite, + Delete: certAuthBackendDelete, + Exists: certAuthBackendExists, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Schema: map[string]*schema.Schema{ + "backend": { + Type: schema.TypeString, + Optional: true, + Description: "Unique name of the auth backend to configure.", + ForceNew: true, + Default: "cert", + // standardise on no beginning or trailing slashes + StateFunc: func(v interface{}) string { + return strings.Trim(v.(string), "/") + }, + }, + "disable_binding": { + Type: schema.TypeBool, + Optional: true, + Description: "If set, during renewal, skips the matching of presented client identity with the client identity used during login.", + Default: false, + }, + "enable_identity_alias_metadata": { + Type: schema.TypeBool, + Optional: true, + Description: "If set, metadata of the certificate including the metadata corresponding to allowed_metadata_extensions will be stored in the alias", + Default: false, + }, + }, + } +} + +func certAuthBackendWrite(d *schema.ResourceData, meta interface{}) error { + config, e := provider.GetClient(d, meta) + if e != nil { + return e + } + + // if backend comes from the config, it won't have the StateFunc + // applied yet, so we need to apply it again. + backend := d.Get("backend").(string) + disable_binding := d.Get("disable_binding").(bool) + enable_identity_alias_metadata := d.Get("enable_identity_alias_metadata").(bool) + + path := certAuthBackendConfigPath(backend) + + data := map[string]interface{}{ + "disable_binding": disable_binding, + "enable_identity_alias_metadata": enable_identity_alias_metadata, + } + + log.Printf("[DEBUG] Writing cert auth backend config to %q", path) + _, err := config.Logical().Write(path, data) + if err != nil { + return fmt.Errorf("error writing to %q: %s", path, err) + } + log.Printf("[DEBUG] Wrote cert auth backend config to %q", path) + + d.SetId(path) + + return certAuthBackendRead(d, meta) +} + +func certAuthBackendRead(d *schema.ResourceData, meta interface{}) error { + config, e := provider.GetClient(d, meta) + if e != nil { + return e + } + log.Printf("[DEBUG] Reading cert auth backend config") + secret, err := config.Logical().Read(d.Id()) + if err != nil { + return fmt.Errorf("error reading cert auth backend config from %q: %s", d.Id(), err) + } + log.Printf("[DEBUG] Read cert auth backend config") + + if v, ok := secret.Data["disable_binding"]; ok { + d.Set("disable_binding", v) + } + if v, ok := secret.Data["enable_identity_alias_metadata"]; ok { + d.Set("enable_identity_alias_metadata", v) + } + return nil +} + +func certAuthBackendDelete(d *schema.ResourceData, meta interface{}) error { + config, e := provider.GetClient(d, meta) + if e != nil { + return e + } + log.Printf("[DEBUG] Deleting cert auth backend config from %q", d.Id()) + _, err := config.Logical().Delete(d.Id()) + if err != nil { + return fmt.Errorf("error deleting cert auth backend config from %q: %s", d.Id(), err) + } + log.Printf("[DEBUG] Deleted cert auth backend config from %q", d.Id()) + + return nil +} + +func certAuthBackendExists(d *schema.ResourceData, meta interface{}) (bool, error) { + config, e := provider.GetClient(d, meta) + if e != nil { + return false, e + } + log.Printf("[DEBUG] Checking if cert auth backend is configured at %q", d.Id()) + secret, err := config.Logical().Read(d.Id()) + if err != nil { + return true, fmt.Errorf("error checking if cert auth backend is configured at %q: %s", d.Id(), err) + } + log.Printf("[DEBUG] Checked if certAuthBackendRead auth backend is configured at %q", d.Id()) + return secret != nil, nil +} + +func certAuthBackendConfigPath(path string) string { + return "auth/" + strings.Trim(path, "/") + "/config" +} diff --git a/vault/resource_cert_auth_backend_config_test.go b/vault/resource_cert_auth_backend_config_test.go new file mode 100644 index 000000000..fece8bcdd --- /dev/null +++ b/vault/resource_cert_auth_backend_config_test.go @@ -0,0 +1,142 @@ +package vault + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + + "github.com/hashicorp/terraform-provider-vault/internal/provider" + "github.com/hashicorp/terraform-provider-vault/testutil" +) + +func TestAccCertAuthBackendConfig_import(t *testing.T) { + backend := acctest.RandomWithPrefix("cert") + resource.Test(t, resource.TestCase{ + PreCheck: func() { testutil.TestAccPreCheck(t) }, + Providers: testProviders, + CheckDestroy: testAccCheckCertAuthBackendConfigDestroy, + Steps: []resource.TestStep{ + { + Config: testAccCertAuthBackendConfig_basic(backend), + Check: testAccCertAuthBackendConfigCheck_attrs(backend), + }, + { + ResourceName: "vault_cert_auth_backend_config.config", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccCertAuthBackendConfig_basic(t *testing.T) { + backend := acctest.RandomWithPrefix("cert") + resource.Test(t, resource.TestCase{ + Providers: testProviders, + PreCheck: func() { testutil.TestAccPreCheck(t) }, + CheckDestroy: testAccCheckCertAuthBackendConfigDestroy, + Steps: []resource.TestStep{ + { + Config: testAccCertAuthBackendConfig_basic(backend), + Check: testAccCertAuthBackendConfigCheck_attrs(backend), + }, + { + Config: testAccCertAuthBackendConfig_updated(backend), + Check: testAccCertAuthBackendConfigCheck_attrs(backend), + }, + }, + }) +} + +func testAccCheckCertAuthBackendConfigDestroy(s *terraform.State) error { + config := testProvider.Meta().(*provider.ProviderMeta).GetClient() + + for _, rs := range s.RootModule().Resources { + if rs.Type != "vault_cert_auth_backend_config" { + continue + } + secret, err := config.Logical().Read(rs.Primary.ID) + if err != nil { + return fmt.Errorf("error checking for cert auth backend %q config: %s", rs.Primary.ID, err) + } + if secret != nil { + return fmt.Errorf("Cert auth backend %q still configured", rs.Primary.ID) + } + } + return nil +} + +func testAccCertAuthBackendConfig_basic(backend string) string { + return fmt.Sprintf(` +resource "vault_auth_backend" "cert" { + type = "cert" + path = "%s" + description = "Test auth backend for cert backend config" +} + +resource "vault_cert_auth_backend_config" "config" { + disable_binding = false + enable_identity_alias_metadata = true +} +`, backend) +} + +func testAccCertAuthBackendConfigCheck_attrs(backend string) resource.TestCheckFunc { + return func(s *terraform.State) error { + resourceState := s.Modules[0].Resources["vault_cert_auth_backend_config.config"] + 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" { + return fmt.Errorf("expected ID to be %q, got %q", "auth/"+backend+"/config", endpoint) + } + + config := testProvider.Meta().(*provider.ProviderMeta).GetClient() + resp, err := config.Logical().Read(endpoint) + if err != nil { + return fmt.Errorf("error reading back cert auth config from %q: %s", endpoint, err) + } + if resp == nil { + return fmt.Errorf("Cert auth not configured at %q", endpoint) + } + attrs := map[string]string{ + "disable_binding": "disable_binding", + "enable_identity_alias_metadata": "enable_identity_alias_metadata", + } + 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 + } +} + +func testAccCertAuthBackendConfig_updated(backend string) string { + return fmt.Sprintf(` + +resource "vault_auth_backend" "cert" { + type = "cert" + path = "%s" + description = "Test auth backend for cert backend config" +} + +resource "vault_cert_auth_backend_config" "config" { + disable_binding = false + enable_identity_alias_metadata = true" +`, backend) +} From be8c61dc8a311d4cf5d7cd01aaf26b5ce4309c53 Mon Sep 17 00:00:00 2001 From: tmanninger Date: Tue, 20 Dec 2022 10:58:46 +0100 Subject: [PATCH 2/3] Add documentation --- .../docs/r/cert_auth_backend_config.html.md | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 website/docs/r/cert_auth_backend_config.html.md diff --git a/website/docs/r/cert_auth_backend_config.html.md b/website/docs/r/cert_auth_backend_config.html.md new file mode 100644 index 000000000..c29192d3d --- /dev/null +++ b/website/docs/r/cert_auth_backend_config.html.md @@ -0,0 +1,47 @@ +--- +layout: "vault" +page_title: "Vault: cert_auth_backend_config resource" +sidebar_current: "docs-vault-resource-cert-auth-backend-config" +description: |- + Configures the certificate auth backend +--- + +# vault\_cert\_auth\_backend\_config + +Provides configuration for [Cert auth backend within Vault](https://www.vaultproject.io/docs/auth/cert.html). + +## Example Usage + +```hcl +resource "vault_auth_backend" "cert" { + type = "cert" + path = "cert" +} + +resource "vault_cert_auth_backend_config" "config" { + disable_binding = false + enable_identity_alias_metadata = true" +``` + +## Argument Reference + +The following arguments are supported: + +* `namespace` - (Optional) The namespace to provision the resource in. + The value should not contain leading or trailing forward slashes. + The `namespace` is always relative to the provider's configured [namespace](/docs/providers/vault#namespace). + *Available only for Vault Enterprise*. + +* `disable_binding` - (Optional) If set, during renewal, skips the matching of + presented client identity with the client identity used during login. + +* `enable_identity_alias_metadata` - (Optional) If set, metadata of the certificate including + the metadata corresponding to allowed_metadata_extensions will be stored in the alias. + +* `backend` - (Optional) Path to the mounted Cert auth backend + +For more details on the usage of each argument consult the [Vault Cert API documentation](https://www.vaultproject.io/api-docs/auth/cert). + +## Attribute Reference + +No additional attributes are exposed by this resource. From a7900af751d42f26c1ed586eeb51abdf2a498554 Mon Sep 17 00:00:00 2001 From: tmanninger <47749648+tmanninger@users.noreply.github.com> Date: Thu, 3 Aug 2023 08:15:25 +0200 Subject: [PATCH 3/3] Fix ReadWrapper --- vault/resource_cert_auth_backend_config.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vault/resource_cert_auth_backend_config.go b/vault/resource_cert_auth_backend_config.go index c56854bba..e1ab8e6bd 100644 --- a/vault/resource_cert_auth_backend_config.go +++ b/vault/resource_cert_auth_backend_config.go @@ -13,7 +13,7 @@ import ( func certAuthBackendConfigResource() *schema.Resource { return &schema.Resource{ Create: certAuthBackendWrite, - Read: ReadWrapper(certAuthBackendRead), + Read: provider.ReadWrapper(certAuthBackendRead), Update: certAuthBackendWrite, Delete: certAuthBackendDelete, Exists: certAuthBackendExists,