paramiko模块使用姿势

Python paramiko 报错 paramiko.ssh_exception.SSHException: not a valid RSA private key file

报错的原因是选择的文件不是一个有效的 RSA 密钥文件
现在通过 ssh-keygen 默认生成的密钥文件是新的格式,并非 RSA 格式。打开文件可以看到是以 -----BEGIN OPENSSH PRIVATE KEY----- 开头的,模块无法识别。

解决方法

生成时指定格式:
通过 ssh-keygen -m PEM -t rsa 命令生成 RSA 格式的密钥对

ssh-keygen 参数

-m:参数指定密钥的格式,PEM(也就是 RSA 格式)是之前使用的旧格式

-b:指定密钥长度

-e:读取openssh的私钥或者公钥文件

-C:添加注释

-f:指定用来保存密钥的文件名

-i:读取未加密的ssh-v2兼容的私钥/公钥文件,然后在标准输出设备上显示openssh兼容的私钥/公钥

-l:显示公钥文件的指纹数据

-N:提供一个新密语

-P:提供(旧)密语

-q:静默模式

-t:指定要创建的密钥类型

参考:https://www.cnblogs.com/dbf-/p/11114264.htmlhttps://freelancing.studio/paramiko-and-rsa-key/

python ImportError: No module named paramiko

apt-cache search python* |grep paramiko

apt-get install python-paramiko

参考:https://blog.51cto.com/scpman/1117503

SSH与Python模块paramiko

paramiko是用python语言写的一个模块,遵循SSH2协议,支持以加密和认证的方式,进行远程服务器的连接。paramiko支持Linux, Solaris, BSD, MacOS X, Windows等平台通过SSH从一个平台连接到另外一个平台。利用该模块,可以方便的进行ssh连接和sftp协议进行sftp文件传输。

一、paramiko模块的安装

paramiko模块依赖PyCrypto模块,而PyCrypto需要GCC库编译,不过一般发行版的源里带有该模块。这里以ubuntu为例,直接借助以下命令可以直接完成安装:

sudo pip3 install paramiko 

二、paramiko的连接

使用paramiko模块有两种连接方式,一种是通过paramiko.SSHClient()函数,另外一种是通过paramiko.Transport()函数。

方法一:

import paramiko
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect("某IP地址",22,"用户名", "口令")

上面的第二行代码的作用是允许连接不在know_hosts文件中的主机。

方法二:

import paramiko
t = paramiko.Transport(("主机","端口"))
t.connect(username = "用户名", password = "口令")

如果连接远程主机需要提供密钥,上面第二行代码可改成:

t.connect(username = "用户名", password = "口令", hostkey="密钥")

三、paramiko ssh连接

以下是一个简单的通过paramiko模块定义的ssh连接并执行命令的函数,如下:

#!/usr/bin/python3
#-*- coding: utf-8 -*-
import paramiko
#paramiko.util.log_to_file('/tmp/sshout')
def ssh2(ip,username,passwd,cmd):
    try:
        ssh = paramiko.SSHClient()
        ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
        ssh.connect(ip,22,username,passwd,timeout=5)
        stdin,stdout,stderr = ssh.exec_command(cmd)
        # stdin.write("Y") #interact with server, typing Y 
        print(stdout.read())
        # for x in stdout.readlines():
        # print x.strip("n")
        print('%s OK\n'%(ip))
        ssh.close()
    except :
        print('%s Error\n' %(ip))

ssh2("45.76.22.112","root","password","ls")

其中第四行的日志部分,是记录ssh连接交互时的一些信息,可以看做是类似于debug的输出,一般情况下不需要开启。

stdin.write部分是用于交互情况下,通过该命令可以执行交互。注意这里可能会引起歧义,这里的交互并不是ssh连接过程中出现的让输入yes的交互,因为paramiko模块在连接过程中会自动处理好yes确认。这里的交互是指后面的cmd需要的执行的程序可能出现交互的情况下,可以通过该参数进行交互。

stdout标准输出,在输出内容比较少时,可以通过直接使用read读取出所有的输出;但在输出内容比较多时,建议通过按行读取进行处理。不过按行读取时,每行结尾会有换行符n,这样输出的结果很不美观。可以通过strip进行字符串的处理。
在函数调用过程中需要注意的是,**IP、username、passwd都是属于字符串型的,所以需要加引号。后面执行的cmd,如果有多个命令需要操作时,需要通过分号进行分割。

