# SSH-Konfiguration: Match-Blöcke und Agent-Genehmigungen

Eine SSH-Genehmigung ist nur dann aussagekräftig, wenn die genehmigte Verbindung der Verbindung entspricht, die SSH tatsächlich herstellt. Das klingt selbstverständlich, bis ein Agent `ssh prod` ausführt, ein Host-Alias einen anderen `HostName` auswählt, ein `Match`-Block den Remote-Benutzer ändert und `ProxyJump` die Sitzung über einen Bastion-Host leitet, den niemand in der Anfrage erwähnt hat.

Die meisten Fehler in SSH-Konfigurationen lassen sich überstehen, wenn jemand ein Terminal beobachtet. Die Person sieht eine unerwartete Host-Key-Abfrage, bemerkt `root@...` oder weiß noch, dass `prod` im Büronetz eine andere Bedeutung hat. Ein autonomer Agent bringt dieses Misstrauen nicht mit. Er verwendet den vorgegebenen Alias und folgt der Konfiguration genau.

Deshalb verdienen SSH-Konfigurationsblöcke mit `Match` eine Sicherheitsprüfung, bevor ein Agent SSH-Zugriff erhält. Es geht nicht darum, Aliase, Bastion-Hosts oder bedingte Konfiguration zu verbieten. Die angeforderte Aktion, die effektive SSH-Konfiguration und die von einem Menschen genehmigte Verbindung sollen dasselbe beschreiben.

## Ein Host-Alias ist eine Eingabe, keine Identität

`ssh app-prod` sagt nicht, wohin SSH verbindet, welches Konto es verwendet, welchen Schlüssel es anbietet oder ob der Datenverkehr zuerst über eine andere Maschine läuft. Der Befehl gibt OpenSSH lediglich an, mit welchem Konfigurationsargument es beginnen soll.

Diese Unterscheidung wird leicht übersehen, weil Aliase die Arbeit auf der Befehlszeile angenehmer machen. Ein kurzer Name wie `app-prod` lässt sich leichter eingeben als ein vollständiger Hostname mit bestimmtem Benutzer und einem nicht standardmäßigen Port. Auch einem Agenten lässt er sich leichter übergeben. Der Alias ist jedoch nur ein Griff. Seine Bedeutung ergibt sich aus allen passenden Einstellungen in den Konfigurationsdateien, die SSH liest.

OpenSSH liest zuerst Optionen auf der Befehlszeile, danach die Konfiguration des Benutzers in `~/.ssh/config` und anschließend die systemweite Konfiguration. Bei den meisten Optionen mit nur einem Wert wird der erste von SSH gefundene Wert verwendet. Das OpenSSH-Handbuch `ssh_config(5)` macht die praktische Konsequenz klar: Spezifische Angaben gehören nach oben, allgemeine Standards weiter nach unten. Ein weit gefasster `Host *`-Block am Anfang kann eine spätere, sorgfältigere bedingte Regel unbemerkt außer Kraft setzen.

Beginne mit einem Inventar, in dem jeder Alias, den ein Agent verwenden darf, so beschrieben wird, dass ein Prüfer ihn kontrollieren kann:

| Alias | Aufgelöstes Ziel | Remote-Benutzer | Route | Zweck der Identität |
|---|---|---|---|---|
| `staging-api` | `api-01.staging.example.net` | `deploy` | direkt | Staging-Deployment-Identität |
| `prod-api` | `api-01.prod.example.net` | `deploy` | `prod-bastion` | Produktions-Deployment-Identität |
| `prod-breakfix` | `api-01.prod.example.net` | `ops` | `prod-bastion` | Nur für Störungen vorgesehene Identität |

Schreibe in die Zielspalte nicht `production`. Trage das tatsächliche Ziel ein, das SSH verwendet. Schreibe nicht `default user`, sondern `deploy`, `ubuntu`, `ec2-user` oder das Konto, das der Server tatsächlich erhält. Wenn die Route einen Jump-Host enthält, benenne ihn. Verhält sich der Alias in verschiedenen Netzwerken unterschiedlich, braucht er eine eigene Zeile, denn dann handelt es sich um eine andere effektive Verbindung.

