Files
edgeguard-native/CLAUDE.md
Debian 32ab2c7f47 chore(lint): Backlog auf 0 + golangci-lint als HARTER Gate — v1.3.3
Go-Quality-Baseline-Rollout ABGESCHLOSSEN.

Code-Quality-Backlog (55 → 0):
- errcheck: unbehandelte Close/Rollback/Remove explizit `_ =`; fmt.Sscanf
  `_, _ =` (Zero-Value degradiert sauber).
- unused: toter Code entfernt (nodeIDOrHostname, stripTrailingNewline,
  acme.Service.user, strFold + ungenutzter Import).
- noctx (net/http): http.NewRequestWithContext mit vorhandenem ctx.
- staticcheck: QF1001/S1009/ST1005/SA9003.
- contextcheck: detached-by-design-Stellen mit begründetem //nolint.

Zwei echte Bugs beim Aufräumen gefunden+gefixt:
- backup/remote SFTP-Upload: dst.Close()-Flush-Fehler wurde verschluckt →
  unvollständiges Remote-File galt als Erfolg. Jetzt geprüft+gemeldet.
- haproxy_test: leere if-Assertion (SA9003) testete faktisch nichts →
  echte t.Errorf-Prüfung (kein HSTS für HSTS-disabled Domain).

Bewusste Config-Entscheidungen (.golangci.yml):
- noctx-on-os/exec ausgeschlossen: System-Command-Reloads (systemctl/nft/
  wg/pg) dürfen NICHT an den Request-Context gebunden werden — ein Client-
  Disconnect darf keinen laufenden Reload mitten in der Ausführung killen.
  net/http-noctx bleibt voll aktiv. KEINE exec-Zeile im Code angefasst.
- rowserrcheck/sqlclosecheck raus (database/sql-Linter, bei pgx nur FPs).

Gate scharf gestellt: Makefile release-check ruft golangci-lint jetzt als
HARTEN Gate (install-if-missing, pinned v2.12.2). `make release-check`
grün: vet, golangci-lint, govulncheck, build, test -race.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-06 00:49:07 +02:00

253 lines
12 KiB
Markdown

