# SSH-Portweiterleitungen für autonome Agenten: sicherere Tunnel

SSH-Portweiterleitungen für autonome Agenten brauchen ein strengeres Design als gewöhnlicher Entwicklerzugriff. Ein Tunnel kann eine Datenbank, ein Administrationspanel oder einen Staging-Dienst, die sonst nur auf localhost erreichbar wären, über einen einzigen SSH-Befehl für einen Agenten zugänglich machen. Hält der Agent weitreichende SSH-Zugangsdaten, ist der Tunnel nur ein Symptom. Das eigentliche Problem ist, dass niemand Ziel, Dauer oder Berechtigung hinter diesem Befehl begrenzt hat.

Ich habe erlebt, dass Teams einen Tunnel als «vorübergehend» bezeichneten, weil jemand `ssh -L` in einem Terminal eingegeben hatte. Danach lief das Terminal tagelang, sein Port wurde zur Abhängigkeit eines anderen Prozesses, und die Person, die ihn geöffnet hatte, war nicht erreichbar, als die Sicherheitsabteilung wissen wollte, warum ein produktionsnaher Dienst einen ungeklärten Listener hatte. Autonome Arbeit verschärft dieses Muster, weil ein Agent wesentlich konsequenter als ein abgelenkter Mensch Wiederholungen und neue Verbindungen versuchen und einen Tunnel weiterverwenden kann.

Der sichere Ansatz ist einfach: Geben Sie einem Agenten einen eng begrenzten Verbindungsweg, genehmigen Sie eine genau beschriebene zeitweilige Freigabe, protokollieren Sie genügend Kontext für eine spätere Rekonstruktion und machen Sie das Schließen zu einem erzwingbaren Ereignis statt zu einer Absicht.

## Ein Tunnel verändert die Netzwerkerreichbarkeit, nicht nur das SSH-Verhalten

Eine SSH-Portweiterleitung erstellt auf einer Seite der SSH-Verbindung einen Listener und transportiert dessen Datenverkehr zu einem Ziel, das von der anderen Seite aus erreichbar ist. Dieser Listener ist ein Zugangsweg. Wer ihn nur als «SSH-Zugriff» prüft, übersieht den riskanten Teil: Wer kann sich verbinden, wo endet der Datenverkehr und was kann durch den Tunnel übertragen werden?

Angenommen, ein Agent läuft auf einem Build-Host und muss `db-admin.internal.example` auf Port 5432 abfragen. Ein lokales Forwarding kann diesen Dienst auf dem Build-Host als `127.0.0.1:15432` erscheinen lassen. Die Datenbank bleibt aus dem öffentlichen Internet verborgen, aber jeder Prozess auf diesem Host, der den Listener erreicht, kann nun eine Verbindung versuchen. Der SSH-Server wird außerdem zu einer autorisierten Route zur Datenbank.

Das kann vertretbar sein. Es ist nicht dasselbe, wie dem Agenten «SSH für Wartungsarbeiten» zu geben. Die Genehmigung sollte genau diese Route beschreiben:

- auslösender Workload und Host
- Listener-Adresse und Port
- Zielhost und Zielport
- Grund für die Route
- Ablaufzeit und genehmigende Person

Bei einem Remote-Forwarding befindet sich der Listener auf der jeweils anderen Seite. Verbindet sich ein Agent beispielsweise mit einem Bastion-Host und bittet ihn, auf Port 18080 zu lauschen, kann ein Prozess, der diesen Listener erreicht, Daten zurück zum Agent-Host senden. Remote-Forwarding überrascht Teams häufig, weil es einen Dienst hinter einer Firewall freigeben kann, ohne eine eingehende Firewall-Regel zu öffnen.

OpenSSH beschreibt diese Modi in `ssh(1)` getrennt: `-L` erstellt lokale Weiterleitungen, `-R` Remote-Weiterleitungen und `-D` einen SOCKS-Proxy. Diese Trennung ist im Betrieb hilfreich. Genehmigen Sie «Portweiterleitung» nicht als eine einheitliche Berechtigung. Jeder Modus erstellt einen anderen Listener und braucht andere Einschränkungen.

## Lokales, remotes und dynamisches Forwarding brauchen unterschiedliche Entscheidungen

Ein lokales Forwarding passt meist zu einem Agent-Auftrag, wenn der Agent einen bekannten Dienst über einen kontrollierten Sprungserver benötigt. Der Listener liegt auf der Agent-Seite, während der SSH-Server eine Verbindung zum internen Ziel herstellt:

