Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding ext key usages support for csr #430

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all 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
3 changes: 2 additions & 1 deletion docs/resources/cert_request.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,8 @@ resource "tls_cert_request" "example" {
- `ip_addresses` (List of String) List of IP addresses for which a certificate is being requested (i.e. certificate subjects).
- `subject` (Block List) 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))
- `uris` (List of String) List of URIs for which a certificate is being requested (i.e. certificate subjects).

- `allowed_uses` (List of String) List of key usages for the certificate singing request. Accepted values: `any_extended`, `client_auth`, `code_signing`, `email_protection`, `ipsec_end_system`, `ipsec_tunnel`, `ipsec_user`, `microsoft_commercial_code_signing`, `microsoft_kernel_code_signing`, `microsoft_server_gated_crypto`, `netscape_server_gated_crypto`, `ocsp_signing`, `server_auth`, `timestamping`.
-
### Read-Only

- `cert_request_pem` (String) The certificate request data in [PEM (RFC 1421)](https://datatracker.ietf.org/doc/html/rfc1421) format. **NOTE**: the [underlying](https://pkg.go.dev/encoding/pem#Encode) [libraries](https://pkg.go.dev/golang.org/x/crypto/ssh#MarshalAuthorizedKey) that generate this value append a `\n` at the end of the PEM. In case this disrupts your use case, we recommend using [`trimspace()`](https://www.terraform.io/language/functions/trimspace).
Expand Down
29 changes: 29 additions & 0 deletions internal/provider/common_cert.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,23 @@ var extendedKeyUsages = map[string]x509.ExtKeyUsage{
"microsoft_kernel_code_signing": x509.ExtKeyUsageMicrosoftKernelCodeSigning,
}

var extendedKeyUsageOIDs = map[string]asn1.ObjectIdentifier{
"any_extended": asn1.ObjectIdentifier{2, 5, 29, 37, 0},
"server_auth": asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 1},
"client_auth": asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 2},
"code_signing": asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 3},
"email_protection": asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 4},
"ipsec_end_system": asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 5},
"ipsec_tunnel": asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 6},
"ipsec_user": asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 7},
"timestamping": asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 8},
"ocsp_signing": asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 9},
"microsoft_server_gated_crypto": asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 311, 10, 3, 3},
"netscape_server_gated_crypto": asn1.ObjectIdentifier{2, 16, 840, 1, 113730, 4, 1},
"microsoft_commercial_code_signing": asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 311, 2, 1, 22},
"microsoft_kernel_code_signing": asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 311, 61, 1, 1},
}

// supportedKeyUsagesStr returns a slice with all the keys in keyUsages and extendedKeyUsages.
func supportedKeyUsagesStr() []string {
res := make([]string, 0, len(keyUsages)+len(extendedKeyUsages))
Expand All @@ -74,6 +91,18 @@ func supportedKeyUsagesStr() []string {
return res
}

// supportedEtendedKeyUsagesStr returns a slice with all the keys in extraExtensions.
func supportedEtendedKeyUsagesStr() []string {
res := make([]string, 0, len(extendedKeyUsages))

for k := range extendedKeyUsages {
res = append(res, k)
}
sort.Strings(res)

return res
}

// generateSubjectKeyID generates a SHA-1 hash of the subject public key.
func generateSubjectKeyID(pubKey crypto.PublicKey) ([]byte, error) {
var pubKeyBytes []byte
Expand Down
1 change: 1 addition & 0 deletions internal/provider/models.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ type certRequestResourceModel struct {
DNSNames types.List `tfsdk:"dns_names"`
IPAddresses types.List `tfsdk:"ip_addresses"`
URIs types.List `tfsdk:"uris"`
AllowedUses types.List `tfsdk:"allowed_uses"`
PrivateKeyPEM types.String `tfsdk:"private_key_pem"`
KeyAlgorithm types.String `tfsdk:"key_algorithm"`
CertRequestPEM types.String `tfsdk:"cert_request_pem"`
Expand Down
49 changes: 49 additions & 0 deletions internal/provider/resource_cert_request.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,13 @@ import (
"context"
"crypto/rand"
"crypto/x509"
"crypto/x509/pkix"
"encoding/asn1"
"encoding/pem"
"fmt"
"net"
"net/url"
"strings"

"github.com/hashicorp/terraform-plugin-framework-validators/listvalidator"
"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
Expand Down Expand Up @@ -87,6 +90,20 @@ func (r *certRequestResource) Schema(_ context.Context, req resource.SchemaReque
},
Description: "List of URIs for which a certificate is being requested (i.e. certificate subjects).",
},
"allowed_uses": schema.ListAttribute{
ElementType: types.StringType,
Optional: true,
PlanModifiers: []planmodifier.List{
listplanmodifier.RequiresReplace(),
},
Validators: []validator.List{
listvalidator.ValueStringsAre(
stringvalidator.OneOf(supportedEtendedKeyUsagesStr()...),
),
},
Description: "List of key usages for the certificate singing request. " +
fmt.Sprintf("Accepted values: `%s`.", strings.Join(supportedEtendedKeyUsagesStr(), "`, `")),
},

// Computed attributes
"key_algorithm": schema.StringAttribute{
Expand Down Expand Up @@ -305,6 +322,38 @@ func (r *certRequestResource) Create(ctx context.Context, req resource.CreateReq
}
}

// Add AllowedUses if provided
if !newState.AllowedUses.IsNull() && !newState.AllowedUses.IsUnknown() && len(newState.AllowedUses.Elements()) > 0 {
tflog.Debug(ctx, "Adding key usages in certificate request", map[string]interface{}{
"extraExtensions": newState.AllowedUses,
})

var extKeyUsages []string
res.Diagnostics.Append(newState.AllowedUses.ElementsAs(ctx, &extKeyUsages, false)...)
if res.Diagnostics.HasError() {
return
}

var extKeyUsageOIDs []asn1.ObjectIdentifier
for _, extKeyUsageStr := range extKeyUsages {
extKeyUsageOID, ok := extendedKeyUsageOIDs[extKeyUsageStr]
if !ok {
res.Diagnostics.AddError("Invalid key usage", fmt.Sprintf("%#v unsupported", extKeyUsageStr))
return
}
extKeyUsageOIDs = append(extKeyUsageOIDs, extKeyUsageOID)
}
asn1ExtKeyUsages, err := asn1.Marshal(extKeyUsageOIDs)
if err != nil {
res.Diagnostics.AddError("Error creating key usage list for certificate request", err.Error())
return
}
certReq.ExtraExtensions = append(certReq.ExtraExtensions, pkix.Extension{
Id: asn1.ObjectIdentifier{2, 5, 29, 37},
Value: asn1ExtKeyUsages,
})
}

// Generate `Certificate Request`
tflog.Debug(ctx, "Generating certificate request", map[string]interface{}{
"certReq": certReq,
Expand Down