# SSH-Verbindungs-Multiplexing: Risiken von Control-Sockets

SSH-Verbindungs-Multiplexing kann einen authentifizierten Weg zu einem Host offenlassen, nachdem der Befehl, der ihn erstellt hat, beendet wurde. Dieses Verhalten ist beabsichtigt. Bei einer automatisierten Aufgabe, einem Agentenlauf oder einem Deployment-Job wird es jedoch leicht missverstanden, wenn dieser angeblich abgeschlossen ist.

Ich habe Teams dabei erlebt, wie sie ein vermeintlich geschlossenes Wartungsfenster untersuchten und feststellten, dass ein lokaler `ssh`-Master weiterhin einen aktiven Transport hielt, ein Socket noch neue Clients akzeptierte und ein späterer Befehl ihn ohne erneute interaktive Authentifizierung wiederverwendete. Nichts Ungewöhnliches war passiert. Die Konfiguration tat genau das, was OpenSSH dokumentiert. Das Team hatte das Ende eines Shell-Befehls mit dem Ende des Zugriffs gleichgesetzt.

Der entscheidende Unterschied liegt zwischen einem abgeschlossenen **Clientbefehl** und einem beendeten **authentifizierten Transport**. Multiplexing macht daraus zwei getrennte Ereignisse. Wenn du autonome Arbeit über SSH ausführst, müssen deine Bereinigung und deine Belege diesen Unterschied berücksichtigen.

## Ein Control-Socket kann länger leben als der Befehl, der ihn geöffnet hat

SSH-Multiplexing verwendet eine einzige langlebige SSH-Verbindung, den Master, um Arbeit für spätere SSH-Clientprozesse zu übertragen. Diese späteren Clients werden in der OpenSSH-Dokumentation oft als Slaves bezeichnet. Ein Slave wiederholt den normalen Verbindungsaufbau und die Benutzerauthentifizierung nicht, wenn er den lokalen Master erfolgreich über dessen Control-Socket erreicht.

Eine typische Konfiguration wirkt harmlos:

```sshconfig
Host build-box
    HostName 192.0.2.44
    User deploy
    ControlMaster auto
    ControlPath ~/.ssh/cm/%C
    ControlPersist 20m
```

Der erste Aufruf von `ssh build-box` erstellt eine Netzwerkverbindung und einen Unix-Domain-Socket unter `~/.ssh/cm/`. Spätere Befehle wie `ssh build-box 'uname -a'`, `scp` und `sftp` können diesen Socket verwenden. Mit `ControlPersist 20m` bleibt der Master nach dem Ende seiner letzten Sitzung zwanzig Minuten verfügbar.

Diese Abfolge ist daher normal:

1. Eine Aufgabe führt `ssh build-box 'apply-change'` aus und endet mit Status null.
2. Der Master bleibt verbunden, weil `ControlPersist` ihn verfügbar halten soll.
3. Neunzehn Minuten später führt ein anderer lokaler Prozess `ssh build-box 'read-status'` aus.
4. Dieser Prozess öffnet einen Kanal über den bereits authentifizierten Transport.

Der spätere Befehl kann durch das lokale Betriebssystem und die Socket-Berechtigungen autorisiert sein. Auf dem entfernten Host führt er jedoch nicht zu einer neuen SSH-Authentifizierung. Wer nur den ursprünglichen Anmeldeeintrag betrachtet, kann leicht annehmen, dass die Aktivität viel früher endete.

OpenSSH beschreibt `ControlMaster` im Handbuch zu `ssh_config` als Möglichkeit, mehrere Sitzungen über eine einzige Netzwerkverbindung zu führen, und `ControlPersist` als Möglichkeit, den Master im Hintergrund offen zu halten. Lies beides zusammen: `ControlPersist` ist nicht nur eine Einstellung zur Leistungsoptimierung. Die Option verändert den Zeitraum, in dem ein lokaler Prozess neue Kanäle auf einer authentifizierten Verbindung anfordern kann.

Für Menschen, die an einem Rechner arbeiten, kann dieser Kompromiss sinnvoll sein. Für aufgabenbezogene Automatisierung braucht er einen klar benannten Verantwortlichen und eine ausdrückliche Stop-Aktion.

## Ein Authentifizierungsereignis ist kein Befehlsereignis