```sh
ssh -N \\
  -L 127.0.0.1:15432:db-admin.internal.example:5432 \\
  agent-db-tunnel@bastion.internal.example
```

`-N` weist SSH an, keinen Remote-Befehl auszuführen. Dadurch wird die Verbindung nicht harmlos. Die Bind-Adresse `127.0.0.1` begrenzt den Listener auf den lokalen Host, während `db-admin.internal.example:5432` das gewünschte Ziel angibt. Das sind getrennte Eigenschaften und beide müssen im Genehmigungsdatensatz auftauchen.

Ein Remote-Forwarding ist nur dann angemessen, wenn ausdrücklich ein Listener auf der entfernten Seite gewünscht ist:

```sh
ssh -N \\
  -R 127.0.0.1:18080:127.0.0.1:8080 \\
  agent-return-path@bastion.internal.example
```

Behandeln Sie einen entfernten Loopback-Listener nicht automatisch als sicher. Ein lokaler Prozess auf dem Bastion-Host kann deutlich mehr Zugriff haben als der Quellrechner des Agenten, und andere Nutzer eines gemeinsam verwendeten Bastion-Hosts können ihn möglicherweise erreichen. Prüfen Sie vor der Freigabe die Nutzer und lokalen Prozesse des entfernten Hosts.

Für autonome Agenten sollte dynamisches Forwarding standardmäßig verboten werden:

```sh
ssh -N -D 127.0.0.1:1080 agent@bastion.internal.example
```

Damit wird ein SOCKS-Listener gestartet. Der Client wählt Ziele später über SOCKS-Anfragen aus, statt im SSH-Befehl ein Ziel zu nennen. Diese Bequemlichkeit beseitigt die Zielgrenze, die Sie für unbeaufsichtigte Arbeit brauchen. Ein Prüfer kann keine einzelne Route genehmigen, und gewöhnliche SSH-Zielbeschränkungen können keine sinnvolle Allowlist für beliebigen SOCKS-Datenverkehr ausdrücken.

Lösen Sie dieses Problem nicht mit einem längeren Genehmigungsformular. Verbieten Sie dynamisches Forwarding für das Agent-Konto. Wenn die Aufgabe mehrere Ziele braucht, definieren Sie jede Weiterleitung einzeln oder setzen Sie einen zweckgebundenen Proxy mit eigener Authentifizierung und Protokollierung vor die Dienste.

## Die Bind-Adresse entscheidet, wer den Listener verwenden kann

Die Bind-Adresse ist Teil der Sicherheitsentscheidung, weil sie bestimmt, welche Rechner eine Verbindung zum Tunnel-Listener herstellen können. Ein Forwarding an Loopback und eines an alle Schnittstellen kann dasselbe Ziel verwenden, aber völlig unterschiedliche Freigaben erzeugen.

Verwenden Sie bei lokalem Forwarding eine ausdrückliche Loopback-Adresse:

```sh
-L 127.0.0.1:15432:db-admin.internal.example:5432
```

Verlassen Sie sich nicht auf den Standardwert. OpenSSH bindet lokale Weiterleitungen normalerweise an Loopback, wenn keine Bind-Adresse angegeben wird. Eine explizite Adresse macht Prüfung, Protokolle und spätere Untersuchungen eindeutiger. Sie verhindert außerdem, dass eine spätere Änderung der Client-Konfiguration den Umfang des Listeners unbemerkt erweitert.

Das ist die gefährliche Variante:

```sh
-L 0.0.0.0:15432:db-admin.internal.example:5432
```

Sie veröffentlicht Port 15432 auf jeder IPv4-Schnittstelle des Client-Hosts. Jeder erreichbare Host kann den Tunnel versuchen. Ein Build-Runner in einem gemeinsam genutzten Subnetz kann so zu einer Brücke in eine interne Datenbank werden, obwohl die Datenbank selbst nur Verbindungen vom Bastion-Host akzeptiert.

Auch IPv6 braucht Aufmerksamkeit. `::1` ist Loopback, `::` lauscht auf allen IPv6-Schnittstellen. Prüfen Sie beide Adressfamilien. Teams stellen manchmal fest, dass `127.0.0.1` sicher war, die Automatisierung aber über einen separaten Konfigurationsweg zusätzlich einen IPv6-Listener geöffnet hatte.

