☰
CentOS离线安装sshpass:RPM与源码编译实战
2026/10/1 2:15:05 网站建设 项目流程

说个最近实际碰到的事。公司内网的一批 CentOS 7.9 机器被安全策略隔离在专网里,没有外网权限,也不允许随意插拔U盘。这批机器要做 SSH 公钥批量分发,跑 ssh-copy-id 时每台机器都要手动输密码验证,几十台下来手都快废了。这时候我第一个想到的就是 sshpass,一个专门用来给 SSH 非交互式登录提供密码的工具。但麻烦点在于:机器上一台都没装,而且没有外网,yum install 直接卡死在仓库连接上。这就是典型的 CentOS 离线安装场景,核心需求其实不复杂,难的是把安装包和依赖一起搬运进去。

下面我把这类隔离网络环境下反复验证过的离线安装方法完整梳理一遍。包括 RPM 包方案、源码编译方案,以及装完以后怎么快速验证、怎么在批量运维脚本里正确使用。不管目标是 CentOS 7.9 还是 CentOS 8/9,只要系统架构一致,下面的方法基本都能直接套用。正在做内网运维、堡垒机前置部署或者批量主机初始化的同学,可以直接照着操作。

1. sshpass解决什么问题,以及离线安装为什么会有坑

1.1 先搞懂sshpass的定位

sshpass 的作用非常直白:给 OpenSSH 的非交互式调用提供密码输入通道。平时我们执行 ssh user@host,系统会从终端读取密码,这个过程没法直接在一条命令里预先输入。而 sshpass 通过伪终端或环境变量的方式,把密码“喂”给 ssh 进程,从而实现一条命令完成带密码的 SSH/SCP 登录,特别适合在脚本里批量操作。

最常见的几个用法:

  • sshpass -p 'password' ssh user@host
  • sshpass -p 'password' scp file user@host:/path/
  • sshpass -p 'password' ssh-copy-id user@host

其中第三个是我用得最多的场景。ssh-copy-id 本身需要交互式输入密码,配合 sshpass 后就能在批量初始化服务器时把公钥一次性推过去。第一次接触这个工具的人可能会问:为什么不直接用 expect?expect 也做得到,但要写 spawn/expect/send 的交互逻辑,脚本冗长不说,调试也麻烦。sshpass 的存在就是把“给 ssh 进程喂密码”这件事专门抽象出来,做成了一个小而精的命令行工具。

1.2 离线安装的坑点

“离线安装”这四个字真正操作起来,卡点通常在两个地方。

第一,没有 yum 源。在线环境下 yum install sshpass 一行命令搞定,顶多等几秒下载。内网环境下,yum 仓库地址不可达,dnf/yum 会长时间超时甚至直接报错。报错信息看着吓人,其实本质就是“找不到仓库”。

第二,依赖链不透明。很多人以为下载一个 rpm 包拷过去 rpm -ivh 就行,结果发现机器上缺某个依赖库,安装直接失败。sshpass 本身依赖很轻,但在不同的 CentOS 大版本上,镜像源里的 rpm 包版本和依赖声明并不完全一样,得提前看清楚。

所以离线安装的本质是两件事:把“安装包”准备好,把“依赖关系”前置解决掉。后面讲到的几种方案,本质上都是围绕这两件事展开的。另外还要注意一点,离线环境往往也是版本冻结的,很多时候你没法临时升级系统组件,所以安装包的选择必须匹配当前的系统版本。

2. 离线安装前的准备工作:版本确认与依赖梳理

2.1 第一步是确认系统版本和架构

不要一上来就找安装包。先登录目标机器,看清系统版本和 CPU 架构。最简单的方式:

cat /etc/redhat-release uname -m

输出结果会类似CentOS Linux release 7.9.2009 (Core)和x86_64。这两个信息决定了你要下载哪个版本的 rpm,以及后续编译时要用的工具链。x86_64 是最常见的,但公司里难免有 aarch64 的 ARM 机器,千万不要拿 x86_64 的 rpm 去装 ARM 机器,否则会报wrong ELF class之类的错误,看起来一脸懵,其实就是架构不匹配。

还有一点容易被忽略:CentOS 7 的 rpm 包不能直接用在 CentOS 8 上,反过来也一样。el7、el8、el9 这些标记就是用来区分大版本的。下载安装包时,一定要看清楚包名里是 el7、el8 还是 el9。

