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>
69 lines
2 KiB
HCL
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
|
|
}
|