Einen Desktop-Host fuer SSH scharfschalten
Bevor ein Agent ein Werkzeug auf einer Workstation ausfuehren kann, muss die Maschine den Server hereinlassen — einmal, ueber ein Netz, mit einem Schluessel. Diese Seite ist das vollstaendige Verfahren fuer einen Windows-Host, der Reihe nach, mit einer Probe nach jedem Schritt. Arbeiten Sie sie ab, und Sie muessen nichts raten.
Die Schritte laufen auf der Maschine, in einer erhoehten PowerShell, ausser wo
ein Schritt es anders sagt. Wo <user> steht, setzen Sie das Konto ein, auf das
die Werkzeuge lizenziert sind.
Was Sie bauen
Abschnitt betitelt „Was Sie bauen“SupaCloud-Server ──SSH (nur Tailnet)──▶ Workstation haelt den privaten Schluessel haelt den oeffentlichen Schluessel, im Secret-Bag der Resource die Werkzeuge, die GPU, die LizenzDer Server waehlt hinaus zu einer Maschine, die er benennt. Es gibt keinen Agenten zu installieren, und nichts auf der Workstation ruft nach Hause.
0. Vorher
Abschnitt betitelt „0. Vorher“-
Die Maschine ist im Tailnet, und Sie kennen ihren Tailnet-Namen oder die
100.x-Adresse. -
Sie tun das nicht auf einem Alltagsrechner. Ein Desktop-Host fuehrt Agenten-Kommandos als echter Benutzer aus, ohne Container-Grenze — kein
cap_drop, kein schreibgeschuetztes Root, keine PID-Grenzen. Nehmen Sie eine dedizierte Maschine. -
Sie haben ein Schluesselpaar fuer diesen Host. Erzeugen Sie es auf der Serverseite, nicht auf der Workstation:
Terminal-Fenster ssh-keygen -t ed25519 -N '' -C 'supacloud→bench-01' -f ./bench-01bench-01.pubkommt auf die Maschine;bench-01kommt in Schritt 7 in den Secret-Bag der Resource und sonst nirgendwohin.
1. Den OpenSSH-Server installieren
Abschnitt betitelt „1. Den OpenSSH-Server installieren“Add-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0Probe: Get-WindowsCapability -Online -Name OpenSSH.Server* | Select-Object State
meldet Installed.
2. Starten und gestartet halten
Abschnitt betitelt „2. Starten und gestartet halten“Set-Service -Name sshd -StartupType AutomaticStart-Service -Name sshdProbe: Get-Service sshd | Select-Object Status, StartType meldet Running
und Automatic. Ein Host, der nur bis zum naechsten Neustart antwortet, ist ein
Host, der um 3 Uhr nachts ausfaellt.
3. Den oeffentlichen Schluessel einlegen — und das hier vorher lesen
Abschnitt betitelt „3. Den oeffentlichen Schluessel einlegen — und das hier vorher lesen“Windows-OpenSSH liest fuer Konten in der lokalen Administratorengruppe nicht
~/.ssh/authorized_keys. Der Match Group administrators-Block am Ende von
C:\ProgramData\ssh\sshd_config leitet diese Konten auf
C:\ProgramData\ssh\administrators_authorized_keys um. Stillschweigend. Das ist
der mit Abstand haeufigste Grund, aus dem ein richtiger Schluessel abgewiesen wird.
Waehlen Sie eines:
A — das Konto ist kein Administrator (bevorzugt; die Werkzeuge brauchen das selten):
$file = "C:\Users\<user>\.ssh\authorized_keys"New-Item -ItemType Directory -Force -Path (Split-Path $file) | Out-NullSet-Content -Path $file -Value (Get-Content .\bench-01.pub) -Encoding asciiicacls $file /inheritance:r /grant "<user>:R" /grant "SYSTEM:F"B — das Konto ist Administrator: legen Sie denselben Schluessel stattdessen in
C:\ProgramData\ssh\administrators_authorized_keys, mit der ACL, die diese Datei
verlangt:
$file = "C:\ProgramData\ssh\administrators_authorized_keys"Set-Content -Path $file -Value (Get-Content .\bench-01.pub) -Encoding asciiicacls $file /inheritance:r /grant "Administrators:F" /grant "SYSTEM:F"So oder so ist die ACL der Punkt: OpenSSH weist eine Schluesseldatei ab, die jemand anders schreiben kann — und zwar indem es den Schluessel ignoriert, nicht indem es etwas sagt.
Probe: icacls $file listet nur die zwei Eintraege, die Sie vergeben haben.
4. Port 22 nur im Tailnet oeffnen
Abschnitt betitelt „4. Port 22 nur im Tailnet oeffnen“Die Installation legt eine breite eingehende Regel an. Ersetzen Sie sie.
Get-NetFirewallRule -Name 'OpenSSH-Server-In-TCP' -ErrorAction SilentlyContinue | Set-NetFirewallRule -Enabled False
New-NetFirewallRule ` -Name 'sshd-tailnet-only' ` -DisplayName 'OpenSSH Server (nur Tailnet)' ` -Direction Inbound -Protocol TCP -LocalPort 22 -Action Allow ` -RemoteAddress 100.64.0.0/10 ` -InterfaceAlias (Get-NetAdapter | Where-Object InterfaceDescription -like '*Tailscale*').Name100.64.0.0/10 ist der Adressbereich, den ein Tailnet vergibt — die Regel bleibt
also richtig, wenn der Adapter umbenannt wird; die Interface-Bindung ist das
zweite Schloss.
Probe: von einer Maschine ausserhalb des Tailnets scheitert
Test-NetConnection <public-ip> -Port 22. Von innen gelingt sie. Beide Haelften
zaehlen — eine Regel, die alle hereinlaesst, besteht die zweite Probe auch.
5. Den Host-Key-Abdruck zurueckmelden
Abschnitt betitelt „5. Den Host-Key-Abdruck zurueckmelden“ssh-keygen -lf C:\ProgramData\ssh\ssh_host_ed25519_key.pubEs druckt etwas wie
256 SHA256:9k2f… C:\ProgramData\ssh\ssh_host_ed25519_key.pub (ED25519).
Melden Sie den SHA256:…-Teil zurueck. Er kommt in das Feld
host_key_fingerprint der Resource und ist das, was den Server davon abhaelt, mit
einer anderen Maschine zu sprechen, die auf dieser Adresse antwortet. Es gibt hier
kein Trust-on-first-use: mit einem ungepinnten Host verbinden wir uns nicht.
6. Die zwei Hilfskommandos installieren
Abschnitt betitelt „6. Die zwei Hilfskommandos installieren“Der Evidenzleser muss auf der Maschine genau zwei Dinge tun: eine Datei ausgeben
und sagen, wann diese Datei zuletzt geschrieben wurde. Beides wird deklariert,
nicht angenommen, weil cat / type / Get-Content nicht auf jedem Host
dasselbe Wort sind.
Legen Sie C:\ProgramData\supacloud\bin\sc-read.ps1 an:
param([Parameter(Mandatory)][string]$Path)if (-not (Test-Path -LiteralPath $Path)) { exit 1 }$bytes = [System.IO.File]::ReadAllBytes($Path)$out = [Console]::OpenStandardOutput()$out.Write($bytes, 0, $bytes.Length)$out.Flush()Es schreibt absichtlich Bytes, nicht Text: eine Pipeline, die ihren Report durch PowerShell leitet, schreibt UTF-16LE mit Byte-Order-Mark, und der Leser dekodiert das nur richtig, wenn die Marke ueberlebt.
Legen Sie C:\ProgramData\supacloud\bin\sc-mtime-ms.ps1 an:
param([Parameter(Mandatory)][string]$Path)if (-not (Test-Path -LiteralPath $Path)) { exit 1 }$utc = (Get-Item -LiteralPath $Path).LastWriteTimeUtc[DateTimeOffset]::new($utc, [TimeSpan]::Zero).ToUnixTimeMilliseconds()Deklarieren Sie beide auf der Resource:
"read_command": "powershell -NoProfile -NonInteractive -File C:\\ProgramData\\supacloud\\bin\\sc-read.ps1","stat_command": "powershell -NoProfile -NonInteractive -File C:\\ProgramData\\supacloud\\bin\\sc-mtime-ms.ps1"Der Aufruf ueber powershell -File sorgt dafuer, dass die Login-Shell keine Rolle
mehr spielt — die Zeile laeuft gleich, ob die SSH-Sitzung in cmd.exe oder in
PowerShell landet.
Probe, in einer SSH-Sitzung, gegen eine existierende Datei:
powershell -NoProfile -NonInteractive -File C:\ProgramData\supacloud\bin\sc-mtime-ms.ps1 C:/Windows/win.iniEs druckt eine 13-stellige Zahl. Beachten Sie die Schraegstriche im Pfad: ein Backslash ist in einem deklarierten Pfad ein abgewiesenes Metazeichen und bleibt es — der Treiber weist Token ab, statt sie zu escapen, weil der entfernte Dialekt nicht erkennbar ist.
Auf einer Linux-Workstation sind dieselben zwei Kommandos cat -- und ein
zweizeiliges mtime-ms-Skript; siehe
Ein lokales Desktop-Werkzeug einbinden.
7. Den privaten Schluessel uebergeben — einmal
Abschnitt betitelt „7. Den privaten Schluessel uebergeben — einmal“Legen Sie die ssh_host-Resource im Workspace an, mit Host, Port, Benutzername und
dem host_key_fingerprint aus Schritt 5, und legen Sie den privaten Schluessel in
ihren Secret-Bag:
{ "private_key": "-----BEGIN OPENSSH PRIVATE KEY-----\n…\n-----END OPENSSH PRIVATE KEY-----\n" }Loeschen Sie danach Ihre lokale Kopie.
Es gibt bewusst keinen Endpunkt, der Ihnen das ausstellt. Ein solcher Endpunkt waere ein zweiter Ort, an dem dieselben Zugangsdaten liegen, und Zugangsdaten mit zwei Heimaten haben zwei Gelegenheiten zu entweichen und keinen einzelnen Ort zum Zurueckziehen. Der Schluessel existiert im Secret-Bag und auf der Workstation und sonst nirgends — nicht im Agenten, nicht in der MCP-Schicht, und auch nicht im Werkzeug der Maschine selbst, das ihn ebenfalls nie sieht.
8. Die ganze Kette nachweisen
Abschnitt betitelt „8. Die ganze Kette nachweisen“Von der Serverseite:
ssh -i ./bench-01 -o StrictHostKeyChecking=yes <user>@<tailnet-name> "echo ok"Dann, als Agent oder ueber die API:
tool.list { "host": "bench-01", "probe": true }Jedes deklarierte Werkzeug sollte mit available: true und einer
reported_version zurueckkommen, die seinen Vertrag erfuellt. Kommt eine Version
unlesbar zurueck, richten Sie das vorher: eine Festlegung, die sich nicht lesen
laesst, blockiert den Lauf, statt still durchzugehen — genau das wollen Sie, und
jetzt ist es leichter zu klaeren als mitten in einem Auftrag.
Was das nicht gibt
Abschnitt betitelt „Was das nicht gibt“Ein ueber SSH gestartetes Kommando laeuft auf Windows in einer nicht-interaktiven Sitzung ohne Desktop. Das ist eine Eigenschaft von Windows, kein Konfigurationsfehler, und es geht nicht weg.
- Die Werkzeugtreiber bleiben unberuehrt. Blender, Unity, FreeCAD, KiCad und OpenSCAD laufen alle kopflos; genau darum ist der Batch-Weg der primaere.
- Die Bildschirmhaelfte steht nicht zur Verfuegung.
desktop.observeunddesktop.actbrauchen eine angemeldete Maschine und einen Agenten, der in dieser interaktiven Sitzung laeuft. Das aufzustellen heisst Autologon plus den Agenten als Vordergrundprozess in dieser Sitzung — nie als Windows-Dienst, denn ein Dienst lebt in Session 0, und Session 0 hat keinen Desktop zum Aufnehmen oder Anklicken.
Das ist Betriebsarbeit an der Maschine, keine Konfiguration auf dieser Seite. Bis
sie getan ist, scheitern desktop.observe und desktop.act auf einem
Windows-Host an der fehlenden Sitzung, und jedes kopflose Werkzeug arbeitet weiter.
Verwandt
Abschnitt betitelt „Verwandt“- Ein lokales Desktop-Werkzeug einbinden — die Deklaration, die ein Werkzeug braucht, sobald die Maschine antwortet.