Teams vermischen oft Authentifizierung, Verbindung, Kanal und Befehl. In SSH sind das getrennte Dinge, und Multiplexing macht den Unterschied sichtbar.

Der ursprüngliche Master baut eine TCP-Verbindung zum Server auf, prüft den Host, handelt die Kryptografie aus und authentifiziert das Konto. Nach der Authentifizierung kann er SSH-Kanäle öffnen. Ein Shell-Befehl, eine interaktive Shell, eine SFTP-Übertragung, eine lokale Portweiterleitung und eine entfernte Portweiterleitung verwenden auf diesem Transport Kanäle oder kanalbezogene Anfragen.

Ein neuer lokaler `ssh`-Aufruf, der einen verwendbaren Control-Socket findet, sendet eine Anfrage über diesen Socket. Der Master entscheidet, ob er den gewünschten Kanal öffnet. Er benötigt den privaten Schlüssel nicht erneut. Eine erneute Agentenabfrage ist nicht nötig. Der Server muss keinen weiteren Anmeldeversuch erhalten.

Das bedeutet nicht, dass OpenSSH ein wiederverwendbares Passwort im Socket speichert. Diese Beschreibung ist ungenau und führt zu falschen Analysen. Das Sicherheitsproblem ist ein bereits authentifizierter Transport mit einer lokalen Schnittstelle, über die er zum Handeln aufgefordert werden kann. Ein Angreifer, der den Socket verwenden kann, braucht möglicherweise die ursprüngliche Zugangsinformation überhaupt nicht.

Dieser Unterschied verändert die Fragen bei einem Vorfall. Ob der Schlüssel auf der Festplatte verblieben ist, ist wichtig, entscheidet aber nicht darüber, ob der Zugriff bestehen blieb. Stelle stattdessen folgende Fragen:

- Blieb ein Masterprozess mit dem entfernten Ziel verbunden?
- Konnte ein anderer lokaler Prozess seinen Control-Socket erreichen?
- Akzeptierte der Master zusätzliche Sitzungen, Dateiübertragungen oder Weiterleitungsanfragen?
- Wer konnte unter dem Besitzer des Sockets laufen oder das Socket-Verzeichnis durchqueren?
- Wann wurde der Master tatsächlich beendet?

Ein Task-Runner, der den Exit-Code eines Befehls meldet, beantwortet keine dieser Fragen. Der Code beschreibt nur den Kindprozess, auf dessen Ende er gewartet hat.

## Gemeinsame Sockets machen lokale Prozessgrenzen zu Zugriffsgrenzen

Ein Control-Socket ist ein lokaler Unix-Domain-Socket. Sein Speicherort und seine Berechtigungen im Dateisystem bestimmen, welche lokalen Prozesse versuchen können, mit dem Master zu kommunizieren. Deshalb kann ein weit gefasster `ControlPath` unabhängige Arbeiten miteinander verbinden, selbst wenn die Aliase der entfernten Hosts ordentlich aussehen.

Betrachte ein CI-Runner-Konto mit dieser Konfiguration:

```sshconfig
Host *
    ControlMaster auto
    ControlPath /tmp/ssh-%r@%h:%p
    ControlPersist 1h
```

Job A verbindet sich als `deploy` mit `app.internal`. Er erstellt einen Master und einen Socket in `/tmp`. Danach endet Job A. Job B läuft unter demselben lokalen Konto und verbindet sich mit demselben Ziel. Wenn Job B den Socket benennen und darauf zugreifen kann, kann er den von Job A authentifizierten Transport wiederverwenden.

Das ist beliebt, weil wiederholte Befehle dadurch ohne nennenswerte Änderungen an der Anwendung schneller werden. Für unabhängige Jobs ist es falsch, weil die Wiederverwendungsgrenze auf Host, Port und Benutzer basiert, nicht auf der Aufgabe, der die Berechtigung gehört. Ein gemeinsames Runner-Konto macht diesen Fehler zu einem regelmäßigen aufgabenübergreifenden Zugriff.

Das Verzeichnis ist genauso wichtig wie die Socket-Datei. Auf Unix-Systemen benötigt ein Prozess Suchberechtigung für Verzeichnisse, um einen Pfad erreichen zu können. Ein Socket in einem Verzeichnis mit privatem Besitzer und dem Modus 0700 schafft eine deutlich klarere Grenze als ein gemeinsam genutztes temporäres Verzeichnis. Die Berechtigungen der Socket-Datei selbst bleiben wichtig, aber sie sind nicht die gesamte Kontrolle.

