自动化运维与python项目实战: CMDB自动化资产扫描(2)路由配置、视图函数

资产管理探测流程

  • 存活探测: 获取局域网内存活的IP列表
  • 主机探测: 获取系统版本(SN、版本、MAC地址)
  • 主机关系探测: 识别宿主主机和虚拟机的关系
  • 网络设备的探测: 探测网络设备信息(SN、设备名等),使用snmp协议
  • 其他设备的探测:

 

主机存活探测模块和工具

Nmap探测工具

Nmap,也就是Network Mapper,最早是Linux下的网络扫描和嗅探工具包。是一款用于网络发现和安全审计的网络安全工具。

 

  • 主机发现 - 识别网络上的主机。例如,列出响应TCP和/或ICMP请求或打开特定端口的主机。
nmap -n -sP 172.25.254.10
nmap -n -sP 172.25.254.0/24
  • 端口扫描 - 枚举目标主机上的开放端口
# Nmap默认端口的扫描范围1-10000
nmap -n -p 172.25.254.10
# 具体指定要扫描的端口为50-80
nmap -n -p50-80 172.25.254.10
# 具体指定要扫描的端口为22和80
nmap -n -p22,80 172.25.254.10
  • OS检测 - 确定网络设备的操作系统和硬件特性。
nmap -O 172.25.254.10

 

查看172.25.254.10这台主机是否开启?

nmap -sP -PE 172.25.254.10

 

查看172.25.254.0/24局域网内存活的主机信息及存活主机个数

nmap -sP -PE 172.25.254.0/24

 

Nmap的Python操作接口: python-nmap

首先需要安装python端的nmap模块

pip3 install python-nmap

视图层的实现
代码全部卸载views.py过于冗余,这里新建utils.py代码用于存放调用

#utils.py
import nmap

def get_active_hosts(host='172.25.254.10'):
    # 实例化对象, portScanner()类用于实现对指定主机进行端口扫描
    nm = nmap.PortScanner()
    # 以指定方式扫描指定主机或网段的指定端口
    result = nm.scan(hosts=host, arguments='-n -sP')
    # print("扫描结果: ", result)
    # 返回的扫描具体的nmap命令行
	# print("nmap命令行: ", nm.command_line())
	# 返回nmap扫描的主机清单,格式为列表类型
    # print("主机清单: ", nm.all_hosts())
    return nm.all_hosts()
##测试:
if __name__ == '__main__':
    get_active_hosts()

执行结果:

/usr/bin/python3.6 /root/PycharmProjects/CMDB/hostinfo/utils.py
扫描结果:  {'nmap': {'command_line': 'nmap -oX - -n -sP 172.25.254.10', 'scaninfo': {}, 'scanstats': {'timestr': 'Sat Aug  1 06:57:28 2020', 'elapsed': '0.00', 'uphosts': '1', 'downhosts': '0', 'totalhosts': '1'}}, 'scan': {'172.25.254.10': {'hostnames': [{'name': '', 'type': ''}], 'addresses': {'ipv4': '172.25.254.10'}, 'vendor': {}, 'status': {'state': 'up', 'reason': 'localhost-response'}}}}
主机清单:  ['172.25.254.10']

 

SSH端口存活扫描基础和实现

使用telnet命令探测主机列表是否属于Linux服务器

telnet命令探测

telnet 172.25.254.10 22

 

SSH端口存活扫描python实现:telnetlib模块

telnetlib模块提供的Telnet类实现了Telnet协议。

补充utils.py

import telnetlib
import re
def get_active_hosts(host='172.25.254.10'):
	#省略代码
	
def is_ssh_up(host='172.25.254.10', port=22, timeout=5):
    try:
        # 实例化对象
        tn = telnetlib.Telnet(host=host, port=port, timeout=timeout)
        # read_until读取直到遇到了换行符或超时秒数。默认返回bytes类型,通过decode方法解码为字符串。
        tn_result = tn.read_until(b"\n", timeout=timeout).decode('utf-8')
        # 通过正则匹配且忽略大小写, 寻找是否ssh服务开启。
        ssh_result = re.search(pattern=r'ssh', string=tn_result, flags=re.I)
    except Exception as e:
        print("%s ssh is down, Beacuse %s" % (host, str(e)))
        return False
    else:
        # 如果能匹配到内容, 说明ssh服务开启, 是Linux服务器.
        return True if ssh_result else False
#测试
if __name__ == '__main__':
    # get_active_hosts()
     print(is_ssh_up(host='172.25.254.10'))

 

主机登陆探测

  • 用一系列的验证方式循环进行SSH登录, 得到争取的登录方式。
  • 主机SSH登录验证方式
    SSH常用来远程登录到远程机器,有两种常用的方法:
    第一种便是账号密码登录。
    第二种就是公钥私钥无密码登录。(如何实现无密码登录?)

 