2.2 sshpass 到底依赖什么

我在不同机器上验证过,sshpass 的依赖非常轻。以 CentOS 7 EPEL 里的 sshpass 1.06 为例,用 rpm -qpR 查看依赖:

rpm -qpR sshpass-1.06-1.el7.x86_64.rpm

输出基本只有 glibc 相关项,比如/bin/sh、libc.so.6(GLIBC_2.14)(64bit)等。这些在正常安装的 CentOS 上基本都是现成的。而源码编译方式下,依赖的是 gcc、make 和 libc 开发库。所以如果你选 RPM 方案,大概率不需要额外处理依赖;如果选源码方案,需要先确认机器上有编译工具链。

这里补充一点背景知识:sshpass 早期版本是通过调用 forkpty 来分配一个伪终端,然后把密码写入这个终端,从而骗过 ssh 的密码读取逻辑。这个机制本身依赖的是 linux 的标准库和终端接口,所以它的运行依赖非常少,这也是为什么它能在各种精简版系统上跑起来。

2.3 先规划好安装包的传递路径

离线环境中,安装包怎么送到目标机器,也是一个容易被忽略的环节。常见的几种方式我列在下面:

传递方式适用场景注意事项
scp/sftp网络可达的局域网若目标机器没有安装 openssh-clients,反而要先解决基础问题
内网 HTTP/FTP批量分发搭一个临时文件服务,wget 即可
U盘/移动硬盘物理隔离环境注意文件系统兼容性,优先用 ext4/vfat
内网 NFS 共享已有存储服务器直接挂载读取,适合大量机器同时安装

我个人的习惯是:如果目标机器能通过局域网访问到一台跳板机,就先用 scp 把安装包传到跳板机,再在目标机器上拉取。这个过程中也方便对比文件 MD5,防止传输损坏。文件传输这块,很多人会栽在“文件明明传过去了但 rpm 就是报错”的问题上,多半就是文件在传输中被损坏了。

3. 方案一:RPM包离线安装(首选)

3.1 在联网机器上下载 RPM

在能上外网的机器上,先启用 EPEL 源然后下载 sshpass 的 rpm 包。CentOS 7 默认 base 源里没有 sshpass,EPEL 里才有。一条命令就能把包拉到本地目录而不实际安装:

yum install --downloadonly --downloaddir=/root/sshpass_rpm sshpass

如果提示没有找到包,先确认 EPEL 是否已安装:

yum install epel-release # 再次尝试下载 yum install --downloadonly --downloaddir=/root/sshpass_rpm sshpass

不想用 EPEL 或者没有外网但可以通过浏览器下载的,也可以直接去 EPEL 镜像站或者 rpmfind.net 搜索对应版本。这里要特别注意:一定要选对 CentOS 大版本。CentOS 7 对应 el7 的包,CentOS 8 对应 el8,CentOS 9 对应 el9,混用很容易出现 GLIBC 版本不满足的问题。

下载完之后,在当前目录下应该能看到类似sshpass-1.06-1.el7.x86_64.rpm的文件。用 rpm -K 验证一下包完整性:

rpm -K sshpass-1.06-1.el7.x86_64.rpm

返回OK说明签名校验通过。这一步在安全要求高的内网环境尤其重要,防止安装包被篡改。之前有些同事图省事,随便从一个第三方网站下载 rpm,结果安装后系统出现异常。尽量从官方 EPEL 镜像或者有 GPG 签名的源下载。

3.2 传输到目标机器并校验

然后就是把 rpm 包传到目标机器。我习惯先放到/root/pkgs目录下:

mkdir -p /root/pkgs scp sshpass-1.06-1.el7.x86_64.rpm root@目标IP:/root/pkgs/

如果网络环境不允许直接 scp,用前面提到的 HTTP 或U盘方式也行。传完之后在目标机器上核对一下 MD5:

md5sum sshpass-1.06-1.el7.x86_64.rpm

和源机器上一对比,完全一致再继续,避免传输包损坏导致的诡异报错。这里多说一句,我遇到过 rpm 安装时报package already installed但实际上又不是同一个版本的情况,后来发现是之前有人手动拷贝过旧包,目录里多个 rpm 文件混在一起,导致操作时用错了安装包。所以在目标机器上,尽量把待安装的包单独放一个干净目录。

3.3 目标机器上安装

