From f9b56977e68bca0ad9703330e6e866d0afad58c1 Mon Sep 17 00:00:00 2001 From: ThetaSinner Date: Tue, 17 Dec 2019 15:05:22 +0000 Subject: [PATCH] Support CA certificates in bundles --- example/data_source/main.tf | 3 +- example/resource/main.tf | 63 ++++++++++++++++++++++++++++++++----- impl/main.go | 27 ++++++++++++++-- resource_pkcs12_bundle.go | 9 +++++- 4 files changed, 89 insertions(+), 13 deletions(-) diff --git a/example/data_source/main.tf b/example/data_source/main.tf index fad64f5..d98d8b9 100644 --- a/example/data_source/main.tf +++ b/example/data_source/main.tf @@ -1,4 +1,3 @@ - provider "keystore" { path = "${path.module}/../out" } @@ -8,5 +7,5 @@ data "keystore_pkcs12_bundle" "my-bundle" { } output "my-bundle" { - value = "${data.keystore_pkcs12_bundle.my-bundle.bundle}" + value = data.keystore_pkcs12_bundle.my-bundle.bundle } diff --git a/example/resource/main.tf b/example/resource/main.tf index 277ccbf..48bf720 100644 --- a/example/resource/main.tf +++ b/example/resource/main.tf @@ -1,21 +1,23 @@ -resource "tls_private_key" "example" { +resource "tls_private_key" "example-ca" { algorithm = "RSA" rsa_bits = 2048 } -resource "tls_self_signed_cert" "example_certificate" { - key_algorithm = "${tls_private_key.example.algorithm}" - private_key_pem = "${tls_private_key.example.private_key_pem}" +resource "tls_self_signed_cert" "example-ca" { + key_algorithm = tls_private_key.example-ca.algorithm + private_key_pem = tls_private_key.example-ca.private_key_pem subject { - common_name = "example.com" + common_name = "example CA" organization = "EphyraSoftware" } - dns_names = ["example.com"] + dns_names = ["exampleca"] validity_period_hours = 2190 // Three months + is_ca_certificate = true + allowed_uses = [ "key_encipherment", "digital_signature", @@ -23,12 +25,57 @@ resource "tls_self_signed_cert" "example_certificate" { ] } +resource "tls_private_key" "example" { + algorithm = "RSA" + rsa_bits = 2048 +} + +resource "tls_cert_request" "example" { + key_algorithm = tls_private_key.example-ca.algorithm + private_key_pem = tls_private_key.example.private_key_pem + + subject { + common_name = "example.com" + organization = "ACME Examples, Inc" + } +} + +resource "tls_locally_signed_cert" "example" { + cert_request_pem = tls_cert_request.example.cert_request_pem + ca_key_algorithm = tls_private_key.example-ca.algorithm + ca_private_key_pem = tls_private_key.example-ca.private_key_pem + ca_cert_pem = tls_self_signed_cert.example-ca.cert_pem + + validity_period_hours = 12 + + allowed_uses = [ + "key_encipherment", + "digital_signature", + "server_auth", + ] +} + provider "keystore" { path = "${path.module}/../out" } +output "cert_pem" { + value = tls_locally_signed_cert.example.cert_pem +} + +output "key_pem" { + value = tls_private_key.example.private_key_pem +} + +output "ca_certs" { + value = tls_self_signed_cert.example-ca.cert_pem +} + resource "keystore_pkcs12_bundle" "my-bundle" { name = "my-bundle-name" - cert_pem = "${tls_self_signed_cert.example_certificate.cert_pem}" - key_pem = "${tls_private_key.example.private_key_pem}" + cert_pem = tls_locally_signed_cert.example.cert_pem + key_pem = tls_private_key.example.private_key_pem + ca_certs = [ + tls_self_signed_cert.example-ca.cert_pem + ] } diff --git a/impl/main.go b/impl/main.go index 0f5039b..32e85c6 100644 --- a/impl/main.go +++ b/impl/main.go @@ -10,7 +10,7 @@ import ( "software.sslmate.com/src/go-pkcs12" ) -func CreateBundle(certPEM string, keyPEM string, outputPath string, name string) error { +func CreateBundle(certPEM string, keyPEM string, caCertsPEM []string, outputPath string, name string) error { block, _ := pem.Decode([]byte(certPEM)) if block == nil || block.Type != "CERTIFICATE" { return errors.New("could not decode public certificate") @@ -35,11 +35,26 @@ func CreateBundle(certPEM string, keyPEM string, outputPath string, name string) return errors.New("could not parse private key") } + caCerts := make([]*x509.Certificate, len(caCertsPEM)) + for i, cert := range caCertsPEM { + block, _ := pem.Decode([]byte(cert)) + if block == nil || block.Type != "CERTIFICATE" { + return errors.New("could not decode CA certificate") + } + + caCert, err := x509.ParseCertificate(block.Bytes) + if err != nil { + return errors.New("could not parse CA certificate") + } + + caCerts[i] = caCert + } + result, err := pkcs12.Encode( rand.Reader, privateKey, publicKey, - []*x509.Certificate{}, + caCerts, pkcs12.DefaultPassword, ) @@ -55,3 +70,11 @@ func CreateBundle(certPEM string, keyPEM string, outputPath string, name string) return nil } + +func SliceOfString(slice []interface{}) []string { + result := make([]string, len(slice), len(slice)) + for i, s := range slice { + result[i] = s.(string) + } + return result +} diff --git a/resource_pkcs12_bundle.go b/resource_pkcs12_bundle.go index b4f9737..b3101a6 100644 --- a/resource_pkcs12_bundle.go +++ b/resource_pkcs12_bundle.go @@ -29,6 +29,12 @@ func pkcsBundle() *schema.Resource { Type: schema.TypeString, Required: true, }, + "ca_certs": &schema.Schema{ + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Schema{Type: schema.TypeString}, + Set: schema.HashString, + }, "bundle": &schema.Schema{ Type: schema.TypeString, Computed: true, @@ -44,8 +50,9 @@ func pkcsBundleCreate(d *schema.ResourceData, m interface{}) error { certPEM := d.Get("cert_pem").(string) keyPEM := d.Get("key_pem").(string) + caCerts := d.Get("ca_certs").(*schema.Set).List() - err := impl.CreateBundle(certPEM, keyPEM, outputPath, name) + err := impl.CreateBundle(certPEM, keyPEM, impl.SliceOfString(caCerts), outputPath, name) if err != nil { return err }