python连接sftp的问题汇总

1、使用paramiko模块连接sftp报错:paramiko.ssh_exception.AuthenticationException: Authentication failed.

 直接上代码:

def connect_with_private_key():
    # SFTP连接参数
    hostname = 'xxxxxxxxx'
    port = 22
    username = 'xxxx'
    private_key_path = r".ssh\id_rsa"  # 替换为您的私钥路径

    # 加载私钥
    private_key = paramiko.RSAKey.from_private_key_file(private_key_path)

    # 创建SSH客户端
    ssh = paramiko.SSHClient()
    ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())

    # 建立连接
    ssh.connect(hostname=hostname, port=port, username=username, pkey=private_key)

    # 创建SFTP客户端
    sftp = ssh.open_sftp()
    print("SFTP连接成功!")

    # 示例: 列出根目录文件
    print("根目录文件:", sftp.listdir('.'))

    # 关闭连接
    sftp.close()
    ssh.close()

if __name__ == "__main__":
    connect_with_private_key()

如果你确认了连接信息都是无误的情况下,还是报错了`Authentication failed`

那么接下来你需要使用第三方客户端连接sftp,我这边使用了MobaXterm软件连接是可以连接上的,那么我们可以肯定的是连接信息和防火墙都是不存在问题的,我们把范围缩小看看MobaXterm连接是不是做了什么操作?

我们可以问下大模型,把所有可能出现的情况都尝试一遍;最终确认了是服务端配置了特定的认证方法顺序

把完整的诊断代码贴上:

import paramiko
import logging
import os

# 配置详细日志
logging.basicConfig()
logging.getLogger("paramiko").setLevel(logging.DEBUG)

def diagnose_connection():
    host = 'your_host'
    port = 22
    username = 'your_username'
    key_path = '/path/to/private_key'
    
    # 检查私钥权限
    try:
        st = os.stat(key_path)
        if st.st_mode & 0o7777 != 0o600:
            print(f"警告: 私钥权限应为 600,当前为 {oct(st.st_mode)[-3:]}")
            print("尝试修复权限...")
            os.chmod(key_path, 0o600)
    except Exception as e:
        print(f"权限检查错误: {e}")
    
    # 创建 SSH 客户端
    client = paramiko.SSHClient()
    client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    
    # 尝试加载私钥
    key = None
    key_types = [
        ('RSA', paramiko.RSAKey),
        ('Ed25519', paramiko.Ed25519Key),
        ('ECDSA', paramiko.ECDSAKey)
    ]
    
    for name, key_class in key_types:
        try:
            key = key_class.from_private_key_file(key_path)
            print(f"成功加载 {name} 密钥")
            break
        except paramiko.ssh_exception.PasswordRequiredException:
            print(f"{name} 密钥需要密码")
            try:
                key = key_class.from_private_key_file(key_path, password='your_passphrase')
                print(f"使用密码成功加载 {name} 密钥")
                break
            except:
                continue
        except:
            continue
    
    if not key:
        print("无法加载任何类型的密钥")
        return
    
    # 尝试连接
    try:
        client.connect(
            hostname=host,
            port=port,
            username=username,
            pkey=key,
            timeout=10,
            allow_agent=False,
            look_for_keys=False,
            disabled_algorithms={'pubkeys': ['rsa-sha2-256', 'rsa-sha2-512']}
        )
        print("连接成功!")
        
        # 测试 SFTP
        sftp = client.open_sftp()
        files = sftp.listdir()
        print(f"找到 {len(files)} 个文件")
        sftp.close()
        
    except paramiko.AuthenticationException as e:
        print(f"认证失败: {e}")
        print("可能原因:")
        print("1. 用户名错误")
        print("2. 私钥不匹配")
        print("3. 服务器不接受此密钥类型")
    except paramiko.SSHException as e:
        print(f"SSH 错误: {e}")
    except Exception as e:
        print(f"其他错误: {e}")
    finally:
        client.close()

# 运行诊断
diagnose_connection()

最终我们连接上了!

知识点:

Paramiko 连接参数详解:allow_agentlook_for_keys 和 disabled_algorithms

这三个参数在 Paramiko 的 connect() 方法中用于控制 SSH 连接的行为,特别是身份验证过程和算法协商。下面我将详细解释每个参数的作用、使用场景和实际影响。

1.1 allow_agent=False