Bei Remote-Weiterleitungen kontrolliert der SSH-Server, welche Bind-Adressen er akzeptiert. In `sshd_config` beeinflusst `GatewayPorts` das Verhalten der Bind-Adresse. Lassen Sie `GatewayPorts no` aktiviert, sofern es keinen geprüften Grund für breitere entfernte Listener gibt. `GatewayPorts clientspecified` gibt einem Agent-Konto zu viel Kontrolle.

Prüfen Sie nach dem Öffnen einer Weiterleitung den Listener auf dem zuständigen Rechner:

```sh
lsof -nP -iTCP:15432 -sTCP:LISTEN
```

Die Ausgabe sollte einen `ssh`-Prozess mit einer Adresse wie `127.0.0.1:15432` oder `::1:15432` zeigen. Bei `*:15432` stoppen Sie den Auftrag und prüfen Befehl und Client-Konfiguration. Dieser Befehl bestätigt nur die lokale Listener-Adresse. Er beweist nicht, dass die entfernte Seite auf das vorgesehene Ziel beschränkt ist.

## Ein dediziertes SSH-Konto muss die erlaubte Route ausdrücken

Ein dediziertes SSH-Konto mit serverseitigen Einschränkungen ist die kleinste sinnvolle Grenze für Agent-Tunnel. Eine Client-Konfigurationsdatei kann nichts gegen einen Agenten erzwingen, der seine eigenen Argumente ändern kann. Legen Sie die Grenzen dort fest, wo der SSH-Server die Verbindung annimmt.

Beginnen Sie für ein Konto mit ausschließlich lokalem Forwarding mit einem Block wie diesem in `sshd_config`:

```text
Match User agent-db-tunnel
    PasswordAuthentication no
    KbdInteractiveAuthentication no
    PubkeyAuthentication yes
    PermitTTY no
    X11Forwarding no
    AllowAgentForwarding no
    AllowStreamLocalForwarding no
    AllowTcpForwarding local
    PermitOpen db-admin.internal.example:5432
    GatewayPorts no
    PermitUserEnvironment no
```

`AllowTcpForwarding local` erlaubt lokale Weiterleitungen und verweigert Remote-Weiterleitungen. `PermitOpen` benennt das Ziel, das dieses Konto anfordern darf. Das ist wichtig, weil ein lokaler Forwarding-Befehl sonst auf jeden Host und Port zeigen kann, der vom Bastion-Host aus erreichbar ist. Ohne `PermitOpen` könnte ein Agent, der die Datenbank benötigt, auch eine Weiterleitung zu einer Administrations-API, einem Cache, einem Metadatendienst oder einem weiteren SSH-Server anfordern.

Gehen Sie mit Hostnamen sorgfältig um. Der SSH-Server löst das Ziel auf, daher muss der Name in `PermitOpen` dort auflösbar sein. Verwenden Sie einen stabilen Namen, der Ihrer Infrastruktur gehört. Wenn sich ein DNS-Name auf beliebige Adressen ändern kann, wirkt die Konfiguration eng, während sich das tatsächliche Ziel unbemerkt verschiebt.

Das Handbuch `sshd_config(5)` beschreibt `PermitOpen` als Zielbeschränkung im Format `host:port`. Die Option macht aus dem Konto keine Policy-Engine. Sie erfüllt genau eine Aufgabe: Eine Weiterleitungsanfrage abzulehnen, deren Ziel nicht aufgeführt ist. Halten Sie das Kontodesign ebenso präzise.

Geben Sie dem Konto keine Shell, nur weil «der Agent sie nicht verwenden wird». Entfernen Sie die Möglichkeit stattdessen. Ein dediziertes Konto sollte für eine einzige Verbindungsrolle existieren. Konfigurieren Sie bei Bedarf einen erzwungenen Befehl für den autorisierten öffentlichen Schlüssel, der bei interaktiver Nutzung beendet wird. Testen Sie diese Entscheidung aber mit dem Forwarding-Verhalten Ihrer Umgebung. Manche Wrapper stören erwartete SSH-Sitzungen, und eine fehlerhafte Einschränkung führt unter Zeitdruck schnell dazu, dass jemand den gesamten Block entfernt.

Vermeiden Sie außerdem Agent-Forwarding. `AllowAgentForwarding no` verhindert, dass der verbundene Server den clientseitigen SSH-Agenten um Signaturen für Authentifizierungsanfragen bittet. Ein SSH-Tunnel-Konto soll Daten übertragen und nicht zum Sprungbrett für andere Hosts werden.

