Anzeige
Anzeige
Anzeige
Anzeige
Anzeige
Anzeige
Lesedauer 6 Min.

SSH für.NET-Applikationen mit SSH.NET

SSH – dahinter verbirgt sich die Abkürzung Secure Shell – ist insbesondere im Bereich der unixoiden Systeme weit verbreitet. Mit SSH.NET steht ein NuGet-Paket zur Verfügung, das die Interaktion mit der sicheren Remote Shell aus .NET-Applikationen heraus zu ermöglichen sucht.
© EMGenie

Die Secure Shell hat sich in vielen Bereichen als unersetzbares Werkzeug etabliert. Unabhängig davon, ob ein im Headless-Betrieb arbeitender Prozessrechner oder eine „unixoide Workstation“ zum Einsatz kommt: Immer dann, wenn der Zugriff unter Verbrauch von wenig Bandbreite zu erfolgen hat, führt an SSH kein Weg vorbei.

Anders als beim absoluten Klassiker Telnet gilt, dass die über die Leitung gesendeten Informationen unter Verwendung vergleichsweise starker Kryptografie abgesichert werden.

In diesem Kurz-Tutorial wollen wir das Paket SSH.NET verwenden. Als Gegenstelle dient ein mit einer aktuellen Version von Raspberry Pi OS ausgestatteter Raspberry Pi. Zu beachten ist, dass die Prozessrechner mit den Standardeinstellungen aus Sicherheitsgründen kein SSH-Interface mehr exponieren. Anleitungen zur Aktivierung findet man als interessierter Experimentator unter dem Stichwort Remote Access in der Raspberry-Pi-Dokumentation.

Einrichtung von Projektstruktur und Bibliothek

Einer der wichtigsten Erfolgsfaktoren von NuGet ist die Einfachheit der Installation selbst bei komplexesten Komponenten. Was einst manuelles Herumklicken mit Assembly und Co. erforderte, können .NET-Entwicklerinnen und -Entwickler nur mit wenigen Klicks innerhalb von Visual Studio erledigen. In den folgenden Schritten wollen wir mit einem auf der Vorlage Konsolen-App basierenden Projekt beginnen, als Name sei SSHTest1 angenommen.

Im Bereich der von den verschiedenen Bibliotheken unterstützten .NET-Versionen gibt es keinen wirklich einheitlichen Standard. Neben der Konsultation der Dokumentation kann es auch empfehlenswert sein, die offizielle NuGet-Galerie zu besuchen. Im Fall von SSH.NET informiert sie wie in Bild 1 gezeigt über die unterstützten Versionen.

Nun ist klar: Unser Konsolenprojekt muss auf .NET 8.0 basieren (Bild 1)

Nun ist klar: Unser Konsolenprojekt muss auf .NET 8.0 basieren (Bild 1)

 

© NuGet.org

Zum Abschluss der Vorbereitungshandlungen bietet es sich an, einen ersten Testlauf gegen den Raspberry Pi durchzuführen. Hierzu ist folgender Code erforderlich:

using Renci.SshNet;

using (var client = new SshClient("192.168.1.103", "pi", "raspberry")) {
  client.Connect();
  using SshCommand cmd = client.RunCommand("echo 'Greets from RPi!'");
  Console.WriteLine(cmd.Result);
}
 

Die Verwendung der Bibliothek erfolgt im Allgemeinen durch Nutzung eines SshClient-Objekts, das die Beziehung zwischen der .NET-Applikation und der unixoiden Gegenstelle repräsentiert. Wir authentifizieren uns hier unter Nutzung von Benutzernamen und Passwort – wer sich stattdessen mit einem Dateipaar ausweisen möchte, muss eine andere Variante des Objekt-Konstruktors verwenden.

Der Rest des Codes nutzt dann den Befehl RunCommand, um einen Befehl auf dem unixoiden Prozessrechner zur Ausführung zu bringen. Zu guter Letzt wird die Antwort in die Kommandozeile der Windows-Workstations ausgeworfen – die Ausführung des Programms führt zum Erscheinen der Begrüßungsmeldung.

Interaktive Experimente mit SSH.NET

