Zum Inhalt springen
Farbschema wählenSprache wählen

Einen Runner sicher einrichten

Diese Anleitung fügt einer SupaCloud-Installation einen Runner hinzu — eine separate Maschine, die Agent-Aufgaben abholt und ausführt. Der Runner verbindet sich rein ausgehend über HTTPS und arbeitet daher hinter NAT ohne eingehende Ports. Für die Konzepte (Pull-Transport, laufspezifisches Secret-Modell, die zwei Bedeutungen von „Runner-Flotte“) siehe die Erklärung Die Runner-Flotte.

  1. Setze eine Umgebungsvariable auf dem Server und starte ihn neu:

    SUPACLOUD_HUB_MODE=true

    Der Standard ist false. Solange aus, läuft jede Aufgabe lokal — egal wie viele Runner registriert sind. Genau das macht es sicher, den Hub-Modus im Notfall auszuschalten, ohne etwas zu deregistrieren.

  2. Bestätige, dass er an ist:

    Terminal window
    curl https://supacloud.example.com/api/runners/config \
    -H "Authorization: Bearer <admin-session-oder-API-token>"
    # → { "hub_mode_enabled": true }
  1. Registriere den Runner als System-Admin und sichere das Token:

    Terminal window
    curl -X POST https://supacloud.example.com/api/runners \
    -H "Authorization: Bearer <admin-session-oder-API-token>" \
    -H "Content-Type: application/json" \
    -d '{
    "name": "gpu-worker-01",
    "capabilities": { "images": ["unified"], "max_parallel": 2 }
    }'
  2. Die Antwort enthält das Klartext-Bearer-Token (scrn_…) genau einmal — der Server speichert nur dessen SHA-256-Hash. Kopiere es jetzt; im nächsten Schritt übergibst du es dem Worker. Geht es verloren, rotiere das Token, statt neu zu registrieren.

Ein Runner kann sich auch dafür entscheiden, Wasm auf seinem Node auszuführen — sowohl Marketplace-Connector-Nodes als auch freie Skripte (JS / TS / Python). Beides läuft in derselben WASI-0.2-Sandbox (pro Lauf Speicher- und Wall-Clock-Grenzen, deny-all-SSRF-geprüfter Egress) hinter der eigenen OS-Grenze des Runners. Zwei Capability-Schlüssel steuern das:

  • connectors: true — der Runner entscheidet sich, Connector-Node-Wasm auszuführen. Der Hub schickt einem Node ohne dieses Flag nie einen Connector-Lauf.
  • wasm.script_languages — die Skriptsprachen, deren Interpreter das Image des Runners tatsächlich bereitgestellt hat. Der Hub schickt einen Skript-Lauf nur an einen Runner, der sowohl connectors: true als auch die Sprache des Skripts hier bewirbt (ein Skript führt Wasm auf dem Node aus, also muss der Node sich für die Wasm-Ausführung entschieden haben und den Interpreter besitzen).
{
"name": "wasm-worker-01",
"capabilities": {
"images": ["unified"],
"connectors": true,
"wasm": { "script_languages": ["js", "ts", "py"] }
}
}

Liste nur Sprachen auf, deren Interpreter der Runner unter SUPACLOUD_SCRIPT_RUNTIME_DIR tatsächlich bereitgestellt hat (reproduzierbar gebaut durch scripts/build-script-runtimes.sh). Der Matcher ist fail-closed: ein fehlender oder fehlerhafter wasm-Schlüssel bedeutet, dass der Runner keine Skriptsprache bewirbt und nie ein Skript erhält — eine bestehende Flotte bleibt also völlig unberührt, und das Hinzufügen des Schlüssels ist ein sicheres, monotones Opt-in.

Wird ein Skript an die Flotte verteilt, schickt der Hub nur Metadaten — die Sprache plus Quelltext und Eingaben als Daten — und einen SHA-256-Integritätsanker seines eigenen Interpreters. Er schickt nie den mehrere MB großen Interpreter. Der Runner löst seinen eigenen lokalen, vertrauenswürdigen Interpreter für diese Sprache auf und prüft den SHA erneut, bevor er ausführt; eine Abweichung (Versionsdrift oder Manipulation) schlägt fail-closed fehl. Eine Anfrage, die versucht, Interpreter-Bytes einzuschmuggeln, wird als Protokollverletzung abgelehnt. Der Interpreter ist stets ein lokal vertrauenswürdiges Runner-Asset — ein per Payload mitgeschickter wird nie ausgeführt.

