From 8e2d325fa3697bfc3bd0b329cfeb3486e4788adc Mon Sep 17 00:00:00 2001 From: Ivan De Marino Date: Thu, 7 Apr 2022 17:59:43 +0100 Subject: [PATCH 1/2] Relaxing validation of `allowed_uses` in `tls_locally_signed_cert` and `tls_self_signed_cert` If practitioner uses a non acceptable value, instead of erroring we will raise a warning and exclude the specific `allowed_use` from the certificate configuration. --- internal/provider/common_cert.go | 26 ++++++++++++++++++- .../resource_self_signed_cert_test.go | 20 +------------- 2 files changed, 26 insertions(+), 20 deletions(-) diff --git a/internal/provider/common_cert.go b/internal/provider/common_cert.go index dfd9745f..02f01cfc 100644 --- a/internal/provider/common_cert.go +++ b/internal/provider/common_cert.go @@ -270,7 +270,7 @@ func setCertificateCommonSchema(s map[string]*schema.Schema) { ForceNew: true, Elem: &schema.Schema{ Type: schema.TypeString, - ValidateFunc: validation.StringInSlice(supportedKeyUsages(), false), + ValidateFunc: StringInSliceOrWarn(supportedKeyUsages(), false), }, Description: "List of key usages allowed for the issued certificate. " + "Values are defined in [RFC 5280](https://datatracker.ietf.org/doc/html/rfc5280) " + @@ -529,3 +529,27 @@ func certificateToMap(cert *x509.Certificate) map[string]interface{} { "sha1_fingerprint": fmt.Sprintf("%x", sha1.Sum(cert.Raw)), } } + +// StringInSliceOrWarn returns a SchemaValidateFunc which tests if the provided value +// is of type string and matches the value of an element in the valid slice. +// +// Differently from validation.StringInSlice, if the element is not part of the valid slice, +// a warning is produced. +func StringInSliceOrWarn(valid []string, ignoreCase bool) schema.SchemaValidateFunc { + return func(i interface{}, k string) (warnings []string, errors []error) { + v, ok := i.(string) + if !ok { + errors = append(errors, fmt.Errorf("expected type of %s to be string", k)) + return warnings, errors + } + + for _, str := range valid { + if v == str || (ignoreCase && strings.EqualFold(v, str)) { + return warnings, errors + } + } + + warnings = append(warnings, fmt.Sprintf("expected %s to be one of %v, got %s so will ignored", k, valid, v)) + return warnings, errors + } +} diff --git a/internal/provider/resource_self_signed_cert_test.go b/internal/provider/resource_self_signed_cert_test.go index 78f3fe5d..cf38b29a 100644 --- a/internal/provider/resource_self_signed_cert_test.go +++ b/internal/provider/resource_self_signed_cert_test.go @@ -521,25 +521,6 @@ func TestAccSelfSignedCert_InvalidConfigs(t *testing.T) { r.UnitTest(t, r.TestCase{ Providers: testProviders, Steps: []r.TestStep{ - { - Config: ` - resource "tls_self_signed_cert" "test" { - subject { - common_name = "common test cert" - } - validity_period_hours = 1 - allowed_uses = [ - "not_valid" - ] - set_subject_key_id = true - private_key_pem = "does not matter" - } - output "cert_pem" { - value = tls_self_signed_cert.test.cert_pem - } - `, - ExpectError: regexp.MustCompile(`expected allowed_uses.0 to be one of \[.*\], got not_valid`), - }, { Config: ` resource "tls_self_signed_cert" "test" { @@ -624,6 +605,7 @@ func selfSignedCertConfig(validity, earlyRenewal uint32, setKeyAlgorithm bool) s "digital_signature", "server_auth", "client_auth", + "non_repudiation", ] %s From 5eaafa5be1ce6be2891e95fa7136180eb8451879 Mon Sep 17 00:00:00 2001 From: Ivan De Marino Date: Thu, 7 Apr 2022 18:14:12 +0100 Subject: [PATCH 2/2] Make linter happy --- internal/provider/common_cert.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/internal/provider/common_cert.go b/internal/provider/common_cert.go index 02f01cfc..4b639520 100644 --- a/internal/provider/common_cert.go +++ b/internal/provider/common_cert.go @@ -269,8 +269,8 @@ func setCertificateCommonSchema(s map[string]*schema.Schema) { Required: true, ForceNew: true, Elem: &schema.Schema{ - Type: schema.TypeString, - ValidateFunc: StringInSliceOrWarn(supportedKeyUsages(), false), + Type: schema.TypeString, + ValidateDiagFunc: StringInSliceOrWarn(supportedKeyUsages(), false), }, Description: "List of key usages allowed for the issued certificate. " + "Values are defined in [RFC 5280](https://datatracker.ietf.org/doc/html/rfc5280) " + @@ -535,8 +535,8 @@ func certificateToMap(cert *x509.Certificate) map[string]interface{} { // // Differently from validation.StringInSlice, if the element is not part of the valid slice, // a warning is produced. -func StringInSliceOrWarn(valid []string, ignoreCase bool) schema.SchemaValidateFunc { - return func(i interface{}, k string) (warnings []string, errors []error) { +func StringInSliceOrWarn(valid []string, ignoreCase bool) schema.SchemaValidateDiagFunc { + return validation.ToDiagFunc(func(i interface{}, k string) (warnings []string, errors []error) { v, ok := i.(string) if !ok { errors = append(errors, fmt.Errorf("expected type of %s to be string", k)) @@ -551,5 +551,5 @@ func StringInSliceOrWarn(valid []string, ignoreCase bool) schema.SchemaValidateF warnings = append(warnings, fmt.Sprintf("expected %s to be one of %v, got %s so will ignored", k, valid, v)) return warnings, errors - } + }) }