SSH登录模块Python实现:paramiko

  • 安装paramiko
pip3 install -i https://pypi.douban.com/simple paramiko==2.6.0
  • paramiko核心组件
    SSHClient和SFTPClient(sftp=ssh file transfer protocol)。
    SSHClient的作用类似于Linux的ssh命令,是对SSH会话的封装,该类封装了传输
    (Transport),通道(Channel)及SFTPClient建立的方法(open_sftp),通常用于执行远程命令。
    SFTPClient的作用类似与Linux的sftp命令,是对SFTP客户端的封装,用以实现远程文件操
    作,如文件上传、下载、修改文件权限等操作。

第一种:基于paramiko实现ssh客户端密码远程登录

代码实现:

import paramiko
def login_ssh_passwd(hostname='172.25.254.10', port=22,username='root', password='123', command='df -h'):
	# 实例化SSHClient
	with paramiko.SSHClient() as client:
	# 自动添加策略,保存服务器的主机名和密钥信息,如果不添加,那么不再本地know_hosts文件中记录的主机将无法连接
	client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
	# 连接SSH服务端,以用户名和密码进行认证
	client.connect(hostname=hostname, port=port, username=username,password=password)
	# 打开一个Channel并执行命令。 stdout 为正确输出,stderr为错误输出,同时是有1个变量有值
	stdin, stdout, stderr = client.exec_command(command)
	# 打印执行结果
	print(stdout.read().decode('utf-8'))
if __name__ == '__main__':
	login_ssh_passwd(hostname='127.0.0.1', command='hostname')

第二种: 基于paramiko实现ssh客户端密钥远程登录(推荐)
测试之前生成公钥和私钥进行测试:

# 生成公钥和私钥, 默认存储在 ~/.ssh/目录下. id_rsa私钥, id_rsa.pub公钥
ssh-keygen
#生成的若不是有效的RSA密钥文件。则用命令ssh-keygen -m PEM -t rsa代替

# 希望我的主机可以无密码连接其他主机(需要将公钥分发给其他主机)
ssh-copy-id -i ~/.ssh/id_rsa.pub user@ip

# 测试无密码连接是否成功
ssh user@ip
ssh -i 私钥位置 user@ip

测试成功后将生成的私钥id_rsa复制到项目中备用
代码实现:

#utils.py
def login_ssh_key(hostname='172.25.254.250', port=22, username='root',
                  private_key='doc/id_rsa', command='df -h'):
    # 0. 读取私钥文件
    pkey = paramiko.RSAKey.from_private_key_file(private_key)
    # 1. 实例化
    client = paramiko.SSHClient()
    # 5.自动应答
    client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    # 2. 连接
    client.connect(hostname, port, username, pkey=pkey)
    # 3.执行命令
    stdin, stdout, stderr = client.exec_command(command)
    # 4. 输出staout结果
    return stdout.read().decode('utf-8')
#测试
if __name__ == '__main__':
    print(login_ssh_key())

运行效果如下:

在这里插入图片描述

 

整合三步代码,至此完成utils.py

补充:STFClient实战代码:实现上传和下载文件
SFTPCLient作为一个sftp的客户端对象,根据ssh传输协议的sftp会话,实现远程文件操作,如上
传、下载、权限、状态。

def paramiko_sftp(hostname='localhost',port=22,username='root',password='123',
                  localpath="/etc/passwd",remotepath="/mnt/passwd"):
    #获取Transport实例
    with paramiko.Transport(hostname,port) as tran:
    	#连接SSH服务端,使用password,也可以用私钥连接
        tran.connect(username=username,password=password)
        ##获取SFTP实例
        sftp = paramiko.SFTPClient.from_transport(tran)
        #执行上传动作
        sftp.put(localpath,remotepath)
        #执行下载动作
        #sftp.get(remotepath,localpath)
        print('file upload success')

 

视图函数实现上述功能的整合并实现scanhost扫描

需求:
1. 用户访问http://ip/hostscan/返回一个html页面
表单填写的是需要扫描的主机IP或者网段,用逗号分隔开
2. 用户填写好网段/IP之后,将填写的信息提交给服务器路由处理(/hostscan/)
POST方法;

代码实现:

from django.shortcuts import render
from django.http import HttpResponse

from hostinfo.models import Server
from hostinfo.utils import get_active_hosts, is_ssh_up, login_ssh_passwordnmccli
from CMDB.settings import commands