Verwende ein für die Aufgabe erstelltes Verzeichnis, das dem ausführenden Konto gehört. Ein Shell-Wrapper kann das erledigen, ohne sich auf eine globale SSH-Konfiguration zu stützen:

```sh
set -eu
run_id="release-4821"
cm_dir="$HOME/.ssh/task-control/$run_id"
mkdir -p "$cm_dir"
chmod 700 "$cm_dir"

socket="$cm_dir/%C"
ssh -o ControlMaster=auto \
    -o ControlPersist=5m \
    -o ControlPath="$socket" \
    build-box 'id && hostname'
```

Das `%C`-Token vermeidet einen langen ausgeschriebenen Namen und verringert Kollisionen zwischen unterschiedlichen Verbindungsparametern. Das OpenSSH-Handbuch zu `ssh_config` definiert es als Hash über Verbindungsdetails. Es enthält jedoch weder dein Deployment-Ticket noch deine Agentensitzung oder Aufgaben-ID. Das übergeordnete Verzeichnis liefert in diesem Beispiel die fehlende Grenze.

Lege einen Control-Socket nicht in einem ausgecheckten Repository, einem weitgehend beschreibbaren Arbeitsbereich oder einfach aus Bequemlichkeit in `/tmp` ab. Bequemlichkeit macht aus einem lokalen Socket eine versehentlich gemeinsam nutzbare Fähigkeit.

## ControlPersist ist eine Aufbewahrungsrichtlinie, kein Bereinigungsplan

`ControlPersist` weist SSH an, einen Master verfügbar zu halten, nachdem Client-Sitzungen geschlossen wurden. Die Option akzeptiert `yes`, wodurch der Master unbegrenzt im Hintergrund läuft, oder einen Zeitwert wie `10m`. Beides sind Entscheidungen über die Aufbewahrungsdauer.

Ein Timeout hilft, wenn ein Client vor der Bereinigung abstürzt. Es beweist nicht, dass die Aufgabe gleichzeitig mit dem Zugriff endete. Während des Timeouts kann ein Prozess mit Zugriff auf den Socket neue Arbeit anfordern. Eine Stunde ist besonders schwer zu rechtfertigen, wenn eine Deployment-Aufgabe zwei Minuten dauerte.

Es gibt Situationen, in denen ein kurzer Timeout sinnvoll ist. Ein kontrollierter Automatisierungsprozess kann mehrere Befehle nacheinander ausführen und von einer bestehenden Verbindung profitieren. In diesem Fall sollte der Timeout kürzer als die erwartete Leerlaufzeit sein. Isoliere den Socket auf diesen Lauf und beende ihn nach erfolgreichem Abschluss. Der Timeout dient dann als Rückfallebene bei Fehlern und nicht als gewöhnlicher Schließmechanismus.

Die Einstellung `ControlPersist yes` braucht einen Verantwortlichen mit einem langfristigen Grund für den Zugriff. Ein interaktiver Arbeitsplatz eines Administrators kann diesen Grund haben. Eine kurzlebige Aufgabe nicht.

Ein weiterer häufiger Fehler ist die Annahme, dass ein Befehlsfehler den Master schließt. Eine Shell kann nach einem fehlgeschlagenen entfernten Befehl frühzeitig zurückkehren, während der Hintergrund-Master weiterläuft. Eine Abbruchanforderung der Aufgabe kann dasselbe bewirken. Die Bereinigung muss nach Erfolg, Fehler und Unterbrechung laufen und festhalten, ob das Herunterfahren funktioniert hat.

Wenn deine Automatisierung einen Trap verwendet, halte die Bereinigung eng begrenzt und prüfe ihr Ziel. Entferne nicht blind zuerst den Socket-Pfad. Das Entfernen eines Pfads kann die Untersuchung erschweren, während Masterprozess und Verbindung weiter bestehen. 

```sh
cleanup() {
    ssh -S "$socket" -O exit build-box >/dev/null 2>&1 || true
    rmdir "$cm_dir" 2>/dev/null || true
}
trap cleanup EXIT HUP INT TERM
```

