在企业运维场景中,局域网内多台服务器的SSH登录凭据(用户名/密码)验证是高频需求——无论是新服务器部署后的凭据校验,还是定期安全巡检中的凭据有效性检查,手动逐台逐用户测试不仅效率低下,还容易出错。
- 本文将从技术原理出发,详解如何通过Shell+Expect实现9台服务器(IP段192.168.1.3-11)、4个用户(root/dmadmin/oracle/was) 的SSH登录批量测试,并提供可直接落地的优化脚本与使用指南。
一、核心技术原理:为什么用Shell+Expect?
要实现SSH登录的批量自动化测试,需解决两个核心问题:非交互式输入密码和批量循环调度。这正是Shell与Expect工具的协作优势所在:
1.1 Shell:批量调度的“总指挥”
Shell(本文使用Bash)作为Linux默认命令行解释器,负责:
- 批量生成目标服务器IP:通过循环自动生成192.168.1.3至192.168.1.11的IP列表,无需手动逐个填写;
- 多用户循环遍历:按顺序调度root、dmadmin、oracle、was四个用户的测试任务;
- 日志输出管理:创建带时间戳的日志文件,统一记录每台服务器、每个用户的测试结果;
- 依赖检查与错误处理:提前校验Expect工具是否安装、用户与密码配置是否匹配,避免脚本中途崩溃。
1.2 Expect:非交互式登录的“执行者”
SSH登录默认需要手动输入密码,而Expect是一款专门处理交互式命令的工具,其核心能力是:
- 模拟用户输入:通过
send
命令自动发送密码,替代手动敲击键盘; - 精准匹配交互场景:通过
expect
命令监听SSH登录过程中的关键提示(如password:
、Permission denied
),触发对应操作; - 超时控制:设置固定超时时间(如5秒),避免因网络故障导致脚本卡死。
举个简单的Expect交互逻辑示例(模拟单用户SSH登录):
# 启动SSH连接
spawn ssh user@192.168.1.3 exit
# 监听密码提示,发送密码
expect "*password:*" { send "user_password\r" }
# 监听登录结果,判断是否成功
expect {"*Permission denied*" { exit 1 } # 密码错误eof { exit 0 } # 登录成功
}
1.3 整体协作流程
- Shell初始化配置(IP段、用户列表、密码、日志路径);
- Shell检查依赖(Expect是否安装)和配置合法性(用户与密码数量是否匹配);
- Shell循环生成目标服务器IP,对每台服务器执行下一步;
- 对当前服务器,Shell循环遍历4个用户,调用Expect脚本;
- Expect模拟SSH登录,返回结果码给Shell;
- Shell根据结果码判断登录状态(成功/密码错误/网络超时等),并写入日志;
- 所有服务器和用户测试完成后,Shell输出总结信息。
二、批量测试脚本实现:优化版代码详解
基于上述原理,我们设计了支持“IP自动生成+多用户循环+详细日志”的优化脚本。以下是完整代码及关键模块解析:
2.1 完整脚本代码
#!/bin/bash
##############################################################################
# 脚本名称:ssh_multi_user_batch_test.sh
# 功能描述:批量测试局域网内多服务器多用户的SSH登录凭据有效性
# 测试范围:IP段 192.168.1.3-11(共9台),用户 root/dmadmin/oracle/was
# 输出日志:带时间戳的log文件(如 ssh_login_test_20250826_163000.log)
############################################################################### ========================== 配置参数(需根据实际环境修改)==========================
BASE_IP="192.168.1." # IP前三位(局域网统一前缀)
START_SUFFIX=3 # IP最后一位起始值(含)
END_SUFFIX=11 # IP最后一位结束值(含)
USERS=("root" "dmadmin" "oracle" "was") # 需测试的用户列表
# 对应用户的密码(顺序必须与USERS数组一致!)
PASSWORDS=("your_root_password" # root用户实际密码"your_dmadmin_password" # dmadmin用户实际密码"your_oracle_password" # oracle用户实际密码"your_was_password" # was用户实际密码
)
LOG_FILE="ssh_login_test_$(date +%Y%m%d_%H%M%S).log" # 日志文件(带时间戳)
TIMEOUT=5 # SSH连接超时时间(秒),建议5-10秒
# ==================================================================================# ========================== 初始化日志与前置检查 ==========================
# 1. 初始化日志文件,写入测试摘要
echo "==================================== 多用户SSH登录批量测试报告 ===================================" > "$LOG_FILE"
echo "测试启动时间:$(date +'%Y-%m-%d %H:%M:%S')" >> "$LOG_FILE"
echo "测试IP范围:${BASE_IP}${START_SUFFIX} ~ ${BASE_IP}${END_SUFFIX}(共$((END_SUFFIX - START_SUFFIX + 1))台服务器)" >> "$LOG_FILE"
echo "测试用户列表:${USERS[*]}" >> "$LOG_FILE"
echo "连接超时时间:${TIMEOUT}秒" >> "$LOG_FILE"
echo "==================================================================================================" >> "$LOG_FILE"
echo "" >> "$LOG_FILE"# 2. 检查用户与密码数组长度是否一致(避免配置错误)
if [ ${#USERS[@]} -ne ${#PASSWORDS[@]} ]; thenecho "【错误】用户列表与密码列表长度不匹配!请检查USERS和PASSWORDS配置" | tee -a "$LOG_FILE"exit 1
fi# 3. 检查Expect工具是否安装(脚本依赖)
if ! command -v expect &> /dev/null; thenecho "【错误】未检测到expect工具,脚本无法运行!" | tee -a "$LOG_FILE"echo " 请先安装expect:" | tee -a "$LOG_FILE"echo " - CentOS/RHEL:sudo yum install expect -y" | tee -a "$LOG_FILE"echo " - Debian/Ubuntu:sudo apt install expect -y" | tee -a "$LOG_FILE"exit 1
fi# 4. 自动生成服务器IP列表(从START_SUFFIX到END_SUFFIX)
SERVERS=()
for ((i=START_SUFFIX; i<=END_SUFFIX; i++)); doSERVERS+=("${BASE_IP}${i}")
done
echo "【信息】已生成服务器列表:${SERVERS[*]}" | tee -a "$LOG_FILE"
echo "【信息】开始批量测试(每台服务器测试${#USERS[@]}个用户)..." | tee -a "$LOG_FILE"
echo "------------------------------------------------------------------------------------------" >> "$LOG_FILE"
echo ""# ========================== 核心测试逻辑:循环测试服务器与用户 ==========================
for server in "${SERVERS[@]}"; do# 打印当前测试的服务器信息(控制台+日志)echo -e "\n=== 正在测试服务器:$server ==="echo -e "=== 测试服务器:$server ===" >> "$LOG_FILE"# 对当前服务器,循环测试所有用户for index in "${!USERS[@]}"; douser=${USERS[$index]} # 当前测试用户password=${PASSWORDS[$index]} # 当前用户密码echo -n " → 测试用户 $user@$server:"# 调用Expect执行非交互式SSH登录测试(核心模块)# 说明:-o StrictHostKeyChecking=no 跳过主机密钥检查;-o UserKnownHostsFile=/dev/null 不写入known_hostsexpect -c "set timeout $TIMEOUT # 设置超时时间spawn ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null $user@$server exitexpect {# 场景1:出现密码提示,发送密码\"*assword:*\" {send \"$password\r\"expect {# 子场景1.1:密码错误(Permission denied/Authentication failed){*Permission denied*} { exit 1 }{*Authentication failed*} { exit 1 }# 子场景1.2:登录成功(退出SSH,获取eof)eof { exit 0 }}}# 场景2:网络不可达(No route to host)\"*No route to host*\" { exit 2 }# 场景3:SSH服务未启动(Connection refused)\"*Connection refused*\" { exit 3 }# 场景4:连接超时timeout { exit 4 }# 场景5:其他未知错误default { exit 99 }}"# 捕获Expect返回的结果码,判断测试结果result_code=$?case $result_code in0)result="登录成功"echo -e "\033[32m$result\033[0m" # 绿色输出成功信息;;1)result="登录失败(用户名或密码错误)"echo -e "\033[31m$result\033[0m" # 红色输出失败信息;;2)result="网络错误(无法到达主机,可能是IP错误或路由问题)"echo -e "\033[33m$result\033[0m" # 黄色输出警告信息;;3)result="连接被拒绝(目标服务器SSH服务未启动或端口被占用)"echo -e "\033[33m$result\033[0m";;4)result="连接超时(超过${TIMEOUT}秒,可能是网络延迟或防火墙拦截)"echo -e "\033[33m$result\033[0m";;*)result="未知错误(返回码:$result_code)"echo -e "\033[31m$result\033[0m";;esac# 将结果写入日志(带时间戳)echo " $(date +'%H:%M:%S') - $user@$server:$result" >> "$LOG_FILE"done# 服务器测试结束,写入分隔符echo -e "=== 服务器 $server 测试完成 ==="echo "------------------------------------------------------------------------------------------" >> "$LOG_FILE"
done# ========================== 测试完成总结 ==========================
echo -e "\n【信息】所有服务器测试完成!"
echo "" >> "$LOG_FILE"
echo "==================================== 测试总结 ===================================" >> "$LOG_FILE"
echo "测试结束时间:$(date +'%Y-%m-%d %H:%M:%S')" >> "$LOG_FILE"
echo "测试服务器总数:$((END_SUFFIX - START_SUFFIX + 1))台" >> "$LOG_FILE"
echo "测试用户总数:${#USERS[@]}个" >> "$LOG_FILE"
echo "测试结果日志:$(pwd)/$LOG_FILE" >> "$LOG_FILE"
echo "==================================================================================" >> "$LOG_FILE"echo "【信息】测试结果已保存至日志文件:$(pwd)/$LOG_FILE"
2.2 关键模块解析
(1)配置参数模块:灵活适配实际环境
脚本开头的配置参数
部分是核心可修改区域,需根据局域网环境调整:
BASE_IP
:局域网IP统一前缀(如192.168.0.或10.0.5.);START_SUFFIX/END_SUFFIX
:IP最后一位的范围(本文3-11,共9台服务器);USERS/PASSWORDS
:需测试的用户列表与对应密码(顺序必须严格一致,避免密码错乱);TIMEOUT
:超时时间(建议5-10秒,太短易误判,太长影响效率)。
(2)前置检查模块:规避脚本运行风险
- 用户密码匹配检查:通过
${#USERS[@]} -ne ${#PASSWORDS[@]}
判断数组长度是否一致,避免“用户多密码少”或“密码多用户少”的配置错误; - Expect依赖检查:通过
command -v expect
判断工具是否安装,若未安装则输出对应系统的安装命令,降低运维门槛。
(3)IP自动生成模块:减少手动配置
通过for ((i=START_SUFFIX; i<=END_SUFFIX; i++))
循环,自动生成192.168.1.3至192.168.1.11的IP列表,无需手动填写9个IP,尤其适合IP段连续的场景。
(4)Expect交互模块:精准处理登录场景
这是脚本的核心,通过expect -c "..."
嵌入Expect脚本,覆盖5种常见场景:
- 密码提示:监听
*password:*
(兼容大小写,如Password:),发送密码; - 密码错误:匹配
Permission denied
或Authentication failed
,返回码1; - 网络不可达:匹配
No route to host
,返回码2; - SSH服务未启动:匹配
Connection refused
,返回码3; - 连接超时:超过
TIMEOUT
时间无响应,返回码4。
(5)结果输出模块:控制台+日志双记录
- 控制台输出:用颜色区分结果(绿色成功、红色失败、黄色警告),便于实时查看;
- 日志输出:写入带时间戳的log文件,包含测试摘要、每台服务器每个用户的详细结果、总结信息,便于后续追溯和问题定位。
三、脚本使用指南:从部署到执行
3.1 环境准备
脚本需在Linux系统(如CentOS 7/8、Ubuntu 20.04+)上运行,且需满足:
- 安装Expect工具:
- CentOS/RHEL:
sudo yum install expect -y
- Debian/Ubuntu:
sudo apt install expect -y
- CentOS/RHEL:
- 执行用户权限:无需root权限,但需确保执行用户可正常调用
ssh
和expect
命令; - 网络连通性:执行脚本的机器需能ping通目标服务器(192.168.1.3-11),且目标服务器开放22端口(SSH默认端口)。
3.2 脚本部署与修改
- 创建脚本文件:
vi ssh_multi_user_batch_test.sh
- 粘贴完整代码:将上文的脚本代码复制粘贴到文件中;
- 修改配置参数:重点修改
PASSWORDS
数组中的密码(替换为实际凭据),并确认BASE_IP
是否与局域网匹配。
3.3 执行脚本与查看结果
- 赋予执行权限:
chmod +x ssh_multi_user_batch_test.sh
- 执行脚本:
./ssh_multi_user_batch_test.sh
- 实时查看进度:控制台会按“服务器→用户”的顺序输出测试结果,颜色区分状态(绿色成功、红色失败);
- 查看日志文件:测试完成后,当前目录会生成带时间戳的log文件(如
ssh_login_test_20250826_163000.log
),可通过cat
或less
查看详细结果:cat ssh_login_test_20250826_163000.log
四、常见问题与优化建议
4.1 常见问题排查
-
“用户列表与密码列表长度不匹配”:
- 原因:
USERS
数组和PASSWORDS
数组元素数量不一致; - 解决:检查两个数组,确保每个用户对应一个密码(顺序一致)。
- 原因:
-
“连接被拒绝”:
- 原因:目标服务器SSH服务未启动(
systemctl status sshd
查看),或22端口被防火墙拦截; - 解决:在目标服务器执行
systemctl start sshd
启动服务,或开放22端口(firewall-cmd --add-port=22/tcp --permanent
)。
- 原因:目标服务器SSH服务未启动(
-
“连接超时”:
- 原因:目标服务器IP错误、网络路由不通,或防火墙拦截ICMP/SSH流量;
- 解决:先通过
ping 192.168.1.3
测试网络连通性,再通过telnet 192.168.1.3 22
测试端口是否开放。
4.2 脚本优化方向
- 支持自定义SSH端口:若目标服务器SSH端口非22,可在
spawn ssh
命令中添加-p 端口号
,并新增SSH_PORT
配置参数; - 密码加密存储:当前脚本密码明文存储存在安全风险,可通过
openssl
加密密码,脚本运行时解密(需额外配置密钥); - 并行测试:当前脚本按顺序测试服务器,可通过
xargs -P
或&
实现并行测试,提升效率(适合服务器数量多的场景); - 结果统计:在日志总结中增加“成功次数/失败次数/错误类型统计”,便于快速掌握整体情况。
五、总结
本文通过“原理讲解→脚本实现→使用指南”的逻辑,详细介绍了局域网多服务器多用户SSH登录批量测试的技术方案。核心是利用Shell的批量调度能力和Expect的非交互式处理能力,解决了手动测试效率低、易出错的痛点。
脚本已针对“IP段192.168.1.3-11+4个用户”的场景优化,只需修改配置参数即可直接落地。同时,文中提供的问题排查和优化方向,也为后续扩展功能提供了思路,适合企业运维人员日常使用。