按照本文批量ssh,需要所有服务器用来ssh互信的账户和密码是一样的,不一样的改一下,后期公钥配置好之后可以再改回来,因为批量的话要向所有主机推送文件和交互,还没有免密,只能用密码
1、首先我们要安装一个自动化的集成套件,我们需要使用这个套件完成ssh批量免密,选一台执行机就行,不需要所有节点安装
yum install -y expect sshpass
2、准备一个shell脚本,名字自定义我的叫ssh_all.sh
#!/bin/sh
DEST_USER=$1
PASSWORD=$2
HOSTS_FILE=$3
if [ $# -ne 3 ]; then
echo "用法:"
echo "$0 远程用户 远程密码 主机文件"
exit 1
fi
SSH_DIR=~/.ssh
SCRIPT_PREFIX=./tmp
# 1. 在当前用户的主目录下,准备.ssh目录 ,并保证权限只有自己有
mkdir $SSH_DIR
chmod 700 $SSH_DIR
# 2. 生成一个统一的ssh密钥
TMP_SCRIPT=$SCRIPT_PREFIX.sh
{
echo "#!/usr/bin/expect"
echo "spawn ssh-keygen -b 1024 -t rsa"
echo "expect *key*"
echo "send \r"
if [ -f $SSH_DIR/id_rsa ]; then
echo "expect *overwrite*"
echo "send y\r"
fi
echo "expect *passphrase*"
echo "send \r"
echo "expect *again:"
echo "send \r"
echo "interact"
} > $TMP_SCRIPT
chmod +x $TMP_SCRIPT
/usr/bin/expect $TMP_SCRIPT
rm $TMP_SCRIPT
# 3. 生成authorized_keys文件
cat $SSH_DIR/id_rsa.pub >> $SSH_DIR/authorized_keys
# 4. 设置authorized_keys文件权限为600
chmod 600 $SSH_DIR/authorized_keys
# 5. 将文件复制到其他主机
for ip in $(cat $HOSTS_FILE)
do
if [ "x$ip" != "x" ]; then
TMP_SCRIPT=${SCRIPT_PREFIX}.$ip.sh
# 检查known_hosts
val=`ssh-keygen -F $ip`
if [ "x$val" == "x" ]; then
echo "$ip 不在 $SSH_DIR/known_hosts 中, 需要添加"
val=`ssh-keyscan $ip 2>/dev/null`
if [ "x$val" == "x" ]; then
echo "ssh-keyscan $ip 失败!"
else
echo $val >> $SSH_DIR/known_ hosts
fi
fi
echo "将 $SSH_DIR 复制到 $ip"
{
echo "#!/usr/bin/expect"
echo "spawn scp -r $SSH_DIR $DEST_USER@$ip:~/"
echo "expect *assword*"
echo "send $PASSWORD\r"
echo "interact"
} > $TMP_SCRIPT
chmod +x $TMP_SCRIPT
/usr/bin/expect $TMP_SCRIPT
rm $TMP_SCRIPT
echo "复制完成."
fi
done
echo 完成
3、准备集群ips文件,一行一个ip,如下
192.168.43.186
192.168.43.187
192.168.43.188
4、将shell脚本赋权
chmod 777 ssh_all.sh
5、运行,参数1是用来ssh的用户,参数2是该用户密码,参数3是集群ips文件
./ssh_all.sh root 123456 ./host_list
如果脚本运行正常会出现如下日志,注意我没有截全,最后会出现几个100%的进度条,之后用测试ssh效果就可以了
上面这个脚本是单线程执行,应用于,测试集群,或者很少的集群上跑一跑,如果你要正式的部署商用化集群,用下面的这个脚本
#!/bin/bash
# 1. 处理脚本需要的参数以及所需服务检查
# 免密用的用户
DEST_USER=$1
# 密码
PASSWORD=$2
# 节点列表文件
HOSTS_FILE=$3
# 默认并行度为1,可通过第4个参数调整
PARALLEL_JOBS=${4:-1}
# 公钥等文件存放的路径
export SSH_DIR=~/.ssh
if [ $# -lt 3 ]; then
echo "+ 用法: $0 远程用户 远程密码 主机文件 [并行度]"
exit 1
fi
if rpm -q expect sshpass >/dev/null 2>&1; then
echo "+ expect 和 sshpass 环境存在"
else
echo "+ expect 和 sshpass 环境不存在"
exit 1
fi
# 2. 让所有节点,生成自己的SSH密钥(如果密钥不存在)
generate_key_with_stats() {
local ip=$1
local user=$2
local password=$3
echo -e "+ \e[1;35;44m[$ip]\e[0m 通知生成密钥"
# 检查密钥是否存在
# sshpass 需要yum安装 本身是一个和expect类似的交互套件,用来专门托管ssh命令,提供密码填充功能
# -p "$password" 直接提供访问密码
# ssh 托管ssh服务
# -o StrictHostKeyChecking=no -o ConnectTimeout=10 跳过ssh客户端认证 设置连接超时为10秒
# "$user@$ip" 目标机器
# "test -f ~/.ssh/id_rsa && test -f ~/.ssh/id_rsa.pub" 检查两个文件是否存在
# 这个 if 判断的是 ssh 的返回,test命令如果文件不存在状态码返回 非 0
if sshpass -p "$password" ssh -o StrictHostKeyChecking=no -o ConnectTimeout=10 "$user@$ip" \
"test -f ~/.ssh/id_rsa && test -f ~/.ssh/id_rsa.pub" 2>/dev/null; then
echo -e "+ \e[1;35;44m[$ip]\e[0m 密钥已存在,跳过"
return 0
fi
# 生成密钥
if sshpass -p "$password" ssh -o StrictHostKeyChecking=no "$user@$ip" \
"mkdir -p ~/.ssh && chmod 700 ~/.ssh && \
ssh-keygen -t rsa -b 2048 -N '' -f ~/.ssh/id_rsa -C '$user@$ip' >/dev/null 2>&1"; then
echo -e "+ \e[1;35;44m[$ip]\e[0m 密钥生成成功"
echo ""
return 0
else
echo -e "+ \e[1;35;44m[$ip]\e[0m 密钥生成失败"
echo ""
return 1
fi
}
# 导出方法,让并行子进程跑
export -f generate_key_with_stats
echo ""
echo -e "+ \e[1;34;47m开始构造所有主机密钥 generate_key_with_stats all\e[0m "
cat "$HOSTS_FILE" | xargs -I {} -P "$PARALLEL_JOBS" bash -c 'generate_key_with_stats "$@" ' _ {} "$USER" "$PASSWORD"
echo -e "+ \e[1;34;47m所有主机密钥构造结束 generate_key_with_stats all\e[0m "
echo ""
# 3. 回收所有公钥,把公钥写在all_authorized_keys文件里,权限600,700也可以,不过不需要执行600就够了
echo "+ 构造新的临时文件 build all_authorized_keys all_known_hosts"
# 保险起见这里先清理一下
rm -f ${SSH_DIR}/all_authorized_keys ${SSH_DIR}/all_known_hosts
touch $SSH_DIR/all_authorized_keys
touch $SSH_DIR/all_known_hosts
echo "+ 设置权限 chmod 600 all_authorized_keys all_known_hosts"
chmod 600 $SSH_DIR/all_authorized_keys
chmod 600 $SSH_DIR/all_known_hosts
for ip in $(cat ${HOSTS_FILE}); do
echo -e "+ \e[1;35;44m[$ip]\e[0m 正在回收公钥/密钥"
# 收集公钥
sshpass -p $PASSWORD ssh $USER@$ip "cat ~/.ssh/id_rsa.pub" >> ${SSH_DIR}/all_authorized_keys
# 收集主机密钥
ssh-keyscan -H $ip >> ${SSH_DIR}/all_known_hosts
done
echo "+ 新的临时文件构造完成 build all_authorized_keys all_known_hosts"
echo ""
echo "+ 覆盖结果文件 overwrite authorized_keys known_hosts"
cat ${SSH_DIR}/all_authorized_keys > ${SSH_DIR}/authorized_keys
cat ${SSH_DIR}/all_known_hosts > ${SSH_DIR}/known_hosts
echo "+ 覆盖结果文件完成 overwrite authorized_keys known_hosts"
echo ""
# 4. 并行分发所有节点,当前的所有用户登录公钥和ssh客户端密钥
process_host() {
local ip=$1
local user=$2
local password=$3
#提示输出 开始
echo -e "+ \e[1;35;44m[$ip]\e[0m 开始执行免密"
# 使用expect进行SCP 把 ~/.ssh 下含有所有节点公钥以及ssh客户端私钥的文件同步所有节点
# set timeout 30 设置目标服务或命令响应超时时间为30秒
# spawn scp -r 文件路径 $user@$ip:~/ 启用一个新的子进程,发送文件
# expect 规定发生交互时的动作
# \"*assword*\" 当遇到 assword 关键字时 发送密码和回车
# \"yes/no\" 当遇到yes/no 关键子时 发送yes和回车
# timeout 如果超时 推出返回 状态码 1
# eof 不超时正常结束交互 wait关键字用来表明等待spawn子进程结束 catch表明捕获可能出现的错误 result声明保存的变量 exit [lindex \$result 3] 表示获取第四位的子线程状态码为expect的返回码
expect -c "
set timeout 30
spawn scp $SSH_DIR/authorized_keys $SSH_DIR/known_hosts $user@$ip:~/
expect {
\"*assword*\" {
send \"$password\r\"
exp_continue
}
\"yes/no\" {
send \"yes\r\"
exp_continue
}
timeout {
exit 1
}
eof {
catch wait result
exit [lindex \$result 3]
}
}
"
if [ $? -eq 0 ]; then
echo -e "+ \e[1;35;44m[$ip]\e[0m 执行免密结束 \e[1;32;44m成功\e[0m"
echo ""
return 0
else
echo -e "+ \e[1;35;44m[$ip]\e[0m 执行免密结束 \e[1;37;41m失败\e[0m"
echo ""
return 1
fi
}
# 导出函数和执行关键参数,以便在子shell中使用
export -f process_host
echo ""
echo -e "+ \e[1;34;47m开始并行分发SSH密钥到各节点\e[0m"
#xargs centos7系统内置 、 -I 行数据占位表达式 、-P 并行度 、 bash -c 开始讲函数动态重组成一个脚本 后面是参数
cat $HOSTS_FILE | xargs -I {} -P $PARALLEL_JOBS bash -c 'process_host "$@"' _ {} $DEST_USER $PASSWORD
echo -e "+ \e[1;34;47m完成所有节点的SSH密钥分发\e[0m"
echo ""
# 5.删除临时文件
echo "+ 临时文件清理 rm -f all_authorized_keys all_known_hosts"
rm -f ${SSH_DIR}/all_authorized_keys ${SSH_DIR}/all_known_hosts
echo "完成免密"
exit 0
注意:你要用那个用户做服务器之间的SSH免密那就登录那个用户去操作,同时把上面所用的脚本文件和文件夹都修改为所用用户所拥有,不然得话免密会出问题
如果在免密中部分节点因为网络等问题失败了,单独执行那些失败的就行,因为使用这个脚本批量完成后,所有的节点用的其实是一套公钥和密钥,自然
免密完成后,检查操作用户主目录下的.ssh
文件夹,正常来说里面会有四个文件
- id_rsa.pub:存放了本台节点的自己的公钥信息,内容通常是
ssh-rsa 公钥串 用户@节点
,ssh-rsa表示公钥串的类型,后面的公钥串和用户以及节点,用来表示其他节点拿到这个串后可以通过那个方式来免密登录本届点,通常是ssh 用户@节点
- authorized_keys:这个文件里数据和id_rsa.pub中格式是一样的,不过意义不同,该文件中的公钥串,放的是其他可连接登录到本机的其他机器的公钥
- known_hosts:这个文件中存放着的也是公钥,它和authorized_keys文件存在的意义是一样的,但它不是
.ssh
文件夹中用户的公钥,而是其他服务器ssh客户端的公钥,有自己的生成和验证逻辑不用人为操心。通俗的讲,known_hosts文件用来验证连接到服务器后客户端和目标服务器做一次公钥检查,来确定不是被中间人攻击了,authorized_keys解决的是那些机器可以用某个用户来登录 - id_rsa:这个是私钥,用来证明当前节点自身,通常不会给别人,在免密登录操作上用不到