6 Min. Lesezeit

Können erzwungene SSH-Befehle ein KI-Dienstkonto absichern?

Erzwungene SSH-Befehle beschränken KI-gesteuerte Dienstkonten auf benannte Serveraktionen, während die menschliche Genehmigung an der Zugriffskontrolle bleibt.

Können erzwungene SSH-Befehle ein KI-Dienstkonto absichern?

Ein KI-Agent sollte niemals einen SSH-Zugangsschlüssel erhalten, der bedeutet: «Mach alles, was dieses Konto kann.» Das ist keine Berechtigungsgrenze. Es ist eine Einladung, eine solche Grenze zu finden, meist über ein unerwartetes Argument, eine weitergeleitete Verbindung, deren Deaktivierung du vergessen hast, oder ein Deployment-Skript, das seinem Aufrufer zu stark vertraut.

Erzwungene SSH-Befehle geben dem entfernten Server das letzte Wort darüber, was nach der Authentifizierung gestartet wird. Sie eignen sich gut für Deployment- und Diagnosekonten, weil sie eine vage Fähigkeit, den Zugriff auf eine entfernte Shell, durch eine klar benannte Operation ersetzen, die du kontrollierst und prüfen kannst. Sie ersetzen nicht die menschliche Genehmigung für die Verwendung des Zugangsschlüssels. Halte diese beiden Kontrollen getrennt: Eine Person entscheidet, ob ein Agent den Schlüssel verwenden darf, und der Server entscheidet, welche eng begrenzte Aktion dieser Schlüssel ausführen kann.

Ein erzwungener Befehl begrenzt die Ausführung, nicht die Authentifizierung

Ein erzwungener Befehl weist sshd an, ein vom Server ausgewähltes Programm auszuführen, selbst wenn der Client eine Shell anfordert oder einen anderen Befehl übergibt. Der Client authentifiziert sich weiterhin zuerst. Diese Unterscheidung klingt selbstverständlich, bis ein Dienstkonto in einer Agent-Konfiguration auftaucht und ein erfolgreicher Login wie ein genehmigtes Deployment behandelt wird.

OpenSSH unterstützt diese Kontrolle an zwei Stellen. Du kannst command="/path/to/wrapper" an einen einzelnen öffentlichen Schlüssel in authorized_keys anhängen oder ForceCommand in sshd_config für einen Benutzer oder eine Gruppe setzen. In beiden Fällen speichert sshd den vom Client angeforderten Befehl in der Umgebungsvariable SSH_ORIGINAL_COMMAND und startet stattdessen das erzwungene Programm.

Das OpenSSH-Handbuch sshd(8) ist beim ersten Punkt eindeutig: Eine command-Option erzwingt nach der Authentifizierung die Ausführung des angegebenen Befehls. Es dokumentiert auch, dass der ursprüngliche Befehl für dieses erzwungene Programm weiterhin verfügbar bleibt. Genau dieses Detail führt bei vielen schwachen Entwürfen zum Fehler. Der Wrapper erhält eine Zeichenfolge von einem nicht vertrauenswürdigen Client. Er muss diese Zeichenfolge als Anfrage parsen und darf sie nicht an eine Shell weitergeben.

Verwende die Variante pro Schlüssel, wenn ein Konto mehrere sorgfältig voneinander getrennte Zugangsschlüssel hat. Ein Release-Schlüssel kann den Deployment-Wrapper starten, während ein Operations-Schlüssel einen schreibgeschützten Diagnose-Wrapper startet. So bleibt die Absicht in authorized_keys sichtbar und du kannst einen Schlüssel entfernen, ohne den übrigen Zugriff des Kontos zu verändern.

Verwende ForceCommand, wenn das Konto selbst niemals eine allgemeine Shell bereitstellen darf, unabhängig davon, wie es sich authentifiziert. Dazu gehören auch ein Passwort, dessen Deaktivierung du vergessen hast, eine künftige Zertifizierungsstelle oder ein Administrator, der einen weiteren öffentlichen Schlüssel hinzufügt, ohne die erforderlichen Optionen zu kopieren. Ein Block wie Match User deploy macht die Regel bei einer Prüfung schwer zu übersehen.

