Windows 上用 paramiko 做免交互 SSH 自动化:一次真实的踩坑与改造

举报
yd_213533429 发表于 2026/10/25 06:03:22 2026/10/25
【摘要】 最近要给一批机器做批量巡检,第一步就卡住了:Windows 自带的 OpenSSH 客户端没法在脚本里自动填密码。## 一、为什么不用 ssh 命令`ssh.exe` 从 `/dev/tty` 读取密码,不是 stdin。所以下面这种写法一定失败:```powershell"mypassword" | ssh root@10.0.0.1 "uptime"```常见绕法有三条:装 `sshpa...

最近要给一批机器做批量巡检,第一步就卡住了:Windows 自带的 OpenSSH 客户端没法在脚本里自动填密码。


## 一、为什么不用 ssh 命令


`ssh.exe` 从 `/dev/tty` 读取密码,不是 stdin。所以下面这种写法一定失败:


```powershell

"mypassword" | ssh root@10.0.0.1 "uptime"

```


常见绕法有三条:装 `sshpass`(Windows 上没有官方版本)、用 PuTTY 的 `plink -pw`(需要额外下载)、或者上 Python 的 paramiko。我选了第三条,因为不想引入新的可执行文件。


## 二、第一次安装就翻车


```powershell

python -m pip install --target D:\work\_libs paramiko

```


装完 import 直接报错:


```

ImportError: DLL load failed while importing _rust:

找不到指定的程序。

```


原因是新版 paramiko 依赖 cryptography 43+,而 cryptography 41 之后把 OpenSSL 绑定换成了 Rust 实现(`_rust.pyd`),这个二进制对 Windows 运行库版本有要求,老一点的 Python 3.9 环境上容易加载失败。


### 解法:退回纯 C 实现的版本


cryptography 3.4.x 还是 CFFI + OpenSSL 的老架构,没有 Rust 依赖:


```powershell

python -m pip install --target D:\work\_libs ^

    "cryptography==3.4.8" ^

    "paramiko==2.11.0" ^

    "bcrypt==3.2.0" ^

    "pynacl==1.5.0"

```


`--target` 很关键:所有包装进独立目录,不污染全局 site-packages,用完删掉目录就干净了。脚本里手动加路径:


```python

import sys

sys.path.insert(0, r"D:\work\_libs")

import paramiko

print(paramiko.__version__) # 2.11.0

```


## 三、密码不要进代码,也不要进日志


最容易犯的错是把密码写进脚本或者当命令行参数传:


```powershell

# 反例:密码会出现在进程列表、命令历史、CI 日志里

python deploy.py --password mypassword

```


改成「密码只存在于一个文件,由子进程自己读」:


```python

import sys

sys.path.insert(0, r"D:\work\_libs")

import paramiko


secret_file, host, user, cmd = sys.argv[1], sys.argv[2], sys.argv[3], sys.argv[4]


with open(secret_file, "r", encoding="utf-8-sig") as f:

    pwd = f.read().strip()


c = paramiko.SSHClient()

c.set_missing_host_key_policy(paramiko.AutoAddPolicy())

try:

    c.connect(host, port=22, username=user, password=pwd,

              timeout=20, look_for_keys=False, allow_agent=False)

finally:

    del pwd


stdin, stdout, stderr = c.exec_command(cmd, timeout=90)

print(stdout.read().decode("utf-8", "replace"))

c.close()

```


调用方只传文件路径:


```powershell

python ssh_run.py D:\work\.secrets\host-a.txt 10.0.0.1 root "ss -tlnp"

```


这样密码不出现在 `argv`、不出现在日志、也不出现在任何输出里。`utf-8-sig` 是为了吃掉记事本存文件时加的 BOM,不加会多一个不可见字符导致认证失败——这个坑很隐蔽,报错只有一句 `Authentication failed`。


## 四、连不上时先分清是「网络」还是「认证」


`Authentication failed` 这个报错信息量太少。可以先只做 SSH 握手,把服务器支持的认证方式列出来:


```python

import socket, paramiko


sock = socket.create_connection((host, 22), timeout=15)

t = paramiko.Transport(sock)

t.start_client(timeout=20)

print("banner:", t.remote_version)


try:

    t.auth_none("root")

except paramiko.BadAuthenticationType as e:

    print("allowed:", e.allowed_types) # 例如 ['publickey', 'password']

```


`auth_none` 一定失败,但失败时抛出的异常里带着 `allowed_types`,一眼就能看出服务器到底支不支持密码登录、还是只允许密钥。这比反复试密码快得多,也不会因为多次失败触发 fail2ban。


顺带还能拿到 `remote_version`,比如 `SSH-2.0-OpenSSH_8.9p1 Ubuntu-3ubuntu0.16`,直接确认对端系统。


## 五、几个实用细节


1. **`look_for_keys=False, allow_agent=False`**:不关掉的话 paramiko 会先去翻 `~/.ssh` 和 ssh-agent,机器上恰好有别的 key 时会绕一大圈才落到密码认证,慢且容易误用错 key。

2. **`banner_timeout` / `auth_timeout`** 单独设:跨公网时默认超时经常不够,表现是连接卡住而不是报错。

3. **`AutoAddPolicy`** 只适合内网或一次性脚本;生产环境应该预置 `known_hosts`,否则等于放弃中间人防护。

4. **认证成功后再 `del pwd`**:减少密码在内存里的存活时间,虽然不能彻底防内存 dump,但至少不会被顺手打印出来。


## 六、小结


在 Windows 上做 SSH 自动化,paramiko 仍然是成本最低的方案,代价是要处理好依赖版本。真正值得记住的是两条:


- 依赖装到独立目录,别污染全局环境;

- 密码走文件、由子进程读取,永远不进命令行参数和日志。


这两条做到了,剩下的就是普通的 Python 编程。


【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。