Skip to content

Latest commit

 

History

History
429 lines (318 loc) · 16.3 KB

12.md

File metadata and controls

429 lines (318 loc) · 16.3 KB

十二章 使用Telnet和SSH远程监控主机

本章中我们将学习如何在服务器通过配置Telnet和SSH来实现基本配置。我们从Telnet模块开始,然后使用推荐的方法来实现相同的配置:使用Python的不同模块来完成SSH连接。我们还将学习telnetlib, subprocess, fabric, Netmiko和paramiko的运行方式。学习本章,读取必须要有网络的基础知识。

本章将涉及如下课题:

  • telnetlib()模块
  • subprocess.Popen()模块
  • SSH之使用fabric模块
  • SSH之使用Paramiko库
  • SSH之使用Netmiko库

telnetlib()模块

这部分中,我们将学习Telnet协议,然后使用telnetlib模块对远程服务器进行Telnet的操作。

Telnet是一个允许用户与远程服务器通讯的网络协议。它多由网络管理员用来远程访问和管理设备。要访问设备,在终端中运行Telnet命令并加上远程服务器的IP地址或主机名。

Telnet使用TCP,默认端口为23。要使用,确保在系统中进行了安装,如未安装,运行如下命令来完成安装:

sudo apt-get install telnetd

通过终端快速运行Telnet命令,可输入如下命令:

$ telnet ip_address_of_your_remote_server

Python带有一个telnetlib模块,可通过Python脚本执行Telnet函数。在对远程设备或交换机进行Telnet之前,确保进行了适当的配置,如果未配置,我们可以在交换机终端中使用如下命令:

configure terminal
enable password 'set_Your_password_to_access_router'
username '设置用户名' password '设置远程访问密码'
line vty 0 4
login local
transport input all
interface f0/0
ip add 'set_ip_address_to_the_router' 'put_subnet_mask'
no shut
end
show ip interface brief

译者注: 以上代码与交换机相关,Ubuntu 开启 Telnet 命令如下:

# 安装openbsd-inetd
sudo apt-get -y install openbsd-inetd

# 安装telnetd
sudo apt-get -y install telnetd

# 重启openbsd-inetd
sudo /etc/init.d/openbsd-inetd restart

下面我们来看一个Telnet远程设备的示例。创建脚本telnet_example.py 并在其中编写如下内容:

import telnetlib, getpass, sys

HOST_IP = "你的主机 IP 地址"
host_user = input("Enter your telnet username: ")
password = getpass.getpass()

t = telnetlib.Telnet(HOST_IP)
# t.read_until(b"Username:")
# t.read_until(b"login:")
t.write(host_user.encode("ascii") + b"\n")
if password:
        t.read_until(b"Password:")
        t.write(password.encode("ascii") + b"\n")

t.write(b"enable\n")
t.write(b"enter_remote_device_password\n") # 远程设备密码
t.write(b"conf t\n")
t.write(b"int loop 1\n")
t.write(b"ip add 10.1.1.1 255.255.255.255\n")
t.write(b"int loop 2\n")
t.write(b"ip add 20.2.2.2 255.255.255.255\n")
t.write(b"end\n")
t.write(b"exit\n")
print(t.read_all().decode("ascii"))

运行脚本,我们将得到如下输出:

$ python3 telnet_example2.py
Enter your telnet username: student
Password:

...

译者注: 原文测试对象为交换机,上例中 Alan 使用了 Ubuntu 进行测试,因此需将第一处的读取改为 login,命令执行部分省略,读者可替换为其它 Linux 命令来进行测试。

上例中,我们使用telnetlib模块访问并配置了Cisco交换机。该脚本中,首先我们接收用户输入用户名和密码来初始化远程设备的Telnet连接。一旦建立了连接,我们对远程设备进行了更进一步的配置。Telnet连接后,我们就能够访问远程服务器或设备。但Telnet协议有一个很重要的缺点,那就是所有的数据,包括用户和密码,都以文本的形式在网络中传输,这就可能会带来安全风险。因此,现在很少会使用Telnet,转而由名为Secure Shell的安全协议在替代,常称为SSH。

SSH

SSH是一个通过远程访问来管理设备或服务器的网络协议。SSH使用公钥加密来保证安全。Telnet和SSH之间的重要区别是SSH使用了加密,也就是说数据在网络上的传输都受保护不被未授权实时拦截。

