Skip to content

sensiblebit/terraform-provider-certkit

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

13 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

terraform-provider-certkit

A Swiss Army knife for certificates in Terraform. Resolve chains, verify trust, generate CSRs, encode and decode PKCS#12 and PKCS#7 bundles. All the certificate plumbing that other providers don't handle.

Why certkit?

Working with certificates in Terraform usually means shelling out to OpenSSL, writing wrapper scripts, or accepting that some operations just aren't possible declaratively. certkit fills those gaps:

  • Fetch a leaf cert from any HTTPS endpoint and automatically resolve the full chain via AIA
  • Verify chains against system, Mozilla, or custom trust stores, including airgapped environments
  • Generate CSRs that match an existing certificate's subject and SANs, for zero-downtime renewals
  • Convert between formats (PEM, PKCS#12/PFX, PKCS#7/P7B) without leaving Terraform
  • Inspect anything by decoding bundles, extracting fingerprints, and comparing SKIs across mixed chains

Pure Go. No OpenSSL. No shell commands. No external dependencies.

Quick Start

terraform {
  required_providers {
    certkit = {
      source  = "sensiblebit/certkit"
      version = "~> 1.0"
    }
  }
}

provider "certkit" {}

# Fetch the leaf cert and resolve the full chain
data "certkit_certificate" "app" {
  url         = "https://example.com"
  trust_store = "mozilla"
}

output "fullchain" {
  value = data.certkit_certificate.app.fullchain_pem
}

output "fingerprint" {
  value = data.certkit_certificate.app.sha256_fingerprint
}

Resources

Resource Description
certkit_cert_request Generate a CSR from an existing certificate's subject and SANs
certkit_pkcs12 Encode a PKCS#12/PFX bundle from cert + key + chain
certkit_pkcs7 Encode a PKCS#7/P7B certificate-only bundle

Data Sources

Data Source Description
certkit_certificate Resolve, verify, and inspect a certificate chain
certkit_cert_request Parse and inspect an existing CSR
certkit_pkcs12 Decode a PKCS#12/PFX bundle into its components
certkit_pkcs7 Decode a PKCS#7/P7B bundle into individual certificates

Data Source: certkit_certificate

Resolves a certificate chain from a leaf certificate (fetched via TLS or provided as PEM), optionally following AIA URLs to discover intermediates, and verifies against a trust store.

Arguments

Name Type Required Description
url String One of url or leaf_pem HTTPS URL to fetch the leaf certificate via TLS handshake
leaf_pem String One of url or leaf_pem PEM-encoded leaf certificate
extra_intermediates_pem List(String) No Additional PEM-encoded intermediate certificates
fetch_aia Bool No Fetch intermediates via AIA URLs (default: true)
aia_timeout_ms Number No Timeout per AIA fetch in milliseconds (default: 2000)
aia_max_depth Number No Maximum AIA fetch depth (default: 5)
trust_store String No Trust store: system, mozilla, or custom (default: system)
custom_roots_pem List(String) No PEM-encoded root certificates when trust_store = "custom"
verify Bool No Verify the chain against the trust store (default: true)
colon_separated Bool No Use colon-separated hex for identifiers (default: true)

Attributes

Name Type Description
cert_pem String Leaf certificate PEM
chain_pem String Leaf + intermediates PEM
fullchain_pem String Leaf + intermediates + root PEM
sha256_fingerprint String SHA-256 fingerprint of the leaf
ski String Leaf Subject Key Identifier (RFC 7093, computed)
ski_embedded String Leaf Subject Key Identifier (from certificate extension)
aki String Leaf Authority Key Identifier (RFC 7093 SKI of issuer)
aki_embedded String Leaf Authority Key Identifier (from certificate extension)
intermediates List(Object) Intermediate certificates with cert_pem, sha256_fingerprint, ski, ski_embedded, aki, aki_embedded
roots List(Object) Root certificates with same attributes as intermediates
warnings List(String) Non-fatal warnings (e.g., AIA fetch failures)

Resource: certkit_cert_request

Generates a Certificate Signing Request by copying Subject and SANs (DNS, IP, URI) from an existing certificate. Useful for preparing renewals that preserve the original certificate's identity. The private key is stored in Terraform state.

Arguments

Name Type Required Description
cert_pem String Yes PEM-encoded certificate to copy Subject and SANs from. Changing forces replacement.
private_key_pem String No PEM-encoded private key for signing. If omitted, an EC P-256 key is auto-generated. Changing forces replacement.

Attributes

Name Type Description
cert_request_pem String PEM-encoded CSR
private_key_pem String, Sensitive Private key used (provided or auto-generated)
key_algorithm String Algorithm: ECDSA, RSA, or Ed25519

Resource: certkit_pkcs12

Encodes a certificate, CA chain, and private key into a PKCS#12/PFX bundle. All inputs force replacement when changed.

Arguments

Name Type Required Description
cert_pem String Yes PEM-encoded leaf certificate
private_key_pem String Yes PEM-encoded private key (Sensitive)
ca_certs_pem List(String) No PEM-encoded CA certificates to include
password String No Password for PKCS#12 encryption (Sensitive, default: empty)

Attributes

Name Type Description
content String, Sensitive Base64-encoded PKCS#12/PFX bundle

Resource: certkit_pkcs7

Encodes certificates into a certs-only PKCS#7/P7B bundle (no private key). All inputs force replacement when changed.

Arguments

Name Type Required Description
cert_pem String No PEM-encoded primary certificate
ca_certs_pem List(String) No PEM-encoded CA certificates to include

At least one of cert_pem or ca_certs_pem must be set.

Attributes

Name Type Description
content String Base64-encoded PKCS#7/P7B bundle

Data Source: certkit_cert_request

Parses a PEM-encoded Certificate Signing Request and exposes its subject, SANs, and key details. The CSR signature is verified during parsing.

Arguments

Name Type Required Description
cert_request_pem String Yes PEM-encoded Certificate Signing Request to inspect

Attributes

Name Type Description
subject_common_name String Common Name (CN) from the CSR subject
subject_organization List(String) Organization (O) values from the CSR subject
subject_country List(String) Country (C) values from the CSR subject
dns_names List(String) DNS Subject Alternative Names
ip_addresses List(String) IP address Subject Alternative Names
uris List(String) URI Subject Alternative Names
key_algorithm String Algorithm of the public key: ECDSA, RSA, or Ed25519
signature_algorithm String Signature algorithm used to sign the CSR

Data Source: certkit_pkcs12

Decodes a base64-encoded PKCS#12/PFX bundle and exposes its components.

Arguments

Name Type Required Description
content String Yes Base64-encoded PKCS#12/PFX bundle (Sensitive)
password String No Password for PKCS#12 decryption (Sensitive, default: empty)

Attributes

Name Type Description
cert_pem String PEM-encoded leaf certificate
private_key_pem String, Sensitive PEM-encoded private key (PKCS#8 format)
ca_certs_pem List(String) PEM-encoded CA certificates
key_algorithm String Algorithm: ECDSA, RSA, or Ed25519

Data Source: certkit_pkcs7

Decodes a base64-encoded PKCS#7/P7B bundle and exposes the certificates it contains.

Arguments

Name Type Required Description
content String Yes Base64-encoded PKCS#7/P7B bundle

Attributes

Name Type Description
certificates List(Object) Certificates with cert_pem, subject_common_name, sha256_fingerprint

Examples

Resolve a certificate chain

data "certkit_certificate" "app" {
  url         = "https://example.com"
  trust_store = "mozilla"
}

output "chain" {
  value = data.certkit_certificate.app.fullchain_pem
}

output "intermediates" {
  value = data.certkit_certificate.app.intermediates
}

Verify against custom roots (offline/airgapped)

data "certkit_certificate" "internal" {
  leaf_pem    = file("certs/leaf.pem")
  fetch_aia   = false
  trust_store = "custom"

  extra_intermediates_pem = [file("certs/intermediate.pem")]
  custom_roots_pem        = [file("certs/root.pem")]
}

Generate a CSR for certificate renewal

# Resolve the current cert
data "certkit_certificate" "app" {
  url         = "https://example.com"
  trust_store = "mozilla"
}

# Generate a CSR with the same subject and SANs
resource "certkit_cert_request" "renewal" {
  cert_pem = data.certkit_certificate.app.cert_pem
}

# Inspect the CSR
data "certkit_cert_request" "inspect" {
  cert_request_pem = certkit_cert_request.renewal.cert_request_pem
}

output "csr_pem" {
  value = certkit_cert_request.renewal.cert_request_pem
}

output "dns_names" {
  value = data.certkit_cert_request.inspect.dns_names
}

Build a PFX bundle and decode it

resource "certkit_pkcs12" "bundle" {
  cert_pem        = data.certkit_certificate.app.cert_pem
  private_key_pem = file("certs/key.pem")
  ca_certs_pem    = [for i in data.certkit_certificate.app.intermediates : i.cert_pem]
  password        = "changeit"
}

data "certkit_pkcs12" "decoded" {
  content  = certkit_pkcs12.bundle.content
  password = "changeit"
}

output "key_algorithm" {
  value = data.certkit_pkcs12.decoded.key_algorithm
}

Build a P7B bundle and decode it

resource "certkit_pkcs7" "bundle" {
  cert_pem     = data.certkit_certificate.app.cert_pem
  ca_certs_pem = [for i in data.certkit_certificate.app.intermediates : i.cert_pem]
}

data "certkit_pkcs7" "decoded" {
  content = certkit_pkcs7.bundle.content
}

output "cert_count" {
  value = length(data.certkit_pkcs7.decoded.certificates)
}

Technical Reference

SKI Calculation

The ski attribute is computed using RFC 7093 Section 2 Method 1: the leftmost 160 bits of the SHA-256 hash of the public key BIT STRING (excluding tag, length, and unused-bits octet). This produces a consistent 20-byte identifier for the same key, regardless of what the issuing CA embeds.

The ski_embedded attribute returns whatever the CA placed in the Subject Key Identifier extension, which is typically SHA-1 (20 bytes) but may be full SHA-256 (32 bytes) for newer CAs.

Trust Stores

Store Description
system OS trust store (macOS Keychain, Linux ca-certificates, Windows CertStore)
mozilla Embedded Mozilla CA bundle (via rootcerts), consistent across all platforms
custom Only the roots provided in custom_roots_pem

Development

# Build
go build -o terraform-provider-certkit

# Unit tests
go test -v ./internal/provider/

# Acceptance tests
TF_ACC=1 go test -v ./internal/provider/ -timeout 10m

# Coverage
TF_ACC=1 go test -coverprofile=coverage.out ./internal/provider/ && go tool cover -func=coverage.out

License

MIT

About

Terraform provider for certificate chain resolution, bundling, and encoding (PKCS#7, PKCS#12, CSR)

Resources

Stars

Watchers

Forks

Releases

Packages

Used by

Contributors

Languages