接下来的安装就很简单了,在目标机器上执行:

rpm -ivh sshpass-1.06-1.el7.x86_64.rpm

正常情况下会输出类似Preparing... ################################# [100%]然后Updating / installing...的信息,说明安装成功。装完之后直接在命令行输入:

sshpass -V

能看到版本号就说明一切正常。

如果出现依赖报错,比如提示缺少某个库,不要急着加--nodeps强制安装。先看缺什么,缺的库是不是机器上真的没有。通常来说 sshpass 的依赖在基础系统里都有,一旦报缺依赖,更多时候是系统本身被裁剪过,比如某些精简版镜像。这种情况下可以先用yum localinstall安装,它会尝试从本地及已配置的源里解析依赖:

yum localinstall -y sshpass-1.06-1.el7.x86_64.rpm

3.4 RPM 方案为什么是首选

从实际运维角度看,RPM 方案比源码编译有太多优势:不需要目标机器上有 gcc/make,安装过程秒级完成,卸载也干净,rpm -e 一条命令即可。离线环境里,能少装一个编译器就少一个风险点,这是我在批量部署时始终坚持的原则。

另外,RPM 方式还有一个隐性好处:它是可追踪的。rpm -qa 能看到包完整列表,出问题方便审计。源码编译方式安装后,如果不小心忘了安装路径,排查起来比较麻烦。对于公司内部的资产管理,RPM 方案显然是更规范的选择。

4. 方案二:源码编译离线安装(准通用方案)

4.1 为什么仍然需要源码方案

RPM 方案虽然好,但有一个前提:你能找到匹配目标系统版本的 rpm 包。如果目标机器是 CentOS Stream、或者是一个特殊的 aarch64 变体、又或者公司安全策略禁止使用来源不明的 rpm,那么源码编译就是更稳妥的兜底方案。源码编译的本质是跨发行版通用的,只要有编译器和基础的 libc 开发库,就能把 sshpass 从源码构建成可执行文件。

我在实际工作中也遇到过一个场景:目标机器是一个特殊定制的 Linux 系统,内核和文件系统都被深度裁剪过,普通的 el7 rpm 装不上,但又确实需要 sshpass 来做自动化。这个时候源码编译几乎是唯一选择。

4.2 准备源码包与编译工具

先在联网机器上下载 sshpass 源码包:

wget https://sourceforge.net/projects/sshpass/files/sshpass/1.10/sshpass-1.10.tar.gz

下载后同样传到目标机器,然后确认编译工具是否齐备:

which gcc which make gcc --version

如果离线机器上没有 gcc,这会是一个比较大的问题。要么想办法从系统安装镜像的 packages 目录里找到 gcc 及其依赖 rpm 离线安装,要么直接放弃源码方案改走 RPM。我遇到过几台机器没有 gcc,最终是在系统 ISO 镜像包里找到 rpm 包,再用 rpm -ivh 按依赖顺序装上,过程比较繁琐。

这里补充一个排查技巧:如果系统连 yum 都没有,但保留着本地 rpm 目录,可以用rpm -qa | grep gcc快速判断 gcc 是否已装。另外,即使没有 gcc,有些精简系统也会带 cc(C 编译器的一个别名),可以先用which cc碰碰运气。

4.3 编译安装过程

在目标机器上解压并编译:

tar -zxf sshpass-1.10.tar.gz cd sshpass-1.10 ./configure --prefix=/usr/local make make install

./configure阶段会检查系统是否有 ssh 命令、是否有可用的编译器。如果报错configure: error: no acceptable C compiler found in $PATH,那就是 gcc 没装。如果报错configure: error: SSH client is required,说明系统里没有 openssh-clients,这个在标准 CentOS 上很少见,但精简镜像里确实可能碰上。

编译完成后,默认安装路径是/usr/local/bin/sshpass。检查一下:

/usr/local/bin/sshpass -V

sshpass 的源码包很小,编译时间通常几秒钟就完成,远比编译其他大型项目轻松。

4.4 动态库与路径问题

源码编译还有一个容易被忽略的细节:如果你编译时使用了某些额外的动态库,安装后运行可能报error while loading shared libraries。不过 sshpass 本身几乎不依赖额外动态库,所以这个概率很低。真要遇到,用 ldd 查一下:

ldd /usr/local/bin/sshpass