Verwende keine der beiden Varianten, um ein menschliches Administratorkonto in ein Automatisierungskonto umzuwandeln. Menschen benötigen für Reparaturarbeiten irgendwann eine echte Shell. Gib der Automatisierung ein eigenes Unix-Konto, einen eigenen Zugangsschlüssel, einen eigenen Befehls-Wrapper und passende Zuständigkeitsgrenzen.

Der Server muss den Deployment-Einstiegspunkt kontrollieren

Ein Deployment-Konto sollte in ein einzelnes Skript gelangen, das du kontrollierst, nicht in einen allgemeinen Befehlsinterpreter. Das Skript kann eine kleine Auswahl von Anfragen akzeptieren, sollte Repository-Pfad, Zielverzeichnis, Service-Unit und ausführbare Datei jedoch selbst bestimmen.

Dieser authorized_keys-Eintrag beschränkt einen einzelnen Zugangsschlüssel auf einen Wrapper und verweigert Verbindungsfunktionen, die in einem Deployment-Konto nichts zu suchen haben:

restrict,command="/usr/local/libexec/release-gate" ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAI... release-agent

Die Option restrict ist nützlich, weil OpenSSH sie als Kurzform dokumentiert, die Port-Weiterleitung, Agent-Weiterleitung, X11-Weiterleitung und die Zuweisung eines Pseudo-Terminals deaktiviert. Das genaue Verhalten richtet sich nach den vom Server unterstützten OpenSSH-Optionen. Teste die Option deshalb mit der von dir eingesetzten Version. Wenn deine Umgebung für Prüfungen oder Kompatibilität ausdrückliche Optionen verlangt, schreibe sie einzeln aus:

command="/usr/local/libexec/release-gate",no-port-forwarding,no-agent-forwarding,no-X11-forwarding,no-pty ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAI... release-agent

Der Wrapper sollte keinen frei formulierten Deployment-Befehl annehmen. Gib den Aufrufern feste Verben und einen begrenzten Wert. So können sie beispielsweise ein Release nur anhand einer unveränderlichen Revision anfordern:

ssh [email protected] "release 9f2a7c6d1e4b8a03"

Ein sicherer Wrapper kann genau diese Grammatik und nichts anderes akzeptieren:

#!/bin/sh
set -eu

request=${SSH_ORIGINAL_COMMAND-}
case "$request" in
  "release "[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]* )
    revision=${request#release }
    case "$revision" in
      *" "*|*[!0-9a-f]*)
        echo "invalid revision" >&2
        exit 64
        ;;
    esac
    exec /usr/local/libexec/run-release "$revision"
    ;;
  *)
    echo "unsupported remote request" >&2
    exit 64
    ;;
esac

Dieses Beispiel benötigt weiterhin eine Längenprüfung, wenn dein Revisionsformat eine bestimmte Länge verlangt. Ein produktiver Wrapper sollte eine vollständige unveränderliche Objekt-ID oder eine Release-Kennung akzeptieren, deren Format du festlegst. Akzeptiere keinen Branch-Namen wie main, wenn der Aufrufer ihn zwischen Genehmigung und Deployment verschieben kann. Ein Branch ist ein Zeiger. Eine unveränderliche Revision sorgt dafür, dass Genehmigungsprotokoll, Deployment-Log und erzeugtes Artefakt auf dasselbe Objekt verweisen.

Das Skript run-release sollte absolute Pfade verwenden und seine eigene Umgebung festlegen. Verlasse dich nicht auf PATH, Arbeitsverzeichnis, Locale, GIT_DIR, GIT_SSH_COMMAND oder LD_PRELOAD, die vom Aufrufer stammen. Ein minimaler Anfang sieht so aus:

#!/bin/sh
set -eu
PATH=/usr/sbin:/usr/bin:/sbin:/bin
export PATH
unset CDPATH ENV BASH_ENV GIT_DIR GIT_WORK_TREE GIT_SSH_COMMAND
cd /srv/release-repo

revision=$1
/usr/bin/git cat-file -e "$revision^{commit}"
/usr/local/libexec/build-and-activate "$revision"

Das Konto sollte nur die Dateien besitzen, die es ändern muss. Wenn es einen Dienst neu starten muss, gib ihm einen einzelnen, eng begrenzten sudoers-Befehl mit festen Argumenten, statt passwortlosen Zugriff auf einen allgemeinen Paketmanager oder eine Shell zu gewähren. Ein Deployment-Konto, das seinen eigenen Wrapper schreiben, seine eigenen authorized_keys ändern oder die Unit bearbeiten kann, unter der sein Code läuft, kann normalerweise weitreichende Kontrolle zurückgewinnen. Prüfe diese Pfade und nicht nur die SSH-Konfiguration.

