paramiko 应用示例

实现密钥方式登录远程主机

实现自动密钥登录方式,第一步需要配置与目标设备的密钥认证支持,具体见 9.2.5 节,私钥文件可以存放在默认路径 "~/.ssh/id_rsa",当然也可以自定义,如本例的 "/home/key/id_rsa",通过 paramiko.RSAKey.from_private_key_file() 方法引用,详细代码如下:

paramiko/simple2.py
#!/usr/bin/env python
import paramiko
import os

hostname = '192.168.1.21'
username = 'root'
paramiko.util.log_to_file('syslogin.log')
ssh = paramiko.SSHClient()
ssh.load_system_host_keys()
privatekey = os.path.expanduser('/home/key/id_rsa')  # 定义私钥存放路径
key = paramiko.RSAKey.from_private_key_file(privatekey)  # 创建私钥对象key
ssh.connect(hostname=hostname, username=username, pkey=key)
stdin, stdout, stderr = ssh.exec_command('free -m')
print(stdout.read())
ssh.close()

程序执行结果见图 6-1。

实现堡垒机模式下的远程命令执行

堡垒机环境在一定程度上提升了运营安全级别,但同时也提高了日常运营成本,作为管理的中转设备,任何针对业务服务器的管理请求都会经过此节点,比如 SSH 协议,首先运维人员在办公电脑通过 SSH 协议登录堡垒机,再通过堡垒机 SSH 跳转到所有的业务服务器进行维护操作,如图6-2所示。

image 2023 12 08 15 48 02 559
Figure 1. 图6-2 堡垒机模式下的远程命令执行

我们可以利用 paramiko 的 invoke_shell 机制来实现通过堡垒机实现服务器操作,原理是 SSHClient.connect 到堡垒机后开启一个新的 SSH 会话(session),通过新的会话运行 "ssh user@IP" 去实现远程执行命令的操作。实现代码如下:

#!/usr/bin/env python
import paramiko
import os, sys, time

blip = "192.168.1.23"  # 定义堡垒机信息
bluser = "root"
blpasswd = "KJsdiug45"

hostname = "192.168.1.21"  # 定义业务服务器信息
username = "root"
password = "IS8t5jgrie"

port = 22
passinfo = '\'s password: '  # 输入服务器密码的前标志串
paramiko.util.log_to_file('syslogin.log')

ssh = paramiko.SSHClient()  # ssh登录堡垒机
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect(hostname=blip, username=bluser, password=blpasswd)

channel = ssh.invoke_shell()  # 创建会话,开启命令调用
channel.settimeout(10)  # 会话命令执行超时时间,单位为秒

buff = ''
resp = ''
channel.send('ssh ' + username + '@' + hostname + '\n')  # 执行ssh登录业务主机

while not buff.endswith(passinfo):  # ssh登录的提示信息判断,输出串尾含有"\'s password:"时
    try:  # 退出while循环
        resp = channel.recv(9999)
    except Exception as e:
        print('Error info:%s connection time.' % (str(e)))
        channel.close()
        ssh.close()
        sys.exit()
    buff += resp
    if not buff.find('yes/no')==-1:    #输出串尾含有"yes/no"时发送"yes"并回车
        channel.send('yes\n')
    buff=''

channel.send(password+'\n')    #发送业务主机密码

buff=''
while not buff.endswith('# '):    #输出串尾为"# "时说明校验通过并退出while循环
    resp = channel.recv(9999)
    if not resp.find(passinfo)==-1:    #输出串尾含有"\'s password: "时说明密码不正确,
        #要求重新输入
        print('Error info: Authentication failed.')
        channel.close()         #关闭连接对象后退出
        ssh.close()
        sys.exit()
    buff += resp

channel.send('ifconfig\n')         #认证通过后发送ifconfig命令来查看结果
buff=''
try:
    while buff.find('# ')==-1:
        resp = channel.recv(9999)
        buff += resp