四、paramiko sftp示例

单个文件小传下载的示例:

import paramiko
#建立一个加密的管道
scp=paramiko.Transport(('192.168.0.102',22))
#建立连接
scp.connect(username='root',password='361way')
#建立一个sftp客户端对象,通过ssh transport操作远程文件
sftp=paramiko.SFTPClient.from_transport(scp)
#Copy a remote file (remotepath) from the SFTP server to the local host
sftp.get('/root/testfile','/tmp/361way')
#Copy a local file (localpath) to the SFTP server as remotepath
sftp.put('/root/crash-6.1.6.tar.gz','/tmp/crash-6.1.6.tar.gz')
scp.close()

一个目录下多个文件上传下载的示例:

#!/usr/bin/env python
#-*- coding: utf-8 -*-
import paramiko,datetime,os
hostname='192.168.0.102'
username='root'
password='361way'
port=22
local_dir='/tmp/getfile'
remote_dir='/tmp/abc'
try:
    t=paramiko.Transport((hostname,port))
    t.connect(username=username,password=password)
    sftp=paramiko.SFTPClient.from_transport(t)
    #files=sftp.listdir(dir_path)
    files=sftp.listdir(remote_dir)
    for f in files:
    print ''
    print '#########################################'
    print 'Beginning to download file from %s %s ' % (hostname,datetime.datetime.now())
    print 'Downloading file:',os.path.join(remote_dir,f)
    sftp.get(os.path.join(remote_dir,f),os.path.join(local_dir,f))#下载
    #sftp.put(os.path.join(local_dir,f),os.path.join(remote_dir,f))#上传
    print 'Download file success %s ' % datetime.datetime.now()
    print ''
    print '##########################################'
    t.close()
except Exception:
    print "connect error!" 

注:本处的目录下所有文件进行下载或上传的示例中,在遇到目录下还有嵌套的目录存在时,会将目录也当做文件进行处理,所以如果想要更加的完美的话,可以通过引入stat模块下的S_ISDIR方法进行处理
paramiko.transport对象也支持以socket的方式进行连接,如下示例:

import paramiko
transport = paramiko.Transport(('localhost',22))
transport.connect(username='root', password = 'password')
sftp = paramiko.SFTPClient.from_transport(transport)
sftp.get(remotefile,localfile)
#如果是上传则用:
#sftp.put(localfile, remotefile)
transport.close()
#用socket连接
tcpsock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
tcpsock.settimeout(5)
tcpsock.connect((ip,22),)
ssh = paramiko.Transport(tcpsock)
ssh.connect(username=user,password=password)
sftpConnect=paramiko.SFTPClient.from_transport(ssh) 

五、利用paramiko实现ssh的交互式连接

以下是通过paramiko模块直接用ssh协议登陆到远程服务器的操作代码,这里先定义一个interactive模块,代码如下:

import socket
import sys
# windows does not have termios...
try:
 import termios
 import tty
 has_termios = True
except ImportError:
 has_termios = False
def interactive_shell(chan):
 if has_termios:
 posix_shell(chan)
 else:
 windows_shell(chan)
def posix_shell(chan):
 import select
 oldtty = termios.tcgetattr(sys.stdin)
 try:
 tty.setraw(sys.stdin.fileno())
 tty.setcbreak(sys.stdin.fileno())
 chan.settimeout(0.0)
 while True:
 r, w, e = select.select([chan, sys.stdin], [], [])
 if chan in r:
 try:
 x = chan.recv(1024)
 if len(x) == 0:
 print 'rn*** EOFrn',
 break
 sys.stdout.write(x)
 sys.stdout.flush()
 except socket.timeout:
 pass
 if sys.stdin in r:
 x = sys.stdin.read(1)
 if len(x) == 0:
 break
 chan.send(x)
 finally:
 termios.tcsetattr(sys.stdin, termios.TCSADRAIN, oldtty)