Für ein Ziel mit zwei legitimen Diensten listen Sie zwei ausdrückliche `PermitOpen`-Einträge auf. Verwenden Sie keinen Wildcard-Eintrag und ersetzen Sie das Konto nicht durch ein allgemeines Bastion-Konto, nur um ein weiteres Konto zu vermeiden. Ein zusätzliches Konto ist günstiger als die Erklärung, warum ein Code-Agent ein ganzes internes Subnetz erreichen konnte.

## Temporär bedeutet erzwungenes Ablaufdatum und sauberes Schließen

Ein Tunnel ist nur dann temporär, wenn etwas anderes als gute Absichten ihn beendet. SSH hält eine Verbindung offen, bis der Client sie schließt, das Netzwerk ausfällt oder ein Timeout greift. Ein autonomer Agent kann einen Prozess nach Erfolg, Fehler oder Timeout eines Tests weiterlaufen lassen.

Geben Sie jedem Auftrag eine feste maximale Sitzungsdauer, die nicht im Ermessen des Agenten liegt. Ein Job-Runner kann dies, sofern verfügbar, mit `timeout` erzwingen:

```sh
timeout 20m ssh -N \\
  -o ExitOnForwardFailure=yes \\
  -o ServerAliveInterval=30 \\
  -o ServerAliveCountMax=3 \\
  -L 127.0.0.1:15432:db-admin.internal.example:5432 \\
  agent-db-tunnel@bastion.internal.example
```

`ExitOnForwardFailure=yes` verhindert, dass der Auftrag fortfährt, wenn SSH den gewünschten Listener nicht erstellen kann. Ohne diese Option meldet ein Agent möglicherweise einen verwirrenden Anwendungsfehler statt eines Problems beim Tunnelaufbau. Die ServerAlive-Einstellungen lassen SSH nach mehreren unbeantworteten Nachrichten beenden. Sie ersetzen nicht die Obergrenze von 20 Minuten.

macOS enthält den GNU-Befehl `timeout` standardmäßig nicht. Verwenden Sie die Timeout-Funktion Ihres Runners, einen überwachten Prozess mit Frist oder einen kleinen Wrapper, der `TERM` sendet und anschließend bestätigt, dass der Prozess beendet wurde. Ersetzen Sie eine fehlende Frist nicht durch einen Cronjob, der «alte SSH-Prozesse» beendet. Dabei werden unabhängige Aufgaben getroffen, und Sie können nicht nachweisen, welche Verbindung beendet wurde.

Der Auftrag sollte den Tunnel zusätzlich in seinem eigenen Aufräumpfad schließen. Behalten Sie für einen gestarteten Prozess seine PID, senden Sie nach Abschluss `TERM`, warten Sie kurz und prüfen Sie anschließend mit `lsof` oder `ss`, dass der Listener verschwunden ist. Die Frist fängt Abstürze ab, der Aufräumpfad kümmert sich um den normalen Abschluss.

Multiplexing braucht besondere Vorsicht. Mit `ControlMaster` und einem gemeinsamen `ControlPath` können mehrere SSH-Aufrufe dieselbe Master-Verbindung wiederverwenden. Für Agenten verwischt das Zuständigkeit und Ablauf: Ein Auftrag kann eine für einen anderen Auftrag genehmigte Verbindung übernehmen, und das Schließen eines Clients beendet möglicherweise nicht den Master. Deaktivieren Sie Multiplexing für das Tunnel-Konto oder erzeugen Sie pro genehmigtem Auftrag einen eindeutigen Control-Pfad.

Ein Wartungsablauf mit regelmäßigem Zugriff sollte regelmäßig wiederkehrende, jeweils begrenzte Sitzungen anfordern. Lassen Sie einen Tunnel nicht offen, weil ein neuer Aufbau unbequem erscheint. Die beim Aufbau gesparten Sekunden erzeugen bei jeder Untersuchung eines unerwarteten Zugriffs neue Unklarheit.

## Eine Genehmigung muss eine rekonstruierbare Verbindung beschreiben

Ein Genehmigungsdatensatz sollte einem Prüfer zeigen, wer eine Route genehmigt hat, was sie geöffnet hat und ob sie innerhalb ihrer Grenze blieb. Ein allgemeiner Ticketkommentar wie «Agent-Zugriff genehmigt» ist kein Beleg. Er nennt weder Listener noch Ziel noch Dauer.

