refactor: Angie → nginx + Unbound (DNS Forwarder + Cluster-Split-Horizon)

Reverse-Proxy von Angie (eigenes APT-Repo) auf nginx (Distro) umgestellt
— vereinfacht Bootstrap (kein angie.software-Repo mehr), reduziert
Offene-Punkte (arm64-Verfügbarkeit entfällt). Neuer Service Unbound
übernimmt zwei Rollen: Caching-Forwarder mit DNSSEC und Cluster-internes
Split-Horizon (Local-Zone eg.cluster, Peer-Adressen aus PG ha_nodes,
Reload via unbound-control). Architektur-Spec §7.5 dokumentiert beide
Rollen + Config-Schichtung.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Debian
2026-05-08 23:06:56 +02:00
parent cf10bd4072
commit 9f75eec756
12 changed files with 74 additions and 35 deletions

View File

@@ -8,7 +8,7 @@ EdgeGuard ist die native Neufassung des bisherigen Docker-basierten Reverse-Prox
## 0. Leitplanken (nicht verhandelbar)
- **Kein Docker.** Alle Dienste nativ unter `systemd`, installiert via `apt`. Distro-Pakete für Drittsoftware (HAProxy, Angie, Squid, WireGuard, PostgreSQL, KeyDB, certbot), eigene `.deb`-Pakete für EdgeGuard-Code (api, ui, ctl).
- **Kein Docker.** Alle Dienste nativ unter `systemd`, installiert via `apt`. Distro-Pakete für Drittsoftware (HAProxy, nginx, Squid, WireGuard, Unbound, PostgreSQL, KeyDB, certbot), eigene `.deb`-Pakete für EdgeGuard-Code (api, ui, ctl).
- **Plattform-Matrix:** Debian 13 (Trixie) **und** Ubuntu 24.04 LTS (Noble Numbat), je amd64 + arm64. Alle vier Targets gleichberechtigt.
- **Auslieferung:** signierte `.deb`-Pakete + Meta-Paket via APT. Bootstrap ist der enconf-analoge curl-Onliner `curl -fsSL https://get.edgeguard.netcell-it.de | sudo bash`.
- **HA nativ als Cluster:** N symmetrische Peers, **KeyDB Active-Active** für Shared State + **PostgreSQL Streaming Replication** (single writer, transparenter API-Write-Proxy) + **Floating-IP des Hosters** für HTTP/HTTPS-Ingress (nicht VRRP, nicht DNS-RR).
@@ -24,9 +24,10 @@ EdgeGuard ist die native Neufassung des bisherigen Docker-basierten Reverse-Prox
| Service | Rolle | Distro-Paket | Config-Quelle |
|---|---|---|---|
| **HAProxy** | Public-Ingress (80/443), interner LB (8081), TLS-PassThrough | `haproxy` (Debian/Ubuntu) | aus PG generiert, `systemctl reload haproxy` |
| **Angie** | Reverse-Proxy, vHost-Routing, ACME-Webroot | eigenes APT-Repo (`angie`) | aus PG generiert, `systemctl reload angie` |
| **nginx** | Reverse-Proxy, vHost-Routing, ACME-Webroot | `nginx` (Debian/Ubuntu) | aus PG generiert, `systemctl reload nginx` |
| **Squid** | Forward-Proxy mit ACL/Auth | `squid` | aus PG generiert, `systemctl reload squid` |
| **WireGuard** | Site-to-Site- + Road-Warrior-VPN | `wireguard-tools` (Kernel-Modul ab Kernel 5.6) | aus PG generiert, `wg syncconf` |
| **Unbound** | Caching-Forwarder mit DNSSEC + Cluster-internes Split-Horizon (siehe §7.5) | `unbound` (Debian/Ubuntu) | aus PG generiert, `unbound-control reload` |
| **nftables** | Firewall (Input + Forward + NAT) | `nftables` | aus PG generiert, `nft -f /etc/nftables.conf` |
**Control-Plane:**
@@ -36,7 +37,7 @@ EdgeGuard ist die native Neufassung des bisherigen Docker-basierten Reverse-Prox
| `edgeguard-api` | Go/Gin REST-API, bindet `127.0.0.1:9443`, Reads aus lokaler PG, Writes an Cluster-Primary |
| `edgeguard-scheduler` | Cron-artige Jobs (ACME-Renewal-Hook, Backup, Health-Aggregation, License-Heartbeat) |
| `edgeguard-ctl` | CLI für Setup/Wartung (`initdb`, `migrate`, `cluster-join`, `promote`, `dump-config`) |
| `management-ui` | React 19 + AntD 6 + Vite, statisch unter `/usr/share/edgeguard/ui/`, Angie liefert aus |
| `management-ui` | React 19 + AntD 6 + Vite, statisch unter `/usr/share/edgeguard/ui/`, nginx liefert aus |
| **PostgreSQL 16** | Single Source of Truth — Domains, Backends, Routing-Rules, ACLs, Peers, etc. |
| **KeyDB** (Redis-kompatibel) | Active-Active-Replication, Cluster-State, Locks, Rate-Counter, Pub/Sub für Config-Reload |
@@ -55,9 +56,10 @@ EdgeGuard ist die native Neufassung des bisherigen Docker-basierten Reverse-Prox
│ ├── handlers/ # HTTP-Handler (REST)
│ ├── services/ # Business-Logik (config-render, health-check, cluster-sync)
│ ├── haproxy/ # HAProxy-Config-Generator
│ ├── angie/ # Angie-Config-Generator (vHosts, Upstreams, ACME)
│ ├── nginx/ # nginx-Config-Generator (vHosts, Upstreams, ACME)
│ ├── squid/ # Squid-Config-Generator (squid.conf + squid.d/*)
│ ├── wireguard/ # WireGuard-Config-Generator (wg-quick + wg syncconf)
│ ├── unbound/ # Unbound-Config-Generator (Forwarder + Cluster-DNS)
│ ├── firewall/ # nftables-Ruleset-Generator
│ ├── cluster/ # Join/Promote/Peer-Discovery, KeyDB-Replication-Setup, pg_basebackup
│ ├── proxy/ # API-Write-Proxy-Middleware (Replica → Primary), mTLS-Calls
@@ -72,8 +74,9 @@ EdgeGuard ist die native Neufassung des bisherigen Docker-basierten Reverse-Prox
├── deploy/
│ ├── systemd/ # *.service, *.target, *.timer
│ ├── haproxy/ # haproxy.cfg.tpl
│ ├── angie/ # nginx.conf.tpl, sni-map.tpl
│ ├── nginx/ # nginx.conf.tpl, sni-map.tpl
│ ├── squid/ # squid.conf.tpl
│ ├── unbound/ # unbound.conf.tpl
│ └── nftables/ # ruleset.nft.tpl
├── scripts/
│ ├── apt-repo/ # build-package.sh, publish.sh, setup-repo.sh
@@ -97,16 +100,14 @@ Drei Pakete + Meta — analog nmg, kein WAF-Paket weil kein WAF in v1.
| Paket | Arch | Inhalt | Depends |
|---|---|---|---|
| `edgeguard-api` | amd64, arm64 | `/usr/bin/edgeguard-{api,scheduler,ctl}`, Unit-Files, Migrations, Default-Configs | `postgresql-16`, `keydb-server`, `angie`, `haproxy`, `squid`, `wireguard-tools`, `nftables`, `certbot` |
| `edgeguard-ui` | all | `/usr/share/edgeguard/ui/` (statische Build-Artefakte), Angie-Site | `edgeguard-api (= ${binary:Version})`, `angie` |
| `edgeguard-api` | amd64, arm64 | `/usr/bin/edgeguard-{api,scheduler,ctl}`, Unit-Files, Migrations, Default-Configs | `postgresql-16`, `keydb-server`, `nginx`, `haproxy`, `squid`, `wireguard-tools`, `unbound`, `nftables`, `certbot` |
| `edgeguard-ui` | all | `/usr/share/edgeguard/ui/` (statische Build-Artefakte), nginx-Site | `edgeguard-api (= ${binary:Version})`, `nginx` |
| `edgeguard-meta` | all | keine Dateien, nur `Depends` | `edgeguard-api`, `edgeguard-ui` |
Pro Release: 1 arch-spezifisch × 2 Dists × 2 Arches = 4 `.deb` + 2 arch-agnostische × 2 Dists = 4 `.deb`**8 Artefakte je Release**.
**KeyDB-Herkunft:** KeyDB ist weder in `trixie` noch `noble` in den offiziellen Repos. Wir bauen es aus Source (amd64 + arm64), veröffentlichen es parallel im eigenen APT-Repo. `edgeguard-api` `Depends: keydb-server` löst aus unserem Repo aus.
**Angie-Herkunft:** Eigenes APT-Repo bei `angie.software``edgeguard-api` `Depends: angie`. Im `install.sh` wird das Angie-Repo zusätzlich zu unserem hinzugefügt.
**Build-Werkzeug:** **direkter `dpkg-deb`-Build** analog WebPanel/EdgeGuardOS-Pattern. **Nicht** `dh_make`/`debhelper`, **nicht** `fpm`. Konsistenz mit existierendem Workflow.
**postinst (`edgeguard-api`):**
@@ -128,9 +129,10 @@ Pro Release: 1 arch-spezifisch × 2 Dists × 2 Arches = 4 `.deb` + 2 arch-agnost
├── edgeguard.yaml # Hauptconfig (conffile)
├── api.env # API-Secrets (mode 0600, edgeguard:edgeguard)
├── haproxy/ # haproxy.cfg-Fragmente (von edgeguard-api generiert)
├── angie/ # vHosts + sni-map (generiert)
├── nginx/ # vHosts + sni-map (generiert)
├── squid/ # squid.conf-Fragmente
├── wireguard/ # wg0.conf etc. (generiert)
├── unbound/ # unbound.conf + cluster-zone.conf (generiert)
├── nftables.d/ # Ruleset-Fragmente
└── tls/ # ACME-verwaltete Zertifikate (0750, edgeguard:edgeguard)
@@ -152,7 +154,7 @@ Pro Release: 1 arch-spezifisch × 2 Dists × 2 Arches = 4 `.deb` + 2 arch-agnost
/usr/share/edgeguard/
├── ui/ # statische React-Build-Artefakte
└── templates/ # Config-Templates für haproxy/angie/squid/wireguard/nftables
└── templates/ # Config-Templates für haproxy/nginx/squid/wireguard/unbound/nftables
```
Entspricht FHS — keine Überraschungen für Admins, Lintian-clean.
@@ -184,9 +186,9 @@ SystemCallFilter=@system-service
ReadWritePaths=/var/lib/edgeguard /var/log/edgeguard /etc/edgeguard
```
Drittsoftware (HAProxy, Angie, Squid, WireGuard via `wg-quick@.service`, nftables) läuft als **Distro-Units**. EdgeGuard generiert deren Config + signalisiert Reload, übernimmt aber die Service-Verwaltung **nicht**.
Drittsoftware (HAProxy, nginx, Squid, WireGuard via `wg-quick@.service`, Unbound, nftables) läuft als **Distro-Units**. EdgeGuard generiert deren Config + signalisiert Reload, übernimmt aber die Service-Verwaltung **nicht**.
API bindet auf `127.0.0.1:9443` (nicht öffentlich). Angie terminiert TLS auf `:443` und proxied an die API.
API bindet auf `127.0.0.1:9443` (nicht öffentlich). nginx terminiert TLS auf `:443` und proxied an die API.
---
@@ -218,6 +220,40 @@ KeyDB hört nur auf `127.0.0.1:6379` für lokale Clients und `<node-ip>:16379` (
---
## 7.5 Unbound — DNS Forwarder + Cluster-DNS
Unbound erfüllt zwei Rollen, beide aus PG generiert:
### Rolle 1 — Caching-Forwarder mit DNSSEC
- Forwardet rekursive Queries an Upstream-Resolver (default `1.1.1.1`, `9.9.9.9`; per UI/PG konfigurierbar).
- **DNSSEC-Validation aktiv** (`auto-trust-anchor-file`).
- Lokaler Cache (TTL nach Upstream-Antwort).
- Listen: `127.0.0.1:53` für die EdgeGuard-Box selbst und `<node-internal-ip>:53` für VPN- und LAN-Clients (über nftables-ACL gefiltert).
- Genutzt von `edgeguard-api`, `edgeguard-scheduler` (License-Heartbeat, ACME), Squid (für Forward-Proxy-Resolutions), HAProxy/nginx (Backend-Health-Checks).
### Rolle 2 — Cluster-internes Split-Horizon
- **Local-Zone** `eg.cluster.` enthält A/AAAA-Records aller Cluster-Peers (Node-Hostnamen aus PG `ha_nodes`).
- Beispiel: `node1.eg.cluster → 10.42.0.11`, `node2.eg.cluster → 10.42.0.12`.
- Wird bei jedem Node-Join/-Leave aus PG regeneriert + via `edgeguard:config-changed` Pub/Sub auf allen Peers neu geladen (`unbound-control reload`).
- Cluster-interner Traffic (PG-Replication, KeyDB-Replication, mTLS-API-Calls, Cert-Push) löst Peer-Adressen ausschließlich über diese Zone auf — kein DNS-Roundtrip ins öffentliche Internet, keine `/etc/hosts`-Synchronisation.
- `<node-name>.eg.cluster` ist **nicht extern erreichbar** (nur über Unbound der Cluster-Peers).
### Config-Schichten
`/etc/edgeguard/unbound/unbound.conf` ist Distro-Konfig-Datei. Generator schreibt drei Includes:
```
# /etc/edgeguard/unbound/forwarders.conf — Upstream-Resolver
# /etc/edgeguard/unbound/cluster-zone.conf — Local-Zone eg.cluster
# /etc/edgeguard/unbound/access.conf — access-control: pro CIDR
```
Reload via `unbound-control reload` (kein Restart, keine Cache-Invalidierung außer für die geänderte Zone — `unbound-control auth_zone_reload eg.cluster`).
---
## 8. Cluster-Topologie & HA pro Service
**N symmetrische Peers** (1 … N Nodes, jeder vollwertig). Keine VRRP, keine Master/Backup-Rollen für Daten-Services. Public-IP: **Floating-IP des Hosters** (siehe §9).
@@ -225,9 +261,10 @@ KeyDB hört nur auf `127.0.0.1:6379` für lokale Clients und `<node-ip>:16379` (
| Service | HA-Strategie |
|---|---|
| **HAProxy** | stateless, pro Node identisch. Floating-IP zeigt zum aktuellen aktiven Node; bei Node-Ausfall API-Call zum Hoster (oder manueller Switch) reicht. |
| **Angie** | stateless, pro Node identisch. ACME-Issue nur auf License-Leader (KeyDB-Lock); Zerts werden via PG/mTLS an alle verteilt. |
| **nginx** | stateless, pro Node identisch. ACME-Issue nur auf License-Leader (KeyDB-Lock); Zerts werden via PG/mTLS an alle verteilt. |
| **Squid** | stateless (Cache lokal, kein Sync nötig). Pro Node identische ACL-Config. |
| **WireGuard** | siehe §8.1 |
| **Unbound** | stateless (Cache lokal). Pro Node identische Forwarder-Config + identische Cluster-internen Local-Zones (siehe §7.5). |
| **nftables** | pro Node identisch, Ruleset aus PG generiert. `crowdsec_blocklist`/`threat_intel_blocklist`-Sets entfallen in v1 (kein CrowdSec). |
| **edgeguard-api** | pro Node, Reads lokal, Writes via Proxy zu Primary. |
| **edgeguard-ui** | statisch, pro Node identisch. |
@@ -246,7 +283,7 @@ Drei Optionen, für v1 wählen wir **Option A**:
### 8.2 Manual Promote (PG-Primary-Failover)
1:1 nmg-Pattern (siehe `mail-gateway/docs/architecture.md` §6.2). Bei Ausfall des Primary antworten Config-Writes mit `503 + actionable Error`. Admin promotet via UI/CLI. Datenebene (HAProxy/Angie/Squid/WireGuard) läuft unbeeinträchtigt weiter, weil jeder Node eine lokale PG-Replica hat.
1:1 nmg-Pattern (siehe `mail-gateway/docs/architecture.md` §6.2). Bei Ausfall des Primary antworten Config-Writes mit `503 + actionable Error`. Admin promotet via UI/CLI. Datenebene (HAProxy/nginx/Squid/WireGuard/Unbound) läuft unbeeinträchtigt weiter, weil jeder Node eine lokale PG-Replica hat.
### 8.3 License-Leader-Election
@@ -284,8 +321,7 @@ Schritte (idempotent, analog `netcell-webpanel/install.sh`):
3. **Base-Deps:** `curl gnupg ca-certificates apt-transport-https`.
4. **APT-Keyrings:**
- `https://apt.netcell-it.de/edgeguard/repository.key``/etc/apt/keyrings/netcell-edgeguard.gpg`
- Angie-Repo-Key (für `angie`-Paket)
5. **APT-Sources:** `/etc/apt/sources.list.d/netcell-edgeguard.list` + Angie-List.
5. **APT-Sources:** `/etc/apt/sources.list.d/netcell-edgeguard.list`.
6. **Install:** `apt-get install -y edgeguard` (Meta-Paket).
7. **Auto-Security-Updates:** `unattended-upgrades` + `apt-listchanges` (nach enconf-Muster).
8. **Setup-Modus:** `edgeguard-api` läuft im Setup-Modus bis Admin-User existiert. UI leitet alle Anfragen auf `/setup` um. Wizard: Admin-Account, FQDN, ACME-Email, Lizenz oder Trial.
@@ -328,9 +364,9 @@ Build-/Release-Scripts identisch zu `mail-gateway/scripts/apt-repo/`.
### 12.2 ACME
- **certbot** (Distro-Paket) mit `--webroot`-Plugin für Angie-vHosts.
- **certbot** (Distro-Paket) mit `--webroot`-Plugin für nginx-vHosts.
- **Lock vor Issue:** `acme:lock:<domain>` in KeyDB verhindert Parallel-Issue auf zwei Nodes.
- **Deploy-Hook:** schreibt Sentinel-Datei → `edgeguard-cert-deploy.path`-Unit triggert Angie-/HAProxy-Reload.
- **Deploy-Hook:** schreibt Sentinel-Datei → `edgeguard-cert-deploy.path`-Unit triggert nginx-/HAProxy-Reload.
- **Cert-Verteilung im Cluster:** Issuing-Node pushed via mTLS-API an alle Peers, Zerts landen in `/etc/edgeguard/tls/`.
---
@@ -379,6 +415,5 @@ Der alte `proxy-lb-waf`-Code bleibt für Bestandskunden im Wartungsmodus, keine
## Offene Punkte
- **Hoster + Floating-IP-API** (§9): Spec dokumentieren.
- **Angie-Repo-Verfügbarkeit** für arm64 prüfen.
- **WireGuard-State-Replication** in der Praxis testen (Handshake-Latenz nach Floating-IP-Switch messen).
- **`get.edgeguard.netcell-it.de`** anlegen oder Übergangs-URL auf `apt.netcell-it.de/edgeguard/install.sh` nutzen.