作用

  • 禁用 SSH 代理支持:阻止 Paramiko 尝试使用本地 SSH 代理(如 ssh-agent 或 Pageant)进行身份验证
  • 防止自动使用代理中的密钥:即使系统中有活动的 SSH 代理,也不会使用其中的密钥

使用场景

  1. 当你想显式控制身份验证凭据,避免使用系统缓存的密钥
  2. 安全敏感环境中,避免意外使用代理中的密钥
  3. 调试身份验证问题时,排除代理干扰
  4. 容器化环境中,可能没有 SSH 代理可用

默认行为

  • 如果未指定,默认为 True,Paramiko 会尝试使用 SSH 代理

示例

# 显式禁用 SSH 代理
client.connect(
    hostname='example.com',
    username='user',
    password='pass',
    allow_agent=False  # 不使用任何 SSH 代理
)

1.2 look_for_keys=False

作用

  • 禁用本地密钥查找:阻止 Paramiko 在用户主目录的 .ssh 文件夹中查找私钥文件
  • 避免自动尝试密钥认证:即使没有提供密钥,也不会尝试使用默认位置的密钥

使用场景

  1. 当你想仅使用密码认证
  2. 显式提供密钥文件时,避免额外的密钥查找
  3. 安全加固环境中,防止意外使用默认密钥
  4. 连接速度很重要时,减少不必要的密钥查找时间

默认行为

  • 如果未指定,默认为 True,Paramiko 会尝试在以下位置查找密钥:
    • ~/.ssh/id_rsa
    • ~/.ssh/id_dsa
    • ~/.ssh/id_ecdsa
    • ~/.ssh/id_ed25519

示例

# 禁用本地密钥查找
client.connect(
    hostname='example.com',
    username='user',
    password='pass',
    look_for_keys=False  # 不查找 ~/.ssh 中的密钥
)

1.3 disabled_algorithms={'pubkeys': ['rsa-sha2-256', 'rsa-sha2-512']}

作用

  • 禁用特定公钥算法:明确禁止使用指定的公钥签名算法
  • 解决算法兼容性问题:当客户端和服务器支持的算法不匹配时

参数结构

  • 接受一个字典,其中键指定算法类别,值是该类别中要禁用的算法列表
  • 本例中禁用了两种公钥算法:
    • rsa-sha2-256:使用 SHA-256 哈希的 RSA 签名
    • rsa-sha2-512:使用 SHA-512 哈希的 RSA 签名

使用场景

  1. 连接旧版 SSH 服务器(如 OpenSSH 7.2 之前版本),这些服务器不支持新的 RSA-SHA2 签名方案
  2. 解决错误:"no matching host key type" 或 "sign_and_send_pubkey: no mutual signature algorithm"
  3. 当服务器配置错误或使用自定义 SSH 实现
  4. 安全策略要求禁用特定算法

示例

# 禁用特定的公钥算法
client.connect(
    hostname='old-server.example.com',
    username='user',
    key_filename='id_rsa',
    disabled_algorithms={'pubkeys': ['rsa-sha2-256', 'rsa-sha2-512']}
)

三个参数组合使用的场景

当这三个参数一起使用时,通常是为了严格控制身份验证过程

client.connect(
    hostname='legacy-server.example.com',
    port=22,
    username='admin',
    pkey=private_key,  # 显式提供密钥对象
    allow_agent=False,  # 不使用 SSH 代理
    look_for_keys=False,  # 不在本地查找密钥
    disabled_algorithms={'pubkeys': ['rsa-sha2-256', 'rsa-sha2-512']}  # 禁用新算法
)

这种组合的典型场景:

  1. 连接旧系统:连接到不支持新 RSA-SHA2 算法的旧 SSH 服务器
  2. 安全敏感环境:精确控制使用的凭据和算法
  3. 自动化脚本:确保一致的行为,不受本地环境的影响
  4. 调试环境:排除代理和自动密钥查找的干扰

参数间的相互作用

