diff --git a/VERSION b/VERSION index 1b033d7..cb7afba 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.1.142 +1.1.143 diff --git a/cmd/edgeguard-api/main.go b/cmd/edgeguard-api/main.go index 8c564b3..82530fb 100644 --- a/cmd/edgeguard-api/main.go +++ b/cmd/edgeguard-api/main.go @@ -60,7 +60,7 @@ import ( usersvc "git.netcell-it.de/projekte/edgeguard-native/internal/services/users" ) -var version = "1.1.142" +var version = "1.1.143" func main() { addr := os.Getenv("EDGEGUARD_API_ADDR") diff --git a/cmd/edgeguard-ctl/main.go b/cmd/edgeguard-ctl/main.go index 4db399a..edf3a96 100644 --- a/cmd/edgeguard-ctl/main.go +++ b/cmd/edgeguard-ctl/main.go @@ -11,7 +11,7 @@ import ( "git.netcell-it.de/projekte/edgeguard-native/internal/services/setup" ) -var version = "1.1.142" +var version = "1.1.143" const usage = `edgeguard-ctl — EdgeGuard CLI diff --git a/cmd/edgeguard-scheduler/main.go b/cmd/edgeguard-scheduler/main.go index 3f913c8..0746abe 100644 --- a/cmd/edgeguard-scheduler/main.go +++ b/cmd/edgeguard-scheduler/main.go @@ -41,7 +41,7 @@ import ( "git.netcell-it.de/projekte/edgeguard-native/internal/services/tlscerts" ) -var version = "1.1.142" +var version = "1.1.143" const ( // renewTickInterval — how often we re-evaluate expiring certs. @@ -1062,11 +1062,18 @@ func runRenewer(ctx context.Context, r *certrenewer.Service, a *alerts.Service, slog.Info("scheduler: renewer pass complete", "checked", res.Checked, "renewed", res.Renewed, "failed", res.Failed, "skipped", res.Skipped) - if a != nil && res.Failed > 0 && d != nil && d.shouldFire("cert.renew_failed") { - _, _ = a.Fire(ctx, "cert.renew_failed", alerts.SeverityError, - "Cert-Renewal teilweise fehlgeschlagen", - fmt.Sprintf("Renewer-Cycle: %d checked, %d renewed, %d failed, %d skipped", - res.Checked, res.Renewed, res.Failed, res.Skipped)) + if a != nil && d != nil { + for _, domain := range res.FailedDomains { + key := "cert.renew_failed:" + domain + if !d.shouldFire(key) { + continue + } + _, _ = a.Fire(ctx, "cert.renew_failed", alerts.SeverityError, + "Cert-Renewal fehlgeschlagen: "+domain, + "Let's Encrypt Erneuerung für "+domain+" ist fehlgeschlagen. "+ + "Prüfe ACME-Konfiguration und DNS-Erreichbarkeit. "+ + "Nächster Versuch beim nächsten Renewer-Tick (alle 6h).") + } } } diff --git a/internal/services/certrenewer/certrenewer.go b/internal/services/certrenewer/certrenewer.go index 9b1e766..4d7f70b 100644 --- a/internal/services/certrenewer/certrenewer.go +++ b/internal/services/certrenewer/certrenewer.go @@ -62,12 +62,14 @@ func (s *Service) log() *slog.Logger { } // Result counts what one Run pass did so the scheduler can log / -// surface it. +// surface it. FailedDomains holds the domain names that failed so +// the scheduler can fire per-domain alerts. type Result struct { - Checked int - Renewed int - Failed int - Skipped int + Checked int + Renewed int + Failed int + Skipped int + FailedDomains []string } // Run scans tls_certs for LE-issued rows that expire within the @@ -95,6 +97,7 @@ func (s *Service) Run(ctx context.Context) (Result, error) { certPEM, chainPEM, keyPEM, err := s.Issuer.Issue(row.Domain) if err != nil { res.Failed++ + res.FailedDomains = append(res.FailedDomains, row.Domain) s.log().Error("certrenewer: issue failed", "domain", row.Domain, "error", err) _ = s.Repo.MarkError(ctx, row.Domain, err.Error()) continue @@ -102,12 +105,14 @@ func (s *Service) Run(ctx context.Context) (Result, error) { info, err := certstore.Parse(certPEM) if err != nil { res.Failed++ + res.FailedDomains = append(res.FailedDomains, row.Domain) _ = s.Repo.MarkError(ctx, row.Domain, "parse: "+err.Error()) continue } path, err := certstore.WriteCombined(s.CertDir, row.Domain, certPEM, chainPEM, keyPEM) if err != nil { res.Failed++ + res.FailedDomains = append(res.FailedDomains, row.Domain) _ = s.Repo.MarkError(ctx, row.Domain, "write: "+err.Error()) continue }