Dieses Muster fordert den Master auf, sich zu beenden, bevor es versucht, das Verzeichnis zu entfernen. Wenn `ssh -O exit` fehlschlägt, bewahre das Verzeichnis auf und untersuche den Fehler, statt die Belege zu löschen. Schreibe in produktivem Code den Fehler, die Prozess-ID und den Socket-Pfad in den Aufgaben-Datensatz.

## `exit` und `stop` haben unterschiedliche operative Bedeutungen

OpenSSH stellt Steuerbefehle über `ssh -O` bereit. Betreiber verwenden regelmäßig den falschen Befehl, weil beide Namen nach Bereinigung klingen.

`ssh -O check host` fragt, ob ein Master läuft, und meldet seine Prozess-ID, wenn einer antwortet. `ssh -O exit host` fordert den Master zum Beenden auf. Für eine abgeschlossene Aufgabe ist `exit` normalerweise der gewünschte Befehl, weil er den wiederverwendbaren Transport beendet.

`ssh -O stop host` weist einen Master an, keine neuen multiplexierten Sitzungen mehr anzunehmen. Bestehende Sitzungen laufen weiter. Das kann nützlich sein, wenn ein Betreiber aktive Arbeit auslaufen lassen muss. Es erfüllt jedoch nicht die Aussage, dass der gesamte SSH-Zugriff mit Abschluss der Aufgabe endete. Ein laufender Master mit aktiven Kanälen hat weiterhin eine aktive Netzwerkverbindung.

Verwende den Socket-Pfad ausdrücklich, wenn die Diagnose nicht von der aktuellen Konfiguration des Benutzers abhängen soll:

```sh
ssh -S "$socket" -O check build-box
# Master running (pid=41782)

ssh -S "$socket" -O exit build-box
# Exit request sent.

ssh -S "$socket" -O check build-box
# Control socket connect(...): No such file or directory
```

Die Wörter und der genaue Fehlertext unterscheiden sich je nach Plattform und OpenSSH-Version. Erfasse deshalb sowohl die Standardausgabe als auch die Standardfehlerausgabe, statt einen einzelnen Satz als feste Schnittstelle zu behandeln. Die Form der Belege ist entscheidend: Eine erfolgreiche Prüfung weist einen aktiven Master nach, ein erfolgreiches `exit` sendet die Beendigungsanforderung, und eine spätere fehlgeschlagene Prüfung stützt die Aussage, dass kein Control-Socket geantwortet hat.

Es gibt einen schwierigen Sonderfall. Nach dem Absturz eines Masters kann der Socket bestehen bleiben, und eine PID kann zwischen zwei Prüfungen verschwinden. Ein veralteter Pfad beweist nicht, dass der Zugriff noch besteht. Umgekehrt beweist eine nicht antwortende Prüfung nicht, dass die entfernte Verbindung beendet wurde, wenn du den Pfad vor der Untersuchung entfernt hast. Prüfe Prozesstabelle und offene Unix-Sockets, bevor du etwas löschst.

Unter macOS ist `lsof` oft das schnellste lokale Prüfwerkzeug:

```sh
lsof -nP -U | grep '/.ssh/task-control/'
ps -p 41782 -o pid=,ppid=,lstart=,etime=,command=
```

Speichere die Ausgabe im Aufgabendatensatz. Der erste Befehl ordnet einen Unix-Socket einem Prozess zu. Der zweite liefert Elternprozess, Startzeit, verstrichene Zeit und Aufrufdetails. Betrachte `grep` als interaktives Hilfsmittel, nicht als Auditkontrolle. Ein echter Collector sollte die relevanten Datensätze direkt abfragen und speichern.

## Portweiterleitungen machen einen untätigen Master folgenreicher

Ein scheinbar untätiger Master kann weiterhin Weiterleitungsstatus halten oder eine spätere Weiterleitungsanfrage akzeptieren. Deshalb reicht es nicht aus, nur Shell-Befehle zu zählen.

Eine lokale Weiterleitung stellt einen lokalen Listener bereit, der Datenverkehr über die SSH-Verbindung sendet. Eine entfernte Weiterleitung weist den Server an, zu lauschen und Verbindungen über den Client zurückzusenden. Eine dynamische Weiterleitung erstellt einen SOCKS-Proxy. Je nachdem, wie Sitzung und Master gestartet wurden, können alle drei die Aufgabe überdauern, die sie eingerichtet hat.