# Create your views here.
"""
需求:
    1. 用户访问http://ip/hostscan/返回一个html页面
    表单[填写的是需要扫描的主机IP或者网段,用逗号分隔开](开始扫描按钮)

    2. 用户填写好网段/IP之后,将填写的信息提交给服务器路由处理(/hostscan/)
    POST方法;
"""


def hostscan(request):
    # print(request.method)
    if request.method == 'POST':
        # how to get form post data
        # {'scanhosts': '172.25.254.250,172.25.254.0/24'}
        # print(request.POST)
        """
        # 1. Get Dictionary key
        request.POST.get('scanhosts')

        # 2. split ip and network
        request.POST.get('scanhosts')[0].split(',')
        **
        s = "172.25.254.250,172.25.34.0/24"
        s.split(',')
        Out[3]: ['172.25.254.250', '172.25.34.0/24']
        """
        # ['172.25.254.250', '172.25.34.0/24']
        scanhosts = request.POST.get('scanhosts').split(',')
        servers = []

        for scanhost in scanhosts:
            print("正在扫描%s......" % (scanhost))
            # 获取所有可以ping通的主机IP
            active_hosts = get_active_hosts(scanhost)
            for host in active_hosts:
                if is_ssh_up(host):
                    # Instance Server(ORM)===> MySQL
                    # If ip exists ,How to manage?
                    server = Server()
                    server.IP = host
                    """
                    commands = {
                    'hostname': 'hostname',
                    'os_type': 'uname',
                    'os_distribution': 'dmidecode -s system-manufacturer',
                    'os_release': 'cat /etc/redhat-release',
                    'MAC': 'cat /sys/class/net/`[^vtlsb]`*/address',
                }
                    """
                    for command_name, command in commands.items():
                        # command_name="os_type", command="uname"
                        result = login_ssh_password(hostname=host, command=command)
                        # set server object attribute
                        setattr(server, command_name, result)
                    # save to mysql
                    server.save()
                    # Now Scan host info
                    servers.append(server)
        return render(request, "hostinfo/hostscan.html", {'servers': servers})
    return render(request, 'hostinfo/hostscan.html')

 

html前端页面配置hostscan.html

hostscan.html存放在项目templates文件夹

<!DOCTYPE html>
<html>
<head>
    <title>Scan Host</title>
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <!-- 引入 Bootstrap -->
    <!-- 新 Bootstrap 核心 CSS 文件 -->
    <link href="https://cdn.staticfile.org/twitter-bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
    <!-- HTML5 Shiv 和 Respond.js 用于让 IE8 支持 HTML5元素和媒体查询 -->
</head>
<body>
<nav class="navbar navbar-inverse" role="navigation">
    <div class="container-fluid">
        <div class="navbar-header">
            <a class="navbar-brand" href="/">CMDB</a>
        </div>
        <div>
            <ul class="nav navbar-nav">
                <li class="active"><a href="/hostscan/">Scanhost</a></li>
            </ul>
        </div>
    </div>
</nav>
<div class="container">
    <div class="row">
        <div class="col-sm-6 col-sm-offset-3">
            <h1>Scan Host</h1>
            <form action="/hostscan/" method="post">
                {% csrf_token %}
                <input type="text" name="scanhosts"
                       width="800px"
                       placeholder="eg:172.25.254.0/24,192.168.0.0/24">
                <input type="submit" value="submit">
            </form>

            <br/>
            {% if servers %}
                <table class="table table-striped table-hover">
                    <tr>
                        <td>ID</td>
                        <td>IP</td>
                        <td>hostname</td>
                        <td>os_distribution</td>
                    </tr>
                    {% for server in servers %}
                        <tr>
                            <td>{{ server.id }}</td>
                            <td>{{ server.IP }}</td>
                            <td>{{ server.hostname }}</td>
                            <td>{{ server.os_distribution }}</td>
                        </tr>
                    {% endfor %}
                </table>
            {% endif %}

        </div>
    </div>
</div>
<!-- jQuery (Bootstrap 的 JavaScript 插件需要引入 jQuery) -->
<!-- jQuery文件。务必在bootstrap.min.js 之前引入 -->
<script src="https://cdn.staticfile.org/jquery/2.1.1/jquery.min.js"></script>
</body>
</html>

 

路由文件配置

#主路由文件:

from django.contrib import admin
from django.urls import path,include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('',include('hostinfo.urls'))
]
#子路由文件

from django.urls import path
from hostinfo import views

urlpatterns = [
    path('hostscan/', views.hostscan, name='hostscan'),
]

 

测试:运行项目

python3 manage.py runserver

访问http://127.0.0.1:8000/hostscan/

输入要查询的ip地址

 

 

 

©️2020 CSDN 皮肤主题: 技术工厂 设计师:CSDN官方博客 返回首页