Erfassen Sie die Genehmigung, bevor der Tunnel startet. Halten Sie Identität und Zeitpunkt der genehmigenden Person, Auftrags- oder Lauf-ID, Identität des Agent-Prozesses, Quellrechner, dediziertes SSH-Konto und Fingerabdruck des öffentlichen Schlüssels fest. Erfassen Sie anschließend die Weiterleitung vollständig: Richtung, Bind-Adresse, Listener-Port, Zielhost, Zielport, Zweck und Ablauf.

Der Ergebnisdatensatz braucht eigene Felder. Dazu gehören, ob SSH die Weiterleitung erstellt hat, die Prozess-ID des Clients, Startzeit, endgültiger Exit-Status, Schließzeit und Schließgrund. Timeout, normales Aufräumen, Widerruf durch einen Operator und Netzwerkausfall haben bei einer Prüfung unterschiedliche Bedeutungen.

Verwenden Sie strukturierte Datensätze statt Prosa, aus der jemand Felder herauslesen muss:

```json
{
  "run_id": "run-7c31",
  "approved_by": "oncall-engineer",
  "approved_at": "2025-04-12T14:03:00Z",
  "ssh_account": "agent-db-tunnel",
  "direction": "local",
  "listen": "127.0.0.1:15432",
  "destination": "db-admin.internal.example:5432",
  "expires_at": "2025-04-12T14:23:00Z",
  "purpose": "run migration compatibility check"
}
```

Die Bezeichner in diesem Beispiel sind Platzhalter, keine zwingende Vorlage für Ihr Schema. Entscheidend ist die Disziplin: Ein Datensatz muss Person, Prozess, Route und Zeitfenster miteinander verbinden.

Lassen Sie nicht denselben Agenten die Genehmigung erzeugen und sie als menschliche Autorisierung behandeln. Der Agent kann erklären, warum er eine Verbindung braucht, aber ein Mensch oder ein separat kontrollierter Ablauf muss die Freigabeentscheidung treffen. Wenn eine Aktion routiniert genug für automatische Genehmigung ist, bilden Sie diese Entscheidung in einer eng begrenzten Auftragsdefinition mit Ablaufzeit ab und bewahren Sie die Regelversion auf, die sie erlaubt hat.

Genehmigungsmüdigkeit ist ein Designfehler. Wer jede Übertragung absegnen soll, erzeugt bedeutungslose Klicks. Eine Genehmigung pro temporärer, konkreter Route gibt dem Prüfer etwas Greifbares. Halten Sie die Anfrage kurz, aber nennen Sie Bind-Adresse und exaktes Ziel. Diese beiden Angaben erkennen überraschend viele fehlerhafte Anfragen.

## Zugangsdaten außerhalb des Agenten und Berechtigungen außerhalb seines Prompts halten

Ein autonomer Agent sollte eine Verbindung anfordern und keinen privaten Schlüssel erhalten, mit dem er später beliebige Verbindungen herstellen kann. Sobald ein Agent einen weitreichenden SSH-Schlüssel in seiner Prozessumgebung oder seinem Arbeitsbereich besitzt, kann er ihn kopieren, einen anderen SSH-Client aufrufen, ihn nach dem genehmigten Auftrag weiterverwenden oder an ein nicht vorgesehenes Werkzeug übergeben. Ein Prompt mit «nur für die Datenbank verwenden» verhindert keine dieser Aktionen.

Ein besseres Design verwahrt SSH-Zugangsdaten in einer separaten Ausführungsgrenze. Der Agent übermittelt eine Aktion mit den Routenfeldern. Diese Grenze prüft die Verfügbarkeit des Tresors, die Autorisierung der Sitzung und gegebenenfalls die Genehmigung für die angeforderten Zugangsdaten. Sie führt die SSH-Aktion aus und gibt das Ergebnis zurück, ohne den privaten Schlüssel an den Agenten zu übergeben.

Sallyport verwendet dieses Modell unter macOS: Der mitgelieferte Helfer `sp-ssh` führt SSH-Aktionen aus, während die SSH-Schlüssel im verschlüsselten Tresor der App bleiben und den MCP-Agenten nicht erreichen.