Das wird instanzweit über SUPACLOUD_CONNECTOR_EXECUTOR_MODE gesteuert (in_process — der Standard — / pooled / runner_fleet); setze es auf runner_fleet, um Connector- und Skript-Läufe an die Flotte zu verteilen. Mit dem Standardmodus in_process laufen Skripte auf dem Hub genau wie zuvor — für bestehende Deployments ist dies ein No-op ohne Konfiguration.

Abschnitt 2 erzeugt ein langlebiges scrn_…-Bearer für einen Runner, den du betreibst. Um einen Runner auf einem Host hochzufahren, dem du weniger vertraust — die Maschine einer Kundin, Edge-Compute, die GPU-Box einer Partnerin — nutze stattdessen den Enrollment-Ablauf. Du übergibst dem Betreiber ein einmaliges, kurzlebiges Enrollment-Token; sein Runner tauscht es beim Boot gegen ein scope-begrenztes, kurzlebiges scrnj_…-JWT ein und erneuert es automatisch. Der Betreiber hält nie ein langlebiges Hub-Credential, und der Runner kann ausschließlich die Workspaces bedienen, auf die du ihn scopt hast. Zum Trust-Modell dahinter siehe Runner-Flotten-Sicherheit.

  1. Enrollment-Token erzeugen (Admin). Wähle Trust-Klasse, Ausführungs-Tier, Isolation und die Workspaces, die der Runner bedienen darf:

    Terminal window
    curl -X POST https://supacloud.example.com/api/runners/enroll \
    -H "Authorization: Bearer <admin-session-oder-API-token>" \
    -H "Content-Type: application/json" \
    -d '{
    "class": "edge",
    "tier": "docker",
    "isolation": "container",
    "allowed_workspace_ids": ["<workspace-uuid>"],
    "allowed_labels": []
    }'

    class ist trusted oder edge; tier ist wasm, docker oder both; isolation ist container oder microvm. Die Antwort enthält das Klartext-Enrollment-Token genau einmal (der Server speichert nur dessen Hash) und eine kurze Ablaufzeit. Kopiere es jetzt und übergib es dem Betreiber über einen sicheren Kanal.

  2. Worker mit dem Installer onboarden (Betreiber). Der Betreiber führt den gehosteten Ein-Zeilen-Installer aus. Dieser lädt das signierte Runner-Artefakt, verifiziert Prüfsumme und Signatur, tauscht das einmalige Enrollment-Token gegen ein scope-begrenztes scrnj_…-JWT und bootet den Runner damit — rein ausgehend, keine eingehenden Ports:

    Terminal window
    curl -fsSL https://supacloud.example.com/install-runner.sh | \
    SUPACLOUD_HUB_URL=https://supacloud.example.com \
    SUPACLOUD_RUNNER_ENROLLMENT_TOKEN=<einmal-token> \
    SUPACLOUD_RUNNER_NAME=customer-edge-01 \
    bash

    Der Installer (nicht das nackte Binary) führt den Eintausch durch: das eigenständige supacloud --runner-Binary liest ausschließlich SUPACLOUD_RUNNER_TOKEN (das scrnj_…-JWT), nie das Enrollment-Token. Zum Onboarding ohne Installer tauschst du das Token selbst — der gRPC-RPC RunnerService.ExchangeToken ({enrollment_token, name} → das JWT, ADR 0050) —, setzt dann SUPACLOUD_RUNNER_TOKEN=<das scrnj_-JWT> und startest das Binary oder Image genau wie in Abschnitt 3.

  3. Der Runner erneuert sein JWT automatisch. Der Eintausch verbrennt das Enrollment-Token (es lässt sich nicht erneut verwenden) und liefert ein scope-begrenztes scrnj_…-JWT, das Trust-Klasse, Tier, Isolation und die erlaubten Workspaces trägt. Das JWT lebt höchstens eine Stunde; der Runner-Daemon holt ein frisches, bevor das alte abläuft — standardmäßig bei etwa der halben Token-TTL, abgeleitet aus token_expires_at des Grants / dem JWT-exp —, und jede Erneuerung ruft den grant-prüfenden gRPC-RPC RunnerService.RefreshToken des Hubs auf, sodass der Entzug eines Workspaces innerhalb einer Token-Lebensdauer wirkt. Um den Takt explizit festzulegen, setze SUPACLOUD_RUNNER_TOKEN_REFRESH_SECS auf die Anzahl Sekunden-vor-Ablauf, bei der neu eingetauscht wird (0, der Standard, bedeutet automatisch ableiten).

