vault-setup #10
@@ -109,7 +109,48 @@ bao read auth/approle/role/monitoring01/role-id
|
|||||||
bao write -f auth/approle/role/monitoring01/secret-id
|
bao write -f auth/approle/role/monitoring01/secret-id
|
||||||
```
|
```
|
||||||
|
|
||||||
### Issue a certificate from PKI
|
### Issue Certificates from PKI
|
||||||
|
|
||||||
|
**Method 1: ACME (Recommended for automated services)**
|
||||||
|
|
||||||
|
First, enable ACME support:
|
||||||
|
```bash
|
||||||
|
bao write pki_int/config/acme enabled=true
|
||||||
|
```
|
||||||
|
|
||||||
|
ACME directory endpoint:
|
||||||
|
```
|
||||||
|
https://vault.home.2rjus.net:8200/v1/pki_int/acme/directory
|
||||||
|
```
|
||||||
|
|
||||||
|
Use with ACME clients (lego, certbot, cert-manager, etc.):
|
||||||
|
```bash
|
||||||
|
# Example with lego
|
||||||
|
lego --email admin@home.2rjus.net \
|
||||||
|
--dns manual \
|
||||||
|
--server https://vault.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`:
|
||||||
|
```hcl
|
||||||
|
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**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Issue certificate for a host
|
# Issue certificate for a host
|
||||||
@@ -192,10 +233,48 @@ Secrets follow a three-tier hierarchy:
|
|||||||
- `skip_tls_verify = true` is acceptable for self-signed certs in homelab
|
- `skip_tls_verify = true` is acceptable for self-signed certs in homelab
|
||||||
- AppRole secret_ids can be scoped to specific CIDR ranges for additional security
|
- 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
|
||||||
|
```bash
|
||||||
|
export BAO_ADDR='https://vault.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://vault.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://vault.home.2rjus.net:8200/v1/pki_int/acme/directory
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Download Root CA Certificate
|
||||||
|
|
||||||
|
For trusting the internal CA on clients:
|
||||||
|
```bash
|
||||||
|
# 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
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Manual test
|
||||||
|
bao write pki_int/issue/homelab common_name="test.home.2rjus.net" ttl="24h"
|
||||||
|
```
|
||||||
|
|
||||||
## Next Steps
|
## Next Steps
|
||||||
|
|
||||||
1. Add more AppRoles for different host types
|
1. Replace step-ca ACME endpoint with OpenBao in `system/acme.nix`
|
||||||
2. Create policies for different service tiers
|
2. Add more AppRoles for different host types
|
||||||
3. Migrate existing sops-nix secrets to OpenBao KV
|
3. Migrate existing sops-nix secrets to OpenBao KV
|
||||||
4. Enable ACME support on PKI intermediate CA (OpenBao 2.0+)
|
4. Set up SSH CA for host and user certificates
|
||||||
5. Set up SSH CA for host and user certificates
|
5. Configure auto-unseal for vault01
|
||||||
|
|||||||
190
terraform/vault/pki.tf
Normal file
190
terraform/vault/pki.tf
Normal file
@@ -0,0 +1,190 @@
|
|||||||
|
# ============================================================================
|
||||||
|
# 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
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user