# ⚠️ GOLDENE REGEL — NIEMALS RATEN. IMMER LESEN.
**Nicht verhandelbar. Keine Ausnahmen.**
Vor jeder Entscheidung über Feldwerte, API-Shapes, Dateinamen, Funktions-Signaturen, Konfigurationsparameter oder Architektur-Details: **erst Original lesen, dann arbeiten**.
- **Referenz-Backend** (Go-Patterns, Migrations, Cluster, Packaging) → `ac_search_code(project_id=6)` oder direkt `/var/www/mail-gateway`
- **Referenz-UI/Bootstrap** (React/AntD, i18n, Design-System, Install-Onliner) → `ac_search_code(project_id=5)` oder direkt `/var/www/netcell-webpanel`
- **Alter Docker-Stack** (nur für Feature-Scope-Referenz!) → `ac_search_code(project_id=3)` oder `/var/www/proxy-lb-waf`
- **Im Zweifel → Rückfrage statt Annahme.**
---
# EdgeGuard Native (`eg`)
> Native Neufassung des Docker-basierten EdgeGuard-Stacks. Kein Docker — alle Dienste nativ unter systemd. Zielplattform: **Debian 13 (Trixie), amd64 + arm64**. Auslieferung als signiertes `.deb`.
>
> **Hinweis:** Mehrere ursprüngliche v1-Nicht-Ziele (WAF, IDS/IPS, DHCP, RADIUS) sind inzwischen umgesetzt — siehe „Feature-Stand" weiter unten.
---
## Repository & Server
- **Gitea:** https://git.netcell-it.de/projekte/edgeguard-native
- **SSH-Clone:** `ssh://git@git.netcell-it.de:1922/projekte/edgeguard-native.git`
- **Lokal (Dev):** `/var/www/edgeguard-native` · `ssh linux@89.163.205.87`
- **Architect Center:** Projekt ID **8**
- **Git-Server:** git.netcell-it.de · SSH Port 1922 · `git push origin main`
---
## MCP-Tools (Architect Center)
**project_id: 8** · Session: `$ARCHITECT_SESSION` · MCP-Server: `architect`
### Beim Session-Start (PFLICHT)
```
ac_recall(query="<aktuelle Aufgabe>", project_id=8, session_name="$(printenv ARCHITECT_SESSION)")
ac_search_code(query="<Stichworte>", project_id=8, session_name="$(printenv ARCHITECT_SESSION)", limit=6)
```
### Alle Tools
| Tool | Wann verwenden |
|------|----------------|
| `ac_recall(query, project_id=8, session_name)` | Session-Start — relevante Memories laden |
| `ac_search_code(query, project_id=8, session_name, limit?)` | Vor JEDEM neuen Code schreiben |
| `ac_search_code_global(query, session_name, limit?)` | Suche über alle Projekte hinweg |
| `ac_remember(text, category, project_id=8, session_name)` | Jede neue Erkenntnis / Entscheidung |
| `ac_create_bug(title, description?, priority?, session_name?)` | Bug gefunden |
| `ac_create_feature(title, description?, priority?, session_name?)` | Feature-Idee |
| `ac_create_task(title, description?, priority?, status?, project_id?)` | Follow-up Arbeit |
| `ac_update_task(id, status?, priority?, title?)` | Task-Status ändern |
| `ac_add_decision(title, content, category?, project_id?)` | Architekturentscheidung |
| `ac_notify(message, title?, type?)` | Status-Update senden |
| `ac_get_project(session_name)` | project_id für Session ermitteln |
| `ac_research(query, context?)` | Doku / CVE / Libraries recherchieren |
| `ac_review(code, language?, focus?)` | Code-Review nach Fertigstellung |
| `ac_analyze(text, question?)` | Logs / Outputs analysieren |
**`ac_remember` Kategorien:** `decision` · `pattern` · `bugfix` · `convention` · `architecture` · `general`
---
## Stack
| Schicht | Technologie |
|---------|-------------|
| **API** | Go 1.26, Gin, GORM (Queries), goose (Migrations) |
| **UI** | React 19, TypeScript strict, Vite, Ant Design 6, TanStack Query 5 |
| **DB** | PostgreSQL 16 (Distro-Paket), goose-Migrations in `migrations/` |
| **State/HA** | PostgreSQL Logical Replication + Cluster-Agent-Heartbeat (mTLS); KeyDB Active-Active nur optional (`Recommends`, für Lizenz-Leader-Election/Shared-Cache) |
| **Proxy/LB** | HAProxy (Distro) — TLS-Termination, L7-Routing, LB |
| **WAF** | Coraza v3 + OWASP CRS, via HAProxy SPOE (`edgeguard-waf`-Agent) |
| **IDS/IPS** | CrowdSec + `crowdsec-firewall-bouncer` (nftables) — managed-wenn-installiert (kein Depends) |
| **VPN** | WireGuard (Kernel-Modul ab 5.6, `wireguard-tools`) |
| **DNS** | Unbound (Distro) — Forwarder+Cache mit DNSSEC, Cluster-internes Split-Horizon |
| **DHCP** | Kea (`kea-dhcp4-server`) — managed, default-off |
| **AAA/RADIUS** | FreeRADIUS (PAP/CHAP, files-based) — managed, default-off |
| **NTP** | chrony (Distro) |
| **VIP/HA** | keepalived (VRRP) |
| **FW** | nftables (Distro) |
| **Forward-Proxy** | Squid (Distro) |
| **Auth/SSO** | JWT (lokal) + 2FA/TOTP + OIDC/OAuth2 (Keycloak u. a.) |
| **TLS** | certbot + webroot-Plugin |
| **Packaging** | dpkg-deb (direkt, wie mail-gateway + netcell-webpanel) |
| **Plattform** | Debian 13 Trixie · amd64 + arm64 |
---
## Feature-Stand (Stand 2026-06)
Über den ursprünglichen v1-Scope hinaus inzwischen **umgesetzt** (waren mal Nicht-Ziele):
- **WAF** — Coraza v3 + OWASP CRS via HAProxy SPOE (`edgeguard-waf`)
- **IDS/IPS** — CrowdSec + `crowdsec-firewall-bouncer` (nftables-Bouncer); managed-wenn-installiert, kein hartes Depends
- **DHCP** — Kea `kea-dhcp4-server` (managed, default-off)
- **RADIUS** — FreeRADIUS PAP/CHAP, files-based (managed, default-off)
- **SSO** — OIDC/OAuth2 (additiv, Rolle aus DB, lokaler Login bleibt)
- **2FA** — TOTP
- **IPv6** — Firewall-Regeln + NAT familienbewusst
- **HA** — VIP via keepalived (VRRP), PG-Logical-Replication, bidirektionaler Cluster-Heartbeat
Damit ist die ursprüngliche v2-Roadmap (WAF, 2FA, IPv6-FW, OIDC, DHCP, RADIUS) abgearbeitet.
## Nicht-Ziele (weiterhin)
- **Kein Docker** — alle Dienste nativ unter systemd
- **Kein Network-IDS Suricata** — Intrusion-Detection läuft über CrowdSec, nicht über Suricata-Paket-Inspektion
- **Keine Mail-Verarbeitung** (eigenes Produkt: mail-gateway)
- **Keine Multi-Tenant-GuardZones**
- **Keine ISO-Builds** (kein EdgeGuardOS-Klon — nur APT)
- **Nur Debian 13** — kein Ubuntu, kein Debian 12, kein RHEL
---
## Binaries & Ports
| Binary | Beschreibung | Bindet | User |
|--------|-------------|--------|------|
| `edgeguard-api` | REST Management-API | `127.0.0.1:9443` | `edgeguard` |
| `edgeguard-scheduler` | Cron-Jobs (ACME-Renew, Backup, Health) | — | `edgeguard` |
| `edgeguard-ctl` | CLI: `initdb migrate cluster-join promote dump-config` | — | root/edgeguard |
HAProxy terminiert TLS auf `:443`, routet per Host-Header an Backends und fällt für Management-FQDN/ACME-Webroot auf `127.0.0.1:9443` (edgeguard-api) zurück. Die Management-UI wird von edgeguard-api ausgeliefert (statisch aus `/usr/share/edgeguard/ui/` oder embedded).
---
## Build
```bash
make build # Host-Architektur (amd64)
make test # go test ./...
make test-race # go test -race ./... (Race-Detector)
make lint # go vet + golangci-lint
make vulncheck # govulncheck ./... (Go-Vuln-DB-Scan)
make release-check # Go-Quality-Baseline (läuft autom. vor jedem deb/publish)
make deb # amd64 + arm64 .deb (führt release-check aus)
make publish # deb + Upload Gitea Package Registry
make install-local # direkt auf Dev-Server installieren (kein .deb)
# UI
cd management-ui && bun install && bun run build
```
### Go-Quality-Baseline (PFLICHT vor jedem Release)
`make deb`/`make publish` führen automatisch `release-check` aus — Reihenfolge:
**`go vet``golangci-lint run``govulncheck ./...``go build``go test -race`**.
- **`govulncheck` ist ein HARTER Gate** — Build bricht ab, wenn der Code eine bekannte CVE tatsächlich aufruft. Tool wird bei Bedarf autom. installiert. Zusätzlich sinnvoll: wöchentlicher CI-Cron (CVEs tauchen auch ohne Code-Änderung auf).
- **`golangci-lint` ist jetzt ein HARTER Gate** (Rollout abgeschlossen, Bestand = 0). `.golangci.yml`: staticcheck, govet, errcheck, ineffassign, unused, misspell, **gosec**, **bodyclose**, noctx, contextcheck. Neuer Fund ⇒ `make deb`/`publish` bricht ab. Bewusste Ausnahmen sind in `.golangci.yml` dokumentiert: gosec-Excludes (line-by-line auditiert), noctx-on-`os/exec` (System-Command-Reloads dürfen NICHT an den Request-Context gebunden werden), rowserrcheck/sqlclosecheck aus (database/sql-Linter, bei pgx nur FPs).
- **`go test -race`** — Race-Detector; findet Nebenläufigkeits-Bugs (Scheduler/Worker), die normale Tests durchlassen.
- Portabel als „Go-Quality-Baseline" für weitere Go-Projekte gedacht.
---
## Dev-Server Quickstart
```bash
# Abhängigkeiten installieren
sudo apt-get install -y postgresql-16 haproxy wireguard-tools squid unbound nftables certbot
# API starten (ohne systemd, für Entwicklung)
go run ./cmd/edgeguard-api/
# UI Dev-Server
cd management-ui && bun run dev
```
---
## Projektstruktur
```
/var/www/edgeguard-native/
├── cmd/
│ ├── edgeguard-api/ # Management-API (Gin, 127.0.0.1:9443)
│ ├── edgeguard-scheduler/ # Cron-Jobs
│ └── edgeguard-ctl/ # CLI
├── internal/
│ ├── database/ # pgxpool + goose-Runner; migrations/ via go:embed
│ │ └── migrations/ # SQL (goose-Format) — embedded ins Binary
│ ├── models/ # GORM-Models
│ ├── handlers/ # HTTP-Handler (REST)
│ ├── services/ # Business-Logik
│ ├── haproxy/ # Config-Generator (TLS + Routing + LB)
│ ├── squid/ # Config-Generator
│ ├── wireguard/ # Config-Generator
│ ├── unbound/ # Config-Generator (Forwarder + Cluster-DNS)
│ ├── firewall/ # nftables-Generator
│ ├── cluster/ # Join/Promote/Peer-Discovery
│ ├── aggregator/ # Cluster-View APIs
│ └── license/ # Lizenz-Validierung
├── management-ui/ # React 19 + AntD 6 (1:1 enconf-Pattern)
├── packaging/debian/ # control, postinst, postrm, systemd-Units
├── deploy/
│ ├── systemd/ # *.service, *.target, *.timer
│ ├── haproxy/ # (Templates liegen jetzt neben Renderer in internal/<svc>/)
│ ├── squid/ # squid.conf.tpl
│ ├── unbound/ # unbound.conf.tpl
│ └── nftables/ # ruleset.nft.tpl
├── scripts/
│ ├── apt-repo/ # build-package.sh, publish.sh
│ └── install.sh # Bootstrap curl-Onliner
├── docs/
│ └── architecture.md # Vollständige Architektur-Spec
├── go.mod
├── Makefile
├── CLAUDE.md # Diese Datei
└── agent.md # Agent-Factory Pipeline
```
---
## Key Conventions
### Go-Code
- **Migrations:** goose SQL-Dateien in `internal/database/migrations/`, via `//go:embed` ins Binary — NICHT GORM AutoMigrate
- **ORM:** GORM für Queries, nicht für Schema-Verwaltung
- **Config-Generierung:** Template-Datei in `deploy/*/`, Generator in `internal/*/`
- **Config-Reload:** `systemctl reload <service>` nach Config-Schreiben
- **Cluster-Primary-Ermittlung:** zuverlässig über `pg_publication` (`edgeguard_shared`); Primary-URL aus `setup.json` `PrimaryFQDN` via `clusterjoin.NormalizePrimaryURL`. **Kein Write-Proxy** — Schreibzugriffe auf geteilte Tabellen erfolgen am Primary.
- **Failover:** `edgeguard-ctl promote` ist Logical-Replication-aware (Subscription lösen → `setupReplicationPrimary` → Publisher werden, inkl. PG-Restart für `wal_level=logical`); erholte Nodes danach via `cluster-setup-standby <neuer-primary>` zurückhängen.
### Packaging
- `dpkg-deb` direkt (wie mail-gateway) — kein dh_make/debhelper/fpm
- postinst: User anlegen → Dirs → initdb (idempotent) → migrate → systemctl
- postrm purge: nur bei `purge` DB + User entfernen
### UI
- 100% enconf-Pattern (netcell-webpanel/management-ui/) — keine eigenen Design-Entscheidungen
- `import type` für alle Type-Imports (verbatimModuleSyntax!)
- AntD 6, kein Tailwind, kein Material UI
### Auth
- JWT-basiert (analog enconf), Secret in `/var/lib/edgeguard/.jwt_fingerprint`
- Admin-Check: immer aus DB-User-Row, nie aus JWT-Payload
---
## Vollständige Architektur
`docs/architecture.md`
---
## Vor jeder Änderung
1. Referenz-Pattern in mail-gateway oder netcell-webpanel lesen
2. `ac_search_code` vor neuem Code — Pattern existiert meist schon
3. `make test` nach Backend-Änderungen
4. `cd management-ui && npx tsc --noEmit` nach Frontend-Änderungen