Die wichtige Unterscheidung lautet: **Ein Alias bezeichnet einen Konfigurationseintrag, ein Ziel bezeichnet den Remote-Endpunkt.** Wer beides vermischt, genehmigt leicht das Falsche. Ein Prüfer kann einer Sitzung zu `staging-api` zustimmen und trotzdem beim tatsächlichen Endpunkt irren, weil der Alias veraltetes oder bedingtes Verhalten enthält.

Aliase sollten außerdem einen betrieblichen Zweck beschreiben und ihn nicht verbergen. `prod-readonly`, `prod-deploy` und `prod-breakfix` lenken die Aufmerksamkeit an die richtige Stelle. Ein einzelner Alias namens `prod`, der Benutzer, Schlüssel und Routen über bedingte Blöcke auswählt, spart ein paar Tastenanschläge und schafft ein dauerhaftes Prüfproblem.

## Match-Blöcke sind ausführbare Verbindungslogik

Ein `Match`-Block ist kein Etikett für eine Hostgruppe. Er ist ein bedingter Abschnitt in `ssh_config`, der bestimmt, welche Direktiven gelten. Die Bedingungen können den angeforderten Host, den ursprünglichen Host, den Remote-Benutzer, den lokalen Benutzer, den Kanonisierungsstatus, das lokale Netzwerk, einen angeforderten Befehl und einen `exec`-Befehl umfassen, den SSH über die lokale Shell ausführt.

Diese Möglichkeiten sind nützlich. Sie bedeuten aber auch, dass eine Konfiguration Verhalten enthalten kann, das unsichtbar bleibt, wenn man nur den unmittelbar benachbarten `Host`-Alias betrachtet.

Betrachte diese Konfiguration:

```sshconfig
Host prod-api
    HostName api-01.prod.example.net
    User deploy
    ProxyJump prod-bastion

Match originalhost prod-api user root
    IdentityFile ~/.ssh/breakfix_ed25519
    IdentitiesOnly yes
```

Wer nur den Block `Host prod-api` liest, sieht eine Deployment-Verbindung als `deploy`. Führt ein Aufrufer `ssh -l root prod-api` aus, kann die Bedingung `Match originalhost prod-api user root` greifen. Ob die Einstellung für die Identität wirksam wird, hängt außerdem davon ab, wo frühere Identitätseinstellungen gefunden wurden und ob die Option mehrere Werte unterstützt. Der entscheidende Punkt ist einfacher: Die Verbindung hat sich wegen des Benutzerarguments auf der Befehlszeile geändert, nicht weil der Alias geändert wurde.

Vermeide bei Agenten `Match user`-Regeln, die eine privilegiertere Identität oder Route gewähren. Die Regel wirkt übersichtlich, weil sie Verhalten nach Kontonamen gruppiert. Ein aufrufendes Tool kann sie jedoch leicht mit `-l`, `user@host` oder einem generierten Befehl verändern. Setze den vorgesehenen `User` stattdessen direkt in einen Alias mit eindeutigem Zweck.

Eine sicherere Variante macht jede Absicht ausdrücklich:

```sshconfig
Host prod-deploy
    HostName api-01.prod.example.net
    User deploy
    ProxyJump prod-bastion
    IdentityFile ~/.ssh/prod_deploy_ed25519
    IdentitiesOnly yes

Host prod-breakfix
    HostName api-01.prod.example.net
    User ops
    ProxyJump prod-bastion
    IdentityFile ~/.ssh/prod_breakfix_ed25519
    IdentitiesOnly yes
```

Das macht privilegierten Zugriff nicht harmlos. Es macht die angeforderte Verbindung lesbar. Ein Agent braucht eine eigene Genehmigung, um `prod-breakfix` aufzurufen. Er kann nicht allein durch das Ändern eines Benutzernamens in dieses Verhalten geraten.

