Image: terminal logo image from Aegis simple image pack, framing the topic of this post.
In diesem kurzen Artikel geht es um Fehler beim Aufbau einer SSH-Verbindung. Egal ob andere Rechner, Server oder Dienste wie Versionsverwaltung - die Fehlermeldungen und zugehörige Lösungen sind meist gleich. Weiterlesen lohnt sich allerdings nur, wenn gängige Hilfeseiten wie Githubs troubleshooting-ssh nicht die erwünschte Lösung brachte.

Problem: “No Route To Host”#

Die Konsolenausgabe zeigt (im lokalen Netz):

➜  blog git:(article) ✗ ssh 192.168.0.27
ssh: connect to host 192.168.0.27 port 22: No route to host

Oder (in einem entfernten Netz)

➜  blog git:(article) ✗ ssh 1.2.3.4
ssh: connect to host 1.2.3.4 port 22: Network is unreachable

Ursache ist sehr wahrscheinlich eine falsch eingegebene IP oder eine fehlgeschlagene DNS-Namensauflösung. Im lokalen Netz ist ein realistischer Grund, dass der Teilnehmer abgeschaltet ist und der Router ihn daher nicht erreichen kann. Abhilfe schafft meist eine Korrektur der Zieladresse, Einschalten des Ziels oder Update der Routing-Tabelle.

Problem: Connection Refused#

Konsolenausgabe:

➜  blog git:(article) ✗ git push gitea
ssh: connect to host git.schallbert.de port 22: Connection refused
fatal: Could not read from remote repository.

Hier kann nicht mal eine Verbindung zum Ziel hergestellt werden. Man ist also “vor” der Authentisierung schon gescheitert. Meiner Erfahrung nach werden solche Fehler aus drei Gründen erzeugt:

  1. Der Host ist noch nicht bereit. Er bootet beispielsweise gerade (Netzwerkkarte ist an) aber der SSH-Dienst ist noch nicht hochgefahren. Hier hilft meist: abwarten.
  2. Eine Firewall, ein Rate Limiter, oder Dienste wie fail2ban weisen die Verbindung ab (REJECT). Die Gründe hierfür sind meist zu viele erfolglose Anmeldeversuche oder eine fehlgeleitete Bot-Erkennung. In seltenen Fällen kann es aber auch sein, dass die eigene IP-Adresse auf einer “deny-list” auftaucht, die von den Zielen eingelesen wurde. Auch hier empfiehlt sich: warten.
  3. Der angesprochene Dienst erwartet einen anderen Port als den SSH-Standard Port 22. Somit stimmen User/Ziel-Adresse/Port nicht überein.

Das Port-Problem lässt sich leicht lösen.

Image: Gitea showing SSH port IDs under /admin/config/Server_Configuration/SSH_Configuration, Port:222, Listen Port: 22

  1. Herausfinden des “Listen Port” auf dem Host (Server Configuration / SSH). Auf dem Bild ist dargestellt, wie die Einstellung auf meiner Gitea-Instanz aussieht. Alternativ genügt meist ein Blick in die Konfigurationsdatei des entsprechenden Services.
  2. Die SSH-Konfiguration unter ~./ssh/config mit einer konkreten Port-Nummer versehen. Beispiel:
# /.ssh/config
 Host gitserver
  Hostname git.schallbert.de
  Port 222
  [...]

Problem: Too Many Authentication Failures#

Konsolenausgabe:

➜  blog git:(article) ✗ git pull
Received disconnect from <ipAddress> port <portId>: Too many authentication failures

Wir haben nun also eine Verbindung zum Server 😁, aber der mag uns nicht reinlassen 😨. Immerhin wird mitgeteilt, warum: Er meint, wir hätten zu viele Schlüssel an seiner Haustür ausprobiert.

Server-Anmeldelimit prüfen#

Dieser Fehler tritt auf, wenn das Server-Limit der Anmeldeversuche gerissen wird. Der Wert beträgt beim Service sshd beispielsweise standardmäßig MaxAuthTries = 6. Hat man viele Schlüssel für mehrere Verbindungen abgespeichert oder verwendet wie ich neuerdings Hardware Security Keys und legt Zweitschlüssel an (also ssh-keys.count*2), wird dieser Wert schnell überschritten.

Fies bei diesem Fehler ist außerdem, dass er nur bei bestimmten Services “unten” in des Agents Schlüsselbund auftritt: unter den ersten sechs dürfen ja fünf Schlüssel falsch sein. Zudem schützt einen nicht, wenn man die Schlüssel in der ~./ssh/config korrekt hinterlegt hat. Der Agent nimmt stets alle Schlüssel zum Probieren mit, wenn man es ihm nicht explizit verbietet.