Die OpenSSH-Handbücher dokumentieren Steuerbefehle wie `forward` und `cancel` für Weiterleitungsanfragen, wenn Multiplexing aktiv ist. Das ist operativ nützlich. Es bedeutet aber auch, dass ein Prozess, der den Socket erreicht, neben einem gewöhnlichen Shell-Befehl weitere Netzwerkpfade anfordern kann, abhängig von Serverrichtlinien und dem Zustand des Masters.

Behandle Weiterleitungen bei automatisierten Aufgaben als ausdrückliche Ausnahme. Erfasse Bind-Adresse, lokalen oder entfernten Port, Zielhost und Zielport sowie das Ergebnis der Bereinigung. Lass einen allgemeinen SSH-Wrapper nicht stillschweigend `LocalForward`, `RemoteForward` oder `DynamicForward` aus einer weit gefassten `Host *`-Konfiguration des Benutzers übernehmen.

Prüfe die aufgelösten Einstellungen, bevor du einem Alias vertraust:

```sh
ssh -G build-box | grep -E '^(controlmaster|controlpath|controlpersist|localforward|remoteforward|dynamicforward) '
```

`ssh -G` gibt die effektive Konfiguration aus, nachdem OpenSSH Host-Matching und Standardeinstellungen angewendet hat. Diese Prüfung findet einen überraschend häufigen Fehler: Eine Aufgabe verwendet einen einfachen Host-Alias, aber eine eingebundene Konfigurationsdatei aktiviert weit entfernt von der eigentlichen Aufgabenkonfiguration Multiplexing oder Weiterleitungen. Je nach Version kann der Befehl weitere Felder anzeigen. Speichere die vollständige Ausgabe von `ssh -G`, nicht nur die erwarteten Zeilen.

Ein entferntes System kann eine einzige Quellverbindung protokollieren, während mehrere weitergeleitete Anwendungsverbindungen darüber laufen. Netzwerk-Telemetrie, SSH-Serverprotokolle und Aufgabenprotokolle beantworten jeweils unterschiedliche Teile dieses Bildes. Keines kann die anderen ersetzen.

## Serverprotokolle allein können die lokale Entscheidungskette nicht rekonstruieren

Der entfernte SSH-Server sieht den Transport und alles, was seine Protokollierung zu Kanälen und Befehlen festhält. Er kann jedoch nicht zuverlässig erkennen, warum ein lokaler Prozess die Berechtigung erhielt, einen Control-Socket zu verwenden, welcher Aufgabe das Socket-Verzeichnis gehörte oder ob ein anderer lokaler Prozess ihn nach dem Ende der ursprünglichen Aufgabe wiederverwendete.

Das ist kein Vorwurf an Serverprotokolle. Sie arbeiten an einer anderen Stelle des Systems. `sshd` kann Authentifizierungs- und Verbindungsereignisse aufzeichnen. Ein Wrapper für erzwungene Befehle oder ein Auditsystem kann entfernte Befehle erfassen. Diese Aufzeichnungen bleiben nützlich. Standardmäßig identifizieren sie jedoch nicht jeden lokalen Multiplex-Client, weil der Server alle Kanäle als Teil desselben bereits authentifizierten Transports sehen kann.

Bewahre Belege auf beiden Seiten der Grenze auf. Ein nützlicher Aufgabendatensatz enthält:

- die vollständig aufgelöste Clientkonfiguration aus `ssh -G`, ohne Geheimnisse;
- den entfernten Hostnamen, die Adresse, das Konto, das Ergebnis der Host-Fingerabdruckprüfung und die anfängliche PID des Masters;
- den Pfad des Control-Sockets, das private übergeordnete Verzeichnis sowie die beobachteten Erstellungs- und Endzeiten;
- jeden angeforderten entfernten Befehl, jede Übertragung und jede Weiterleitungsaktion mit ihrem Exit-Status;
- das Ergebnis von `check` vor der Bereinigung, das Ergebnis von `exit` sowie die Prüfung von Prozess und Socket nach der Bereinigung.

Füge eine Aufgabenkennung zum Verzeichnisnamen und zum Protokolldatensatz hinzu, nicht zu einem globalen Control-Socket-Pfad, den jede Aufgabe gemeinsam verwendet. Die Kennung verbindet lokale Ereignisse, ist aber nicht allein eine Sicherheitsgrenze.

