Python - Paramiko 客户端通过 SSL 套接字 - 非阻塞问题

Python - Paramiko Client over SSL Socket - Non-Blocking Issue

我写了一些 Python 代码,使用户能够使用编辑后的 ​​HTML 文件更新我的服务器并上传图像。但是我遇到了一些连接问题。

在服务器端,我是 运行 VMware 中的 Ubuntu 虚拟机,位于 pfSense 路由器 运行 HAproxy 后面。该代理使具有正确客户端证书的用户能够通过 SSH 连接到正确的机器。现在这已经运行了很长时间,没有任何问题。

我认为我的问题是在我的 SFTP 代码中,由于 SSL 包装器不是非阻塞的,套接字关闭了。但是我想不出也找不到修复代码的方法。特别是因为我不是使用这些软件包的专家。 Paramiko 客户端和 SFTP 客户端均已在具有 VPN 连接的远程服务器上分别进行了测试,它们在那种情况下运行良好。 SSL 套接字使用的证书也可以正常工作。

版本:
Python: 3.8.3
openssl: 1.1.1.f
参数:2.7.2

import socket
import ssl
import paramiko

class GE_SFTP_Client:
    def __init__(self, Username, Password):
        self.Username = Username
        self.Password = Password
        
        context = ssl.create_default_context(ssl.Purpose.SERVER_AUTH) # Verify Server cert
        context.load_cert_chain(certfile=_client_cert, keyfile=_client_key) # Load Client cert
        context.set_alpn_protocols(['ssh/2.0'])

        self.s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

        self.conn = context.wrap_socket(self.s, server_side=False, server_hostname=_target_host)
        self.conn.connect((_proxy_host, _proxy_port))

        self.client = paramiko.SSHClient()
        self.client.set_missing_host_key_policy(paramiko.AutoAddPolicy())

        self.connected_status = False
        SSH_Connection_Tries = 0
        while SSH_Connection_Tries < 4: # Try 4 times to connect.
            try:
                self.client.connect(
                                    hostname=_target_host, username=self.Username, password=self.Password,
                                    sock=self.conn, timeout=200, banner_timeout=200, auth_timeout=200
                                    )
                self.connected_status = True
                break
            except:
                SSH_Connection_Tries += 1
                pass

        if self.connected_status:
            self.sftp = self.client.open_sftp()
    
    def GE_SFTP_ClientEnd(self):
        try:
            self.sftp.close()
        except AttributeError:
            pass
        self.client.close()
        self.conn.close()
        self.s.close()

使用 paramiko SFTP 客户端 get() 一些图像时的输出示例。

succes! /images/fulls/3.jpg
succes! /images/fulls/2.jpg
succes! /images/fulls/9.jpg
Socket exception: A non-blocking socket operation could not be completed immediately (10035)

谢谢,如有任何建议,我们将不胜感激!

我放弃了使用 SSL 包中的标准 SSL 包装器的想法,而是找到了一种不同的做事方式,即使用 asyncssh 包与 asyncio 相结合。因为他们一起为我提供了解决这个问题所需的工具。

连接现在更加稳定,即使在下载更大的文件时也是如此。我希望这也能帮助其他人! :)

import socket
import ssl
import asyncio
import asyncssh

class SSL_Socket:
    async def create_connection(self, protocol_factory, host, port):
        loop = asyncio.get_event_loop()

        context = ssl.create_default_context(ssl.Purpose.SERVER_AUTH) # Verify Server cert
        context.load_cert_chain(certfile=_client_cert, keyfile=_client_key) # Load Client cert
        context.set_alpn_protocols(['ssh/2.0'])

        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        s.connect((_proxy_host, _proxy_port))
        
        return (await loop.create_connection(protocol_factory, sock=s, ssl=context, server_hostname=host))

async def WorkingExample():
    async with asyncssh.connect(_target_host, tunnel=SSL_Socket(), username=Username, password=Password, known_hosts=_known_hosts) as conn:
        async with conn.start_sftp_client() as sftp:
            print(await sftp.stat(f'{remote_dir}/index.html'))