SSH_ORIGINAL_COMMAND ist eine Eingabe, keine Befehlszeile

Der häufigste Fehler bei erzwungenen Befehlen ist diese Zeile:

sh -c "$SSH_ORIGINAL_COMMAND"

Damit hebst du die gerade eingerichtete Kontrolle wieder auf. Der Client kann release goodrev; curl ... | sh, Befehlsauswertung, umgeleitete Ausgabe oder ein sorgfältig maskiertes Argument anfordern, das ein privilegiertes Tool erreicht. Ein Wrapper, der eval, sh -c, bash -c oder eine nicht maskierte Expansion aufruft, hat den Zugriff auf eine entfernte Shell unter einem anderen Dateinamen wiederhergestellt.

Versuche nicht, einen vollständigen Shell-Parser zu bauen. Du brauchst keinen. Definiere ein bewusst kleines Protokoll und lehne alles ab, was außerhalb davon liegt. Für ein Deployment-Konto kann eine Anfrage aus einem Verb und einer Kennung bestehen. Für ein Diagnosekonto kann sie aus einem exakten Wort wie health oder version bestehen.

Ein Verteiler-Wrapper für Diagnosen kann ganz auf Parsing verzichten:

#!/bin/sh
set -eu

case "${SSH_ORIGINAL_COMMAND-}" in
  health)
    exec /usr/local/libexec/report-health
    ;;
  queue-depth)
    exec /usr/local/libexec/report-queue-depth
    ;;
  version)
    exec /usr/local/libexec/report-version
    ;;
  "")
    echo "a diagnostic name is required" >&2
    exit 64
    ;;
  *)
    echo "diagnostic is not allowed" >&2
    exit 64
    ;;
esac

Auch diese Diagnose-Skripte müssen ihre Argumente selbst kontrollieren. report-health sollte feste Binärdateien gegen feste lokale Sockets oder bekannte Dienstnamen aufrufen. Es sollte keinen Host-Parameter akzeptieren und curl "$host" ausführen, ebenso wenig einen Journal-Filter an eine Shell weitergeben. Ein schreibgeschützter Befehl kann trotzdem Datenbankzugangsdaten, interne Netzwerktopologie, Umgebungswerte oder Kundendaten offenlegen.

Oft heißt es, ein sorgfältig maskierter Shell-Befehl reiche aus, weil der aufrufende Agent vertrauenswürdig sei. Diese Annahme scheitert, wenn der Agent feindseligen Anweisungen aus einem Repository folgt, einen Wert mit einer Anweisung verwechselt oder schlicht einen Fehler macht. Der entfernte Server kann nicht erkennen, ob eine gefährliche Anfrage aus böser Absicht oder aus einem übereifrigen Tool-Aufruf stammt. Er sieht nur Eingaben. Seine Entscheidung muss deterministisch sein.

Wenn du strukturierte Eingaben brauchst, verwende ein begrenztes Format und einen Parser, der zusätzliche Felder ablehnt. JSON ist nicht automatisch sicherer, weil ein Shell-Wrapper es weiterhin falsch behandeln kann. Eine kleine Anfrage wie release <64 Kleinbuchstaben im Hexadezimalformat> lässt sich leichter validieren, dokumentieren, testen und prüfen als ein JSON-Objekt mit optionalen Feldern.

Weiterleitungen können den Sinn der Einschränkung umgehen

Ein erzwungener Befehl verhindert nicht automatisch, dass ein authentifizierter Client SSH als Tunnel verwendet. Das OpenSSH-Handbuch behandelt Befehlsausführung und Weiterleitungen als getrennte Kontrollen. Wenn du nur command="..." hinzufügst, kann ein Client je nach übriger Serverkonfiguration weiterhin verlangen, dass sshd einen lokalen Port zu einem internen Dienst weiterleitet.

Das ist wichtig, weil ein eingeschränktes Konto möglicherweise über Netzwerkzugriff verfügt, den der Agent nicht haben sollte. Ein Agent, der auf dem entfernten Host kein /usr/bin/ps ausführen kann, erreicht über diesen Host vielleicht trotzdem einen Datenbankport, wenn Weiterleitungen offen bleiben. Dann ist das Konto keine Deployment-Identität mehr, sondern ein Netzwerk-Sprungbrett.

