想得到你从未拥有过的东西, 就必须做你从未做过的事情。

Python 自动化运维模块 Pramiko

Python,Linux 2017-05-01 浏览量: 3593 字数统计: 1303 最后更新: 2017-05-02 09:07

文章目录[显示]

首先在使用 paramiko 之前假设咱都 知道什么是 SSH 了,于是SSH 的 文字性介绍 就简单带过几句话

SSH(Secure Shell)就是 一项创建在应用层和传输层基础上的安全协议,为计算机上的Shell(壳层)提供安全的传输和使用环境。

  • 在安全性上来讲,SSH要比 传统的网络服务程序,如FTP、POP和Telnet安全,因为FTP、POP和Telnet等 在网络上用明文传送数据、用户帐号和用户口令,很容易受到中间人(man-in-the-middle)攻击方式的攻击。SSH 是专为远程登录会话和其他网络服务提供安全性的协议。但是 SSH把被MITM(man-in-the-middle)的风险转接给了用户,所以如果某天遇到了提示指纹变了那么 你就可能被 中间人攻击截取了 你和服务器之间的通信,这个时候可以 查一下服务器的日志,看看有没有可疑IP 了
  • 在传输速度上来说 SSH 传输的数据可以是经过压缩的,所以可以加快传输的速度。

ssh 基本使用

ssh默认22端口,当然是可以改的
在/etc/ssh/sshd_config 文件, 找到 #Port 22 这一行,去掉前面的注释,自己设定一个数。然后重启sshd 服务
systemctl restart sshd.service
于是就可以使用自己设置的端口号连接服务器了

ssh -p port [email protected]

如果本地用户名和远程用户名一致,也可以省略用户名,当然如果你的端口号是默认的 22,那么也可以省略端口号
ssh host

上面的连接是 使用密码连接服务器,SSH 提供了另一种登录方式叫做 公钥登录

什么是SSH 公钥登录

SSH 密码认证过程中包含服务器自己的一对公私钥,我们称为Pub1, Pri1, 这个是自动生成的不用我们干预;另一对用ssh-keygen我们称作Pub2, Pri2,密钥认证的正确过程是这样的

服务器生成一个随机数,使用我们的Pub2将其加密,然后发送给客户,客户使用私钥Pri2解密,得到随机数;客户端再使用自动的Pub1加密,将其传回;服务器使用Pri 1解密,如果和生成的随机数一致,则认证通过。

实现公钥登录

  • 首先生成一对密钥
[[email protected] ~]# 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:
17:c6:6d:72:e1:d5:dd:3f:c4:c1:ea:99:9d:b9:81:dd [email protected]
The key's randomart image is:
+--[ RSA 2048]----+
|            . +o+|
|         . o o ++|
|          = = o .|
|         . = . ..|
|        S . . * =|
|         .   = *E|
|                o|
|               . |
|                 |
+-----------------+
  • 将公钥保存到 ~/.ssh/authorized_keys文件
[[email protected] ~ ]# cat /root/.ssh/id_rsa.pub > /root/.ssh/authorized_keys 
  • 把私钥 (/root/.ssh/id_rsa )复制到自己的电脑上
  • 公钥登录
ssh -i 私鈅地址 用户名@IP

但是登录的时候 可能会遇到这样的问题

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@         WARNING: UNPROTECTED PRIVATE KEY FILE!          @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
Permissions 0664 for 'XXXXXXXXXXXXXXXXXX' are too open.
It is required that your private key files are NOT accessible by others.
This private key will be ignored.
Load key "XXXXXXXXXXXXXXXXXXXX": bad permissions

这是说你的私钥权限不对
于是更改一下私钥文件权限

$ chmod 600 <本地私钥文件路径>>

这也就是说

authorized_keys文件和id_rsa文件要求权限为 600 / 400 /700(基本上700就算了,也用不着执行) ,反正就是只能自己操作 读写 ,所属的组和其它人看都不能操作,看也不行!

使用ssh配置文件简化操作

当你需要的命令参数复杂的时候,可能就需要简化一些操作

所以这个时候ssh客户端可以提供 一些配置文件来简化这些操作


[email protected]:~/.ssh$ touch ~/.ssh/config
[email protected]:~/.ssh$ vi ~/.ssh/config 

Host lmy
    HostName <服务器的主机名或 IP 地址>
    Port <端口>
    User <用户名>
    PreferredAuthentications  publickey  # 这里选择公钥验证
    IdentityFile <私钥文件路径>
    # scp 复用 ssh 的连接
    Controlpath ~/.ssh/ssh-%[email protected]%h:%p.sock   

然后ssh lmy就可以登录你的服务器了

还可以scp把 本地文件/ 文件夹 拷贝到远程服务器


$ scp 本地文件 远程主机(就是Host后面的名字):远程文件
$ scp -r 本地文件夹  远程主机(就是Host后面的名字):远程文件夹

比如说俺想把test.py文件传到远程服务器,文件夹也是同理

