Python自动化管理sshy介绍

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命令连接远程服务器。如下所示:

[root@python ~]# ssh xgp@localhost 
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
xgp@localhost's password: 
[xgp@python ~]$ 

如果服务器端不是使用默认的22端口,可以通过SSH命令的-p参数指定建立连接的端口号,格式如下所示:

ssh username@remote_host -p 端口号

我们也可以不进入交互式的Shell,直接使用ssh命令在远程服务器中执行Linux命令,如下所示:

[root@python ~]# ssh [email protected] 'date'
[email protected]'s password: 
2020年 05月 15日 星期五 15:55:08 CST
s
[root@python ~]#

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文件

[root@python ~]# find / -name ssh_config          #查看找ssh_config 文件
/etc/ssh/ssh_config
[root@python ~]# 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地址。在下次登录时,可以省去输入的烦恼。如下所示:

[root@python ~]# vim /opt/login.sh
#!/usr/bin/bash

ssh [email protected] -p 22

[root@python ~]# useradd test
[root@python ~]# passwd test
更改用户 test 的密码 。
新的 密码:
无效的密码: 密码少于 8 个字符
重新输入新的 密码:
passwd:所有的身份验证令牌已经成功更新。
[root@python ~]# 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.
[email protected]'s password: 
[test@python ~]$ 

对于这里的需求,还有更好的解决方案。命令会读取~/.ssh/config文件中配置,因此,我们可以在~/.ssh/config文件中提前配置好访问远程服务器的信息。如下所示:

[root@python ~]# cp /etc/ssh/ssh_config -p  ~/.ssh/config
[root@python ~]# vim  ~/.ssh/config 

Host python
  ForwardAgent yes
  StrictHostKeyChecking no
  port 22
  User test
  Controlpath ~/.ssh/ssh-%r@%h:%p.sock

Host *
  StrictHostKeyChecking no
  HostName %h
  Port  22
  User  test
  Controlpath ~/.ssh/ssh-%r@%h:%p.sock

配置完成后,直接在命令行执行ssh host2就可以使用用户名test,以及端口号2092登录到10.166.224.14中。此外,我们还使用通配符的方式定义了ssh默认用户名与端口号。假设我们要使用用户名laoyu、端口号2092访问10.166.226.153,有了前面的配置以后,可以在命令行直接进行登录。如下所示:

[root@python ~]# ssh python
Warning: Permanently added 'python,fe80::c64e:c937:2ea8:6676%ens33' (ECDSA) to the list of known hosts.
test@python's password: 
Permission denied, please try again.
test@python's password: 
Last login: Fri May 15 14:34:54 2020 from localhost
[test@python ~]$ 

可以看到,我们只需要在~/.ssh/config文件中进行简单的配置就能够有效提高工作效率。

4、使用密钥登录远程服务器

  • ssh使用密码
  • ssh不使用密码,使用密钥

在上面的例子中,我们没有指定认证的方式,默认使用密码进行认证。在生产环境中一般不使用密码认证,一方面是因为密码认证没有密钥认证安全;另一方面,密码认证每次登录时都需要输入密码,比较繁琐。使用密钥认证,省去了输入密码的烦恼。因此,在生产环境中,一般会使用密钥进行登录。

密钥登录的原理也很简单,即事先将用户的公钥储存在远程服务器上(~/.ssh/authorized_keys文件)。使用密钥登录时,远程服务器会向用户发送一段随机字符串,SSH使用用户的私钥加密字符串后发送给远程服务器。远程服务器用事先储存的公钥进行解密,如果成功,就证明用户是可信的,直接允许登录Shell,不再要求密码。

OpenSSH除了提供服务端的sshd、客户端的SSH程序以外,还提供了若干与密钥认证相关的工其。其中,ssh-keygen是用来生成密钥对的工具。如下所示:

[root@python ~]# ssh-keygen 

python自动化管理sshy(ssh,ssh-copy-id,ssh-agent)_第1张图片

ssh-keygen执行完以后,用户的~/.ssh目录下会存在一个名为id_ rsa的私钥文件与一个名为id_rsa.pub的公钥文件。

接下来要做的是将公钥保存到远程服务器的~/.ssh/authorized_keys文件中。可以使用下面的命令将公钥保存到远程服务器的authorized_keys文件中:

[root@python ~]#  ssh [email protected] '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.
[email protected]'s password: 
[root@python ~]# 

上面的命令是使用Shell脚本的方式将公钥保存到远程服务器,除此之外,OpenSSH专门提供了一个名为ssh-copy-id的工具。我们可以使用该工具将公钥保存到远程服务器中,这种方式比前面Shell脚本的方式更加方便。如下所示:

[root@python ~]# 
[root@python ~]# 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
[email protected]'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.

[root@python ~]# rm -rf  ~/.ssh/config
[root@python ~]# ssh 192.168.1.80
Last login: Fri May 15 15:16:23 2020 from 192.168.1.80
[root@python ~]# 

python自动化管理sshy(ssh,ssh-copy-id,ssh-agent)_第2张图片

配置私钥认证以后,就可以直接使用私钥进行登录。ssh命令会默认读取~/.ssh/id_rsa这个私钥文件。如果私钥文件保存在其他位置,或者是其他名称,可以使用-t参放指定私钥文件的地址。如下所示:

ssh [email protected] -p 22 -i ~/私钥文件所在的位置(含文件名)

