From 4b62925e566c313461293a1d5238e7e27b669af7 Mon Sep 17 00:00:00 2001 From: Rachel Culpepper <84159930+rculpepper@users.noreply.github.com> Date: Mon, 9 Dec 2024 12:23:20 -0600 Subject: [PATCH] Add support for CMPv2 configuration (#2330) * add resource and data source for CMPv2 config * fix test * add doc links and changelog * Update website/docs/d/pki_secret_backend_config_cmpv2.html.md Co-authored-by: Steven Clark * Update website/docs/d/pki_secret_backend_config_cmpv2.html.md Co-authored-by: Steven Clark * Update website/docs/d/pki_secret_backend_config_cmpv2.html.md Co-authored-by: Steven Clark * Update website/docs/d/pki_secret_backend_config_cmpv2.html.md Co-authored-by: Steven Clark * add docs * fix changelog --------- Co-authored-by: Steven Clark --- CHANGELOG.md | 1 + ..._source_pki_secret_backend_config_cmpv2.go | 150 +++++++++++++++ ...ce_pki_secret_backend_config_cmpv2_test.go | 52 ++++++ vault/provider.go | 8 + ...esource_pki_secret_backend_config_cmpv2.go | 162 +++++++++++++++++ ...ce_pki_secret_backend_config_cmpv2_test.go | 172 ++++++++++++++++++ ...urce_pki_secret_backend_config_est_test.go | 10 +- .../d/pki_secret_backend_config_cmpv2.html.md | 64 +++++++ .../r/pki_secret_backend_config_cmpv2.html.md | 95 ++++++++++ website/vault.erb | 8 + 10 files changed, 715 insertions(+), 7 deletions(-) create mode 100644 vault/data_source_pki_secret_backend_config_cmpv2.go create mode 100644 vault/data_source_pki_secret_backend_config_cmpv2_test.go create mode 100644 vault/resource_pki_secret_backend_config_cmpv2.go create mode 100644 vault/resource_pki_secret_backend_config_cmpv2_test.go create mode 100644 website/docs/d/pki_secret_backend_config_cmpv2.html.md create mode 100644 website/docs/r/pki_secret_backend_config_cmpv2.html.md diff --git a/CHANGELOG.md b/CHANGELOG.md index add41ebaf..4b1eb854b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ FEATURES: * Add support for ACME configuration with the `vault_pki_secret_backend_config_acme` resource. Requires Vault 1.14+ ([#2157](https://github.com/hashicorp/terraform-provider-vault/pull/2157)). * Update `vault_pki_secret_backend_role` to support the `cn_validations` role field ([#1820](https://github.com/hashicorp/terraform-provider-vault/pull/1820)). * Add new resource `vault_pki_secret_backend_acme_eab` to manage PKI ACME external account binding tokens. Requires Vault 1.14+. ([#2367](https://github.com/hashicorp/terraform-provider-vault/pull/2367)) +* Add new data source and resource `vault_pki_secret_backend_config_cmpv2`. Requires Vault 1.18+. *Available only for Vault Enterprise* ([#2330](https://github.com/hashicorp/terraform-provider-vault/pull/2330)) ## 4.5.0 (Nov 19, 2024) diff --git a/vault/data_source_pki_secret_backend_config_cmpv2.go b/vault/data_source_pki_secret_backend_config_cmpv2.go new file mode 100644 index 000000000..75954b3ad --- /dev/null +++ b/vault/data_source_pki_secret_backend_config_cmpv2.go @@ -0,0 +1,150 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package vault + +import ( + "context" + "errors" + "fmt" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-provider-vault/internal/consts" + "github.com/hashicorp/terraform-provider-vault/internal/provider" + "github.com/hashicorp/vault/api" + "strings" +) + +func pkiSecretBackendConfigCMPV2DataSource() *schema.Resource { + return &schema.Resource{ + Description: "Reads Vault PKI CMPv2 configuration", + ReadContext: provider.ReadContextWrapper(readPKISecretBackendConfigCMPV2), + Schema: map[string]*schema.Schema{ + consts.FieldBackend: { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: "Path where PKI engine is mounted", + }, + consts.FieldEnabled: { + Type: schema.TypeBool, + Computed: true, + Description: "Specifies whether CMPv2 is enabled", + }, + consts.FieldDefaultPathPolicy: { + Type: schema.TypeString, + Computed: true, + Description: "Can be sign-verbatim or a role given by role:", + }, + consts.FieldAuthenticators: { + Type: schema.TypeList, + Computed: true, + Description: "Lists the mount accessors CMPv2 should delegate authentication requests towards", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "cert": { + Type: schema.TypeMap, + Optional: true, + Description: "The accessor and cert_role properties for cert auth backends", + }, + }, + }, + }, + consts.FieldEnableSentinelParsing: { + Type: schema.TypeBool, + Computed: true, + Description: "If set, parse out fields from the provided CSR making them available for Sentinel policies", + }, + consts.FieldAuditFields: { + Type: schema.TypeList, + Computed: true, + Description: "Fields parsed from the CSR that appear in the audit and can be used by sentinel policies", + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + consts.FieldLastUpdated: { + Type: schema.TypeString, + Computed: true, + Description: "A read-only timestamp representing the last time the configuration was updated", + }, + }, + } +} + +func readPKISecretBackendConfigCMPV2(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + if err := verifyPkiEstFeatureSupported(meta); err != nil { + return diag.FromErr(err) + } + + client, err := provider.GetClient(d, meta) + if err != nil { + return diag.FromErr(fmt.Errorf("failed getting client: %w", err)) + } + + backend := d.Get(consts.FieldBackend).(string) + path := pkiSecretBackendConfigCMPV2Path(backend) + + if err := readCMPV2Config(ctx, d, client, path); err != nil { + return diag.FromErr(err) + } + + return nil +} + +func readCMPV2Config(ctx context.Context, d *schema.ResourceData, client *api.Client, path string) error { + resp, err := client.Logical().ReadWithContext(ctx, path) + if err != nil { + return fmt.Errorf("error reading from Vault: %w", err) + } + if resp == nil { + return fmt.Errorf("got nil response from Vault from path: %q", path) + } + + d.SetId(path) + + keyComputedFields := []string{ + consts.FieldEnabled, + consts.FieldDefaultPathPolicy, + consts.FieldEnableSentinelParsing, + consts.FieldAuditFields, + consts.FieldLastUpdated, + } + + for _, k := range keyComputedFields { + if fieldVal, ok := resp.Data[k]; ok { + if err := d.Set(k, fieldVal); err != nil { + return fmt.Errorf("failed setting field [%s] with val [%s]: %w", k, fieldVal, err) + } + } + } + + if authenticators, authOk := resp.Data[consts.FieldAuthenticators]; authOk { + if err := d.Set(consts.FieldAuthenticators, []interface{}{authenticators}); err != nil { + return fmt.Errorf("failed setting field [%s] with val [%s]: %w", consts.FieldAuthenticators, authenticators, err) + } + } + + return nil +} + +// verifyPkiCMPV2FeatureSupported verifies that we are talking to a Vault enterprise edition +// and its version 1.18.0 or higher, returns nil if the above is met, otherwise an error +func verifyPkiCMPV2FeatureSupported(meta interface{}) error { + currentVersion := meta.(*provider.ProviderMeta).GetVaultVersion() + + minVersion := provider.VaultVersion118 + if !provider.IsAPISupported(meta, minVersion) { + return fmt.Errorf("feature not enabled on current Vault version. min version required=%s; "+ + "current vault version=%s", minVersion, currentVersion) + } + + if !provider.IsEnterpriseSupported(meta) { + return errors.New("feature requires Vault Enterprise") + } + return nil +} + +func pkiSecretBackendConfigCMPV2Path(backend string) string { + return strings.Trim(backend, "/") + "/config/cmp" +} diff --git a/vault/data_source_pki_secret_backend_config_cmpv2_test.go b/vault/data_source_pki_secret_backend_config_cmpv2_test.go new file mode 100644 index 000000000..eb2092f64 --- /dev/null +++ b/vault/data_source_pki_secret_backend_config_cmpv2_test.go @@ -0,0 +1,52 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +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-provider-vault/internal/consts" + "github.com/hashicorp/terraform-provider-vault/internal/provider" + "github.com/hashicorp/terraform-provider-vault/testutil" +) + +func TestAccDataSourcePKISecretConfigCMPV2(t *testing.T) { + backend := acctest.RandomWithPrefix("tf-test-pki-backend") + dataName := "data.vault_pki_secret_backend_config_cmpv2.test" + resource.Test(t, resource.TestCase{ + ProviderFactories: providerFactories, + PreCheck: func() { + testutil.TestAccPreCheck(t) + testutil.TestEntPreCheck(t) + SkipIfAPIVersionLT(t, testProvider.Meta(), provider.VaultVersion118) + }, + Steps: []resource.TestStep{ + { + Config: testPKISecretEmptyCMPV2ConfigDataSource(backend), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(dataName, consts.FieldBackend, backend), + resource.TestCheckResourceAttrSet(dataName, consts.FieldEnabled), + resource.TestCheckResourceAttrSet(dataName, consts.FieldEnableSentinelParsing), + resource.TestCheckResourceAttrSet(dataName, consts.FieldLastUpdated), + ), + }, + }, + }) +} + +func testPKISecretEmptyCMPV2ConfigDataSource(path string) string { + return fmt.Sprintf(` +resource "vault_mount" "test" { + path = "%s" + type = "pki" + description = "PKI secret engine mount" +} + +data "vault_pki_secret_backend_config_cmpv2" "test" { + backend = vault_mount.test.path +}`, path) +} diff --git a/vault/provider.go b/vault/provider.go index abdee04da..1a1ced330 100644 --- a/vault/provider.go +++ b/vault/provider.go @@ -167,6 +167,10 @@ var ( Resource: UpdateSchemaResource(raftAutopilotStateDataSource()), PathInventory: []string{"/sys/storage/raft/autopilot/state"}, }, + "vault_pki_secret_backend_config_cmpv2": { + Resource: UpdateSchemaResource(pkiSecretBackendConfigCMPV2DataSource()), + PathInventory: []string{"/pki/config/cmp"}, + }, "vault_pki_secret_backend_config_est": { Resource: UpdateSchemaResource(pkiSecretBackendConfigEstDataSource()), PathInventory: []string{"/pki/config/est"}, @@ -591,6 +595,10 @@ var ( Resource: UpdateSchemaResource(pkiSecretBackendConfigClusterResource()), PathInventory: []string{"/pki/config/cluster"}, }, + "vault_pki_secret_backend_config_cmpv2": { + Resource: UpdateSchemaResource(pkiSecretBackendConfigCMPV2Resource()), + PathInventory: []string{"/pki/config/cmp"}, + }, "vault_pki_secret_backend_config_est": { Resource: UpdateSchemaResource(pkiSecretBackendConfigEstResource()), PathInventory: []string{"/pki/config/est"}, diff --git a/vault/resource_pki_secret_backend_config_cmpv2.go b/vault/resource_pki_secret_backend_config_cmpv2.go new file mode 100644 index 000000000..40530903d --- /dev/null +++ b/vault/resource_pki_secret_backend_config_cmpv2.go @@ -0,0 +1,162 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package vault + +import ( + "context" + "fmt" + "log" + "strings" + + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-provider-vault/internal/consts" + "github.com/hashicorp/terraform-provider-vault/internal/provider" +) + +func pkiSecretBackendConfigCMPV2Resource() *schema.Resource { + return &schema.Resource{ + Description: "Manages Vault PKI CMPv2 configuration", + CreateContext: provider.MountCreateContextWrapper(pkiSecretBackendConfigCMPV2Write, provider.VaultVersion118), + UpdateContext: pkiSecretBackendConfigCMPV2Write, + ReadContext: pkiSecretBackendConfigCMPV2Read, + DeleteContext: pkiSecretBackendConfigCMPV2Delete, + Importer: &schema.ResourceImporter{ + StateContext: schema.ImportStatePassthroughContext, + }, + + Schema: map[string]*schema.Schema{ + consts.FieldBackend: { + Type: schema.TypeString, + Required: true, + Description: "The PKI secret backend the resource belongs to", + ForceNew: true, + }, + consts.FieldEnabled: { + Type: schema.TypeBool, + Optional: true, + Description: "Specifies whether CMPv2 is enabled", + }, + consts.FieldDefaultPathPolicy: { + Type: schema.TypeString, + Optional: true, + Description: "Can be sign-verbatim or a role given by role:", + }, + consts.FieldAuthenticators: { + Type: schema.TypeList, + Optional: true, + Computed: true, + Description: "Lists the mount accessors CMPv2 should delegate authentication requests towards", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "cert": { + Type: schema.TypeMap, + Optional: true, + }, + }, + }, + MaxItems: 1, + }, + consts.FieldEnableSentinelParsing: { + Type: schema.TypeBool, + Optional: true, + Description: "If set, parse out fields from the provided CSR making them available for Sentinel policies", + }, + consts.FieldAuditFields: { + Type: schema.TypeList, + Optional: true, + Computed: true, + Description: "Fields parsed from the CSR that appear in the audit and can be used by sentinel policies", + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + consts.FieldLastUpdated: { + Type: schema.TypeString, + Computed: true, // read-only property + Description: "A read-only timestamp representing the last time the configuration was updated", + }, + }, + } +} + +func pkiSecretBackendConfigCMPV2Write(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + if err := verifyPkiCMPV2FeatureSupported(meta); err != nil { + return diag.FromErr(err) + } + + client, e := provider.GetClient(d, meta) + if e != nil { + return diag.FromErr(e) + } + + backend := d.Get(consts.FieldBackend).(string) + path := pkiSecretBackendConfigCMPV2Path(backend) + + fieldsToSet := []string{ + consts.FieldEnabled, + consts.FieldDefaultPathPolicy, + consts.FieldEnableSentinelParsing, + consts.FieldAuditFields, + } + + data := map[string]interface{}{} + for _, field := range fieldsToSet { + if val, ok := d.GetOk(field); ok { + data[field] = val + } + } + + if authenticatorsRaw, ok := d.GetOk(consts.FieldAuthenticators); ok { + authenticators := authenticatorsRaw.([]interface{}) + var authenticator interface{} + if len(authenticators) > 0 { + authenticator = authenticators[0] + } + + data[consts.FieldAuthenticators] = authenticator + } + + log.Printf("[DEBUG] Updating CMPv2 config on PKI secret backend %q:\n%v", backend, data) + _, err := client.Logical().WriteWithContext(ctx, path, data) + if err != nil { + return diag.Errorf("error updating CMPv2 config for PKI secret backend %q: %s", backend, err) + } + log.Printf("[DEBUG] Updated CMPv2 config on PKI secret backend %q", backend) + + d.SetId(path) + + return pkiSecretBackendConfigCMPV2Read(ctx, d, meta) +} + +func pkiSecretBackendConfigCMPV2Read(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + id := d.Id() + if id == "" { + return diag.FromErr(fmt.Errorf("no path set for import, id=%q", id)) + } + + backend := strings.TrimSuffix(id, "/config/cmp") + if err := d.Set("backend", backend); err != nil { + return diag.FromErr(fmt.Errorf("failed setting field [%s] with value [%v]: %w", "backend", backend, err)) + } + + if err := verifyPkiCMPV2FeatureSupported(meta); err != nil { + return diag.FromErr(err) + } + + client, err := provider.GetClient(d, meta) + if err != nil { + return diag.FromErr(fmt.Errorf("failed getting client: %w", err)) + } + + if err := readCMPV2Config(ctx, d, client, id); err != nil { + return diag.FromErr(err) + } + return nil +} + +func pkiSecretBackendConfigCMPV2Delete(_ context.Context, _ *schema.ResourceData, _ interface{}) diag.Diagnostics { + // There isn't any delete API for the CMPv2 config. + return nil +} diff --git a/vault/resource_pki_secret_backend_config_cmpv2_test.go b/vault/resource_pki_secret_backend_config_cmpv2_test.go new file mode 100644 index 000000000..fb059d29a --- /dev/null +++ b/vault/resource_pki_secret_backend_config_cmpv2_test.go @@ -0,0 +1,172 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +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-provider-vault/internal/consts" + "github.com/hashicorp/terraform-provider-vault/internal/provider" + "github.com/hashicorp/terraform-provider-vault/testutil" +) + +func TestAccPKISecretBackendConfigCMPV2_Empty(t *testing.T) { + t.Parallel() + + backend := acctest.RandomWithPrefix("tf-test-pki") + resourceType := "vault_pki_secret_backend_config_cmpv2" + resourceBackend := resourceType + ".test" + dataName := "data.vault_pki_secret_backend_config_cmpv2.test" + + resource.Test(t, resource.TestCase{ + ProviderFactories: providerFactories, + PreCheck: func() { + testutil.TestAccPreCheck(t) + testutil.TestEntPreCheck(t) + SkipIfAPIVersionLT(t, testProvider.Meta(), provider.VaultVersion118) + }, + CheckDestroy: testCheckMountDestroyed(resourceType, consts.MountTypePKI, consts.FieldBackend), + Steps: []resource.TestStep{ + { + Config: testAccPKISecretBackendConfigCMPV2Disabled(backend), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(resourceBackend, consts.FieldBackend, backend), + resource.TestCheckResourceAttr(resourceBackend, consts.FieldEnabled, "false"), + resource.TestCheckResourceAttr(resourceBackend, consts.FieldDefaultPathPolicy, ""), + resource.TestCheckResourceAttr(resourceBackend, consts.FieldAuthenticators+".#", "1"), + resource.TestCheckNoResourceAttr(resourceBackend, consts.FieldAuthenticators+".0.cert"), + resource.TestCheckResourceAttrSet(dataName, consts.FieldLastUpdated), + + // Validate we read back the data back as we did upon creation + resource.TestCheckResourceAttr(dataName, consts.FieldBackend, backend), + resource.TestCheckResourceAttr(dataName, consts.FieldEnabled, "false"), + resource.TestCheckResourceAttr(dataName, consts.FieldDefaultPathPolicy, ""), + resource.TestCheckResourceAttr(dataName, consts.FieldLabelToPathPolicy+".%", "0"), + resource.TestCheckResourceAttr(dataName, consts.FieldAuthenticators+".#", "1"), + resource.TestCheckNoResourceAttr(dataName, consts.FieldAuthenticators+".0.cert"), + resource.TestCheckResourceAttrSet(dataName, consts.FieldLastUpdated), + ), + }, + testutil.GetImportTestStep(resourceBackend, false, nil), + }, + }) + +} + +func TestAccPKISecretBackendConfigCMPV2_AllFields(t *testing.T) { + t.Parallel() + + backend := acctest.RandomWithPrefix("tf-test-pki") + resourceType := "vault_pki_secret_backend_config_cmpv2" + resourceBackend := resourceType + ".test" + dataName := "data.vault_pki_secret_backend_config_cmpv2.test" + + resource.Test(t, resource.TestCase{ + ProviderFactories: providerFactories, + PreCheck: func() { + testutil.TestAccPreCheck(t) + testutil.TestEntPreCheck(t) + SkipIfAPIVersionLT(t, testProvider.Meta(), provider.VaultVersion118) + }, + CheckDestroy: testCheckMountDestroyed(resourceType, consts.MountTypePKI, consts.FieldBackend), + Steps: []resource.TestStep{ + { + Config: testAccPKISecretBackendConfigCMPV2Complete(backend), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(resourceBackend, consts.FieldBackend, backend), + resource.TestCheckResourceAttr(resourceBackend, consts.FieldEnabled, "true"), + resource.TestCheckResourceAttr(resourceBackend, consts.FieldDefaultPathPolicy, "role:cmpv2-role"), + resource.TestCheckResourceAttr(resourceBackend, consts.FieldAuthenticators+".#", "1"), + resource.TestCheckResourceAttr(resourceBackend, consts.FieldAuthenticators+".0.%", "1"), + resource.TestCheckResourceAttr(resourceBackend, consts.FieldAuthenticators+".0.cert.%", "2"), + resource.TestCheckResourceAttr(resourceBackend, consts.FieldAuthenticators+".0.cert.accessor", "test"), + resource.TestCheckResourceAttr(resourceBackend, consts.FieldAuthenticators+".0.cert.cert_role", "a-role"), + resource.TestCheckResourceAttr(resourceBackend, consts.FieldEnableSentinelParsing, "true"), + resource.TestCheckResourceAttr(resourceBackend, consts.FieldAuditFields+".#", "20"), + resource.TestCheckResourceAttrSet(dataName, consts.FieldLastUpdated), + + // Validate that the data property can read back everything filled in + resource.TestCheckResourceAttr(dataName, consts.FieldBackend, backend), + resource.TestCheckResourceAttr(dataName, consts.FieldEnabled, "true"), + resource.TestCheckResourceAttr(dataName, consts.FieldDefaultPathPolicy, "role:cmpv2-role"), + resource.TestCheckResourceAttr(dataName, consts.FieldAuthenticators+".#", "1"), + resource.TestCheckResourceAttr(dataName, consts.FieldAuthenticators+".0.%", "1"), + resource.TestCheckResourceAttr(dataName, consts.FieldAuthenticators+".0.cert.%", "2"), + resource.TestCheckResourceAttr(dataName, consts.FieldAuthenticators+".0.cert.accessor", "test"), + resource.TestCheckResourceAttr(resourceBackend, consts.FieldAuthenticators+".0.cert.cert_role", "a-role"), + resource.TestCheckResourceAttr(dataName, consts.FieldEnableSentinelParsing, "true"), + resource.TestCheckResourceAttr(dataName, consts.FieldAuditFields+".#", "20"), + resource.TestCheckResourceAttrSet(dataName, consts.FieldLastUpdated), + ), + }, + testutil.GetImportTestStep(resourceBackend, false, nil), + }, + }) +} + +func testAccPKISecretBackendConfigCMPV2Complete(pkiPath string) string { + return fmt.Sprintf(` +resource "vault_mount" "test" { + path = "%s" + type = "pki" + description = "PKI secret engine mount" +} + +resource "vault_pki_secret_backend_role" "cmpv2_role" { + backend = vault_mount.test.path + name = "cmpv2-role" + ttl = 3600 + key_type = "ec" + key_bits = "256" +} + +resource "vault_pki_secret_backend_role" "cmpv2_role_2" { + backend = vault_mount.test.path + name = "cmpv2-role-2" + ttl = 3600 + key_type = "ec" + key_bits = "256" +} + +resource "vault_pki_secret_backend_config_cmpv2" "test" { + backend = vault_mount.test.path + enabled = true + default_path_policy = format("role:%%s", vault_pki_secret_backend_role.cmpv2_role.name) + authenticators { + cert = { "accessor" = "test", "cert_role" = "a-role" } + } + enable_sentinel_parsing = true + audit_fields = ["csr", "common_name", "alt_names", "ip_sans", "uri_sans", "other_sans", + "signature_bits", "exclude_cn_from_sans", "ou", "organization", "country", + "locality", "province", "street_address", "postal_code", "serial_number", + "use_pss", "key_type", "key_bits", "add_basic_constraints"] +} + +data "vault_pki_secret_backend_config_cmpv2" "test" { + backend = vault_pki_secret_backend_config_cmpv2.test.backend +} +`, pkiPath) +} + +func testAccPKISecretBackendConfigCMPV2Disabled(path string) string { + return fmt.Sprintf(` +resource "vault_mount" "test" { + path = "%s" + type = "pki" + description = "PKI secret engine mount" +} + +resource "vault_pki_secret_backend_config_cmpv2" "test" { + backend = vault_mount.test.path +} + +data "vault_pki_secret_backend_config_cmpv2" "test" { + backend = vault_pki_secret_backend_config_cmpv2.test.backend +} +`, path) +} diff --git a/vault/resource_pki_secret_backend_config_est_test.go b/vault/resource_pki_secret_backend_config_est_test.go index b25192818..af6c25e8e 100644 --- a/vault/resource_pki_secret_backend_config_est_test.go +++ b/vault/resource_pki_secret_backend_config_est_test.go @@ -96,8 +96,7 @@ func TestAccPKISecretBackendConfigEst_AllFields(t *testing.T) { resource.TestCheckResourceAttr(resourceBackend, consts.FieldAuthenticators+".0.%", "2"), resource.TestCheckResourceAttr(resourceBackend, consts.FieldAuthenticators+".0.cert.%", "2"), resource.TestCheckResourceAttr(resourceBackend, consts.FieldAuthenticators+".0.cert.accessor", "test"), - // @TODO add these back in when Vault 1.16.3 is released (https://github.com/hashicorp/vault-enterprise/pull/5785) - // resource.TestCheckResourceAttr(resourceBackend, consts.FieldAuthenticators+".0.cert.cert_role", "a-role"), + resource.TestCheckResourceAttr(resourceBackend, consts.FieldAuthenticators+".0.cert.cert_role", "a-role"), resource.TestCheckResourceAttr(resourceBackend, consts.FieldAuthenticators+".0.userpass.%", "1"), resource.TestCheckResourceAttr(resourceBackend, consts.FieldAuthenticators+".0.userpass.accessor", "test2"), resource.TestCheckResourceAttr(resourceBackend, consts.FieldEnableSentinelParsing, "true"), @@ -116,8 +115,7 @@ func TestAccPKISecretBackendConfigEst_AllFields(t *testing.T) { resource.TestCheckResourceAttr(dataName, consts.FieldAuthenticators+".0.%", "2"), resource.TestCheckResourceAttr(dataName, consts.FieldAuthenticators+".0.cert.%", "2"), resource.TestCheckResourceAttr(dataName, consts.FieldAuthenticators+".0.cert.accessor", "test"), - // @TODO add these back in when Vault 1.16.3 is released (https://github.com/hashicorp/vault-enterprise/pull/5785) - // resource.TestCheckResourceAttr(dataName, consts.FieldAuthenticators+".0.cert.cert_role", "a-role"), + resource.TestCheckResourceAttr(dataName, consts.FieldAuthenticators+".0.cert.cert_role", "a-role"), resource.TestCheckResourceAttr(dataName, consts.FieldAuthenticators+".0.userpass.%", "1"), resource.TestCheckResourceAttr(dataName, consts.FieldAuthenticators+".0.userpass.accessor", "test2"), resource.TestCheckResourceAttr(dataName, consts.FieldEnableSentinelParsing, "true"), @@ -164,9 +162,7 @@ resource "vault_pki_secret_backend_config_est" "test" { "test-label-2": format("role:%%s", vault_pki_secret_backend_role.est_role_2.name) } authenticators { - # @TODO add these back in when Vault 1.16.3 is released (https://github.com/hashicorp/vault-enterprise/pull/5785) - # cert = { "accessor" = "test", "cert_role" = "a-role" } - cert = { "accessor" = "test", "cert_role" = "" } + cert = { "accessor" = "test", "cert_role" = "a-role" } userpass = { "accessor" = "test2" } } enable_sentinel_parsing = true diff --git a/website/docs/d/pki_secret_backend_config_cmpv2.html.md b/website/docs/d/pki_secret_backend_config_cmpv2.html.md new file mode 100644 index 000000000..752142e71 --- /dev/null +++ b/website/docs/d/pki_secret_backend_config_cmpv2.html.md @@ -0,0 +1,64 @@ +--- +layout: "vault" +page_title: "Vault: vault_pki_secret_backend_config_cmpv2 data source" +sidebar_current: "docs-vault-datasource-pki-secret-backend-config-cmpv2" +description: |- + Reads the PKI CMPv2 configuration from Vault Enterprise. +--- + +# vault\_pki\_secret\_backend\_config\_cmp + +Reads the PKI CMPv2 configuration from Vault Enterprise. + +~> **Important** All data retrieved from Vault will be +written in cleartext to state file generated by Terraform, will appear in +the console output when Terraform runs, and may be included in plan files +if secrets are interpolated into any resource attributes. +Protect these artifacts accordingly. See +[the main provider documentation](../index.html) +for more details. + +## Example Usage + +```hcl +resource "vault_mount" "pki" { + path = "pki" + type = "pki" + description = "PKI secret engine mount" +} + +data "vault_pki_secret_backend_config_cmpv2" "cmpv2_config" { + backend = vault_mount.pki.path +} +``` + +## Argument Reference + +The following arguments are supported: + +* `namespace` - (Optional) The namespace of the target resource. + The value should not contain leading or trailing forward slashes. + The `namespace` is always relative to the provider's configured [namespace](/docs/providers/vault/index.html#namespace). + *Available only for Vault Enterprise*. + +* `backend` - (Required) The path to the PKI secret backend to + read the CMPv2 configuration from, with no leading or trailing `/`s. + +# Attributes Reference + +* `authenticators` - Lists the mount accessors CMPv2 should delegate authentication requests towards (see [below for nested schema](#nestedatt--authenticators)). + +* `default_path_policy` - Specifies the behavior for requests using the non-role-qualified CMPv2 requests. Can be sign-verbatim or a role given by role:. + +* `enable_sentinel_parsing` - If set, parse out fields from the provided CSR making them available for Sentinel policies. + +* `enabled` - Specifies whether CMPv2 is enabled. + +* `audit_fields` - Fields parsed from the CSR that appear in the audit and can be used by sentinel policies. + +* `last_updated` - A read-only timestamp representing the last time the configuration was updated. + + +### Nested Schema for `authenticators` + +* `cert` - "The accessor and cert_role properties for cert auth backends". \ No newline at end of file diff --git a/website/docs/r/pki_secret_backend_config_cmpv2.html.md b/website/docs/r/pki_secret_backend_config_cmpv2.html.md new file mode 100644 index 000000000..ef304bd4a --- /dev/null +++ b/website/docs/r/pki_secret_backend_config_cmpv2.html.md @@ -0,0 +1,95 @@ +--- +layout: "vault" +page_title: "Vault: vault_pki_secret_backend_config_cmpv2 resource" +sidebar_current: "docs-vault-resource-pki-secret-backend-config-cmpv2" +description: |- + Sets the CMPv2 configuration on a PKI Secret Backend for Vault. +--- + +# vault\_pki\_secret\_backend\_config\_cmpv2 + +Allows setting the CMPv2 configuration on a PKI Secret Backend + +## Example Usage + +```hcl +resource "vault_mount" "pki" { + path = "pki-root" + type = "pki" + description = "PKI secret engine mount" +} + +resource "vault_pki_secret_backend_role" "cmpv2_role" { + backend = vault_mount.pki.path + name = "cmpv2-role" + ttl = 3600 + key_type = "ec" + key_bits = "256" +} + +resource "vault_pki_secret_backend_role" "cmpv2_role_2" { + backend = vault_mount.pki.path + name = "cmpv2-role-2" + ttl = 3600 + key_type = "ec" + key_bits = "256" +} + +resource "vault_pki_secret_backend_config_cmpv2" "example" { + backend = vault_mount.pki.path + enabled = true + default_path_policy = format("role:%s", vault_pki_secret_backend_role.cmpv2_role.name) + authenticators { + cert = { + "accessor" = "test", + "cert_role" = "cert-auth-role" + } + } + enable_sentinel_parsing = true + audit_fields = ["csr", "common_name", "alt_names", "ip_sans", "uri_sans", "other_sans", + "signature_bits", "exclude_cn_from_sans", "ou", "organization", "country", + "locality", "province", "street_address", "postal_code", "serial_number", + "use_pss", "key_type", "key_bits", "add_basic_constraints"] +} +``` + +## Argument Reference + +The following arguments are supported: + +* `namespace` - (Optional) The namespace of the target resource. + The value should not contain leading or trailing forward slashes. + The `namespace` is always relative to the provider's configured [namespace](/docs/providers/vault/index.html#namespace). + *Available only for Vault Enterprise*. + +* `backend` - (Required) The path to the PKI secret backend to + read the CMPv2 configuration from, with no leading or trailing `/`s. + +* `authenticators` - (Optional) Lists the mount accessors CMPv2 should delegate authentication requests towards (see [below for nested schema](#nestedatt--authenticators)). + +* `default_path_policy` - (Optional) Specifies the behavior for requests using the non-role-qualified CMPv2 requests. Can be sign-verbatim or a role given by role:. + +* `enable_sentinel_parsing` - (Optional) If set, parse out fields from the provided CSR making them available for Sentinel policies. + +* `enabled` - (Optional) Specifies whether CMPv2 is enabled. + +* `audit_fields` - (Optional) Fields parsed from the CSR that appear in the audit and can be used by sentinel policies. + + +### Nested Schema for `authenticators` + +* `cert` - "The accessor (required) and cert_role (optional) properties for cert auth backends". + +## Attributes Reference + +* `last_updated` - A read-only timestamp representing the last time the configuration was updated. + +## Import + +The PKI config cluster can be imported using the resource's `id`. +In the case of the example above the `id` would be `pki-root/config/cmpv2`, +where the `pki-root` component is the resource's `backend`, e.g. + +``` +$ terraform import vault_pki_secret_backend_config_cmpv2.example pki-root/config/cmpv2 +``` diff --git a/website/vault.erb b/website/vault.erb index aab6d1825..bb237f845 100644 --- a/website/vault.erb +++ b/website/vault.erb @@ -129,6 +129,10 @@ vault_policy_document + > + pki_secret_backend_config_cmpv2 + + > pki_secret_backend_config_est @@ -565,6 +569,10 @@ vault_pki_secret_backend_config_cluster + > + vault_pki_secret_backend_config_cmpv2 + + > vault_pki_secret_backend_config_est