server läuft
This commit is contained in:
1 parent
c8df9d7651
commit
42f0c0accb
21 files changed
+4185
-10
No files matched your search
@@ -148,7 +148,33 @@ Klassische Ingress-Lösungen wie Nginx Proxy Manager (NPM) erfordern eine manuel
|
||||
Jeder Payload vom Agenten an den Server muss vom Server gegen die zugewiesene Policy geprüft werden:
|
||||
|
||||
* **Domain Spoofing Protection:** Beansprucht ein Agent ein Label `Host('kunde.de')`, prüft der Server, ob `kunde.de` im Feld `allowed_domains` der Mandanten-Lizenz hinterlegt ist. Falls nicht: Ablehnung mit Sicherheits-Audit-Log.
|
||||
* **Route Limits:** Übersteigt die Anzahl der deklarierten Routen das Limit (`max_routes`), werden überzählige Routen verworfen.
|
||||
* **Route Limits:** Übersteigt die Anzahl der deklarierten Routen das Limit (`max_routes`), werden überzählige Routen verworfen (deterministisch: nach Router-Namen sortiert, die hinteren fallen weg).
|
||||
|
||||
Umsetzung in `pkg/policy`. Jeder Verstoß entfernt nur den betroffenen Router und wird als Violation (Audit-Log) gemeldet:
|
||||
|
||||
* **Regel-Analyse statt Textsuche:** Traefik-Regeln werden geparst (Matcher, `&&`, `||`, `!`, Klammern).
|
||||
* Jeder `||`-Zweig muss durch `Host(...)` eingeschränkt sein. ``Host(`kunde.de`) || PathPrefix(`/`)`` würde sonst den Verkehr **aller** Mandanten abgreifen.
|
||||
* Negierte Hosts schränken nicht ein.
|
||||
* `HostRegexp` ist nicht erlaubt, weil sich ein Regex nicht gegen `allowed_domains` prüfen lässt.
|
||||
* **Namensraum:** Router, Services und Middlewares heißen in der ausgelieferten Konfiguration `<tenant>__<node>__<name>`.
|
||||
* Mandanten können sich damit weder überschreiben noch gegenseitig referenzieren.
|
||||
* IDs: alphanumerisch mit `-`/`_`, nicht am Rand, kein `__`.
|
||||
* Das Präfix `sultan__` und die Mandanten-ID `sultan` sind reserviert.
|
||||
* **Fremde Provider:** Referenzen mit `@` (z. B. `api@internal` = Traefik-Dashboard) sind verboten, außer sie stehen in einer Freigabeliste des Admins (z. B. `auth@file`).
|
||||
* **Middleware-Ziele begrenzen (geplant):** Middlewares mit eigenem Netzwerkziel (z. B. `forwardAuth`, Redirects auf andere Hosts) sollen auf das eigene Backend des Dienstes beschränkt werden können.
|
||||
* Grenze: das Tupel `ip:port`, `[ipv6]:port` bzw. `fqdn:port`, eventuell plus höchstens ein Pfadsegment.
|
||||
* Redirect/Auth auf einen Host derselben Zone (z. B. `acronym.meine.home.lab` → `acronym-service.meine.home.lab`) soll möglich bleiben, um Zertifikate zu erleichtern.
|
||||
* Aktivierung **per Label**; zunächst im **Beobachtungsmodus** (nur Audit-Log, keine Sperre).
|
||||
* Zunächst werden nur die mitgelieferten Redirects (to-www/to-non-www) angeboten; alles andere wird **geduldet**.
|
||||
* **Umgesetzt ist die Andockstelle** `policy.Engine.MiddlewareTargetCheck(mwName, def, backend)`:
|
||||
* nil (Default): alles dulden
|
||||
* mit Prüffunktion und `EnforceMiddlewareTargets = false`: Verstoß nur melden (`Observed`)
|
||||
* mit `true`: Router entfernen
|
||||
* Regeln und Label-Name folgen später.
|
||||
* **Middleware-Gates:**
|
||||
* Ohne Feature `custom_middleware` sind nur Basistypen erlaubt: redirectScheme, redirectRegex, stripPrefix(Regex), addPrefix, replacePath(Regex), compress, headers.
|
||||
* `chain` und `errors` sind **immer** verboten, weil sie per Namen auf andere Middlewares/Services verweisen und den Namensraum umgehen könnten.
|
||||
* Nicht referenzierte Services/Middlewares werden nicht ausgeliefert.
|
||||
|
||||
### 3.4 Bedrohungsmodell: Erstkontakt (TOFU) und Server-Events
|
||||
|
||||
@@ -191,6 +217,55 @@ Jeder Payload vom Agenten an den Server muss vom Server gegen die zugewiesene Po
|
||||
|
||||
```
|
||||
|
||||
* **Signatur:** Ed25519 mit dem Server-Master-Key.
|
||||
* Signiert wird `SULTAN-LICENSE-V1\n` + JSON der Felder in fester Reihenfolge (`tenant_id`, `tier`, `max_routes`, `allowed_domains`, `features`, `valid_until`).
|
||||
* `valid_until` geht als RFC 3339 in UTC mit ganzen Sekunden ein.
|
||||
* Jede Änderung eines Felds, auch die Reihenfolge der Domains, macht die Signatur ungültig.
|
||||
* Das Präfix trennt Lizenz-Signaturen von künftigen anderen Nutzungen desselben Master-Keys (z. B. signierte Server-Events, 3.4).
|
||||
* **Einlesen:** Unbekannte Felder werden abgelehnt; `features` fehlt → leere Liste.
|
||||
* **`allowed_domains`:** Kleinbuchstaben, ohne Punkt am Ende, IDN als Punycode (`xn--…`).
|
||||
* `shop.de` deckt **genau** `shop.de`.
|
||||
* `*.firma.de` deckt **jede Subdomain beliebiger Tiefe**, aber **nicht** `firma.de` selbst.
|
||||
* Wildcards nur ganz vorne; `*.de` (Wildcard auf TLD) und `*` sind unzulässig.
|
||||
* **Ablauf:** Der Zeitpunkt `valid_until` selbst gilt als abgelaufen. „Verbleibende Tage“ (Checkmk) = abgerundete volle Tage, nach Ablauf negativ.
|
||||
* **Routen:** `max_routes = -1` bedeutet unbegrenzt.
|
||||
|
||||
### 4.1a Leases (Entscheidung 2026-10-01)
|
||||
|
||||
* Eine **Lease ist eine kurzlaufende, signierte Kopie der Lizenz** (gleiches Format, eigenes `valid_until`). Sie ist der „Ausweis“, mit dem ein Knoten Routen bekommt.
|
||||
* **Kommerziell:** Laufzeit **7 Tage**, Erneuerung nach rund 2/3 (ab Tag 4–5); bei Wartung/Baustelle 10 Tage, Erneuerung ab Tag 5. Angelehnt an ACME (90 Tage, Erneuerung nach ~60). Die Lease endet spätestens mit dem bezahlten Abo-Zyklus (`valid_until` der Lizenz).
|
||||
* **Community:** Laufzeit **max. 14 Tage**, Erneuerung nach **ca. 7 Tagen mit zufälliger Streuung** (damit nicht alle Agents gleichzeitig erneuern). Der Server stellt Lizenz und Lease selbständig neu aus (re-license). Es gibt kein Mahnwesen.
|
||||
* Eine Wartung des Servers darf damit bis zu 7 Tage dauern: Bis die Lease abläuft, wird nichts invalidiert, keine Authentifizierung, keine Policy, keine Route.
|
||||
* Ist das Backend in dieser Zeit nicht erreichbar, zeigt der Lander eine 50x-Seite („geht gerade nicht …“) mit Hinweis auf den Pro-Service.
|
||||
* **Unterbrechung jederzeit serverseitig möglich:** Agent und Traefik pollen bzw. halten den Stream, also wirkt eine Unterbrechung sofort (`Hub.Disconnect`, keine Erneuerung, Policy-Stufe).
|
||||
* Gründe: Security-Breach, DDoS, Domain-Verstoß.
|
||||
* Nur kommerziell zusätzlich: Verstoß gegen Nutzungsrichtlinien (z. B. strafbare Inhalte), Zahlungsausfall, unterbrochenes Abo.
|
||||
* **Eskalation (nur kommerziell), analog zu Mahnstufen:** Karenzzeit mit Wasserzeichen → Firemark → Paywall-Landingpage („Kauf mich!“), siehe Stufen in 5.2.
|
||||
|
||||
### 4.1b Agent-Konfiguration `/etc/sultan.d/*.yaml` (Entscheidung 2026-10-01)
|
||||
|
||||
* **Format: YAML.** Eine Datei beschreibt einen oder mehrere Dienste mit allem, was Traefik-Labels hergeben würden, plus Komfortschaltern. Beispiel (Schema noch auszuarbeiten):
|
||||
|
||||
```yaml
|
||||
services:
|
||||
- name: kunde-a
|
||||
url: http://127.0.0.1:8080 # lokaler Dienst (oder: interface + port, s. u.)
|
||||
hosts: [kunde-a.de, www.kunde-a.de]
|
||||
https: true
|
||||
redirect_http_to_https: true
|
||||
redirect_www: to_non_www # none | to_www | to_non_www
|
||||
healthcheck:
|
||||
interval: 30s
|
||||
expect_status: 200
|
||||
expect_title: "KundeA - Willkommen in KundeA's Online-Welt" # optional
|
||||
expect_contains: { tag: h2, text: "Impressum" } # optional
|
||||
labels: # optional: rohe traefik.*-Labels für alles Weitere
|
||||
traefik.http.middlewares.x.headers.stsseconds: "31536000"
|
||||
```
|
||||
|
||||
* **Healthcheck → `service_down`:** Der Agent ruft den lokalen Dienst ab und erwartet `200 OK`, optional auch einfache Inhaltsprüfungen (`<title>`, Tag mit Text). Ein Fehlschlag meldet `service_down`, die Policy setzt `maintenance`.
|
||||
* **Backend-Adresse/NetBird:** Interface (z. B. `wt0`) und Port werden **konfiguriert**; die IP wird über das Interface automatisch ermittelt. Ports lassen sich nicht zuverlässig erraten. Als Vorschlag dienen typische Ports: tcp/8080, tcp/80, tcp/443, tcp/3000, tcp/8000, tcp/8443.
|
||||
|
||||
### 4.2 Stream Event Protocol (Agent <-> Server)
|
||||
|
||||
Beide Richtungen nutzen dasselbe JSON-Event-Format. Server→Agent läuft über den SSE-Stream; jedes Event ist ein SSE-Rahmen `id: <event_id>`, `event: <type>`, `data: <JSON in einer Zeile>`, gefolgt von einer Leerzeile (max. 1 MiB). Agent→Server läuft über signierte HTTPS-Requests. `node_id` setzt der Server immer selbst aus der geprüften Identität.
|
||||
@@ -215,7 +290,7 @@ Liefert ein verlängertes Krypto-Lease an den Agenten zurück.
|
||||
|
||||
### 4.3 SQLite Datenbankschema (Server State)
|
||||
|
||||
Der Server persistiert Mandanten, Tokens und Richtlinien lokal in SQLite (`/var/lib/sultan/sultan.db`).
|
||||
Der Server persistiert Mandanten, Tokens und Richtlinien lokal in SQLite (`<data-dir>/sultan.db`, Default-Datenverzeichnis `./suldata`).
|
||||
|
||||
* **Treiber:** `modernc.org/sqlite` (reines Go, kein cgo → statisches Single Binary).
|
||||
* **Pragmas:** `foreign_keys=ON` (sonst greift `ON DELETE CASCADE` nicht), WAL, `busy_timeout`, Schreib-Transaktionen `IMMEDIATE`.
|
||||
@@ -277,6 +352,38 @@ sultan
|
||||
|
||||
```
|
||||
|
||||
**Umsetzung:** `main.go` ist eine dünne Hülle (Signale → `pkg/cli.Run`); `pkg/cli` parst Befehle und Flags; `pkg/server` verdrahtet Store, Zertifikat, Master-Key, Hub, Verifier, Registry und Policy.
|
||||
|
||||
**Datenverzeichnis:** Default **`./suldata`** (relativ zum Arbeitsverzeichnis), für Server **und** Agent; per `--data-dir` änderbar. Es wird mit Rechten 0700 angelegt und enthält Schlüssel. Es gehört gesichert und nie ins Git (steht in `.gitignore`).
|
||||
|
||||
**`sultan server` – Flags und Defaults:**
|
||||
|
||||
| Flag | Default | Bedeutung |
|
||||
|---|---|---|
|
||||
| `--data-dir` | `./suldata` | `sultan.db`, `server.key`/`server.crt`, `master.key` |
|
||||
| `--agent-addr` | `:8443` | Agent-Port, nur TLS 1.3 |
|
||||
| `--provider-addr` | `127.0.0.1:8080` | Traefik-Provider, Klartext. Bewusst Loopback, weil die Konfiguration Backend-IPs aller Mandanten enthält. Im Container `:8080` setzen und nur auf `127.0.0.1` veröffentlichen. |
|
||||
| `--interceptor-url` | `http://127.0.0.1:8081` | Sultan-Interceptor/Lander |
|
||||
| `--refresh` | `1m` | Neubewertung aller Knoten (zeitabhängige Stufen, Mandantenänderungen) |
|
||||
|
||||
**HTTP-Routen des Servers:**
|
||||
|
||||
| Port | Route | Zweck |
|
||||
|---|---|---|
|
||||
| Agent (TLS) | `POST /api/v1/enroll` | Body `{token, node_id, hostname}`, signiert mit neuem Schlüssel → 201 `{node_id, tenant_id, master_public_key}`; Token ungültig/verbraucht/abgelaufen → 403 ohne Details; ID/Schlüssel vergeben → 409 |
|
||||
| Agent (TLS) | `GET /api/v1/stream` | SSE-Stream (signiert); Stream offen = Knoten `online` |
|
||||
| Agent (TLS) | `POST /api/v1/state` | vollständiger Schnappschuss `{workloads: [{name, host, labels}], down_services}`; nur bei offenem Stream (sonst 409) → 204 |
|
||||
| Provider | `GET /v1/traefik-config` | `?format=json\|yaml` |
|
||||
| Provider | `GET /healthz` | Lebenszeichen |
|
||||
|
||||
**Lebenszyklus eines Knotens:**
|
||||
* Enrollment legt ihn `offline` an.
|
||||
* Stream verbunden → `online`.
|
||||
* Stream getrennt → Routen sofort entfernt, `offline`.
|
||||
* `drained` bleibt bei beidem unverändert.
|
||||
|
||||
Der Agent muss deshalb nach jedem (Wieder-)Verbinden seinen Zustand erneut senden.
|
||||
|
||||
### 5.1 Agenten-Subsysteme
|
||||
|
||||
1. **Watcher Engine:**
|
||||
@@ -311,6 +418,37 @@ sultan
|
||||
3. **Policy & Interception Engine:**
|
||||
* **Sperrung bei Zahlungsverzug:** Modifiziert den Traefik-Router auf einen internen Redirect zu einer Bezahlseite (`HTTP 402 / Payment Required`).
|
||||
* **Notfall-Wartung:** Schlägt das Backend fehl oder meldet der Agent `service_down`, liefert Traefik eine statische Statusseite mit `Retry-After: 60` (`HTTP 503`).
|
||||
* **Stufen (`pkg/policy.StageFor`)**, Reihenfolge der Prüfung:
|
||||
|
||||
| Bedingung | Stufe | Wirkung |
|
||||
|---|---|---|
|
||||
| Status `quarantined` | `blocked` | keine Routen |
|
||||
| Status `expired` oder Lizenz abgelaufen | `landing` | Informations-Landingpage |
|
||||
| Tier `community` | `normal` | **kein Mahnwesen** |
|
||||
| Status `suspended_payment` oder Mahnstufe ≥ 3 | `paywall` | Kunde ist offline: freundliche Einladung des Hosters, den Sultan-Service und weitere Angebote kennenzulernen (402), **kein Durchlass** |
|
||||
| Mahnstufe 2 | `firemark` | Hinweisseite, dann Weiterleitung zum Original (längere Wartezeit) |
|
||||
| Mahnstufe 1 | `watermark` | Hinweisseite, dann Weiterleitung zum Original (kurze Wartezeit) |
|
||||
| Healthcheck meldet Dienst down | `maintenance` (je Router) | 503, `Retry-After: 60` |
|
||||
|
||||
* **Sultan-Interceptor / Web-Lander** (eigener Dienst, nur Loopback, z. B. `127.0.0.1:8081`): Bei jeder Stufe außer `normal` leitet die Policy den Router auf den Service `sultan__interceptor` um.
|
||||
* Regel, TLS und die Middlewares des Mandanten bleiben erhalten.
|
||||
* Ein `headers`-Middleware setzt `X-Sultan-Stage`, `X-Sultan-Tenant` und `X-Sultan-Tier`. Traefik überschreibt damit vom Besucher gesendete Werte.
|
||||
* Der Lander liefert nur eine eigene HTML-Seite je Stufe. Er verändert keine Originalseiten und braucht deshalb kein Upstream.
|
||||
* **Mahnhinweis mit Durchlass (watermark/firemark):**
|
||||
1. Der Lander zeigt den Hinweis und leitet per `<meta http-equiv="refresh" content="<x>;url=<ursprüngliche URL>">` zurück. Wartezeit konfigurierbar, Default **5 s (Stufe 1)** und **10 s (Stufe 2)**.
|
||||
2. Dabei setzt er das Cookie `sultan_ack=<stufe>` auf der Mandanten-Domain, gültig **60 Minuten** (durchschnittlicher Besuch). Der Hinweis darf also mehrmals am Tag erscheinen.
|
||||
3. Die Policy erzeugt dafür einen zweiten Router `sultan__<tenant>__<node>__<router>` mit der Regel ``(<Originalregel>) && HeaderRegexp(`Cookie`, `(^|;\s*)sultan_ack=<stufe>(;|$)`)``. Er führt direkt zum Original, sonst würde die Weiterleitung wieder beim Lander landen (Schleife).
|
||||
4. Priorität: Ohne gesetzte Priorität gewinnt der Durchlass über die längere Regel, sonst über Priorität+1.
|
||||
5. Eine bestätigte niedrigere Stufe gilt nicht für eine höhere.
|
||||
6. Das Cookie ist bewusst kein Sicherheitsmerkmal: Wer es selbst setzt, überspringt nur den Hinweis. `paywall`, `landing` und `maintenance` haben keinen Durchlass.
|
||||
* **Eskalation in der Praxis:** Bei Stufe 1 und 2 alarmiert Checkmk. Zwei Alarme genügen; spätestens mit Stufe 3 ist der Kunde offline.
|
||||
* **Lander-Inhalte:**
|
||||
* **Eingebaut:** je Stufe eine minimale statische HTML-Seite, ohne CSS. Kurzer, nicht technischer Text in **de_DE und en_US**, was gerade funktioniert und was nicht.
|
||||
* **Eigene Vorlagen (optional):** Liegen in einem vereinbarten, per Docker gemounteten Verzeichnis Vorlagen, werden diese gerendert und ausgeliefert.
|
||||
* Format: Jinja2-artig; in Go umgesetzt über eine kompatible Template-Engine oder `html/template`, noch festzulegen.
|
||||
* Pro Lander-Seite höchstens **1× HTML-Vorlage, 1× CSS, 1× Grafik (WebP oder SVG)**.
|
||||
* Bewusst flach: kein allgemeiner Webserver.
|
||||
* **Mahnstufen-Zeitplan (`policy.DunningSchedule`):** Der Server berechnet die Stufe aus dem Fälligkeitsdatum der offenen Zahlung, angelehnt an Bank-Zahlungsfristen: **Stufe 1 ab 8 Tagen, Stufe 2 ab 25, Stufe 3 ab 32 Tagen** Verzug. Die Werte sind konfigurierbar.
|
||||
|
||||
|
||||
|
||||
@@ -376,12 +514,13 @@ services:
|
||||
image: sultan:latest
|
||||
container_name: sultan-server
|
||||
restart: unless-stopped
|
||||
command: ["sultan", "server", "--data-dir=/var/lib/sultan"]
|
||||
# Im Container auf allen Interfaces lauschen; nach außen nur 127.0.0.1 veröffentlichen.
|
||||
command: ["sultan", "server", "--data-dir=/suldata", "--provider-addr=:8080"]
|
||||
ports:
|
||||
- "8443:8443" # Agent Inbound (TLS Stream)
|
||||
- "127.0.0.1:8080:8080" # Traefik Config Provider & Checkmk Metrics
|
||||
- "127.0.0.1:8080:8080" # Traefik Config Provider & Checkmk Metrics – Klartext, nie öffentlich
|
||||
volumes:
|
||||
- ./sultan-data:/var/lib/sultan # sultan.db, server.key (0600), server.crt – server.key sichern!
|
||||
- ./suldata:/suldata # sultan.db, server.key/.crt, master.key – server.key und master.key sichern!
|
||||
|
||||
```
|
||||
|
||||
@@ -400,7 +539,7 @@ services:
|
||||
- "--token=sltn_enroll_<secret>.<server-fingerprint>" # Ausgabe von `sultan token`
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock:ro
|
||||
- ./agent-data:/var/lib/sultan # node.key (0600) und known_servers – persistent halten!
|
||||
- ./suldata:/suldata # node.key (0600) und known_servers – persistent halten! (Agent mit --data-dir=/suldata)
|
||||
|
||||
kunde-wordpress:
|
||||
image: wordpress:latest
|
||||
@@ -434,4 +573,73 @@ services:
|
||||
| 2026-10-01 | Authentifizierung sperrt nur `quarantined`; übrige Sperren über Routing-Policy. | Agent braucht den Kanal weiterhin (Lease, Reaktivierung). |
|
||||
| 2026-10-01 | **Kein Klartext für Agent-Verkehr**: TLS 1.3 only, Signer nur für `https`, Verifier nur mit TLS. | Signaturen und Labels dürfen nie unverschlüsselt übertragen werden. |
|
||||
| 2026-10-01 | **Token-Format `sltn_enroll_<secret>.<fingerprint>`**, Pinning mit geprüftem Erstkontakt statt blindem TOFU. | Schließt die Man-in-the-Middle-Lücke beim Erstkontakt (3.4). Entscheidet zugleich den früheren Widerspruch `sltn_token_` vs. `sltn_enroll_`. |
|
||||
| 2026-10-01 | Lizenz-Signatur mit Versionspräfix `SULTAN-LICENSE-V1`; Wildcard `*.domain` deckt alle Subdomain-Ebenen, nicht die Apex-Domain; `*.<tld>` verboten. | Eindeutige, manipulationssichere Kodierung; Wildcard-Semantik entspricht der Erwartung „Mandant besitzt die Zone“, ohne versehentlich ganze TLDs freizugeben. |
|
||||
| 2026-10-01 | **Leases**: signierte Lizenz-Kopie, 7 Tage (Wartung 10), Erneuerung nach ~2/3; kommerziell an Abo-Zyklus gebunden; Community automatisch neu ausgestellt; jederzeit serverseitig unterbrechbar. | Kurze Laufzeit begrenzt Schaden bei Missbrauch; Sperren wirken über Polling ohnehin sofort (ACME-Analogie). |
|
||||
| 2026-10-01 | **Eskalatives Mahnwesen nur kommerziell**: Wasserzeichen → Firemark → Paywall (402); umgesetzt über einen Sultan-Interceptor, zu dem die Policy umleitet. | Mehrstufige Dringlichkeit analog zu Mahnstufen; Traefik selbst kann Seiteninhalte nicht verändern. |
|
||||
| 2026-10-01 | **Policy parst Regeln** und verlangt Host-Einschränkung in jedem `\|\|`-Zweig; `HostRegexp` verboten; Namensraum `<tenant>__<node>__<name>`; `@`-Referenzen nur per Freigabeliste; `chain`/`errors` verboten. | Verhindert Catch-all-Übernahmen, Zugriff auf `api@internal` und Querverweise zwischen Mandanten. |
|
||||
| 2026-10-01 | `/etc/sultan.d` als **YAML** mit Komfortschaltern + rohen Labels; Healthcheck (Status + einfache Inhaltsprüfung) im YAML; Interface/Port konfiguriert. | Für Bare-Metal-Admins lesbarer als Labels; Ports sind nicht sicher erratbar. |
|
||||
| 2026-10-01 | **Benachrichtigung:** Community per Webhook; kommerziell SMTP an Mandanten + Ticketsystem (Zammad per Webhook/API). | Homelab braucht keinen Mailserver; kommerziell nachvollziehbare Kommunikation. |
|
||||
| 2026-10-01 | **Admin-Zugriff:** Community nur lokale CLI; kommerziell zusätzlich Admin-API (Rechnungssystem, Ansible). | Automatisierte Lizenzpflege im kommerziellen Betrieb. |
|
||||
| 2026-10-01 | **Community-Lease 14 Tage**, Erneuerung ~7 Tage mit Streuung; Wartung bis 7 Tage ohne Invalidierung, solange 50x-Lander mit Pro-Hinweis. | Homelab-Betrieb robust gegen Server-Wartung; Upsell statt Ausfall. |
|
||||
| 2026-10-01 | **Mahnstufen 8/25/32 Tage** (Bank-Zahlungsfristen), vom Server berechnet. **Watermark/Firemark als Hinweisseite mit `meta refresh`** statt Einblendung ins Original; Durchlass per Cookie `sultan_ack=<stufe>` über einen zweiten Router. | Deutlich einfacher als Seiten umzuschreiben; Cookie verhindert die Weiterleitungsschleife. |
|
||||
| 2026-10-01 | **Middleware-Zielbegrenzung** vorgesehen (Backend-Tupel `host:port`, per Label, erst Beobachtungsmodus). | SSRF-Risiko durch `forwardAuth` u. ä.; Regeln erst nach Beobachtung festlegen. |
|
||||
| 2026-10-01 | **Admin-API mit API-Token**; TLS des Admin-Interfaces über ACME (öffentliches Zertifikat via Traefik), kein Pinning nötig. | Gängige Praxis für Rechnungssystem/Ansible; ACME ist auf dem Ingress ohnehin vorhanden. |
|
||||
| 2026-10-01 | Mahnhinweis-Weiterleitung **5 s / 10 s**, Cookie **60 min**; Stufe 3 ohne Durchlass = Kunde offline mit Hoster-Einladung. | Hinweis pro Besuch statt pro Request; Eskalation endet mit Abschaltung nach zwei Checkmk-Alarmen. |
|
||||
| 2026-10-01 | **Lander:** eingebaute minimale HTML-Seiten (de_DE/en_US, ohne CSS); optional eigene Vorlagen aus gemountetem Verzeichnis, max. 1 HTML-Vorlage + 1 CSS + 1 Grafik pro Seite. | Funktioniert ohne Konfiguration; Anpassung möglich ohne komplexen Webserver. |
|
||||
| 2026-10-01 | Middleware-Zielbegrenzung als **Andockstelle mit Beobachtungs-/Durchsetzungsmodus**, Default dulden. | Erst beobachten, dann Regeln festlegen. |
|
||||
| 2026-10-01 | **Registry**: `service_state` = vollständiger Schnappschuss je Knoten (Workloads mit Host + Labels, Liste ausgefallener Services); Workloads einzeln übersetzt, bei Namenskonflikt innerhalb eines Knotens gewinnt der alphabetisch erste; fail-static bei Directory-Ausfall; Verstöße nur beim ersten Auftreten melden. | Robust gegen Teilausfälle; Audit-Log ohne Wiederholungsflut. |
|
||||
| 2026-10-01 | **Lander-Vorlagen mit Go `html/template`**; die eingebauten Seiten lassen sich als Beispiel exportieren (`dump-landing`). | Eingebaut, ohne Abhängigkeit, schützt automatisch gegen eingeschleustes HTML; Syntax erlernbar. |
|
||||
| 2026-10-01 | **Datenverzeichnis `./suldata`** für Server und Agent; Ports `:8443`/`8080` als konfigurierbare Defaults, Provider-Port auf Loopback. | Einheitlich, einfach zu mounten; Klartext-Port nicht versehentlich öffentlich. |
|
||||
| 2026-10-01 | **`sultan server`**: Knoten-Lebendigkeit = offener Stream; Routen beim Trennen sofort entfernt; `POST /api/v1/state` nur bei offenem Stream. | Keine verwaisten Routen; einfaches, eindeutiges Modell. |
|
||||
| geplant | Signierte Server-Events; geordneter Zertifikatswechsel; optionale Root-CA. | Zweite Sicherheitsschicht neben dem TLS-Pin; Betrieb ohne Aussperren. |
|
||||
|
||||
---
|
||||
|
||||
## 9. Umsetzungsstand & Roadmap
|
||||
|
||||
**Umgesetzt (Go-Packages, testgetrieben):**
|
||||
|
||||
| Package | Inhalt |
|
||||
|---|---|
|
||||
| `pkg/traefik` | Labels → Traefik-Konfiguration, Merge, JSON/YAML |
|
||||
| `pkg/stream` | SSE-Hub (Server) und Client (Agent) inkl. Keepalive, Backoff, Disconnect |
|
||||
| `pkg/store` | SQLite: Mandanten, Knoten, Enrollment |
|
||||
| `pkg/auth` | Ed25519-Request-Signatur, Verifier, Schlüsseldateien |
|
||||
| `pkg/crypto` | Server-Zertifikat, TLS 1.3, Pinning, Enrollment-Token |
|
||||
| `pkg/license` | Lizenz ausstellen, prüfen, Domain/Routen/Feature-Checks |
|
||||
| `pkg/policy` | Regel-Parser, Domain-/Routen-/Feature-/Referenz-Prüfung, Namensraum, Stufen & Interceptor-Umleitung, Mahnstufen-Zeitplan, Andockstelle Middleware-Ziele |
|
||||
| `pkg/registry` | Aggregator: Zustand je Knoten → Labels → Policy → Merge; `/v1/traefik-config`-Handler; Audit-Callback |
|
||||
| `pkg/server` | `sultan server`: Datenverzeichnis, Master-Key, HTTP-Routen beider Ports, Knotenstatus, Refresh-Schleife, Graceful Shutdown, Audit-Log über `slog` |
|
||||
| `pkg/cli`, `main.go` | Befehle `server`, `version`, `help` (`agent`/`token` als Platzhalter) |
|
||||
|
||||
**Offen – Bausteine:**
|
||||
|
||||
1. **Sultan-Interceptor / Web-Lander** (5.2): eigener Dienst auf Loopback; je `X-Sultan-Stage` eine HTML-Seite (eingebaut de_DE/en_US oder eigene `html/template`-Vorlage aus dem Datenverzeichnis; Befehl **`dump-landing`** exportiert die eingebauten Seiten als Vorlage zum Anpassen): Mahnhinweis mit `meta refresh` (5/10 s) + Cookie `sultan_ack` (60 min), Paywall/Hoster-Einladung (402), Landing, Wartung (503, `Retry-After: 60`; Community mit Pro-Hinweis). Audit-Log für Policy-Violations (Callback der Registry).
|
||||
2. **Server-Ergänzungen:** `GET /metrics/checkmk`; Rate-Limit für `/enroll`; Gnadenfrist vor dem Entfernen von Routen beim Trennen (derzeit sofort); Konfiguration auch per Umgebungsvariablen.
|
||||
3. *(entfällt – umgesetzt in `pkg/server`)*
|
||||
4. **Leases:**
|
||||
* Lease bei Enrollment ausstellen und per `lease_renew` erneuern (Master-Key und Übergabe des Public Keys beim Enrollment sind umgesetzt).
|
||||
* Persistenz → Migration v2: `features` und offene Zahlung (`payment_overdue_since`, daraus die Mahnstufe per `DunningSchedule`) fehlen im Schema; Leases (aktuelle Lease je Knoten).
|
||||
* Lease-Laufzeiten (kommerziell 7/10 Tage, Community 14 Tage mit Erneuerung ~7 Tage + Streuung), Bindung an Abo-Zyklus (4.1a).
|
||||
5. **Agent:**
|
||||
* Docker-Watcher (Events API über `/var/run/docker.sock`)
|
||||
* File-Provider `/etc/sultan.d/*.yaml` per inotify (Schema 4.1b ausarbeiten, Übersetzung in Labels)
|
||||
* Healthchecks (Status, `<title>`, Tag mit Text) → `service_down`
|
||||
* State-Serializer: Ziel-IP LAN oder NetBird `100.64.x.y`
|
||||
* Senden von `service_state`; Behandlung von `node_command`
|
||||
6. **Lizenzfristen & Benachrichtigung:** periodischer Job (Status → `expired` an Tag 0, Mahnstufen), Checkmk-Ausgabe (6.1); Community: Webhook; kommerziell: SMTP an Mandanten + Zammad (Webhook/API).
|
||||
7. **Admin-API (nur kommerziell):** Mandanten, Lizenzen, Zahlungsstatus für Rechnungssystem und Ansible; Authentifizierung per API-Token, TLS über ACME.
|
||||
8. **CLI:**
|
||||
* `sultan agent` (Default `--data-dir=./suldata`)
|
||||
* `sultan token`: Mandant anlegen, Token erzeugen, Lizenz ausstellen, Knoten auflisten/drainen/löschen. Arbeitet direkt auf der SQLite-DB des Servers (WAL erlaubt parallelen Zugriff).
|
||||
* `sultan dump-landing`: eingebaute Lander-Seiten als Vorlage exportieren.
|
||||
9. **Betrieb:**
|
||||
* Strukturiertes Logging (`log/slog`), Graceful Shutdown, Versionsinfo
|
||||
* Dockerfile (statisch, distroless), systemd-Unit
|
||||
* CI (`go vet`, `go test -race`, Linter), Releases
|
||||
10. **Sicherheit (geplant, 3.4):** signierte Server-Events, geordneter Zertifikatswechsel, optionale Root-CA.
|
||||
|
||||
**Offene Fachfragen (vor Umsetzung klären):**
|
||||
|
||||
* Lander-Vorlagen: genauer Unterordner im Datenverzeichnis (z. B. `./suldata/landing/`) und Namensschema der Dateien.
|
||||
* Middleware-Zielbegrenzung: genaue Grenze (Pfadsegment? Zonen-Redirects?), Label-Name, Wechsel vom Beobachtungs- in den Sperrmodus.
|
||||
Reference in new issue
Block a user