确认缺失项后,把相关库的位置写进/etc/ld.so.conf.d/下的配置文件,再执行ldconfig即可。这个操作步骤虽然简单,但你真在批量部署时遇到过一次,就会理解为什么我总说离线环境最怕的就是“隐藏依赖”。

另外,./configure --prefix=/usr/local只是指定了安装前缀,并不影响运行时动态库的搜索路径。如果你在编译时通过CFLAGS指到了自定义的库路径,记得在ld.so.conf里也加上对应路径,否则会出现“编译能过、运行找不到”的尴尬情况。

4.5 源码方案的可复制性

源码编译的好处是:只要源码包和编译器具备,几乎不挑发行版,CentOS 7、8、9、Stream 都能用同一个 tar 包编译。缺点也很明显:编译时间稍长(sshpass 很小,其实也就几秒)、需要开发工具链、后续 rpm -e 无法干净卸载。所以它是 RPM 方案的补充,而不是替代。

有一个折中思路也值得考虑:在一台联网的机器上用源码方式编译,然后把编好的二进制文件直接拷贝到其他相同架构、相同系统的机器上。前提是 glibc 和系统库版本保持一致,否则二进制文件可能因为找不到匹配的库而无法运行。这个方式我在 CentOS 7 的同版本机器间测试过,是可以跑的,但只在确实没法装 rpm 的情况下才推荐。

5. 安装后的验证与批量运维实战

5.1 验证安装是否成功

无论用哪种方式安装,装完都要做一次功能验证。最保险的验证方式是先测试一次本地 SSH 免交互登录:

sshpass -p '你的密码' ssh -o StrictHostKeyChecking=no root@127.0.0.1 'hostname'

这里加-o StrictHostKeyChecking=no是为了避免首次连接时的公钥指纹确认交互。正常情况下,这条命令会直接返回 hostname,而不需要手动输入密码。如果返回的是密码错误或认证失败,先确认密码是否正确、目标机的 sshd 是否允许对应认证方式。

如果系统开启了堡垒机或者 PAM 模块,测试的时候还要注意,某些环境会对 root 的密码登录做额外限制,导致Permission denied,这时不是 sshpass 的问题,而是系统策略的问题。

5.2 批量分发 SSH 公钥实战

离线安装 sshpass 的最终目的,通常是规模化运维。我分享一个常用的批量初始化脚本思路:

#!/bin/bash SERVER_LIST=/root/servers.txt PASSWORD='TemporaryInitPass123' while read ip; do sshpass -p "$PASSWORD" ssh-copy-id -o StrictHostKeyChecking=no root@"$ip" done < "$SERVER_LIST"

servers.txt 里每行一个 IP。这个脚本会把当前机器的公钥推送到所有目标机器上,推送完成后,后续的批量操作就不需要再输入密码,直接用 ssh root@ip '命令' 就能执行。我在五十台机器的批量初始化时就这么干,效果很稳定。

注意,如果机器数量多,建议在循环里加超时控制,避免某台机器无法连通导致脚本卡死。一种简单做法:

timeout 15 sshpass -p "$PASSWORD" ssh -o StrictHostKeyChecking=no -o ConnectTimeout=5 root@"$ip" 'hostname'

timeout 15是命令级超时,ConnectTimeout=5是 SSH 连接超时。两者配合,批量脚本的健壮性能提升不少。

5.3 几个使用注意事项

这里必须强调一下安全习惯。sshpass 把密码放在命令行里,意味着通过 ps 查看进程参数就能看到明文密码。所以:

  • 生产环境不要长期使用 sshpass -p 方式,建议用它完成公钥分发后,立刻改为密钥认证。
  • 如果一定要用密码方式,优先使用-f从文件读取密码,并把文件权限设为 600。
  • 脚本里的临时密码用完以后及时改掉。

另外,SSH 客户端默认对 known_hosts 有严格检查,批量场景下一定要配合-o StrictHostKeyChecking=no并做好 known_hosts 的管理,否则首次连接会被指纹确认打断,脚本直接卡在那里。在真正的重要环境里,我建议反过来处理:在跳板机上预先维护好可信的 known_hosts,批量连接时不关闭指纹校验,这样安全性更高,但要花精力维护主机清单。

6. 常见问题与排查技巧

6.1 rpm 安装报依赖错误

