Files
sultan/docs/hoster.md
brandClaude Opus 5.5 c2d6dd694f sultan token tenant update: Mandanten nachträglich ändern
Weitere Compose-Stacks eines Kunden sind der Regelfall; bisher ließen sich
Domains und Routenlimit nur per delete + neu anlegen (und damit ohne Knoten)
ändern. Die Änderung liegt bewusst nur auf dem Server: Ein Agent darf sich
keine Domains selbst freigeben.

- Domains und Features werden hinzugefügt/entfernt statt ersetzt; unbekannter
  Eintrag beim Entfernen ist ein Fehler, die letzte Domain bleibt
- Verlängerung in die Zukunft setzt expired auf active (der Ablauf-Job setzt
  das nie zurück); Quarantäne und Zahlungsverzug bleiben
- Wechsel auf kommerziell mit vergangenem valid_until verlangt --valid-until

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-02 18:32:28 +01:00

13 KiB
Raw Permalink Blame History

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).

Alle Werte außer der Kennung lassen sich später mit tenant update ändern (siehe unten).

Mandant nachträglich ändern

sultan token tenant update --id k10042 --add-domains "zweitshop.de,www.zweitshop.de"   # weiterer Stack des Kunden
sultan token tenant update --id k10042 --remove-domains alt-kunde.de                   # Routen darauf fallen weg
sultan token tenant update --id k10042 --max-routes 10
sultan token tenant update --id k10042 --valid-until 2028-12-31                        # Vertrag verlängern
sultan token tenant update --id k10042 --tier pro --valid-until 2027-12-31             # von community auf pro
sultan token tenant update --id k10042 --add-features websockets --remove-features tcp_stream
  • Nur Angegebenes ändert sich. Es wirkt ohne Neustart beim nächsten Refresh (bis zu 1 Minute); Knoten und Einladungen bleiben.
  • Verlängern: Liegt das neue --valid-until in der Zukunft, wird der Status expired wieder active. Quarantäne und Zahlungsverzug bleiben bestehen.
  • Wechsel von community auf pro/enterprise: Liegt das bisherige Datum in der Vergangenheit, verlangt der Befehl --valid-until, sonst wären die Seiten sofort abgelaufen.
  • Die letzte Domain lässt sich nicht entfernen. Ein Tippfehler bei --remove-domains ist ein Fehler und ändert nichts.
  • Ein verkürztes Limit wirkt alphabetisch: Überzählige Router werden verworfen.

2. Netzwerk mit dem Kunden klären

Das ist der häufigste Grund für ein holpriges Onboarding. Zwei Wege müssen frei sein:

  1. Agent → Server: ausgehend TCP 8443 vom Kunden-Host zu deiner --public-url. Meist ohnehin offen.
  2. 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 nur 127.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) brauchen SULTAN_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:

  1. Einladung (sicherer Kanal) und Gültigkeit.
  2. Compose-Vorlage deploy/compose/agent + Image zot.chmln.de/sultan:<version>.
  3. 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"
    
  4. DNS-Vorgabe: Domains per CNAME auf deinen Ingress (z. B. web11.r25dc.de). Erst dann klappt Let's Encrypt.
  5. Freigabe aus Abschnitt 2 (Ports nur für deine Ingress-IP).
  6. Die Wartungsregeln aus wartung.md, vor allem: stop statt down und sultan 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 vor valid_until. Benachrichtigungen gehen einmal pro Frist raus.
  • Ab valid_until wird der Mandant expired, 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), danach node delete und 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
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 –