用户访问远程服务器或设备时应使用SSH客户端。在终端中通过如下命令可安装SSH:

sudo apt install ssh

同时在用户要远程连接的远程服务器上需要安装和运行SSH服务端。SSH使用TCP协议并且默认运行在22端口上。

我们可以通过终端运行ssh命令如下:

ssh host_name@host_ip_address

下面我们来学习使用Python中的不同模块来进行SSH连接,如subprocess, fabric, Netmiko和Paramiko。接下来我们就逐一学习这些模块。

subprocess.Popen()模块

Popen类处理进程的创建和管理。通过使用这一模块,开发人员可处理不太常见的用例。子程序将在新的进程中进行。要在Unix/Linux上执行子程序,该类会使用os.execvp()函数。要在Windows上执行子程序,该类会使用CreateProcess()函数。

下面我们来看subprocess.Popen()的一些有用的参数:

class subprocess.Popen(args, bufsize=-1, executable=None,
    stdin=None, stdout=None, stderr=None,
    preexec_fn=None, close_fds=_PLATFORM_DEFAULT_CLOSE_FDS,
    shell=False, cwd=None, env=None, universal_newlines=False,
    startupinfo=None, creationflags=0,
    restore_signals=True, start_new_session=False,
    pass_fds=(), *, encoding=None, errors=None):

我们来看下各个参数:

  • args:可以是一个程序参数序列或单个字符串。如果args是一个序列,args中的第一项会被执行。如果args是一个字符,它推荐以序列传入args。
  • shell:shell参数默认设为False,它指定是否使用shell来执行程序。如果shell的值为True,它推荐以字符串传入args。在Linux中,如果shell=True,shell默认为/bin/sh。如果args是一个字符串,字符串指定通过shell执行的命令。
  • bufsize:如果bufsize为0(默认值为0,译者注:Alan 本地3.6和3.7的默认值均为-1),这表示未缓冲,如果bufsize值为1,则表示行缓冲。若bufsize为任意其它正值,使用指定大小的缓冲。如果bufsize为任意其它负值,表示全量缓冲。
  • executable:它指定要执行的替代程序。
  • stdin, stdout, and stderr:这些参数分别定义标准输入、标准输出和标准错误。
  • preexec_fn:设置为可调用对象,会在执行子进程之前调用。
  • close_fds:在Linux中,如果close_fds为真,所以除0, 1和2以外的文件描述符都会在子进程执行之前关闭。在Windows中,如果close_fds为真那么子进程将不继承指针。
  • env:如果其值不为None,那么映射会定义新进程的环境变量。
  • universal_newlines:若值为True,那么stdout和stderr会以新行模式的文本文件打开。

下面我们来看一个subprocess.Popen()的示例。为此创建一个脚本ssh_using_sub.py并在其中编写如下内容:

import subprocess, sys

HOST="主机用户名@主机 IP"
COMMAND = "ls"

ssh_obj = subprocess.Popen(["ssh", "%s" % HOST, COMMAND],
        shell=False,
        stdout=subprocess.PIPE,
        stderr=subprocess.PIPE)

result = ssh_obj.stdout.readlines()
if result == []:
        err = ssh_obj.stderr.readlines()
        print(sys.stderr, "ERROR: %s" % err)
else:
        print(result)

运行脚本,我们将得到如下输出:

$ python3 ssh_using_sub2.py

# 输出结果(第一次需确认连接):
The authenticity of host '192.168.0.14 (192.168.0.14)' can't be established.
ECDSA key fingerprint is SHA256:0Yzjg/Ipsb3ilbpABfih6b55ET6ub0c2MkgtbWqwm/8.
Are you sure you want to continue connecting (yes/no)? yes
student@192.168.0.14's password:
[b'testing\n', b'work\n']

上例中,首先我们导入了subprocess模块,然后定义了想要建立SSH连接的主机地址。接着我们添加了一条在远程设备上执行的简单命令。一切设置完成后,我们将这一信息传入到subprocess.Popen()函数中。该函数执行定义在函数内的参数来创建与远程设备的连接。在建立了SSH连接之后,我们所定义的命令被执行并返回结果。然后我们在终端中打印出了SSH的返回结果,参见输出。