Diese Unterscheidung ist bei der Untersuchung eines Fehlers wichtig. Die Verwahrung der Zugangsdaten beantwortet: «Konnte der Agent das Geheimnis anderswo wiederverwenden?» Die Routenbeschränkung beantwortet: «Konnte dieses Konto ein nicht genehmigtes Ziel erreichen?» Die Genehmigung beantwortet: «Wer hat genau diese Nutzung erlaubt?» Teams fassen diese Fragen oft zu einer Kontrolle zusammen und stellen später fest, dass ihr Audit-Log zwar die Nutzung eines Schlüssels, nicht aber die dadurch mögliche Route beschreibt.

Machen Sie die Anfrageschnittstelle enger als rohe SSH-Argumente. Akzeptieren Sie Felder wie `destination_host`, `destination_port`, `listen_address`, `listen_port` und `max_duration`. Lehnen Sie vom Client gelieferte Optionen ab, die Proxying, Weiterleitungsrichtung, Remote-Befehle, Identitätsdateien oder Control-Sockets ändern. Wer einen beliebigen `ssh`-Befehlsstring akzeptiert, überträgt die Policy-Auslegung an das Parsen von Zeichenketten. Das scheitert, sobald jemand `-R`, `-D`, `ProxyCommand` oder eine zweite Weiterleitung ergänzt.

Bieten Sie für einen Tunnel-Ablauf keine allgemeine Shell als Ausweg an. Der Agent muss `ssh` nicht interaktiv ausführen, wenn die Aktion aus einem einzigen begrenzten Kanal besteht. Enge Aktionen lassen sich leichter genehmigen und widerrufen.

## Audit-Belege müssen einer feindseligen Prüfung standhalten

Tunnelprotokolle sind nur dann nützlich, wenn ein kompromittierter Agent oder lokaler Prozess sie nachträglich nicht umschreiben kann. Textdateien neben dem Agent-Arbeitsbereich sind bequem, beweisen aber wenig, wenn dieselbe Identität sowohl den Tunnelbefehl als auch dessen Historie bearbeiten kann.

Protokollieren Sie Genehmigung, Anfrage, Ausführungsergebnis und Schließergebnis als getrennte Ereignisse mit einer gemeinsamen Lauf-ID. Bewahren Sie die ursprünglichen Anfragefelder auf, statt nur eine dargestellte Befehlszeile zu speichern. Eine Befehlszeile kann Werte in einer Konfigurationsdatei verbergen oder Standardwerte auslassen, die den Listener beeinflusst haben.

Sammeln Sie bestätigende Datensätze aus mehreren Quellen. Die SSH-Protokolle des Bastion-Hosts können angenommene Authentifizierung und fehlgeschlagene Weiterleitungen zeigen. Der Job-Runner zeigt Prozesserstellung und Ende. Der Zieldienst kann eine Verbindung vom Bastion-Host protokollieren. Keiner dieser Belege erklärt allein das gesamte Ereignis, zusammen machen sie Widersprüche sichtbar.

Sallyport schreibt Sitzungs- und einzelne Aufrufjournale in ein verschlüsseltes, Hash-verkettetes Audit-Log. `sp audit verify` prüft diese Kette offline, ohne den Tresorschlüssel zu benötigen. Das ist stärker als eine Protokolldatei, die ein Agent-Prozess verändern kann, ersetzt aber keine schwachen Kontobeschränkungen.

Führen Sie die Prüfung im normalen Betrieb durch, nicht erst nach einem Vorfall. Eine manipulationssichere Kette zeigt, ob die aufgezeichnete Historie vollständig fortgesetzt wurde. Sie sagt nicht, ob Sie anfangs genügend Felder aufgezeichnet haben. Entscheiden Sie, was Sie an einem schlechten Tag wissen müssen, und sorgen Sie dafür, dass diese Felder vor Beginn der Verbindung ins Ereignis gelangen.

## Widerrufen Sie den Weg, bevor Sie die Geschichte untersuchen

Wenn Sie einen falschen oder missbrauchten Tunnel vermuten, stoppen Sie zuerst den Verbindungsweg. Die Untersuchung kann einige Minuten warten. Eine offene Route kann in dieser Zeit Daten übertragen oder lateralen Zugriff ermöglichen.

Beenden Sie den Client-Prozess und bestätigen Sie, dass sein Listener verschwunden ist. Deaktivieren Sie das dedizierte SSH-Konto oder entfernen Sie seinen öffentlichen Schlüssel auf dem annehmenden Server. Wenn ein über den Tunnel übermitteltes Zugangsmittel einem nicht vertrauenswürdigen Prozess zugänglich gewesen sein könnte, rotieren Sie es nach dem Verfahren des jeweiligen Dienstes. Rotieren Sie nicht allein deshalb unabhängige Zugangsdaten, weil ein Tunnel existierte.