Der Runner ist dasselbe SupaCloud-Binary im --runner-Modus. Wähle die Plattform, auf der du installierst. In jedem Fall ist die Konfiguration dieselbe Menge an Umgebungsvariablen; nur der Start unterscheidet sich.

Sobald dein Release-Kanal die signierten Binaries hostet, übernehmen die Installer das gesamte Onboarding auf einer Worker-Node — Plattform erkennen, das gepinnte Binary herunterladen, Prüfsumme und Signatur verifizieren, das einmalige Enrollment-Token eintauschen, installieren und supacloud --runner über TLS ohne eingehende Ports starten. Das einzige Secret, das du übergibst, ist das einmalige Enrollment-Token aus dem Mint-Schritt; der Skript-Body enthält keines.

Linux / macOS (genau die Ein-Zeilen-Anweisung, die die SupaCloud-UI dir gibt):

Terminal window
curl -fsSL "$SUPACLOUD_URL/install-runner.sh" \
| SUPACLOUD_ENROLLMENT_TOKEN=<einmal-token> bash

Windows (PowerShell):

Terminal window
$env:SUPACLOUD_URL = "https://supacloud.example.com"
$env:SUPACLOUD_ENROLLMENT_TOKEN = "<einmal-token>"
irm "$env:SUPACLOUD_URL/install-runner.ps1" | iex

Nützliche Overrides (Umgebungsvariablen, beide Installer): SUPACLOUD_RUNNER_NAME (Standard: Hostname), SUPACLOUD_RUNNER_VERSION / SUPACLOUD_DOWNLOAD_BASE (ein bestimmtes Release oder einen Artifact-Host pinnen) und SUPACLOUD_RUNNER_DRY_RUN=1, um den aufgelösten Download-, Verify- und Start-Plan auszugeben, ohne das Netzwerk anzufassen.

SUPACLOUD_HUB_URL=https://supacloud.example.com
SUPACLOUD_RUNNER_TOKEN=scrn_xxxxxxxxxxxxxxxxxxxxxxxx
SUPACLOUD_RUNNER_NAME=gpu-worker-01
# Optionale Takt-Overrides (sinnvolle Defaults gezeigt):
# SUPACLOUD_RUNNER_HEARTBEAT_SECS=30
# SUPACLOUD_RUNNER_POLL_SECS=3
# Docker-Netzwerk für Agent-Container (muss auf dem Worker existieren):
# AGENT_NETWORK=supacloud-agents

Die Hub-URL muss https:// sein. Ein Klartext-http://-Hub wird beim Start abgelehnt, außer du setzt explizit SUPACLOUD_RUNNER_ALLOW_INSECURE_TRANSPORT=true — nur in einem vertrauenswürdigen privaten Netz je akzeptabel.

  1. Lege die obigen Variablen in eine .env neben das Binary (oder exportiere sie), dann:

    Terminal window
    supacloud --runner
  2. Oder mit dem Docker-Image (Docker-Socket einhängen, damit der Runner Agent-Container auf seinem eigenen Host starten kann):

    Terminal window
    docker run -d --name supacloud-runner \
    -e SUPACLOUD_HUB_URL=https://supacloud.example.com \
    -e SUPACLOUD_RUNNER_TOKEN=scrn_xxxxxxxxxxxxxxxxxxxxxxxx \
    -e SUPACLOUD_RUNNER_NAME=gpu-worker-01 \
    -v /var/run/docker.sock:/var/run/docker.sock \
    <dein-supacloud-image> --runner
  1. Der Runner braucht einen Docker-Host, um Agent-Container zu starten — installiere Docker Desktop (WSL-2-Backend) auf dem Worker.

  2. Setze die Variablen in PowerShell für die Sitzung und starte das Binary:

    Terminal window
    $env:SUPACLOUD_HUB_URL = "https://supacloud.example.com"
    $env:SUPACLOUD_RUNNER_TOKEN = "scrn_xxxxxxxxxxxxxxxxxxxxxxxx"
    $env:SUPACLOUD_RUNNER_NAME = "win-worker-01"
    .\supacloud.exe --runner

    Für einen dauerhaft laufenden Worker führe denselben Befehl als Dienst aus (z. B. mit NSSM) oder nutze den Docker-Image-Befehl aus dem Linux/macOS-Tab innerhalb von WSL 2.