`Match exec` verdient in einer Konfiguration für Agenten noch weniger Vertrauen. SSH führt den lokalen Shell-Befehl aus, während es die Konfiguration auswertet. Teams nutzen dies für die Erkennung des Netzwerks, Inventarabfragen oder die Auswahl von Zugangsdaten. Damit wird ein Verbindungsversuch zu einem lokalen Codepfad mit Abhängigkeiten von der Umgebung. Wenn du diese Flexibilität für menschliche Arbeit brauchst, halte solche Aliase aus der Menge heraus, die ein Agent aufrufen darf. Eine Verbindungsprüfung sollte nicht das Rückwärtslesen eines beliebigen Shell-Befehls erfordern.

## Der erste passende Wert kann deine Ausnahme verhindern

Der hartnäckigste Fehler in einer SSH-Konfiguration ist keine ungültige Anweisung. Es ist eine gültige Anweisung, die hinter einer allgemeineren Regel steht, die den Wert bereits gesetzt hat.

Angenommen, ein Entwickler schreibt Folgendes, um für die Produktion einen Bastion-Host zu erzwingen:

```sshconfig
Host *
    User deploy
    ProxyJump dev-bastion

Host prod-*
    ProxyJump prod-bastion
```

Die Erwartung ist verständlich: `prod-*` sieht spezifischer aus und sollte daher gewinnen. OpenSSH sortiert Blöcke jedoch nicht nach ihrer Spezifität. Es verarbeitet sie in Dateireihenfolge, und bei vielen Direktiven gewinnt der zuerst gefundene Wert. `prod-api` behält `dev-bastion`, weil der frühere `Host *` den Wert für `ProxyJump` bereits geliefert hat.

Setze spezifische Blöcke nach oben:

```sshconfig
Host prod-*
    ProxyJump prod-bastion

Host *
    User deploy
    ServerAliveInterval 30
```

Das ist nicht bloß Stil. Eine Route über den falschen Bastion-Host kann die Sitzung auf den falschen Netzwerkpfad setzen. Ein allgemeiner Standard wie `User deploy` kann dazu führen, dass sich ein Produktions-Alias als Konto anmeldet, das auf diesem Host nichts zu suchen hat. Ein allgemeines `IdentityFile` kann unerwartete Zugangsdaten vor dem vorgesehenen Schlüssel anbieten.

Übertrage die Regel zum ersten Wert nicht blind auf alle Direktiven. Manche Optionen akzeptieren absichtlich mehrere Werte. `IdentityFile` ist ein häufiges Beispiel. Mehrere konfigurierte Identitäten können in die Menge aufgenommen werden, die SSH prüft. Daraus entsteht ein anderes Problem: Ein enger Alias kann zwar die richtige Identität nennen, aber durch frühere Konfiguration oder den lokalen SSH-Agenten weitere Identitäten verfügbar lassen.

Für automatisierte Verbindungen sollte die Identitätsauswahl ausdrücklich und langweilig sein:

```sshconfig
Host prod-deploy
    HostName api-01.prod.example.net
    User deploy
    IdentityFile ~/.ssh/prod_deploy_ed25519
    IdentitiesOnly yes
    ProxyJump prod-bastion
```

`IdentitiesOnly yes` weist OpenSSH an, nur Identitäten aus der SSH-Konfiguration oder von der Befehlszeile zu verwenden, statt frei jede über einen Agenten verfügbare Identität auszuprobieren. Eine schlampige Konfiguration wird dadurch nicht repariert. Ein fremder, lokal geladener Schlüssel wird jedoch nicht mehr versehentlich zu einem möglichen Kandidaten.

Die Empfehlung, alle Standards in `Host *` zu setzen und nur bei Bedarf zu überschreiben, ist für harmlose Einstellungen wie Keepalive-Intervalle vertretbar. Für Benutzer, Routen, Identitätsdateien, Ports, `ProxyCommand` und Host-Umschreibungen ist sie schlechte Praxis. Standards, die Berechtigungen beeinflussen, sollten sparsam sein. Ein wenig Wiederholung ist günstiger, als erklären zu müssen, warum ein Agent den richtigen Rechner über den falschen Weg erreicht hat.