Das Hashen oder Signieren des Datensatzes nach der Erfassung hilft, spätere Änderungen zu erkennen. Fehlende Ereignisse lassen sich dadurch jedoch nicht reparieren. Erfasse Lebenszyklusereignisse während ihres Auftretens. Ein nach einem Vorfall aus der Shell-Historie zusammengesetzter Datensatz ist schwacher Beleg, besonders wenn Hintergrund-Master und Wiederholungsversuche beteiligt sind.

Bei agentengesteuerter Arbeit muss zwischen der Absicht des Agenten und der ausgeführten SSH-Aktion unterschieden werden. «Version X bereitstellen» ist eine Absicht. `ssh build-box 'sudo systemctl restart api'` ist eine Aktion. Der Datensatz zur Socket-Wiederverwendung zeigt, ob diese Aktion einen neuen Transport erhielt oder über einen bestehenden lief. Das sind unterschiedliche Audit-Fakten.

## Aufgabenspezifische Sockets geben der Bereinigung einen Verantwortlichen

Das sicherste Muster für kurze automatisierte Arbeiten ist einfach: Verwende für jede Aufgabe ein eigenes Control-Socket-Verzeichnis, erlaube die Wiederverwendung nur innerhalb dieser Aufgabe und sende vor der Meldung des Abschlusses eine ausdrückliche `exit`-Anfrage.

Ein praktischer Wrapper braucht einen klaren Lebenszyklus. Er sollte das Verzeichnis mit restriktiven Berechtigungen erstellen, vor der ersten Verbindung einen Datensatz schreiben, Befehle mit demselben ausdrücklich gesetzten `ControlPath` ausführen, den Master beenden, das Ergebnis prüfen und erst dann das leere Verzeichnis entfernen. Eine Aufgabe, die die Bereinigung nicht überprüfen kann, sollte einen Bereinigungsfehler melden, selbst wenn der entfernte Befehl erfolgreich war.

Dieses Beispiel verwendet `mktemp`, um nicht selbst einen eindeutigen Verzeichnisnamen erraten zu müssen:

```sh
set -eu
base="$HOME/.ssh/task-control"
mkdir -p "$base"
chmod 700 "$base"
cm_dir=$(mktemp -d "$base/run.XXXXXX")
chmod 700 "$cm_dir"
socket="$cm_dir/%C"

finish() {
    status=$?
    ssh -S "$socket" -O exit build-box >>"$cm_dir/cleanup.log" 2>&1 || \
        printf '%s\n' 'master exit request failed' >>"$cm_dir/cleanup.log"
    ssh -S "$socket" -O check build-box >>"$cm_dir/cleanup.log" 2>&1 || true
    exit "$status"
}
trap finish EXIT HUP INT TERM

ssh -o ControlMaster=auto \
    -o ControlPersist=2m \
    -o ControlPath="$socket" \
    build-box 'deployctl apply release-4821'
```

Die kurze Einstellung `ControlPersist` deckt einen Prozess ab, der stirbt, bevor sein Trap ausgeführt wird. Sie sollte nicht als Erlaubnis verstanden werden, dass eine andere Aufgabe den Master wiederverwenden darf. Das zufällige Verzeichnis verhindert diese Wiederverwendung, weil die zweite Aufgabe den Pfad der ersten weder kennt noch übernimmt.

Vor der Übernahme dieses genauen Beispiels ist eine Korrektur nötig: Schreibe keine sensiblen Befehlsargumente, Umgebungswerte oder kopierten privaten Daten in `cleanup.log`. Auditdatensätze müssen genug Details enthalten, um festzustellen, wer was getan hat. Sie dürfen jedoch kein neuer Geheimnisspeicher werden. Maskiere Argumente an der Grenze des Wrappers, solange du ihre Bedeutung noch kennst.

Sallyport leitet SSH-Aktionen über seinen gebündelten zustandslosen Helfer `sp-ssh` weiter und bewahrt SSH-Schlüssel in seinem verschlüsselten Tresor auf, statt sie dem Agenten zugänglich zu machen. Dadurch entfällt ein häufiger Fehler bei der Verwaltung von Zugangsdaten. Teams sollten dennoch die Aktionsgrenze definieren und Datensätze aufbewahren, aus denen hervorgeht, wann der Lauf beendet wurde.

