feat(config-preview): chrony + wireguard in Config-Preview verfügbar
- chrony.RenderToString(): rendert /etc/chrony/conf.d/edgeguard.conf ohne Datei-Write oder Service-Reload - wireguard.RenderToString(): kombiniert alle aktiven Interface-Configs; PrivateKey + PresharedKey werden als <redacted> ausgegeben (sicher für UI-Anzeige) - main.go: beide in WithConfigPreviewers eingetragen - Settings UI: chrony + wireguard im Generator-Dropdown Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -49,6 +49,30 @@ func New(pool *pgxpool.Pool) *Generator {
|
||||
|
||||
func (g *Generator) Name() string { return "chrony" }
|
||||
|
||||
// RenderToString renders the chrony config to a string without writing
|
||||
// to disk or reloading the service. Used by the config-preview endpoint.
|
||||
func (g *Generator) RenderToString(ctx context.Context) (string, error) {
|
||||
settings, err := g.Repo.GetSettings(ctx)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("settings: %w", err)
|
||||
}
|
||||
pools, err := g.Repo.ListPools(ctx)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("pools: %w", err)
|
||||
}
|
||||
view := View{
|
||||
Settings: settings,
|
||||
Pools: pools,
|
||||
ListenAddresses: filterNonLoopback(splitCSV(settings.ListenAddresses)),
|
||||
AllowACLs: splitCSV(settings.AllowACL),
|
||||
}
|
||||
var body bytes.Buffer
|
||||
if err := tpl.Execute(&body, view); err != nil {
|
||||
return "", fmt.Errorf("template: %w", err)
|
||||
}
|
||||
return body.String(), nil
|
||||
}
|
||||
|
||||
func (g *Generator) Render(ctx context.Context) error {
|
||||
settings, err := g.Repo.GetSettings(ctx)
|
||||
if err != nil {
|
||||
|
||||
@@ -44,6 +44,78 @@ func New(pool *pgxpool.Pool, box *secrets.Box) *Generator {
|
||||
|
||||
func (g *Generator) Name() string { return "wireguard" }
|
||||
|
||||
// RenderToString renders all active interface configs to a combined
|
||||
// string for the config-preview endpoint. Private keys are redacted
|
||||
// so the output is safe to display in the management UI.
|
||||
func (g *Generator) RenderToString(ctx context.Context) (string, error) {
|
||||
ifs, err := g.Ifaces.List(ctx)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("list ifaces: %w", err)
|
||||
}
|
||||
var combined strings.Builder
|
||||
for _, ifc := range ifs {
|
||||
if !ifc.Active {
|
||||
continue
|
||||
}
|
||||
fmt.Fprintf(&combined, "# ── %s (%s) ────────────────────────────────\n", ifc.Name, ifc.Mode)
|
||||
combined.WriteString("[Interface]\n")
|
||||
fmt.Fprintf(&combined, "Address = %s\n", ifc.AddressCIDR)
|
||||
combined.WriteString("PrivateKey = <redacted>\n")
|
||||
if ifc.ListenPort != nil {
|
||||
fmt.Fprintf(&combined, "ListenPort = %d\n", *ifc.ListenPort)
|
||||
}
|
||||
if ifc.MTU != nil {
|
||||
fmt.Fprintf(&combined, "MTU = %d\n", *ifc.MTU)
|
||||
}
|
||||
combined.WriteString("\n")
|
||||
switch ifc.Mode {
|
||||
case "client":
|
||||
if ifc.PeerPublicKey != nil && ifc.PeerEndpoint != nil {
|
||||
combined.WriteString("[Peer]\n")
|
||||
fmt.Fprintf(&combined, "PublicKey = %s\n", *ifc.PeerPublicKey)
|
||||
fmt.Fprintf(&combined, "Endpoint = %s\n", *ifc.PeerEndpoint)
|
||||
if ifc.AllowedIPs != nil && *ifc.AllowedIPs != "" {
|
||||
fmt.Fprintf(&combined, "AllowedIPs = %s\n", *ifc.AllowedIPs)
|
||||
} else {
|
||||
combined.WriteString("AllowedIPs = 0.0.0.0/0,::/0\n")
|
||||
}
|
||||
if ifc.PersistentKeepalive != nil {
|
||||
fmt.Fprintf(&combined, "PersistentKeepalive = %d\n", *ifc.PersistentKeepalive)
|
||||
}
|
||||
if len(ifc.PeerPSKEnc) > 0 {
|
||||
combined.WriteString("PresharedKey = <redacted>\n")
|
||||
}
|
||||
}
|
||||
case "server":
|
||||
peers, err := g.Peers.ListForInterface(ctx, ifc.ID)
|
||||
if err == nil {
|
||||
sort.Slice(peers, func(i, j int) bool { return peers[i].Name < peers[j].Name })
|
||||
for _, p := range peers {
|
||||
if !p.Enabled {
|
||||
continue
|
||||
}
|
||||
combined.WriteString("[Peer]\n")
|
||||
fmt.Fprintf(&combined, "# %s\n", p.Name)
|
||||
fmt.Fprintf(&combined, "PublicKey = %s\n", p.PublicKey)
|
||||
fmt.Fprintf(&combined, "AllowedIPs = %s\n", p.AllowedIPs)
|
||||
if p.Keepalive != nil {
|
||||
fmt.Fprintf(&combined, "PersistentKeepalive = %d\n", *p.Keepalive)
|
||||
}
|
||||
if len(p.PSKEnc) > 0 {
|
||||
combined.WriteString("PresharedKey = <redacted>\n")
|
||||
}
|
||||
combined.WriteString("\n")
|
||||
}
|
||||
}
|
||||
}
|
||||
combined.WriteString("\n")
|
||||
}
|
||||
if combined.Len() == 0 {
|
||||
return "# No active WireGuard interfaces configured.\n", nil
|
||||
}
|
||||
return combined.String(), nil
|
||||
}
|
||||
|
||||
func (g *Generator) Render(ctx context.Context) error {
|
||||
if err := os.MkdirAll(ConfDir, 0o700); err != nil {
|
||||
return fmt.Errorf("mkdir %s: %w", ConfDir, err)
|
||||
|
||||
Reference in New Issue
Block a user