Sichern Sie danach die Belege: Genehmigungsdatensatz, angeforderte Route, Prozessprotokolle, SSH-Serverprotokolle, Verbindungsdaten des Zieldienstes und Schließergebnis. Verwenden Sie eine gemeinsame Zeitzone, vorzugsweise UTC. Bei Untersuchungen gehen viele Stunden verloren, wenn ein Runner lokale Zeit, ein Bastion-Host UTC und eine Anwendung Zeitstempel ohne Zeitzone verwendet.

Prüfen Sie vor der Einstufung als eingedämmt, ob ein zweiter Listener existiert. Untersuchen Sie auf dem Agent-Host den angeforderten Port und nahegelegene Weiterleitungsprozesse. Prüfen Sie auf dem Bastion-Host aktive SSH-Sitzungen und Authentifizierungsprotokolle des dedizierten Kontos. Suchen Sie am Ziel nach Verbindungen, die nach dem vorgesehenen Ablauf begonnen haben. Ziel ist es, verbliebene Zugangswege zu finden, nicht schnell eine aufgeräumte Geschichte zu erzählen.

Wenn eine menschliche Genehmigung die falsche Route ermöglicht hat, verbessern Sie zuerst die Anfrageschnittstelle. Eine schönere Prüfseite, die weiterhin beliebige SSH-Flags akzeptiert, wird denselben Fehler wiederholen. Wenn eine Serverbeschränkung versagt hat, testen Sie nach der Reparatur den exakt abgelehnten Befehl und bewahren Sie die Fehlermeldung auf. Kontrollen schaffen Vertrauen, wenn Sie bestätigen, dass sie genau die vorgesehene Route ablehnen.

## Testen Sie Ablehnungspfade, bevor ein Agent vom Tunnel abhängt

Ein Tunnel-Design ist erst fertig, wenn Sie sowohl die genehmigte Verbindung als auch verbotene Varianten testen. Erfolgsfalltests finden Tippfehler. Ablehnungstests zeigen, ob die Grenze tatsächlich existiert.

Verwenden Sie ein Nichtproduktionskonto und versuchen Sie zunächst eine erlaubte Weiterleitung:

```sh
ssh -N -o ExitOnForwardFailure=yes \\
  -L 127.0.0.1:15432:db-admin.internal.example:5432 \\
  agent-db-tunnel@bastion.internal.example
```

Versuchen Sie danach ein Ziel, das `PermitOpen` nicht erlaubt:

```sh
ssh -N -o ExitOnForwardFailure=yes \\
  -L 127.0.0.1:15433:admin-api.internal.example:443 \\
  agent-db-tunnel@bastion.internal.example
```

Der zweite Befehl sollte beim Aufbau der Weiterleitung scheitern. Speichern Sie Clientausgabe und Serverprotokolleintrag. Versuchen Sie anschließend `-R` und `-D`; beide sollten bei einem Konto mit `AllowTcpForwarding local` fehlschlagen. Fordern Sie zuletzt einen Listener mit `0.0.0.0` an und bestätigen Sie, dass Ihre Anfrageschnittstelle ihn vor dem Start von SSH ablehnt oder die lokale Umgebung die zu verhindernde Freigabe tatsächlich verhindert.

Testen Sie den Ablauf mit einer kurzen Dauer. Starten Sie den erlaubten Tunnel, lassen Sie die Frist des Supervisors greifen und prüfen Sie drei Dinge: Der SSH-Prozess endet, `lsof` findet den Listener nicht mehr und der Schließdatensatz nennt die Frist, statt fälschlich einen normalen Abschluss zu behaupten. Diese kleine Übung findet verwaiste Prozesse, bevor Produktionsarbeit von einem Tunnel abhängt, der nie geschlossen wird.

Halten Sie sich bei Zeitdruck an eine einfache Regel: Ein autonomer Agent darf für seinen aktuellen Auftrag für eine begrenzte Zeit und unter einer benannten Genehmigung nur die unbedingt erforderliche Route erhalten. Wenn Sie diese Route nicht in einer Zeile benennen und ihr späteres Schließen beweisen können, hat der Agent mehr Netzwerkberechtigungen, als die Aufgabe rechtfertigt.