## ProxyJump erzeugt eine weitere Verbindung, die geprüft werden muss

`ProxyJump` ergänzt eine Zielverbindung nicht einfach um eine Eigenschaft. SSH verbindet sich zuerst mit dem Jump-Host und stellt anschließend von dort einen TCP-Weiterleitungspfad zum Ziel her. Mehrere Proxys können angegeben und nacheinander durchlaufen werden. Das OpenSSH-Handbuch weist außerdem darauf hin, dass die Konfiguration des Zielhosts im Allgemeinen nicht für Jump-Hosts gilt.

Gerade dieses Detail führt häufig zu fehlerhaften Prüfungen. Eine Konfiguration kann `prod-api` präzise beschreiben und `prod-bastion` völlig unklar lassen.

```sshconfig
Host prod-api
    HostName 10.40.8.17
    User deploy
    ProxyJump prod-bastion

Host prod-bastion
    HostName bastion.prod.example.net
    User jump
    IdentityFile ~/.ssh/bastion_ed25519
    IdentitiesOnly yes
```

Hier gibt es zwei Authentifizierungsentscheidungen und zwei Host-Identitäten:

1. SSH authentifiziert den lokalen Client bei `bastion.prod.example.net` als `jump`.
2. Der Bastion-Host leitet einen TCP-Datenstrom an `10.40.8.17` weiter.
3. SSH authentifiziert sich über diesen Datenstrom beim Ziel als `deploy`.

Der Jump-Host kann einen anderen Schlüssel, Benutzer, Port und Host-Key-Eintrag haben. Er kann auch durch einen Wildcard-Alias oder einen bedingten Block ausgewählt werden, den niemand prüft, weil der Agent nur `prod-api` angefordert hat.

Prüfe jeden Hop mit `ssh -G`, nicht nur den endgültigen Alias:

```sh
ssh -G prod-api | egrep '^(hostname|user|port|proxyjump|identityfile|identitiesonly) '
ssh -G prod-bastion | egrep '^(hostname|user|port|identityfile|identitiesonly) '
```

Die Ausgabe enthält eine Option pro Zeile. Eine plausible Prüfung könnte so aussehen:

```text
hostname api-01.prod.example.net
user deploy
port 22
proxyjump prod-bastion
identityfile ~/.ssh/prod_deploy_ed25519
identitiesonly yes
```

Prüfe den Bastion-Host anschließend separat. Verwendet `prod-api` eine durch Kommas getrennte Kette wie `edge-bastion,prod-bastion`, führe den Befehl für beide Aliase aus. Eine Kette ist keine undurchsichtige Route, sondern mehrere getrennte SSH-Client-Konfigurationen.

Vermeide eine allgemeine Jump-Route in `Host *` oder einem weiten Muster wie `Host *.internal`. Solche Regeln erfassen leicht temporäre Hosts, Staging-Umgebungen und Aliase, die Monate später hinzugefügt werden. Definiere die Jump-Route in den Aliasen, die sie benötigen. Wenn viele Produktions-Aliase sie brauchen, verwende ein enges Muster, das ausschließlich für Produktions-Aliase reserviert ist.

Prüfe außerdem auf `ProxyCommand`. OpenSSH behandelt `ProxyJump` und `ProxyCommand` als konkurrierende Optionen: Die zuerst angegebene verhindert, dass spätere Instanzen der jeweils anderen Option greifen. Eine Konfiguration, die scheinbar einen Bastion-Host verwendet, kann stattdessen einen früheren Proxy-Befehl ausführen. Beide Einstellungen sollten markiert werden, denn beide verändern, wo die Netzwerkverbindung entsteht und wie sie das Ziel erreicht.

## Die Benutzerauswahl verändert die genehmigte Berechtigung

Das Remote-Konto gehört zur angeforderten Aktion. `deploy@api-01.prod.example.net` und `ops@api-01.prod.example.net` können denselben Server erreichen, haben aber möglicherweise unterschiedliche Berechtigungen, Shell-Profile, erzwungene Befehle, `sudo`-Rechte und Audit-Spuren.

