feat(configgen): Phase 2 Config-Generator + nginx → HAProxy-only Pivot
Architektur-Pivot: nginx fällt komplett weg. HAProxy 2.8+ übernimmt
TLS-Termination, L7-Routing per Host-Header und LB. ACME-Webroot
und Management-UI werden von edgeguard-api ausgeliefert (Phase 3
implementiert die zugehörigen Handler); HAProxy proxied
/.well-known/acme-challenge/* und Management-FQDN-Traffic an
127.0.0.1:9443. Eine Distro-Abhängigkeit weniger, ein Renderer
weniger, sauberere Trennung.
Renderer (alle mit Embed-Templates + Tests):
* internal/configgen/ — atomic write + systemctl reload helpers
* internal/haproxy/ — :80 + :443, ACME-ACL, Host-Header-Routing,
Stats-Frontend, api_backend Fallback
* internal/firewall/ — default-deny input, stateful baseline,
SSH-Rate-Limit, :80/:443 accept,
Cluster-Peer-Set für mTLS :8443,
Custom-Rules aus PG
* internal/{squid,wireguard,unbound}/ — Stubs (ErrNotImplemented)
Orchestrator + CLI:
* internal/services/configorch/ — fester Reihenfolge-Run, Stubs
sind soft-skip statt fatal
* cmd/edgeguard-ctl render-config [--no-reload] [--only=svc1,svc2]
Packaging:
* postinst: /etc/edgeguard/nginx raus, /var/lib/edgeguard/acme rein,
self-signed _default.pem via openssl req (damit HAProxy startet
bevor certbot etwas issuet hat)
* control: Depends nginx raus, openssl rein
* edgeguard-ui: dependency auf nginx weg, "Served by edgeguard-api
gin StaticFS"
Live-Smoke: render-config gegen lokale PG schreibt /etc/edgeguard/
haproxy/haproxy.cfg + nftables.d/ruleset.nft korrekt; CRUD-Test aus
Phase 2 läuft weiter unverändert.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -23,6 +23,7 @@ Commands:
|
||||
migrate check Validate embedded migrations (no DB connect)
|
||||
migrate dump [dir] Write embedded SQL files to dir (default: ./migrations)
|
||||
initdb Create PostgreSQL role + database (idempotent)
|
||||
render-config Regenerate haproxy / nftables configs from PG (--no-reload, --only=)
|
||||
cluster-join Join an existing cluster (Phase 3, not yet implemented)
|
||||
promote Promote this node's PG to primary (Phase 3, not yet implemented)
|
||||
dump-config Print effective config (Phase 3, not yet implemented)
|
||||
@@ -42,6 +43,8 @@ func main() {
|
||||
os.Exit(cmdMigrate(os.Args[2:]))
|
||||
case "initdb":
|
||||
os.Exit(cmdInitDB(os.Args[2:]))
|
||||
case "render-config":
|
||||
os.Exit(cmdRenderConfig(os.Args[2:]))
|
||||
case "cluster-join", "cluster-leave", "promote", "dump-config":
|
||||
fmt.Fprintf(os.Stderr, "edgeguard-ctl: %q is a Phase-3 stub — not yet implemented\n", os.Args[1])
|
||||
os.Exit(1)
|
||||
|
||||
73
cmd/edgeguard-ctl/render.go
Normal file
73
cmd/edgeguard-ctl/render.go
Normal file
@@ -0,0 +1,73 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"git.netcell-it.de/projekte/edgeguard-native/internal/configgen"
|
||||
"git.netcell-it.de/projekte/edgeguard-native/internal/database"
|
||||
"git.netcell-it.de/projekte/edgeguard-native/internal/firewall"
|
||||
"git.netcell-it.de/projekte/edgeguard-native/internal/haproxy"
|
||||
"git.netcell-it.de/projekte/edgeguard-native/internal/services/configorch"
|
||||
"git.netcell-it.de/projekte/edgeguard-native/internal/squid"
|
||||
"git.netcell-it.de/projekte/edgeguard-native/internal/unbound"
|
||||
"git.netcell-it.de/projekte/edgeguard-native/internal/wireguard"
|
||||
)
|
||||
|
||||
// cmdRenderConfig regenerates every per-service config file from PG
|
||||
// state. Used after package install (postinst), after admin
|
||||
// mutations (UI button), or as a manual recovery action.
|
||||
//
|
||||
// Flags:
|
||||
//
|
||||
// --no-reload Write configs but skip systemctl reload.
|
||||
// --only=svc1,svc2 Run only the named generators.
|
||||
func cmdRenderConfig(args []string) int {
|
||||
skipReload := false
|
||||
var only []string
|
||||
for i := 0; i < len(args); i++ {
|
||||
switch {
|
||||
case args[i] == "--no-reload":
|
||||
skipReload = true
|
||||
case strings.HasPrefix(args[i], "--only="):
|
||||
val := strings.TrimPrefix(args[i], "--only=")
|
||||
only = strings.Split(val, ",")
|
||||
case args[i] == "-h" || args[i] == "--help":
|
||||
fmt.Println("Usage: edgeguard-ctl render-config [--no-reload] [--only=svc1,svc2]")
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
|
||||
defer cancel()
|
||||
|
||||
pool, err := database.Open(ctx, database.ConnStringFromEnv())
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, "render-config: open db:", err)
|
||||
return 1
|
||||
}
|
||||
defer pool.Close()
|
||||
|
||||
hap := haproxy.New(pool)
|
||||
fw := firewall.New(pool)
|
||||
sq := squid.New()
|
||||
wg := wireguard.New()
|
||||
ub := unbound.New()
|
||||
if skipReload {
|
||||
hap.SkipReload = true
|
||||
fw.SkipReload = true
|
||||
}
|
||||
|
||||
gens := []configgen.Generator{hap, fw, sq, wg, ub}
|
||||
|
||||
results, runErr := configorch.Run(ctx, gens, only)
|
||||
fmt.Print(configorch.Summarise(results))
|
||||
if runErr != nil {
|
||||
fmt.Fprintln(os.Stderr, "render-config aborted:", runErr)
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
Reference in New Issue
Block a user