$ scp test.py  step:test.py
test.py                                       100%    0     0.0KB/s   00:00    

paramiko

好,铺垫做完了,咱开始进入正题了

paramiko 是啥

paramiko实现了SSH协议,能够方便地与远程计算机交互。就是说 在 终端 下执行的语句,也可以通过 python 的 paramiko 实现

例えば:(比如说)下面这个 shell语句用 paramiko实现一下
ssh -i <私钥地址> [email protected] -e 'ls'

安装

$ pip install paramiko

连接

可以使用私鈅连接也可以直接使用密码连接

使用私钥连接

import paramiko
ssh_client = paramiko.SSHClient()
ssh_client.connect('主机IP', port, 'username',key_filename='私钥地址')

可能会遇到这这样的错误

`paramiko.ssh_exception.SSHException: Server '123.206.175.183' not found in known_hosts
`
说明主机没有在known_hosts 里面

执行
ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy())

重新连接

ssh_client.connect('IP', 22, 'root',key_filename='XXX')

使用密码连接

import paramiko
ssh_client = paramiko.SSHClient()
#ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh_client.connect("IP",  port, "username", "password")

执行shell命令

>>> ssh_client.exec_command('ls')
(<paramiko.ChannelFile from <paramiko.Channel 0 (open) window=2097152 -> <paramiko.Transport at 0x8a43d450L (cipher aes128-ctr, 128 bits) (active; 1 open channel(s))>>>, <paramiko.ChannelFile from <paramiko.Channel 0 (open) window=2097152 -> <paramiko.Transport at 0x8a43d450L (cipher aes128-ctr, 128 bits) (active; 1 open channel(s))>>>, <paramiko.ChannelFile from <paramiko.Channel 0 (open) window=2097152 -> <paramiko.Transport at 0x8a43d450L (cipher aes128-ctr, 128 bits) (active; 1 open channel(s))>>>)

但是你会发现 ,这TM是啥。
别急,这玩意在 执行shell命令以后,并不会马上就 打印执行结果,而是返回几个 ChannelFile , 如果你想获得文件 你 ls 的 结果 ,可以这样输出:

# stdin 标准输入
# stdout 标准输出
# stderr  错误输出

>>> stdin, stdout, stderr =ssh_client.exec_command('ls')

# 打印输出结果
>>> print stdout.readlines()
[u'craw.sh\n', u'glances\n', u'jpg_links.txt\r\n', u'lolcat-master\n', u'mail_2.py\n', u'mail.py\n', u'master.zip\n', u'test.py\n', u'viteza.py\n']

注意: 当 命令执行出错的时候 并不会直接 抛出异常,而是在 stderr里面打印错误信息

# 假设没有 lllllll 这个命令
>>> stdin, stdout, stderr =ssh_client.exec_command('lllllll')
>>> print stdout.readlines()
[]
>>> print stderr.readlines()
[u'bash: lllllll: command not found\n']

paramiko sftp 传文件

通过paramiko可以传输文件,新建一个SFTPClient对象,该对象复用之前的SSH连接

连接

sftp = paramiko.SFTPClient.from_transport(ssh_client.get_transport())

下载文件

# 第一个参数是服务器上的文件名字,
# 第二个参数是下载到本地的文件名
>>> sftp.get('mail.py','mail_copy.py')

上传文件

# 第一个参数是需要上传的 文件名
# 第二个参数是上传到服务器上面的文件名

>>> sftp.put('test', 'test_copy')
<SFTPAttributes: [ size=1871 uid=0 gid=0 mode=0100644 atime=1493621636 mtime=1493621636 ]>

查看管理多台服务器

通过hosts文件 管理多台服务器

[email protected]:~/keys$ cat hosts 
IP1    port    username1    password1
IP2    port    username2    password2

提供

[email protected]:~/keys$ cat test.py 
#!/usr/bin/python 

import paramiko 

with open('hosts', 'r') as f:
    for line in f.readlines():
        hostname=str(line.split('\t')[0])
        port=int(line.split('\t')[1]) 
        username=str(line.split('\t')[2])
        password=str(line.split('\t')[3]).strip() 
        print ('\033[32m--------------\033[0m'),hostname,('\033[32m--------------\033[0m')
        ssh_client=paramiko.SSHClient() 
        ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) 
        ssh_client.connect(hostname,port,username,password) 
        stdin,stdout,sterr=ssh_client.exec_command("ls") 
        print stdout.read()
        # 关闭连接! 
        ssh_client.close()

print ('\033[35m--------------\033[0m'),'END',('\033[35m--------------\033[0m')

参数形式提供执行命令

[email protected]:~/keys$ cat test.py 
#!/usr/bin/python 

import paramiko 
import sys 