Für ein Konto, das keine interaktive Sitzung benötigt, solltest du alle folgenden Funktionen verweigern, sofern du nicht begründen kannst, warum das Konto eine davon braucht:

  • TCP-Weiterleitung
  • Agent-Weiterleitung
  • X11-Weiterleitung
  • Zuweisung eines Pseudo-Terminals
  • vom Benutzer kontrollierte Umgebungsvariablen

restrict deckt die ersten vier Kategorien bei modernen OpenSSH-Installationen ab. Wenn dein Konto tatsächlich eine Ausnahme benötigt, solltest du nicht die gesamte Einschränkung aufheben. OpenSSH unterstützt Optionen wie permitopen="host:port", um das Ziel einer Weiterleitung zu begrenzen. Behandle dies als eigenes Zugriffskonzept und teste sowohl erlaubte als auch verweigerte Ziele.

Prüfe außerdem den ausgehenden Netzwerkzugriff des Wrappers. Ein Deployment-Skript, das beliebige URLs abrufen, beliebige Repositories klonen oder beliebige Daten nach außen senden kann, hat einen breiten Kommunikationskanal, auch wenn SSH-Weiterleitungen deaktiviert sind. Feste Artefaktquellen und angeheftete Revisionen verringern dieses Risiko. Firewall-Regeln oder dienstspezifische Zugangsdaten müssen den Rest möglicherweise absichern.

Die Genehmigung muss vor dem Verbindungsaufbau erfolgen

Agenten ein Aktions-Gateway geben
Claude Code und andere MCP-fähige Agenten verbinden sich über den enthaltenen sp mcp stdio Shim.

Ein erzwungener Befehl begrenzt den Schaden, den eine genehmigte SSH-Nutzung anrichten kann. Er beantwortet nicht die Frage, ob der aktuelle Agent-Prozess den Zugangsschlüssel überhaupt verwenden darf. Diese Entscheidung gehört an die Zugriffskontrolle, bevor der Agent eine SSH-Verbindung herstellt.

Das ist besonders für autonome Coding-Agenten wichtig. Ein Repository kann den Agenten anweisen, einen Deployment-Befehl auszuführen. Eine Tool-Ausgabe kann dies anfordern. Eine kompromittierte Abhängigkeit kann den Agenten in diese Richtung lenken. Liegt der Zugangsschlüssel in der Umgebung oder im Dateisystem des Agenten, kann dieser ihn verwenden, ohne dass jemand den Moment der Verwendung sieht.

Bewahre private SSH-Schlüssel außerhalb des Agent-Prozesses auf und fordere eine Genehmigung an, wenn ein neuer Agent-Lauf zum ersten Mal Zugriff verlangt. Bei Konten mit weitreichenden Folgen solltest du jede Verwendung bestätigen lassen. Der erzwungene Befehl auf dem entfernten Server setzt dann eine feste Obergrenze für die von dieser Genehmigung erlaubte Aktion.

Sallyport setzt diese Trennung um, indem SSH-Schlüssel in seinem verschlüsselten Tresor bleiben, neue Agent-Prozesse standardmäßig pro Sitzung autorisiert werden und SSH über den Helfer sp-ssh ausgeführt wird, statt den Schlüssel an den Agenten zu übergeben.

Verwechsle eine Genehmigungskarte nicht mit der Serverautorisierung. Die Genehmigung beantwortet: «Darf dieser Prozess diesen Zugangsschlüssel jetzt verwenden?» Der Server beantwortet: «Was darf dieser Zugangsschlüssel nach dem Login tun?» Du brauchst beide Antworten, weil sie auf unterschiedliche Weise versagen. Eine Genehmigung kann einen überraschenden Prozess stoppen. Erzwungene Befehle können verhindern, dass ein genehmigter Prozess einen Release-Schlüssel in eine Shell verwandelt.

Die Beschreibung der Genehmigung sollte nützlich sein. Nenne Umgebung und Aktion in der Bezeichnung des Zugangsschlüssels, etwa production release oder staging diagnostics. Eine Bezeichnung wie deploy-key-2 zwingt die prüfende Person, sich während einer Unterbrechung an die Vorgeschichte zu erinnern. So werden routinemäßige Genehmigungen schnell zu automatischen Klicks.

