TCP-Erreichbarkeit prüfen, ganz ohne externe Werkzeuge wie nc oder nmap
Bash bringt mit der Pseudo-Datei /dev/tcp ein eingebautes Werkzeug mit, um zu prüfen, ob ein TCP-Port auf einem anderen Host erreichbar ist, ganz ohne netcat, nmap oder ein Zusatzpaket zu installieren. Für einfache Erreichbarkeits-Checks in Monitoring- und Deployment-Skripten reicht das oft völlig aus, für echte Sicherheitsanalysen oder große Netzbereiche ist es dagegen der falsche Werkzeugkasten.
Inhaltsverzeichnis
- 1. Was /dev/tcp ist und wofür ein einfacher Port-Check reicht
- 2. Einen einzelnen Port mit /dev/tcp prüfen
- 3. Rückgabewerte richtig interpretieren und Fehlerquellen unterscheiden
- 4. Mehrere Ports und Hosts in einer Schleife prüfen
- 5. Timeout-Handling: warum /dev/tcp ohne Hilfe blockieren kann
- 6. Grenzen von reinem Bash gegenüber echten Portscannern
- 7. Wann ein echter Portscanner wie nmap nötig ist
- 8. Praxisbeispiel: Pre-Deployment-Healthcheck in einer CI-Pipeline
- 9. Werkzeugwahl im Überblick
- 10. Zusammenfassung
- 11. FAQ
1. Was /dev/tcp ist und wofür ein einfacher Port-Check reicht
/dev/tcp ist keine echte Gerätedatei im Dateisystem, sondern eine Konvention, die Bash selbst interpretiert, sobald eine Umleitung auf einen Pfad der Form /dev/tcp/host/port trifft. Statt eine reale Datei zu öffnen, baut Bash intern eine TCP-Verbindung zum angegebenen Host und Port auf und stellt sie als lesbaren beziehungsweise schreibbaren Dateideskriptor zur Verfügung. Dieses Verhalten ist in vielen, aber nicht allen Bash-Builds aktiviert, es fehlt zum Beispiel in manchen minimalen Distributionen, die Bash ohne das Feature net-redirections kompilieren.
Für ein einfaches Erreichbarkeits-Check, wie es Monitoring-Skripte vor einem Deployment oder in einem Cron-Job häufig brauchen, ist das ausreichend: Die Frage lautet meist nur, ob überhaupt eine Verbindung zustande kommt, nicht welcher Dienst genau dahinter läuft oder welche Version er meldet. Genau für diese binäre Ja-oder-Nein-Frage ist /dev/tcp das schlankeste verfügbare Werkzeug, weil keine Installation nötig ist und der Check in wenigen Zeilen Bash-Code passt.
2. Einen einzelnen Port mit /dev/tcp prüfen
Der grundlegende Aufbau nutzt exec, um einen freien Dateideskriptor auf die Pseudo-Datei umzuleiten, und wertet danach den Exit-Code dieser Umleitung aus. Gelingt der Verbindungsaufbau, liefert die Umleitung selbst den Erfolg zurück, scheitert er, etwa weil der Port geschlossen ist und der Zielhost eine RST-Antwort schickt, meldet Bash einen Fehler und der Exit-Code ist ungleich null.
Wichtig ist, den geöffneten Deskriptor nach der Prüfung wieder zu schließen, sonst bleibt die TCP-Verbindung offen, bis das Skript oder die Shell endet, was bei vielen aufeinanderfolgenden Checks unnötig Ressourcen auf beiden Seiten der Verbindung bindet.
#!/usr/bin/env bash
set -uo pipefail
HOST="db.internal.example.com"
PORT=5432
if exec 3<>"/dev/tcp/$HOST/$PORT"; then
echo "Port $PORT on $HOST is open"
exec 3<&-
exec 3>&-
else
echo "Port $PORT on $HOST is closed or unreachable"
fi
3. Rückgabewerte richtig interpretieren und Fehlerquellen unterscheiden
Ein fehlgeschlagener Verbindungsversuch über /dev/tcp kann mehrere sehr unterschiedliche Ursachen haben, die sich am reinen Exit-Code nicht unterscheiden lassen: Der Port könnte geschlossen sein und aktiv eine Verbindung ablehnen, der Host könnte hinter einer Firewall verschwinden, die Pakete stillschweigend verwirft, oder der Hostname selbst könnte gar nicht auflösbar sein. Für ein aussagekräftiges Monitoring-Skript reicht ein einfaches Ja oder Nein daher oft nicht aus.
Ein robusteres Skript trennt deshalb die DNS-Auflösung von der eigentlichen Verbindungsprüfung, indem es zuerst mit getent hosts oder host prüft, ob der Name überhaupt auflösbar ist, und erst danach den Verbindungsversuch über /dev/tcp unternimmt. So lässt sich in der Fehlermeldung klar unterscheiden, ob das Problem beim Namen, beim Netzwerkpfad oder direkt beim Zielport liegt.
#!/usr/bin/env bash
set -uo pipefail
check_port() {
local host="$1" port="$2"
if ! getent hosts "$host" >/dev/null 2>&1; then
echo "DNS resolution failed for $host"
return 2
fi
if exec 3<>"/dev/tcp/$host/$port" 2>/dev/null; then
exec 3<&- 3>&-
return 0
else
echo "Cannot reach $host:$port"
return 1
fi
}
check_port "api.internal.example.com" 443
4. Mehrere Ports und Hosts in einer Schleife prüfen
Für einen einfachen Healthcheck über mehrere Dienste hinweg genügt eine Schleife über eine Liste aus Host-Port-Paaren, wobei jeder Eintrag einzeln mit der oben gezeigten Funktion geprüft wird. Wichtig ist dabei, dass ein einzelner fehlgeschlagener Port nicht das ganze Skript sofort mit set -e abbricht, sondern gesammelt und am Ende zusammengefasst ausgewertet wird, damit ein Deployment-Skript alle relevanten Probleme auf einmal meldet statt nur das erste.
Diese Art von Skript ersetzt zwar keinen dedizierten Healthcheck-Endpunkt einer Anwendung, ist aber ein sinnvoller erster Schritt vor einem Deployment, um grundlegende Netzwerk- und Firewall-Probleme frühzeitig zu erkennen, bevor die eigentliche Anwendung überhaupt startet und komplexere Fehlermeldungen produziert.
#!/usr/bin/env bash
set -uo pipefail
declare -A targets=(
["db.internal.example.com"]=5432
["cache.internal.example.com"]=6379
["queue.internal.example.com"]=5672
)
failed=0
for host in "${!targets[@]}"; do
port="${targets[$host]}"
if timeout 3 bash -c "exec 3<>/dev/tcp/$host/$port" 2>/dev/null; then
echo "OK $host:$port"
else
echo "FAIL $host:$port"
failed=1
fi
done
exit "$failed"
5. Timeout-Handling: warum /dev/tcp ohne Hilfe blockieren kann
/dev/tcp selbst bringt keine eingebaute Timeout-Option mit. Bei einem offenen Port antwortet der Verbindungsaufbau meist sofort, bei einem geschlossenen Port mit RST-Antwort ebenfalls schnell, aber bei einem Host, der Pakete stillschweigend verwirft, etwa hinter einer restriktiven Firewall, wartet Bash auf das Betriebssystem-Timeout des TCP-Stacks, das je nach System zwischen einigen Sekunden und mehreren Minuten liegen kann.
Das externe Kommando timeout löst dieses Problem zuverlässig, indem es den gesamten Bash-Subshell-Aufruf nach einer festen Anzahl Sekunden hart beendet, unabhängig davon, wie lange der zugrunde liegende TCP-Verbindungsversuch selbst gebraucht hätte. Für Monitoring-Skripte, die regelmäßig laufen und eine vorhersagbare Laufzeit brauchen, ist die Kombination aus timeout und /dev/tcp deshalb Pflicht, nicht optional.
6. Grenzen von reinem Bash gegenüber echten Portscannern
So praktisch /dev/tcp für einzelne Checks ist, es bleibt ein sehr eingeschränktes Werkzeug im Vergleich zu einem dedizierten Portscanner. Bash kann ausschließlich TCP-Verbindungen aufbauen, UDP-Ports lassen sich über /dev/tcp gar nicht prüfen, weil UDP als verbindungsloses Protokoll kein Handshake-Ergebnis liefert, das sich als Erfolg oder Fehlschlag interpretieren ließe. Auch Banner-Grabbing, also das Auslesen der Softwareversion eines antwortenden Dienstes, oder eine Betriebssystem-Erkennung anhand von TCP-Stack-Eigenheiten liegen außerhalb dessen, was eine einfache Bash-Umleitung leisten kann.
Zusätzlich ist ein sequenzieller Bash-Loop über viele Ports oder viele Hosts spürbar langsam, weil jeder Check auf den vorherigen wartet, während ein dedizierter Scanner Hunderte Verbindungsversuche parallel öffnet und damit ein ganzes Subnetz in Sekunden statt in Minuten abdeckt. Für den gelegentlichen Healthcheck von drei oder vier bekannten Diensten spielt das keine Rolle, für einen Scan über ein ganzes Netzsegment ist reines Bash schlicht das falsche Werkzeug.
7. Wann ein echter Portscanner wie nmap nötig ist
Sobald die Fragestellung über eine einfache Ja-oder-Nein-Erreichbarkeitsprüfung hinausgeht, gehört die Aufgabe in die Hände eines dedizierten Werkzeugs wie nmap. Das betrifft insbesondere Sicherheitsaudits, bei denen alle offenen Ports auf einem Zielsystem inklusive der dahinterliegenden Dienste und deren Versionen erfasst werden sollen, sowie jede Situation, in der UDP-Dienste, Betriebssystem-Fingerprinting oder Scans über größere IP-Bereiche gefragt sind.
nmap bringt außerdem Funktionen mit, die aus gutem Grund nicht in ein einfaches Bash-Skript gehören, etwa verschiedene Scan-Techniken zur Umgehung von Firewalls, parallele Scans mit konfigurierbarer Geschwindigkeit und eine strukturierte, maschinenlesbare Ausgabe für die Weiterverarbeitung in Sicherheits-Tools. Wer regelmäßig mehr als einen einzelnen bekannten Port auf Erreichbarkeit prüfen will, sollte nmap in Betracht ziehen, statt ein selbstgebautes Bash-Skript immer weiter auszubauen.
8. Praxisbeispiel: Pre-Deployment-Healthcheck in einer CI-Pipeline
Ein realistischer Einsatzzweck für /dev/tcp ist ein Pre-Deployment-Check in einer CI-Pipeline, der vor dem eigentlichen Rollout prüft, ob alle benötigten Backend-Dienste vom Deployment-Runner aus erreichbar sind. Schlägt dieser Check fehl, bricht die Pipeline sofort mit einer klaren Fehlermeldung ab, statt dass die Anwendung erst später beim Start mit einer verwirrenden Verbindungsfehlermeldung scheitert.
Solche Checks sollten immer mit einem klaren Exit-Code enden, damit die CI-Pipeline den Zustand eindeutig auswerten kann, und die Ausgabe sollte pro geprüftem Ziel eine eigene Zeile enthalten, damit im Fehlerfall sofort ersichtlich ist, welcher Dienst genau das Problem verursacht, ohne die komplette Pipeline-Logausgabe durchsuchen zu müssen.
9. Werkzeugwahl im Überblick
Die richtige Wahl zwischen reinem Bash, netcat und nmap hängt davon ab, wie tief die Prüfung gehen muss, wie viele Ziele geprüft werden und ob eine strukturierte Auswertung der Ergebnisse gebraucht wird.
| Werkzeug | Protokolle | Installation nötig | Typischer Einsatz |
|---|---|---|---|
/dev/tcp in Bash |
Nur TCP | Nein, Bash-Builtin | Einzelner Erreichbarkeits-Check in Deployment-Skripten |
nc (netcat) |
TCP und UDP | Meist ja, oft aber vorinstalliert | Schneller Port-Check inklusive Banner-Grabbing |
curl mit --connect-timeout |
TCP über HTTP(S) | Meist vorinstalliert | HTTP-Endpunkt-Erreichbarkeit statt reiner TCP-Ebene |
nmap |
TCP, UDP, ICMP und mehr | Ja, dediziertes Paket | Sicherheitsaudits, Netzbereich-Scans, Service-Erkennung |
Mironsoft
Shell-Automatisierung, DevOps-Tooling und Deployment-Infrastruktur
Shell-Skripte, die in der Produktion zuverlässig laufen?
Wir analysieren bestehende Bash-Skripte, erkennen fragile Muster und ersetzen sie durch robuste Bash-Patterns: mit vollständiger Fehlerbehandlung, Logging und sicherer Parallelisierung für euren Deployment-Stack.
Code-Review
ShellCheck-Analyse und manuelle Prüfung auf kritische Bash-Pattern-Verstöße.
Refactoring
Fehlerbehandlung, Logging und sichere Dateioperationen nachrüsten.
CI-Integration
ShellCheck und BATS in Pipelines integrieren und Regressionstests aufbauen.
10. Zusammenfassung
Port-Checks in Bash: Das Wichtigste auf einen Blick
Grundprinzip
/dev/tcp/host/port ist eine Bash-interne Konvention, die bei einer Umleitung eine echte TCP-Verbindung aufbaut.
Timeout
/dev/tcp hat kein eingebautes Timeout, das externe Kommando timeout ist deshalb für zuverlässige Skripte Pflicht.
Grenzen
Kein UDP, kein Banner-Grabbing, keine Parallelisierung: für Sicherheitsaudits ist reines Bash das falsche Werkzeug.
Einsatzbereich
Ideal für einfache Erreichbarkeits-Checks weniger bekannter Ports in Monitoring- und Deployment-Skripten.