186 lines
9.2 KiB
Markdown
186 lines
9.2 KiB
Markdown
# 👑 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).
|