SSH kann den Remote-Benutzer aus mehreren Quellen beziehen: aus `user@host` im Befehl, aus `ssh -l user host`, aus einer `User`-Direktive oder, falls nichts anderes vorhanden ist, aus dem lokalen Benutzernamen. Eine Konfigurationsprüfung, die nur fragt «Welcher Host?», ist unvollständig.

Verwende Aliase mit festem Benutzer, wenn ein Agent eine klar definierte Aufgabe hat:

```sshconfig
Host inventory-read
    HostName inventory.prod.example.net
    User inventory_ro
    IdentityFile ~/.ssh/inventory_ro_ed25519
    IdentitiesOnly yes

Host inventory-deploy
    HostName inventory.prod.example.net
    User deploy
    IdentityFile ~/.ssh/inventory_deploy_ed25519
    IdentitiesOnly yes
```

Gib einem Agenten keinen allgemeinen Hostnamen und verlasse dich darauf, dass eine Abfrage oder ein Wrapper das richtige Konto beibehält. Ein Befehlsgenerator kann genauso leicht `ops@inventory.prod.example.net` wie `deploy@inventory.prod.example.net` erzeugen. Die Konfiguration sollte den autorisierten Weg zum einfachsten machen und privilegierte Wege klar sichtbar abgrenzen.

Teste die alternativen Formen, die ein Tool erzeugen kann:

```sh
ssh -G inventory-read | grep '^user '
ssh -G -l ops inventory-read | grep '^user '
ssh -G ops@inventory-read | grep '^user '
```

Ergibt der zweite oder dritte Befehl ein Konto, das ein Agent nicht verwenden sollte, ist die Konfiguration nicht geprüft. Repariere die aufrufende Schnittstelle oder isoliere den Alias. Ein `Match user`-Block kann bei einer dieser Varianten ebenfalls greifen, weshalb er ausdrücklich getestet und nicht nur oberflächlich gelesen werden sollte.

Reserviere `root`-Zugriff für einen separat benannten Notfall-Alias und halte ihn außerhalb der normalen Agent-Berechtigungen. `User root` hinter einer `Match`-Bedingung zu verstecken, ist schlechter, als es offen zu schreiben. Die Bedingung wird im Ernstfall zu einer Suche, und ein Aufrufer kann sie manchmal durch Ändern eines Parameters auf der Befehlszeile erfüllen.

## IdentityFile steuert mehr als den Schlüsselpfad

`IdentityFile` sieht wie eine Einstellung zur Dateiauswahl aus. In der Praxis entscheidet sie, welche Zugangsdaten SSH präsentieren darf. Dadurch wird bestimmt, welche Autorisierungsregeln der Server auf der Gegenseite auswertet.

Ein typischer Fehler sieht so aus:

```sshconfig
Host *
    IdentityFile ~/.ssh/id_ed25519

Host prod-*
    IdentityFile ~/.ssh/prod_ed25519
```

Der Betreiber glaubt, die Produktion verwende `prod_ed25519`. SSH kann jedoch beide Identitätsdateien in seiner Kandidatenliste haben, weil `IdentityFile` mehrere Einträge unterstützt. Befinden sich weitere Schlüssel im SSH-Agenten und fehlt `IdentitiesOnly`, kann SSH auch diese anbieten. Manche Server lehnen wiederholte Angebote früh ab, andere akzeptieren eine unbeabsichtigte Identität mit passenden Zugriffsrechten. Beides beschreibt die Absicht nicht sauber.

Ein Alias für Agenten sollte einen einzigen Zweck für Zugangsdaten nennen und die Angebote begrenzen:

```sshconfig
Host reports-export
    HostName reports.prod.example.net
    User exporter
    IdentityFile ~/.ssh/reports_export_ed25519
    IdentitiesOnly yes
```

Prüfe anschließend die effektive Konfiguration, statt der einzelnen Anweisung zu vertrauen:

```sh
ssh -G reports-export | grep '^identityfile '
ssh -G reports-export | grep '^identitiesonly '
```

