Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ NEW FEATURES:

* data-source/tls_public_key: Added support for [ED25519](https://ed25519.cr.yp.to/) key algorithm ([#160](https://github.com/hashicorp/terraform-provider-tls/pull/160)).

* resource/tls_cert_request: Added support for [ED25519](https://ed25519.cr.yp.to/) key algorithm ([#173](https://github.com/hashicorp/terraform-provider-tls/pull/173)).

* resource/tls_self_signed_cert: Added support for [ED25519](https://ed25519.cr.yp.to/) key algorithm ([#173](https://github.com/hashicorp/terraform-provider-tls/pull/173)).

* resource/tls_locally_signed_cert: Added support for [ED25519](https://ed25519.cr.yp.to/) key algorithm ([#173](https://github.com/hashicorp/terraform-provider-tls/pull/173)).

ENHANCEMENTS:

* resource/tls_private_key: New attributes `private_key_openssh` (OpenSSH PEM format) and `public_key_fingerprint_sha256` ([#151](https://github.com/hashicorp/terraform-provider-tls/pull/151)).
Expand All @@ -17,12 +23,25 @@ ENHANCEMENTS:
* resource/tls_locally_signed_cert: Resource will validate that `allowed_uses` list contains values that are part
of the documented set, throwing an error if they are invalid, instead of silently ignoring it
([#169](https://github.com/hashicorp/terraform-provider-tls/pull/169)).
* resource/tls_locally_signed_cert: The argument `ca_key_algorithm` is now optional and deprecated, as the resource infers the
algorithm from the given key `ca_private_key_pem`. It will be replaced by a read-only attribute,
exposing the inferred algorithm, in the next major release
([#173](https://github.com/hashicorp/terraform-provider-tls/pull/173)).

* resource/tls_self_signed_cert: Resource will validate that `validity_period_hours` and `early_renewal_hours`
are set to a value greater or equal then zero ([#169](https://github.com/hashicorp/terraform-provider-tls/pull/169)).
* resource/tls_self_signed_cert: Resource will validate that `allowed_uses` list contains values that are part
of the documented set, throwing an error if they are invalid, instead of silently ignoring it
([#169](https://github.com/hashicorp/terraform-provider-tls/pull/169)).
* resource/tls_self_signed_cert: The argument `key_algorithm` is now optional and deprecated, as the resource infers the
algorithm from the given key `private_key_pem`. It will be replaced by a read-only attribute,
exposing the inferred algorithm, in the next major release
([#173](https://github.com/hashicorp/terraform-provider-tls/pull/173)).

* resource/tls_cert_request: The argument `key_algorithm` is now optional and deprecated, as the resource infers the
algorithm from the given key `private_key_pem`. It will be replaced by a read-only attribute,
exposing the inferred algorithm, in the next major release
([#173](https://github.com/hashicorp/terraform-provider-tls/pull/173)).

NOTES:

Expand Down
3 changes: 1 addition & 2 deletions docs/resources/cert_request.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ state and does not create any external managed resources.

```terraform
resource "tls_cert_request" "example" {
key_algorithm = "ECDSA"
private_key_pem = file("private_key.pem")

subject {
Expand All @@ -38,14 +37,14 @@ resource "tls_cert_request" "example" {

### Required

- `key_algorithm` (String) Name of the algorithm used when generating the private key provided in `private_key_pem`.
- `private_key_pem` (String, Sensitive) Private key in [PEM (RFC 1421)](https://datatracker.ietf.org/doc/html/rfc1421) format, that the certificate will belong to. This can be read from a separate file using the [`file`](https://www.terraform.io/language/functions/file) interpolation function. Only an irreversible secure hash of the private key will be stored in the Terraform state.
- `subject` (Block List, Min: 1) The subject for which a certificate is being requested. The acceptable arguments are all optional and their naming is based upon [Issuer Distinguished Names (RFC5280)](https://tools.ietf.org/html/rfc5280#section-4.1.2.4) section. (see [below for nested schema](#nestedblock--subject))

### Optional

- `dns_names` (List of String) List of DNS names for which a certificate is being requested (i.e. certificate subjects).
- `ip_addresses` (List of String) List of IP addresses for which a certificate is being requested (i.e. certificate subjects).
- `key_algorithm` (String, Deprecated) Name of the algorithm used when generating the private key provided in `private_key_pem`. **NOTE**: this is deprecated and ignored, as the key algorithm is now inferred from the key. It it will be made read-only in the next major release.
- `uris` (List of String) List of URIs for which a certificate is being requested (i.e. certificate subjects).

### Read-Only
Expand Down
3 changes: 1 addition & 2 deletions docs/resources/locally_signed_cert.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ or when deployed internally to an organization.
```terraform
resource "tls_locally_signed_cert" "example" {
cert_request_pem = file("cert_request.pem")
ca_key_algorithm = "ECDSA"
ca_private_key_pem = file("ca_private_key.pem")
ca_cert_pem = file("ca_cert.pem")

Expand All @@ -39,13 +38,13 @@ resource "tls_locally_signed_cert" "example" {

- `allowed_uses` (List of String) List of key usages allowed for the issued certificate. Values are defined in [RFC 5280](https://datatracker.ietf.org/doc/html/rfc5280) and combine flags defined by both [Key Usages](https://datatracker.ietf.org/doc/html/rfc5280#section-4.2.1.3) and [Extended Key Usages](https://datatracker.ietf.org/doc/html/rfc5280#section-4.2.1.12). Accepted values: `any_extended`, `cert_signing`, `client_auth`, `code_signing`, `content_commitment`, `crl_signing`, `data_encipherment`, `decipher_only`, `digital_signature`, `email_protection`, `encipher_only`, `ipsec_end_system`, `ipsec_tunnel`, `ipsec_user`, `key_agreement`, `key_encipherment`, `microsoft_commercial_code_signing`, `microsoft_kernel_code_signing`, `microsoft_server_gated_crypto`, `netscape_server_gated_crypto`, `ocsp_signing`, `server_auth`, `timestamping`.
- `ca_cert_pem` (String) Certificate data of the Certificate Authority (CA) in [PEM (RFC 1421)](https://datatracker.ietf.org/doc/html/rfc1421) format.
- `ca_key_algorithm` (String) Name of the algorithm used when generating the private key provided in `ca_private_key_pem`.
- `ca_private_key_pem` (String, Sensitive) Private key of the Certificate Authority (CA) used to sign the certificate, in [PEM (RFC 1421)](https://datatracker.ietf.org/doc/html/rfc1421) format.
- `cert_request_pem` (String) Certificate request data in [PEM (RFC 1421)](https://datatracker.ietf.org/doc/html/rfc1421) format.
- `validity_period_hours` (Number) Number of hours, after initial issuing, that the certificate will remain valid for.

### Optional

- `ca_key_algorithm` (String, Deprecated) Name of the algorithm used when generating the private key provided in `ca_private_key_pem`. **NOTE**: this is deprecated and ignored, as the key algorithm is now inferred from the key. It it will be made read-only in the next major release.
- `early_renewal_hours` (Number) The resource will consider the certificate to have expired the given number of hours before its actual expiry time. This can be useful to deploy an updated certificate in advance of the expiration of the current certificate. However, the old certificate remains valid until its true expiration time, since this resource does not (and cannot) support certificate revocation. Also, this advance update can only be performed should the Terraform configuration be applied during the early renewal period. (default: `0`)
- `is_ca_certificate` (Boolean) Is the generated certificate representing a Certificate Authority (CA) (default: `false`).
- `set_subject_key_id` (Boolean) Should the generated certificate include a subject key identifier (default: `false`).
Expand Down
3 changes: 1 addition & 2 deletions docs/resources/self_signed_cert.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ connecting to a server that has a self-signed certificate.

```terraform
resource "tls_self_signed_cert" "example" {
key_algorithm = "ECDSA"
private_key_pem = file("private_key.pem")

subject {
Expand All @@ -43,7 +42,6 @@ resource "tls_self_signed_cert" "example" {
### Required

- `allowed_uses` (List of String) List of key usages allowed for the issued certificate. Values are defined in [RFC 5280](https://datatracker.ietf.org/doc/html/rfc5280) and combine flags defined by both [Key Usages](https://datatracker.ietf.org/doc/html/rfc5280#section-4.2.1.3) and [Extended Key Usages](https://datatracker.ietf.org/doc/html/rfc5280#section-4.2.1.12). Accepted values: `any_extended`, `cert_signing`, `client_auth`, `code_signing`, `content_commitment`, `crl_signing`, `data_encipherment`, `decipher_only`, `digital_signature`, `email_protection`, `encipher_only`, `ipsec_end_system`, `ipsec_tunnel`, `ipsec_user`, `key_agreement`, `key_encipherment`, `microsoft_commercial_code_signing`, `microsoft_kernel_code_signing`, `microsoft_server_gated_crypto`, `netscape_server_gated_crypto`, `ocsp_signing`, `server_auth`, `timestamping`.
- `key_algorithm` (String) Name of the algorithm used when generating the private key provided in `private_key_pem`.
- `private_key_pem` (String, Sensitive) Private key in [PEM (RFC 1421)](https://datatracker.ietf.org/doc/html/rfc1421) format, that the certificate will belong to. This can be read from a separate file using the [`file`](https://www.terraform.io/language/functions/file) interpolation function. Only an irreversible secure hash of the private key will be stored in the Terraform state.
- `subject` (Block List, Min: 1) The subject for which a certificate is being requested. The acceptable arguments are all optional and their naming is based upon [Issuer Distinguished Names (RFC5280)](https://tools.ietf.org/html/rfc5280#section-4.1.2.4) section. (see [below for nested schema](#nestedblock--subject))
- `validity_period_hours` (Number) Number of hours, after initial issuing, that the certificate will remain valid for.
Expand All @@ -54,6 +52,7 @@ resource "tls_self_signed_cert" "example" {
- `early_renewal_hours` (Number) The resource will consider the certificate to have expired the given number of hours before its actual expiry time. This can be useful to deploy an updated certificate in advance of the expiration of the current certificate. However, the old certificate remains valid until its true expiration time, since this resource does not (and cannot) support certificate revocation. Also, this advance update can only be performed should the Terraform configuration be applied during the early renewal period. (default: `0`)
- `ip_addresses` (List of String) List of IP addresses for which a certificate is being requested (i.e. certificate subjects).
- `is_ca_certificate` (Boolean) Is the generated certificate representing a Certificate Authority (CA) (default: `false`).
- `key_algorithm` (String, Deprecated) Name of the algorithm used when generating the private key provided in `private_key_pem`. **NOTE**: this is deprecated and ignored, as the key algorithm is now inferred from the key. It it will be made read-only in the next major release.
- `set_subject_key_id` (Boolean) Should the generated certificate include a subject key identifier (default: `false`).
- `uris` (List of String) List of URIs for which a certificate is being requested (i.e. certificate subjects).

Expand Down
1 change: 0 additions & 1 deletion examples/resources/tls_cert_request/resource.tf
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
resource "tls_cert_request" "example" {
key_algorithm = "ECDSA"
private_key_pem = file("private_key.pem")

subject {
Expand Down
1 change: 0 additions & 1 deletion examples/resources/tls_locally_signed_cert/resource.tf
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
resource "tls_locally_signed_cert" "example" {
cert_request_pem = file("cert_request.pem")
ca_key_algorithm = "ECDSA"
ca_private_key_pem = file("ca_private_key.pem")
ca_cert_pem = file("ca_cert.pem")

Expand Down
1 change: 0 additions & 1 deletion examples/resources/tls_self_signed_cert/resource.tf
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
resource "tls_self_signed_cert" "example" {
key_algorithm = "ECDSA"
private_key_pem = file("private_key.pem")

subject {
Expand Down
61 changes: 57 additions & 4 deletions internal/provider/common_cert.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,10 +122,14 @@ func setCertificateSubjectSchema(s map[string]*schema.Schema) {
}

s["key_algorithm"] = &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
Description: "Name of the algorithm used when generating the private key provided in `private_key_pem`.",
Type: schema.TypeString,
Optional: true,
ForceNew: true,
Deprecated: "This is now ignored, as the key algorithm is inferred from the `private_key_pem`. " +
"It it will be made read-only in the next major release.",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This deprecation seems to imply that this attribute should be available as read-only today too, so folks can update their configurations in preparation of the change. However, without calling (ResourceData).Set() on the attribute, the only time it would be available for reference today is if it is configured and relying on the SDK's behavior of copying the attribute's planned value (from the configuration) to the state.

My suggestion for these is to mark this attribute as Computed: true and call d.Set("key_algorithm", /*...*./) as appropriate. This removes any confusion about when the attribute value may be present, rather than it being dependent on the configuration. This will also ensure practitioners that remove the attribute from their configuration do not see an unexpected "whatever" -> null plan when removing the configuration, since the attribute will remain in state in that case.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In my testing I concluded that a 2 step approach (first make it Optional, release, then make it Computed) was necessary to avoid causing unnecessary re-creation of the resources.

I can remove the reference to "future behaviour" as what matters is that ppl stop using it.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I should've clarified better, sorry. I meant Optional and Computed, with calling d.Set(). Optional + Computed + ForceNew should not trigger recreation when removing the attribute from the configuration and if it does, that would be unexpected behavior we should investigate further.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So, here is the issue with adding Computer to Optional now: it will be a breaking change.

I think we are expected to cross that bridge only once we release a major version, after a minor version with the deprecation - no?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To be clear, I tried yesterday all the combinations, and just changing from Required to Optional for now, seemed like the only path forward to allow for an opt-in migration.

All other options would end up with a breaking change.

Description: "Name of the algorithm used when generating the private key provided in `private_key_pem`. " +
"**NOTE**: this is deprecated and ignored, as the key algorithm is now inferred from the key. " +
"It it will be made read-only in the next major release.",
}

s["private_key_pem"] = &schema.Schema{
Expand Down Expand Up @@ -461,3 +465,52 @@ func distinguishedNamesFromSubjectAttributes(nameMap map[string]interface{}) *pk

return result
}

func parseCertificate(d *schema.ResourceData, pemKey string) (*x509.Certificate, error) {
block, err := decodePEM(d, pemKey, "")
if err != nil {
return nil, err
}

certs, err := x509.ParseCertificates(block.Bytes)
if err != nil {
return nil, fmt.Errorf("failed to parse %s: %s", pemKey, err)
}
if len(certs) < 1 {
return nil, fmt.Errorf("no certificates found in %s", pemKey)
}
if len(certs) > 1 {
return nil, fmt.Errorf("multiple certificates found in %s", pemKey)
}

return certs[0], nil
}

func parseCertificateRequest(d *schema.ResourceData, pemKey string) (*x509.CertificateRequest, error) {
block, err := decodePEM(d, pemKey, CertificateRequest.String())
if err != nil {
return nil, err
}

certReq, err := x509.ParseCertificateRequest(block.Bytes)
if err != nil {
return nil, fmt.Errorf("failed to parse %s: %s", pemKey, err)
}

return certReq, nil
}

func certificateToMap(cert *x509.Certificate) map[string]interface{} {
return map[string]interface{}{
"signature_algorithm": cert.SignatureAlgorithm.String(),
"public_key_algorithm": cert.PublicKeyAlgorithm.String(),
"serial_number": cert.SerialNumber.String(),
"is_ca": cert.IsCA,
"version": cert.Version,
"issuer": cert.Issuer.String(),
"subject": cert.Subject.String(),
"not_before": cert.NotBefore.Format(time.RFC3339),
"not_after": cert.NotAfter.Format(time.RFC3339),
"sha1_fingerprint": fmt.Sprintf("%x", sha1.Sum(cert.Raw)),
}
}
Loading