From 9861c25949e08da9c6d3942470b19917bd6a8e22 Mon Sep 17 00:00:00 2001 From: Bjoern Runden Date: Tue, 29 Sep 2026 22:45:38 +0200 Subject: [PATCH] init des architektur beschreibenden MD... noch kein code, nur REadme, ai.agent md s --- .ai/sultan-system-architecture.md | 368 ++++++++++++++++++++++++++++++ CLAUDE.md | 45 ++++ README.md | 78 ++++++- 3 files changed, 488 insertions(+), 3 deletions(-) create mode 100644 .ai/sultan-system-architecture.md create mode 100644 CLAUDE.md diff --git a/.ai/sultan-system-architecture.md b/.ai/sultan-system-architecture.md new file mode 100644 index 0000000..6c3cf9f --- /dev/null +++ b/.ai/sultan-system-architecture.md @@ -0,0 +1,368 @@ +# CLAUDE.md + + +# Sultan: High-Performance Distributed Ingress & ZeroTrust Service Discovery + +Dieses Dokument beschreibt das vollständige Architektur-, Daten-, Netzwerk- und Anwendungsmodell des Projekts **Sultan**. Es dient als Kontext und Referenz für Software-Architekten und AI-gestützte Coding-Agenten (z. B. via Continue in VSCodium). + +--- + +## 1. Executive Summary & Problemstellung + +Klassische Ingress-Lösungen wie Nginx Proxy Manager (NPM) erfordern eine manuelle, GUI-basierte Konfiguration des Routings. In verteilten Umgebungen (Mehrere Proxmox-Cluster, Docker-Hosts, Bare-Metal-LAMP/Apache-Server, Kunden-Setups hinter DS-Lite/CGNAT) führt dies zu Dokumentationsbrüchen, toten Routen und hohem Wartungsaufwand. + +**Sultan** löst dies durch eine entkoppelte, zweigeteilte Architektur: +1. **In-Service-Deklaration:** Backends deklarieren ihr Routing direkt am Dienst (native Traefik-Labels in Docker oder Drop-in-Konfigurationen unter `/etc/sultan.d/`). +2. **Dynamische Registrierung:** Ein leichtgewichtiger Agent überträgt diese Konfiguration kryptografisch gesichert über einen langlebigen, ressourcenschonenden Keepalive-Stream an den zentralen Ingress. +3. **ZeroTrust & Public Reachability:** Optionale Anbindung über ein WireGuard-Mesh (z. B. NetBird), um auch Hosts ohne statische IPv4/IPv6 oder hinter NAT/Firewalls öffentlich mit TLS bereitzustellen. +4. **Integrierte Autorisierung & Monetarisierung:** Mandantensichere Capability-Lizenzen via Ed25519-Kryptografie, Lease-System und automatisierte Überwachung von Lizenzfristen. + +--- + +## 2. Netzwerkmodell (Network Model) + +### 2.1 Topologie + +```text +[ Öffentliches Internet ] + │ + │ HTTP (80) / HTTPS (443) / TCP-Streams + ▼ +┌────────────────────────────────────────────────────────┐ +│ Ingress-Knoten (Zentrales Rechenzentrum / Public IP) │ +│ │ +│ ┌────────────────────────────────────────────────┐ │ +│ │ Traefik v3 (Ingress / ACME DNS-01 Validierung) │ │ +│ └───────────────────────▲────────────────────────┘ │ +│ │ HTTP Poll (:8080) │ +│ │ (/v1/traefik-config) │ +│ ┌───────────────────────┴────────────────────────┐ │ +│ │ Sultan-Server (Registry / Policy Engine) │ │ +│ └───────────────────────▲────────────────────────┘ │ +│ │ │ +│ │ Control Plane: │ +│ │ TLS-Stream (:8443) │ +│ │ (45s Heartbeat, │ +│ │ Instant Event Push) │ +│ │ │ +│ ┌───────────────────────┴────────────────────────┐ │ +│ │ NetBird / WireGuard Gateway (100.64.0.1) │ │ +│ └───────────────────────▲────────────────────────┘ │ +└───────────────────────────┼────────────────────────────┘ + │ + ZeroTrust WireGuard Tunnel (Traffic) + │ + ┌──────────────────┴──────────────────┐ + │ │ +┌────────┴──────────────┐ ┌──────────┴─────────────┐ +│ Client-Host A │ │ Client-Host B │ +│ (Bare-Metal / LAMP) │ │ (Docker-Compose Stack) │ +│ │ │ │ +│ ┌───────────────────┐ │ │ ┌────────────────────┐ │ +│ │ sultan-agent │ │ │ │ sultan-agent │ │ +│ │ (File-Watcher) │ │ │ │ (Docker-Socket) │ │ +│ └───────────────────┘ │ │ └────────────────────┘ │ +│ ┌───────────────────┐ │ │ ┌────────────────────┐ │ +│ │ Apache / VHost │ │ │ │ Web-Container │ │ +│ └───────────────────┘ │ │ └────────────────────┘ │ +└───────────────────────┘ └────────────────────────┘ + +``` + +### 2.2 Datenfluss & Verbindungstypen + +1. **Control Plane (Agent -> Server):** +* **Transport:** Ausgehende Verbindung vom Agenten zum Server über TCP `:8443` (TLS). +* **Firewall-Freundlichkeit:** Reine Outbound-Verbindung; funktioniert hinter DS-Lite, Mobilfunk, Carrier-Grade-NAT und Unternehmens-Firewalls ohne Portweiterleitung. +* **Keepalive-Mechanik:** Persistenter HTTP/2- oder SSE-Stream. Alle 45 Sekunden wird ein minimales Keepalive-Byte (`:\n\n`) ausgetauscht. Verhindert das Schließen von State-Tables in NAT-Routern (Conntrack Timeout). +* **Event-Driven Push:** Änderungen an Containern (Start/Stop) oder Notfall-Sperren werden verzögerungsfrei (Latenz < 5 ms) über den bestehenden Stream übertragen. + + +2. **Data Plane (Traffic: Besucher -> Backend):** +* **Direkt:** Liegen Ingress und Backend im selben VLAN/LAN, routet Traefik direkt auf die interne LAN-IP. +* **ZeroTrust Overlay (NetBird):** Sitzt das Backend im Fremdnetz/Homelab, routet Traefik auf die WireGuard-Overlay-IP (`100.64.x.y`). End-to-End verschlüsselt. + + +3. **Ingress zu Traefik:** +* Traefik pollt den lokalen Sultan-Server via HTTP-Provider: `GET http://127.0.0.1:8080/v1/traefik-config`. +* Intervall: 2–5 Sekunden. Traefik verarbeitet die In-Memory-Konfiguration ohne Neustart und ohne Verbindungsabbrüche. + + + +--- + +## 3. Sicherheits- und Vertrauensmodell + +### 3.1 Transport-Verschlüsselung (TLS ohne CA-Zirkus) + +* **Server-Zertifikat:** Generiert sich beim ersten Start selbst (Self-Signed / Snakeoil ECDSA P-256). +* **Vertrauensmodell:** Trust-on-First-Use (TOFU) analog zu SSH. Der Agent verifiziert beim ersten Connect den SHA256-Fingerprint des Servers und speichert ihn in `known_servers`. +* **Optional:** Unterstützung für explizit gemountete Root-CA-Zertifikate. + +### 3.2 Authentifizierung & Enrollment (CSR-Handshake) + +* Private Keys werden **ausschließlich lokal auf dem Ziel-Host** generiert (Ed25519) und verlassen diesen niemals. +* **Enrollment:** +1. Admin erzeugt kurzlebigen Einladungs-Token auf dem Server (`sltn_token_...`). +2. Agent generiert lokalen Key (`node.key`) und sendet seinen Public Key (`node.pub`) zusammen mit dem Einladungs-Token an den Server (`POST /api/v1/enroll`). +3. Server speichert den Public Key des Agenten, weist ihm eine Mandanten-ID zu und signiert das initiale Berechtigungszertifikat (Capability Lease). +4. Der Einladungs-Token wird entwertet; alle künftigen Heartbeats werden kryptografisch vom Agenten mit `node.key` signiert. + + + +### 3.3 Autorisierung & Domain-Ownership-Schutz + +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. + +--- + +## 4. Datenmodell (Data Specifications) + +### 4.1 Lizenz & Capabilities (`license.json`) + +```json +{ + "$schema": "[http://json-schema.org/draft-07/schema#](http://json-schema.org/draft-07/schema#)", + "title": "SultanCapabilities", + "type": "object", + "properties": { + "tenant_id": { "type": "string" }, + "tier": { "type": "string", "enum": ["community", "pro", "enterprise"] }, + "max_routes": { "type": "integer", "description": "-1 für unbegrenzt" }, + "allowed_domains": { + "type": "array", + "items": { "type": "string" } + }, + "features": { + "type": "array", + "items": { "type": "string", "enum": ["websockets", "tcp_stream", "custom_middleware"] } + }, + "valid_until": { "type": "string", "format": "date-time" }, + "signature": { "type": "string", "description": "Ed25519 Signatur des Server-Master-Keys über den Payload" } + }, + "required": ["tenant_id", "tier", "max_routes", "allowed_domains", "valid_until", "signature"] +} + +``` + +### 4.2 Stream Event Protocol (Agent <-> Server) + +Das bidirektionale Streaming-Format nutzt standardisierte JSON-Events: + +```json +{ + "event_id": "evt_01J8Z9X2Y3...", + "timestamp": 1727618531, + "type": "service_state | lease_renew | node_command | error", + "node_id": "node-proxmox-vm-42", + "payload": {} +} + +``` + +* **Typ `service_state` (Agent -> Server):** +Enthält die vollständige Liste der aktuell aktiven Traefik-Labels des Knotens. +* **Typ `node_command` (Server -> Agent):** +Enthält Aktionen wie `re_enroll`, `drain`, `maintenance`. +* **Typ `lease_renew` (Server -> Agent):** +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`): + +```sql +-- Mandanten & Abonnements +CREATE TABLE tenants ( + id TEXT PRIMARY KEY, + name TEXT NOT NULL, + email TEXT NOT NULL, + tier TEXT NOT NULL DEFAULT 'community', + max_routes INTEGER NOT NULL DEFAULT 5, + allowed_domains TEXT NOT NULL, -- JSON-Array: ["*.firma.de", "shop.de"] + valid_until DATETIME NOT NULL, -- Ablaufdatum des Vertrags / Abos + status TEXT NOT NULL DEFAULT 'active', -- active, suspended_payment, quarantined + created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP +); + +-- Registrierte Agenten / Knoten +CREATE TABLE nodes ( + id TEXT PRIMARY KEY, + tenant_id TEXT NOT NULL REFERENCES tenants(id) ON DELETE CASCADE, + hostname TEXT NOT NULL, + public_key TEXT NOT NULL UNIQUE, -- Ed25519 Public Key des Agenten + last_seen DATETIME NOT NULL, + ip_address TEXT NOT NULL, + status TEXT NOT NULL DEFAULT 'online', -- online, offline, drained + FOREIGN KEY(tenant_id) REFERENCES tenants(id) +); + +-- Einmalige Enrollment-Tokens +CREATE TABLE enrollment_tokens ( + token_hash TEXT PRIMARY KEY, + tenant_id TEXT NOT NULL REFERENCES tenants(id) ON DELETE CASCADE, + expires_at DATETIME NOT NULL, + used INTEGER NOT NULL DEFAULT 0 +); + +``` + +--- + +## 5. Anwendungsmodell (Application Model) + +Das gesamte System ist als **Single-Binary-Architektur** in Go implementiert: + +```text +sultan +├── server # Führt Registry, Stream-Hub, Policy-Engine & Traefik-Provider aus +├── agent # Führt Docker-/File-Watcher aus und verbindet sich zum Server +└── token # CLI-Tools zur Schlüsselverwaltung, Enrollment- & Lizenzausstellung + +``` + +### 5.1 Agenten-Subsysteme + +1. **Watcher Engine:** +* **Docker Engine:** Lauscht am `/var/run/docker.sock` via Docker Events API. Filtert nach Labels mit Präfix `traefik.`. +* **File Provider:** Überwacht Verzeichnis `/etc/sultan.d/*.conf` via Inotify auf Konfigurationsdateien im INI/YAML-Format. + + +2. **State Serializer:** +* Extrahiert Services, Host-Ports und Routen. +* Injiziert automatisch die Ziel-IP (LAN-IP oder NetBird-IP) in die Server-URL-Definitionen für Traefik. + + +3. **Transport Engine:** +* Verwaltet den permanenten TLS-Stream mit 45s-Keepalive. +* Führt automatischen Reconnect mit exponentiellem Backoff durch. + + + +### 5.2 Server-Subsysteme + +1. **Stream Hub:** +* Hält offene Sockets aller Agents im RAM. +* Verarbeitet State-Pushes und führt Dead-Node-Detection durch (Timeout: 90s). + + +2. **Dynamic Config Aggregator:** +* Führt die flachen Traefik-Labels aller aktiven Knoten zusammen. +* Wandelt die Labels in das verschachtelte Traefik-JSON-Format um. +* Stellt `GET /v1/traefik-config` bereit. + + +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`). + + + +--- + +## 6. Überwachung, Lizenzfristen & Alerting (Checkmk-Integration) + +Um zu verhindern, dass Kunden-Instanzen bei Ablauf von Quartals- oder Jahresabonnements unerwartet in den Wartungsmodus fallen, implementiert der Sultan-Server Schnittstellen für **Checkmk** und externe Alerting-Pipelines. + +### 6.1 Checkmk Local Check Protocol + +Der Server stellt einen internen Management-Endpunkt bereit: `GET /metrics/checkmk` (oder auslesbar über ein lokales Checkmk-Agent-Plugin-Script `/usr/lib/check_mk_agent/local/sultan_licenses`). + +Das Ausgabeformat entspricht der nativen Checkmk Local-Check-Syntax: + +```text +# Syntax: +# 0 = OK, 1 = WARN, 2 = CRIT, 3 = UNKNOWN + +0 "Sultan_Tenant_kunde_schmidt" days_left=74;30;14 OK - Abo aktiv bis 2026-12-15 (74 Tage verbleibend) +1 "Sultan_Tenant_meier_gmbh" days_left=12;30;14 WARN - Abo laeuft in 12 Tagen ab! Verlaengerung erforderlich. +2 "Sultan_Tenant_alt_ag" days_left=0;30;14 CRITICAL - Abo seit 1 Tagen abgelaufen! Status: suspended_payment + +``` + +### 6.2 Schwellenwerte & Alarmierungskaskade + +* **30 Tage vor Ablauf:** Warnung im Checkmk-Dashboard (Monitoring-Status: `WARN`). +* **14 Tage vor Ablauf:** Kritischer Alert (`CRIT`). Automatischer E-Mail-Dispatch / Ticket-Erstellung an Kunden und Admin: *„Abo-Verlängerung erforderlich – bitte Rechnung begleichen“*. +* **Tag 0 (Ablaufzeitpunkt):** +* Status wechselt in SQLite auf `expired`. +* Traefik leitet Domain auf Informations-Landingpage um. +* Checkmk triggert Alarm: `CRITICAL - Service offline geschaltet`. + + + +--- + +## 7. Referenzkonfigurationen + +### 7.1 Compose-Setup: Ingress-Knoten + +```yaml +services: + traefik: + image: traefik:v3.1 + container_name: traefik-ingress + restart: unless-stopped + network_mode: host + command: + - "--providers.http.endpoint=[http://127.0.0.1:8080/v1/traefik-config](http://127.0.0.1:8080/v1/traefik-config)" + - "--providers.http.pollInterval=3s" + - "--entrypoints.web.address=:80" + - "--entrypoints.web.http.redirections.entryPoint.to=websecure" + - "--entrypoints.web.http.redirections.entryPoint.scheme=https" + - "--entrypoints.websecure.address=:443" + - "--certificatesresolvers.dns-resolver.acme.dnschallenge=true" + - "--certificatesresolvers.dns-resolver.acme.dnschallenge.provider=rfc2136" + volumes: + - ./acme.json:/etc/traefik/acme.json + + sultan-server: + image: sultan:latest + container_name: sultan-server + restart: unless-stopped + command: ["sultan", "server", "--data-dir=/var/lib/sultan"] + ports: + - "8443:8443" # Agent Inbound (TLS Stream) + - "127.0.0.1:8080:8080" # Traefik Config Provider & Checkmk Metrics + volumes: + - ./sultan-data:/var/lib/sultan + +``` + +### 7.2 Compose-Setup: Kunden-Host (Backend mit Docker) + +```yaml +services: + sultan-agent: + image: sultan:latest + container_name: sultan-agent + restart: unless-stopped + command: + - "sultan" + - "agent" + - "--server=[https://ingress.dein-netz.de:8443](https://ingress.dein-netz.de:8443)" + - "--token=sltn_enroll_01J8Z9..." + volumes: + - /var/run/docker.sock:/var/run/docker.sock:ro + - ./agent-data:/var/lib/sultan + + kunde-wordpress: + image: wordpress:latest + restart: unless-stopped + labels: + - "traefik.enable=true" + - "traefik.http.routers.wp.rule=Host(`kunde.de`) || Host(`www.kunde.de`)" + - "traefik.http.routers.wp.entrypoints=websecure" + - "traefik.http.routers.wp.tls=true" + - "traefik.http.routers.wp.tls.certresolver=dns-resolver" + - "traefik.http.middlewares.wp-www.redirectregex.regex=^https?://kunde\\.de/(.*)" + - "traefik.http.middlewares.wp-www.redirectregex.replacement=[https://www.kunde.de/$$](https://www.kunde.de/$$){1}" + - "traefik.http.middlewares.wp-www.redirectregex.permanent=true" + - "traefik.http.routers.wp.middlewares=wp-www" + - "traefik.http.services.wp.loadbalancer.server.port=80" + +``` diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..a0b5624 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,45 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Instruktionen + +Lies die `.ai/*.md` Files. Diese liefern den Context für die App, die hier entstehen soll. +Maßgeblich ist aktuell [.ai/sultan-system-architecture.md](.ai/sultan-system-architecture.md) (Netzwerk-, Sicherheits-, Daten- und Anwendungsmodell inkl. SQL-Schema, Event-Format und Referenz-Compose-Files). Bei Widersprüchen zwischen diesem File und der Spec gilt die Spec. + +## Projektstatus + +Greenfield: Es existiert noch kein Go-Code, kein `go.mod`, keine Build-/Test-Toolchain. Build-, Lint- und Test-Befehle hier ergänzen, sobald sie existieren. + +## Big Picture + +Sultan ersetzt manuell gepflegtes Ingress-Routing (z. B. Nginx Proxy Manager): Backends deklarieren ihr Routing selbst (Traefik-Labels an Docker-Containern oder Drop-ins in `/etc/sultan.d/*.conf` für Bare-Metal/LAMP), ein Agent meldet das an einen zentralen Server, und Traefik auf dem Ingress-Knoten pollt daraus seine Konfiguration. + +**Single Binary in Go** mit drei Subcommands: +- `sultan server` – Registry, Stream-Hub (hält Agent-Verbindungen im RAM, Dead-Node-Timeout 90s), Policy-Engine, Traefik-HTTP-Provider +- `sultan agent` – Watcher (Docker Events API auf `traefik.*`-Labels bzw. inotify auf `/etc/sultan.d`), State-Serializer (injiziert LAN- oder NetBird-IP `100.64.x.y` als Backend-Ziel), Transport mit Reconnect + exponentiellem Backoff +- `sultan token` – CLI für Schlüssel, Enrollment-Tokens, Lizenzausstellung + +**Ports / Endpunkte des Servers:** +- `:8443` (TLS) – Control Plane, ausschließlich *ausgehend* vom Agent (NAT/CGNAT/DS-Lite-tauglich). Persistenter Stream (HTTP/2 oder SSE), 45s-Keepalive, Events werden sofort gepusht. Enrollment: `POST /api/v1/enroll`. +- `127.0.0.1:8080` – `GET /v1/traefik-config` (Traefik pollt alle 2–5s; flache Labels aller aktiven Nodes → verschachteltes Traefik-JSON) und `GET /metrics/checkmk` (Checkmk Local-Check-Format). + +**Sicherheitsmodell (Kerninvarianten):** +- Server-TLS-Zertifikat self-signed (ECDSA P-256), Agent pinnt den SHA256-Fingerprint per TOFU in `known_servers`. +- Agent-Keys (Ed25519) werden nur lokal erzeugt und verlassen den Host nie; Enrollment tauscht Public Key + einmaligen Token gegen Mandanten-Zuordnung und signierten Capability-Lease. Danach ist jeder Heartbeat mit `node.key` signiert. +- Jeder Agent-Payload wird serverseitig gegen die Mandanten-Policy geprüft: Hosts in `Host(...)`-Regeln müssen in `allowed_domains` stehen (sonst ablehnen + Audit-Log), Routen über `max_routes` werden verworfen. +- Lizenzen (`license.json`) sind mit dem Ed25519-Server-Master-Key signiert. + +**Stream-Events** (JSON: `event_id`, `timestamp`, `type`, `node_id`, `payload`): `service_state` (Agent→Server, vollständiger Label-Snapshot, nicht Delta), `node_command` (`re_enroll`/`drain`/`maintenance`), `lease_renew`, `error`. + +**Persistenz:** SQLite unter `/sultan.db` (Default `/var/lib/sultan`), Tabellen `tenants`, `nodes`, `enrollment_tokens` (Tokens nur als Hash gespeichert). + +**Policy/Lizenzablauf:** Mandanten-Status steuert das generierte Routing – Zahlungsverzug → Router auf Bezahlseite (402), Backend down/`service_down` → Statusseite 503 mit `Retry-After: 60`. Checkmk-Schwellen: WARN bei 30, CRIT bei 14 Tagen Restlaufzeit, bei Ablauf Umleitung auf Landingpage. + +## Bekannte Unstimmigkeiten in der Spec + +Vor der Implementierung mit dem User klären statt raten: +- Token-Präfix: `sltn_token_...` (Abschnitt 3.2) vs. `sltn_enroll_...` (Compose-Beispiel 7.2). +- Tenant-Status: Schema kennt `active`/`suspended_payment`/`quarantined`, Abschnitt 6.2 setzt `expired`. +- Stream-Transport ist offen: HTTP/2 *oder* SSE (SSE wäre nur Server→Agent; bidirektionale Events brauchen dann einen Rückkanal). +- Einige URLs in JSON/YAML-Blöcken der Spec sind durch Copy-Paste als Markdown-Links verstümmelt (`"[http://...](http://...)"`) – die nackte URL ist gemeint. diff --git a/README.md b/README.md index 11e02e1..2d389f8 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,76 @@ -# sultan +# 👑 sultan -sultan service + agent in GO -Target: a Traefik label registry docker host over span \ No newline at end of file +**Verteiltes Ingress-Routing mit ZeroTrust-Service-Discovery – geschrieben in Go.** + +Schluss mit Klick-Konfiguration im Proxy-GUI und toten Routen 🪦: Bei Sultan sagen deine Dienste selbst, wie sie erreichbar sein wollen. Ob Docker-Container mit Traefik-Labels 🐳 oder klassischer Bare-Metal-/LAMP-Server mit Drop-in-Config 🖥️ – ein kleiner Agent meldet das Routing sicher an den zentralen Sultan-Server, und Traefik holt sich daraus live seine Konfiguration. Ohne Neustart, ohne Portweiterleitung. + +## ✨ Was Sultan kann + +- 🏷️ **Routing am Dienst deklarieren** – native `traefik.*`-Labels in Docker oder Dateien unter `/etc/sultan.d/` +- 📡 **Firewall-freundlich** – der Agent baut nur *ausgehende* Verbindungen auf; funktioniert hinter NAT, CGNAT und DS-Lite +- ⚡ **Echtzeit-Updates** – Container starten oder stoppen, Traefik weiß es sofort +- 🔐 **Sicher von Haus aus** – TLS mit Fingerprint-Pinning (wie bei SSH), Ed25519-Schlüssel, die den Host nie verlassen +- 🛡️ **Mandantenfähig** – jeder Mandant darf nur seine eigenen Domains beanspruchen +- 🌐 **ZeroTrust-Overlay** – optional über ein WireGuard-Mesh (z. B. NetBird), auch für Hosts ohne öffentliche IP +- 📊 **Monitoring** – Checkmk-Integration für Knoten- und Lizenzstatus + +## 🧭 So funktioniert's + +```text +Internet ──▶ Traefik ──(pollt)──▶ sultan server ◀──(TLS-Stream)── sultan agent (Docker / Bare-Metal) +``` + +Ein einziges Binary, drei Rollen: + +| Befehl | Aufgabe | +|-----------------|----------------------------------------------------------------| +| `sultan server` | Registry, Policy-Engine und Konfigurationsquelle für Traefik | +| `sultan agent` | Beobachtet Docker bzw. `/etc/sultan.d/` und meldet an den Server | +| `sultan token` | Schlüssel, Einladungs-Tokens und Lizenzen verwalten | + +Die ausführliche Architektur steht in [.ai/sultan-system-architecture.md](.ai/sultan-system-architecture.md). + +## 🚀 Installation + +> 🚧 **Hinweis:** Sultan befindet sich noch am Anfang der Entwicklung. Die folgenden Schritte zeigen, wie die Installation aussehen *wird* – Befehle und Flags können sich noch ändern. + +### Voraussetzungen + +- Go (aktuelle stabile Version) +- Für den Ingress-Knoten: Traefik v3 +- Für Docker-Hosts: Zugriff auf `/var/run/docker.sock` + +### Binary bauen + +```bash +git clone ssh://git@git.everdc.eu:10022/chmln-app-dev/sultan.git +cd sultan +go build -o sultan . +sudo install -m 0755 sultan /usr/local/bin/sultan +``` + +### Server starten (Ingress-Knoten) + +```bash +sultan server --data-dir=/var/lib/sultan +``` + +Der Server lauscht auf `:8443` für Agents und auf `127.0.0.1:8080` für Traefik. Traefik bindest du über den HTTP-Provider an: + +```bash +--providers.http.endpoint=http://127.0.0.1:8080/v1/traefik-config +--providers.http.pollInterval=3s +``` + +### Agent verbinden (Backend-Host) + +1. Auf dem Server mit `sultan token` einen Einladungs-Token erzeugen 🎟️ +2. Auf dem Backend-Host den Agent starten: + +```bash +sultan agent --server=https://ingress.example.com:8443 --token= +``` + +Beim ersten Verbinden merkt sich der Agent den Fingerprint des Servers – ab dann läuft alles automatisch. 🎉 + +Fertige Docker-Compose-Beispiele für Ingress und Backend-Host findest du in der [Architektur-Doku](.ai/sultan-system-architecture.md#7-referenzkonfigurationen).