# thanks to Mike Looijmans for this code
def windows_shell(chan):
 import threading
 sys.stdout.write("Line-buffered terminal emulation. Press F6 or ^Z to send EOF.rnrn")
 def writeall(sock):
 while True:
 data = sock.recv(256)
 if not data:
 sys.stdout.write('rn*** EOF ***rnrn')
 sys.stdout.flush()
 break
 sys.stdout.write(data)
 sys.stdout.flush()
 writer = threading.Thread(target=writeall, args=(chan,))
 writer.start()
 try:
 while True:
 d = sys.stdin.read(1)
 if not d:
 break
 chan.send(d)
 except EOFError:
 # user hit ^Z or F6
 pass

代码内容可以从paramiko 在github项目上的demo里获取。再另外写一个ssh_inter.py的交互主程序,内容如下:

import paramiko
import interactive
#记录日志
paramiko.util.log_to_file('/tmp/test')
#建立ssh连接
ssh=paramiko.SSHClient()
ssh.load_system_host_keys()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect('192.168.0.102',port=22,username='root',password='xxxxxx',compress=True)
#建立交互式shell连接
channel=ssh.invoke_shell()
#建立交互式管道
interactive.interactive_shell(channel)
#关闭连接
channel.close()
ssh.close()

执行效果就像我们平时直接使用ssh登录一样。

六、总结

paramiko模块是一个比较强大的ssh连接模块,以上的示例只是列出了该模块的一些简单的使用方法,还可以使用threading模块加块程序并发的速度;也可以使用configparser模块处理配置文件,而我们将所有IP、用户信息操作都放入配置文件;使用setproctitle模块为执行的程序加一个容易区分的title等。
同样,虽然连fabric这样大名鼎鼎的软件使用的ssh都是用paramiko模块进行的封装,不过你依然可以选择不使用它,你也可以选择pexpect模块实现封装一个简易的ssh连接工具、或者使用同样比较火的salt-ssh模块。
链接:https://www.jianshu.com/p/486dd9993125

SSH 密钥类型的的选择(RSA或DSA )

  在用 ssh-keygen 生成密钥对时,通常会面临是使用RSA还是DSA的选择:RSA or DSA, this is a question! 今天在这里分析一下:


原理与安全性

    RSA 与 DSA 都是非对称加密算法。其中RSA的安全性是基于极其困难的大整数的分解(两个素数的乘积);DSA 的安全性是基于整数有限域离散对数难题。基本上可以认为相同密钥长度的 RSA 算法与 DSA 算法安全性相当。

    有点要注意,RSA 的安全性依赖于大数分解,但是否等同于大数分解一直未能得到理论上的证明,因为没有证明破解 RSA 就一定需要作大数分解。不过也不必太过担心,RSA 从诞生以来,经历了各种攻击,至今未被完全攻破(依靠暴力破解,小于1024位密钥长度的 RSA 有被攻破的记录,但未从算法上被攻破)。


用途:

    DSA 只能用于数字签名,而无法用于加密(某些扩展可以支持加密);RSA 即可作为数字签名,也可以作为加密算法。不过作为加密使用的 RSA 有着随密钥长度增加,性能急剧下降的问题。


性能:

    相同密钥长度下,DSA 做签名时速度更快,但做签名验证时速度较慢,一般情况验证签名的次数多于签名的次数。

    相同密钥长度下,DSA (在扩展支持下)解密密文更快,而加密更慢;RSA 正好反过来,一般来说解密次数多于加密次数。不过由于非对称加密算法的先天性能问题,两者都不是加密的好选择。


业界支持:

    在业界支持方面,RSA 显然是赢家。RSA 具有更为广泛的部署与支持。


使用 ssh-keygen 时的选择:

    上面说了那么多,可以看到RSA 与 DSA 各有优缺点。回到开头的问题,在使用 ssh-keygen 时,RSA 与 DSA到底选哪个? 比较有意思的是,这个问题最终答案与上面那些优缺点无关。虽然理论上可以生成更长长度的 DSA 密钥 (NIST FIPS 186-3),但ssh-keygen在生成 DSA 密钥时,其长度只能为1024位(基于NIST FIPS 186-2);而 ssh-keygen 在 RSA 的密钥长度上没有限制。

     由于小于1024位密钥长度的 RSA 已经有被攻破的记录,所以说现在:RSA 2048 位密钥是更好的选择。

 

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值