tf-ct-prod/main.tf
quinn 30470d47e8 feat(tf-ct-prod): com.uvlava.ct.prod DMZ droplet (droplet+reserved-ip+fw)
Standalone module (uvlava state is tangled). Public edge host: reserved IP
134.199.244.34, firewall 22/80/443, cloud-init swap+caddy+node20. Hosts prospector
behind Caddy (apps.ct.uvlava.com / apps.ftw.pw -> 127.0.0.1:3210, 403 /internal/*).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-01 06:54:17 -04:00

69 lines
2 KiB
HCL

###############################################################################
# com.uvlava.ct.prod — hardened public DMZ droplet for CT's public app surface.
# Caddy (80/443, Let's Encrypt) reverse-proxies apps.ftw.pw -> 127.0.0.1:3210
# (prospector NestJS, same-origin PWA) and 403s /internal/*. Stable reserved IP
# so apps.ct.uvlava.com never hand-copies. Standalone module (uvlava state is
# tangled — do NOT fold into it).
###############################################################################
resource "digitalocean_droplet" "ct_prod" {
name = var.name
image = "ubuntu-24-04-x64"
size = var.droplet_size
region = var.region
ssh_keys = var.ssh_key_fingerprints
tags = ["ct", "prod", "dmz", "edge"]
user_data = file("${path.module}/cloud-init.yaml")
lifecycle {
ignore_changes = [user_data, name]
}
}
resource "digitalocean_reserved_ip" "ct_prod" {
droplet_id = digitalocean_droplet.ct_prod.id
region = var.region
}
resource "digitalocean_firewall" "ct_prod" {
name = "ct-prod-fw"
droplet_ids = [digitalocean_droplet.ct_prod.id]
inbound_rule {
protocol = "tcp"
port_range = "22"
source_addresses = ["0.0.0.0/0", "::/0"]
}
inbound_rule {
protocol = "tcp"
port_range = "80"
source_addresses = ["0.0.0.0/0", "::/0"]
}
inbound_rule {
protocol = "tcp"
port_range = "443"
source_addresses = ["0.0.0.0/0", "::/0"]
}
outbound_rule {
protocol = "tcp"
port_range = "1-65535"
destination_addresses = ["0.0.0.0/0", "::/0"]
}
outbound_rule {
protocol = "udp"
port_range = "1-65535"
destination_addresses = ["0.0.0.0/0", "::/0"]
}
outbound_rule {
protocol = "icmp"
destination_addresses = ["0.0.0.0/0", "::/0"]
}
}
output "ct_prod_droplet_ip" {
value = digitalocean_droplet.ct_prod.ipv4_address
}
output "ct_prod_reserved_ip" {
value = digitalocean_reserved_ip.ct_prod.ip_address
}