python自动化管理sshy(ssh,ssh-copy-id,ssh-agent)
ssh优势:
- 安全传输文件
- 登录
- 批量执行命令
对于一名刚开始接触Linux系统管理的工程师来说,他眼里的系统管理的步骤可能是:使用SSH登录服务器,修改应用相关的配置文件,执行一些Linux命令,重启相应的进程,最后退出服务器。如果还有更多的服务器,那么,就重复上述过程。
上面这一系列步骤是Linux系统管理的基础知识,是系统管理的基本功。但是,在实际工作中,一般不会手动对
服务器进行操作,而是使用程序进行自动化管理。即使服务器的数量很少,也推荐大家编写程序进行自动化。相对于手动管理服务器,自动化管理有许多优点。例如:
1)效率高:
自动化操作效率比手动操作效率高。这里的效率高可以从两方面来理解:一方面是程序执行的效率比手动操作的效率高;另一方面是指对工程师来说,使用程序可以提高自身的工作效率,减少不必要的时间浪费。即使只有一台服务器,手动操作虽然可以很快完成,但其操作效率也不能与程序相提并论。如果管理的是服务器集群,显然,人工操作非常不现实,不但效率低下.而且枯操乏味,费时费力。程序的好处是一次编写,多次运行。虽然在编写程序的时候,花费的时间可能比单次手动操作的时间多,但是,只要程序编写完成,就可以多次反复地运行,节省大量时间。
2)不容易犯错:
俗话说“人无完人”,如果一直使用人工管理的方式管理服务器集群,那么,出错是不可避免的。工程师会有情绪的变化,也会有身体健康状况等问题,但程序不会。只要程序编写完成,并且考虑到了相应的异常,程序总是能够严格一致地执行管理操作。
3)享受乐趣:
从事计算机行业有一个天然的好处,那就是不用进行重复性的工作。有任何重复性的工作,找们都可以通过编写程序消灭掉。消灭重复性的工作,不但节省工作时间,还能够获得更多的乐趣和成就感。以管理服务器集群为例,看到自己编写的程序、指挥成百上千的服务器按照既定的需求执行操作,是不是有种指点江山、挥斥方遒的感觉?
这一章将会讨论如何使用Python批量管理服务器。首先,我们将会介绍批量管理服务器的基础知识,即SSH协议;随后,本章会介绍一个Python编写的批处理工具;然后将会介绍如何在Python程序中对远程服务器进行操作;在本章的最后,我们会介绍一个非常强大的系统管理工具,即Fabric,这一部分是本章的重点和难点。
一、使用SSH协议访问远程服务器
SSH ( Secure Shell)是一种由IETF的网络工作小组制定、创建在应用层和传输层基础上的安全协议,为计算机上的Shell提供安全的传输和使用环境。
1、SSH协议
在互联网早期,通信都是明文的,如rsh、 FTP、POP和Telnet。一旦通信报文被截获,内容就泄漏无疑。1995年,芬兰学者Tatu Ylonen设计了SSH协议。将登录信息全部加密,成为互联网安全的一个基本解决方案。这个方案迅速在全世界获得推广,目前已经成为Linux系统的标准配。
SSH只是一种协议,存在多种实现,既有商业实现也有开源实现。目前。在Linux下广泛使用的是OpenSSH,它是一款应用广泛的开源软件。本文即将介绍的paramiko是SSH协议的一种Python实现。
SSH除了提供安全的传输和登录以外,还可以进行批量命令执行,使用非常方便。正是由于SSH简单好用的特点,本章介绍的几个工具,以及稍后即将介绍的Ansible,都依赖SSH进行远程服务器的管理。使用SSH的好处非常明显,既充分利用了现成的机制,又省去了在远程服务器安装代理(Agent)程序。因此,诸多自动化工其都依赖SSH。
2、OpenSSH实现
OpenSSH (OpenBSD Secure Shell)是OpenBSD的一个子项目,是SSH协议的开源实现。在服务端,OpenSSH启动sshd守护进程,该进程默认监听22端口。客户端使用用户名和密码连接服务端,连接成功以后,OpenSSH返回给用户一个Shell,用户可以使用该Shell在远程服务器执行命令。
在Debian系统中,OpenSSH的服务端默认读取/etc/ssh/sshd_config中的配置。在生产环境中,为了防止******,一般会修改ssh服务的默认端口号,修改ssh服务默认端口号就是在/etc/ssh/sshd_config中完成的。我们也可以通过该配置文件禁止用户使用密码进行认证,只能使用密钥认证。修改完配置文件以后,执行下面的命令重启OpenSSH的守护进程才能生效:
对于一名刚开始接触Linux系统管理的工程师来说,他眼里的系统管理的步骤可能是:使用SSH登录服务器,修改应用相关的配置文件,执行一些Linux命令,重启相应的进程,最后退出服务器。如果还有更多的服务器,那么,就重复上述过程。
上面这一系列步骤是Linux系统管理的基础知识,是系统管理的基本功。但是,在实际工作中,一般不会手动对
服务器进行操作,而是使用程序进行自动化管理。即使服务器的数量很少,也推荐大家编写程序进行自动化。相对于手动管理服务器,自动化管理有许多优点。例如:
/etc/init.d/ssh restart
OpenSSH的客户端是一个名为ssh可执行程序,我们可以使用ssh命令连接远程服务器。如下所示:
[ ~]# ssh The authenticity of host ‘localhost (::1)‘ can‘t be established. ECDSA key fingerprint is bf:c7:de:84:e1:28:f4:d4:7e:41:49:9f:54:dc:e9:83. Are you sure you want to continue connecting (yes/no)? yes ‘s password: [ ~]$
如果服务器端不是使用默认的22端口,可以通过SSH命令的-p参数指定建立连接的端口号,格式如下所示:
ssh _host -p 端口号
我们也可以不进入交互式的Shell,直接使用ssh命令在远程服务器中执行Linux命令,如下所示:
[ ~]# ssh ‘date‘ ‘s password: 2020年 05月 15日 星期五 15:55:08 CST s [ ~]#
3、配置ssh的方法:
- 编辑/etc/ssh/ssh_config
- 编辑~/.ssh/config
ssh会读取/etc/ssh/ssh_config文件中的配置。例如,远程服务器使用的不是默认的22端口号,我们只需要在/etc/ssh/ssh_config进行简单的配置,就可以在连接远程服务器时省去指定端口号的参数。除了修/etc/ssh/ssh_config文件以外,更常见的做法是修改用户home目录下的~/.ssh/config文件。
查看ssh_config文件
[ ~]# find / -name ssh_config #查看找ssh_config 文件 /etc/ssh/ssh_config [ ~]# cat /etc/ssh/ssh_config | awk # Usage: awk [POSIX or GNU style options] -f progfile [--] file ... Usage: awk [POSIX or GNU style options] [--] ‘program‘ file ... POSIX options: GNU long options: (standard) -f progfile --file=progfile -F fs --field-separator=fs -v var=val --assign=var=val Short options: GNU long options: (extensions) -b --characters-as-bytes -c --traditional -C --copyright -d[file] --dump-variables[=file] -e ‘program-text‘ --source=‘program-text‘ -E file --exec=file -g --gen-pot -h --help -L [fatal] --lint[=fatal] -n --non-decimal-data -N --use-lc-numeric -O --optimize -p[file] --profile[=file] -P --posix -r --re-interval -S --sandbox -t --lint-old -V --version To report bugs, see node `Bugs‘ in `gawk.info‘, which is section `Reporting Problems and Bugs‘ in the printed version. gawk is a pattern scanning and processing language. By default it reads standard input and writes standard output. Examples: gawk ‘{ sum += $1 }; END { print sum }‘ file gawk -F: ‘{ print $1 }‘ /etc/passwd
例如,我们经常要使用某一个用户名、端口号访问某一台远程服务器。为了省去记住服务器ip的负担,很多工程师会编写一个Shell脚本,在脚本中保存用户名、端口号和ip地址。在下次登录时,可以省去输入的烦恼。如下所示:
[ ~]# vim /opt/login.sh #!/usr/bin/bash ssh -p 22 [ ~]# useradd test [ ~]# passwd test 更改用户 test 的密码 。 新的 密码: 无效的密码: 密码少于 8 个字符 重新输入新的 密码: passwd:所有的身份验证令牌已经成功更新。 [ ~]# sh /opt/login.sh The authenticity of host ‘127.0.0.1 (127.0.0.1)‘ can‘t be established. ECDSA key fingerprint is bf:c7:de:84:e1:28:f4:d4:7e:41:49:9f:54:dc:e9:83. Are you sure you want to continue connecting (yes/no)? yes Warning: Permanently added ‘127.0.0.1‘ (ECDSA) to the list of known hosts. ‘s password: [ ~]$
对于这里的需求,还有更好的解决方案。命令会读取~/.ssh/config文件中配置,因此,我们可以在~/.ssh/config文件中提前配置好访问远程服务器的信息。如下所示:
[ ~]# cp /etc/ssh/ssh_config -p ~/.ssh/config [ ~]# vim ~/.ssh/config Host python ForwardAgent yes StrictHostKeyChecking no port 22 User test Controlpath ~/.ssh/ssh-%%h:%p.sock Host * StrictHostKeyChecking no HostName %h Port 22 User test Controlpath ~/.ssh/ssh-%%h:%p.sock
配置完成后,直接在命令行执行ssh host2就可以使用用户名test,以及端口号2092登录到10.166.224.14中。此外,我们还使用通配符的方式定义了ssh默认用户名与端口号。假设我们要使用用户名laoyu、端口号2092访问10.166.226.153,有了前面的配置以后,可以在命令行直接进行登录。如下所示:
[ ~]# ssh python Warning: Permanently added ‘python,fe80::c64e:c937:2ea8:6676%ens33‘ (ECDSA) to the list of known hosts. ‘s password: Permission denied, please try again. ‘s password: Last login: Fri May 15 14:34:54 2020 from localhost [ ~]$
可以看到,我们只需要在~/.ssh/config文件中进行简单的配置就能够有效提高工作效率。
4、使用密钥登录远程服务器
- ssh使用密码
- ssh不使用密码,使用密钥
在上面的例子中,我们没有指定认证的方式,默认使用密码进行认证。在生产环境中一般不使用密码认证,一方面是因为密码认证没有密钥认证安全;另一方面,密码认证每次登录时都需要输入密码,比较繁琐。使用密钥认证,省去了输入密码的烦恼。因此,在生产环境中,一般会使用密钥进行登录。
密钥登录的原理也很简单,即事先将用户的公钥储存在远程服务器上(~/.ssh/authorized_keys文件)。使用密钥登录时,远程服务器会向用户发送一段随机字符串,SSH使用用户的私钥加密字符串后发送给远程服务器。远程服务器用事先储存的公钥进行解密,如果成功,就证明用户是可信的,直接允许登录Shell,不再要求密码。
OpenSSH除了提供服务端的sshd、客户端的SSH程序以外,还提供了若干与密钥认证相关的工其。其中,ssh-keygen是用来生成密钥对的工具。如下所示:
[ ~]# ssh-keygen
ssh-keygen执行完以后,用户的~/.ssh目录下会存在一个名为id_ rsa的私钥文件与一个名为id_rsa.pub的公钥文件。
接下来要做的是将公钥保存到远程服务器的~/.ssh/authorized_keys文件中。可以使用下面的命令将公钥保存到远程服务器的authorized_keys文件中:
[ ~]# ssh ‘mkdir -p .ssh && cat >>.ssh/authorized_keys‘ <~/.ssh/id_rsa.pub The authenticity of host ‘192.168.1.80 (192.168.1.80)‘ can‘t be established. ECDSA key fingerprint is SHA256:UbtfIBIs2tGQU1m/SIvSZX72VUQ+r1fH/aIMxVbdobg. ECDSA key fingerprint is MD5:56:10:49:b1:ae:f6:3c:d3:5e:2d:c5:d9:38:2c:45:e7. Are you sure you want to continue connecting (yes/no)? yes Warning: Permanently added ‘192.168.1.80‘ (ECDSA) to the list of known hosts. ‘s password: [ ~]#
上面的命令是使用Shell脚本的方式将公钥保存到远程服务器,除此之外,OpenSSH专门提供了一个名为ssh-copy-id的工具。我们可以使用该工具将公钥保存到远程服务器中,这种方式比前面Shell脚本的方式更加方便。如下所示:
[ ~]# [ ~]# ssh-copy-id -i ~/.ssh/id_rsa.pub 192.168.1.80 /usr/bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed /usr/bin/ssh-copy-id: INFO: 1 key(s) remain to be installed -- if you are prompted now it is to install the new keys ‘s password: Number of key(s) added: 1 Now try logging into the machine, with: "ssh ‘192.168.1.80‘" and check to make sure that only the key(s) you wanted were added. [ ~]# rm -rf ~/.ssh/config [ ~]# ssh 192.168.1.80 Last login: Fri May 15 15:16:23 2020 from 192.168.1.80 [ ~]#
配置私钥认证以后,就可以直接使用私钥进行登录。ssh命令会默认读取~/.ssh/id_rsa这个私钥文件。如果私钥文件保存在其他位置,或者是其他名称,可以使用-t参放指定私钥文件的地址。如下所示:
ssh -p 22 -i ~/私钥文件所在的位置(含文件名)
使用私钥登录时需要注意,私钥文件与远程服务器中authorized_keys文件的权限都必须为600,否则登录会出错,这也是工程师使用私钥登录时最容易遇到的错误。
测试登陆一下
[ ~]# vim ~/.ssh/config Host python ForwardAgent yes StrictHostKeyChecking no HostName 192.168.1.80 Port 22 User test Controlpath ~/.ssh/ssh-%%h:%p.sock
[ ~]# ssh python ‘s password: Last login: Fri May 15 14:49:17 2020 from fe80::c64e:c937:2ea8:6676%ens33 [ ~]$
查看公钥是否一致
[ ~]# ssh 192.168.1.80 Last login: Fri May 15 15:16:23 2020 from 192.168.1.80 [ ~]# cat ~/.ssh/authorized_keys #远程的 ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCWLJAYWCpbRPQX8gDbr2mIyXMw/qEKd46u4QhcaDPY7CeGd/buIGsWsuz+DAcnowk095rIwspGGHOdt54s+aeXGXcsRh7Hpuf0Py20Krim+v2LIUQW8vQSJDj1HiQUSnNQNPT3HAm0aqQp8u2EZ0StLYtf/uYSbg6rSzW08mKwhBQkrP0olWb+hD4ak3LxA05OI/WnanGKqtqjLg+4MbgGK96fY53dKvwrdt9NWiuof3pLgTw9fTvPU6CD+cH4LmRg8IVhthlBRhrXPA7oa8gvupTvpMYdNPPUSBsVR2rBcimrUFwdOpzb6T30C7o566noRd3t3nNxkQ/HrKalo9Bn [ ~]# exit 登出 Connection to python closed. [ ~]# cat ~/.ssh/id_rsa.pub #本地的 ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCWLJAYWCpbRPQX8gDbr2mIyXMw/qEKd46u4QhcaDPY7CeGd/buIGsWsuz+DAcnowk095rIwspGGHOdt54s+aeXGXcsRh7Hpuf0Py20Krim+v2LIUQW8vQSJDj1HiQUSnNQNPT3HAm0aqQp8u2EZ0StLYtf/uYSbg6rSzW08mKwhBQkrP0olWb+hD4ak3LxA05OI/WnanGKqtqjLg+4MbgGK96fY53dKvwrdt9NWiuof3pLgTw9fTvPU6CD+cH4LmRg8IVhthlBRhrXPA7oa8gvupTvpMYdNPPUSBsVR2rBcimrUFwdOpzb6T30C7o566noRd3t3nNxkQ/HrKalo9Bn
二、使用ssh-agent管理私钥
OpenSSH还提供了一个名为ssh-agent的程序,该程序可以简化SSH私钥的管理操作。ssh-agent是个长时间持续运行的守护进程(daemon),它的唯一目的就是对私钥进行高速缓存。
使用ssh-agent有以下几个好处:
(1)如果我们使用了一个加密的私钥,那么,使用这个私钥时将需要输入密码才能使用私钥文件。如果我们使用加密的私钥并且没有使用ssh-agent,那么将不得不在每次使用这个私钥时都输入密码。如果使用ssh-agent管理私钥,只需要在私钥加入到ssh-agent的那一刻输入密码,在之后的使用中都不用输入私钥的密码;
(2)如果我们有多台远程服务器与多个私钥文件,使用ssh-agent以后,不用在每次登录服务器时都使用-i参数指定使用哪一个私钥文件。ssh-agent将会尝试使用不同的私钥文件建立连接。直至成功;
(3)使用ssh-agent可以实现私钥转发功能。假设现在有三台服务器,分别是A, B, C。其中,A是我们的控制节点,我们可以在A上直接访问B,但是我们无法直接访问C。如果要访问C,就只能先登最B,再从B登录C。对于这种情况,是否需要在B中保存用户的私钥呢?对于这里的情况,我们可以使用agent forwarding。使用agent forwarding以后,不用将私钥保存到B服务器上,只需要在A中保存私钥,在B和C中保存公钥,便可在A中访问B与C这两台服务器。为了使用agent forwarding,我们必须使用ssh-agent管理私钥。
如果在Windows下使用Xshell进行SSH访问,要启动ssh-agent非常简单,只需要在“连接“-->"SSH“中勾选“使用密码处理的Xagent (SSH代理)”即可。
(1)先把原先的密钥删除并生成新的密钥
[ .ssh]# cd ~/.ssh/ [ .ssh]# rm -rf id* [ .ssh]# ssh-keygen Generating public/private rsa key pair. Enter file in which to save the key (/root/.ssh/id_rsa): Enter passphrase (empty for no passphrase): Enter same passphrase again: Your identification has been saved in /root/.ssh/id_rsa. Your public key has been saved in /root/.ssh/id_rsa.pub. The key fingerprint is: dc:57:da:10:e5:1a:8b:15:e3:98:61:d4:02:9f:56:8f The key‘s randomart image is: +--[ RSA 2048]----+ | .o+.=.. | | o.B.B | | *.E + | | . o o O | | S o = . | | . | | | | | | | +-----------------+ [ .ssh]# ssh-copy-id -i id_rsa.pub 192.168.1.80 /usr/bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed /usr/bin/ssh-copy-id: INFO: 1 key(s) remain to be installed -- if you are prompted now it is to install the new keys ‘s password: Number of key(s) added: 1 Now try logging into the machine, with: "ssh ‘192.168.1.80‘" and check to make sure that only the key(s) you wanted were added. [ .ssh]# ssh 192.168.1.80 Enter passphrase for key ‘/root/.ssh/id_rsa‘: Last login: Fri May 15 15:28:54 2020 from fe80::c64e:c937:2ea8:6676%ens33 [ ~]# exit 登出 Connection to 192.168.1.80 closed.
现在登陆需要私钥
(2)使用ssh-agent保存密钥
在Linux下,直接执行ssh-agent命令启动ssh-agent即可。启动以后,使用ssh-add命令将私钥添加到ssh-agent中。如下所示:
[ ~]# ssh-agent bash [ ~]# ssh-add ~/.ssh/id_rsa Enter passphrase for /root/.ssh/id_rsa: Identity added: /root/.ssh/id_rsa (/root/.ssh/id_rsa) [ ~]#
私钥添加完成后,可以执行ssh_add -L命令查看哪些私钥已经被添加到ssh-agent中。如下所示:
[ ~]# ssh-add -L ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDsP4VSOhnnCfidbg7e0OAQFcW5wAgPHld9S8KDTWv3X0/LNilYM3RkXQZ10XQ8Mw34i9rXa3SfqaHk6QYHXjNEUv6PEA/rKWY3kLXH9VUVHry4iwt9kVg9PowfccKLXPi8iWpqS7tk5ZEAnxihBtQattMTC44iz9X6hJDEn1r3r3YplJJGilIR+NaYJrM3ltxUBVuoJ82MfHOomhirc37ihLEwNbqRBMPYC4u1SoXDkagFsh0+HcuE0436yEByFxFw87jPmrjl7bgFsPahQsydrXySXOVdCzQJ8WuzJa1RvKr0xmgCjhZKExUYnMGAN9M79UBAyzZf2vUrwvvpPqWd /root/.ssh/id_rsa
启动ssh-agent以后,当我们尝试与远程服务器建立连接时,ssh客户端将会尝试使用存储在ssh-agent中的私钥与远程服务器进行认证。
[ ~]# ssh 192.168.1.80 Last login: Fri May 15 15:38:28 2020 from 192.168.1.80 [ ~]# exit 登出 Connection to 192.168.1.80 closed.
现在因为本地保持了私钥,所以不需要输入私钥了