Skip to content

Certificate Leaf

The certificate_leaf resource generates leaf certificates signed by a Certificate Authority (CA). These are end-entity certificates used for servers, clients, or other applications that require TLS/SSL certificates signed by a trusted authority.

As a lab author, you can use certificate_leaf resources to:

  • Server Certificate Generation: Create TLS certificates for web servers, APIs, and other network services
  • Client Certificate Authentication: Generate client certificates for mutual TLS authentication scenarios
  • Service-to-Service Communication: Enable secure communication between microservices using signed certificates

Certificate leaf resources provide signed certificates for specific services while maintaining the certificate chain of trust.

resource "certificate_leaf" "name" {
ca_key = resource.certificate_ca.root.private_key.path
ca_cert = resource.certificate_ca.root.certificate.path
output = "./server-certs"
}
resource "certificate_leaf" "name" {
ca_key = resource.certificate_ca.root.private_key.path
ca_cert = resource.certificate_ca.root.certificate.path
output = "./server-certs"
dns_names = [
"localhost",
"api.example.com",
"www.example.com"
]
ip_addresses = [
"127.0.0.1",
"192.168.1.100",
"10.0.0.5"
]
}
FieldRequiredTypeDescription
ca_keystringPath to the CA private key file used for signing
ca_certstringPath to the CA certificate file
outputstringOutput directory where certificate and key files will be written
dns_nameslist(string)DNS names to include in the certificate’s Subject Alternative Names (SAN)
ip_addresseslist(string)IP addresses to include in the certificate’s Subject Alternative Names (SAN)

These attributes are set by the system after certificate generation:

Field Type Description
private_key File The private key of the generated leaf certificate
public_key_pem File The PEM-formatted public key
public_key_ssh File The SSH-formatted public key
certificate File The generated leaf certificate signed by the CA

The File object contains information about generated certificate files:

FieldTypeDescription
filenamestringThe name of the generated file
directorystringThe directory where the file is written
pathstringThe full absolute path to the file
contentsstringThe contents of the generated file
  • CA key and certificate files must exist and be valid
  • Output directory must be a valid path
  • DNS names must be valid domain names or hostnames
  • IP addresses must be valid IPv4 or IPv6 addresses
  • Generated files are written with appropriate permissions for certificate files
  • Certificate generation is idempotent - same configuration produces same output
  • Directory structure is created automatically if it doesn’t exist
