目录
通过前端页面输入多个IP地址列表,实现批量IP地址封禁操作。格式可以是单个IP地址,CIDR地址段和IP地址范围,例如:
主机IP:112.111.1.1
子网端:112.111.1.0/24
范围:112.111.1.1-112.111.1.100
执行一键封禁时,将对输入的IP地址格式进行检查和格式化,不符合规范的IP地址将通过提示返回,前段修正输入的IP地址列表后重新提交。
一、创建一键封禁菜单
通过PearAdmin的【系统管理】-【权限管理】在护网工具目录下创建【一键封禁】菜单,并设置权限标识和路径信息。

二、前端页面设计
2.1 前端页面布局
前端页面整体展现效果,上半部份为输入需要封建的IP地址列表,后半部分为封禁过程产生的日志信息。

2.2 前端代码
在PearAdmin项目的【templates】-【system】-【huwang】目录下新建文件block.html。
<!DOCTYPE html>
<html>
<head>
<title>一键封禁</title>
{% include 'system/common/header.html' %}
</head>
</head>
<body>
<div class="layui-fluid">
<div class="layui-card">
<fieldset class="layui-elem-field layui-field-title" style="margin-top: 10px;"><legend>IP地址封禁</legend></fieldset>
<div class="layui-card-body">
<form class="layui-form layui-form-pane" action="">
<div class="layui-form-item layui-form-text">
<div class="layui-input-block">
<textarea name="iplist" required lay-verify="required" placeholder="示例: 192.168.1.1 192.168.10.0/24 192.168.2.1-192.168.2.10 " class="layui-textarea" style="height: 200px;"></textarea>
</div>
</div>
<br>
<div class="layui-form-item layui-form-pane">
<div class="layui-input-block" style="margin-inline-start: 0%;">
<button class="layui-btn layui-btn-md" lay-submit lay-filter="block" >提交</button>
<button class="layui-btn layui-btn-md" type="reset" >重置</button>
</div>
</div>
</form>
</div>
</div>
<div class="layui-card">
<fieldset class="layui-elem-field layui-field-title" style="margin-top: 10px;">
<legend>封禁日志</legend>
</fieldset>
<div class="layui-card-body">
<table id="log-table" lay-filter="log-table"></table>
</div>
</div>
</div>
</body>
{% include 'system/common/footer.html' %}
<script>
layui.use(['table', 'form', 'jquery', 'popup'], function(){
let table = layui.table,
popup = layui.popup,
form = layui.form,
$ = layui.$;
form.on('submit(block)', function(data){//监听按钮
$.ajax({
url:"/system/huwang/exec_block", //提交请求的URL
data: JSON.stringify(data.field),
dataType: 'json',
contentType: 'application/json',
type: 'post',
success:function(result){
if (result.success){
layer.msg(result.msg, {icon: 1, time: 2000, title:'成功'}, function(){location.reload();});
return;
}else{
layer.msg(result.msg, { icon: 2, time: 4000, title:'失败' })
}
},
error:function(result){
alert("接口错误!!!"); //无返回或处理有报错时弹框
}
});
return false; // 别忘记这行,防止页面跳转
});
let cols = [
[
{title: '时间', field: 'timestamp', align: 'center', width: 280}, // 时间需较宽
{title: '进程名', field: 'logger_name', align: 'center', width: 280},
{title: '级别', field: 'level', align: 'center', width: 100},
{title: '内容', field: 'message', align: 'left',} // 内容列用minWidth
]
];
table.render({
elem: '#log-table',
url: '/system/huwang/block_log',
page:{limit: 10,limits:[10,30,50]},
cols: cols,
skin: 'line',
text: {none: '暂无设备信息'},
})
});
</script>
</html>
三、后端代码设计
3.1 一键封禁路由
在huwang.py文件中增加一键封禁页面路由。
@bp.get('/block')
@authorize("system:huwang:block")
def block():
return render_template('system/huwang/block.html')
3.2 一键封禁接口
在huwang.py文件中增加一键封禁后端处理接口。
@bp.post('/exec_block')
@authorize("system:huwang:block")
def exec_block():
req_json = request.get_json(force=True)
iplist = str_escape(req_json.get('iplist'))
ip_list = iplist.strip().split('\n')
ip_list = ip_check(ip_list)
if len(ip_list['error']) > 0:
return fail_api('IP地址不正确:'+str(ip_list['error']))
filter = []
filter.append(Resources.location=='外网边界')
filter.append(Resources.enable == 1)
query = db.session.query(Resources).filter(*filter).all()
logger = setup_logger("block_ip")
for dev in query:
try:
logger.info(f"开始进行防火墙: {dev.hostname}封禁操作")
config = {
"host": dev.mgtip,
"vsys": dev.vsys,
"username": dev.loginname,
"password": dev.loginpass,
"apikey": dev.apikey,
"logger": logger
}
# 创建管理器实例
if dev.loginmeth.lower() == 'api' and dev.vendor.lower() == 'fortigate':
firewall = FortiGateManager(**config)
result = firewall.block_ips(ip_list)
if result['success']:
return success_api(str(result['message']))
else:
return fail_api(str(result['message']))
else:
return ('不支持的产品和登陆方式')
except Exception as e:
print(f"封禁IP过程发生未知性错误: {str(e)}")
3.3 一键封禁日志
在huwang.py文件中增加一键封禁日志显示接口。
@bp.get('/block_log')
@authorize("system:huwang:temporary")
def block_log():
query = db.session.query(Hwlog).filter(Hwlog.logger_name=='block_ip').order_by(Hwlog.timestamp.desc()).layui_paginate()
return table_api(
data = [{
'timestamp': record.timestamp,
'logger_name': record.logger_name,
'level': record.level,
'message': record.message
} for record in query.items], count = query.total)
四、IP地址合规检查
在PearAdmin项目【applications】-【common】-【utils】目录下新建check_ip.py实现IP地址格式检查功能。
import re
from IPy import IP
def ip_check(ip_list):
# 定义正则表达式模式
single_ip_pattern = r'^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$'
cidr_pattern = r'^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)/([0-2]?[0-9]|3[0-2])$'
ip_range_pattern = r'^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)-((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$'
# 依次检查是否匹配三种格式
result = {'ok':[], 'error':[]}
for item in ip_list:
if re.match(single_ip_pattern, item):
tmp = {'name':item+'/32','type':'ipmask','subnet':item + ' 255.255.255.255'}
if tmp not in result['ok']:
result['ok'].append(tmp)
elif re.match(cidr_pattern, item):
try:
ip = IP(item)
ip_cidr = f"{ip.strNormal(0)} {ip.netmask().strNormal(0)}"
tmp = {'name':item,'type':'ipmask','subnet':ip_cidr}
if tmp not in result['ok']:
result['ok'].append(tmp)
except:
result['error'].append(item)
elif re.match(ip_range_pattern, item):
ip_start = item.split('-')[0]
ip_end = item.split('-')[1]
if ip_start > ip_end:
result['error'].append(item)
else:
tmp = {'name': item, 'type': 'iprange', 'start-ip': item.split('-')[0], 'end-ip': item.split('-')[1]}
if tmp not in result['ok']:
result['ok'].append(tmp)
else:
result['error'].append(item)
return (result)
五、批量封禁测试
5.1 一次封禁200个IP地址测试

5.2 子网和IP范文封禁测试

日志记录

5.3 动态地址组测试


5.4 IP地址格式检查测试

六、总结
当封禁超过200个地址时,后台处理过程需要时间,前段页面会有少许等待。后续考虑有没有办法优化,从而提高封禁执行效率。
1024

被折叠的 条评论
为什么被折叠?



