全自动化SSH免密脚本建立过程详解 郝伟 2021/06/23 [TOC]

1. 1 功能说明

1.1. 1.1 功能简介

本脚本主要实现以下两个过程:

  • 自动化创建免密过程,给定IP和登陆密码建立自动化免密过程;
  • 自动化删除免密过程,从指定IP上删除已经配置好的内容。

1.2. 1.2 主要难点

这两个内容在自动化的过程中会有很多交互内容,造成无法自动化的主要原因,所以核心的要求是如何取消这些交互过程。

2. 2 SSH密码生成

2.1. 2.1 密钥无交互生成

首先需要理解密钥产生的机制。SSH是基于密钥的,所以系统提供了SSH的命令 ssh-keygen用于生成密钥。 我们可以直接使用命令指定RSA生成一对密钥。

但是在自动过程中,为了避免交互,可以使用以下命令: ssh-keygen -f id_rsa -t rsa -N "" 其中

  • -f 是输入文件名,会生成 id_rsaid_rsa.pub 两个文件分别表示密钥和公钥;
  • -t 是密钥内容,文件名称为 RSA;
  • -N 解密的密码,默认为空。

2.2. 2.2 注意事项

在执行此代码后,会在控制台输出以下内容:

Your identification has been saved in id_rsa.
Your public key has been saved in id_rsa.pub.
The key fingerprint is:
SHA256:G4u9JtXE2NhFEF6JXGfcgeUkxwea9UnJ3O0gKO7+nHk root@instance-j33hvw56
The key's randomart image is:
+---[RSA 2048]----+
|         .oB++@O=|
|        ..+.+BO**|
|       . B..o. =o|
|        + =     .|
|       .So       |
|       oo+.      |
|      .o+        |
|      . oo oE    |
|       o..=.     |
+----[SHA256]-----+

如果不希望输出,可以生定向输出至 /dev/null

ssh-keygen -f id_rsa -t rsa -N "" > /dev/null

另外还有一个问题就是如果文件已经存在,则会出现:

root@instance-j33hvw56:~# ssh-keygen -f id_rsa -t rsa -N ""
Generating public/private rsa key pair.
id_rsa already exists.
Overwrite (y/n)?

所以,为了避免这个问题,可以加上删除命令,如:

rm id_rsa*; ssh-keygen -f id_rsa -t rsa -N "" > /dev/null

3. 3 SSH登陆确认

使用SSH登陆时第一次登陆会出现以下内容,进行确认是否登陆,如下所示:

root@server00:~/hao# ssh user@host
The authenticity of host '106.12.31.101 (106.12.31.101)' can't be established.
ECDSA key fingerprint is SHA256:SRQLrDAimMXE8tRTMyELTgOL9r+tvcJX4ZmwOQZ2d+Q.
Are you sure you want to continue connecting (yes/no)?

以上的内容是从安全角度让用户进行确认,所以不显示此内容,可以通过配置实现: ssh -o StrictHostKeyChecking=no user@host 注:

  • 一定登陆以后,登陆的机器的SHA256会保存在本机的 ~/.ssh/known_hosts 中。
  • 以后在登陆时使用以下命令即可:ssh -i ssum_key.pem user@host
  • 若要避免每次使用 -i 参数,可在 ~/.ssh/config 添加 IdentityFile ~/.ssh/ssum_key.pem
  • 若密钥路径为 ~/.ssh/id_rsa,也可省去 -i 参数。

4. 4 SSH登陆输入密码

输入密码有两种方法:sshpassexpect。这两种工具只需要在本机安装好即可,目标机器无需安装。

4.1. 4.1 sshpass

sshpass 命令可以用于 ssh 和 scp 命令,如: sshpass -p 'password' ssh -o StrictHostKeyChecking=no user@host

4.2. 4.2 expect

安装时也可以免交互,只需要在install后加上 -y 即可,如下所示: apt install -y expect 示例脚本

#!/usr/bin/expect
set timeout 30
spawn ssh -l root 106.12.31.101
expect "password*"
send "\password\r"
interact

5. 5 自动化脚本(基于 sshpass)

5.1. 5.1 建立本地密码

此脚本在本机执行,只需在本地1次: ssh-keygen -f id_rsa -t rsa -N ""

5.2. 5.2 安装 sshpass

apt install -y sshpass

5.3. 5.3 上传公钥并追加

使用以下基于 sshpass 的脚本可以实现自动化上传公钥的脚本。

#!/usr/bin/expect
# Step 1: upload mykey.pub to server
sshpass -p 'password' scp mykey.pub user@host:

# Step 2: Add mykey.pub to authorized_keys
sshpass -p 'password' ssh user@host "cat mykey.pub >> ~/.ssh/authorized_keys"