Mehr als eine Zeile mit `identityfile` ist nicht automatisch falsch. Zertifikatsbasierte Konfigurationen und geplante Schlüsselrotation können mehrere Einträge rechtfertigen. Jede aufgeführte Identität sollte jedoch zur selben Berechtigungsgrenze gehören. Kann ein Alias einen persönlichen Administratorschlüssel, einen alten Deployment-Schlüssel und einen Produktionsschlüssel für Automatisierung anbieten, fehlt ihm eine klare Autorisierung.

Löse dieses Problem nicht, indem du private Schlüssel in Dateien, Umgebungsvariablen, Abfragen oder generierte Skripte eines Agenten legst. Damit wird eine Konfigurationsunklarheit lediglich in die Offenlegung von Zugangsdaten verwandelt. Sallyport bewahrt SSH-Schlüssel in seinem verschlüsselten Tresor auf und führt SSH-Aktionen über seinen Helper aus. Eine unklare SSH-Konfiguration kann Sallyport jedoch nicht sinnvoll machen. Alias, Route, Benutzer und Zweck der Identität müssen klar sein, bevor ein Betreiber den Agentenlauf genehmigt.

Dasselbe gilt für Schlüsselnamen. Ein Pfad wie `~/.ssh/id_ed25519` sagt nichts über den vorgesehenen Zweck aus. `prod_deploy_ed25519` ist besser, aber die Konfiguration muss zusätzlich erklären, für welche Hostgruppe, welchen Benutzer und welche Route der Schlüssel verwendet wird. Dateinamen unterstützen die Prüfung, ersetzen sie aber nicht.

## Kanonisierung kann dazu führen, dass ein Alias zweimal greift

Die Kanonisierung von Hostnamen gehört zu den unsichtbarsten Möglichkeiten, mit denen SSH eine Konfiguration verändert. Wenn `CanonicalizeHostname yes` aktiviert ist, kann OpenSSH einen unqualifizierten Namen um konfigurierte Domain-Endungen ergänzen, ihn auflösen und die Konfiguration anschließend mit dem neuen Zielnamen erneut verarbeiten. `Match canonical` greift bei diesem späteren Durchlauf. `Match final` fordert eine abschließende Analyse an und greift während dieses Durchlaufs; bei aktivierter Kanonisierung treffen kanonische und abschließende Bedingungen gemeinsam zu.

In großen internen Netzwerken kann dieses Verhalten nützlich sein. Es kann einen kurzen Alias aber auch in eine bedingte Konfigurationsfalle verwandeln.

```sshconfig
CanonicalizeHostname yes
CanonicalDomains corp.example.net

Host build
    User ci

Match canonical host *.prod.example.net
    ProxyJump prod-bastion
```

Ein Aufrufer gibt `ssh build` ein. Im ersten Durchlauf sieht SSH `build`. Löst die Kanonisierung diesen Namen als `build.prod.example.net` auf, liest SSH die Konfiguration erneut ein und der Block `Match canonical host *.prod.example.net` kann eine Produktionsroute setzen. Die Verbindung änderte sich nicht, weil der Aufrufer einen anderen Alias verwendet hat. Sie änderte sich, weil DNS und ein zweiter Analyse-Durchlauf den Host änderten, den spätere Regeln sehen.

Das OpenSSH-Handbuch unterscheidet zwei Bedingungen, die oft gleich behandelt werden:

- `Match originalhost` prüft das Host-Token, wie es der Aufrufer eingegeben hat.
- `Match host` prüft das Ziel nach der Ersetzung durch `HostName` oder nach der Kanonisierung.

Verwende `originalhost`, wenn Verhalten an einen bewusst benannten Alias gebunden sein muss. Verwende `host`, wenn das Verhalten vom tatsächlich aufgelösten Ziel abhängen soll. Für Privilegänderungen sollte keine der beiden Bedingungen unbedacht verwendet werden.

