11 Python编程:从入门到实践---paramiko模块详解
paramiko
是基于Python实现的SSH2远程安全连接,支持认证及秘钥方式。可以实现远程命令执行、文件传输、中间SSH代理功能,相当于Pexpect,封装的层次更高,更贴近SSH协议的功能。
paramiko是第三方模块,需要进行安装后使用。
SSHClient类示例:
通过使用用户名,密码的方式,通过exec_commands()方法执行命令。
1 #!/usr/bin/evn python 2 # --*-- coding: utf-8 --*-- 3 # Auther : Liu WeiDong 4 import paramiko 5 6 hostname = '192.168.2.22' 7 username = 'root' 8 password = 'SKJh935yft#' 9 paramiko.util.log_to_file('syslogin.log') #发送paramiko日志到syslogin.log文件 10 11 ssh = paramiko.SSHClient() #创建一个ssh客户端client对象 12 ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) #允许连接不在know_hosts文件中的主机 13 ssh.load_system_host_keys() # 获取客户端host_keys,默认~/.ssh/know_hosts,非默认路径需要指定 14 15 ssh.connect(hostname=hostname,username=username,password=password) #创建ssh连接 16 stdin,stdout,stderr = ssh.exec_command('free -m') #调用远程执行命令方法exec_command() 17 print(stdout.read()) #打印命令执行结果,得到python列表形式,可以使用stdout.readlines() 18 ssh.close() #关闭ssh连接
SSHClient类方法讲解
connect方法
connect(hostname, port=22, username=None, password=None, pkey=None, key_filename=None, timeout=None, allow_agent=True, look_for_keys=True, compress=False, sock=None, gss_auth=False, gss_kex=False, gss_deleg_creds=True, gss_host=None, banner_timeout=None, auth_timeout=None, gss_trust_dns=True, passphrase=None, disabled_algorithms=None)
参数说明:
- hostname(str类型),连接目标主机IP地址或主机名
- port(int类型),连接目标主机的端口,默认为22
- username(str类型),校验的用户名(默认为当前的本地用户名)
- password(str类型),密码用于身份校验或解锁私钥
- pkey,私钥方式用于身份验证
- key_filename(str or list(str)类型),一个文件名或文件名的列表,用于私钥的身份验证;
- timeout(float类型),一个可选的超时时间(以秒为单位)的TCP连接
- allow_agent(bool类型),设置为False时用于禁用连接到SSH代理
- look_for_keys(bool类型),设置为False时用来禁用在~./ssh中搜索秘钥文件;
- compress(bool类型),设置为True时打开压缩
exec_command方法
exec_command(command, bufsize=-1, timeout=None, get_pty=False, environment=None)
- command(str类型),执行的命令串
- bufsize(int类型),文件缓冲区大小,默认为-1(不限制)
load_system_host_keys
load_system_host_keys(filename=None)
加载本地公钥校验文件,默认为~/.ssh/known_hosts,非默认路径需要收工指定
- filename(str类型),指定远程主机公钥记录文件
set_missing_host_key_policy方法
set_missing_host_key_policy(policy)
设置连接远程主机没有本地主机秘钥或HostKeys对象时的策略,目前支持三种方式,分别是RejectPolicy(默认),AutoAddPolicy、WarningPolicy,仅限用于SSHClient类,分别代表如下含义
- AutoAddPolicy,自动添加主机名及主机秘钥到本地HostKeys对象,并将其保存,不依赖load_system_host_keys,即使~/.ssh/known_hosts不存在也产业影响
- RejectPolicy(默认),自动拒绝未知的主机名或秘钥,依赖load_system_host_keys()配置
- WarningPolicy,用于记录一个未知的主机秘钥的Python警告,并接受它,功能上与AutoAddPolicy相似,但未知主机会有告警。
SFTPClient类示例
下面为SFTPClient类的一个完整示例,实现文件上传,下载,创建与删除目录等,需要注意的是put和get方法需要指定文件名,不能省略。
1 #!/usr/bin/evn python 2 # --*-- coding: utf-8 --*-- 3 # Auther : Liu WeiDong 4 5 import paramiko 6 7 username = "root" 8 password = "KJSD8T34D" 9 hostname = "192.168.1.22" 10 port = 22 11 12 try: 13 t = paramiko.Transport((hostname,port)) 14 t.connect(username=username,password=password) 15 sftp = paramiko.SFTPClient.from_transport(t) 16 sftp.put("/home/user/info.db","/data/user/info.db") #上传文件 17 sftp.get("/data/user/info_1.db","/home/user/info_1.db") #下载文件 18 sftp.mkdir("/home/userdir","0755") #创建目录 19 sftp.rmdir("/home/userdir") #删除目录 20 sftp.rename("/home/test.sh","/home/testfile.sh") #文件重命名 21 print(sftp.stat("/home/testfile.sh")) #打印文件信息 22 print(sftp.listdir("/home")) #打印目录列表 23 t.close() 24 except Exception as e: 25 print(str(e))
SFTPClient类方法讲解
from_transport方法
创建一个已连通的SFTP客户端通道,方法定义:
from_transport(cls,t)
参数说明:
t(Transport),一个已通过验证的传输对象。
例子说明:
t = paramiko.Transport((hostname,port))
t.connect(username=username,password=password)
sftp = paramiko.SFTPClient.from_transport(t)
put方法
上传本地文件到远程SFTP服务器
get方法
从远程SFTP服务端下载文件到本地
其他方法
- mkdir,在SFTP服务器端创建目录
- remove,删除SFTP服务器端制定目录
- rename,重命名SFTP服务器端文件或目录
- stat,获取远程SFTP服务器端制定文件信息
- listdir,获取远程SFTP服务器端指定目录列表
paramiko应用示例
实现秘钥方式登录远程主机
实现自动秘钥登录方式,第一步需要配置与目标设备的秘钥认证支持(即主机间ssh互信),私钥文件可以存放在默认路径“~/.ssh/id_rsa”,也可以自定义路径,通过paramiko.RSAKey.from_private_key_file()方法引用,代码详解如下:
1 #!/usr/bin/evn python 2 # --*-- coding: utf-8 --*-- 3 # Auther : Liu WeiDong 4 5 import paramiko 6 import os 7 8 hostname = '192.168.1.22' 9 username = 'root' 10 paramiko.util.log_to_file('syslogin.log') 11 12 ssh = paramiko.SSHClient() 13 ssh.load_system_host_keys() 14 ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) 15 privatekey = os.path.expanduser('/home/key/id_rsa') 16 key = paramiko.RSAKey.from_private_key_file(privatekey) 17 18 ssh.connect(hostname=hostname,username=username,pkey=key) 19 stdin,stdout,stderr = ssh.exec_command('free -m') 20 print(stdout.read()) 21 ssh.close()
实现堡垒机模式下的远程命令执行
堡垒机环境在一定程度上提升了运营安全级别,但同时也提高了日常运营成本,作为管理的中转设备,任何针对业务服务器的请求都需要经过此节点,比如ssh协议。首先运维人员在办公电脑通过ssh登录堡垒机,在由堡垒机SSH跳转到所有业务服务器进行维护操作。
我们通过paramiko的invoke_shell方法来实现通过堡垒机登录服务器操作,原理是SSHClient.connect到堡垒机后开启一个新的SSH会话,通过新的会话运行“ssh user@IP”去实现远程命令执行操作,代码如下
1 #!/usr/bin/evn python 2 # --*-- coding: utf-8 --*-- 3 # Auther : Liu WeiDong 4 5 import paramiko 6 import os,sys,time 7 8 #定义堡垒机信息 9 blip = "192.168.1.23" 10 bluser = "root" 11 blpasswd = "Kd03dfd" 12 13 #定义通过堡垒机连接的业务主机信息 14 hostname = "192.168.1.22" 15 username = "root" 16 password = "OD908k" 17 port = 22 18 19 #输入服务器登录前输入密码提示 20 passinfo = "\'s password: " 21 paramiko.util.log_to_file("syslogin.log") 22 23 #登录堡垒机 24 ssh = paramiko.SSHClient() 25 ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) 26 ssh.connect(hostname=blip,username=bluser,password=blpasswd) 27 28 #创建会话,开启命令调用 29 channel = ssh.invoke_shell() 30 #定义命令执行超时时间,单位为秒 31 channel.settimeout(10) 32 33 buff = "" 34 resp = "" 35 #执行ssh登录业务主机 36 channel.send("ssh "+username+"@"+hostname+"\n") 37 38 #ssh登录的提示信息判断,输出字符串尾含有's password: 若是以password结尾,则进入循环,否则不进入循环 39 while not buff.endswith(passinfo): 40 try: 41 resp = channel.recv(9999) 42 except Exception as e: 43 print("Error info:%S connection time." % (str(e))) 44 channel.close() 45 ssh.close() 46 sys.exit() 47 buff += resp 48 # 判断输出字符串结尾如果含有“yes/no”时发送“yes”并回车 49 if not buff.find("yes/no") == -1: #find方法,发现子字符串包含在字符串内,则返回字符串索引值,否则返回-1 50 channel.send("yes\n") 51 buff = "" 52 #发送业务主机密码并回车 53 channel.send(password+"\n") 54 55 buff = "" 56 #输出的字符串为“#”号,说明校验通过不进入循环 57 while not buff.endswith("# "): 58 resp = channel.recv(9999) 59 # 输出字符串含有“password”,说明密码不正确,要求重新输入 60 if not resp.find(passinfo) == -1: 61 print("Error info: Authentication failed.") 62 channel.close() 63 ssh.close() 64 sys.exit() 65 buff += resp 66 67 # 认证通过后发送ifconfig命令查看结果 68 channel.send("ifconfig\n") 69 70 buff = "" 71 try: 72 while buff.find("# ") == -1: 73 resp = channel.recv(9999) 74 buff += resp 75 except Exception as e: 76 print("error info:" + str(e)) 77 78 print(buff) 79 channel.close() 80 ssh.close()
实现堡垒机模式下的远程文件上传
实现堡垒机模式下文件上传,原理是通过paramiko的SFTPClient将文件从办公设备上传至堡垒机制定临时目录,如/tmp,在通过SSHClient的invoke_shell方法开启ssh会话,执行scp命令,将/tmp下指定文件复制到目录业务主机上
本示例具体使用sftp.put()方法上传文件至堡垒机临时目录,在通过send()方法执行scp命令,将堡垒机临时目录下文件复制到目标主机,具体代码如下:
1 #!/usr/bin/evn python 2 # --*-- coding: utf-8 --*-- 3 # Auther : Liu WeiDong 4 5 import paramiko 6 import os,sys,time 7 8 #定义堡垒机信息 9 blip = "192.168.1.23" 10 bluser = "root" 11 blpasswd = "Kd03dfd" 12 13 #定义通过堡垒机连接的业务主机信息 14 hostname = "192.168.1.22" 15 username = "root" 16 password = "OD908k" 17 port = 22 18 19 tmpdir = "/tmp" 20 remotedir = "data" 21 #本地源文件路径 22 localpath = "/home/nginx_access.tar.gz" 23 #堡垒机临时路径 24 tmppath = tmpdir + "/nginx_access.tar.gz" 25 #业务主机目标路径 26 remotepath = remotedir + "/nginx_access_hd.tar.gz" 27 #输入服务器登录前输入密码提示 28 passinfo = "\'s password: " 29 paramiko.util.log_to_file("syslogin.log") 30 31 t = paramiko.Transport((blip,port)) 32 t.connect(username=bluser,password=blpasswd) 33 sftp = paramiko.SFTPClient.from_transport(t) 34 #上传本地源文件到堡垒机临时目录 35 sftp.put(localpath,tmppath) 36 sftp.close() 37 38 ssh = paramiko.SSHClient() 39 ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) 40 ssh.connect(hostname=blip,username=bluser,password=blpasswd) 41 42 channel = ssh.invoke_shell() 43 channel.settimeout(10) 44 45 buff = "" 46 resp = "" 47 #scp中传目录文件到目标主机 48 channel.send("scp " + tmppath + " " + username + "@" + hostname + ":" +remotepath + "\n" ) 49 50 #ssh登录的提示信息判断,输出字符串尾含有's password: 若是以password结尾,则进入循环,否则不进入循环 51 while not buff.endswith(passinfo): 52 try: 53 resp = channel.recv(9999) 54 except Exception as e: 55 print("Error info:%S connection time." % (str(e))) 56 channel.close() 57 ssh.close() 58 sys.exit() 59 buff += resp 60 # 判断输出字符串结尾如果含有“yes/no”时发送“yes”并回车 61 if not buff.find("yes/no") == -1: #find方法,发现子字符串包含在字符串内,则返回字符串索引值,否则返回-1 62 channel.send("yes\n") 63 buff = "" 64 #发送业务主机密码并回车 65 channel.send(password+"\n") 66 67 buff = "" 68 #输出的字符串为“#”号,说明校验通过不进入循环 69 while not buff.endswith("# "): 70 resp = channel.recv(9999) 71 # 输出字符串含有“password”,说明密码不正确,要求重新输入 72 if not resp.find(passinfo) == -1: 73 print("Error info: Authentication failed.") 74 channel.close() 75 ssh.close() 76 sys.exit() 77 buff += resp 78 79 print(buff) 80 channel.close() 81 ssh.close()