Python paramiko模块使用解析实现ssh

时间:2023-02-17 11:07:30

一、安装paramiko

pip install paramiko

二、核心组件SSHClient类

SSHClient类是一个SSH服务会话的高级表示,该类封装了传输(transport),通道(channel)及SFTPClient的校验,建立的方法,通常用于执行远程命令。有如下方法:
(1)ssh远程连接服务器
connect(self, hostkey=None, username='', password=None, pkey=None,gss_host=None, gss_auth=False, gss_kex=False, gss_deleg_creds=True)
参数说明:
hostname(str类型):连接的目标主机地址
port(int类型):连接目标主机的端口,默认为22
username(str类型):用户名
password(str类型):密码
pkey(PKey类型):私钥方式,用于身份验证
key_filename(str o list(str)类型):一个文件名或文件名的列表,用于私钥的身份验证
timeout(float类型):一个可选的超时时间(已秒为单位)的TCP连接
allow_agent(bool类型):设置为False时用于禁用连接到SSH代理
look_for_keys(bool类型):设置为False时用来禁用在~/.ssh中搜索秘钥文件
compress(bool类型):设置为True时打开压缩

(2)远程主机没有本地主机密钥或HostKeys对象时的连接方法。
格式为:set_missing_host_key_policy(policy)。
其中policy参数常见取值有3种,分别如下:
AutoAddPolicy(最常用):
自动添加主机名及主机密钥到本地的known_hosts,不依赖load_system_host_key的配置。即新建立ssh连接时不需要再输入yes或no进行确认。
RejectPolicy(默认):
自动拒绝未知的主机名和密钥,依赖load_system_host_key的配置。此为默认选项
WarningPolicy:
用于记录一个未知的主机密钥的python警告。并接受,功能上和AutoAddPolicy类似,但是会提示是新连接。

(3)远程执行命令,
函数格式:exec_command(command, bufsize=-1, timeout=None, get_pty=False, environment=None)
该命令的输入与输入流为标准输入(stdin)、输出(stdout)、错误(stderr)

(4)在远程服务器上生成新的交互式shell。
函数格式为:invoke_shell(term='vt100', width=80, height=24, width_pixels=0, height_pixels=0, environment=None)。

exec_command和invoke_shell区别
exec_command使用的是SSH exec channel的方式执行,不具备持久化的能力,也就是每次运行都是一次全新的环境,不是说你先切换到root,下一
条命令运行就是以root执行了,说简单点就是把命令当作参数发送出去
适合场景:不想使用终端仿真;不执行诸如分页,着色和主要是交互式确认之类的操作,有一个典型的坑,不能使用nohup
invoke_shell使用的是SSH shell channel的方式执行,具备持久化能力,就类似和我们平时用MobaXterm,xshell等这些终端软件连接上去一样
适合场景:需要一些持久化的操作;需要使用一些交互式命令

(5)load_system_host_keys方法
load_system_host_keys(self, filename)
加载本地总要校验文件,默认为~/.ssh/known_hosts,非默认另需要手工指定。
参数说明:
filename(str类型)指定远程主机公钥记录文件

三、基于SSHClient连接

1、基于用户名和密码的sshclient

import paramiko
# 创建SSH对象
ssh=paramiko.SSHClient()
# 允许连接不在know_hosts文件中的主机
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
# 连接服务器
ssh.connect(hostname="192.168.10.131",port=22,username='root',password='123456')
# 执行命令
# stdin:标准输入(就是你输入的命令);
# stdout:标准输出(就是命令执行结果);
# stderr:标准错误(命令执行过程中如果出错了就把错误打到这里)
# stdout和stderr仅会输出一个
stdin,stdout,stderr=ssh.exec_command("df -h")
# 获取命令结果
res,err=stdout.read(),stderr.read()
result=res if res else err
print(result.decode())
# 关闭连接
ssh.close()

2、执行多个命令

