多数情况Authentication failed不是密码错误,而是SSH服务端禁用密码认证或paramiko未正确选择密钥认证方式;应检查sshd_config中PasswordAuthentication是否为yes(调试用),优先配置并显式加载RSA私钥,关闭look_for_keys和allow_agent避免干扰。

paramiko连接失败:Authentication failed报错怎么快速定位
多数情况不是密码错了,而是 SSH 服务端默认禁用了密码认证,或者 paramiko 没走对认证方式。它默认尝试密钥 → 密码 → keyboard-interactive,但很多服务器只开密钥,且要求私钥无密码或已用 ssh-agent 托管。
- 先确认目标机
/etc/ssh/sshd_config中PasswordAuthentication是yes(仅调试用),更推荐配好密钥后设为no - 用
paramiko.RSAKey.from_private_key_file()显式加载私钥,别依赖系统~/.ssh/id_rsa自动发现 - 如果私钥有密码,必须传
password=xxx给from_private_key_file,不能只在connect()里传 - 加
look_for_keys=False和allow_agent=False到connect()参数里,避免 paramiko 去碰本地 ssh-agent 或其他密钥文件干扰判断
同步文件时 scp 卡住或超时:为什么不用 paramiko 自带的 SFTP
paramiko.SFTPClient 是唯一靠谱的选择。自己拼 scp 命令调 exec_command 很容易因 shell 解析、权限、路径空格崩掉,而且没法可靠捕获传输进度或失败原因。
- 别写
client.exec_command("scp -r ...")—— 这是在远端起一个新 shell,SFTP 通道根本不管它 - 用
sftp = paramiko.SFTPClient.from_transport(transport),再调sftp.put()或sftp.get() -
sftp.put()的callback参数能传函数实时看进度,比如lambda x, y: print(f"{x}/{y}") - 注意远端路径权限:如果目标目录属主不是登录用户,
sftp.put()可能静默失败,建议提前用exec_command("mkdir -p /path && chmod 755 /path")
批量执行命令返回乱码或截断:stdout.read() 拿不到完整输出
根本原因是 paramiko 的 stdout 是 channel 级流,没等远端进程彻底退出就 read(),会漏掉最后几行,尤其遇到 sudo 提示或交互式命令时更明显。
- 必须等
channel.recv_exit_status()返回后再读,而不是一exec_command完就read() - 推荐写法:
stdin, stdout, stderr = client.exec_command(cmd); stdout.channel.recv_exit_status(); out = stdout.read().decode() - 如果命令可能卡住(如等待输入),务必设
timeout:在exec_command后加stdout.channel.settimeout(30),再用 try/catch 包住read() - 中文路径或输出乱码?远端 shell 编码未必是 utf-8,加
export LANG=C.UTF-8到命令前,比如cmd = "export LANG=C.UTF-8; ls /tmp/中文"
多机同步失败后难排查:别让一台挂了拖垮全部
paramiko 默认阻塞式操作,一台连不上、传一半断了、命令卡死,整个脚本就停住。得把每台机器的操作包成独立可恢复的单元,失败只影响当前节点。
- 每个主机逻辑用独立
try/except包住,捕获paramiko.AuthenticationException、paramiko.SSHException、IOError等,别让异常冒泡到外层循环 - 连接和传输都设超时:
connect(..., timeout=10, auth_timeout=10),SFTP 操作也加timeout参数(需 paramiko ≥ 2.9) - 记录每台机器的
hostname+error到列表,最后统一打印失败清单,比直接 exit 更利于运维补救 - 别省事复用同一个
Transport对象去连不同主机 —— paramiko 不支持 reuse,会出SSHException: Incompatible version
真正麻烦的是远端磁盘满、SELinux 拦截、systemd-journald 占满日志导致 sshd 拒绝新连接——这些不会抛 paramiko 异常,只能靠命令返回码和 stderr 内容判断,得在关键步骤后检查 exit_status == 0 并读 stderr。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