Deployment und Diagnose trennen, bevor die Allowlist wächst

Audit-Verlauf offline überprüfen
Mit sp audit verify lässt sich der verschlüsselte Audit-Chiffretext ohne Tresorschlüssel prüfen.

Deployment und Diagnose wirken ähnlich, weil beide SSH benötigen. Sie haben jedoch unterschiedliche Datenflüsse und Fehlerbilder. Lege sie nach Möglichkeit hinter separate Konten oder separate Zugangsschlüssel mit erzwungenen Befehlen.

Ein Deployment-Konto verändert den Zustand. Es kann eine feste Revision abrufen, ein Artefakt bauen, ein Release-Verzeichnis austauschen und einen Dienst neu starten. Seine Ausgabe sollte Revision, Ziel, Exit-Status und eine kurze Fehlermeldung enthalten. Es benötigt weder beliebigen Log-Zugriff noch Prozessinspektion oder Datenbankabfragen.

Ein Diagnosekonto liest den Zustand. Es kann das Ergebnis eines Health-Endpunkts, eine begrenzte Anzahl von Warteschlangeneinträgen, eine Dienstversion oder das Ende eines sorgfältig gefilterten lokalen Logs melden. Es sollte keine Dienste neu starten, Dateien rotieren, jeden Prozess abfragen oder beliebige Pfade lesen. Sobald ein Diagnose-Wrapper einen vom Benutzer angegebenen Dateinamen, Unit-Namen, Host oder eine Befehlsoption akzeptiert, musst du sein Eingabemodell erneut prüfen.

Ein gemeinsames Konto beginnt mit einer harmlosen Liste:

release <revision>
health
logs <service>
restart <service>

Dann braucht jemand logs api --since, eine andere Person einen Notfallneustart, und der Wrapper beginnt, Argumente an journalctl oder systemctl durchzureichen. Bald enthält der Code Sonderfälle, die niemand erklären kann. Teile die Konten, bevor es so weit kommt. Separate Zugangsschlüssel ermöglichen strengere Genehmigungen für Änderungen in der Produktion und zugleich einen weniger riskanten Diagnoseablauf.

Jede Aktion sollte einen Eintrag erzeugen, der festhält, was der Wrapper akzeptiert hat, nicht nur die undurchsichtige SSH-Befehlszeichenfolge. Bei einem Release gehören die unveränderliche Revision und der Zielname ins Log. Bei einer Diagnose gehören der benannte Diagnoseschritt und sein Ergebnis hinein. Halte geheime Werte aus Argumenten und Logs heraus. Wenn eine Aktion ein Geheimnis benötigt, sollte das entfernte Skript es über einen eigenen kontrollierten Mechanismus abrufen, statt es vom SSH-Client entgegenzunehmen.

Ablehnungspfade von einem Wegwerf-Client aus testen

Ein eingeschränktes Konto ist erst dann wirklich eingeschränkt, wenn du die Anfragen testest, die es ablehnen muss. Führe diese Prüfungen von einem Wegwerf-Konto oder Test-Host aus, bevor du die Konfiguration in einer Produktionsumgebung einsetzt. Die Beispiele setzen voraus, dass der Zugangsschlüssel bereits auf dem Server installiert ist.

ssh [email protected] "release 9f2a7c6d1e4b8a03"
ssh [email protected]
ssh [email protected] "id"
ssh [email protected] "release 9f2a; id"
ssh -N -L 15432:db.internal:5432 [email protected]
ssh -tt [email protected] "health"

Der erste Befehl sollte nur dann den Release-Wrapper erreichen, wenn die Revision seine Regeln erfüllt. Die nächsten drei sollten mit der Ablehnungsnachricht des Wrappers und einem Status ungleich null fehlschlagen. Der Versuch einer Port-Weiterleitung sollte scheitern, bevor ein Listener eingerichtet wird. Die Terminalanforderung sollte fehlschlagen oder ohne Terminal laufen, je nachdem, wie dein Client die verweigerte Zuweisung meldet.

Teste anschließend die weniger offensichtlichen Fälle. Probiere führende und nachgestellte Leerzeichen, Tabulatoren, eine leere Befehlszeichenfolge in Anführungszeichen, Zeilenumbrüche, ein sehr langes Argument, Unicode-Leerzeichen, Befehlsauswertung, Umleitungen und doppelte Argumente aus. Wenn deine Shell oder dein Wrapper einen dieser Fälle in eine akzeptierte Anfrage umwandelt, verschärfe die Grammatik.