import paramiko
def execMultiCmd(host,port,user,pswd,list):
with paramiko.SSHClient() as conn:
conn.set_missing_host_key_policy(paramiko.AutoAddPolicy())
try:
conn.connect(host, port, user, pswd)
except Exception as e:
print("服务器连接失败")
print(e)
else:
cmd_list = ';'.join(cmdlist)
_, stdout, stderr = conn.exec_command(cmd_list, get_pty=True)
result = stdout if stdout else stderr
print(result.read().decode("utf-8"))
finally:
conn.close()
if __name__ == '__main__':
cmd_list=["pwd","free -h"]
host="192.168.10.131"
port=22
user="root"
pswd="123456"
execMultiCmd(host,port,user,pswd,cmd_list)
get_pty(bool类型):实际在远程执行sudo命令时,一般主机都会需要通过tty才能执行,通过把get_pty值设置为True,可以模拟tty;
在远程执行某些命令时,可能需要管理员权限,这种时候需要做一些判断,首先判断登录提供的用户名如果不是root,则需要对命令做一些修改。这里
的修改有两种情况,一是,该普通用户本身就有sudo权限,只需要把执行的命令加到sudo之后执行就可以,还有一种是普通用户没有sudo权限,需要
通过su先切换到root身份之后再执行,这种情况下需要提供root密码。

还有一点要注意的是get_pty这个参数,实际在远程执行sudo命令时,一般主机都会需要通过tty才能执行,通过把get_pty值设置为True,可以模拟
tty,但是随之而来也会有一个问题,如果是远程执行一个需要长期运行的进程,例如启动nginx服务,当远程命令执行后SSH退出之后,此次运行的所
有程序也会随之结束,所以在需要通过远程命令运行某些服务或程序时,是不能指定get_pty参数的;但同时,如果是普通用户远程登录,是没有权限
执行service命令的。建议的一种方式是修改/etc/sudoers配置文件,注释掉Defaults requiretty这行。

3、获取执行命令的结果码

import paramiko
ssh=paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
try:
ssh.connect("192.168.10.131",22,'root','123456')
except Exception as e:
print("服务器连接失败")
print(e)
else:
stdin,stdout,stderr=ssh.exec_command('df -k')
result=stdout if stdout else stderr
result_code=result.channel.recv_exit_status()
print(result_code)
finally:
ssh.close()

4、在远程服务器生成子进程并交互执行命令

import paramiko
def execMultiCmd(host,port,user,pswd,list):
with paramiko.SSHClient() as conn:
conn.set_missing_host_key_policy(paramiko.AutoAddPolicy())
try:
conn.connect(host, port, user, pswd)
except Exception as e:
print("服务器连接失败")
print(e)
else:
cmd_list = ';'.join(cmdlist)
_, stdout, stderr = conn.exec_command(cmd_list, get_pty=True)
#result = stdout if stdout else stderr
#print(result.read().decode("utf-8"))
interact=conn.invoke_shell()
time.sleep(1)
interact.send('echo hello world' + '\n')
time.sleep(1)
interact.send('free -h' + '\n')
time.sleep(1)
interact.send('echo hello world' + '\n')
time.sleep(2)
interact.send('df -k!' + '\n')
result=interact.recv(65535)
print(result.decode("utf-8"))
finally:
conn.close()
if __name__ == '__main__':
cmd_list=["pwd","free -h"]
host="192.168.10.131"
port=22
user="root"
pswd="123456"
execMultiCmd(host,port,user,pswd,cmd_list)

5、基于公钥密钥连接

import paramiko

# 指定私钥路径
private_key = paramiko.RSAKey.from_private_key_file('/root/.ssh/id_rsa')
# 创建SSH对象
ssh = paramiko.SSHClient()
# 允许连接不在know_hosts文件中的主机
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
# 连接服务器
ssh.connect(hostname='10.0.0.171', port=22, username='root', pkey=private_key)
# 执行命令
stdin, stdout, stderr = ssh.exec_command('df')
# 获取命令结果
result = stdout.read()
print(result.decode())
# 关闭连接
ssh.close()