resource "certificate_ca" "root" {
output = "./ca"
}
resource "certificate_leaf" "server" {
ca_key = resource.certificate_ca.root.private_key.path # e.g., "./ca/private_key.pem"
ca_cert = resource.certificate_ca.root.certificate.path # e.g., "./ca/certificate.pem"
output = "./server-certs"
dns_names = ["localhost"]
ip_addresses = ["127.0.0.1"]
}
output "server_cert_path" {
value = resource.certificate_leaf.server.certificate.path # e.g., "./server-certs/certificate.pem"
}
resource "certificate_leaf" "web" {
ca_key = resource.certificate_ca.root.private_key.path
ca_cert = resource.certificate_ca.root.certificate.path
output = "./web-certs"
dns_names = [
"example.com",
"www.example.com",
"api.example.com",
"admin.example.com"
]
ip_addresses = [
"192.168.1.10",
"10.0.0.100"
]
}
resource "container" "web_server" {
image {
name = "nginx:alpine"
}
volume {
source = resource.certificate_leaf.web.certificate.path # e.g., "./web-certs/certificate.pem"
destination = "/etc/ssl/certs/server.crt"
type = "bind"
read_only = true
}
volume {
source = resource.certificate_leaf.web.private_key.path # e.g., "./web-certs/private_key.pem"
destination = "/etc/ssl/private/server.key"
type = "bind"
read_only = true
}
port {
local = "443"
host = "8443"
}
}
resource "certificate_leaf" "client" {
ca_key = resource.certificate_ca.root.private_key.path
ca_cert = resource.certificate_ca.root.certificate.path
output = "./client-certs"
dns_names = ["client.internal"]
}
resource "template" "client_config" {
source = <<-EOF
client:
certificate: ${resource.certificate_leaf.client.certificate.path} # e.g., "./client-certs/certificate.pem"
private_key: ${resource.certificate_leaf.client.private_key.path} # e.g., "./client-certs/private_key.pem"
ca_certificate: ${resource.certificate_ca.root.certificate.path} # e.g., "./ca/certificate.pem"
tls:
verify_peer: true
verify_host: true
EOF
destination = "./client-config.yaml"
}
resource "certificate_ca" "service_mesh_ca" {
output = "./mesh-ca"
}
# API service certificate
resource "certificate_leaf" "api_service" {
ca_key = resource.certificate_ca.service_mesh_ca.private_key.path
ca_cert = resource.certificate_ca.service_mesh_ca.certificate.path
output = "./api-service-certs"
dns_names = [
"api-service",
"api-service.default.svc.cluster.local"
]
}
# Database service certificate
resource "certificate_leaf" "database_service" {
ca_key = resource.certificate_ca.service_mesh_ca.private_key.path
ca_cert = resource.certificate_ca.service_mesh_ca.certificate.path
output = "./database-service-certs"
dns_names = [
"database-service",
"database-service.default.svc.cluster.local"
]
}
resource "container" "api" {
image {
name = "api:latest"
}
environment = {
TLS_CERT_FILE = resource.certificate_leaf.api_service.certificate.path # e.g., "./api-service-certs/certificate.pem"
TLS_KEY_FILE = resource.certificate_leaf.api_service.private_key.path # e.g., "./api-service-certs/private_key.pem"
CA_CERT_FILE = resource.certificate_ca.service_mesh_ca.certificate.path # e.g., "./mesh-ca/certificate.pem"
}
}
resource "certificate_leaf" "load_balancer" {
ca_key = resource.certificate_ca.root.private_key.path
ca_cert = resource.certificate_ca.root.certificate.path
output = "./lb-certs"
dns_names = [
"lb.example.com",
"loadbalancer.internal",
"*.apps.example.com" # Wildcard certificate
]
ip_addresses = [
"192.168.1.50", # Load balancer IP
"10.0.0.50" # Internal IP
]
}
resource "template" "nginx_ssl_config" {
source = <<-EOF
server {
listen 443 ssl;
server_name lb.example.com *.apps.example.com;
ssl_certificate ${resource.certificate_leaf.load_balancer.certificate.path}; # e.g., "./lb-certs/certificate.pem"
ssl_certificate_key ${resource.certificate_leaf.load_balancer.private_key.path}; # e.g., "./lb-certs/private_key.pem"
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
location / {
proxy_pass http://backend;
}
}
EOF
destination = "./nginx-ssl.conf"
}
resource "certificate_leaf" "app_cert" {
ca_key = resource.certificate_ca.root.private_key.path
ca_cert = resource.certificate_ca.root.certificate.path
output = "./app-certs"
dns_names = ["app.local"]
}
resource "container" "secure_app" {
image {
name = "myapp:latest"
}
environment = {
# Pass certificate contents directly as environment variables
TLS_CERTIFICATE = resource.certificate_leaf.app_cert.certificate.contents
TLS_PRIVATE_KEY = resource.certificate_leaf.app_cert.private_key.contents
TLS_CA_CERT = resource.certificate_ca.root.certificate.contents
# Certificate metadata
CERT_FILENAME = resource.certificate_leaf.app_cert.certificate.filename # e.g., "certificate.pem"
CERT_DIRECTORY = resource.certificate_leaf.app_cert.certificate.directory # e.g., "./app-certs"
}
}
  1. Subject Alternative Names: Always include relevant DNS names and IP addresses for proper certificate validation
  2. Certificate Organization: Use descriptive output directories to organize certificates by service or purpose
  3. File References: Use .path for file system operations and .contents for inline certificate data
  4. Security: Protect private keys and limit access to certificate files
  5. Validation: Include all hostnames and IPs that clients will use to connect to your services
  6. Certificate Chain: Ensure CA certificates are properly distributed to clients for validation
  7. Naming Conventions: Use clear naming that indicates the certificate’s purpose and scope
  8. Environment Separation: Generate separate certificates for different environments (dev, staging, prod)