使用私钥登录时需要注意,私钥文件与远程服务器中authorized_keys文件的权限都必须为600,否则登录会出错,这也是工程师使用私钥登录时最容易遇到的错误。

测试登陆一下

[root@python ~]# vim ~/.ssh/config 

Host python
  ForwardAgent yes
  StrictHostKeyChecking no
  HostName 192.168.1.80
  Port 22
  User test
  Controlpath ~/.ssh/ssh-%r@%h:%p.sock

python自动化管理sshy(ssh,ssh-copy-id,ssh-agent)_第3张图片

[root@python ~]# ssh python
[email protected]'s password: 
Last login: Fri May 15 14:49:17 2020 from fe80::c64e:c937:2ea8:6676%ens33
[test@python ~]$ 

查看公钥是否一致

[root@python ~]# ssh 192.168.1.80
Last login: Fri May 15 15:16:23 2020 from 192.168.1.80

[root@python ~]# cat ~/.ssh/authorized_keys        #远程的
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCWLJAYWCpbRPQX8gDbr2mIyXMw/qEKd46u4QhcaDPY7CeGd/buIGsWsuz+DAcnowk095rIwspGGHOdt54s+aeXGXcsRh7Hpuf0Py20Krim+v2LIUQW8vQSJDj1HiQUSnNQNPT3HAm0aqQp8u2EZ0StLYtf/uYSbg6rSzW08mKwhBQkrP0olWb+hD4ak3LxA05OI/WnanGKqtqjLg+4MbgGK96fY53dKvwrdt9NWiuof3pLgTw9fTvPU6CD+cH4LmRg8IVhthlBRhrXPA7oa8gvupTvpMYdNPPUSBsVR2rBcimrUFwdOpzb6T30C7o566noRd3t3nNxkQ/HrKalo9Bn root@python

[root@python ~]# exit
登出
Connection to python closed.

[root@python ~]# cat ~/.ssh/id_rsa.pub              #本地的
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCWLJAYWCpbRPQX8gDbr2mIyXMw/qEKd46u4QhcaDPY7CeGd/buIGsWsuz+DAcnowk095rIwspGGHOdt54s+aeXGXcsRh7Hpuf0Py20Krim+v2LIUQW8vQSJDj1HiQUSnNQNPT3HAm0aqQp8u2EZ0StLYtf/uYSbg6rSzW08mKwhBQkrP0olWb+hD4ak3LxA05OI/WnanGKqtqjLg+4MbgGK96fY53dKvwrdt9NWiuof3pLgTw9fTvPU6CD+cH4LmRg8IVhthlBRhrXPA7oa8gvupTvpMYdNPPUSBsVR2rBcimrUFwdOpzb6T30C7o566noRd3t3nNxkQ/HrKalo9Bn root@python

python自动化管理sshy(ssh,ssh-copy-id,ssh-agent)_第4张图片

二、使用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)先把原先的密钥删除并生成新的密钥

[root@python .ssh]# cd ~/.ssh/
[root@python .ssh]# rm -rf id*
[root@python .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 root@python
The key's randomart image is:
+--[ RSA 2048]----+
|        .o+.=..  |
|         o.B.B   |
|          *.E +  |
|       . o o O   |
|        S o = .  |
|           .     |
|                 |
|                 |
|                 |
+-----------------+
[root@python .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
[email protected]'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.

[root@python .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
[root@python ~]# exit
登出
Connection to 192.168.1.80 closed.

现在登陆需要私钥

(2)使用ssh-agent保存密钥

在Linux下,直接执行ssh-agent命令启动ssh-agent即可。启动以后,使用ssh-add命令将私钥添加到ssh-agent中。如下所示:

[root@python ~]# ssh-agent bash
[root@python ~]# ssh-add ~/.ssh/id_rsa
Enter passphrase for /root/.ssh/id_rsa: 
Identity added: /root/.ssh/id_rsa (/root/.ssh/id_rsa)
[root@python ~]# 

私钥添加完成后,可以执行ssh_add -L命令查看哪些私钥已经被添加到ssh-agent中。如下所示:

[root@python ~]# ssh-add -L
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDsP4VSOhnnCfidbg7e0OAQFcW5wAgPHld9S8KDTWv3X0/LNilYM3RkXQZ10XQ8Mw34i9rXa3SfqaHk6QYHXjNEUv6PEA/rKWY3kLXH9VUVHry4iwt9kVg9PowfccKLXPi8iWpqS7tk5ZEAnxihBtQattMTC44iz9X6hJDEn1r3r3YplJJGilIR+NaYJrM3ltxUBVuoJ82MfHOomhirc37ihLEwNbqRBMPYC4u1SoXDkagFsh0+HcuE0436yEByFxFw87jPmrjl7bgFsPahQsydrXySXOVdCzQJ8WuzJa1RvKr0xmgCjhZKExUYnMGAN9M79UBAyzZf2vUrwvvpPqWd /root/.ssh/id_rsa

启动ssh-agent以后,当我们尝试与远程服务器建立连接时,ssh客户端将会尝试使用存储在ssh-agent中的私钥与远程服务器进行认证。

[root@python ~]# ssh 192.168.1.80
Last login: Fri May 15 15:38:28 2020 from 192.168.1.80
[root@python ~]# exit
登出
Connection to 192.168.1.80 closed.

现在因为本地保持了私钥,所以不需要输入私钥了

python自动化管理sshy(ssh,ssh-copy-id,ssh-agent)_第5张图片