OpenBao Terraform Configuration
This directory contains Terraform/OpenTofu configuration for managing OpenBao (Vault) infrastructure as code.
Overview
Manages the following OpenBao resources:
- AppRole Authentication: For host-based authentication
- PKI Infrastructure: Root CA + Intermediate CA for TLS certificates
- KV Secrets Engine: Key-value secret storage (v2)
- Policies: Access control policies
Setup
-
Copy the example tfvars file:
cp terraform.tfvars.example terraform.tfvars -
Edit
terraform.tfvarswith your OpenBao credentials:vault_address = "https://vault01.home.2rjus.net:8200" vault_token = "hvs.your-root-token-here" vault_skip_tls_verify = true -
Initialize Terraform:
tofu init -
Review the plan:
tofu plan -
Apply the configuration:
tofu apply
Files
main.tf- Provider configurationvariables.tf- Variable definitionsapprole.tf- AppRole authentication backend and rolespki.tf- PKI engines (root CA and intermediate CA)secrets.tf- KV secrets engine and test secretsterraform.tfvars- Credentials (gitignored)terraform.tfvars.example- Example configuration
Resources Created
AppRole Authentication
- AppRole backend at
approle/ - Host-based roles and policies (defined in
locals.host_policies)
PKI Infrastructure
- Root CA at
pki/(10 year TTL) - Intermediate CA at
pki_int/(5 year TTL) - Role
homelabfor issuing certificates to*.home.2rjus.net - Certificate max TTL: 30 days
Secrets
- KV v2 engine at
secret/ - Secrets and policies defined in
locals.secretsandlocals.host_policies
Usage Examples
Adding a New Host
- Define the host policy in
approle.tf:
locals {
host_policies = {
"monitoring01" = {
paths = [
"secret/data/hosts/monitoring01/*",
"secret/data/services/prometheus/*",
]
}
}
}
- Add secrets in
secrets.tf:
locals {
secrets = {
"hosts/monitoring01/grafana-admin" = {
auto_generate = true
password_length = 32
}
}
}
- Apply changes:
tofu apply
- Get AppRole credentials:
# Get role_id
bao read auth/approle/role/monitoring01/role-id
# Generate secret_id
bao write -f auth/approle/role/monitoring01/secret-id
Issue Certificates from PKI
Method 1: ACME (Recommended for automated services)
First, enable ACME support:
bao write pki_int/config/acme enabled=true
ACME directory endpoint:
https://vault01.home.2rjus.net:8200/v1/pki_int/acme/directory
Use with ACME clients (lego, certbot, cert-manager, etc.):
# Example with lego
lego --email admin@home.2rjus.net \
--dns manual \
--server https://vault01.home.2rjus.net:8200/v1/pki_int/acme/directory \
--accept-tos \
run -d test.home.2rjus.net
Method 2: Static certificates via Terraform
Define in pki.tf:
locals {
static_certificates = {
"monitoring" = {
common_name = "monitoring.home.2rjus.net"
alt_names = ["grafana.home.2rjus.net", "prometheus.home.2rjus.net"]
ttl = "720h"
}
}
}
Terraform will auto-issue and auto-renew these certificates.
Method 3: Manual CLI issuance
# Issue certificate for a host
bao write pki_int/issue/homelab \
common_name="test.home.2rjus.net" \
ttl="720h"
Read a secret
# Authenticate with AppRole first
bao write auth/approle/login \
role_id="..." \
secret_id="..."
# Read the test secret
bao kv get secret/test/example
Managing Secrets
Secrets are defined in the locals.secrets block in secrets.tf using a declarative pattern:
Auto-Generated Secrets (Recommended)
Most secrets can be auto-generated using the random_password provider:
locals {
secrets = {
"hosts/monitoring01/grafana-admin" = {
auto_generate = true
password_length = 32
}
}
}
Manual Secrets
For secrets that must have specific values (external services, etc.):
# In variables.tf
variable "smtp_password" {
type = string
sensitive = true
}
# In secrets.tf locals block
locals {
secrets = {
"shared/smtp/credentials" = {
auto_generate = false
data = {
username = "notifications@2rjus.net"
password = var.smtp_password
server = "smtp.gmail.com"
}
}
}
}
# In terraform.tfvars
smtp_password = "super-secret-password"
Path Structure
Secrets follow a three-tier hierarchy:
hosts/{hostname}/*- Host-specific secretsservices/{service}/*- Service-wide secrets (any host running the service)shared/{category}/*- Shared secrets (SMTP, backup, etc.)
Security Notes
terraform.tfvarsis gitignored to prevent credential leakage- Root token should be stored securely (consider using a limited admin token instead)
skip_tls_verify = trueis acceptable for self-signed certs in homelab- AppRole secret_ids can be scoped to specific CIDR ranges for additional security
Initial Setup Steps
After deploying this configuration, perform these one-time setup tasks:
1. Enable ACME
export BAO_ADDR='https://vault01.home.2rjus.net:8200'
export BAO_TOKEN='your-root-token'
export BAO_SKIP_VERIFY=1
# Configure cluster path (required for ACME)
bao write pki_int/config/cluster path=https://vault01.home.2rjus.net:8200/v1/pki_int
# Enable ACME on intermediate CA
bao write pki_int/config/acme enabled=true
# Verify ACME is enabled
curl -k https://vault01.home.2rjus.net:8200/v1/pki_int/acme/directory
2. Download Root CA Certificate
For trusting the internal CA on clients:
# Download root CA certificate
bao read -field=certificate pki/cert/ca > homelab-root-ca.crt
# Install on NixOS hosts (add to system/default.nix or similar)
security.pki.certificateFiles = [ ./homelab-root-ca.crt ];
3. Test Certificate Issuance
# Manual test
bao write pki_int/issue/homelab common_name="test.home.2rjus.net" ttl="24h"
Next Steps
- Replace step-ca ACME endpoint with OpenBao in
system/acme.nix - Add more AppRoles for different host types
- Migrate existing sops-nix secrets to OpenBao KV
- Set up SSH CA for host and user certificates
- Configure auto-unseal for vault01