参数影响 allow_agent影响 look_for_keys影响 disabled_algorithms
身份验证源控制代理使用控制本地密钥查找不影响身份验证源
算法选择不影响不影响限制可用算法
连接速度减少代理查询时间减少密钥查找时间可能减少算法协商时间
安全性防止意外使用代理密钥防止意外使用默认密钥可能降低安全性(禁用新算法)
### 如何使用Python通过SFTP协议实现远程文件传输 为了实现基于SFTP协议的远程文件传输,`paramiko` 是一个非常强大的 Python 库。它支持安全的 SSH 连接以及 SFTP 文件传输功能。以下是具体的操作方式: #### 安装 `paramiko` 在开始之前,需要确保已安装 `paramiko` 库。可以通过以下命令进行安装: ```bash pip install paramiko ``` #### 基本概念说明 SFTP(SSH File Transfer Protocol)是一种利用 SSH 协议加密数据传输的安全文件传输协议。`paramiko` 提供了对 SFTP 的全面支持,允许开发者轻松地创建客户端程序来管理服务器上的文件。 --- #### 实现代码示例 ##### 1. 创建并配置 SFTP 连接 下面是一个简单的代码片段,用于建立与远程服务器的 SFTP 连接,并执行基本的文件上传和下载操作。 ```python import os from paramiko import Transport, SFTPClient def create_sftp_connection(host, port, username, password): """创建 SFTP 连接""" transport = Transport((host, port)) transport.connect(username=username, password=password) sftp_client = SFTPClient.from_transport(transport) return sftp_client, transport def upload_file(sftp_client, local_path, remote_path): """上传单个文件至远程服务器""" try: sftp_client.put(local_path, remote_path) print(f"File uploaded successfully to {remote_path}") except Exception as e: print(f"Error uploading file: {e}") def download_file(sftp_client, remote_path, local_path): """从远程服务器下载单个文件""" try: sftp_client.get(remote_path, local_path) print(f"File downloaded successfully from {remote_path} to {local_path}") except Exception as e: print(f"Error downloading file: {e}") if __name__ == "__main__": host = "example.com" port = 22 username = "your_username" password = "your_password" # 创建 SFTP 连接 sftp_client, transport = create_sftp_connection(host, port, username, password) # 上传文件 local_file_to_upload = "/path/to/local/file.txt" remote_destination = "/path/on/remote/server/file.txt" upload_file(sftp_client, local_file_to_upload, remote_destination) # 下载文件 remote_file_to_download = "/path/on/remote/server/existing_file.txt" local_destination = "/path/to/save/downloaded_file.txt" download_file(sftp_client, remote_file_to_download, local_destination) # 关闭连接 sftp_client.close() transport.close() ``` 此代码展示了如何设置 SFTP 连接、上传文件到远程服务器以及从远程服务器下载文件[^1]。 --- ##### 2. 复制整个目录结构 如果需要复制整个目录而不是单一文件,可以扩展上述逻辑以递归处理子目录中的所有文件。以下是一段完整的代码示例: ```python import os from paramiko import Transport, SFTPClient def mkdir_p(sftp_client, remote_directory): """递归创建远程目录""" directories = [] while remote_directory and not os.path.exists(remote_directory): remote_directory, directory = os.path.split(remote_directory) directories.append(directory) directories.reverse() for directory in directories: remote_directory = os.path.join(remote_directory, directory).replace("\\", "/") try: sftp_client.mkdir(remote_directory) except IOError: pass # Directory may already exist def copy_local_dir_to_remote(sftp_client, local_dir, remote_dir): """将本地目录及其内容复制到远程服务器""" for item in os.listdir(local_dir): local_item = os.path.join(local_dir, item) remote_item = f"{remote_dir}/{item}" if os.path.isfile(local_item): sftp_client.put(local_item, remote_item) print(f"Copied file: {local_item} -> {remote_item}") elif os.path.isdir(local_item): mkdir_p(sftp_client, remote_item) copy_local_dir_to_remote(sftp_client, local_item, remote_item) if __name__ == "__main__": host = "example.com" port = 22 username = "your_username" password = "your_password" # 创建 SFTP 连接 transport = Transport((host, port)) transport.connect(username=username, password=password) sftp_client = SFTPClient.from_transport(transport) # 将本地目录复制到远程服务器 local_directory = "/path/to/local/directory" remote_directory = "/path/on/remote/server/" mkdir_p(sftp_client, remote_directory) # 确保目标路径存在 copy_local_dir_to_remote(sftp_client, local_directory, remote_directory) # 关闭连接 sftp_client.close() transport.close() ``` 这段代码实现了更复杂的场景——即递归地将整个本地目录树复制到远程服务器上[^2]。 --- ### 总结 以上提供了两种主要的功能:一是针对单独文件的上传和下载;二是针对复杂需求下的整目录同步。这两种方案均依赖于 `paramiko` 中的核心组件 `Transport` 和 `SFTPClient` 来构建稳定可靠的通信链路。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值