Das Verbot erteilen wir per IdentitiesOnly yes. Hiermit wird dem Agent klar gesagt, dass er nur die für diesen Host explizit angegebenen Identities (Also Schlüsseldateien oder Nutzername/Passwort) verwenden soll.

# /.ssh/config
Host gitserver
  Hostname git.schallbert.de
  Port 222
  User <user>
  PreferredAuthentications publickey
  IdentityFile <path_to_private_key1>
  Identityfile <path_to_private_key2>
  IdentitiesOnly yes

Git-Config überprüfen#

Diesen Fehler bekommt man auch, wenn die Git-Konfiguration des Repository unter <reponame>/.git/config nicht mit dem in der SSH-Konfiguration ~/.ssh/config hinterlegten Datensatz für Server und Repository übereinstimmt. Ändert man die SSH-Konfiguration, so sind alle auf diesem Server bereitgehaltenen Repo-Konfigurationen ebenfalls zu ändern.

Beispiel:

# <reponame>/.git/config
# [...]
[remote "origin"]
	url = ssh://git@git.schallbert.de:222/schallbert/<reponame>.git
	fetch = +refs/heads/*:refs/remotes/origin/*
[branch "main"]
	remote = origin
	merge = refs/heads/main
	vscode-merge-base = origin/main

In diesem Falle wird die gitserver-Konfiguration gar nicht verwendet, sondern sich direkt einwählt. Dies führt dazu, dass der Agent kein IdentitiesOnly yes Flag mitgeliefert bekommt und er Schlüssel durchprobiert, bis der Server abwinkt. Lösung:

[remote "origin"]
  url = git@gitserver:schallbert/<reponame>.git
	fetch = +refs/heads/*:refs/remotes/origin/*

Problem: Permission Denied (publickey,password)#

Konsolenausgabe:

➜  blog git:(article) ✗ ssh -T git@gitserver
git@git.schallbert.de: Permission denied (publickey,password).

Dieses Problem kann leider viele verschiedene Ursachen haben. Ein paar räumt Githubs Anleitung bereits aus dem Weg, darunter:

  • sudo verwendet
  • Falscher Server
  • nicht den git-user verwendet
  • Falschen Schlüssel verwendet (client)
  • Schlüssel nicht hinterlegt (server)

Die Ausgabe bei falschem Username sieht so aus:

➜  blog git:(article) ✗ ssh -T mit@gitserver
mit@git.schallbert.de: Permission denied (publickey).

Eine weitere Ursache kann eine fehlende Abstimmung zwischen der Konfigurationsdatei ~./ssh/config und dem im git client hinterlegten Remote-Adresse liegen. Sind diese nicht deckungsgleich, wird eine eventuell bestehende Konfiguration gar nicht erst benutzt.

Eine Überprüfung kann man mit git remote -v durchführen.

➜  blog git:(article) ✗ git remote -v
origin	ssh://git@git.schallbert.de:222/schallbert/blog.git (fetch)
origin	ssh://git@git.schallbert.de:222/schallbert/blog.git (push)

Korrekt wäre mit der verbesserten Konfigurationsdatei (Ports eindeutig gesetzt, Identity Files definiert und auf die Angegebenen beschränkt):

# Update link to server's git repository, use host alias from config file
➜  blog git:(article) ✗ git remote update origin git@gitserver:schallbert/blog.git
# Now check if the update was effective
➜  blog git:(article) ✗ git remote -v
origin	git@gitserver:schallbert/blog.git (fetch)
origin	git@gitserver:schallbert/blog.git (push)

Problem: Agent Refused Operation#

Konsolenausgabe:

➜  blog git:(local-setup) ✗ git push --set-upstream origin local-setup 
sign_and_send_pubkey: signing failed for ED25519-SK <path-to-private-key> from agent: agent refused operation

Diesen Fehler sehe ich erst, seitdem ich Hardware Security Keys für ssh verwende. Aus meiner Sicht kann er zwei Ursachen haben. Beide sind zum Glück leicht zu beheben:

  1. Der Agent kennt den Schlüssel nicht. Der Befehl ssh-add löst das Problem. Prüfung per ssh-add -l | grep “<your-key’s-comment>”
  2. Der angeforderte Schlüssel ist nicht da. Kann bei HSK und HSM passieren, falls sie nicht eingesteckt sind, nicht richtig auf dem NFC-Lesegerät platziert sind oder das HSM im Netzwerk nicht verfügbar ist. Oder wenn manuell im Verzeichnis ~./ssh/config gearbeitet wurde und der Schlüssel nun umbenannt oder gelöscht ist.