Prüfe im Rahmen des Tests auch die Dateiberechtigungen und Eigentumsverhältnisse des Kontos. Ein Angreifer, der /usr/local/libexec/release-gate ersetzen kann, muss SSH nicht umgehen. Ein Konto, das seine eigene Deployment-Quelle ändern kann, kann möglicherweise auch den Code verändern, der mit höheren Rechten ausgeführt wird. Prüfe die gesamte Kette: authorized_keys, sshd-Konfiguration, Wrapper, Deployment-Skripte, Service-Definitionen, beschreibbare Verzeichnisse und jeden sudoers-Eintrag.

Die Anfrage auf beiden Seiten der Grenze prüfen

SSH-Schlüssel außerhalb von Agenten aufbewahren
Sallyports verschlüsselter Tresor bewahrt SSH-Schlüssel auf, während sp-ssh die Verbindung herstellt.

Entfernte Logs erklären, was der Server akzeptiert hat. Logs an der Zugriffskontrolle erklären, welcher lokale Prozess die Verbindungsberechtigung angefordert hat. Bewahre beide auf, denn keines kann die Frage des anderen beantworten.

Protokolliere auf der entfernten Seite erfolgreiche Authentifizierungen, die vom erzwungenen Wrapper akzeptierte Aktion, die unveränderliche Revision oder den Diagnosenamen, eine Anfrage-ID und den abschließenden Status. Sende diese Einträge an einen Ort, den das Dienstkonto nicht überschreiben kann. Protokolliere nicht den rohen Wert von SSH_ORIGINAL_COMMAND, wenn Aufrufer darin geheime Daten unterbringen könnten. Noch besser: Lass dein Protokoll von vornherein keine geheimen Daten zu.

Bewahre auf der lokalen Seite die Sitzungsidentität und die einzelne Anfrage zur SSH-Aktion auf. Sallyport zeichnet Agent-Läufe und einzelne Aufrufe in getrennten Protokollen innerhalb eines verschlüsselten, hashverketteten Audit-Logs auf. Mit sp audit verify lässt sich diese Kette offline und ohne Tresorschlüssel prüfen.

Eine Hash-Kette macht aus einer schlechten Berechtigung keine gute. Sie erleichtert jedoch das Erkennen nachträglicher Änderungen am aufgezeichneten Verlauf. Das ist nach einem fehlgeschlagenen Deployment, einer strittigen Genehmigung oder einer überraschenden Befehlsanfrage hilfreich. Gleichzeitig erzwingt sie eine gute Disziplin: Definiere das Aktionsvokabular früh, damit die Einträge für Menschen verständlich bleiben.

Die erste Umsetzung sollte langweilig sein. Erstelle ein eigenes Unix-Konto, einen erzwungenen Wrapper, eine Operation mit fester Eingabegrammatik, deaktivierte Weiterleitungen und einen Test, der beweist, dass ssh account@host keine Shell liefert. Füge Fähigkeiten erst hinzu, wenn du ihre Eingaben, Ausgaben, Dateizugriffe, Netzwerkzugriffe und die Person benennen kannst, die sie genehmigen sollte.

FAQ

Was ist ein erzwungener SSH-Befehl?

Verwende ein SSH-Konto, dessen authorized_keys-Eintrag die Option command="..." enthält, oder ForceCommand in sshd_config, wenn jede Authentifizierungsmethode für dieses Konto derselben Regel folgen soll. Der Server führt deinen Wrapper statt des vom Client übergebenen Befehls aus und stellt die ursprüngliche Anfrage über SSH_ORIGINAL_COMMAND bereit.

Reichen erzwungene SSH-Befehle aus, um ein KI-Dienstkonto abzusichern?

Nein. Ein erzwungener Befehl begrenzt, welches Programm der SSH-Server nach erfolgreicher Authentifizierung startet. Er entscheidet nicht, wer den Zugangsschlüssel verwenden darf. Kombiniere ihn mit einer Zugriffskontrolle, die eine Person einen neuen Agent-Lauf oder eine sensible Nutzung genehmigen lässt. Danach sollte das Serverkonto so stark eingeschränkt sein, dass die Genehmigung nur begrenzte Folgen haben kann.