6. 6 整体架构

整体框架以下所示。

@startuml
left to right direction
actor Manager as g

artifact ControlServer as cs {
  folder accounts {
    file "root@host1" as f1
    file "hao@host2" as f2
    file "jack@host3" as f3
    file "lucy@host4" as f4
    file "bruce@host5" as f5
  }
}

package CloudServers {
  rectangle "host1" as UC1
  rectangle "host2" as UC2
  rectangle "host3" as UC3
  rectangle "host4" as UC4
  rectangle "host5" as UC5
}

g --> cs:initialize
f1 --> UC1: manage
f2 --> UC2: manage
f3 --> UC3: manage
f4 --> UC4: manage
f5 --> UC5: manage
@enduml

解释

  • Manager:系统管理员
  • ControlServer:用于管理所有机器的云服务器
  • CouldServers: 待管理的云主机
  • initialize: 初始化操作,在机器上安装 sshpass

服务器上存储了所有通过SSH登陆的文件,每个文件中的内容是密码,文件名为 登陆名@HostIP,如 root@106.12.31.101,内容是 $erver2019。 这样,只需使用 sshpass -f $1 ssh $1 "ls" 即可执行相关命令。

批量执行任务

#!/bin/bash
cmd="cd /opt; ls"
for file in $1/*;
do
        name=$(basename $file)
        echo "------------------------------------------------------------------"
        echo "sshpass -f $name ssh $name \"$cmd\""
        sshpass -f $file ssh -o StrictHostKeyChecking=no $name "$cmd"
        echo ""
done

执行结果

root@server00:~# sh sh1.sh task1/
------------------------------------------------------------------
sshpass -f root@106.12.31.101 ssh root@106.12.31.101 "cd /opt; ls"
avalokita
bcm-agent
containerd
hosteye
java
tomcat8

------------------------------------------------------------------
sshpass -f root@121.199.10.158 ssh root@121.199.10.158 "cd /opt; ls"
apache-tomcat-8.5.57
jdk-11.0.8
jdk1.8.0_261

7. 参考资料

7.1. 1 在bash循环执行命令示例

# 示例1:循环输出编号
for a in {1..10}; do echo "ID:$a"; done

# 示例2:使用for循环,注:格式上必需为两层括号
for((i=1;i<=10;i+=2)); do echo "Welcome $i times"; done

# 示例3:文件循环遍历
for i in /etc/*.conf; do cp $i /backup; done

7.2. 2 测试内容

以下内容测试功能

# add public key 
sshpass -p 'password' scp mykey.pub user@host:; sshpass -p 'password' ssh user@host "cat mykey.pub >> ~/.ssh/authorized_keys"

# remove public key
ssh -i mykey user@host "cat ~/.ssh/authorized_keys | sed  '/root@server00/d' >> v1.txt; rm -f ~/.ssh/authorized_keys; mv v1.txt ~/.ssh/authorized_keys"
编写成脚本
# password pubkey user ip
#!/bin/bash
sshpass -p '$1' scp $2 $user@$ip:; sshpass -p '$1' ssh $user@$ip "cat $pk >> ~/.ssh/authorized_keys"

# remove public key
V1: ssh -i mykey user@host "cat ~/.ssh/authorized_keys | sed '/root@server00/d' >> v1.txt; rm -f ~/.ssh/authorized_keys; mv v1.txt ~/.ssh/authorized_keys"
V2: ssh -i mykey user@host "cat ~/.ssh/authorized_keys | sed -i '/root@server00/d' ~/.ssh/authorized_keys"

7.3. 6.3 其他材料

1. Make data: for a in {1..10}; do echo "ID:$a" >> v0.txt; done; cat v0.txt
2. cat v0.txt | sed  '/1/d' >> v1.txt; rm -f v0.txt; mv v1.txt v0.txt

sshpass -f $1 ssh $1 "ls"

# 使用
# 根据ip列表指定的程序类型,将程序上传至 ip:/etc/fsy 目录下
cat $2 | while read line  #读取iplist.txt中的每一行
do
  if [ ${line:0:2} == 'gw' ];then                    # 根据是否是gw开头,判断是否为网关
    scp -r $1/gateway root@${line:3:20}:$tarpath     # 上传网关程序
  elif [ ${line:0:2} == 'tm' ];then                  # 根据是否是tm开头,判断是否为远控
    scp -r $1/terminal root@${line:3:20}:$tarpath    # 上传远控程序程序
  else                                               # 否则为路由程序,上传路由程序
    scp -r $1/rcontrol root@${line:0:2}:$tarpath
  fi
done

results matching ""

    No results matching ""