microVM-Ausführungs-Backend (stärkste Isolation)

Abschnitt betitelt „microVM-Ausführungs-Backend (stärkste Isolation)“

Das Standard-Backend docker führt jede Aufgabe in einem Container auf dem lokalen Docker-Host des Runners aus. Für einen nicht vertrauenswürdigen oder Edge-Runner ist das microVM-Backend die stärkste Isolationsstufe: jede beanspruchte Aufgabe läuft in einem eigenen kurzlebigen hardware-virtualisierten Gast (eine Firecracker/Kata-artige microVM), sodass ein kompromittierter Agent-Prozess hinter einer VM-Grenze eingeschlossen ist, nicht nur in einem Kernel-Namespace. Wie die Stufen sich vergleichen, siehe Runner-Flotte Defense-in-Depth → Isolationsstufen.

  1. Baue (oder beziehe) ein Runner-Binary, das mit dem microvm-backend-Feature kompiliert wurde.

  2. Stelle auf dem Worker einen microVM-Launcher bereit (z. B. einen Firecracker/Kata-Wrapper), wähle dann das Backend und richte den Runner darauf aus:

    SUPACLOUD_RUNNER_EXECUTION_BACKEND=microvm
    SUPACLOUD_RUNNER_MICROVM_CMD=/usr/local/bin/launch-microvm
  3. Onboarde (oder onboarde neu) den Runner mit isolation: microvm, damit der fail-closed Dispatch-Filter des Hubs microVM-pflichtige Aufgaben an ihn und nur an ihn routet.

Standardmäßig vertraut der Runner dem Hub über One-Way-TLS (der Hub präsentiert ein Server-Zertifikat; der Runner verifiziert es gegen den System-Trust-Store). Um einen gegenseitigen TLS-Handshake zu verlangen — sodass der Hub auch jeden Runner per Client-Zertifikat authentifiziert, ein starker zweiter Faktor zusätzlich zum Bearer-Token — konfiguriere beide Seiten. mTLS ist optional; ist nichts davon gesetzt, ist der Transport genau wie heute.

  1. Auf dem Hub (Server). TLS direkt ausliefern und Client-Zertifikate verlangen:

    SUPACLOUD_TLS_CERT_PATH=/etc/supacloud/tls/hub.crt
    SUPACLOUD_TLS_KEY_PATH=/etc/supacloud/tls/hub.key
    SUPACLOUD_TLS_CLIENT_CA_PATH=/etc/supacloud/tls/runner-ca.crt

    SUPACLOUD_TLS_CERT_PATH + SUPACLOUD_TLS_KEY_PATH schalten TLS ein; SUPACLOUD_TLS_CLIENT_CA_PATH verlangt und verifiziert zusätzlich ein Client-Zertifikat gegen diese CA. Eine Verbindung ohne vertrauenswürdiges Client-Zertifikat wird beim Handshake abgewiesen.

  2. Auf jedem Runner. Eine Client-Identität präsentieren und (optional) die Hub-CA pinnen:

    SUPACLOUD_RUNNER_TLS_CLIENT_CERT_PATH=/etc/supacloud/tls/runner.crt
    SUPACLOUD_RUNNER_TLS_CLIENT_KEY_PATH=/etc/supacloud/tls/runner.key
    SUPACLOUD_RUNNER_TLS_CA_PATH=/etc/supacloud/tls/hub-ca.crt # optional: Hub-CA pinnen
  1. Beim Start macht der Runner einen Heartbeat-Aufruf. Wird das Token abgelehnt, beendet er sich sofort mit klarer Fehlermeldung; ist der Hub kurz nicht erreichbar, startet er trotzdem und versucht es weiter.

  2. Die Zeile des Runners wechselt beim ersten erfolgreichen Heartbeat von pending → online. Bestätige es vom Server aus:

    Terminal window
    curl https://supacloud.example.com/api/runners \
    -H "Authorization: Bearer <admin-session-oder-API-token>"
  3. Starte eine Aufgabe, deren Image der Runner bewirbt. Bei aktivem Hub-Modus und einem passenden Online-Runner verteilt der Server sie an den Runner, und der Live-Ereignisstrom zeigt den Fortschritt genau wie bei einem lokalen Lauf.