报错信息类似libc.so.6(GLIBC_2.14)(64bit) is needed by sshpass-1.06-1.el7.x86_64。这种一般是因为系统里的 glibc 版本过低,或者系统被过度精简。先检查:

ldd --version

如果 glibc 版本确实不够,说明目标系统不适合装这个版本的 sshpass,需要找对应旧版本的包,或者改用源码编译。千万不要直接rpm -ivh --nodeps硬装,那样即使装上,运行的时候也会出问题。比如 sshpass 内部调用了某个 glibc 新接口,老版本库上跑起来直接段错误,排查起来更痛苦。

顺带说一句,如果你在公司内部维护了一套基线镜像,建议把 sshpass 直接做进镜像里,这样每次新机器交付就少一步离线安装,这也是从根源上规避依赖问题的方法。

6.2 编译报错 gcc 缺失

源码编译时最常见的就是no acceptable C compiler found in $PATH。解决办法有两个方向:一是从系统 ISO 镜像里提取 gcc、gcc-c++、make 及其依赖 rpm,按顺序 rpm -ivh 安装;二是如果公司内网有可以临时开放的 yum 源,把 iso 挂载进去临时用一下。相比起来,直接找对应版本的 RPM 包安装仍然是最省事的路线。

这里要提醒一下:从 ISO 里提取 gcc 不是只装 gcc 一个包,它有一堆依赖,包括 cpp、glibc-devel、libgcc、binutils 等。建议在联网机器上先跑一遍yum install --downloadonly gcc make,把所有依赖 rpm 一次性拉下来,再按顺序拷过去安装,这样比在 ISO 里一个一个找要高效得多。

6.3 sshpass 命令找不到

RPM 安装后,sshpass 默认在/usr/bin/sshpass,源码编译在/usr/local/bin/sshpass。如果执行时提示 command not found,检查 PATH 是否包含对应目录。如果不想改 PATH,直接用绝对路径,或者做个软链接:

ln -s /usr/local/bin/sshpass /usr/bin/sshpass

另外还有一种情况:你明明装了 sshpass,但用 sudo 执行时却说找不到,这是 sudo 的环境变量 PATH 被重置了。可以用sudo visudo在 secure_path 里加上/usr/local/bin,或者直接用sudo /usr/local/bin/sshpass来规避。

6.4 权限与认证被拒绝

安装成功后 sshpass 执行报Permission denied, please try again.,这种基本和 sshpass 本身无关,而是目标机器的 sshd 配置限制了密码认证。检查目标机器/etc/ssh/sshd_config里PasswordAuthentication是否为 yes,以及用户是否在AllowUsers之类的白名单里。改完配置后记得systemctl restart sshd。

另外,如果密码中包含$、!、&等特殊字符,在双引号内会被 shell 解释,导致密码错误。建议优先用单引号包裹,或者用-f从文件读取密码,能从根本上避免转义问题。我实际遇到过一次,密码里有个!,在 bash 历史扩展的极端场景下甚至会触发奇怪的错误,当时排查了很久才发现是特殊字符的问题。

6.5 批量脚本里反复卡住的坑

使用 sshpass 做批量循环时,我踩过最典型的坑就是 known_hosts 累积。机器第一次连接会提示确认公钥指纹,第二次 IP 被重装系统后又会提示指纹变更,这两种情况都会让脚本卡在 yes/no 询问上。解决方式是在 ssh 参数里固定加-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null,尤其是在批量初始化或测试环境里,宁可不记录指纹也不要让脚本被卡住。

还有个和 sshpass 无关但很常见的坑:批量执行远程命令时,如果命令里包含重定向符或者管道符,比如ssh root@host 'echo xxx > /tmp/a',在脚本里嵌套多层引号后很容易出问题。建议把要执行的命令写成一个单独的脚本文件传到目标机器上,再通过 ssh 执行,能明显减少转义错误。

最后再分享一个小经验。因为离线环境的网络和仓库千差万别,与其每次现找安装包,不如在公司内部搭一个简单的离线软件仓库,把常用的 rpm 和源码包都归档进去,包括 sshpass、常用编译工具、Python 运行时这类高频离线组件。平时用的时候直接 scp 或挂 NFS,省下的时间和踩坑成本绝对不是一点半点。我这边现在所有内网机器初始化,都会先执行一个标准离线包拷贝脚本,把包括 sshpass 在内的几个核心工具一次装好,后续批量操作就很顺了。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询