# talos-platform – gemeinsame Cluster-Dienste für Produktion (Talos) # Hier liegt alles, was ALLE Apps brauchen. Apps (boday_tf, ...) bringen nur ihr eigenes Chart mit. terraform { required_providers { kubernetes = { source = "hashicorp/kubernetes" version = "~> 2.26" } helm = { source = "hashicorp/helm" version = "~> 2.12" } } backend "consul" { address = "consul:8500" path = "terraform/states/talos_platform" } } variable "kubeconfig_path" { type = string default = "./talos_kubeconfig" } # Version des Traefik-Helm-Charts (traefik/traefik). Bewusst pinnen, damit Updates geplant passieren. variable "traefik_chart_version" { type = string } provider "kubernetes" { config_path = var.kubeconfig_path } provider "helm" { kubernetes { config_path = var.kubeconfig_path } } # Feste IP für Traefik aus dem MetalLB-Pool. Auf diese IP zeigt das OPNsense-Portforwarding (80/443). variable "traefik_lb_ip" { type = string } # Hostname des Longhorn-Dashboards (internes DNS, z.B. Unbound-Override in OPNsense) variable "longhorn_host" { type = string default = "longhorn.boday.lan" } # BasicAuth für Longhorn als htpasswd-Zeile, z.B.: htpasswd -nbB admin 'geheim' variable "longhorn_basic_auth" { type = string sensitive = true } # Kein hostPort mehr -> der Talos-Standard (PSA "baseline") reicht resource "kubernetes_namespace" "traefik" { metadata { name = "traefik" } } # Traefik hinter einem MetalLB-LoadBalancer: VIP wandert bei Node-Ausfall auf den anderen Node resource "helm_release" "traefik" { name = "traefik" repository = "https://traefik.github.io/charts" chart = "traefik" version = var.traefik_chart_version namespace = kubernetes_namespace.traefik.metadata[0].name values = [yamlencode({ deployment = { replicas = 2 } # Je ein Pod pro Node topologySpreadConstraints = [{ maxSkew = 1 topologyKey = "kubernetes.io/hostname" whenUnsatisfiable = "ScheduleAnyway" labelSelector = { matchLabels = { "app.kubernetes.io/name" = "traefik" "app.kubernetes.io/instance" = "traefik-traefik" } } }] service = { type = "LoadBalancer" annotations = { "metallb.io/loadBalancerIPs" = var.traefik_lb_ip } spec = { # Echte Client-IP bleibt erhalten (Django-Logs, CrowdSec) externalTrafficPolicy = "Local" } } ingressClass = { enabled = true isDefaultClass = true name = "traefik" } providers = { kubernetesIngress = { enabled = true } kubernetesCRD = { enabled = true } } # Gemeinsame Routen der Plattform: Longhorn-Dashboard mit Login hinter Traefik extraObjects = [ { apiVersion = "v1" kind = "Secret" metadata = { name = "longhorn-basic-auth", namespace = "longhorn-system" } type = "Opaque" stringData = { users = var.longhorn_basic_auth } }, { apiVersion = "traefik.io/v1alpha1" kind = "Middleware" metadata = { name = "longhorn-auth", namespace = "longhorn-system" } spec = { basicAuth = { secret = "longhorn-basic-auth" } } }, { apiVersion = "networking.k8s.io/v1" kind = "Ingress" metadata = { name = "longhorn" namespace = "longhorn-system" annotations = { "traefik.ingress.kubernetes.io/router.middlewares" = "longhorn-system-longhorn-auth@kubernetescrd" } } spec = { ingressClassName = "traefik" rules = [{ host = var.longhorn_host http = { paths = [{ path = "/" pathType = "Prefix" backend = { service = { name = "longhorn-frontend", port = { number = 80 } } } }] } }] } }, ] })] } output "traefik_lb_ip" { value = var.traefik_lb_ip }