Sollte ich ForceCommand oder den command-Eintrag in authorized_keys verwenden?

In der Regel nicht. Ein eigener Schlüssel in authorized_keys stellt eine klare Verbindung zwischen einem Zugangsschlüssel und einer Regel für einen erzwungenen Befehl her. Eine gemeinsame Kontoeinstellung kann dagegen Menschen und Automatisierung gemeinsam betreffen. Verwende ForceCommand, wenn bewusst jeder Zugriff auf ein eingeschränktes Konto denselben Wrapper durchlaufen soll.

Wie erhält ein erzwungener Befehl den ursprünglichen SSH-Befehl?

OpenSSH speichert den vom Client angeforderten Befehl in SSH_ORIGINAL_COMMAND, wenn es einen erzwungenen Befehl ausführt. Behandle diesen Wert als nicht vertrauenswürdige Eingabe: Lehne Shell-Metazeichen und nicht vorgesehene Optionen ab und akzeptiere nur wenige, klar definierte Befehlsformen.

Deaktiviert command= in authorized_keys die SSH-Port-Weiterleitung?

Nein. Die Option command="..." allein deaktiviert weder Port-Weiterleitung noch Agent-Weiterleitung, X11-Weiterleitung oder die Zuweisung eines Pseudo-Terminals. Verwende restrict, wenn es passt, oder deaktiviere jede Funktion ausdrücklich. Teste das Ergebnis anschließend von einem Client aus.

Wie beschränke ich ein SSH-Konto ausschließlich auf Deployments?

Ein Deployment-Konto sollte genau ein Skript ausführen, das du kontrollierst. Verwende feste Pfade, ein festes Arbeitsverzeichnis, eine eingeschränkte Umgebung und eine Allowlist für Deployment-Ziele. Akzeptiere keine beliebigen Branches, Hosts, Pfade oder Shell-Fragmente, die du an git, rsync, sudo oder eine Shell weitergibst.

Was sollte ein Wrapper für erzwungene Befehle ablehnen?

Gib bei einer leeren Anfrage, einer Shell, einem unbekannten Unterbefehl, fehlerhaften Argumenten sowie unerwarteten Leerzeichen oder Metazeichen einen Status ungleich null zurück. Protokolliere die Ablehnung mit dem authentifizierten Konto und den Informationen zur Quelle. Schreibe jedoch niemals Geheimnisse aus Umgebungsvariablen oder Befehlszeilen in das Log.

Können KI-Coding-Agenten erzwungene SSH-Befehle nicht interaktiv verwenden?

Erzwungene Befehle funktionieren mit SSH, weil der Server und nicht der KI-Prozess entscheidet, welches Programm nach der Authentifizierung startet. Auf dem entfernten Host ist dafür keine interaktive Eingabeaufforderung nötig. Damit eignen sie sich für nicht interaktive Agent-Läufe, sofern die Genehmigung an der Zugriffskontrolle erhalten bleibt.

Wie prüfe ich Aktionen über ein Konto mit erzwungenem SSH-Befehl?

Bewahre die Logs des entfernten Wrappers, Deployment-Logs und SSH-Authentifizierungslogs auf und verknüpfe sie mit dem Protokoll der Zugangsschlüsselverwendung. Ein manipulationssicheres lokales Aktionsprotokoll ist hilfreich, weil es festhält, welcher Agent-Prozess die Verwendung des Zugangsschlüssels angefordert hat, bevor der entfernte Server die Verbindung erhielt.

Sollten Deployment- und Diagnosezugriff über separate SSH-Konten erfolgen?

Verwende ein eigenes Konto, wenn die Diagnose einen anderen Zweck, andere erlaubte Befehle oder andere Folgen als ein Deployment hat. Werden beide Aufgaben in einem Wrapper kombiniert, wächst die Allowlist meist so lange, bis daraus versehentlich eine entfernte Shell wird.

Sallyport

Sallyport führt API-Aufrufe und SSH-Befehle für Ihren KI-Agenten aus. Die Schlüssel bleiben in einem lokalen Tresor auf Ihrem Mac; Sie geben jeden Lauf frei, und jede Aktion landet in einem versiegelten Journal.

© 2026 Sallyport · Open Source unter Apache-2.0 · Oleg Sotnikov