## Globale Komforteinstellungen unterlaufen Aufgabengrenzen

Ein globaler `Host *`-Abschnitt aktiviert Multiplexing oft für jede interaktive Shell, jedes Skript, Repository und jeden untergeordneten Automatisierungsprozess eines Kontos. Das ist zu weit gefasst, wenn dasselbe Konto Aufgaben mit unterschiedlichen Freigaben oder Verantwortlichen ausführt.

Die Einstellung kann aus `Include`-Dateien, Konfigurationsmanagement, persönlichen Dotfiles eines Entwicklers oder einem Build-Image stammen. Auch die Befehlszeile kann sie überschreiben. Leite das aktive Verhalten nie aus einer einzigen sichtbaren Konfigurationsdatei ab. Verwende `ssh -G` mit dem genauen Host-Alias und dem Benutzerkontext, den die Aufgabe verwendet.

Wenn eine Automatisierungsumgebung keinen privaten ControlPath garantieren kann, deaktiviere Multiplexing für diese Aktion:

```sh
ssh -o ControlMaster=no \
    -o ControlPath=none \
    build-box 'maintenancectl status'
```

Das kostet für jeden Aufruf eine neue Verbindung und Authentifizierung. Für Aktionen mit hohen Folgen, seltenen Notfallzugriff oder Arbeit, die Aufgaben- und Vertrauensgrenzen überschreitet, ist das ein guter Preis. Die wiederholte Authentifizierung liefert ein klareres Autorisierungsereignis und macht die Betrachtung des Lebenszyklus deutlich eindeutiger.

Verwechsle `ControlMaster=auto` nicht mit einer Garantie, dass ein Prozess nur seine eigene Verbindung wiederverwendet. `auto` bedeutet, dass der Client versucht, einen Master am konfigurierten Pfad zu finden, und einen neuen erstellt, wenn er keinen findet. Der konfigurierte Pfad entscheidet, wessen Verbindung er finden kann.

Manche Teams argumentieren, ein gemeinsamer Socket sei unproblematisch, weil alle Jobs unter einem Dienstkonto laufen. Das stimmt nur, wenn jeder Job mit dieser Unix-Identität dieselbe Berechtigung hat, über dieses Konto auf jedes Ziel zuzugreifen, und wenn das Team akzeptiert, dass ein Job den aktiven Transport eines anderen erben kann. In den meisten ausgereiften Umgebungen ist das tatsächlich nicht gewünscht.

## Ein sauberes Aufgabenende braucht einen Nachweis über das Schließen des Transports

Erkläre eine SSH-Aufgabe nicht für abgeschlossen, sobald der letzte entfernte Befehl zurückkehrt. Erkläre sie erst dann für abgeschlossen, wenn die Aufgabe ihren Master geschlossen und dies nachweisbar aufgezeichnet hat oder gemeldet hat, dass dies nicht möglich war.

Der abschließende Datensatz sollte einem Ermittler zeigen, ob ein späterer Befehl die Verbindung noch hätte wiederverwenden können. Er braucht eine Aufgabenidentität, die aufgelöste SSH-Konfiguration, den Socket-Pfad, Details zum Masterprozess, Aktivitätsaufzeichnungen, die Ergebnisse ausdrücklicher Steuerbefehle und die Beobachtung nach der Bereinigung. Außerdem braucht er Belege vom entfernten System für Änderungen, die unabhängig von SSH weiterliefen, etwa einen Neustart eines Dienstes oder einen abgelösten Prozess.

Lege einen Standard fest, den Betreiber auch unter Druck ausführen können: Socket isolieren, vor der Bereinigung prüfen, `exit` anfordern, bestätigen, dass kein Master antwortet, und das Ergebnis aufbewahren. Dieses Verfahren ist nützlicher als ein langes Standard-Timeout, weil es eine Annahme über den Zugriff in eine überprüfbare Tatsache verwandelt.

Wenn die Arbeit so sensibel ist, dass ein verbliebener authentifizierter Transport nicht akzeptabel wäre, optimiere sie nicht mit einem gemeinsam genutzten Master. Öffne eine neue Verbindung, führe die Aktion aus, schließe sie und bewahre den Datensatz auf. Die zusätzlichen Sekunden sind günstiger, als erklären zu müssen, warum ein Zugriffspfad nach dem vermeintlichen Ende der Aufgabe weiterbestand.