Die Kanonisierung hat bei Bastion-Hosts eine weitere wichtige Besonderheit. `CanonicalizeHostname yes` gilt normalerweise nicht für Verbindungen mit `ProxyCommand` oder `ProxyJump`; `CanonicalizeHostname always` erweitert sie auf weitergeleitete Verbindungen. Daher können zwei strukturell ähnliche Aliase allein aufgrund eines Jump-Hosts unterschiedlichen Umschreibungsregeln folgen.

Für Agent-Berechtigungen ist die einfachste Regel meist die beste: Deaktiviere die Kanonisierung für Aliase, die ein Agent verwenden darf, und setze explizite vollständig qualifizierte `HostName`-Werte. Wenn deine Umgebung Kanonisierung braucht, teste jeden erlaubten Alias genau in dem Netzwerk, in dem der Agent läuft. Gehe nicht davon aus, dass ein kurzer Hostname im Heimnetz, im Unternehmensnetz, im VPN und im Büro-WLAN gleich aufgelöst wird.

`Match localnetwork` wirft dasselbe Problem auf. OpenSSH weist darauf hin, dass die lokale Netzwerkadresse für sicherheitsrelevante Konfiguration nicht vertrauenswürdig ist, besonders in DHCP-konfigurierten Netzwerken. Für Komforteinstellungen ist sie in Ordnung. Verwende sie nicht, um zu entscheiden, ob ein Agent eine privilegiertere Identität erhält, einen Bastion-Host überspringt oder die Produktion erreicht.

## Rendere die Verbindung, bevor du sie genehmigst

`ssh -G` ist der schnellste Weg, eine SSH-Konfiguration aus Prosa in etwas Testbares zu verwandeln. Der Befehl gibt die Konfiguration aus, die SSH nach der Verarbeitung von Host- und Match-Regeln verwenden wird, und beendet sich anschließend, ohne eine Verbindung zu öffnen.

Führe ihn mit genau dem Alias und den Argumenten aus, die der Agent verwenden wird. Teste nicht nur eine von Hand bereinigte Version des Befehls.

```sh
ssh -G prod-deploy | egrep '^(hostname|user|port|proxyjump|proxycommand|identityfile|identitiesonly|canonicalizehostname) '
```

Für eine gründliche Prüfung solltest du die vollständige Ausgabe als Testdatei in dem Repository speichern, dem die Automatisierung gehört. Verwende eine bewusst benannte Konfigurationsdatei, damit der Test nicht stillschweigend die persönlichen Einstellungen eines Entwicklers übernimmt:

```sh
ssh -F ./agent-ssh-config -G prod-deploy > ./testdata/prod-deploy.effective
```

Prüfe die Testdatei, wenn sich die Konfiguration ändert. Ein sinnvoller Diff erkennt eine veränderte `hostname`-, `user`-, `proxyjump`- oder Identitätsliste, bevor sie einen Genehmigungsprozess erreicht. Ein lauter Diff der vollständigen Konfiguration ist immer noch besser, als einem Block zu vertrauen, den jemand in einen Pull Request kopiert hat.

Verwende `ssh -vvv` erst, wenn `ssh -G` die erwarteten Werte zeigt. Ausführliche Verbindungsprotokolle helfen zu bestätigen, welche Hostschlüssel und Authentifizierungsmethoden SSH tatsächlich ausprobiert. Sie vermischen jedoch Konfigurationsentscheidungen mit Netzwerkgeräuschen. `-G` beantwortet zuerst die Frage «Was sagt diese Konfiguration?». Genau das musst du vor der Fehlersuche bei der Erreichbarkeit klären.

Teste Varianten bewusst:

```sh
ssh -F ./agent-ssh-config -G prod-deploy
ssh -F ./agent-ssh-config -G -l ops prod-deploy
ssh -F ./agent-ssh-config -G ops@prod-deploy
ssh -F ./agent-ssh-config -G prod-deploy.prod.example.net
```

Die Ergebnisse sollten innerhalb der erwarteten Berechtigungsgrenze bleiben oder fehlschlagen. Ändert eine Benutzerüberschreibung das Konto, überspringt die vollständig qualifizierte Form den Bastion-Host oder erhält ein kurzer Name nach der Kanonisierung eine andere Identität, hast du einen Konfigurationspfad gefunden, den du schließen solltest.