In der Praxis sind „direkt und uninteraktiv“ ablaufende Kommandos höchst selten – das geringste Ärgernis, das man sich einhandelt, sind erhebliche Latenzen. Im nächsten Schritt wollen wir den für Prozessrechnerjobs hilfreichen Scanner nmap in Betrieb nehmen. Im Interesse der didaktischen Komplexität wird der Autor davon ausgehen, dass das Image des Raspberry Pi das Produkt noch nicht enthält – wäre das Paket bereits vorhanden, würden die folgenden Experimente weniger lehrreich ausfallen.

Als ersten Versuch bietet es sich jedenfalls an, nach folgendem Schema die Ausführung der Engine zu befehligen. Beachten Sie außerdem, dass die Auswertung nun sowohl das Feld Error als auch das Feld Result tangiert:

 

using SshCommand cmd = client.RunCommand("nmap -sN 192.168.1.0/24");
Console.WriteLine(cmd.Result + " / " +  cmd.Error);

 

Als Ergebnis gibt die Ausführung den folgenden String zurück:

 

bash: line 1: nmap: command not found.

 

Würde unser Code das Error-Feld nicht auswerten, so erschiene ein leeres Kommandozeilenfenster am Bildschirm.

Aus der Logik folgt, dass der nächste Schritt das probeweise Nachinstallieren des Pakets ist. Unter unixoiden Betriebssystemen ist die Paketverwaltung einfach, weshalb eine naive Modifikation folgendermaßen aussehen würde:

 

using SshCommand cmd = client.RunCommand("sudo apt-get install nmap");

 

Als Resultat erscheint ein „leeres“ Kommandozeilenfenster, das auch nach einiger Zeit nicht vom Feld am Bildschirm verschwindet. Ursache dieses unbefriedigenden Verhaltens ist, dass der Befehl apt-get install vom Benutzer eine Eingabe verlangt. Spezifischerweise präsentiert sich der Flow wie in Bild 2 gezeigt.

Das Programm hängt beim Statement Do you want to continue? [Y/n] (Bild 2)

Das Programm hängt beim Statement Do you want to continue? [Y/n] (Bild 2)

© Autor

Die nächste Version des Programms präsentiert sich folgendermaßen:

 

using (var client = new SshClient("192.168.1.103", "pi", "raspberry"))
{
  client.Connect();
  using (ShellStream shellStream = client.CreateShellStream("ShellName", 80, 24, 800, 600, 1024))
  {
    string prompt = shellStream.Expect(new Regex(@"[$>]"));

 

Anstatt wie bisher unter Nutzung von RunCommand direkt Befehle gegen den Prozessrechner zur Ausführung zu bringen, setzen wir nun auf einen ShellStream. Dabei handelt es sich um eine Art des IO-Streams, der allerdings mit der angeschlossenen SSH-Gegenstelle zu interagieren sucht.

Shells sind für die Abarbeitung von per Batch ablaufender Aufgaben vorgesehen. Da der Prozessrechner für die Bereitstellung der Shell-Session mitunter etwas Zeit in Anspruch nimmt, sorgt die Zeile mit dem Regex @"[$>]" für eine variable Totzeit. Spezifischerweise gilt, dass der ShellStream so lange anhält, bis eine den Regex „befriedigende“ Antwort eintrifft. Da wir hier auf den Systemprompt matchen, deutet dies auf das Verfügbarsein des Prozessrechners hin.

Die eigentliche Installationsanweisung nutzt dann einen weiteren Matcher-Block, um auf das Erscheinen der Abfrage zu warten. Die Wartezeit ist erforderlich, weil die Berechnung der notwendigen Paket-Transaktionen insbesondere auf langsameren Prozessrechnern einige Zeit in Anspruch nehmen kann:

 

  shellStream.WriteLine("sudo apt-get install nmap");    
  prompt = shellStream.Expect("Do you want to");    
  Thread.Sleep(100);    
  shellStream.WriteLine("y");    
  shellStream.Expect(new Regex(@"[$>]"));  
}

 

Die Zeile shellStream.WriteLine("y"); kümmert sich dann um das Senden der Quittierung; ein weiterer Regex wartet die erfolgreiche Abarbeitung der (lange dauernden) Paket-Operationen des Betriebssystems ab.

Nach der erfolgreichen Abarbeitung des Installationsprozesses zerstören wir das ShellStream-Objekt, um danach nach dem bekannten Schema einen Scanbefehl loszutreten:

 

  using SshCommand cmd = client.RunCommand("nmap -sN 192.168.1.0/24");  
  Console.WriteLine(cmd.Result + " / " + cmd.Error);
}

 

