package networkifs import ( "bytes" "context" "encoding/json" "fmt" "os/exec" "strings" "git.netcell-it.de/projekte/edgeguard-native/internal/configgen" ) // ConfPath is read by edgeguard-apply-interfaces. const ConfPath = "/etc/edgeguard/interfaces.conf" type Generator struct { Repo *Repo } func NewGenerator(repo *Repo) *Generator { return &Generator{Repo: repo} } // Render writes /etc/edgeguard/interfaces.conf (format: type|name|parent|vlan_id|mtu|members) // for VLAN/bridge/bond interfaces and triggers edgeguard-interfaces.service. // Ethernet and WireGuard interfaces are managed by the OS / wg-quick and are excluded. func (g *Generator) Render(ctx context.Context) error { rows, err := g.Repo.Pool.Query(ctx, ` SELECT type, name, COALESCE(parent, ''), COALESCE(vlan_id::text, ''), COALESCE(mtu::text, ''), members FROM network_interfaces WHERE active = true AND type IN ('vlan', 'bridge', 'bond') AND (type = 'vlan' OR jsonb_array_length(members) > 0) ORDER BY type, name`, ) if err != nil { return fmt.Errorf("query: %w", err) } defer rows.Close() type ifRow struct { typ string name string parent string vlanID string mtu string members []string } var entries []ifRow for rows.Next() { var r ifRow var membersRaw []byte if err := rows.Scan(&r.typ, &r.name, &r.parent, &r.vlanID, &r.mtu, &membersRaw); err != nil { return fmt.Errorf("scan: %w", err) } if len(membersRaw) > 0 { _ = json.Unmarshal(membersRaw, &r.members) } 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-interfaces. Format: type|name|parent|vlan_id|mtu|members\n") for _, e := range entries { fmt.Fprintf(&buf, "%s|%s|%s|%s|%s|%s\n", sanitizeIf(e.typ), sanitizeIf(e.name), sanitizeIf(e.parent), sanitizeIf(e.vlanID), sanitizeIf(e.mtu), sanitizeIf(strings.Join(e.members, ","))) } if err := configgen.AtomicWrite(ConfPath, buf.Bytes(), 0o644); err != nil { return fmt.Errorf("write %s: %w", ConfPath, err) } if err := applyInterfaces(); err != nil { return fmt.Errorf("apply: %w", err) } return nil } func applyInterfaces() error { cmd := exec.Command("sudo", "-n", "/usr/bin/systemctl", "restart", "edgeguard-interfaces.service") out, err := cmd.CombinedOutput() if err != nil { return fmt.Errorf("systemctl restart edgeguard-interfaces.service: %s: %w", strings.TrimSpace(string(out)), err) } return nil } func sanitizeIf(s string) string { s = strings.ReplaceAll(s, "|", "") s = strings.ReplaceAll(s, "\n", "") return strings.TrimSpace(s) }