Hallo,
ich habe mehrere Anwendungen, die SSH.NET (SSH.NET) nutzen. Hierbei handelt es sich bisher immer um Datenbankzugriffe auf MySQL- bzw. MariaDB-Datenbanken. Nun muss ich eine weitere Anwendung erstellen, die ebenfalls auf eine MariaDB-Datenbank zugreift – das treibt mich aber zum Wahnsinn.
Die SSH-Verbindung wird dabei erfolgreich hergestellt und ich kann beliebige Shell-Befehle absetzen. So weit, so gut. Allerdings möchte ich auf die Datenbank zugreifen, so dass ich eine lokale Portweiterleitung einrichte – diese scheint jedoch nicht zu wirken, obwohl im Debugger alles richtig erscheint.
Nun ein wenig Code, bevor ich weiter erkläre:
ConnectionInfo ConnNfo = new ConnectionInfo("domäne.tld", 22, "ssh-user",
new AuthenticationMethod[]
{
new PrivateKeyAuthenticationMethod("ssh-user", new PrivateKeyFile[]
{
new PrivateKeyFile(@"C:\Daten\Visual Studio\Projects\Z-Tests\sshtest2\.ssh\private.key", "PassPhrase")
}
),
}
);
using var sshclient = new SshClient(ConnNfo);
sshclient.Connect();
var portfwd = new ForwardedPortLocal(IPAddress.Loopback.ToString(), 3306, "localhost", 3306);
sshclient.AddForwardedPort(portfwd);
portfwd.Start();
using MySqlConnection MySqlCon = new ("server=127.0.0.1; user id=benutzer; password=kennwort; database=datenbank; Allow Zero Datetime=True; persistsecurityinfo=False");
/*
* Unbehandelte Ausnahme
*
* MySql.Data.MySqlClient.MySqlException: "Reading from the stream has failed."
*
* Innere Ausnahme
* EndOfStreamException: Attempted to read past the end of the stream.
*
*/
MySqlCon.Open(); // <--- Ausnahme
MySqlCon.Close();
sshclient.Disconnect();
Im Debugger sehe ich, dass die lokale Portweiterleitung richtig eingerichtet und gestartet ist:
- portfwd {Renci.SshNet.ForwardedPortLocal} Renci.SshNet.ForwardedPortLocal
BoundHost "127.0.0.1" string
BoundPort 3306 uint
Host "localhost" string
IsStarted true bool
Port 3306 uint
Auch der SSH-Client ist richtig eingerichtet und ich kann u. a. Shell-Befehle wir z. B.
Console.WriteLine(sshclient.CreateCommand("ls -lah ~/.ssh").Execute());
erfolgreich ausführen.
Nun halte ich das Programm vor MySqlCon.Open() an und schaue mir die im System benutzten Ports mit
netstat -bano -p TCP
an. Dabei stelle ich folgendes fest (Ausschnitt):
Aktive Verbindungen
Proto Lokale Adresse Remoteadresse Status PID
TCP 10.0.70.5:59619 192.168.70.204:49172 WARTEND 0
TCP 10.0.70.5:59620 192.168.70.204:49172 WARTEND 0
TCP 10.0.70.5:59773 192.168.70.204:135 HERGESTELLT 5732
[spoolsv.exe]
TCP 10.0.70.5:59774 192.168.70.204:135 HERGESTELLT 5732
[spoolsv.exe]
TCP 10.0.70.5:59775 192.168.70.204:135 HERGESTELLT 5732
[spoolsv.exe]
TCP 10.0.70.5:59776 192.168.70.204:49172 SYN_GESENDET 5732
[spoolsv.exe]
TCP 10.0.70.5:59777 192.168.70.204:49172 HERGESTELLT 5732
[spoolsv.exe]
TCP 127.0.0.1:3306 0.0.0.0:0 ABHÖREN 42608
[sshtest2.exe]
TCP 127.0.0.1:8884 0.0.0.0:0 ABHÖREN 4
Es konnten keine Besitzerinformationen abgerufen werden.
TCP 127.0.0.1:9395 127.0.0.1:56791 HERGESTELLT 6704
Bei Zeile
TCP 127.0.0.1:3306 0.0.0.0:0 ABHÖREN 42608
sehe ich den Port und die PID = 42608, die mein Testprogramm sshtest2.exe bekommen hat.
Wie ist das zu deuten? Ich hätte folgendes erwartet:
TCP 127.0.0.1:3306 127.0.0.1:3306 ABHÖREN 42608
Ich verstehe das einfach nicht, denn andere Programme, die genauso arbeiten, funktionieren tadellos.
Ein Parameter unterscheidet sich: Es handelt sich um einen anderen Internet-Provider (all-inkl, Webspace-Tarif ALL-INKL Business, Webhosting Tarif ALL-INKL BUSINESS - ALL-INKL.COM). Allerdings kann ich mir beim besten Willen nicht vorstellen, dass dies etwas damit zu tun hat, da es sich um eine lokale Portweiterleitung handelt.
Kennt sich jemand von euch damit aus und kann mir weiterhelfen?
Schonmal lieben Dank und viele Grüße
René
René
Versuch es mal mit:
var portfwd = new ForwardedPortLocal("127.0.0.1", 3306, "127.0.0.1", 3306);
oder
var portfwd = new ForwardedPortLocal(IPAddress.Loopback.ToString(), 3306, "127.0.0.1", 3306);
Danke! Ich hatte in meiner unendlichen Verzweiflung vergessen, zu erzählen, dass ich dies schon alles durchgetestet habe.
René
Danke! Ich hatte in meiner unendlichen Verzweiflung vergessen, zu erzählen, dass ich dies schon alles durchgetestet habe.
Mittlerweile habe ich auch die Verbindungszeichenkette so angepasst, dass ich für meine Tests "nur" an der Methode ForwardedPortLocal drehen muss.
var portfwd = new ForwardedPortLocal("127.0.0.1", 3306, "127.0.0.1", 3306);
sshclient.AddForwardedPort(portfwd);
portfwd.Start();
using MySqlConnection MySqlCon = new($"server={portfwd.BoundHost}; port={portfwd.BoundPort}; user id=...");
Ich habe mich auch zu Tode gegoogelt, wobei ich nicht alleine zu sein scheine. Allerdings keiner der Tipps konnte weiterhelfen. Daher habe ich das direkt auf GitHub unter Local port forwarding "ForwardedPortLocal" does not work · Issue #1066 · sshnet/SSH.NET beschrieben. Bisher aber kein Feedback, leider.
Das Ausführen auf anderen Rechnern bzw. sogar mit Admin-Rechten brachte nichts. Nun gehen mir die Ideen aus.
René
Daher habe ich das direkt auf GitHub unter
> beschrieben. Bisher aber kein Feedback, leider.
Du hast nicht wirklich erwartet, dass Du binnen einer Stunde eine Antwort bekommst, oder...?
Realistisch: Tage, wenn nicht mehr.
new ForwardedPortLocal("127.0.0.1", 3306, "127.0.0.1", 3306);
Die Wahrscheinlichkeit, dass Du Inbound und Outbound exakt den gleichen Port auf dem gleichen Host verwenden kannst, ist unter 0.
So funktioniert leider Networking nicht.
- performance is a feature -
Microsoft MVP - @Website - @AzureStuttgart - github.com/BenjaminAbt - Sustainable Code
Doch, so funktioniert SSH.NET. Nachdem man eine SSH-Verbindung hergestellt hat, kann man die Portweiterleitung einrichten. Die Methode
new ForwardedPortLocal("127.0.0.1", 3306, "127.0.0.1", 3306);
leistet das:
- portfwd {Renci.SshNet.ForwardedPortLocal} Renci.SshNet.ForwardedPortLocal
BoundHost "127.0.0.1" string
BoundPort 3306 uint
Host "127.0.0.1t" string
IsStarted true bool
Port 3306 uint
ForwardedPortLocal.BoundHost und ForwardedPortLocal.BoundPort stellen die Quelle dar während ForwardedPortLocal.Host und ForwardedPortLocal.Port das Ziel – es ist durchaus möglich und in den meisten Fällen sogar die Regel, dass Quelle und Ziel (visuell) gleich sind.
René
UPDATE++++
Ich habe das gleiche Testprogramm aber mit anderen Verbindungs- und Zugangsdaten ausgeführt. Im Wesentlichen handelt es sich um andere Internet-Provider. Hier stelle ich fest, dass die anderen Provider keine Probleme machen und alles läuft, wie es sein sollte. Dabei ist mir etwas aufgefallen: Den Fehler macht nicht die Methode ForwardedPortLocal, wie ich fälschlicherweise vermutete. Wenn ich nach der Portweiterleitung anhalte, sehe ich in allen Fällen folgendes:
TCP 127.0.0.1:3306 0.0.0.0:0 ABHÖREN 42608
Also das ist korrekt und ich hatte mich geirrt.
Nun habe ich bei den fehlerfreien Programmläufen direkt nach MySqlConnection.Open angehalten und siehe da, folgendes Bild bot sich mir an:
Proto Lokale Adresse Remoteadresse Status PID
TCP 127.0.0.1:3306 0.0.0.0:0 ABHÖREN 51156
[sshtest2.exe]
TCP 127.0.0.1:3306 127.0.0.1:62024 HERGESTELLT 51156
[sshtest2.exe]
TCP 127.0.0.1:62024 127.0.0.1:3306 HERGESTELLT 51156
[sshtest2.exe]
Hier sieht man, dass zwei Verbindungen hergestellt sind und dass der Port 62024 als "Zwischenport" verwendet wird.
Nun glaube ich langsam, dass der Provider (all-inkl) in die Suppe spuckt und das Aushandeln des "Zwischenports" unterbindet.
Ich werde daher ein Ticket bei denen eröffnen, um sicher zu sein, dass die es unterbinden.
René
Die Wahrscheinlichkeit, dass Du Inbound und Outbound exakt den gleichen Port auf dem gleichen Host verwenden kannst, ist unter 0.
Doch, so funktioniert SSH.NET.
Das kann auch SSH.NET nicht, denn es ist eine Einschränkung des Betriebssystems, dass für Inbound und Outbound auf der gleichen Maschine nicht der selbe Port verwendet werden kann.
Aus Deinem Update wird klar: SSH.NET wendet (intern wohl) einen Trick an, erzeugt zwei Bindings, nicht nur eines. War mir nicht klar; Beschreibung bleibt dennoch korrekt.
- performance is a feature -
Microsoft MVP - @Website - @AzureStuttgart - github.com/BenjaminAbt - Sustainable Code
Zunächst sorry, dass ich etwas abschweife, aber ich denke, das kann für den einen oder anderen interessant sein.
Ich habe weiter geforscht und bin zur Erkenntnis gekommen, dass mein Provider all-inkl den Zugriff auf Datenbanken über einen SSH-Tunnel unterbindet. Zwar ist SSH möglich, nicht jedoch einen SSH-Tunnel einzurichten. Daher habe ich den Support kontaktiert, der – wie immer – sehr schnell antwortete:
vielen Dank für Ihre Anfrage.
Der Zugriff auf die Datenbanken über einen SSH-Tunnel ist bei uns nicht möglich. Über eine VPN-Verbindung ist dennoch ein verschlüsselter Transfer möglich. Auf unserer Website finden Sie folgenden Link zur Einrichtung:
Allerdings kommt bei uns VPN nicht in Frage, denn die Software soll leicht zu deployen sein, ohne einen OpenVPN-Client installieren zu müssen, der auch programmgesteuert überwacht und ferngesteuert werden müsste.
Daraufhin habe ich gefragt, warum kein SSH-Tunnel möglich ist und ob dies zumindest in Managed-Servern (auch das haben wir bei denen) gehen würde. Schließlich haben andere Provider dieses Feature im Portfolio – sie werben sogar damit und stellen Anleitungen zur Verfügung. Dazu gehören IONOS, Mittwald, Hosteurope und weitere. All-inkl meint dazu:
Der SSH Tunnel ist bei uns vor Jahren aus Sicherheitsgründen abgeschafft worden.
Auch auf einem Managed-Server können wir dies leider nicht freischalten.
Das hat mich nachdenklich gemacht. Wir verwenden nämlich SSH ausschließlich mit Zertifikaten, welche zusätzlich durch eine sehr starke Passphrase gesichert sind. D. h. auch wenn ein Zertifikat (der private Schlüssel) gestohlen wird, kann man ohne Passphrase nichts damit anfangen. Dagegen ist bei deren VPN-Lösung das Zertifikat nicht gesichert, so dass es beim Entwenden sofort missbraucht werden kann. Für mich ein sehr großes Fragezeichen.
Nun habe ich all-inkl gefragt, ob sie irgendwann neben PHP, Phyton und Perl, auch .NET anbieten würden. Dann könnte ich meine Anwendung auf ASP.NET umschreiben. Die Antwort war kurz und prägnant:
Die Implementierung von .NET ist derzeit bei uns nicht geplant.
Wir sind seit sehr vielen Jahren Kunde bei all-inkl und schätzen deren schnellen Support und Robustheit deren Produkte. Allerdings scheint es mir an dieser Stelle ein wenig zu viel des Guten zu sein. Schließlich dreht sich die Welt weiter und man muss einigermaßen mit der Zeit gehen.
Kennt jemand von euch Provider/Tarife auf Linux-Basis (kein Root-Server), bei denen SSH-Tunneling möglich ist und auch .NET unterstützt wird?
Ansonsten werden wir auf einen vorhandenen Root-Server ausweichen, den wir bei einem anderen Provider mieten, um uns schnell aus der Affaire zu ziehen.
Vielen Dank für eure Hilfe!
René
René
Der SSH Tunnel ist bei uns vor Jahren aus Sicherheitsgründen abgeschafft worden.
Unrecht haben sie nicht; VPN ist prinzipiell sicherer als der manuelle SSH Tunnel - für Deinen Zweck aber wohl kein Ersatz.
Die Implementierung von .NET ist derzeit bei uns nicht geplant.
Weil Du nach Providern fragst der reine Hinweis: auf Azure löst man das einfach mit Managed Identities und vNETs:
Managed Identities (bzw. die technische Implementierung dahinter) ist die absolut sicherste Branchenlösung, die es derzeit gibt.
Auf AWS nennt sich die Implementierung AWS Identity Service bzw IAM.
Die Vermutung, dass Du All Inkl nutzt lässt mich aber dazu schließen, dass AWS und Azure Dir zu teuer sein werden (weil Du deren Mehrleistung (und Mehr-Sicherheit?) wohl nicht benötigst).
auch wenn ein Zertifikat (der private Schlüssel) gestohlen wird, kann man ohne Passphrase nichts damit anfangen.
Weil das ist und bleibt immer der Angriffsvektor.
- performance is a feature -
Microsoft MVP - @Website - @AzureStuttgart - github.com/BenjaminAbt - Sustainable Code
Die Vermutung, dass Du All Inkl nutzt lässt mich aber dazu schließen, dass AWS und Azure Dir zu teuer sein werden (weil Du deren Mehrleistung (und Mehr-Sicherheit?) wohl nicht benötigst).
Bisher haben wir uns über Azure und AWS keinen Kopf gemacht. Für die hier angesprochene Anwendung wäre das auch Overkill, denn es handelt sich um eine einfache Datenbankanwendung, welche Daten nur temporär verwaltet. Diese sind weder ein Betriebsgeheimnis, noch werden persönliche und/oder wichtige Daten verwaltet. Dennoch ein sehr interessantes Thema – danke fürs Ansprechen.
auch wenn ein Zertifikat (der private Schlüssel) gestohlen wird, kann man ohne Passphrase nichts damit anfangen.
Weil das ist und bleibt immer der Angriffsvektor.
Richtig, aber die All-inkl-VPN-Lösung ist, so wie sie ausgeliefert wird, noch anfälliger als die von uns verwendete SSH-Lösung: Konfigurationsdatei und Zertifikat klauen und ich bin drin.
Nochmals Danke und frohe Weihnachten!
René
René