12 KiB
Sultan für Hoster: Kunden-Onboarding (pro / enterprise)
Für Hoster, die Sultan als bezahlten Dienst anbieten: Du betreibst Ingress (Traefik + sultan server), der Kunde betreibt seine Backends mit sultan agent. Grundinstallation: siehe community.md. Wartung: siehe wartung.md.
Stand: Der kommerzielle Teil (Lizenzen, Mahnwesen, Vertragsende) ist durch Tests abgedeckt, im echten Betrieb aber noch nicht erprobt. Bekannte Lücken stehen am Ende.
Wer hat was?
| Hoster | Kunde | |
|---|---|---|
| Läuft dort | Traefik, sultan server |
Docker-Host(s) mit sultan agent |
| Geheimnisse | suldata/ des Servers: TLS-Schlüssel, Master-Key (signiert Lizenzen, Leases, Befehle) |
node.key je Host; verlässt den Host nie, auch du siehst ihn nicht |
| Entscheidet | welche Domains, wie viele Routen, bis wann | welche Dienste wie geroutet werden (Labels) |
| Sieht | alle Routen, Knoten, Verstöße, Fristen; je Container Compose-Projekt und -Verzeichnis, Startzeit, Health; je Host FQDN, Betriebssystem, Docker-Version, NetBird-IP | nur sein eigenes Agent-Log |
Die Host- und Container-Angaben sammelt der Agent automatisch für Inventar und Störfall (wo läuft welche URL, in welchem Compose-Verzeichnis). Sag das dem Kunden; enthält ein Verzeichnisname Vertrauliches, sieht der Hoster es.
Der Kunde kann nur Domains aus seinem Mandanten beanspruchen und nur bis max_routes. Alles andere verwirft der Server mit Eintrag im Audit-Log. Kunden sehen sich gegenseitig nicht. Gleichnamige Router verschiedener Kunden kollidieren nicht, weil jeder Router in Traefik als <mandant>__<knoten>__<router> erscheint.
1. Vor dem Onboarding klären
Aus dem Vertrag gehen diese Werte in den Mandanten:
| Wert | Flag | Hinweise |
|---|---|---|
| Kennung | --id |
a–z 0–9 - _, dauerhaft (erscheint in Routernamen, Logs, Monitoring). Z. B. Kundennummer. |
| Name, Kontakt | --name, --email |
für dich (Listen, Benachrichtigungen) |
| Domains | --domains |
kunde.de = genau diese; *.kunde.de = alle Subdomains, ohne kunde.de selbst → meist beides eintragen. Wildcard auf TLD (*.de) ist verboten. Umlaut-Domains als Punycode (xn--…). |
| Paket | --tier pro oder enterprise |
Beide verhalten sich technisch derzeit gleich (Mahnwesen, Vertragsende). Der Unterschied ist vertraglich. community = kein Mahnwesen, läuft nicht ab. |
| Routen | --max-routes |
Anzahl Router (jeder traefik.http.routers.<name> und traefik.tcp.routers.<name> zählt, auch Redirect-Router). -1 = unbegrenzt. Überzählige werden alphabetisch verworfen. |
| Vertragsende | --valid-until JJJJ-MM-TT |
Default: heute + 1 Jahr. Danach sehen Besucher die Landingpage „Hosting abgelaufen“. |
| Zusatzfunktionen | --features |
custom_middleware, websockets, tcp_stream. Ohne custom_middleware sind nur Standard-Middlewares erlaubt (Redirects, Pfad-Umschreibung, Header, Kompression). BasicAuth, Rate-Limit, IP-Allowlist usw. brauchen das Feature, sonst wird der Router mit Verstoß abgelehnt. chain und errors sind nie erlaubt. tcp_stream erlaubt TLS-Passthrough (Kunde mit eigener CA/TLS, z. B. tinyCA): nur HostSNI mit einzelnen Hosts aus den Domains, nur Port 443; ab Paywall oder bei Wartung ist die Route ganz weg (keine Hinweisseite möglich). |
Achtung: Vertragsende, Domains und Paket lassen sich bisher nicht nachträglich ändern (tenant update fehlt, siehe Lücken). Deshalb --valid-until und --domains beim Anlegen sorgfältig setzen.
2. Netzwerk mit dem Kunden klären
Das ist der häufigste Grund für ein holpriges Onboarding. Zwei Wege müssen frei sein:
- Agent → Server: ausgehend TCP 8443 vom Kunden-Host zu deiner
--public-url. Meist ohnehin offen. - Traefik → Kunden-Backend: dein Ingress verbindet sich per HTTP auf den veröffentlichten Port des Containers. Dafür gibt es drei Möglichkeiten:
| Situation beim Kunden | Lösung |
|---|---|
| öffentliche IP / eigenes Rechenzentrum | Kunde gibt die Ports nur für deine Ingress-IP(s) frei |
| hinter NAT/CGNAT/DS-Lite | VPN/Overlay (z. B. NetBird): Agent mit SULTAN_BACKEND_INTERFACE=wt0; eventuell Einladung mit --server-url https://<netbird-ip>:8443 |
| gleiches LAN wie der Ingress | nichts zu tun |
Vorgaben für den Kunden:
- Ports veröffentlichen (
"9080:80"), nicht nur127.0.0.1:9080:80. - Der Agent meldet als Backend die Adresse, über die er deinen Server erreicht. Das kann eine IPv6-Adresse sein. Dann muss dein Ingress IPv6 können, sonst setzt der Kunde
SULTAN_BACKEND_HOST. - Knoten-ID: Default ist der Hostname. Gleichnamige Hosts beim selben Kunden (z. B.
web01) brauchenSULTAN_NODE_ID.
3. Mandant anlegen und einladen
cd /docker/sultan-server
docker compose exec sultan-server sultan token tenant add \
--id k10042 --name "Novesia Bonn" --email it@novesia-bonn.de \
--domains "novesia-bonn.de,www.novesia-bonn.de" \
--tier pro --max-routes 10 --valid-until 2027-10-31
docker compose exec sultan-server sultan token invite --tenant k10042 --ttl 7d
Die Einladung (sltn_enroll_…):
- Ein Knoten pro Einladung. Drei Hosts = drei Einladungen.
- Bis zur Benutzung geheim: Wer sie hat, kann einen Knoten für diesen Kunden anmelden. Danach ist sie wertlos. Übergabe über einen sicheren Kanal (Kundenportal, verschlüsselte Mail, Passwortmanager), nicht ins Ticket kopieren.
- Gespeichert wird nur ein Hash; du kannst sie später also nicht noch einmal anzeigen. Verloren oder abgelaufen → einfach neu erzeugen. Aufräumen:
sultan token purge. - Sie enthält Server-Adresse und Zertifikats-Fingerprint. Der Kunde braucht nichts weiter, kein CA-Zertifikat, keine IP-Liste. Mit einer gefälschten Adresse landet der Agent nie bei einem fremden Server (Pinning).
4. Was du dem Kunden schickst
Ein Paket aus:
- Einladung (sicherer Kanal) und Gültigkeit.
- Compose-Vorlage deploy/compose/agent + Image
zot.chmln.de/sultan:<version>. - Die Labels, passend zu deinen Traefik-Namen (Entrypoint, Certresolver):
labels: - "traefik.enable=true" - "traefik.http.routers.shop.rule=Host(`www.novesia-bonn.de`) || Host(`novesia-bonn.de`)" - "traefik.http.routers.shop.entrypoints=websecure" - "traefik.http.routers.shop.tls.certresolver=le" - "traefik.http.services.shop.loadbalancer.server.port=80" - DNS-Vorgabe: Domains per CNAME auf deinen Ingress (z. B.
web11.r25dc.de). Erst dann klappt Let's Encrypt. - Freigabe aus Abschnitt 2 (Ports nur für deine Ingress-IP).
- Die Wartungsregeln aus wartung.md, vor allem:
stopstattdownundsultan agent maintenance on.
5. Abnahme
docker compose exec sultan-server sultan token node list --tenant k10042 # online, Lease bis …
curl -s 'http://127.0.0.1:8080/v1/traefik-config?format=yaml' | grep k10042 # Router + Backend-URL
curl -sI http://<backend-ip>:<port> # vom Ingress: Backend erreichbar?
curl -sI https://www.novesia-bonn.de # Zertifikat + Seite
docker compose logs sultan-server | grep -i verstoß # abgelehnte Domains/Routen?
Fehlt eine Route, steht der Grund im Server-Log („Policy-Verstoß“: fremde Domain, HostRegexp, Routenlimit, verbotene Middleware) oder im Agent-Log des Kunden („Container nicht übernommen“: Port nicht veröffentlicht).
6. Laufender Betrieb
Zahlung und Mahnwesen
Sultan kennt keine Rechnungen, nur das Fälligkeitsdatum einer offenen Zahlung. Das trägst du (oder später dein Rechnungssystem) ein:
docker compose exec sultan-server sultan token tenant payment --id k10042 --overdue-since 2026-11-01
docker compose exec sultan-server sultan token tenant payment --id k10042 --paid
Daraus ergibt sich automatisch:
| Tage überfällig | Stufe | Besucher sehen |
|---|---|---|
| < 8 | – | normale Seite |
| ab 8 | watermark | Hinweisseite, nach 5 s weiter zur Seite (dann 60 min Ruhe) |
| ab 25 | firemark | wie oben, 10 s |
| ab 32 | paywall | Seite offline, Hinweisseite mit deinem Angebot (--hoster-name, --hoster-url) |
Mit --paid ist sofort (nächster Refresh, ≤ 1 min) alles normal. Bei Stufe 1 und 2 meldet Checkmk bzw. Slack/ntfy.
Sofort sperren, z. B. bei Zahlungsausfall oder Missbrauch:
sultan token tenant set-status --id k10042 --status suspended_payment # Paywall-Seite
sultan token tenant set-status --id k10042 --status quarantined # Routen weg, Agent-Anfragen abgelehnt
sultan token tenant set-status --id k10042 --status active # zurück
Vertragsende
- Checkmk (
/metrics/checkmk): WARN 30, CRIT 14 Tage vorvalid_until. Benachrichtigungen gehen einmal pro Frist raus. - Ab
valid_untilwird der Mandantexpired, Besucher sehen „Hosting abgelaufen“ (402). Die Leases enden spätestens zum Vertragsende. - Verlängern: siehe Lücken.
Monitoring
curl -s http://127.0.0.1:8080/metrics/checkmk # als Checkmk-Local-Check einbinden
Liefert pro kommerziellem Mandanten Vertragsfrist und Mahnstufe, pro Knoten online/offline/Wartung und Lease, dazu Policy-Verstöße der letzten 24 h. Zusätzlich Push per SULTAN_NOTIFY_SLACK / SULTAN_NOTIFY_NTFY: Knoten > 15 min offline, wieder online, Fristen, Mahnstufen. Policy-Verstöße erscheinen nur im Server-Log und als Zähler in Checkmk.
Inventar (Accountant)
Betreibst du mehrere Ingress-Server, melden sie ihr Inventar an deinen sultan accountant: Mandanten, Knoten und je URL das Ziel (<fqdn>:<port>), Container, Compose-Verzeichnis, Health und das Produkt aus sultan.accountant.billing. Im Störfall:
sultan accountant where kunde-a.de # Backend <fqdn>:<port>, Container, Compose-Verzeichnis, seit wann, wo vorher
sultan accountant routes --tenant=k10042
Einrichtung: sultan accountant invite --payee=<server> auf dem Accountant, die Einladung als SULTAN_ACCOUNTANT_TOKEN in die .env des Ingress-Servers, Neustart. Danach liegen payee.key und accountant.json in suldata/ (mitsichern).
Abrechnung: Kunden legt der Accountant aus den Mandanten automatisch an; du ergänzt Zahlweise, Pauschale, Gruppe und Notiz (customer set), pflegst den Produktkatalog (product add|set|price, Preise optional) und ordnest Posten zu (Label sultan.accountant.billing am Container oder assign --host). sultan accountant billing --month=JJJJ-MM --out=…csv liefert die Monatsliste; die Spalte „Hinweis“ zeigt Lücken (kein Kunde, nicht zugeordnet, Zahlweise offen) und Kündigungen mit tagesgenauer Gegenrechnung.
Checkmk auf dem Accountant-Host: curl -s http://127.0.0.1:8082/metrics/checkmk als Local Check einbinden. Je URL ein Service Sultan URL <host> (WARN bei gestoppt, Policy-Verstoß, unhealthy, Knoten getrennt; Wartung bleibt OK) mit Ziel http://<fqdn>:<port> und Compose-Verzeichnis im Text, je Ingress-Server Sultan Payee <id> (WARN ab 2 h ohne Bericht, CRIT ab 6 h). Den Text passt du mit sultan accountant dump-checkmk an.
Sicherheit
- Verdacht auf kompromittierten Kunden-Host:
sultan token node drain --id …(Routen sofort weg), danachnode deleteund neue Einladung für einen sauberen Host. - Schlüssel des Knotens tauschen, ohne Neuanmeldung:
sultan token node command --id … --cmd rotate_key. - Dein
suldata/(Master-Key!) regelmäßig verschlüsselt sichern. Wer den Master-Key hat, kann Lizenzen und Befehle für alle Kunden ausstellen.
Kunde kündigt
sultan token node drain --id <knoten> # Routen sofort weg (für jeden Knoten)
sultan token tenant delete --id k10042 # löscht Mandant, Knoten und offene Einladungen
Bekannte Lücken (Stand heute)
| Lücke | Folge | Notlösung |
|---|---|---|
tenant update fehlt |
Vertragsende, Domains, Paket, Routenlimit nicht änderbar | Keine saubere: tenant delete + neu anlegen löscht auch die Knoten (neue Einladungen nötig). Erste Priorität für den kommerziellen Betrieb. |
| pro = enterprise | kein technischer Unterschied | vertraglich regeln |
| Keine Admin-API | Zahlungsstatus nur per CLI | Skript über docker compose exec |
| Kommerzieller Betrieb nicht im Feld getestet | – | mit einem Testmandanten und kurzen Fristen (--valid-until in wenigen Tagen, payment --overdue-since in der Vergangenheit) durchspielen |
public_reason einsprachig |
Wartungstext für de und en gleich | – |