SSH之使用fabric模块

Fabric是一个Python库,并且是一个可用于SSH的命令行工具。它用于系统运维及在网上部署应用。我们也可以在SSH之上执行shell命令。

要使用fabric模块,首先我们要使用如下命令来进行安装:

 pip3 install fabric3

下面我们来看一个示例。创建脚本fabfile.py并在其中编写如下内容:

from fabric.api import *

env.hosts = ["用户名@主机ip"]
env.password = '你的密码'

def dir():
        run('mkdir fabric')
        print('Directory named fabric has been created on your host network')

def diskspace():
        run('df')

运行脚本,我们将得到如下输出:

$ fab -f fabfile.py dir

# 输出结果:
[student@192.168.0.14] Executing task 'dir'
[student@192.168.0.14] run: mkdir fabric
...
Directory named fabric has been created on your host network

Done.
Disconnecting from student@192.168.0.14... done.

上例中,首先我们导入了fabric.api模块,然后我们设置了主机名和密码来与网络上主机进行连接。接着,我们设置了通过SSH执行的任务。因此,执行我们的程序不是使用Python3 fabfile.py,而是要使用fab工具,然后上述的任务就会从fabfile.py文件中进行执行。本例中,我们执行了dir,在远程主机上创建了一个名为fabric目录。你也可以在Python文件中添加你自己的任务。它可通过fabric模块中的fab工具来进行执行。

SSH之使用Paramiko库

Paramiko是一个实现了安全连接远程设备SSHv2协议的库。Paramiko是针对SSH的纯Python接口。

在使用Paramiko之前,确保正确地在系统中进行了安装。如未安装,我们可以通过在终端中运行如下命令来进行安装:

sudo pip3 install paramiko

下面我们来看一个使用paramiko的示例。对于paramiko连接,我们使用Cisco设备(译者注:Alan 将继续使用本地虚拟机)。Paramiko既支持密码验证也支持密钥对验来对主机进行安全连接。我们的脚本中,会使用密码验证,也即对密码进行检测,如存在则尝试使用用户名/密码来进行验证。在使用SSH连接远程设备或多层交换机时,首先确保对它们进行了适当的配置,若未配置,可使用如下命令在多层交换机中进行基本配置:

configure t
ip domain-name cciepython.com
crypto key generate rsa
How many bits in the modulus [512]: 1024
interface range f0/0 - 1
switchport mode access
switchport access vlan 1
no shut
int vlan 1
ip add 'set_ip_address_to_the_router' 'put_subnet_mask'
no shut
exit
enable password 'set_Your_password_to_access_router'
username 'set_username' password 'set_password_for_remote_access'
username 'username' privilege 15
line vty 0 4
login local
transport input all
end

译者注: 有相应设备的朋友请自行验证配置命令

下面,创建一个脚本pmiko.py并在其中编写如下内容:

import paramiko, time

ip_address = '主机 IP'
usr = '主机用户名'
pwd = '主机密码'

c = paramiko.SSHClient()
c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
c.connect(hostname=ip_address, username=usr, password=pwd)

print("SSH connection is successfully established with ", ip_address)

rc = c.invoke_shell()
for n in range(2, 6):
        print("Creating VLAN " + str(n))
        rc.send("vlan database\n")
        rc.send("vlan " + str(n) + "\n")
        rc.send("exit\n")
        time.sleep(0.5)

time.sleep(1)
output = rc.recv(65535)
print(output)
c.close()

运行脚本,我们将得到如下输出:

$ python3 pmiko.py
SSH connection is successfully established with  192.168.0.14
Creating VLAN 2
Creating VLAN 3
Creating VLAN 4

# 测试连接正常,但会报错OSError: Socket is closed,可考虑修改为其它命令

上例中,首先我们导入了paramiko模块,然后定义了需要远程连接设备的SSH认证信息。在传入身份信息后,我们创建了一个paramiko.SSHclient()的实例c,它是用于连接远程设备和执行命令或运算的主客户端。SSHClient对象的创建允许我们使用.connect()函数来建立远程连接。然后,我们设置了 paramiko连接的策略,因为默认paramiko.SSHclientu将SSH策略设置为拒绝状态。这会让策略拒绝任何未验证的SSH连接。我们的脚本中通过使用AutoAddPolicy()函数来自动添加主机的密钥而不进行弹出,忽略了这种SSH连接断掉的可能性。我们使用策略来进行测试,但出于安全目的不推荐在生产环境使用。

