terraform { required_version = ">= 1.0" required_providers { null = { source = "hashicorp/null" version = "~> 3.2" } } } locals { compose_file = templatefile("${path.module}/templates/docker-compose.yml.tftpl", {}) default_conf = templatefile("${path.module}/templates/default.conf.tftpl", { server_name = var.server_name backend_host = var.backend_host demos_backend_port = var.demos_backend_port gitea_backend_port = var.gitea_backend_port }) default_vcl = templatefile("${path.module}/templates/default.vcl.tftpl", { backend_host = var.backend_host backend_port = tostring(var.backend_port) }) haproxy_cfg = templatefile("${path.module}/templates/haproxy.cfg.tftpl", { stats_user = var.haproxy_stats_user stats_password = var.haproxy_stats_password }) squid_conf = templatefile("${path.module}/templates/squid.conf.tftpl", { backend_host = var.backend_host backend_port = tostring(var.backend_port) }) config_hash = sha256(join("\n---\n", [ local.compose_file, local.default_conf, local.default_vcl, local.haproxy_cfg, local.squid_conf, ])) } resource "null_resource" "edge_services" { triggers = { host = var.edge_host user = var.edge_user install_dir = var.edge_install_dir server_name = var.server_name enable_letsencrypt = tostring(var.enable_letsencrypt) letsencrypt_email = var.letsencrypt_email letsencrypt_staging = tostring(var.letsencrypt_staging) certbot_version = "2" config_hash = local.config_hash } connection { type = "ssh" user = self.triggers.user private_key = file(var.edge_ssh_key_path) host = self.triggers.host } provisioner "remote-exec" { inline = [ "rm -rf /tmp/homelab-edge", "mkdir -p /tmp/homelab-edge/config_files", ] } provisioner "file" { content = local.compose_file destination = "/tmp/homelab-edge/docker-compose.yml" } provisioner "file" { content = local.default_conf destination = "/tmp/homelab-edge/config_files/default.conf" } provisioner "file" { content = local.default_vcl destination = "/tmp/homelab-edge/config_files/default.vcl" } provisioner "file" { content = local.haproxy_cfg destination = "/tmp/homelab-edge/config_files/haproxy.cfg" } provisioner "file" { content = local.squid_conf destination = "/tmp/homelab-edge/config_files/squid.conf" } provisioner "remote-exec" { inline = [ </dev/null | grep -q "install ok installed"; then missing_packages="$missing_packages $package" fi done if [ -n "$missing_packages" ]; then sudo apt-get update sudo apt-get install -y --no-install-recommends $missing_packages fi } install_missing_packages ca-certificates curl openssl if ! command -v docker >/dev/null 2>&1; then curl -fsSL https://get.docker.com | sudo sh fi if ! sudo docker compose version >/dev/null 2>&1; then install_missing_packages docker-compose-plugin fi sudo mkdir -p \ "$install_dir/config_files" \ "$install_dir/certs" \ "$install_dir/certbot/www" \ "$install_dir/letsencrypt" sudo cp /tmp/homelab-edge/docker-compose.yml "$install_dir/docker-compose.yml" sudo cp /tmp/homelab-edge/config_files/default.conf "$install_dir/config_files/default.conf" sudo cp /tmp/homelab-edge/config_files/default.vcl "$install_dir/config_files/default.vcl" sudo cp /tmp/homelab-edge/config_files/haproxy.cfg "$install_dir/config_files/haproxy.cfg" sudo cp /tmp/homelab-edge/config_files/squid.conf "$install_dir/config_files/squid.conf" if [ ! -s "$install_dir/certs/current.crt" ] || [ ! -s "$install_dir/certs/current.key" ]; then sudo openssl req -x509 -nodes -days 3650 -newkey rsa:2048 \ -subj "/CN=$server_name" \ -keyout "$install_dir/certs/current.key" \ -out "$install_dir/certs/current.crt" fi deploy_current_certificate() { if ! sudo test -s "$install_dir/letsencrypt/live/$server_name/fullchain.pem" || ! sudo test -s "$install_dir/letsencrypt/live/$server_name/privkey.pem"; then return 1 fi sudo cp -L "$install_dir/letsencrypt/live/$server_name/fullchain.pem" "$install_dir/certs/current.crt" sudo cp -L "$install_dir/letsencrypt/live/$server_name/privkey.pem" "$install_dir/certs/current.key" sudo chmod 0644 "$install_dir/certs/current.crt" sudo chmod 0600 "$install_dir/certs/current.key" } install_renewal_timer() { sudo tee /usr/local/sbin/homelab-edge-renew-certs.sh >/dev/null </dev/null <<'SERVICE_EOT' [Unit] Description=Renew Homelab Edge TLS certificates After=docker.service network-online.target Wants=network-online.target [Service] Type=oneshot ExecStart=/usr/local/sbin/homelab-edge-renew-certs.sh SERVICE_EOT sudo tee /etc/systemd/system/homelab-edge-renew-certs.timer >/dev/null <<'TIMER_EOT' [Unit] Description=Renew Homelab Edge TLS certificates twice daily [Timer] OnCalendar=*-*-* 03,15:17:00 RandomizedDelaySec=30m Persistent=true [Install] WantedBy=timers.target TIMER_EOT sudo systemctl daemon-reload sudo systemctl enable --now homelab-edge-renew-certs.timer >/dev/null } check_edge_health() { if ! curl "$@" >/dev/null; then sudo docker compose ps || true sudo docker compose logs --tail=80 nginx-dev || true exit 1 fi } cleanup_legacy_edge_containers() { local container for container in nginx-dev haproxy-dev varnish-dev squid-dev; do if sudo docker container inspect "$container" >/dev/null 2>&1; then sudo docker rm -f "$container" >/dev/null fi done } cd "$install_dir" cleanup_legacy_edge_containers sudo docker compose pull sudo docker compose up -d --remove-orphans sudo docker compose ps if [ "$enable_letsencrypt" = "true" ]; then email_args="--register-unsafely-without-email" if [ -n "$letsencrypt_email" ]; then email_args="--email $letsencrypt_email" fi staging_args="" if [ "$letsencrypt_staging" = "true" ]; then staging_args="--staging" fi certbot_log="$(mktemp)" if ! sudo docker run --rm \ -v "$install_dir/letsencrypt:/etc/letsencrypt" \ -v "$install_dir/certbot/www:/var/www/certbot" \ "$certbot_image" certonly \ --webroot \ -w /var/www/certbot \ -d "$server_name" \ --preferred-challenges http \ --agree-tos \ --non-interactive \ --keep-until-expiring \ $email_args \ $staging_args > "$certbot_log" 2>&1; then cat "$certbot_log" if grep -Eq "Certificate not yet due for renewal|no action taken" "$certbot_log" && sudo test -s "$install_dir/letsencrypt/live/$server_name/fullchain.pem" && sudo test -s "$install_dir/letsencrypt/live/$server_name/privkey.pem"; then echo "Using existing Let's Encrypt certificate for $server_name" else rm -f "$certbot_log" exit 1 fi else cat "$certbot_log" fi rm -f "$certbot_log" deploy_current_certificate sudo docker compose exec -T nginx-dev nginx -s reload || sudo docker compose restart nginx-dev install_renewal_timer check_edge_health -fsS --connect-timeout 10 --resolve "$server_name:443:127.0.0.1" "https://$server_name/edge-health" else sudo systemctl disable --now homelab-edge-renew-certs.timer >/dev/null 2>&1 || true check_edge_health -kfsS --connect-timeout 10 https://127.0.0.1/edge-health fi EOT ] } } output "edge_host" { value = var.edge_host } output "edge_install_dir" { value = var.edge_install_dir }