Ergebnis der Programmausführung ist nur das Erscheinen der in Bild 3 gezeigten Fehlermeldung: Ursache dafür ist, dass manche fortgeschrittene Scanarten Superuserrechte voraussetzen, unser Programm allerdings mit den Rechten des Pi-Users arbeitet.

Der Start von nmap verlief (prinzipiell) erfolgreich (Bild 3)
Der Start von nmap verlief (prinzipiell) erfolgreich (Bild 3) © Autor

Der Gutteil der Ausführungszeit – bis zum Erscheinen der Meldung kann etwas Zeit ins Land gehen – wird für die Operationen des Paketmanagers verbraucht. Dies lässt sich dadurch überprüfen, dass man beispielsweise unter Nutzung von Putty eine zweite Verbindung zum Prozessrechner aufbaut und dort eine beliebige Paketmanager-Operation ausführt. Dies scheitert – wie in Bild 4 gezeigt – mit einem Verweis darauf, dass das Paketmanager-Singleton gesetzt ist und deshalb keine weiteren Paketoperationen durchgeführt werden können.

Die Fehlermeldung vermeldet paradoxerweise Erfolg (Bild 4)

Die Fehlermeldung vermeldet paradoxerweise Erfolg (Bild 4)

© Autor

Fazit und Ausblick

Obwohl wir hier nur einen winzigen Teil der Möglichkeiten der SSH.NET-Bibliothek tangiert haben, ist offensichtlich, dass sie die verschiedensten Arten der Kommunikation zwischen .NET-Applikation und Prozessrechnersystem ermöglicht.

Angemerkt sei, dass es sich hierbei nur um eine von vielen im NuGet-Ökosystem lebenden Bibliotheken handelt. In Folgeartikeln stellen wir weitere nützliche Module vor – bleiben Sie dran, denn das Selbstimplementieren von kostenlos Vorhandenem ist immer die ineffizienteste Vorgehensweise.

Neueste Beiträge

DDC hakt nach: Hör auf, Dich über Verbindungsstrings zu ärgern
Die App läuft lokal wie geschmiert, aber sobald Backend-Services ins Spiel kommen, stottert die Maschine. Connection Strings, die nicht wollen. Emulator-Konfigurationen, die nerven. Johan Smarius erklärt im Interview und auf der .NET Developer Conference 2025, wie es anders - und viel besser geht.
4 Minuten
10. Nov 2025
Hierarchische Testdata-Builder - Testdata-Builder, Teil 1
Lesbare Tests bei tiefen Objekthierarchien mit dem Collection-Builder-Pattern.
4 Minuten
Deep Learning mit Python - Python und AI, Teil 3
In der heutigen KI-Entwicklung spielen Deep-Learning-Frameworks eine zentrale Rolle. Ein Vergleich der beiden populärsten Bibliotheken TensorFlow und PyTorch.
10 Minuten

Das könnte Dich auch interessieren

Studie: DSGVO birgt Nachteile für kleine Unternehmen - EU-Datenschutz-Grundverordnung
Laut aktueller Umfrage der TeamDrive Systems GmbH „Datensicherheit in der Cloud“ bremsen horrende Strafsummen und strikte Richtlinien die Entwicklung deutscher Unternehmen aus.
3 Minuten
11. Dez 2017
Schutz und Lizenzierung für Python-Anwendungen - Wibu Systems
Wibu-Systems hat eine schlanke und zugleich sichere Methode zum Schutz von Python-Anwendungen vorgestellt.
3 Minuten
22. Mär 2021
Windows: RSA-Schlüssel müssen 2048 Bit lang sein - Microsoft
Microsoft hat angekündigt, dass RSA-Schlüssel, die kürzer als 2048 Bit sind, in Windows Transport Layer Security (TLS) bald veraltet sein werden, um die Sicherheit zu erhöhen.
2 Minuten
25. Mär 2024
Anzeige
Anzeige
Anzeige
Anzeige
Anzeige