command = " ".join(sys.argv[1:])
with open('hosts', 'r') as f:
    for line in f.readlines():
        hostname=str(line.split('\t')[0])
        port=int(line.split('\t')[1]) 
        username=str(line.split('\t')[2])
        password=str(line.split('\t')[3]).strip() 
        print ('\033[32m--------------\033[0m'),hostname,('\033[32m--------------\033[0m')
        ssh_client=paramiko.SSHClient() 
        ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) 
        ssh_client.connect(hostname,port,username,password) 
        stdin,stdout,sterr=ssh_client.exec_command("ls") 
        print stdout.read()
        # 关闭连接! 
        ssh_client.close()

print ('\033[35m--------------\033[0m'),'END',('\033[35m--------------\033[0m')

运行结果

[email protected]:~/keys$ python test.py ls
-------------- IP1 --------------

glances
jpg_links.txt
lolcat-master
mail.py
master.zip
test.py
viteza.py

-------------- IP2 --------------
mail.py
python_test
test

-------------- END --------------

# 查看磁盘信息
[email protected]:~/keys$ python test.py df -h
-------------- IP1 --------------
/dev/xvda1       40G  6.7G   31G  18% /
devtmpfs        487M     0  487M   0% /dev
tmpfs           496M     0  496M   0% /dev/shm
tmpfs           496M   57M  440M  12% /run
tmpfs           496M     0  496M   0% /sys/fs/cgroup
tmpfs           100M     0  100M   0% /run/user/0

-------------- IP2 --------------
Filesystem      Size  Used Avail Use% Mounted on
udev            414M     0  414M   0% /dev
tmpfs            87M  9.3M   78M  11% /run
/dev/vda1        20G  1.7G   17G   9% /
tmpfs           433M     0  433M   0% /dev/shm
tmpfs           5.0M     0  5.0M   0% /run/lock
tmpfs           433M     0  433M   0% /sys/fs/cgroup
tmpfs            87M     0   87M   0% /run/user/500

-------------- END --------------


参考资料

  1. https://paramiko-docs.readthedocs.io/en/1.15/index.html
  2. https://blog.yoitsu.moe/linux/connect_through_ssh.html
小蜗牛 说:
Freedom is the source from which all meaning and all values spring .


文章版权归 原文作者所有丨本站默认采用CC-BY-NC-SA 4.0协议进行授权| 转载必须包含本声明,并以超链接形式注明原文作者和本文原始地址: https://www.tougetu.com/2017/05/python-paramiko-ssh.html

7 条评论

  1. 小土豆

    我记得我跟你说过SSH没有可靠的身份认证机制(不像SSL/TLS有PKI体系),所以SSH把被MITM的风险转接给了用户,所以如果某天遇到了提示指纹变了一定要仔细思考原因。

  2. 小土豆

    还有啊,你这公钥认证的原理是在哪找的,大错特错啊我说。我们知道SSH是加密通道,所谓加密通道就是要想办法在不安全的网络中用某种办法交换一个会话密钥。

    你文中所述

    服务器向用户发送一段随机字符串,用户用自己的私钥加密后……

    服务器所发送的这个随机字符串,实际上就相当于认证信息,是可以被中间人篡改的(明文啊),而且双方都不会有任何察觉。尽管中间人没法冒充(没有私钥)但是中间人可以篡改字符串来阻止连接啊,而且这是单向通信了还安全个毛线啊。

    而且还有问题:

    远程主机用事先储存的公钥进行解密,如果成功……

    解密是一定能够成功的,只不过当密钥不对、密文错误时看起来像是“乱码”,实际上对实现来说就是失败。这里如果非得纠错,那就是“如果用公钥解密之后和生成的随机数一致则认证通过”,然而大前提是错的,改正这里也没用。

    SSH认证密码认证过程中包含服务器自己的一对公私钥,我们称为Pub1, Pri1, 这个是自动生成的不用我们干预;另一对用ssh-keygen我们称作Pub2, Pri2,所以密钥认证的正确过程是
    服务器生成一个随机数,使用我们的Pub2将其加密,然后发送给客户,客户使用私钥Pri2解密,得到随机数;客户端再使用自动的Pub1加密,将其传回;服务器使用Pri 1解密,如果和生成的随机数一致,那么就是认证通过。

    由于公钥密码的严谨性,获取到密文是没用的;如果有人想要中间人,那么会导致指纹变化。

    我搜了一圈,估计大家伙都是被阮一峰的某篇博文忽悠了吧。唉,这种低级错误不应该啊……我看原文评论有几个人质疑。

    不好意思长篇评论了。

    1. 小蜗牛

      谢谢土豆同学指出的错误 , 俺改正了一下

      1. 小土豆

        其实这个过程还不是特别严谨,只能说大概是这么思路吧。完整的要包括密钥交换、身份认证,怪复杂的…

        1. 小蜗牛

          那估计需要另码一篇了...

          1. 小土豆

            等过几天我码一篇吧,顺便谈谈读书的问题,哈哈

          2. 小蜗牛

            吼啊!

添加新评论

代码 Pastebin Gist 加粗 删除线 斜体 链接 签到