7 Commits

Author SHA1 Message Date
3f2f91aedd terraform: add vault pki management to terraform
Some checks failed
Run nix flake check / flake-check (push) Has been cancelled
2026-02-01 23:23:03 +01:00
5d513fd5af terraform: add vault secret managment to terraform 2026-02-01 23:07:47 +01:00
b6f1e80c2a chore: run tofu fmt 2026-02-01 23:04:02 +01:00
4133eafc4e flake: add openbao to devshell
Some checks failed
Run nix flake check / flake-check (push) Failing after 18m52s
2026-02-01 22:16:52 +01:00
ace848b29c vault: replace vault with openbao 2026-02-01 22:16:52 +01:00
b012df9f34 ns: add vault01 host to zone
Some checks failed
Run nix flake check / flake-check (push) Failing after 15m40s
Periodic flake update / flake-update (push) Successful in 1m7s
2026-02-01 20:54:22 +01:00
ab053c25bd opentofu: add tmp device to vms 2026-02-01 20:54:05 +01:00
14 changed files with 811 additions and 65 deletions

9
.gitignore vendored
View File

@@ -10,3 +10,12 @@ terraform/terraform.tfvars
terraform/*.auto.tfvars
terraform/crash.log
terraform/crash.*.log
terraform/vault/.terraform/
terraform/vault/.terraform.lock.hcl
terraform/vault/*.tfstate
terraform/vault/*.tfstate.*
terraform/vault/terraform.tfvars
terraform/vault/*.auto.tfvars
terraform/vault/crash.log
terraform/vault/crash.*.log

View File

@@ -334,38 +334,38 @@
sops-nix.nixosModules.sops
];
};
testvm01 = nixpkgs.lib.nixosSystem {
inherit system;
specialArgs = {
inherit inputs self sops-nix;
testvm01 = nixpkgs.lib.nixosSystem {
inherit system;
specialArgs = {
inherit inputs self sops-nix;
};
modules = [
(
{ config, pkgs, ... }:
{
nixpkgs.overlays = commonOverlays;
}
)
./hosts/testvm01
sops-nix.nixosModules.sops
];
};
modules = [
(
{ config, pkgs, ... }:
{
nixpkgs.overlays = commonOverlays;
}
)
./hosts/testvm01
sops-nix.nixosModules.sops
];
};
vault01 = nixpkgs.lib.nixosSystem {
inherit system;
specialArgs = {
inherit inputs self sops-nix;
vault01 = nixpkgs.lib.nixosSystem {
inherit system;
specialArgs = {
inherit inputs self sops-nix;
};
modules = [
(
{ config, pkgs, ... }:
{
nixpkgs.overlays = commonOverlays;
}
)
./hosts/vault01
sops-nix.nixosModules.sops
];
};
modules = [
(
{ config, pkgs, ... }:
{
nixpkgs.overlays = commonOverlays;
}
)
./hosts/vault01
sops-nix.nixosModules.sops
];
};
};
packages = forAllSystems (
{ pkgs }:
@@ -380,6 +380,7 @@
packages = with pkgs; [
ansible
opentofu
openbao
(pkgs.callPackage ./scripts/create-host { })
];
};

View File

@@ -1,7 +1,7 @@
$ORIGIN home.2rjus.net.
$TTL 1800
@ IN SOA ns1.home.2rjus.net. admin.test.2rjus.net. (
2063 ; serial number
2064 ; serial number
3600 ; refresh
900 ; retry
1209600 ; expire
@@ -63,6 +63,7 @@ actions1 IN CNAME nix-cache01
pgdb1 IN A 10.69.13.16
nats1 IN A 10.69.13.17
auth01 IN A 10.69.13.18
vault01 IN A 10.69.13.19
; http-proxy cnames
nzbget IN CNAME http-proxy

View File

@@ -1,8 +1,29 @@
{ ... }:
{
services.vault = {
services.openbao = {
enable = true;
storageBackend = "file";
settings = {
ui = true;
storage.file.path = "/var/lib/openbao";
listener.default = {
type = "tcp";
address = "0.0.0.0:8200";
tls_cert_file = "/run/credentials/openbao.service/cert.pem";
tls_key_file = "/run/credentials/openbao.service/key.pem";
};
listener.socket = {
type = "unix";
address = "/run/openbao/openbao.sock";
};
};
};
systemd.services.openbao.serviceConfig = {
LoadCredential = [
"key.pem:/var/lib/openbao/key.pem"
"cert.pem:/var/lib/openbao/cert.pem"
];
};
}

View File

@@ -8,7 +8,7 @@ resource "proxmox_cloud_init_disk" "ci" {
name = each.key
pve_node = each.value.target_node
storage = "local" # Cloud-init disks must be on storage that supports ISO/snippets
storage = "local" # Cloud-init disks must be on storage that supports ISO/snippets
# User data includes SSH keys and optionally NIXOS_FLAKE_BRANCH
user_data = <<-EOT
@@ -25,34 +25,34 @@ resource "proxmox_cloud_init_disk" "ci" {
: ""}
EOT
# Network configuration - static IP or DHCP
network_config = each.value.ip != null ? yamlencode({
version = 1
config = [{
type = "physical"
name = "ens18"
subnets = [{
type = "static"
address = each.value.ip
gateway = each.value.gateway
dns_nameservers = split(" ", each.value.nameservers)
dns_search = [each.value.search_domain]
}]
# Network configuration - static IP or DHCP
network_config = each.value.ip != null ? yamlencode({
version = 1
config = [{
type = "physical"
name = "ens18"
subnets = [{
type = "static"
address = each.value.ip
gateway = each.value.gateway
dns_nameservers = split(" ", each.value.nameservers)
dns_search = [each.value.search_domain]
}]
}) : yamlencode({
version = 1
config = [{
type = "physical"
name = "ens18"
subnets = [{
type = "dhcp"
}]
}]
}) : yamlencode({
version = 1
config = [{
type = "physical"
name = "ens18"
subnets = [{
type = "dhcp"
}]
})
}]
})
# Instance metadata
meta_data = yamlencode({
instance_id = sha1(each.key)
local-hostname = each.key
})
# Instance metadata
meta_data = yamlencode({
instance_id = sha1(each.key)
local-hostname = each.key
})
}

37
terraform/vault/.terraform.lock.hcl generated Normal file
View File

@@ -0,0 +1,37 @@
# This file is maintained automatically by "tofu init".
# Manual edits may be lost in future updates.
provider "registry.opentofu.org/hashicorp/random" {
version = "3.8.1"
constraints = "~> 3.6"
hashes = [
"h1:EHn3jsqOKhWjbg0X+psk0Ww96yz3N7ASqEKKuFvDFwo=",
"zh:25c458c7c676f15705e872202dad7dcd0982e4a48e7ea1800afa5fc64e77f4c8",
"zh:2edeaf6f1b20435b2f81855ad98a2e70956d473be9e52a5fdf57ccd0098ba476",
"zh:44becb9d5f75d55e36dfed0c5beabaf4c92e0a2bc61a3814d698271c646d48e7",
"zh:7699032612c3b16cc69928add8973de47b10ce81b1141f30644a0e8a895b5cd3",
"zh:86d07aa98d17703de9fbf402c89590dc1e01dbe5671dd6bc5e487eb8fe87eee0",
"zh:8c411c77b8390a49a8a1bc9f176529e6b32369dd33a723606c8533e5ca4d68c1",
"zh:a5ecc8255a612652a56b28149994985e2c4dc046e5d34d416d47fa7767f5c28f",
"zh:aea3fe1a5669b932eda9c5c72e5f327db8da707fe514aaca0d0ef60cb24892f9",
"zh:f56e26e6977f755d7ae56fa6320af96ecf4bb09580d47cb481efbf27f1c5afff",
]
}
provider "registry.opentofu.org/hashicorp/vault" {
version = "4.8.0"
constraints = "~> 4.0"
hashes = [
"h1:SQkjClJDo6SETUnq912GO8BdEExhU1ko8IG2mr4X/2A=",
"zh:0c07ef884c03083b08a54c2cf782f3ff7e124b05e7a4438a0b90a86e60c8d080",
"zh:13dcf2ed494c79e893b447249716d96b665616a868ffaf8f2c5abef07c7eee6f",
"zh:6f15a29fae3a6178e5904e3c95ba22b20f362d8ee491da816048c89f30e6b2de",
"zh:94b92a4bf7a2d250d9698a021f1ab60d1957d01b5bab81f7d9c00c2d6a9b3747",
"zh:a9e207540ef12cd2402e37b3b7567e08de14061a0a2635fd2f4fd09e0a3382aa",
"zh:b41667938ba541e8492036415b3f51fbd1758e456f6d5f0b63e26f4ad5728b21",
"zh:df0b73aff5f4b51e08fc0c273db7f677994db29a81deda66d91acfcfe3f1a370",
"zh:df904b217dc79b71a8b5f5f3ab2e52316d0f890810383721349cc10a72f7265b",
"zh:f0e0b3e6782e0126c40f05cf87ec80978c7291d90f52d7741300b5de1d9c01ba",
"zh:f8e599718b0ea22658eaa3e590671d3873aa723e7ce7d00daf3460ab41d3af14",
]
}

280
terraform/vault/README.md Normal file
View File

@@ -0,0 +1,280 @@
# 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
1. **Copy the example tfvars file:**
```bash
cp terraform.tfvars.example terraform.tfvars
```
2. **Edit `terraform.tfvars` with your OpenBao credentials:**
```hcl
vault_address = "https://vault.home.2rjus.net:8200"
vault_token = "hvs.your-root-token-here"
vault_skip_tls_verify = true
```
3. **Initialize Terraform:**
```bash
tofu init
```
4. **Review the plan:**
```bash
tofu plan
```
5. **Apply the configuration:**
```bash
tofu apply
```
## Files
- `main.tf` - Provider configuration
- `variables.tf` - Variable definitions
- `approle.tf` - AppRole authentication backend and roles
- `pki.tf` - PKI engines (root CA and intermediate CA)
- `secrets.tf` - KV secrets engine and test secrets
- `terraform.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 `homelab` for issuing certificates to `*.home.2rjus.net`
- Certificate max TTL: 30 days
### Secrets
- KV v2 engine at `secret/`
- Secrets and policies defined in `locals.secrets` and `locals.host_policies`
## Usage Examples
### Adding a New Host
1. **Define the host policy in `approle.tf`:**
```hcl
locals {
host_policies = {
"monitoring01" = {
paths = [
"secret/data/hosts/monitoring01/*",
"secret/data/services/prometheus/*",
]
}
}
}
```
2. **Add secrets in `secrets.tf`:**
```hcl
locals {
secrets = {
"hosts/monitoring01/grafana-admin" = {
auto_generate = true
password_length = 32
}
}
}
```
3. **Apply changes:**
```bash
tofu apply
```
4. **Get AppRole credentials:**
```bash
# 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:
```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
# Issue certificate for a host
bao write pki_int/issue/homelab \
common_name="test.home.2rjus.net" \
ttl="720h"
```
### Read a secret
```bash
# 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:
```hcl
locals {
secrets = {
"hosts/monitoring01/grafana-admin" = {
auto_generate = true
password_length = 32
}
}
}
```
### Manual Secrets
For secrets that must have specific values (external services, etc.):
```hcl
# 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 secrets
- `services/{service}/*` - Service-wide secrets (any host running the service)
- `shared/{category}/*` - Shared secrets (SMTP, backup, etc.)
## Security Notes
- `terraform.tfvars` is gitignored to prevent credential leakage
- Root token should be stored securely (consider using a limited admin token instead)
- `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
## 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
1. Replace step-ca ACME endpoint with OpenBao in `system/acme.nix`
2. Add more AppRoles for different host types
3. Migrate existing sops-nix secrets to OpenBao KV
4. Set up SSH CA for host and user certificates
5. Configure auto-unseal for vault01

View File

@@ -0,0 +1,74 @@
# Enable AppRole auth backend
resource "vault_auth_backend" "approle" {
type = "approle"
path = "approle"
}
# Define host access policies
locals {
host_policies = {
# Example: monitoring01 host
# "monitoring01" = {
# paths = [
# "secret/data/hosts/monitoring01/*",
# "secret/data/services/prometheus/*",
# "secret/data/services/grafana/*",
# "secret/data/shared/smtp/*"
# ]
# }
# Example: ha1 host
# "ha1" = {
# paths = [
# "secret/data/hosts/ha1/*",
# "secret/data/shared/mqtt/*"
# ]
# }
# TODO: actually use this policy
"ha1" = {
paths = [
"secret/data/hosts/ha1/*",
]
}
# TODO: actually use this policy
"monitoring01" = {
paths = [
"secret/data/hosts/monitoring01/*",
]
}
}
}
# Generate policies for each host
resource "vault_policy" "host_policies" {
for_each = local.host_policies
name = "${each.key}-policy"
policy = <<EOT
%{~for path in each.value.paths~}
path "${path}" {
capabilities = ["read", "list"]
}
%{~endfor~}
EOT
}
# Generate AppRoles for each host
resource "vault_approle_auth_backend_role" "hosts" {
for_each = local.host_policies
backend = vault_auth_backend.approle.path
role_name = each.key
token_policies = ["${each.key}-policy"]
# Token configuration
token_ttl = 3600 # 1 hour
token_max_ttl = 86400 # 24 hours
# Security settings
bind_secret_id = true
secret_id_ttl = 0 # Never expire (we'll rotate manually)
}

19
terraform/vault/main.tf Normal file
View File

@@ -0,0 +1,19 @@
terraform {
required_version = ">= 1.0"
required_providers {
vault = {
source = "hashicorp/vault"
version = "~> 4.0"
}
random = {
source = "hashicorp/random"
version = "~> 3.6"
}
}
}
provider "vault" {
address = var.vault_address
token = var.vault_token
skip_tls_verify = var.vault_skip_tls_verify
}

190
terraform/vault/pki.tf Normal file
View 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
}

View File

@@ -0,0 +1,76 @@
# Enable KV v2 secrets engine
resource "vault_mount" "kv" {
path = "secret"
type = "kv"
options = { version = "2" }
description = "KV Version 2 secret store"
}
# Define all secrets with auto-generation support
locals {
secrets = {
# Example host-specific secrets
# "hosts/monitoring01/grafana-admin" = {
# auto_generate = true
# password_length = 32
# }
# "hosts/ha1/mqtt-password" = {
# auto_generate = true
# password_length = 24
# }
# Example service secrets
# "services/prometheus/remote-write" = {
# auto_generate = true
# password_length = 40
# }
# Example shared secrets with manual values
# "shared/smtp/credentials" = {
# auto_generate = false
# data = {
# username = "notifications@2rjus.net"
# password = var.smtp_password # Define in variables.tf and set in terraform.tfvars
# server = "smtp.gmail.com"
# }
# }
# TODO: actually use the secret
"hosts/monitoring01/grafana-admin" = {
auto_generate = true
password_length = 32
}
# TODO: actually use the secret
"hosts/ha1/mqtt-password" = {
auto_generate = true
password_length = 24
}
}
}
# Auto-generate passwords for secrets with auto_generate = true
resource "random_password" "auto_secrets" {
for_each = {
for k, v in local.secrets : k => v
if lookup(v, "auto_generate", false)
}
length = each.value.password_length
special = true
}
# Create all secrets in Vault
resource "vault_kv_secret_v2" "secrets" {
for_each = local.secrets
mount = vault_mount.kv.path
name = each.key
data_json = jsonencode(
lookup(each.value, "auto_generate", false)
? { password = random_password.auto_secrets[each.key].result }
: each.value.data
)
}

View File

@@ -0,0 +1,6 @@
# Copy this file to terraform.tfvars and fill in your values
# terraform.tfvars is gitignored to keep credentials safe
vault_address = "https://vault.home.2rjus.net:8200"
vault_token = "hvs.XXXXXXXXXXXXXXXXXXXX"
vault_skip_tls_verify = true

View File

@@ -0,0 +1,26 @@
variable "vault_address" {
description = "OpenBao server address"
type = string
default = "https://vault.home.2rjus.net:8200"
}
variable "vault_token" {
description = "OpenBao root or admin token"
type = string
sensitive = true
}
variable "vault_skip_tls_verify" {
description = "Skip TLS verification (for self-signed certs)"
type = bool
default = true
}
# Example variables for manual secrets
# Uncomment and add to terraform.tfvars as needed
# variable "smtp_password" {
# description = "SMTP password for notifications"
# type = string
# sensitive = true
# }

View File

@@ -39,10 +39,11 @@ locals {
flake_branch = "pipeline-testing-improvements"
}
"vault01" = {
ip = "10.69.13.19/24"
cpu_cores = 2
memory = 2048
disk_size = "20G"
ip = "10.69.13.19/24"
cpu_cores = 2
memory = 2048
disk_size = "20G"
flake_branch = "vault-setup" # Bootstrap from this branch instead of master
}
}
@@ -118,6 +119,11 @@ resource "proxmox_vm_qemu" "vm" {
}
}
# TPM device
tpm_state {
storage = each.value.storage
}
# Start on boot
start_at_node_boot = true