# ============================================================================ # PKI Infrastructure Configuration # ============================================================================ # # This file configures a two-tier PKI hierarchy: # - Root CA (pki/) - 10 year validity, EC P-384, kept offline (internal to Vault) # - Intermediate CA (pki_int/) - 5 year validity, EC P-384, used for issuing certificates # - Leaf certificates - Default to EC P-256 for optimal performance # # Key Type Choices: # - Root/Intermediate: EC P-384 (secp384r1) for long-term security # - Leaf certificates: EC P-256 (secp256r1) for performance and compatibility # - EC provides smaller keys, faster operations, and lower CPU usage vs RSA # # Certificate Issuance Methods: # # 1. ACME (Automated Certificate Management Environment) # - Services fetch certificates automatically using ACME protocol # - ACME directory: https://vault.home.2rjus.net:8200/v1/pki_int/acme/directory # - Enable ACME: bao write pki_int/config/acme enabled=true # - Compatible with cert-manager, lego, certbot, etc. # # 2. Direct Issuance (Non-ACME) # - Certificates defined in locals.static_certificates # - Terraform manages lifecycle (issuance, renewal) # - Useful for services without ACME support # - Certificates auto-renew 7 days before expiry # # 3. Manual Issuance (CLI) # - bao write pki_int/issue/homelab common_name="service.home.2rjus.net" # - Useful for one-off certificates or testing # # ============================================================================ # Root CA resource "vault_mount" "pki_root" { path = "pki" type = "pki" description = "Root CA" default_lease_ttl_seconds = 315360000 # 10 years max_lease_ttl_seconds = 315360000 # 10 years } resource "vault_pki_secret_backend_root_cert" "root" { backend = vault_mount.pki_root.path type = "internal" common_name = "home.2rjus.net Root CA" ttl = "315360000" # 10 years format = "pem" private_key_format = "der" key_type = "ec" key_bits = 384 # P-384 curve (NIST P-384, secp384r1) exclude_cn_from_sans = true organization = "Homelab" country = "NO" } # Intermediate CA resource "vault_mount" "pki_int" { path = "pki_int" type = "pki" description = "Intermediate CA" default_lease_ttl_seconds = 157680000 # 5 years max_lease_ttl_seconds = 157680000 # 5 years } resource "vault_pki_secret_backend_intermediate_cert_request" "intermediate" { backend = vault_mount.pki_int.path type = "internal" common_name = "home.2rjus.net Intermediate CA" key_type = "ec" key_bits = 384 # P-384 curve (NIST P-384, secp384r1) organization = "Homelab" country = "NO" } resource "vault_pki_secret_backend_root_sign_intermediate" "intermediate" { backend = vault_mount.pki_root.path csr = vault_pki_secret_backend_intermediate_cert_request.intermediate.csr common_name = "Homelab Intermediate CA" ttl = "157680000" # 5 years exclude_cn_from_sans = true organization = "Homelab" country = "NO" } resource "vault_pki_secret_backend_intermediate_set_signed" "intermediate" { backend = vault_mount.pki_int.path certificate = vault_pki_secret_backend_root_sign_intermediate.intermediate.certificate } # PKI Role for issuing certificates via ACME and direct issuance resource "vault_pki_secret_backend_role" "homelab" { backend = vault_mount.pki_int.path name = "homelab" allowed_domains = ["home.2rjus.net"] allow_subdomains = true max_ttl = 2592000 # 30 days ttl = 2592000 # 30 days default # Key configuration - EC (Elliptic Curve) by default key_type = "ec" key_bits = 256 # P-256 curve (NIST P-256, secp256r1) # ACME-friendly settings allow_ip_sans = true # Allow IP addresses in SANs allow_localhost = false # Disable localhost allow_bare_domains = false # Require subdomain or FQDN allow_glob_domains = false # Don't allow glob patterns in domain names # Server authentication server_flag = true client_flag = false code_signing_flag = false email_protection_flag = false # Key usage (appropriate for EC certificates) key_usage = [ "DigitalSignature", "KeyAgreement", ] ext_key_usage = ["ServerAuth"] # Certificate properties require_cn = false # ACME doesn't always use CN } # Configure CRL and issuing URLs resource "vault_pki_secret_backend_config_urls" "config_urls" { backend = vault_mount.pki_int.path issuing_certificates = [ "${var.vault_address}/v1/pki_int/ca" ] crl_distribution_points = [ "${var.vault_address}/v1/pki_int/crl" ] ocsp_servers = [ "${var.vault_address}/v1/pki_int/ocsp" ] } # ============================================================================ # Direct Certificate Issuance (Non-ACME) # ============================================================================ # Define static certificates to be issued directly (not via ACME) # Useful for services that don't support ACME or need long-lived certificates locals { static_certificates = { # Example: Issue a certificate for a specific service # "vault" = { # common_name = "vault.home.2rjus.net" # alt_names = ["vault01.home.2rjus.net"] # ip_sans = ["10.69.13.19"] # ttl = "8760h" # 1 year # } } } # Issue static certificates resource "vault_pki_secret_backend_cert" "static_certs" { for_each = local.static_certificates backend = vault_mount.pki_int.path name = vault_pki_secret_backend_role.homelab.name common_name = each.value.common_name alt_names = lookup(each.value, "alt_names", []) ip_sans = lookup(each.value, "ip_sans", []) ttl = lookup(each.value, "ttl", "720h") # 30 days default auto_renew = true min_seconds_remaining = 604800 # Renew 7 days before expiry } # Output static certificate data for use in configurations output "static_certificates" { description = "Static certificates issued by Vault PKI" value = { for k, v in vault_pki_secret_backend_cert.static_certs : k => { common_name = v.common_name serial = v.serial_number expiration = v.expiration issuing_ca = v.issuing_ca certificate = v.certificate private_key = v.private_key } } sensitive = true }