I'm having issues trying to establish a connection to an AWS Managed SFTP server. Using the credentials I have on hand, I'm able to connect to the server from my Windows command line using the sftp command. Here's my .NET code:
using (var client = new SshClient(new ConnectionInfo(baseHost, user,
new AuthenticationMethod[]{
new PrivateKeyAuthenticationMethod(user,new PrivateKeyFile[]{
new PrivateKeyFile(keyLocation, pkpassword)
}),
}
)))
{
client.Connect(); // Timeout here
}
The code above gets to the client.Connect() line, then times out after 30 seconds with a Renci.SshNet.Common.SshOperationTimeoutException exception. When I look at what's happening with Wireshark, I see that the protocol being used by the sftp command line utility is SSH, while the SSH.NET is using TCP, and the packet sizes are completely different.
Does anybody know what I might be missing here?
I'm running the sftp command-line utility on the same computer as the above code. The first Wireshark image below is from the C# code above. The second is from the sFTP utility:
When I attempt to connect to the server's port 22 using PuTTY in raw mode, I get no response.
Thanks, Jim
This is bug both in old versions of SSH.NET and on Amazon Managed SFTP server side.
According to RFC 4253 Section 4.2. Protocol Version Exchange:
When the connection has been established, both sides MUST send an identification string.
Both SSH.NET client and Amazon Managed SFTP server fail this requirement. Both first wait for the other side to send the identification string before sending its own. A deadlock is inevitable (interrupted only by a timeout). That also explains why Wireshark does not identify the session as SSH, as there's no data exchanged at all. Hence, there's nothing by which the protocol can be identified.
If you can modify SSH.NET source code, moving this line in Session.Connect:
SocketAbstraction.Send(_socket, Encoding.UTF8.GetBytes(string.Format(CultureInfo.InvariantCulture, "{0}\x0D\x0A", ClientVersion)));
... above this block:
Match versionMatch;
// Get server version from the server,
// ignore text lines which are sent before if any
while (true)
{
...
}
... should fix the problem.
Also consider reporting the bug to Amazon.
I have reported the bug to SSH.NET
including the needed change. The fix is included from SSH.NET 2020.0.0 up.
If you cannot change SSH.NET code, you will need to use another SFTP library.
For example my WinSCP .NET assembly is compatible with Amazon Managed SFTP server.
This is an equivalent of your code:
// Set up session options
SessionOptions sessionOptions = new SessionOptions
{
Protocol = Protocol.Sftp,
HostName = baseHost,
UserName = user,
SshHostKeyFingerprint = ...,
SshPrivateKeyPath = keyLocation,
PrivateKeyPassphrase = pkpassword,
};
using (Session session = new Session())
{
// Connect
session.Open(sessionOptions);
// Your code
}
WinSCP GUI can generate a code template like the one above for you.
Related
I have SFTP server which is able to connect via (Bitvise SSH client) with the help of Obfuscation Password. But, unable to connect the same via c# code.
Here is my code:
winSCPSrc.SessionOptions sessionOptionss = new winSCPSrc.SessionOptions();
sessionOptionss.Protocol = winSCPSrc.Protocol.Sftp;
sessionOptionss.PortNumber = Port;
sessionOptionss.HostName = Host;
sessionOptionss.UserName = Username;
if (!string.IsNullOrEmpty(Password))
{
sessionOptionss.Password = Password;
}
//sessionOptionss.GiveUpSecurityAndAcceptAnySshHostKey = true;
sessionOptionss.SshHostKeyFingerprint = fingerPrint;
using (winSCPSrc.Session session = new winSCPSrc.Session())
{
State.KPILog.Info(" SFTP Connection trying to Open ");
session.AddRawConfiguration("DefaultConfiguration", "false");
// Connect
session.Open(sessionOptionss);
State.KPILog.Info("SFTP Connection Open ");
// ...
}
I'm getting "Server unexpectedly closed network connection" error while session.Open(sessionOptionss).
Please let me know there is any other libraries available other than winscp to connect SFTP.
What I have tried:
Able to connect and download files via Bitvise SSH client but unable to implement it in C# code.
Our IT provider has given some "Obfuscation Password" for connecting via Bitvise ssh client. I have no idea how it works in C# code.
Bitvise FAQ says (emphasis mine):
If all of your legitimate connections come from Bitvise SSH Client, you can enable SSH protocol obfuscation in the SSH Server in Advanced settings, under Bindings and UPnP. If you enable obfuscation, only Bitvise SSH Client will be able to connect, and then only if configured with the correct obfuscation keyword.
It seems that the "obfuscation" is a proprietary feature of Bitvise. So it's likely that Bitvise products support it only. Bitvise has FlowSsh library. As Bitvise client is apparently built on top of the library, you should be able to use the library from your C# code.
I have implemented FTP code with use of WinSCP .NET assembly and hosted it on an Azure AppService.
It works locally and on Azure.
But in very few random times, when hosted on Azure, it throws the following error:
Error transferring file 'D:\local\Temp\test_settings.txt'. Server sent passive reply with unroutable address 10.YYY.YYY.YYY, using host address instead. Copying files to remote side failed. Rejected data connection for transfer of "/test_settings.txt", IP addresses of control and data connection do not match
Since the IP starts with 10. does that mean that it's local in the FTP server's network?
Can I do something to improve the implementation?
Do you think that the solution will have a problem when used concurrently by multiple requests?
My code is a copy of the Simple C# example with the following settings:
SessionOptions sessionOptions = new SessionOptions
{
Protocol = Protocol.Ftp,
UserName = username,
Password = password,
GiveUpSecurityAndAcceptAnyTlsHostCertificate = true,
FtpSecure = FtpSecure.Explicit,
HostName = 'ftp.domain.com'
};
IP addresses of control and data connection do not match
That's a message from FileZilla FTP server. It's a security measure. It might indicate that external IP address of your app service instance changed mid transfer. Or of course, it might indicate that you connection was hijacked (that's what the server tries to detect).
It has nothing to do with WinSCP.
I do not know if the IP address of the Azure app service can be fixed somehow. If not, all you can do is to reconnect and retry the transfer. I believe you would have the same problem with any FTP client. Maybe with IPv6 connection, the problem would not happen. But I'm not sure, it's just a wild guess. Though you cannot force IPv6 with WinSCP (only by disabling IPv4 altogether, but I do not know if that's even possible with the app service).
In my scenario i am trying to go from Local App Server -> Middle Server (DMZ) -> Client Server
I need to move files from the Local App Server to the Client Server and back.
So my question is what is the most widely used standard for doing this?
I am currently using WinSCP to connect to the Middle Server via SFTP, and then invoking a command on the Middle Server to download and upload files to the Client Server. I'm not really a fan of this, as i feel like its prone to error as i am manually entering a command, rather than using the WinSCP's library to upload and download. It also leaves me stuck when i try to list all files on the Client Server with a command, as the function returns void
I have looked at SSH.NET which seems like its more widely used, however i cant see any real way of performing a "double hop" with that library either.
With WinSCP .NET assembly, it's easy:
SessionOptions sessionOptions = new SessionOptions
{
Protocol = Protocol.Sftp,
HostName = "example.com",
UserName = "username",
Password = "password",
SshHostKeyFingerprint = "ssh-rsa 2048 xxxxxxxxxxx...=",
};
sessionOptions.AddRawSettings("Tunnel", "on");
sessionOptions.AddRawSettings("TunnelHostName", "tunnel.example.com");
sessionOptions.AddRawSettings("TunnelUserName", "username");
sessionOptions.AddRawSettings("TunnelPasswordPlain", "password");
sessionOptions.AddRawSettings("TunnelHostKey", "ssh-rsa 2048 xxxxxxxxxxx...=");
using (Session session = new Session())
{
session.Open(sessionOptions);
// Your code
}
WinSCP GUI can generate a code template to connect through tunnel, like the one above, for you (except for the TunnelHostKey).
With SSH.NET you can implement a port forwarding explicitly by:
opening connection to the "Middle Server";
creating a forwarded port;
opening a second connection to the forwarded port.
For some example, see Connection to MySQL from .NET using SSH.NET Library.
Another hackish solution is to execute ssh on the "Middle Server" to facilitate the second "hop".
From my script trying to connect to the unix server to download a file but getting below error..
Renci.SshNet.Common.SshConnectionException : Client not connected.
I can connect properly to that server from WinScp by using the same credentials.
Not sure what's going wrong here. Any idea/pointer ?
Code
using (var client = new ScpClient(Config.UnixServer, Config.UnixUsername, Config.UnixPassword))
{
client.Connect();
client.Upload(new FileInfo(fileUpload), fileName);
client.Disconnect();
}
Error
Renci.SshNet.Common.SshConnectionException : Client not connected.
at Renci.SshNet.Session.WaitOnHandle(WaitHandle waitHandle)
at Renci.SshNet.Session.Connect()
at Renci.SshNet.BaseClient.Connect()
WinSCP Session Log
The session log shows that WinSCP is using the sftp protocol (WinSCP supports both scp and sftp protocols). Not all sftp servers will accept scp connections. Switch to the SftpClient class and use the UploadFile method.
I also suspect you meant to call OpenRead() on your FileInfo instance to get a stream.
using (var client = new SftpClient(Config.UnixServer, Config.UnixUsername, Config.UnixPassword))
{
client.Connect();
client.UploadFile(new FileInfo(fileUpload).OpenRead(), fileName);
client.Disconnect();
}
Maybe that helps?
Maybe something to do with unix paths? Check client.RemotePathTransformation = RemotePathTransformation.ShellQuote;
Have quick read here
By default, SSH.NET applies a simple quoting mechanism for remote
paths in ScpClient. More precisely, any remote path is enclosed in
double quotes before it is passed to the scp command on the remote
host.
Perhaps the trust isn't there? In the original logs, OP has this:
2017-09-14 09:10:17.495 Host key matches cached key.
The github page: https://github.com/sshnet/SSH.NET
has information about how to establish an expected fingerprint.
The other issue I run into is when a server has a white list of IP addresses and the server is on it, but my test environment is not.
#Lee, the log from the application performing the connection is most helpful in figuring out these kinds of things, as Martin has pointed out.
Update the Renci reference lib will solved the issues.
I have the following piece of code that tries to connect to an SFTP Server built with OpenSSH (The server works because I've been able to successfully connect to this FTP via WinSCP client):
ConnectionInfo ConnNfo = new ConnectionInfo("127.0.0.1", 22, "usertest",
new AuthenticationMethod[]{
// Pasword based Authentication
new PasswordAuthenticationMethod("usertest","usertest"),
}
);
// Upload A File
using (var sftp = new SftpClient(ConnNfo))
{
sftp.Connect();
sftp.ChangeDirectory(#"/C:/IISFTP/");
using (var uplfileStream = File.OpenRead(uploadfn))
{
sftp.UploadFile(uplfileStream, uploadfn, true);
}
sftp.Disconnect();
}
When calling the sftp.Connect() line, it raises the following Exception:
Message type 80 is not valid
Why is this happening? How can I connect to my SFTP server with SSH.NET?
Thank you
The message 80 stands for SSH_MSG_GLOBAL_REQUEST.
Modern versions of OpenSSH server use this generic message for various proprietary extensions of the SSH protocol.
Most clients will/should silently ignore unrecognized messages. The SSH.NET does ignore the SSH_MSG_GLOBAL_REQUEST too, but it does not expect the message until an authentication completes.
Unfortunately it seems that OpenSSH sends some of these (maybe the hostkeys-prove-00#openssh.com) even before the authentication.
The problem has been fixed in SSH.NET 2016.0.0-beta1. See Issue #8 and Commit a1f46d4.
In older versions, go to the Session.cs and in the Session.Connect() method move the below line somewhat up, above the authentication code:
RegisterMessage("SSH_MSG_GLOBAL_REQUEST");
I'd put it, just below this line:
RegisterMessage("SSH_MSG_USERAUTH_BANNER");