来源:点击进入
资产管理探测流程
存活探测: 获取局域网内存活的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地址