如何使用shell在多服务器上批量操作
说公钥登陆之前,先来说一下SSH协议。
SSH是一种网络协议,我们常说的ssh一般指其实现,即OpenSSH,在shell中,也就是ssh命令。
SecureShell(安全外壳协议,简称SSH)是一种加密的网络传输协议,可在不安全的网络中为网络服务提供安全的传输环境。SSH通过在网络中建立安全隧道来实现SSH客户端与服务器之间的连接。
SSH的原理跟HTTPS差不多,都是基于TCP和非对称加密进行的应用层协议。它跟HTTPS的不同之处在于HTTPS通过数字证书和数字证书认证中心来防止中间人攻击,而ssh服务器的公钥没有人公证,只能通过其公钥指纹来人工确定其身份。
如下图所示,我们第一次使用ssh登陆某台服务器时,ssh会提示我们验证服务器的公钥指纹。
当我们验证此公钥指纹是我们要登陆的服务器后,服务器的公钥会被添加到~/.ssh/known_hosts里,再登陆时,ssh检测到是已认证服务器后就会跳过公钥验证阶段。
关于通信加密的概念,我在之前的文章也有所介绍,参见:再谈加密-RSA非对称加密的理解和使用。至于SSH协议的建连过程,则可以参阅:ProtocolBasics:SecureShellProtocol。
总结起来主要包括以下步骤:
- TCP三次握手
- SSH协议版本协商
- 客户端与服务端的公钥交换
- 加密算法协商
- 客户端使用对称加密的密钥认证
- 客户端与服务端安全通信
我使用tcpdump+wireshark抓包并查看了一下其SSH的建连过程,如下图所示:
不得不再次感叹tcpdump+wireshark是学习网络协议的真神器。
作为工具,ssh分为服务端和客户端,在服务端,它是sshd,一般占用22端口。我们平常使用的是其客户端,一般用法为sshuser@host,然后根据ssh的提示,我们输入密码后登陆到服务器。
它的功能非常强大,看其支持参数就知道了。
介绍完了SSH协议和ssh命令,终于说到公钥登陆了。
理解了非对称加密的原理后,再公钥登陆会非常简单。由于公私钥是唯一的一对,在客户端保障自己私钥安全的情况下,服务端通过公钥就可以完全确定客户端的真实性,所以要实现公钥登陆,我们就要先生成一个公私密钥对。
通过ssh-keygen命令来生成密钥对,为了让步骤更完整,我把它们暂时保存到工作目录,默认会保存到~/.ssh目录。
Generatingpublic/privatersakeypair.
Enterfileinwhichtosavethekey(/Users/zbs/.ssh/id_rsa):./test
Enterpassphrase(emptyfornopassphrase):
Entersamepassphraseagain:
Youridentificationhasbeensavedin./test.
Yourpublickeyhasbeensavedin./test.pub.
Thekeyfingerprintis:
SHA256:xxxxx/B17z/xxxxxxzbs@zbs.local
Thekey'srandomartimageis:
+---[RSA2048]----+
| o+*..EO* |
| .... |
| oo+ .o++.o|
+----[SHA256]-----+
~ls./test*
./test ./test.pub
把私钥文件./test的内容放到客户端的~/.ssh/id_rsa,再使用密码试登陆到服务器后,将公钥内容./test.pub里的内容放到服务器的~/.ssh/authorized_keys。
再次登陆时,ssh会自动使用自己的私钥来认证,也就避免了输出密码。
公钥登陆帮我们避免了每次登陆服务器要输出密码的麻烦,它同时也解决了每个登陆会话都会同步阻塞的问题,这样我们就可以利用ssh的sshuser@hostcommand方式来直接在服务器上执行命令。
同时,在我们拥有一个ip列表的情况下,使用for循环遍历ip列表,在多个服务器上批量执行命令也就成为了可能。
前几天,帮同事在多个服务器上查找日志,需要把在多个服务器上查到的日志都汇总到同一台机器上进行统计分析。我是用pssh登陆的多个服务器,由于日志量太大,查出来的结果输出到终端上再复制有些不现实,而使用重定向,结果又会重定向到各自的服务器。
这时候可以使用scp,scp跟ssh是同一家族的命令,也是基于SSH协议实现的安全传输协议。只要在各个服务器之间互相保存着对方的公钥,就可以跟ssh命令一样,实现免密操作。
scp的常见用法是scpsrcdst,其中远程路径可以表示为user@host:/path。在批量登陆的情况下,可以使用grep等命令先把结果文件输入到一个文件中,再使用scp命令将其复制到同一台服务器。
为了避免各个服务器的文件名冲突,可以使用uuidgen|xargs-I{}scpresult.logroot@ip:/result/{}将各个服务器的结果复制到不同的文件中,再使用cat将result文件夹中的文件合并到一块。
当然,大多数情况下,我们的服务器之间并不会互相保存公钥,不过nc命令可以完美解决这个问题。
nc的-k选项,可以让nc服务端在文件传输结束后保持连接不关闭。这样,我们使用nc-k-4lport>result.log启动一个nc服务端,再使用grepxxxinfo.log|ncipport即可实现结果数据的合并。