package ipaddresses import ( "bytes" "context" "fmt" "os/exec" "strings" "git.netcell-it.de/projekte/edgeguard-native/internal/configgen" ) // ConfPath wird von edgeguard-apply-ipaddresses gelesen. const ConfPath = "/etc/edgeguard/ip-addresses.conf" type Generator struct { Repo *Repo } func NewGenerator(repo *Repo) *Generator { return &Generator{Repo: repo} } // Render schreibt /etc/edgeguard/ip-addresses.conf (Format: dev|addr/prefix) // und triggert das apply-Skript via sudo. func (g *Generator) Render(ctx context.Context) error { return g.render(ctx, false) } // RenderSecondary wie Render, aber schließt Ethernet-Interface-IPs aus. // Auf einem Secondary-Node werden eth0-IPs (Public-IP + VIP) von // cloud-init bzw. Keepalived verwaltet — edgeguard soll sie nicht // überschreiben oder entfernen. func (g *Generator) RenderSecondary(ctx context.Context) error { return g.render(ctx, true) } func (g *Generator) render(ctx context.Context, excludeEthernet bool) error { type addrRow struct { dev string addr string prefix int } q := ` SELECT ni.name, ia.address, ia.prefix FROM ip_addresses ia JOIN network_interfaces ni ON ni.id = ia.interface_id WHERE ia.active = true` if excludeEthernet { q += ` AND ni.type != 'ethernet'` } q += ` ORDER BY ni.name, ia.address` rows, err := g.Repo.Pool.Query(ctx, q) if err != nil { return fmt.Errorf("query: %w", err) } defer rows.Close() var entries []addrRow for rows.Next() { var r addrRow if err := rows.Scan(&r.dev, &r.addr, &r.prefix); err != nil { return fmt.Errorf("scan: %w", err) } entries = append(entries, r) } if err := rows.Err(); err != nil { return err } var buf bytes.Buffer buf.WriteString("# Generated by edgeguard-api — DO NOT EDIT.\n") buf.WriteString("# Read by edgeguard-apply-ipaddresses. Format: dev|address/prefix\n") for _, e := range entries { fmt.Fprintf(&buf, "%s|%s/%d\n", sanitize(e.dev), sanitize(e.addr), e.prefix) } if err := configgen.AtomicWrite(ConfPath, buf.Bytes(), 0o644); err != nil { return fmt.Errorf("write %s: %w", ConfPath, err) } if err := applyIPAddresses(); err != nil { return fmt.Errorf("apply: %w", err) } return nil } func applyIPAddresses() error { cmd := exec.Command("sudo", "-n", "/usr/bin/systemctl", "restart", "edgeguard-ipaddresses.service") out, err := cmd.CombinedOutput() if err != nil { return fmt.Errorf("systemctl restart edgeguard-ipaddresses.service: %s: %w", strings.TrimSpace(string(out)), err) } return nil } func sanitize(s string) string { s = strings.ReplaceAll(s, "|", "") s = strings.ReplaceAll(s, "\n", "") return strings.TrimSpace(s) }