- DnsProvisioner (dependency-frei, cURL) legt pro Reseller *.<slug>.<portal> A-Record via Hetzner-Cloud-DNS-API an (deckt firma.reseller.portal ab, was der globale *.<portal>-Eintrag nicht kann) - ResellerDnsListener (Doctrine postPersist/preRemove), fail-soft, überspringt Plattform-Reseller - Env HCLOUD_DNS_TOKEN/HCLOUD_DNS_ZONE_NAME (leer = aus); Terraform reicht Cloud-Token + Zone an die App-Nodes durch (nur bei manage_dns) - Ziel-IP = APP_EDGE_IP oder DNS-Auflösung der Portal-Domain Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
190 lines
5.2 KiB
HCL
190 lines
5.2 KiB
HCL
locals {
|
|
db_private_ip = "10.0.1.10"
|
|
caddy_private_ip = "10.0.1.5"
|
|
app_base_ip = 20 # App-Nodes: 10.0.1.20, .21, ...
|
|
|
|
database_url = "mysql://${var.db_user}:${var.db_password}@${local.db_private_ip}:3306/${var.db_name}?serverVersion=11.4.0-MariaDB&charset=utf8mb4"
|
|
|
|
# Caddy-Upstreams = private IPs der App-Nodes (:80)
|
|
app_upstreams = join(" ", [for i in range(var.app_count) : "10.0.1.${local.app_base_ip + i}:80"])
|
|
ask_upstream = "10.0.1.${local.app_base_ip}" # app-1 für die On-Demand-TLS-Abfrage
|
|
}
|
|
|
|
resource "hcloud_ssh_key" "admin" {
|
|
name = "vcard4-admin"
|
|
public_key = var.ssh_public_key
|
|
}
|
|
|
|
# --- Privates Netz ---
|
|
resource "hcloud_network" "net" {
|
|
name = "vcard4-net"
|
|
ip_range = "10.0.0.0/16"
|
|
}
|
|
|
|
resource "hcloud_network_subnet" "subnet" {
|
|
network_id = hcloud_network.net.id
|
|
type = "cloud"
|
|
network_zone = var.network_zone
|
|
ip_range = "10.0.1.0/24"
|
|
}
|
|
|
|
# --- Firewalls ---
|
|
resource "hcloud_firewall" "app" {
|
|
name = "vcard4-app-fw"
|
|
rule {
|
|
direction = "in"
|
|
protocol = "tcp"
|
|
port = "22"
|
|
source_ips = [var.admin_cidr]
|
|
}
|
|
rule {
|
|
direction = "in"
|
|
protocol = "tcp"
|
|
port = "80"
|
|
source_ips = ["10.0.0.0/16"] # nur aus dem privaten Netz (Load Balancer)
|
|
}
|
|
}
|
|
|
|
resource "hcloud_firewall" "db" {
|
|
name = "vcard4-db-fw"
|
|
rule {
|
|
direction = "in"
|
|
protocol = "tcp"
|
|
port = "22"
|
|
source_ips = [var.admin_cidr]
|
|
}
|
|
rule {
|
|
direction = "in"
|
|
protocol = "tcp"
|
|
port = "3306"
|
|
source_ips = ["10.0.0.0/16"] # nur App-Nodes
|
|
}
|
|
}
|
|
|
|
# --- DB-Node ---
|
|
resource "hcloud_server" "db" {
|
|
name = "vcard4-db"
|
|
server_type = var.db_server_type
|
|
image = "ubuntu-24.04"
|
|
location = var.location
|
|
ssh_keys = [hcloud_ssh_key.admin.id]
|
|
firewall_ids = [hcloud_firewall.db.id]
|
|
|
|
user_data = templatefile("${path.module}/cloud-init-db.yaml.tftpl", {
|
|
db_name = var.db_name
|
|
db_user = var.db_user
|
|
db_password = var.db_password
|
|
db_root_password = var.db_root_password
|
|
})
|
|
|
|
network {
|
|
network_id = hcloud_network.net.id
|
|
ip = local.db_private_ip
|
|
}
|
|
|
|
depends_on = [hcloud_network_subnet.subnet]
|
|
}
|
|
|
|
# --- App-Nodes (zustandslos) ---
|
|
resource "hcloud_server" "app" {
|
|
count = var.app_count
|
|
name = "vcard4-app-${count.index + 1}"
|
|
server_type = var.app_server_type
|
|
image = "ubuntu-24.04"
|
|
location = var.location
|
|
ssh_keys = [hcloud_ssh_key.admin.id]
|
|
firewall_ids = [hcloud_firewall.app.id]
|
|
|
|
user_data = templatefile("${path.module}/cloud-init-app.yaml.tftpl", {
|
|
repo_url = var.repo_url
|
|
repo_branch = var.repo_branch
|
|
run_migrations = count.index == 0 # Migrationen nur auf dem ersten Node
|
|
app_secret = var.app_secret
|
|
database_url = local.database_url
|
|
domain = var.domain
|
|
cors_allow_origin = "^https?://(www\\.)?${replace(var.domain, ".", "\\.")}$"
|
|
jwt_passphrase = var.jwt_passphrase
|
|
jwt_private_key = var.jwt_private_key
|
|
jwt_public_key = var.jwt_public_key
|
|
s3_endpoint = var.s3_endpoint
|
|
s3_region = var.s3_region
|
|
s3_bucket = var.s3_bucket
|
|
s3_key = var.s3_key
|
|
s3_secret = var.s3_secret
|
|
# DNS-Automatik für Firmen-Subdomains (nur wenn wir das DNS verwalten)
|
|
hcloud_dns_token = var.manage_dns ? var.hcloud_token : ""
|
|
dns_zone_name = var.manage_dns ? var.dns_zone_name : ""
|
|
})
|
|
|
|
network {
|
|
network_id = hcloud_network.net.id
|
|
ip = "10.0.1.${local.app_base_ip + count.index}"
|
|
}
|
|
|
|
# cloud-init zählt nur beim Erstboot; Code-Updates laufen über den Rollout
|
|
# (terraform_data.app_deploy) → Server nicht wegen user_data-Änderung ersetzen.
|
|
lifecycle {
|
|
ignore_changes = [user_data]
|
|
}
|
|
|
|
depends_on = [hcloud_network_subnet.subnet, hcloud_server.db]
|
|
}
|
|
|
|
# --- Caddy-Edge (TLS-Terminierung + Reverse-Proxy/Load-Balancing) ---
|
|
resource "hcloud_firewall" "caddy" {
|
|
name = "vcard4-caddy-fw"
|
|
rule {
|
|
direction = "in"
|
|
protocol = "tcp"
|
|
port = "22"
|
|
source_ips = [var.admin_cidr]
|
|
}
|
|
rule {
|
|
direction = "in"
|
|
protocol = "tcp"
|
|
port = "80"
|
|
source_ips = ["0.0.0.0/0", "::/0"]
|
|
}
|
|
rule {
|
|
direction = "in"
|
|
protocol = "tcp"
|
|
port = "443"
|
|
source_ips = ["0.0.0.0/0", "::/0"]
|
|
}
|
|
rule {
|
|
direction = "in"
|
|
protocol = "udp"
|
|
port = "443"
|
|
source_ips = ["0.0.0.0/0", "::/0"]
|
|
}
|
|
}
|
|
|
|
resource "hcloud_server" "caddy" {
|
|
name = "vcard4-caddy"
|
|
server_type = var.app_server_type
|
|
image = "ubuntu-24.04"
|
|
location = var.location
|
|
ssh_keys = [hcloud_ssh_key.admin.id]
|
|
firewall_ids = [hcloud_firewall.caddy.id]
|
|
|
|
user_data = templatefile("${path.module}/cloud-init-caddy.yaml.tftpl", {
|
|
acme_email = var.acme_email
|
|
domain = var.domain
|
|
app_upstreams = local.app_upstreams
|
|
ask_upstream = local.ask_upstream
|
|
})
|
|
|
|
network {
|
|
network_id = hcloud_network.net.id
|
|
ip = local.caddy_private_ip
|
|
}
|
|
|
|
# Caddyfile-Änderungen werden auf dem laufenden Node aktualisiert (caddy reload),
|
|
# nicht durch Server-Neuerstellung (sonst neue IP → DNS).
|
|
lifecycle {
|
|
ignore_changes = [user_data]
|
|
}
|
|
|
|
depends_on = [hcloud_network_subnet.subnet, hcloud_server.app]
|
|
}
|