except Exception as e:
    print("error info:" + str(e))

print(buff)  # 打印输出串
channel.close()
ssh.close()

运行结果如下:

# python /home/test/paramiko/simple3.py
ifconfig
eth0      Link encap:Ethernet  HWaddr 00:50:56:28:63:2D
          inet addr:192.168.1.21  Bcast:192.168.1.255  Mask:255.255.255.0
          inet6 addr: fe80::250:56ff:fe28:632d/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:3523007 errors:0 dropped:0 overruns:0 frame:0
          TX packets:6777657 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:606078157 (578.0 MiB)  TX bytes:1428493484 (1.3 GiB)
lo         Link encap:Local Loopback
          inet addr:127.0.0.1  Mask:255.0.0.0

显示 "inet addr:192.168.1.21" 说明命令已经成功执行。

实现堡垒机模式下的远程文件上传

实现堡垒机模式下的文件上传,原理是通过 paramiko 的 SFTPClient 将文件从办公设备上传至堡垒机指定的临时目录,如 /tmp,再通过 SSHClient 的 invoke_shell 方法开启 ssh 会话,执行 scp 命令,将 /tmp 下的指定文件复制到目标业务服务器上,如图6-3所示。

image 2023 12 08 15 56 45 089
Figure 2. 图6-3 堡垒机模式下的文件上传

本示例具体使用 sftp.put() 方法上传文件至堡垒机临时目录,再通过 send() 方法执行 scp 命令,将堡垒机临时目录下的文件复制到目标主机,详细的实现源码如下:

#!/usr/bin/env python
import paramiko
import os, sys, time

blip = "192.168.1.23"  # 定义堡垒机信息
bluser = "root"
blpasswd = " IS8t5jgrie"

hostname = "192.168.1.21"  # 定义业务服务器信息
username = "root"
password = " KJsdiug45"

tmpdir = "/tmp"
remotedir = "/data"
localpath = "/home/nginx_access.tar.gz"  # 本地源文件路径
tmppath = tmpdir + "/nginx_access.tar.gz"  # 堡垒机临时路径
remotepath = remotedir + "/nginx_access_hd.tar.gz"  # 业务主机目标路径

port = 22
passinfo = '\'s password: '
paramiko.util.log_to_file('syslogin.log')

t = paramiko.Transport((blip, port))
t.connect(username=bluser, password=blpasswd)
sftp = paramiko.SFTPClient.from_transport(t)
sftp.put(localpath, tmppath)  # 上传本地源文件到堡垒机临时路径
sftp.close()

ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect(hostname=blip, username=bluser, password=blpasswd)

channel = ssh.invoke_shell()
channel.settimeout(10)

buff = ''
resp = ''
# scp中转目录文件到目标主机
channel.send('scp ' + tmppath + ' ' + username + '@' + hostname + ':' + remotepath + '\n')

while not buff.endswith(passinfo):
    try:
        resp = channel.recv(9999)
    except Exception as e:
        print('Error info:%s connection time.' % (str(e)))
        channel.close()
        ssh.close()
        sys.exit()
    buff += resp
    if not buff.find('yes/no') == -1:
        channel.send('yes\n')
    buff = ''

channel.send(password + '\n')

buff = ''
while not buff.endswith('# '):
    resp = channel.recv(9999)
    if not resp.find(passinfo) == -1:
        print('Error info: Authentication failed.')
        channel.close()
        ssh.close()
        sys.exit()
    buff += resp

print(buff)
channel.close()
ssh.close()

运行结果如下,如目标主机 /data/nginx_access_hd.tar.gz 存在,则说明文件已成功上传。

# python /home/test/paramiko/simple4.py
nginx_access.tar.gz           100% 1590KB   1.6MB/s   00:00

当然,整合以上两个示例,再引入主机清单及功能配置文件,可以实现更加灵活、强大的功能,大家可以自己动手,在实践中学习,打造适合自身业务环境的自动化运营平台。

参考提示