Prüfe auch eingebundene Dateien. `Include` kann dafür sorgen, dass die sichtbare `~/.ssh/config` nur der Eingang zu einem Verzeichnis voller automatisch erzeugter, unternehmensweiter oder projektspezifischer Regeln ist. Prüfe die effektive Ausgabe mit demselben lokalen Benutzer und demselben Konfigurationspfad, die der Agent verwenden wird. Ein Test aus deiner eigenen Shell, während der Agent unter einem anderen Konto läuft, vermittelt eine falsche Sicherheit.

## Halte die SSH-Konfiguration für Agenten klein und zweckgebunden

Die beste SSH-Konfiguration für einen autonomen Programmieragenten ist normalerweise nicht deine persönliche SSH-Konfiguration mit einigen zusätzlichen Kommentaren. Persönliche Konfigurationen sammeln Abkürzungen, Ausnahmen für einzelne Clients, alte Host-Aliase, Verhalten für lokale Netzwerke, weitergeleitete Agenten und Identitäten, die irgendwann praktisch waren. Ein Agent braucht einen engen Katalog von Verbindungen.

Erstelle eine eigene Konfigurationsdatei, die nur genehmigte Aliase und die erforderlichen Jump-Hosts enthält. Weise den Agenten oder seinen Ausführungs-Wrapper mit `-F` auf diese Datei. Jeder Alias sollte genau eine Aufgabe, einen expliziten `HostName`, einen `User`, eine Route und einen Zweck für die Identität haben. Halte bedingte Logik heraus, sofern du nicht zeigen kannst, warum ein statischer Alias die Aufgabe nicht erfüllen kann.

Ein kompaktes Beispiel:

```sshconfig
Host prod-bastion
    HostName bastion.prod.example.net
    User jump
    IdentityFile ~/.ssh/prod_bastion_ed25519
    IdentitiesOnly yes

Host prod-deploy
    HostName api-01.prod.example.net
    User deploy
    ProxyJump prod-bastion
    IdentityFile ~/.ssh/prod_deploy_ed25519
    IdentitiesOnly yes

Host staging-deploy
    HostName api-01.staging.example.net
    User deploy
    IdentityFile ~/.ssh/staging_deploy_ed25519
    IdentitiesOnly yes
```

Diese Konfiguration wiederholt sich. Das ist gut. Die Datei erklärt einem Prüfer, was jede Verbindung bedeutet, ohne dass er Wildcard-Reihenfolge und bedingten Zustand gedanklich ausführen muss.

Verwechsle eine eigene Konfigurationsdatei nicht mit einer Richtlinien-Engine. Sie kann nicht beweisen, dass ein Befehl nach dem Öffnen der Sitzung sicher ist. Sie kann die Transportverbindung jedoch konkret und prüfbar machen: dieser Alias, dieser Endpunkt, dieser Benutzer, diese Route, diese Identität. Das ist eine sinnvolle Grenze.

Sallyports Genehmigung pro Sitzung und seine Aktivitätsaufzeichnungen geben Betreibern einen menschlichen Kontrollpunkt und eine Spur für Agent-Aktionen. Die SSH-Konfiguration liefert jedoch weiterhin die Fakten hinter der Aktion. Wenn `prod-deploy` in mehrere Netzwerkpfade oder Konten wechseln kann, hat die Konfiguration die Zuverlässigkeit der Genehmigung bereits geschwächt.

Bevor du einem Agenten die Verwendung eines SSH-Alias erlaubst, rendere ihn, prüfe jeden Jump-Host und teste die Varianten auf der Befehlszeile, die der Agent erzeugen kann. Wenn dich die effektive Verbindung auch nur einmal überrascht, gehe davon aus, dass sie jemanden im ungünstigsten Moment überraschen wird. Überarbeite den Alias, bis er wie eine Genehmigung klingt, die ein Mensch tatsächlich erteilen kann.
