Files
sultan/README.md
T
2026-10-02 22:42:56 +02:00

186 lines
9.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 👑 sultan
**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
- Zum Bauen: Go (Version laut `go.mod`) bzw. Docker mit buildx
- Für den Ingress-Knoten: Traefik v3
- Für Docker-Hosts: Zugriff auf `/var/run/docker.sock`
### 1. Quellen holen
```bash
git clone ssh://git@git.everdc.eu:10022/chmln-app-dev/sultan.git
cd sultan
```
### 2. Binary bauen (amd64 + arm64)
```bash
go build -o sultan . && ./sultan version # schnell, nur für die eigene Architektur
deploy/build.sh # Release-Binaries für linux/amd64 + linux/arm64
# → dist/<version>/sultan_linux_amd64, sultan_linux_arm64, SHA256SUMS
sudo install -m 0755 dist/<version>/sultan_linux_amd64 /usr/local/bin/sultan
```
Die Version kommt aus `git describe` (Tag, sonst Commit-Hash; `-dirty` bei uncommitteten Änderungen) und steht in `sultan version`.
### 3. Docker-Image bauen (ein Image für Server *und* Agent, amd64 + arm64)
```bash
docker build -t sultan:latest . # lokal, eigene Architektur
docker run --rm sultan:latest sultan version
```
Das Image ist distroless (nur das statische Binary, ~17 MB) und läuft als nonroot-User `65532`. Die Rolle wählt der Befehl: `sultan server …` bzw. `sultan agent …`.
### 4. Image in die Registry schieben (Zot, `pod.chmln.de`)
```bash
cp deploy/.env.example deploy/.env # einmalig; gitignored – nie einchecken!
$EDITOR deploy/.env # REGISTRY_PASSWORD eintragen (Pflicht)
deploy/push-image.sh # → pod.chmln.de/sultan:<version>
LATEST=1 deploy/push-image.sh # zusätzlich :latest (nur für Releases)
```
Das Skript liest `deploy/.env` (Defaults: `REGISTRY=pod.chmln.de`, `REGISTRY_USER=sultan`, `IMAGE_NAME=sultan`; ohne `REGISTRY_PASSWORD` Abbruch), meldet sich per `docker login --password-stdin` an und baut per `docker buildx` gleichzeitig für `linux/amd64` und `linux/arm64` (Go kompiliert quer, kein QEMU nötig). Versionen mit `-dirty` werden verweigert. Weitere Stellschrauben: `PLATFORMS`, `ENV_FILE`.
Danach auf den Hosts: `SULTAN_IMAGE=pod.chmln.de/sultan:<version>` in der Compose-`.env` und `docker compose pull && docker compose up -d`.
### 5. Binaries auf den statischen Webhost legen
```bash
TARGET=deploy@webdocker11:/srv/inst/htdocs deploy/publish-static.sh
STABLE=1 TARGET=… deploy/publish-static.sh # zusätzlich sultan/releases/stable setzen
```
Ablage per `rsync` unter `sultan/releases/<version>/` (Binaries + `SHA256SUMS`); ältere Versionen bleiben herunterladbar. Grundlage für das spätere `curl … | bash`.
### 6. Mit Docker Compose betreiben
Vorlagen: [deploy/compose/server](deploy/compose/server) (Ingress-Knoten neben Traefik) und [deploy/compose/agent](deploy/compose/agent) (Docker-Host).
```bash
cp -r deploy/compose/server ~/sultan-server && cd ~/sultan-server
cp .env.example .env && $EDITOR .env # SULTAN_IMAGE, SULTAN_PUBLIC_URL
sudo install -d -m 0700 -o 65532 -g 65532 suldata # Datenverzeichnis für den nonroot-User
docker compose up -d
docker compose exec sultan-server sultan token tenant add --id=… --domains=…
docker compose exec sultan-server sultan token invite --tenant=…
```
Der Agent läuft mit `network_mode: host`. Nur so meldet er die Host-IP als Backend-Adresse (statt der Container-IP) und den Hostnamen als Knoten-ID. Für den Docker-Socket braucht er `DOCKER_GID` in der `.env` (`getent group docker | cut -d: -f3`).
### Server starten (Ingress-Knoten)
```bash
sultan server --public-url=https://ingress.example.com:8443 # Daten landen in ./suldata
sultan server --help # alle Optionen
```
Alle Optionen gehen auch als Umgebungsvariable, z. B. `SULTAN_PUBLIC_URL=… sultan server` (praktisch für Docker und systemd). Der Server lauscht auf `:8443` für Agents und auf `127.0.0.1:8080` für Traefik (beides per `--agent-addr` / `--provider-addr` änderbar). Im Verzeichnis `./suldata` liegen Datenbank und Schlüssel – bitte sichern 🔑 und nie einchecken. 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 einen Mandanten anlegen und eine Einladung erzeugen 🎟️
```bash
sultan token tenant add --id=homelab --name="Mein Homelab" --email=ich@example.com --domains="example.com,*.example.com"
sultan token invite --tenant=homelab # gibt die Einladung aus, 24 h gültig
sultan token --help # alle Verwaltungsbefehle
```
2. Auf dem Backend-Host den Agent starten:
```bash
SULTAN_TOKEN=<einladung> sultan agent # Adresse steckt in der Einladung (sultan server --public-url=…)
sultan agent --server=https://ingress.example.com:8443 --token=<einladung> # sonst Adresse angeben
# danach genügt:
sultan agent
```
Der Agent liest die Container mit `traefik.enable=true` aus Docker und meldet sie an den Server. Wichtig: Der Ingress erreicht nur **veröffentlichte** Ports (`ports: "8081:80"`), nicht solche, die nur auf `127.0.0.1` veröffentlicht sind. Ohne Docker: `--docker-socket=none`. Über welche Adresse der Ingress den Host erreicht, ermittelt der Agent automatisch; für NetBird z. B. `--backend-interface=wt0`.
**Ohne Docker (Bare-Metal, LAMP …)** 🖥️ beschreibst du deine Dienste in `/etc/sultan.d/*.yaml`:
```yaml
services:
- name: kunde-a
url: http://127.0.0.1:8080
hosts: [kunde-a.de]
https: true
redirect_http_to_https: true
redirect_www: to_non_www
healthcheck: { expect_title: "KundeA - Willkommen" } # bei Ausfall: Wartungsseite statt Fehler
```
Beim ersten Verbinden merkt sich der Agent den Fingerprint des Servers – ab dann läuft alles automatisch. 🎉
### Wartung 🔧
```bash
sultan agent maintenance on --for 2h --public-reason "Wir spielen ein Sicherheitsupdate ein." # auf dem Knoten
sultan token node maintenance --id=pi --for 2h # oder auf dem Server
sultan token node resume --id=pi # beenden
```
Besucher sehen währenddessen die Wartungsseite mit Grund und voraussichtlichem Ende – auch, wenn der Knoten gerade neu startet.
### Monitoring & Benachrichtigungen 📊
```bash
sultan server --notify-slack=https://hooks.slack.com/… --notify-ntfy=https://ntfy.sh/mein-sultan
curl -s http://127.0.0.1:8080/metrics/checkmk # Checkmk Local Checks: Verträge, Zahlungen, Knoten
```
Sultan meldet ablaufende Verträge, Mahnstufen, ausgefallene Knoten und Policy-Verstöße – jedes Ereignis genau einmal.
### Hinweis- und Wartungsseiten 🚧
Fällt ein Dienst aus oder läuft ein Abo ab, zeigt Sultan statt eines Fehlers eine freundliche Seite (Deutsch und Englisch). Eigene Gestaltung:
```bash
sultan dump-landing # Vorlagen nach ./suldata/landing – anpassen, wirkt sofort
sultan server --hoster-name="Mein Hosting" --hoster-url=https://hosting.example
```
Fertige Docker-Compose-Vorlagen für Ingress und Backend-Host liegen unter [deploy/compose](deploy/compose).