SSH连接建立之后,我们可以进行想在设备上进行的配置或运算。这里,我们在远程设备上创建了几个虚拟LAN。在创VLAN之后,我们关闭了连接。

SSH之使用Netmiko库

这部分中我们将学习Netmiko。Netmiko库是Paramiko的一个高级版本。这是一个基于Paramiko的多供应商(multi_vendor)库。Netmiko简化了网络设备的SSH连接及对设备的具体操作。在通过SSH连接远程设备或多层交换机之前,确保对它们进行了相应的配置,若未配置,我们可以使用在Paramiko一节中使用的命令来进行基本配置。

下面我们来看一个示例。创建一个脚本nmiko.py并在其中编写如下代码:

from netmiko import ConnectHandler

remote_device = {
        'device_type': 'cisco_ios',
        # 'device_type': 'linux',
        'ip': '远程设备 IP地址',
        'username': '用户名',
        'password': '密码'
}

remote_connection = ConnectHandler(**remote_device)
# net_connect.find_prompt()

for n in range(2, 6):
        print("Creating VLAN " + str(n))
        commands = ['exit', 'vlan database', 'vlan ' + str(n), 'exit']
        output = remote_connection.send_config_set(commands)
        print(output)

command = remote_connection.send_command('show vlan-switch brief')
# command = remote_connection.send_command('ls')
print(command)

运行脚本,将得到如下输出:

~$ python3 nmiko.py
Output:
Creating VLAN 2
config term
Enter configuration commands, one per line. End with CNTL/Z.
server(config)#exit
server #vlan database
server (vlan)#vlan 2
VLAN 2 modified:
server (vlan)#exit
APPLY completed.
Exiting....
server #
..
..
..
..
switch#
Creating VLAN 5
config term
Enter configuration commands, one per line. End with CNTL/Z.
server (config)#exit
server #vlan database
server (vlan)#vlan 5
VLAN 5 modified:
server (vlan)#exit
APPLY completed.
Exiting....
VLAN Name 

----
1 default active Fa0/0, Fa0/1, Fa0/2, Fa0/3,
Fa0/4, Fa0/5, Fa0/6, Fa0/7, Fa0/8, Fa0/9, Fa0/10, Fa0/11, Fa0/12, Fa0/13,
Fa0/14, Fa0/15
2 VLAN0002 active
3 VLAN0003 active
4 VLAN0004 active
5 VLAN0005 active
1002 fddi-default active
1003 token-ring-default active
1004 fddinet-default active
1005 trnet-default active

译者注: 以上代码未验证,仅使用ls 代码进行了简单验证

$ python3 nmiko.py
fabric	testing  work

上例中,我们使用了Netmiko库来代替Paramiko进行SSH连接。该脚本中首先我们从Netmiko库中导入了ConnectHandler,用于以SSH连接远程网络设备,在设备字典中进行传入。本例中的字典为remote_device。在建立了连接之后,我们执行了配置命令来使用send_config_set()函数创建一些虚拟LAN。

在使用这类.send_config_set()函数向远程设备传递命令,它自动设置我们的设备为配置模式。在发送配置命令之后,我们还传递了一些简单命令来获取所配置设备的相关信息。

总结

本章中我们学习Telnet和SSH。我们还学习不同的Python模块,如 telnetlib, subprocess, fabric, Netmiko和Paramiko,使用它们我们执行了Telnet和SSH连接。SSH使用公钥加密来保持安全,要比Telnet安全的多。

下一章中我们会使用不同的Python库,通过它们来生成图形化用户界面。

课后问题

  1. 使用是客户端-服务端架构?

  2. 如何在Python代码中运行指定操作系统命令?

  3. LAN和VLAN之间的区别是什么?

  4. 如下代码的输出是什么?

    list = ['a', 'b', 'c', 'd', 'e']
    print(list[10:])
    
  5. 编写一个Python程序来显示日历(提示:使用calendar模块)

  6. 编写一个Python程序来对文本文件的行进行计数

扩展阅读