diff --git a/internal/api/v1/helpers.go b/internal/api/v1/helpers.go index a680dbf..94338cd 100644 --- a/internal/api/v1/helpers.go +++ b/internal/api/v1/helpers.go @@ -60,16 +60,41 @@ func (api *API) reconcileNetworking() { } } - // Determine central domain for HAProxy + // Determine central domain for HAProxy — only if its cert exists centralDomain := "" - if globalCfg.Cloud.Central.Domain != "" { - centralDomain = globalCfg.Cloud.Central.Domain + if d := globalCfg.Cloud.Central.Domain; d != "" { + if _, err := os.Stat(certbot.HAProxyCertPath(d)); err == nil { + centralDomain = d + } else { + slog.Debug("reconcile: skipping central domain in HAProxy (no cert)", "domain", d) + } + } + + // Only include L7 HTTP routes if a wildcard cert exists for TLS termination. + // Without it, HAProxy validation fails and the entire config is rejected. + wildcardCert := "/etc/haproxy/certs/wildcard.pem" + gatewayDomain := "" + if parts := strings.SplitN(globalCfg.Cloud.Central.Domain, ".", 2); len(parts) == 2 { + gatewayDomain = parts[1] + // Also check per-domain cert path + if _, err := os.Stat(certbot.HAProxyCertPath(gatewayDomain)); err == nil { + wildcardCert = certbot.HAProxyCertPath(gatewayDomain) + } + } + + activeHTTPRoutes := httpRoutes + if _, err := os.Stat(wildcardCert); err != nil { + if len(httpRoutes) > 0 { + slog.Warn("reconcile: skipping L7 HTTP routes in HAProxy (no wildcard cert)", "path", wildcardCert) + } + activeHTTPRoutes = nil } // Generate and write HAProxy config haproxyCfg := api.haproxy.GenerateWithOpts(instanceRoutes, nil, haproxy.GenerateOpts{ CentralDomain: centralDomain, - HTTPRoutes: httpRoutes, + HTTPRoutes: activeHTTPRoutes, + WildcardCert: wildcardCert, }) if err := api.haproxy.WriteConfig(haproxyCfg); err != nil {