简介:SecureCRT 与 SecureFX 绿色便携整合包面向需要频繁管理多台服务器、兼顾远程登录与安全文件传输的运维人员、开发者和网络学习者,可在保持 SSH 会话的情况下相互调用,减少终端与 SFTP 工具之间的切换成本。压缩包约 11.8MB,资源页文件总数标记为 0、主要类型明细暂无,结合描述可判断为便携版可执行程序及相关运行文件组合,适合随身携带或临时排障使用。资源已有 6001 人学习下载,在 SSH 客户端与 SFTP 传输场景中具备一定关注度。使用者可借此体验多标签 SSH 会话、SCP/SSL 等安全协议连接,以及较完整的 SFTP 文件管理能力,并了解终端管理与文件传输协同的工作方式。
1. 为什么还有人守着 SecureCRT:一个 200 台设备运维场景的选型复盘
机房搬迁那周,我手上压着 217 台网络设备和 Linux 主机的清单,要在 48 小时内逐台确认版本、时钟、磁盘和配置备份状态。当时试了三个终端工具,最后留在一台老工作站上的还是 SecureCRT——不是因为情怀,是因为它的会话树、脚本引擎和日志落盘这三件事凑在一起,能让我把重复动作压成一条命令跑完。SecureCRT 是一款商业 SSH/串口终端仿真软件,支持 SSH2、Telnet、Serial、RDP 等协议,核心价值在于会话组织、密钥认证管理和可编程的自动化脚本。它适合谁?每天要连十几台以上设备、需要稳定日志和批量巡检的运维、网络工程师;如果你一周只连两次服务器,那真是杀鸡用牛刀。至于网上那些来路不明的安装包,我拆过几个,捆绑和改过的二进制不值得拿生产环境去赌,后面会讲清楚为什么。
2. 会话目录与批量下发:把 200 条连接配置变成一份模板
手工在 GUI 里点 200 次"新建会话",这件事我做过多半次就放弃了。真正省时间的路径是把 SecureCRT 的会话配置文件当成代码来管——它本来就是纯文本。
2.1 先搞清楚配置到底存在哪
SecureCRT 的配置分两层:全局配置和单会话配置,两者都在用户的配置目录下,而不是安装目录。这一点很关键,很多人重装软件后找不到自己的会话,是因为只备份了Program Files。
| 平台 | 配置根目录(示例) |
|---|---|
| Windows | %APPDATA%\<厂商目录>\Config |
| macOS | ~/Library/Application Support/<厂商目录>/Config |
| Linux | ~/.config/<厂商目录>/Config |
Config下有三个东西要记住:
Sessions/—— 每个会话一个.ini文件,子目录结构 = 会话管理器里的文件夹树Global.ini—— 全局默认值,新建会话时继承这里Scripts/(可选)—— 脚本存放位置,可以在全局选项里改
我一般的做法是把这个Config目录整体丢进 Git 仓库。会话文件是文本,diff 出来一目了然,谁改了哪个设备的端口、谁把某个会话的编码从 UTF-8 改成 GBK,都能追溯到。配置目录支持自定义路径,团队里如果有多套环境(生产/测试/客户现场),我建议用不同的配置目录切换,而不是靠命名前缀区分——前缀迟早会乱。
提示:会话
.ini里如果保存了密码,那个字段和当前机器绑定加密,拷到另一台电脑上大概率解不开。所以配置目录版本化时,要么用密钥认证不存密码,要么在.gitignore里把敏感会话排除掉。
2.2 会话 ini 的字段结构,一次拆干净
不要把.ini当成标准 INI 来解析。它用的是厂商自己的键值格式,每行形如S:"Key"=Value、D:"Key"=Value、B:"Key"=Value,前缀字母表示值类型(字符串、双字/整数、布尔),没有[section]头。用configparser去读会直接报错。
字段名在不同大版本之间会有出入,最稳的办法是:先在 GUI 里手动建一个"样板会话",把该配的都配好,然后打开这份文件照着抄。常见的几个字段:
| 字段 | 含义 | 备注 |
|---|---|---|
S:"Protocol Name" | 协议类型 | SSH2、Telnet、Serial |
S:"Hostname" | 主机地址 | IP 或域名 |
S:"Username" | 登录用户名 | 空则连接时询问 |
D:"[SSH2] Port" | SSH 端口 | 十六进制,22 →00000016 |
S:"Authentication" | 认证方式及顺序 | PublicKey,Password,Keyboard Interactive |
B:"... | 各类布尔开关 | 如是否记录日志、是否压缩 |
端口这个十六进制是最容易翻车的地方。我见过有人直接把22写进去,结果会话连的是 34 端口,排查了半天以为是防火墙策略。转换很简单:format(port, "08X"),22 得到00000016,2222 得到000008AE。
认证顺序那一行也有讲究——SecureCRT 是按列表从左到右尝试的。如果你把Password放在PublicKey前面,每次连接都会先拿密码去撞一次,某些做了失败次数限制的设备会直接把你锁在外面。
2.3 用 Python 批量生成会话文件
有了字段结构,剩下的就是把一份 CSV 设备清单喂给模板。下面这段脚本我用了很多次,改改路径就能跑。
import csv import os from pathlib import Path # 会话配置根目录(Windows 示例,其他平台换成 2.1 里对应的路径) SESSIONS_ROOT = Path(os.environ["APPDATA"]) / "<厂商目录>" / "Config" / "Sessions" # 会话文件模板:键值前缀必须保留,末尾留一个空行 TEMPLATE = """S:"Protocol Name"=SSH2 S:"Hostname"={host} S:"Username"={user} D:"[SSH2] Port"={port_hex} S:"Authentication"=PublicKey,Password,Keyboard Interactive B:"Compression"=00000000 B:"[SSH2] Send Protocol Keepalive"=00000001 D:"[SSH2] Protocol Keepalive Interval"=0000003C """ def port_to_hex(port: int) -> str: """端口转 SecureCRT 使用的 8 位十六进制表示:22 -> 00000016""" if not 1 <= port <= 65535: raise ValueError(f"端口越界: {port}") return format(port, "08X") def parse_auth(raw: str) -> str: """把 'key,password' 这种简写展开成 SecureCRT 认识的认证顺序""" mapping = { "key": "PublicKey", "password": "Password", "kbd": "Keyboard Interactive", } items = [mapping.get(x.strip().lower(), x.strip()) for x in raw.split(",") if x.strip()] if not items: raise ValueError("认证方式不能为空") return ",".join(items) def build_session(row: dict, group: str) -> Path: """按一行记录生成一个会话文件,返回写入路径""" name = row["name"].strip() target_dir = SESSIONS_ROOT / group target_dir.mkdir(parents=True, exist_ok=True) target = target_dir / f"{name}.ini" content = TEMPLATE.format( host=row["host"].strip(), user=row["user"].strip(), port_hex=port_to_hex(int(row.get("port", 22))), ) # 统一使用 \r\n,Windows 下 GUI 读取更稳 target.write_text(content, encoding="utf-8", newline="\r\n") return target def main(csv_path: str) -> None: created, skipped = 0, 0 with open(csv_path, newline="", encoding="utf-8-sig") as f: for row in csv.DictReader(f): group = row.get("group", "default").strip() or "default" try: path = build_session(row, group) created += 1 print(f"[OK] {path}") except Exception as e: # 单条失败不影响整批 skipped += 1 print(f"[SKIP] {row.get('name')}: {e}") print(f"done. created={created} skipped={skipped}") if __name__ == "__main__": main("devices.csv")配套的devices.csv长这样:
name,group,host,user,port,auth core-sw-01,网络设备/核心,10.10.1.1,netops,22,key edge-fw-01,安全设备,10.10.2.1,secops,2222,key,password app-node-07,服务器/生产,10.10.3.7,deploy,22,key逻辑说明:build_session只负责一次渲染,失败了抛异常;main里逐条捕获,这样 217 条里挂掉两条不会让整批白跑。encoding="utf-8-sig"是给 Excel 导出的 CSV 留的余地,不然首列会带 BOM 变成乱码。newline="\r\n"强制 CRLF,避免某些版本读配置时行尾识别异常。
参数说明:SESSIONS_ROOT一定要写绝对路径,用环境变量拼;port_to_hex里的越界检查别删,我吃过一次手滑写成22222的亏;认证顺序按你的安全策略排,做密钥认证的环境永远把PublicKey放第一位,这样密码登录失败次数不会被白白消耗。
跑完脚本,重启 SecureCRT,会话树里就自动出现分组和条目了。如果没出现,先确认进程已经完全退出——它启动时读一次配置,运行期间改了不刷新。
2.4 导入导出与团队复用
SecureCRT 自带的导入导出走的是菜单里的配置迁移入口,本质还是打包整个Config目录。团队里我更喜欢直接发 Git 仓库:新同事克隆下来,在全局选项里把配置目录指过去,重启即生效,比发一个压缩包再让他手动找路径可靠得多。
有两个细节要交代清楚。第一,跨平台迁移时路径分隔符不同,会话文件里如果写了带绝对路径的日志目录,换平台会失效,所以日志路径尽量用相对路径或者干脆在全局配置里统一设。第二,配置目录里还有一个记录已打开会话的清单文件,迁移时最好把它删掉,否则新机器打开会尝试恢复一堆不存在的标签页,卡在那儿转圈。
3. 密钥认证全流程:从生成到禁用密码登录
密码认证在两百台设备的规模上就是个灾难:密码要轮换、要分发、要记在某个地方,而且每一次自动化尝试都会在日志里留一条失败记录。密钥是唯一能让"人和脚本用同一套凭据"的方案。
3.1 为什么必须走密钥,密码登录的边界在哪
先说清楚边界。密钥不是万能的:老设备可能只支持 RSA 且只认 SHA-1 签名,新版本客户端默认不发送这类算法;某些设备的 SSH 实现有 bug,公钥认证会莫名失败;还有一类设备根本不支持从 authorized_keys 读公钥,只能在 Web 界面里粘。所以"禁用密码登录"这个动作,一定是所有设备都验证过密钥可用之后才做的,顺序反了就把自己关在门外。
至于密钥类型,我的选择顺序是:设备支持就用 Ed25519(短、快、安全性好),不支持退到 ECDSA P-256,再不行才用 RSA 4096。很多网络设备固件较老,只认 RSA,这个没办法,兼容优先。
3.2 在客户端生成密钥并导出 OpenSSH 公钥
SecureCRT 自带密钥生成工具,路径在菜单的工具项下面,按向导走:选类型 → 选长度 → 设口令 → 设注释 → 生成。生成出来的密钥对默认放在配置目录的密钥子目录里。
我一般会同时用命令行再生成一份,方便在脚本里引用:
# 生成 Ed25519 密钥对,-C 是注释,方便在服务端识别来源 ssh-keygen -t ed25519 -a 100 -C "ops@workstation" -f ~/.ssh/id_ed25519_ops # 查看 OpenSSH 格式公钥,SSH2 格式(RFC4716)在这里派不上用场 cat ~/.ssh/id_ed25519_ops.pub-a 100是 KDF 轮数,设高一点能提高口令暴力破解成本,代价是每次使用多等几百毫秒,我一般设 64~100。生成的私钥权限必须是 600,ssh-keygen会自动做,但如果你从别处拷贝过来,记得手动chmod 600。
SecureCRT 里还有个细节:它支持"全局密钥"和"会话密钥"两套。全局密钥放在全局选项里,所有没单独指定密钥的会话都用它;会话级密钥覆盖全局。团队共用一台机器时我偏向用会话级,避免一个人换密钥影响所有人。
3.3 服务端 authorized_keys 的权限与坑
公钥怎么落地,取决于目标是什么系统。Linux 主机:
# 建目录、设权限,两步都不能省 mkdir -p ~/.ssh && chmod 700 ~/.ssh # 追加公钥,用 here-doc 避免手抖漏字符 cat >> ~/.ssh/authorized_keys <<'EOF' ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAI... ops@workstation EOF chmod 600 ~/.ssh/authorized_keys # 检查 home 目录权限,组或其他可写会导致公钥认证被拒 ls -ld ~最后一行的ls -ld ~是血泪经验。sshd对权限的检查是层层递进的:~不能对 group/other 可写,.ssh必须是 700,authorized_keys必须是 600,任何一层不满足,日志里只有一句干巴巴的Authentication refused: bad ownership or modes,不告诉你具体是哪一层。
SELinux 开着的话还有一道:把文件拷过去而不是在目标机上创建,上下文可能不对,restorecon -Rv ~/.ssh能修。
网络设备这边就简单粗暴一些:进配置,把公钥粘到对应用户的认证配置下,指定认证方式为公钥。不同厂商命令差异很大,但套路一致——先建用户,再挂公钥,最后改认证方式。改之前一定要留一个密码登录的账号做后备。
3.4 会话里绑定密钥并验证
GUI 路径:打开会话属性 → 连接 → SSH2 → 认证 → 在列表里添加PublicKey→ 选中后点属性 → 指定私钥文件 →用右侧的上下箭头把它拖到第一位。
绑定完别急着欢呼,先用命令行验一遍,这是我最常用的对照手段:
# -vvv 打出完整握手过程,重点看 "Offering public key" 和 "Server accepts key" ssh -vvv -i ~/.ssh/id_ed25519_ops \ -o PreferredAuthentications=publickey \ -o IdentitiesOnly=yes \ ops@10.10.3.7-o PreferredAuthentications=publickey强制只走公钥,排除了"其实是密码登录成功"的假象。-o IdentitiesOnly=yes保证只用指定的这把私钥,而不是把 agent 里所有密钥挨个试一遍——后者在老设备上特别容易触发Too many authentication failures,因为默认最多试 6 把。
对着-vvv的输出可以判断问题出在哪一层:
| 输出片段 | 含义 | 下一步 |
|---|---|---|
Offering public key后无Server accepts key | 服务端不认这把公钥 | 检查authorized_keys内容与权限 |
no mutual signature algorithm | 算法不匹配(常见于 RSA + SHA-1) | 客户端放行旧算法或换密钥类型 |
Too many authentication failures | 试了太多把密钥 | 加IdentitiesOnly=yes |
Permission denied (publickey)但日志无拒绝记录 | 用户名或路径不对 | 核对用户名与 home 目录 |
命令行通了,GUI 里基本就通了。如果 GUI 不通而命令行通,八成是会话里的私钥路径指错了,或者认证列表顺序没调。
4. 脚本自动化:用 Python 和 VBScript 把巡检跑成一条命令
会话管理解决的是"连得上",脚本解决的是"连上之后自动干什么"。SecureCRT 的脚本能力是它和普通终端拉开差距的地方,也是很多人买了却从来没用过的功能。
4.1 SecureCRT 的脚本对象模型
脚本运行在终端进程内,通过一个全局对象拿到控制权。开头两行是元数据声明,不能删也不能改格式:
# $language = "Python" # $interface = "1.0"这两行告诉终端用哪个引擎解释这个文件。Python 脚本依赖系统里装了对应版本的 Python,装了多个版本时要注意 PATH 指向哪一个;VBScript 走的是 Windows 内置引擎,不需要额外安装,所以在只能用 Windows 客户端的环境里 VBScript 反而更省事。
核心对象就那么几个:
| 对象/方法 | 作用 | 注意 |
|---|---|---|
crt.Screen.Synchronous | 同步模式开关 | 必须设 True,否则 Send 后立刻返回,读到的全是空 |
crt.Screen.Send(str) | 发送字符串 | 行尾用\r,不是\n |
crt.Screen.WaitForString(s, t) | 等指定字符串出现 | 返回布尔值,超时返回 False 但不抛异常 |
crt.Screen.ReadString(s, t) | 读到指定字符串为止 | 返回中间的内容 |
crt.Session.Log(True) | 开日志 | 配合LogFileName指定路径 |
crt.Session.ConnectInTab(cmd) | 在新标签页建连接 | 批量场景的关键 |
crt.Dialog.MessageBox(s) | 弹框 | 调试期最好用的一招 |
crt.Arguments.GetArg(n) | 取命令行参数 | 同一个脚本跑不同设备靠它 |
Synchronous这个开关是新手最容易漏的。不开同步,Send是异步的,下一行ReadString可能在设备还没回显时就开始读,结果拿到空字符串,脚本看起来"跑了但什么都没抓到"。
4.2 一段能跑的巡检脚本
下面这段是我现在还在用的精简版,作用是连上设备后依次执行几条命令,把回显落到本地日志,并做最基本的异常判断。
# $language = "Python" # $interface = "1.0" import os import time LOG_DIR = r"D:\ops\logs" def safe_name(raw: str) -> str: """把主机名/地址洗成安全的文件名,避免出现非法字符""" return "".join(c if c.isalnum() or c in "-_." else "_" for c in raw) def run_cmd(cmd: str, prompt: str, timeout: int = 20) -> str: """发一条命令并等待提示符,返回中间的回显;超时返回空串""" crt.Screen.Send(cmd + "\r") if not crt.Screen.WaitForString(prompt, timeout): return "" return crt.Screen.ReadString(prompt, timeout) def main() -> None: # 同步模式 + 忽略 ESC,避免设备回显控制字符打乱匹配 crt.Screen.Synchronous = True crt.Screen.IgnoreEscape = True host = crt.Arguments.GetArg(1) or "unknown" os.makedirs(LOG_DIR, exist_ok=True) log_path = os.path.join(LOG_DIR, safe_name(host) + ".log") crt.Session.LogFileName = log_path crt.Session.Log(True) results = [] for cmd, prompt in [ ("show version", "#"), ("show clock", "#"), ("show interfaces status", "#"), ]: out = run_cmd(cmd, prompt) if not out: results.append(f"{cmd} -> TIMEOUT") else: results.append(f"{cmd} -> {len(out.splitlines())} lines") crt.Session.Log(False) crt.Dialog.MessageBox("\n".join(results)) main()逻辑说明:run_cmd把"发命令—等提示符—读回显"三步封成一个原子操作,任何一步失败返回空串,调用方据此判断超时。主流程先开日志再执行,保证即使脚本中途崩了,前面的回显也落盘了。最后用弹框把每条命令的行数汇总出来,比翻日志快。
参数说明:timeout=20是经验值,跑show interfaces这种长输出命令时,慢设备可能要 40 秒以上,按实际调;prompt必须和设备的特权提示符一致,有的是#,有的是>,有的是[hostname],写错就永远等不到。crt.Arguments.GetArg(1)的下标从 1 开始,0 是脚本文件名本身。
调用方式:在会话窗口里执行脚本时传参,或者用命令行启动终端时带上参数。批量场景下我一般写一个外层清单,逐台ConnectInTab再逐个执行。
4.3 批量并发与日志落盘
SecureCRT 的脚本是单线程的,一个脚本实例只能操作它所在的那个会话。真要做并发,有两条路:
| 方案 | 优点 | 代价 |
|---|---|---|
| 脚本内多标签循环 | 不依赖外部环境,日志统一 | 串行,200 台要跑很久 |
| 外部 Python + SSH 库并发 | 真并发,可控线程数 | 需要另装依赖,交互式排障不方便 |
我常用的组合是:交互式排障用 SecureCRT,批量采集用外部脚本。两边共用同一套密钥和同一份设备清单,环境一致,结果可以互相印证。
日志落盘这块有个参数值得单独说:日志文件名支持时间占位符,我一般设成%H_%Y%M%D.log这种带主机名和日期的形式。不带日期的后果是每次覆盖,出了问题想翻三天前的记录,没有。
4.4 脚本调试与常见失败点
脚本报错时最烦的是没有堆栈。我的做法是在可疑位置插crt.Dialog.MessageBox(str(变量)),虽然土,但立刻能看到值。另外注意脚本运行在终端进程里,print的输出你看不见,别指望控制台。
几个高频失败点:
- 忘了
Synchronous = True,读到的全是空 - 行尾用了
\n,设备收到的是换行符而不是回车,命令不执行 WaitForString超时返回 False 但代码不检查,后面继续跑,日志里一片空白- 提示符匹配串写得太宽(比如只写
>),被回显里的其他字符提前命中,读到的内容截断
最后一个坑我踩过:设备回显里有--More--分页提示,脚本等的是#,结果一直等到超时。解决办法是在run_cmd里加一层分页处理,检测到--More--就发空格。
5. 避坑与排查:五条血泪记录
这一章全是我在实际环境里翻过的车,按"现象 → 原因 → 解决"记下来。
5.1 中文回显变成问号或方块
现象:连上某台老设备,show出来的中文全是问号,英文正常。
原因:会话的字符编码设的是 UTF-8,而设备输出的是 GBK。SecureCRT 默认按 UTF-8 解码,遇到 GBK 字节就替换成占位符。
解决:会话属性 → 终端 → 外观 → 字符编码,改成 GBK 或 GB2312。更好的做法是设备端统一改成 UTF-8,但老设备改编码可能影响其他系统对接,得权衡。改完记得重连会话,编码是在连接时生效的。
5.2 配置目录拷到新电脑,会话全部认证失败
现象:换了台工作站,把整个 Config 目录拷过去,双击会话提示认证失败,密码框里的星号还在,但就是登不上。
原因:会话里保存的密码用当前机器的信息做了加密,跨机器解不开。GUI 里显示星号是因为它只存了掩码,实际解密失败后发出去的是空串。
解决:不要依赖会话里存密码。改成会话级密钥认证,密码框留空。如果非要用密码,迁移后逐台重新输入——两百台的话,还是老老实实上密钥吧。这也是我坚持推密钥认证的现实理由之一,跟安全理念关系不大,纯粹是迁移成本。
5.3 密钥配好了还是提示认证失败
现象:公钥已经追加到authorized_keys,ssh -i还是Permission denied。
原因:按概率排序,①~/.ssh或authorized_keys权限过宽,sshd直接拒绝;② home 目录对 group 可写;③ SELinux 上下文不对;④ 客户端默认不发送ssh-rsa签名,而设备只认这个。
解决:先看服务端日志,/var/log/secure或journalctl -u sshd,里面会写清楚是哪条规则拒绝的。权限问题按chmod 700 ~/.ssh && chmod 600 ~/.ssh/authorized_keys修;SELinux 用restorecon -Rv ~/.ssh;算法问题在客户端显式放行旧算法,或者在服务端把设备加进兼容列表。实在定位不了,ssh -vvv的输出从下往上读,第一处异常就是根因。
5.4 挂机十分钟回来会话已经断开
现象:开会回来,标签页显示"已断开连接",需要重新登录,历史回显也没了。
原因:路径上的 NAT 设备或防火墙会话表老化,空闲连接被静默回收。默认老化时间常见的是 5~30 分钟,正好覆盖一次会议。
解决:两头都设。服务端在sshd_config里加ClientAliveInterval 60和ClientAliveCountMax 3,让服务端主动发探测;客户端在会话属性的连接页开启协议层 keepalive,间隔设 60 秒。另外终端层面的 TCP keepalive 也打开,双保险。别把间隔设得太短,1 秒一次会被某些设备的防护策略当成异常流量。
5.5 某天终端起不来,C 盘红了
现象:早上打开终端,卡在启动画面,任务管理器里 CPU 占用正常,磁盘占用 100%。
原因:全局默认会话里开了自动保存日志,路径是用户目录下的默认位置,跑了半年,日志文件堆了几十 GB,系统盘写满。
解决:全局选项 → 默认会话 → 日志文件,把路径改到非系统盘,文件名带上日期占位符,同时开"追加到文件"而不是覆盖。再配一个计划任务,每周清理 30 天前的日志。这件事我现在的做法是装完就配,不等出事——后悔药没处买。
6. 授权、迁移与验证:把工具链固定下来的一套方法
关于授权,把话说在前面:这是一款商业软件,官方提供限期试用,长期使用需要购买许可,团队规模大时按并发用户数授权。至于网上流传的所谓绿色版、注册机,我不建议碰——这类二进制被改过签名,你没法确认里面多了什么,而终端软件恰恰握着所有设备的凭据。真要控制成本,先算清楚替代方案能不能覆盖你的场景。
替代品的对照,我按实际用过的排:
| 工具 | 授权 | 会话树 | 脚本能力 | 适合场景 |
|---|---|---|---|---|
| SecureCRT | 商业 | 强 | Python/VBScript/Perl | 大规模设备、需要自动化 |
| WindTerm | 开源 | 中 | 有限 | 日常连接,轻量 |
| Tabby | 开源 | 中 | 插件式 | 跨平台、界面现代 |
| Windows Terminal + OpenSSH | 免费 | 弱 | 靠 shell 脚本 | 纯 Linux 主机、命令行流 |
| PuTTY | 开源 | 弱 | 无 | 临时连接、串口 |
迁移验证不能靠"点开看看能连上",我列了一份清单,逐项打勾:
import paramiko HOSTS = [("10.10.3.7", "deploy"), ("10.10.1.1", "netops")] def verify(host, user, key_path="~/.ssh/id_ed25519_ops"): """只用公钥认证连一次,执行一条无害命令,验证凭据链路""" client = paramiko.SSHClient() client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) try: client.connect( hostname=host, username=user, key_filename=key_path, look_for_keys=False, # 不扫描本地其他密钥,避免触发失败锁定 allow_agent=False, timeout=8, auth_timeout=8, banner_timeout=8, ) _, stdout, _ = client.exec_command("echo ok", timeout=5) return stdout.read().decode().strip() == "ok" except Exception as e: print(f"FAIL {host}: {type(e).__name__}: {e}") return False finally: client.close() for h, u in HOSTS: print(h, "PASS" if verify(h, u) else "FAIL")验证清单我固定看六项:密钥登录可用、密码登录已按策略关闭、会话日志能正常落盘、字符编码正确、keepalive 生效不断连、脚本能跑通一条无害命令。前两项决定能不能进门,后四项决定进门之后顺不顺手。
look_for_keys=False和allow_agent=False这两个参数别省,它们保证只用你指定的那把密钥,不会因为本地还有其他密钥而触发服务端的失败次数限制——这个坑我在一批老设备上连着栽过三次,每次都是"明明密钥是对的但就是被拒"。
从那以后我每次做终端工具迁移或者密钥轮换,都会先拿这份清单在 3 台代表性设备上跑一遍,全绿了才敢推到全部。工具链这种事,出问题时往往不是在你能看见的地方。希望帮到你。
本文还有配套的精品资源,点击获取