[infra] Auto-create Cloudflare DNS records from ingress_factory
## Context
Deploying new services required manually adding hostnames to
cloudflare_proxied_names/cloudflare_non_proxied_names in config.tfvars —
a separate file from the service stack. This was frequently forgotten,
leaving services unreachable externally.
## This change:
- Add `dns_type` parameter to `ingress_factory` and `reverse_proxy/factory`
modules. Setting `dns_type = "proxied"` or `"non-proxied"` auto-creates
the Cloudflare DNS record (CNAME to tunnel or A/AAAA to public IP).
- Simplify cloudflared tunnel from 100 per-hostname rules to wildcard
`*.viktorbarzin.me → Traefik`. Traefik still handles host-based routing.
- Add global Cloudflare provider via terragrunt.hcl (separate
cloudflare_provider.tf with Vault-sourced API key).
- Migrate 118 hostnames from centralized config.tfvars to per-service
dns_type. 17 hostnames remain centrally managed (Helm ingresses,
special cases).
- Update docs, AGENTS.md, CLAUDE.md, dns.md runbook.
```
BEFORE AFTER
config.tfvars (manual list) stacks/<svc>/main.tf
| module "ingress" {
v dns_type = "proxied"
stacks/cloudflared/ }
for_each = list |
cloudflare_record auto-creates
tunnel per-hostname cloudflare_record + annotation
```
## What is NOT in this change:
- Uptime Kuma monitor migration (still reads from config.tfvars)
- 17 remaining centrally-managed hostnames (Helm, special cases)
- Removal of allow_overwrite (keep until migration confirmed stable)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
95d2a6abf8
commit
b1d152be1f
94 changed files with 471 additions and 34 deletions
|
|
@ -1,3 +1,15 @@
|
|||
terraform {
|
||||
required_providers {
|
||||
cloudflare = {
|
||||
source = "cloudflare/cloudflare"
|
||||
version = "~> 4"
|
||||
}
|
||||
kubernetes = {
|
||||
source = "hashicorp/kubernetes"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
variable "name" {}
|
||||
variable "namespace" {
|
||||
default = "reverse-proxy"
|
||||
|
|
@ -45,6 +57,31 @@ variable "skip_global_rate_limit" {
|
|||
type = bool
|
||||
default = false
|
||||
}
|
||||
variable "dns_type" {
|
||||
type = string
|
||||
default = "none"
|
||||
description = "Cloudflare DNS: 'proxied' (CNAME to tunnel), 'non-proxied' (A/AAAA to public IP), or 'none'"
|
||||
validation {
|
||||
condition = contains(["proxied", "non-proxied", "none"], var.dns_type)
|
||||
error_message = "dns_type must be 'proxied', 'non-proxied', or 'none'."
|
||||
}
|
||||
}
|
||||
variable "cloudflare_zone_id" {
|
||||
type = string
|
||||
default = "fd2c5dd4efe8fe38958944e74d0ced6d"
|
||||
}
|
||||
variable "cloudflare_tunnel_id" {
|
||||
type = string
|
||||
default = "75182cd7-bb91-4310-b961-5d8967da8b41"
|
||||
}
|
||||
variable "public_ip" {
|
||||
type = string
|
||||
default = "176.12.22.76"
|
||||
}
|
||||
variable "public_ipv6" {
|
||||
type = string
|
||||
default = "2001:470:6e:43d::2"
|
||||
}
|
||||
|
||||
|
||||
resource "kubernetes_service" "proxied-service" {
|
||||
|
|
@ -88,7 +125,9 @@ resource "kubernetes_ingress_v1" "proxied-ingress" {
|
|||
"traefik.ingress.kubernetes.io/router.entrypoints" = "websecure"
|
||||
"traefik.ingress.kubernetes.io/service.serversscheme" = var.backend_protocol == "HTTPS" ? "https" : null
|
||||
"traefik.ingress.kubernetes.io/service.serverstransport" = var.backend_protocol == "HTTPS" ? "traefik-insecure-skip-verify@kubernetescrd" : null
|
||||
}, var.extra_annotations)
|
||||
}, var.extra_annotations,
|
||||
var.dns_type != "none" ? { "cloudflare.viktorbarzin.me/dns-type" = var.dns_type } : {}
|
||||
)
|
||||
}
|
||||
|
||||
spec {
|
||||
|
|
@ -166,3 +205,37 @@ resource "kubernetes_manifest" "custom_csp" {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Cloudflare DNS records — created automatically when dns_type is set.
|
||||
resource "cloudflare_record" "proxied" {
|
||||
count = var.dns_type == "proxied" ? 1 : 0
|
||||
name = var.name
|
||||
content = "${var.cloudflare_tunnel_id}.cfargotunnel.com"
|
||||
proxied = true
|
||||
ttl = 1
|
||||
type = "CNAME"
|
||||
zone_id = var.cloudflare_zone_id
|
||||
allow_overwrite = true
|
||||
}
|
||||
|
||||
resource "cloudflare_record" "non_proxied_a" {
|
||||
count = var.dns_type == "non-proxied" ? 1 : 0
|
||||
name = var.name
|
||||
content = var.public_ip
|
||||
proxied = false
|
||||
ttl = 1
|
||||
type = "A"
|
||||
zone_id = var.cloudflare_zone_id
|
||||
allow_overwrite = true
|
||||
}
|
||||
|
||||
resource "cloudflare_record" "non_proxied_aaaa" {
|
||||
count = var.dns_type == "non-proxied" ? 1 : 0
|
||||
name = var.name
|
||||
content = var.public_ipv6
|
||||
proxied = false
|
||||
ttl = 1
|
||||
type = "AAAA"
|
||||
zone_id = var.cloudflare_zone_id
|
||||
allow_overwrite = true
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ module "tls_secret" {
|
|||
# https://pfsense.viktorbarzin.me/
|
||||
module "pfsense" {
|
||||
source = "./factory"
|
||||
dns_type = "proxied"
|
||||
name = "pfsense"
|
||||
external_name = "pfsense.viktorbarzin.lan"
|
||||
tls_secret_name = var.tls_secret_name
|
||||
|
|
@ -53,6 +54,7 @@ module "pfsense" {
|
|||
# https://nas.viktorbarzin.me/
|
||||
module "nas" {
|
||||
source = "./factory"
|
||||
dns_type = "proxied"
|
||||
name = "nas"
|
||||
external_name = "nas.viktorbarzin.lan"
|
||||
port = 5001
|
||||
|
|
@ -74,6 +76,7 @@ module "nas" {
|
|||
# https://files.viktorbarzin.me/
|
||||
module "nas-files" {
|
||||
source = "./factory"
|
||||
dns_type = "non-proxied"
|
||||
name = "files"
|
||||
external_name = "nas.viktorbarzin.lan"
|
||||
port = 5001
|
||||
|
|
@ -89,6 +92,7 @@ module "nas-files" {
|
|||
# https://idrac.viktorbarzin.me/
|
||||
module "idrac" {
|
||||
source = "./factory"
|
||||
dns_type = "proxied"
|
||||
name = "idrac"
|
||||
external_name = "idrac.viktorbarzin.lan"
|
||||
port = 443
|
||||
|
|
@ -110,6 +114,7 @@ module "idrac" {
|
|||
# TODO: Not working yet
|
||||
module "tp-link-gateway" {
|
||||
source = "./factory"
|
||||
dns_type = "proxied"
|
||||
name = "gw"
|
||||
external_name = "gw.viktorbarzin.lan"
|
||||
port = 443
|
||||
|
|
@ -124,6 +129,7 @@ module "tp-link-gateway" {
|
|||
# https://truenas.viktorbarzin.me/
|
||||
module "truenas" {
|
||||
source = "./factory"
|
||||
dns_type = "proxied"
|
||||
name = "truenas"
|
||||
external_name = "truenas.viktorbarzin.lan"
|
||||
port = 80
|
||||
|
|
@ -168,6 +174,7 @@ module "r730" {
|
|||
# https://proxmox.viktorbarzin.me/
|
||||
module "proxmox" {
|
||||
source = "./factory"
|
||||
dns_type = "proxied"
|
||||
name = "proxmox"
|
||||
external_name = "proxmox.viktorbarzin.lan"
|
||||
port = 8006
|
||||
|
|
@ -189,6 +196,7 @@ module "proxmox" {
|
|||
# https://docker.viktorbarzin.me/ (registry web UI)
|
||||
module "docker-registry-ui" {
|
||||
source = "./factory"
|
||||
dns_type = "proxied"
|
||||
name = "docker"
|
||||
external_name = "docker-registry.viktorbarzin.lan"
|
||||
port = 8080
|
||||
|
|
@ -209,6 +217,7 @@ module "docker-registry-ui" {
|
|||
# https://registry.viktorbarzin.me/ (Docker CLI push/pull endpoint)
|
||||
module "docker-registry-cli" {
|
||||
source = "./factory"
|
||||
dns_type = "non-proxied"
|
||||
name = "registry"
|
||||
external_name = "docker-registry.viktorbarzin.lan"
|
||||
port = 5050
|
||||
|
|
@ -228,6 +237,7 @@ module "docker-registry-cli" {
|
|||
# https://valchedrym.viktorbarzin.me/
|
||||
module "valchedrym" {
|
||||
source = "./factory"
|
||||
dns_type = "proxied"
|
||||
name = "valchedrym"
|
||||
external_name = "valchedrym.viktorbarzin.lan"
|
||||
tls_secret_name = var.tls_secret_name
|
||||
|
|
@ -293,6 +303,7 @@ resource "kubernetes_manifest" "ha_sofia_rate_limit" {
|
|||
|
||||
module "ha-sofia" {
|
||||
source = "./factory"
|
||||
dns_type = "non-proxied"
|
||||
name = "ha-sofia"
|
||||
external_name = "ha-sofia.viktorbarzin.lan"
|
||||
port = 8123
|
||||
|
|
@ -317,6 +328,7 @@ module "ha-sofia" {
|
|||
# https://music-assistant.viktorbarzin.me/
|
||||
module "music-assistant" {
|
||||
source = "./factory"
|
||||
dns_type = "non-proxied"
|
||||
name = "music-assistant"
|
||||
external_name = "ha-sofia.viktorbarzin.lan"
|
||||
port = 8095
|
||||
|
|
@ -332,6 +344,7 @@ module "music-assistant" {
|
|||
# https://ha-london.viktorbarzin.me/
|
||||
module "ha-london" {
|
||||
source = "./factory"
|
||||
dns_type = "non-proxied"
|
||||
name = "ha-london"
|
||||
external_name = "ha-london.viktorbarzin.lan"
|
||||
port = 8123
|
||||
|
|
@ -351,6 +364,7 @@ module "ha-london" {
|
|||
# https://london.viktorbarzin.me/
|
||||
module "london" {
|
||||
source = "./factory"
|
||||
dns_type = "proxied"
|
||||
name = "london"
|
||||
external_name = "openwrt-london.viktorbarzin.lan"
|
||||
port = 443
|
||||
|
|
@ -374,6 +388,7 @@ module "london" {
|
|||
}
|
||||
module "pi-lights" {
|
||||
source = "./factory"
|
||||
dns_type = "proxied"
|
||||
name = "pi"
|
||||
external_name = "ha-london.viktorbarzin.lan"
|
||||
port = 5000
|
||||
|
|
@ -401,6 +416,7 @@ module "pi-lights" {
|
|||
|
||||
module "mbp14" {
|
||||
source = "./factory"
|
||||
dns_type = "proxied"
|
||||
name = "mbp14"
|
||||
external_name = "mbp14.viktorbarzin.lan"
|
||||
port = 4020
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue