WCF服务使用SSH.NET连接OpenSSH SFTP失败问题排查
WCF调试时SSH.NET连接本机OpenSSH失败排查指南
问题背景
- 生产环境基于.NET 4.5.2搭建集成服务层,运行多个WCF服务,其中一个服务通过SFTP连接同机部署的SFTP服务器,采用用户名密码认证完成文件读取、移动操作,生产环境运行稳定无异常。
- 本地调试环境为Windows Server 2012 R2,已安装OpenSSH(版本为
OpenSSH_for_Windows_8.9p1, LibreSSL 3.4.3),使用Visual Studio 2019(16.8.6)以管理员身份启动调试。项目依赖Renci SSH.NET v2016.0.0版本,受条件限制无法升级该依赖,仅需在开发环境完成调试即可。 - 异常现象:在CMD、FileZilla中使用指定用户名密码均可正常连接SFTP;独立编写的简易控制台应用也可正常连接、读取目标目录文件。但调试简易WCF服务及服务宿主时连接均失败,抛出错误:
An existing connection was forcibly closed by the remote host。
复现代码与错误信息
正常运行的控制台连接代码
SftpClient _client = new SftpClient(host, u1, password1); _client.Connect();
WCF服务中的连接代码
public string GetSFTPConnection(ConnectionDetails connectionDetails) { try { SftpClient _client = new SftpClient(connectionDetails.Host, connectionDetails.UserName, connectionDetails.Password); _client.Connect(); if (_client.IsConnected) { return "SFTP Connected Successfully!"; } return "SFTP Connection Failed!"; } catch (Exception e) { return $"SFT Connection Error@ {e}"; } }
不同地址连接的错误差异
- 使用
localhost连接:返回错误An existing connection was forcibly closed by the remote host - 使用
127.0.0.1连接:返回错误Server response does not contain SSH protocol identification. - 使用环境IP(10.xx.xx.xx)连接:返回错误
Permission denied (password). - 使用计算机名连接:返回错误
An existing connection was forcibly closed by the remote host
完整错误栈
InnerException = An existing connection was forcibly closed by the remote host StackTrace = at Renci.SshNet.Session.WaitOnHandle(WaitHandle waitHandle, TimeSpan timeout) at Renci.SshNet.Session.Connect() at Renci.SshNet.BaseClient.Connect() at SFTPServiceNew.Service1.GetSFTPConnection(ConnectionDetails connectionDetails) in C... <s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"> <s:Header /> <s:Body> <GetSFTPConnectionResponse xmlns="http://tempuri.org/"> <GetSFTPConnectionResult>SFT Connection Error@ Renci.SshNet.Common.SshConnectionException: An existing connection was forcibly closed by the remote host ---> System.Net.Sockets.SocketException: An existing connection was forcibly closed by the remote host at Renci.SshNet.Abstractions.SocketAbstraction.Read(Socket socket, Byte[] buffer, Int32 offset, Int32 size, TimeSpan timeout) at Renci.SshNet.Session.SocketRead(Int32 length, Byte[] buffer) at Renci.SshNet.Session.ReceiveMessage() at Renci.SshNet.Session.MessageListener() --- End of inner exception stack trace --- at Renci.SshNet.Session.WaitOnHandle(WaitHandle waitHandle, TimeSpan timeout) at Renci.SshNet.Session.Connect() at Renci.SshNet.BaseClient.Connect() at SFTPServiceNew.Service1.GetSFTPConnection(ConnectionDetails connectionDetails) in C:...</GetSFTPConnectionResult> </GetSFTPConnectionResponse> </s:Body> </s:Envelope>
排查步骤
之前尝试执行的mkpasswd -l -u [my_user_account] >> etc\passwd是Linux平台OpenSSH的配置逻辑,Windows版OpenSSH 8.9默认直接对接Windows系统账户体系,不会读取安装目录下的etc\passwd文件,这个方向不需要再尝试。按以下顺序排查即可:
- 确认WCF宿主的实际运行身份
控制台应用能正常连接,核心差异是进程运行上下文,不要默认管理员启动VS就等于WCF服务以你的账户运行:- 如果使用IIS Express作为WCF宿主,默认会以当前登录用户的受限令牌运行,部分场景不会继承管理员权限,甚至会使用应用池配置的虚拟账户运行
- 如果是自托管宿主(控制台/Windows Service做宿主),打开任务管理器找到对应WCF宿主进程,查看详细信息栏的用户名,确认和能正常连接SFTP的控制台进程的运行用户名完全一致
- 快速验证:在WCF连接SFTP的代码里临时加一行返回
WindowsIdentity.GetCurrent().Name,调用接口直接获取当前进程运行的用户名,和测试用控制台返回的用户名对比,不一致的话先把WCF宿主的运行身份改成和正常连接的控制台完全一致。
- 解决127.0.0.1连接的协议识别错误
这个错误说明你访问的22端口根本不是OpenSSH服务在响应,大概率是端口被占用,或者WCF进程的流量被代理/安全软件转发:- 执行命令
netstat -ano | findstr ":22",确认22端口的监听进程是sshd.exe,如果是其他进程占用,要么更换OpenSSH的监听端口,要么关闭占用22端口的其他服务 - 检查系统代理、安全软件配置:打开控制面板-Internet选项-连接-局域网设置,确认是否开启了全局代理,临时关闭代理工具、安全软件后再测试,SSH.NET 2016版本本身不会读取系统代理,但部分安全软件的LSP驱动会强制转发本地回环流量。
- 执行命令
- 解决10网段IP连接的密码拒绝错误
这个错误说明TCP连接已经正常建立,但是OpenSSH拒绝了密码认证:- Windows版OpenSSH默认对非回环地址的连接,会校验账户的网络访问权限:打开本地安全策略-本地策略-用户权限分配,确认你使用的SFTP账户在“允许从网络访问这台计算机”列表中,同时不在“拒绝从网络访问这台计算机”列表内
- 打开OpenSSH配置文件
C:\ProgramData\ssh\sshd_config,确认PasswordAuthentication yes配置项没有被注释,也没有配置AllowUsers/DenyUsers规则拦截你的账户,修改配置后重启sshd服务再测试。
- 解决localhost/计算机名连接被强制断开的问题
这个问题是老版本SSH.NET和新版OpenSSH的加密算法不兼容导致的:OpenSSH 8.9默认禁用了一批老旧不安全的密钥交换、加密算法,而2016版SSH.NET默认只支持这些老算法,握手阶段会被sshd直接断开连接。控制台能正常连接是因为普通控制台进程的加密策略限制更宽松,而WCF服务运行在服务宿主上下文下,系统会施加更严格的加密套件限制。
临时调整sshd配置兼容老版本SSH.NET即可(仅用于本地调试,不要带到生产环境):在sshd_config末尾添加以下两行配置:
保存后重启sshd服务,再测试连接即可。另外可以检查KexAlgorithms +diffie-hellman-group14-sha1,diffie-hellman-group1-sha1 Ciphers +aes128-cbc,aes256-cbc,3des-cbcC:\Windows\System32\drivers\etc\hosts文件,确认localhost没有被解析到非回环地址,计算机名对应的IP为本机正确IP。
内容的提问来源于stack exchange,提问作者MichaelO
相关产品推荐
相关产品推荐

