feat:1.增加程序鲁棒性 2:优化操作 3:增加是否启动运行

This commit is contained in:
samwaf
2024-07-04 16:36:41 +08:00
parent ab85678573
commit c7c311602f
18 changed files with 511 additions and 146 deletions

View File

@@ -3,6 +3,8 @@ package api
import (
"SamWaf/enums"
"SamWaf/global"
"SamWaf/globalobj"
"SamWaf/innerbean"
"SamWaf/model"
"SamWaf/model/common/response"
"SamWaf/model/request"
@@ -24,13 +26,20 @@ func (w *WafHostAPi) AddApi(c *gin.Context) {
//端口从未在本系统加过,检测端口是否被其他应用占用
if wafHostService.CheckPortExistApi(req.Port) == 0 && utils.PortCheck(req.Port) == false {
response.FailWithMessage("端口被其他应用占用不能使用,如果使用的宝塔请在Samwaf系统管理-一键修改进行操作", c)
return
//发送websocket 推送消息
global.GQEQUE_MESSAGE_DB.PushBack(innerbean.OpResultMessageInfo{
BaseMessageInfo: innerbean.BaseMessageInfo{OperaType: "提示信息", Server: global.GWAF_CUSTOM_SERVER_NAME},
Msg: "端口被其他应用占用不能使用,如果使用的宝塔请在Samwaf系统管理-一键修改进行操作",
Success: "true",
})
//return
req.START_STATUS = 1 //设置成不能启动
}
err = wafHostService.CheckIsExistApi(req)
if err != nil && errors.Is(err, gorm.ErrRecordNotFound) {
hostCode, err := wafHostService.AddApi(req)
if err == nil {
w.NotifyWaf(hostCode)
w.NotifyWaf(hostCode, nil)
response.OkWithMessage("添加成功", c)
} else {
@@ -114,16 +123,23 @@ func (w *WafHostAPi) ModifyHostApi(c *gin.Context) {
var req request.WafHostEditReq
err := c.ShouldBindJSON(&req)
if err == nil {
wafHostOld := wafHostService.GetDetailByCodeApi(req.CODE)
//端口从未在本系统加过,检测端口是否被其他应用占用
if wafHostService.CheckPortExistApi(req.Port) == 0 && utils.PortCheck(req.Port) == false {
response.FailWithMessage("端口被其他应用占用不能使用", c)
return
//发送websocket 推送消息
global.GQEQUE_MESSAGE_DB.PushBack(innerbean.OpResultMessageInfo{
BaseMessageInfo: innerbean.BaseMessageInfo{OperaType: "提示信息", Server: global.GWAF_CUSTOM_SERVER_NAME},
Msg: "端口被其他应用占用不能使用,如果使用的宝塔请在Samwaf系统管理-一键修改进行操作",
Success: "true",
})
//return
req.START_STATUS = 1 //设置成不能启动
}
err = wafHostService.ModifyApi(req)
if err != nil {
response.FailWithMessage("编辑发生错误", c)
} else {
w.NotifyWaf(req.CODE)
w.NotifyWaf(req.CODE, wafHostOld)
response.OkWithMessage("编辑成功", c)
}
@@ -150,21 +166,56 @@ func (w *WafHostAPi) ModifyGuardStatusApi(c *gin.Context) {
}
}
/*
*
修改启动状态
*/
func (w *WafHostAPi) ModifyStartStatusApi(c *gin.Context) {
var req request.WafHostStartStatusReq
err := c.ShouldBind(&req)
if err == nil {
wafHostOld := wafHostService.GetDetailByCodeApi(req.CODE)
_, svrOk := globalobj.GWAF_RUNTIME_OBJ_WAF_ENGINE.ServerOnline[wafHostOld.Port]
if req.START_STATUS == 0 && !svrOk && utils.PortCheck(wafHostOld.Port) == false {
//发送websocket 推送消息
global.GQEQUE_MESSAGE_DB.PushBack(innerbean.OpResultMessageInfo{
BaseMessageInfo: innerbean.BaseMessageInfo{OperaType: "提示信息", Server: global.GWAF_CUSTOM_SERVER_NAME},
Msg: "端口被其他应用占用不能使用,如果使用的宝塔请在Samwaf系统管理-一键修改进行操作",
Success: "true",
})
response.FailWithMessage("端口被其他应用占用不能开启", c)
return
} else {
err = wafHostService.ModifyStartStatusApi(req)
if err != nil {
response.FailWithMessage("更新状态发生错误", c)
} else {
//发送状态改变通知
w.NotifyWaf(req.CODE, wafHostOld)
response.OkWithMessage("状态更新成功", c)
}
}
} else {
response.FailWithMessage("解析失败", c)
}
}
/*
*
通知到waf引擎实时生效
*/
func (w *WafHostAPi) NotifyWaf(hostCode string) {
//情况1端口是新的域名也是新的
//情况2, 端口不变,就是重新加载数据
//情况3端口从A切换到B了域名是旧的
//情况4端口更改后当前这个端口下没有域名了应该是关闭了并移除数据
func (w *WafHostAPi) NotifyWaf(hostCode string, oldHostInterface interface{}) {
var hosts []model.Hosts
global.GWAF_LOCAL_DB.Where("code = ? ", hostCode).Find(&hosts)
var chanInfo = spec.ChanCommonHost{
HostCode: hostCode,
Type: enums.ChanTypeHost,
Content: hosts,
HostCode: hostCode,
Type: enums.ChanTypeHost,
Content: hosts,
OldContent: oldHostInterface,
}
global.GWAF_CHAN_MSG <- chanInfo
}

View File

@@ -36,6 +36,7 @@ var (
GWAF_RUNTIME_IS_UPDATETING bool = false //是否正在升级中
GWAF_RUNTIME_CURRENT_EXEPATH string = "" //当前程序运行路径
GWAF_RUNTIME_CURRENT_WEBPORT string = "" //当前程序所占用端口
/**
遥测数据

View File

@@ -43,6 +43,16 @@ type UpdateResultMessageInfo struct {
Success string `json:"success"`
}
/*
*
实时操作反馈
*/
type OpResultMessageInfo struct {
BaseMessageInfo
Msg string `json:"msg"`
Success string `json:"success"`
}
func (r RuleMessageInfo) ToFormat() map[string]*wechat.DataItem {
Data := map[string]*wechat.DataItem{}
Data["domain"] = &wechat.DataItem{

View File

@@ -16,3 +16,55 @@ export function hostlist(data) {
data: data
})
}
//更改防护状态
export function changeGuardStatus(params) {
return request({
url: 'wafhost/host/guardstatus',
method: 'get',
params: params
})
}
//更改启动状态
export function changeStartStatus(params) {
return request({
url: 'wafhost/host/startstatus',
method: 'get',
params: params
})
}
//加载详情
export function getHostDetail(params) {
return request({
url: 'wafhost/host/detail',
method: 'get',
params: params
})
}
//删除主机
export function delHost(params) {
return request({
url: 'wafhost/host/del',
method: 'get',
params: params
})
}
//添加主机
export function addHost(data) {
return request({
url: 'wafhost/host/add',
method: 'post',
data: data
})
}
//编辑主机
export function editHost(data) {
return request({
url: 'wafhost/host/edit',
method: 'post',
data: data
})
}

View File

@@ -17,6 +17,12 @@ export const SSL_STATUS = {
NOT_SSL: 0,
SSL: 1,
};
// 启动状态枚举
export const START_STATUS = {
START: 0,
NOT_START: 1,
};
// 合同状态枚举
export const CONTRACT_STATUS = {
FAIL: 0,

View File

@@ -39,20 +39,14 @@
@page-change="rehandlePageChange" @change="rehandleChange" @select-change="rehandleSelectChange"
:headerAffixedTop="true" :headerAffixProps="{ offsetTop: offsetTop, container: getContainer }">
<template #guard_status="{ row }">
<t-popconfirm theme="default" :visible="row.guard_status_visiable" @visible-change="onVisibleChange">
<template slot="content">
<p class="title">防护规则</p>
<p class="describe">带描述的气泡确认框在主要说明之外增加了操作相关的详细描述</p>
</template>
<t-switch size="large" v-model="row.guard_status ===1" :label="['已防护', '未防护']"
@change="changeGuardStatus($event,row)">
</t-switch>
</t-popconfirm>
<!-- <t-tag v-if="row.guard_status === GUARD_STATUS.UN_GUARDDING" theme="warning" variant="light">未防护</t-tag>
<t-tag v-if="row.guard_status === GUARD_STATUS.GUARDDING" theme="success" variant="light">已防护</t-tag> -->
</template>
<template #start_status="{ row }">
<t-switch size="large" v-model="row.start_status===0" :label="['自动启动', '手工启动']"
@change="changeStartStatus($event,row)">
</t-switch>
</template>
<template #ssl="{ row }">
<p v-if="row.ssl === SSL_STATUS.NOT_SSL"></p>
@@ -71,8 +65,9 @@
<template #op="slotProps">
<!--<a class="t-button-link" @click="handleClickEdit(slotProps)">系统自带防御</a>-->
<a class="t-button-link" @click="handleClickEdit(slotProps)">编辑</a>
<a class="t-button-link" @click="handleClickDelete(slotProps)">删除</a>
<a class="t-button-link" v-if="slotProps.row.global_host!==1" @click="handleClickCopy(slotProps)">复制</a>
<a class="t-button-link" v-if="slotProps.row.global_host!==1" @click="handleClickEdit(slotProps)">编辑</a>
<a class="t-button-link" v-if="slotProps.row.global_host!==1" @click="handleClickDelete(slotProps)">删除</a>
</template>
</t-table>
</div>
@@ -98,8 +93,7 @@
<t-form-item label="网站" name="host">
<t-tooltip class="placement top center" content="输入您需要防护的网站域名:如 www.samwaf.com" placement="top"
:overlay-style="{ width: '200px' }" show-arrow>
<t-input :style="{ width: '480px' }" v-model="formData.host" placeholder="请输入网站的网址"
@change="ChangeHost('add')"></t-input>
<t-input :style="{ width: '480px' }" v-model="formData.host" placeholder="请输入网站的网址"></t-input>
</t-tooltip>
</t-form-item>
<t-form-item label="端口" name="port">
@@ -119,6 +113,15 @@
</t-radio-group>
</t-tooltip>
</t-form-item>
<t-form-item label="启动状态" name="start_status">
<t-tooltip class="placement top center" content="该功能是选择是否直接启动。" placement="top"
:overlay-style="{ width: '200px' }" show-arrow>
<t-radio-group v-model="formData.start_status">
<t-radio value="0">直接启动</t-radio>
<t-radio value="1">等待人工启动</t-radio>
</t-radio-group>
</t-tooltip>
</t-form-item>
<t-form-item label="密钥串" name="keyfile" v-if="formData.ssl=='1'">
<t-tooltip class="placement top center"
content="通常文件名:*.key 内容格式如下:-----BEGIN RSA PRIVATE KEY----- 全选复制填写进来" placement="top"
@@ -137,12 +140,12 @@
</t-tooltip>
</t-form-item>
<t-form-item label="后端域名" name="remote_host">
<!--<t-form-item label="后端域名" name="remote_host">
<t-tooltip class="placement top center" content="后端域名通常同第一项网站域名相同(加上协议 http:// 或 https://"
placement="top" :overlay-style="{ width: '200px' }" show-arrow>
<t-input :style="{ width: '480px' }" v-model="formData.remote_host" placeholder="请输入后端域名"></t-input>
</t-tooltip>
</t-form-item>
</t-form-item>-->
<t-form-item label="后端IP" name="remote_ip">
<t-tooltip class="placement top center" content="如SamWaf同网站在同一台服务器 填写127.0.0.1 如果是不同服务器请填写实际IP"
placement="top" :overlay-style="{ width: '200px' }" show-arrow>
@@ -239,8 +242,7 @@
<t-tabs :defaultValue="1">
<t-tab-panel :value="1" label="基础内容">
<t-form-item label="网站" name="host">
<t-input :style="{ width: '480px' }" v-model="formEditData.host" placeholder="请输入网站的网址"
@change="ChangeHost('edit')"></t-input>
<t-input :style="{ width: '480px' }" v-model="formEditData.host" placeholder="请输入网站的网址" disabled></t-input>
</t-form-item>
<t-form-item label="端口" name="port">
<t-input-number :style="{ width: '150px' }" v-model="formEditData.port" placeholder="请输入网站的端口一般是80/443">
@@ -252,19 +254,25 @@
<t-radio value="1">加密证书需填写证书</t-radio>
</t-radio-group>
</t-form-item>
<t-form-item label="证书串" name="certfile" v-if="formEditData.ssl=='1'">
<t-textarea :style="{ width: '480px' }" v-model="formEditData.certfile" placeholder="请输入内容"
name="certfile">
</t-textarea>
</t-form-item>
<!--<t-form-item label="启动状态" name="start_status">
<t-radio-group v-model="formEditData.start_status">
<t-radio value="0">直接启动</t-radio>
<t-radio value="1">等待人工启动</t-radio>
</t-radio-group>
</t-form-item>-->
<t-form-item label="密钥串" name="keyfile" v-if="formEditData.ssl=='1'">
<t-textarea :style="{ width: '480px' }" v-model="formEditData.keyfile" placeholder="请输入内容"
name="keyfile">
</t-textarea>
</t-form-item>
<t-form-item label="后端域名" name="remote_host">
<t-input :style="{ width: '480px' }" v-model="formEditData.remote_host" placeholder="请输入后端域名"></t-input>
<t-form-item label="证书串" name="certfile" v-if="formEditData.ssl=='1'">
<t-textarea :style="{ width: '480px' }" v-model="formEditData.certfile" placeholder="请输入内容"
name="certfile">
</t-textarea>
</t-form-item>
<!--<t-form-item label="后端域名" name="remote_host">
<t-input :style="{ width: '480px' }" v-model="formEditData.remote_host" placeholder="请输入后端域名"></t-input>
</t-form-item>-->
<t-form-item label="后端IP" name="remote_ip">
<t-input :style="{ width: '480px' }" v-model="formEditData.remote_ip" placeholder="请输入后端IP"></t-input>
</t-form-item>
@@ -348,10 +356,22 @@
:onCancel="onCancel">
</t-dialog>
<t-dialog :visible.sync="ImportXlsxVisible">
<t-dialog :visible.sync="ImportXlsxVisible" @confirm="ImportXlsxVisible=false">
<t-upload :action="fileUploadUrl" :tips="tips" :headers="fileHeader" v-model="files" @fail="handleFail"
@success="onSuccess" theme="file-input" placeholder="未选择文件"></t-upload>
</t-dialog>
<t-dialog header="防护状态提示" :visible.sync="guardConfirmVisible" @confirm="onGuardStatusConfirm"
:onCancel="onGuardStatusCancel">
<div slot="body">
<div>防护状态开启该网站进行实时防护防护状态关闭该网站会关闭实时防护</div>
</div>
</t-dialog>
<t-dialog header="启动状态提示" :visible.sync="startConfirmVisible" @confirm="onStartStatusConfirm"
:onCancel="onStartStatusCancel">
<div>启动状态开启会正常接收用户请求 启动状态关闭会停止用户请求</div>
</t-dialog>
</div>
</template>
<script lang="ts">
@@ -361,14 +381,15 @@ import {FileSafetyIcon, LinkIcon, SearchIcon} from 'tdesign-icons-vue';
import {prefix} from '@/config/global';
import {export_api} from '@/apis/common';
import {allhost, hostlist} from '@/apis/host';
import {allhost, changeGuardStatus, changeStartStatus, hostlist,getHostDetail,delHost,addHost,editHost} from '@/apis/host';
import {
CONTRACT_PAYMENT_TYPES,
CONTRACT_STATUS,
CONTRACT_STATUS_OPTIONS,
CONTRACT_TYPES,
GUARD_STATUS,
SSL_STATUS
SSL_STATUS,
START_STATUS
} from '@/constants';
const INITIAL_DATA = {
@@ -383,6 +404,7 @@ const INITIAL_DATA = {
guard_status: '',
remarks: '',
defense_json: '{"bot":1,"sqli":1,"xss":1,"scan"1,"rce":1}',
start_status: '0',
};
export default Vue.extend({
name: 'ListBase',
@@ -423,6 +445,21 @@ export default Vue.extend({
message: '请输入网站名称',
type: 'error'
}],
port: [{
required: true,
message: '请输入网站端口',
type: 'error'
}],
remote_ip: [{
required: true,
message: '请输入远端IP',
type: 'error'
}],
remote_port: [{
required: true,
message: '请输入远端端口',
type: 'error'
}],
},
remote_system_options: [{
label: '宝塔',
@@ -460,6 +497,7 @@ export default Vue.extend({
],
GUARD_STATUS,
SSL_STATUS,
START_STATUS,
CONTRACT_STATUS,
CONTRACT_STATUS_OPTIONS,
CONTRACT_TYPES,
@@ -484,6 +522,14 @@ export default Vue.extend({
ellipsis: true,
colKey: 'port',
},
{
title: '启动状态',
colKey: 'start_status',
width: 100,
cell: {
col: 'start_status'
}
},
{
title: '防护状态',
colKey: 'guard_status',
@@ -540,12 +586,17 @@ export default Vue.extend({
//索引区域
deleteIdx: -1,
guardStatusIdx: -1,
startStatusIdx: -1,
//来源页面
sourcePage: "",
hostAddUrl: this.samwafglobalconfig.getOnlineUrl() + '/guide/Host.html#_2-新增可被防火墙保护的网站',
//主机字典
host_dic: {}
host_dic: {},
//弹窗确认
guardConfirmVisible: false,//更改防护状态的弹窗控制
startConfirmVisible: false,//更改启动状态的弹窗控制
};
},
computed: {
@@ -658,6 +709,47 @@ export default Vue.extend({
},
},);
},
handleClickCopy(e) {
console.log(e)
const {
code, global_host
} = e.row
if (global_host === 1) {
this.$message.warning("全局网站不能操作");
return
}
console.log(code)
this.addFormVisible = true
let that = this
getHostDetail({
CODE: code,
})
.then((res) => {
let resdata = res
console.log(resdata)
if (resdata.code === 0) {
let detail_data_tmp = resdata.data;
detail_data_tmp.ssl = detail_data_tmp.ssl.toString()
detail_data_tmp.start_status = detail_data_tmp.start_status.toString()
that.formData= {
...detail_data_tmp
}
that.formData.code = null
let defenseJson = JSON.parse(detail_data_tmp.defense_json)
that.hostDefenseData.bot = defenseJson.bot.toString()
that.hostDefenseData.sqli = defenseJson.sqli.toString()
that.hostDefenseData.xss = defenseJson.xss.toString()
that.hostDefenseData.scan = defenseJson.scan.toString()
that.hostDefenseData.rce = defenseJson.rce.toString()
}
})
.catch((e: Error) => {
console.log(e);
})
.finally(() => {
});
},
handleClickEdit(e) {
console.log(e)
@@ -686,14 +778,14 @@ export default Vue.extend({
let postdata = {
...that.formData
}
if (postdata.remote_host.length == 0) {
postdata.remote_host = "http://" + postdata.host
} else {
if (postdata.remote_host.indexOf("http://") != 0 && postdata.remote_host.indexOf("https://") != 0) {
postdata.remote_host = "http://" + postdata.remote_host
}
postdata.host = postdata.host.toLowerCase();
if (postdata.host.indexOf("http://") >=0 || postdata.host.indexOf("https://") >=0) {
that.$message.warning("主机请不要填写http和https 直接写域名即可");
return
}
postdata.remote_host = "http://" + postdata.host
postdata['ssl'] = Number(postdata['ssl'])
postdata['start_status'] = Number(postdata['start_status'])
let defenseData = {
bot: parseInt(this.hostDefenseData.bot),
sqli: parseInt(this.hostDefenseData.sqli),
@@ -702,8 +794,7 @@ export default Vue.extend({
rce: parseInt(this.hostDefenseData.rce),
}
postdata['defense_json'] = JSON.stringify(defenseData)
this.$request
.post('/wafhost/host/add', {
addHost( {
...postdata
})
.then((res) => {
@@ -713,6 +804,21 @@ export default Vue.extend({
that.$message.success(resdata.msg);
that.addFormVisible = false;
that.pagination.current = 1
that.formData = {
host: 'www.baidu.com',
port: 80,
remote_host: 'http://www.baidu.com',
remote_ip: '127.0.0.1',
remote_port: 81,
ssl: '0',
remote_system: "默认",
remote_app: "默认",
guard_status: '',
remarks: '',
defense_json: '{"bot":1,"sqli":1,"xss":1,"scan"1,"rce":1}',
start_status: '0',
};
that.getList("")
} else {
that.$message.warning(resdata.msg);
@@ -740,6 +846,7 @@ export default Vue.extend({
}
postdata['ssl'] = Number(postdata['ssl'])
postdata['start_status'] = Number(postdata['start_status'])
let defenseData = {
bot: parseInt(this.hostDefenseData.bot),
sqli: parseInt(this.hostDefenseData.sqli),
@@ -749,8 +856,7 @@ export default Vue.extend({
}
postdata['defense_json'] = JSON.stringify(defenseData)
console.log(postdata)
this.$request
.post('/wafhost/host/edit', {
editHost( {
...postdata
})
.then((res) => {
@@ -817,12 +923,9 @@ export default Vue.extend({
code
} = this.data[this.deleteIdx]
let that = this
this.$request
.get('/wafhost/host/del', {
params: {
delHost({
CODE: code,
}
})
})
.then((res) => {
let resdata = res
console.log(resdata)
@@ -852,18 +955,16 @@ export default Vue.extend({
},
getDetail(id) {
let that = this
this.$request
.get('/wafhost/host/detail', {
params: {
getHostDetail({
CODE: id,
}
})
})
.then((res) => {
let resdata = res
console.log(resdata)
if (resdata.code === 0) {
that.detail_data = resdata.data;
that.detail_data.ssl = that.detail_data.ssl.toString()
that.detail_data.start_status = that.detail_data.start_status.toString()
that.formEditData = {
...that.detail_data
}
@@ -916,6 +1017,8 @@ export default Vue.extend({
*/
HandleImportExcel() {
this.ImportXlsxVisible = true
this.tips = ""
this.files= []
},
changeGuardStatus(e, row) {
@@ -926,72 +1029,22 @@ export default Vue.extend({
return value['code'] == code
})
console.log("rowIndex", rowIndex)
this.data[rowIndex].guard_status_visiable = !this.data[rowIndex].guard_status_visiable
this.guardStatusIdx = rowIndex
console.log(e)
this.guardConfirmVisible = true
},
onVisibleChange(val, context = {}) {
let that = this
console.log("this.guardStatusIdx", this.guardStatusIdx)
if (this.guardStatusIdx == -1) {
return
}
changeStartStatus(e, row) {
console.log("this.data", this.data[that.guardStatusIdx])
let {
code, guard_status
} = this.data[this.guardStatusIdx]
console.log(context, context.trigger)
// trigger 表示触发来源,可以根据触发来源自由控制 visible
if (context && context.trigger === 'confirm') {
console.log('guardStatusIdx', that.guardStatusIdx)
const msg = that.$message.info('提交中');
that.$request
.get('/wafhost/host/guardstatus', {
params: {
CODE: code,
GUARD_STATUS: guard_status == 1 ? 0 : 1,
}
})
.then((res) => {
let resdata = res
console.log(resdata)
if (resdata.code === 0) {
that.getList("")
that.$message.close(msg);
that.$message.success(resdata.msg)
that.data[that.guardStatusIdx].guard_status_visiable = false
that.guardStatusIdx = -1;
} else {
that.$message.warning(resdata.msg);
}
})
.catch((e: Error) => {
console.log(e);
})
.finally(() => {
});
} else if (context && context.trigger === 'cancel') {
//TODO 不知此处为何点击无效
console.log("this.data", that.data)
console.log("that.data[that.guardStatusIdx]", that.data[that.guardStatusIdx])
that.data[that.guardStatusIdx].guard_status_visiable = false
that.guardStatusIdx = -1;
}
},
ChangeHost(form) {
if (form == "add") {
if (this.formData.remote_host.indexOf("http://") != 0 && this.formData.remote_host.indexOf("https://") != 0) {
this.formData.remote_host = "http://" + this.formData.remote_host
}
} else if (form == "edit") {
if (this.formEditData.remote_host.indexOf("http://") != 0 && this.formEditData.remote_host.indexOf("https://") != 0) {
this.formEditData.remote_host = "http://" + this.formEditData.remote_host
}
}
console.log(e, row)
let {code} = row
let rowIndex = this.data.findIndex(function (value, index, arr) {
console.log("findIndex", value, index, arr)
return value['code'] == code
})
console.log("rowIndex", rowIndex)
this.startStatusIdx = rowIndex
console.log(e)
this.startConfirmVisible = true
},
handleFail({file}) {
this.$message.error(`文件 ${file.name} 上传失败`);
@@ -1022,6 +1075,84 @@ export default Vue.extend({
//this.formAddData = event.target.value;
},
//弹窗部分代码
onGuardStatusConfirm(){
let that = this
console.log("this.guardStatusIdx", this.guardStatusIdx)
if (this.guardStatusIdx == -1) {
return
}
console.log("this.data", this.data[that.guardStatusIdx])
let {
code, guard_status
} = this.data[this.guardStatusIdx]
changeGuardStatus({
CODE: code,
GUARD_STATUS: guard_status == 1 ? 0 : 1,
})
.then((res) => {
let resdata = res
console.log(resdata)
if (resdata.code === 0) {
that.getList("")
that.$message.success(resdata.msg)
that.guardStatusIdx = -1;
this.guardConfirmVisible = false
} else {
that.$message.warning(resdata.msg);
this.guardStatusIdx = -1;
this.guardConfirmVisible = false
}
})
.catch((e: Error) => {
console.log(e);
})
.finally(() => {
});
},
onGuardStatusCancel(){
this.guardConfirmVisible = false
this.guardStatusIdx = -1;
},
onStartStatusConfirm() {
let that = this
this.startConfirmVisible = false
let {
code, start_status
} = this.data[this.startStatusIdx]
console.log("code,start_status", code, start_status)
changeStartStatus({
CODE: code,
START_STATUS: start_status === 1 ? 0 : 1,
}
)
.then((res) => {
let resdata = res
console.log(resdata)
if (resdata.code === 0) {
that.getList("")
that.$message.success(resdata.msg)
this.startStatusIdx = -1;
} else {
that.$message.warning(resdata.msg);
this.startStatusIdx = -1;
}
})
.catch((e: Error) => {
console.log(e);
})
.finally(() => {
});
},
onStartStatusCancel() {
this.startConfirmVisible = false
this.startStatusIdx = -1;
},
//end method
},
});

44
main.go
View File

@@ -330,18 +330,52 @@ func (m *wafSystenService) run() {
case enums.ChanTypeHost:
hosts := msg.Content.([]model.Hosts)
if len(hosts) == 1 {
if globalobj.GWAF_RUNTIME_OBJ_WAF_ENGINE.HostTarget[hosts[0].Host+":"+strconv.Itoa(hosts[0].Port)].RevProxy != nil {
globalobj.GWAF_RUNTIME_OBJ_WAF_ENGINE.HostTarget[hosts[0].Host+":"+strconv.Itoa(hosts[0].Port)].RevProxy = nil
zlog.Debug("主机重新代理", hosts[0].Host+":"+strconv.Itoa(hosts[0].Port))
//情况1端口是新的域名也是新的
//情况2端口不变,域名也不变,就是重新加载数据
//情况3端口从A切换到B了域名是旧的 ;端口更改后当前这个端口下没有域名了,应该是关闭了,并移除数据
//情况1
if msg.OldContent == nil {
zlog.Debug("主机处理情况1 端口是新的,域名也是新的")
globalobj.GWAF_RUNTIME_OBJ_WAF_ENGINE.LoadHost(hosts[0])
globalobj.GWAF_RUNTIME_OBJ_WAF_ENGINE.StartAllProxyServer()
} else {
hostsOld := msg.OldContent.(model.Hosts)
if hosts[0].Host == hostsOld.Host && hosts[0].Port == hostsOld.Port {
//情况2
zlog.Debug("主机处理情况2 端口不变,域名也不变,就是重新加载数据")
if globalobj.GWAF_RUNTIME_OBJ_WAF_ENGINE.HostTarget[hosts[0].Host+":"+strconv.Itoa(hosts[0].Port)] != nil &&
globalobj.GWAF_RUNTIME_OBJ_WAF_ENGINE.HostTarget[hosts[0].Host+":"+strconv.Itoa(hosts[0].Port)].RevProxy != nil {
//设置空代理
globalobj.GWAF_RUNTIME_OBJ_WAF_ENGINE.HostTarget[hosts[0].Host+":"+strconv.Itoa(hosts[0].Port)].RevProxy = nil
zlog.Debug("主机重新代理", hosts[0].Host+":"+strconv.Itoa(hosts[0].Port))
}
//如果本次是关闭,那么应该关闭主机
if hosts[0].START_STATUS == 1 {
globalobj.GWAF_RUNTIME_OBJ_WAF_ENGINE.RemoveHost(hosts[0])
}
globalobj.GWAF_RUNTIME_OBJ_WAF_ENGINE.LoadHost(hosts[0])
globalobj.GWAF_RUNTIME_OBJ_WAF_ENGINE.StartAllProxyServer()
} else if hosts[0].Host == hostsOld.Host && hosts[0].Port != hostsOld.Port {
//情况3
zlog.Debug("主机处理情况3 端口从A切换到B了域名是旧的 ;端口更改后当前这个端口下没有域名了,应该是关闭了,并移除数据")
if globalobj.GWAF_RUNTIME_OBJ_WAF_ENGINE.HostTarget[hostsOld.Host+":"+strconv.Itoa(hostsOld.Port)] != nil &&
globalobj.GWAF_RUNTIME_OBJ_WAF_ENGINE.HostTarget[hostsOld.Host+":"+strconv.Itoa(hostsOld.Port)].RevProxy != nil {
globalobj.GWAF_RUNTIME_OBJ_WAF_ENGINE.HostTarget[hostsOld.Host+":"+strconv.Itoa(hostsOld.Port)].RevProxy = nil
}
globalobj.GWAF_RUNTIME_OBJ_WAF_ENGINE.RemoveHost(hostsOld)
globalobj.GWAF_RUNTIME_OBJ_WAF_ENGINE.LoadHost(hosts[0])
globalobj.GWAF_RUNTIME_OBJ_WAF_ENGINE.StartAllProxyServer()
}
}
globalobj.GWAF_RUNTIME_OBJ_WAF_ENGINE.LoadHost(hosts[0])
globalobj.GWAF_RUNTIME_OBJ_WAF_ENGINE.StartAllProxyServer()
}
break
case enums.ChanTypeDelHost:
host := msg.Content.(model.Hosts)
if host.Id != "" {
globalobj.GWAF_RUNTIME_OBJ_WAF_ENGINE.RemoveHost(host)
globalobj.GWAF_RUNTIME_OBJ_WAF_ENGINE.EnumAllPortProxyServer()
}
break
}

View File

@@ -19,6 +19,7 @@ type Hosts struct {
REMARKS string `json:"remarks"` //备注
GLOBAL_HOST int `json:"global_host"` //默认全局 1 全局 0非全局
DEFENSE_JSON string `json:"defense_json"` //自身防御 json
START_STATUS int `json:"start_status"` //启动状态 如果是0 启动 ; 如果是1 不启动
}
type HostsDefense struct {

View File

@@ -13,4 +13,5 @@ type WafHostAddReq struct {
Certfile string `json:"certfile"` // 证书文件
Keyfile string `json:"keyfile"` // 密钥文件
DEFENSE_JSON string `json:"defense_json"` //自身防御 json
START_STATUS int `json:"start_status"` //启动状态
}

View File

@@ -14,4 +14,5 @@ type WafHostEditReq struct {
Certfile string `json:"certfile"` // 证书文件
Keyfile string `json:"keyfile"` // 密钥文件
DEFENSE_JSON string `json:"defense_json"` //自身防御 json
START_STATUS int `json:"start_status"` //启动状态
}

View File

@@ -0,0 +1,6 @@
package request
type WafHostStartStatusReq struct {
CODE string `json:"code"`
START_STATUS int `json:"start_status"` //启动状态
}

View File

@@ -1,7 +1,8 @@
package spec
type ChanCommonHost struct {
HostCode string
Type int
Content interface{}
HostCode string
Type int
Content interface{}
OldContent interface{}
}

View File

@@ -17,5 +17,6 @@ func (receiver *HostRouter) InitHostRouter(group *gin.RouterGroup) {
hostRouter.GET("/samwaf/wafhost/host/del", hostApi.DelHostApi)
hostRouter.POST("/samwaf/wafhost/host/edit", hostApi.ModifyHostApi)
hostRouter.GET("/samwaf/wafhost/host/guardstatus", hostApi.ModifyGuardStatusApi)
hostRouter.GET("/samwaf/wafhost/host/startstatus", hostApi.ModifyStartStatusApi)
hostRouter.GET("/samwaf/wafhost/host/allhost", hostApi.GetAllListApi)
}

View File

@@ -39,6 +39,7 @@ func (receiver *WafHostService) AddApi(wafHostAddReq request.WafHostAddReq) (str
REMARKS: wafHostAddReq.REMARKS,
GLOBAL_HOST: 0,
DEFENSE_JSON: wafHostAddReq.DEFENSE_JSON,
START_STATUS: wafHostAddReq.START_STATUS,
}
global.GWAF_LOCAL_DB.Create(wafHost)
return wafHost.Code, nil
@@ -76,6 +77,7 @@ func (receiver *WafHostService) ModifyApi(wafHostEditReq request.WafHostEditReq)
"Keyfile": wafHostEditReq.Keyfile,
"UPDATE_TIME": customtype.JsonTime(time.Now()),
"DEFENSE_JSON": wafHostEditReq.DEFENSE_JSON,
"START_STATUS": wafHostEditReq.START_STATUS,
}
err := global.GWAF_LOCAL_DB.Debug().Model(model.Hosts{}).Where("CODE=?", wafHostEditReq.CODE).Updates(hostMap).Error
@@ -158,7 +160,15 @@ func (receiver *WafHostService) ModifyGuardStatusApi(req request.WafHostGuardSta
err := global.GWAF_LOCAL_DB.Model(model.Hosts{}).Where("CODE=?", req.CODE).Updates(hostMap).Error
return err
}
func (receiver *WafHostService) ModifyStartStatusApi(req request.WafHostStartStatusReq) error {
hostMap := map[string]interface{}{
"START_STATUS": req.START_STATUS,
"UPDATE_TIME": customtype.JsonTime(time.Now()),
}
err := global.GWAF_LOCAL_DB.Model(model.Hosts{}).Where("CODE=?", req.CODE).Updates(hostMap).Error
return err
}
func (receiver *WafHostService) GetAllHostApi() []model.Hosts {
var webHosts []model.Hosts
global.GWAF_LOCAL_DB.Order("global_host desc").Find(&webHosts)
@@ -169,6 +179,13 @@ func (receiver *WafHostService) CheckPortExistApi(port int) int64 {
global.GWAF_LOCAL_DB.Model(&model.Hosts{}).Where("port=?", port).Count(&total)
return total
}
func (receiver *WafHostService) CheckAvailablePortExistApi(port int) int64 {
var total int64 = 0
global.GWAF_LOCAL_DB.Model(&model.Hosts{}).Where(" (start_status = 0 or start_status is null) and port=?", port).Count(&total)
return total
}
func (receiver *WafHostService) IsEmptyHost() bool {
var total int64 = 0
err := global.GWAF_LOCAL_DB.Model(&model.Hosts{}).Where("global_host=?", 0).Count(&total).Error

View File

@@ -194,6 +194,7 @@ func (receiver *WafStatService) StatHomeRumtimeSysinfo() []response2.WafNameValu
data = append(data, response2.WafNameValue{Name: "当前队列数", Value: fmt.Sprintf("主数据:%v 日志数据:%v 统计数据:%v 消息队列:%v", global.GQEQUE_DB.Len(), global.GQEQUE_LOG_DB.Len(), global.GQEQUE_STATS_DB.Len(), global.GQEQUE_MESSAGE_DB.Len())})
data = append(data, response2.WafNameValue{Name: "当前日志队列处理QPS", Value: fmt.Sprintf("%v", atomic.LoadUint64(&global.GWAF_RUNTIME_LOG_PROCESS))})
data = append(data, response2.WafNameValue{Name: "当前web端口使用列表", Value: fmt.Sprintf("%v", global.GWAF_RUNTIME_CURRENT_WEBPORT)})
return data
}

2
vue/dist/index.html vendored
View File

@@ -6,7 +6,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>SamWaf网站防火墙系统Web Application Firewall</title>
<script type="module" crossorigin src="./assets/index.89789988.js"></script>
<script type="module" crossorigin src="./assets/index.e97cb7ae.js"></script>
<link rel="stylesheet" href="./assets/style.559a5c8f.css">
</head>
<body>

View File

@@ -173,6 +173,35 @@ func ProcessDequeEngine() {
}
}
break
case innerbean.OpResultMessageInfo:
//操作实时结果
updatemessage := messageinfo.(innerbean.OpResultMessageInfo)
//发送websocket
for _, ws := range global.GWebSocket.SocketMap {
if ws != nil {
//信息包体进行单独处理
msgBody, _ := json.Marshal(model.MsgDataPacket{
MessageId: uuid.NewV4().String(),
MessageType: "信息通知",
MessageData: updatemessage.Msg,
MessageAttach: nil,
MessageDateTime: time.Now().Format("2006-01-02 15:04:05"),
MessageUnReadStatus: true,
})
//写入ws数据
msgBytes, err := json.Marshal(model.MsgPacket{
MsgCode: "200",
MsgDataPacket: wafsec.AesEncrypt(msgBody, global.GWAF_COMMUNICATION_KEY),
MsgCmdType: "Info",
})
err = ws.WriteMessage(1, msgBytes)
if err != nil {
zlog.Info("发送websocket错误", err)
continue
}
}
}
break
}
}

View File

@@ -785,6 +785,7 @@ func (waf *WafEngine) StartWaf() {
Keyfile: "",
REMARKS: "",
GLOBAL_HOST: 1,
START_STATUS: 0,
}
global.GWAF_LOCAL_DB.Create(wafGlobalHost)
}
@@ -865,7 +866,6 @@ func (waf *WafEngine) LoadHost(inHost model.Hosts) innerbean.ServerRunTime {
return innerbean.ServerRunTime{}
}
//all_certificate[hosts[i].Port][hosts[i].Host] = &cert
mm, ok := waf.AllCertificate[inHost.Port] //[hosts[i].Host]
if !ok {
mm = make(map[string]*tls.Certificate)
@@ -875,11 +875,16 @@ func (waf *WafEngine) LoadHost(inHost model.Hosts) innerbean.ServerRunTime {
}
_, ok := waf.ServerOnline[inHost.Port]
if ok == false && inHost.GLOBAL_HOST == 0 {
waf.ServerOnline[inHost.Port] = innerbean.ServerRunTime{
ServerType: utils.GetServerByHosts(inHost),
Port: inHost.Port,
Status: 1,
if inHost.START_STATUS == 0 {
waf.ServerOnline[inHost.Port] = innerbean.ServerRunTime{
ServerType: utils.GetServerByHosts(inHost),
Port: inHost.Port,
Status: 1,
}
} else {
delete(waf.ServerOnline, inHost.Port)
}
}
//加载主机对于的规则
ruleHelper := &utils.RuleHelper{}
@@ -964,7 +969,7 @@ func (waf *WafEngine) RemoveHost(host model.Hosts) {
AllCertificate map[int]map[string]*tls.Certificate*/
//1.如果这个port里面没有了主机 那可以直接停掉服务
//2.除了第一个情况之外的,把所有他的主机信息和关联信息都干掉
if waf_service.WafHostServiceApp.CheckPortExistApi(host.Port) == 0 {
if waf_service.WafHostServiceApp.CheckAvailablePortExistApi(host.Port) == 0 {
zlog.Debug("准备移除这个端口下所有信息")
// a.移除证书数据
_, ok := waf.AllCertificate[host.Port]
@@ -978,13 +983,18 @@ func (waf *WafEngine) RemoveHost(host model.Hosts) {
//d.暂停服务 并 移除服务信息
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
err := waf.ServerOnline[host.Port].Svr.Shutdown(ctx)
if err != nil {
zlog.Error("shutting down: " + err.Error())
} else {
zlog.Info("shutdown processed successfully port" + strconv.Itoa(host.Port))
_, svrOk := waf.ServerOnline[host.Port]
if svrOk {
err := waf.ServerOnline[host.Port].Svr.Shutdown(ctx)
if err != nil {
zlog.Error("shutting down: " + err.Error())
} else {
zlog.Info("shutdown processed successfully port" + strconv.Itoa(host.Port))
}
delete(waf.ServerOnline, host.Port)
}
delete(waf.ServerOnline, host.Port)
} else {
zlog.Debug("准备移除没用的主机信息")
// a.移除某个端口下的证书数据
@@ -1001,10 +1011,22 @@ func (waf *WafEngine) RemoveHost(host model.Hosts) {
// 开启所有代理
func (waf *WafEngine) StartAllProxyServer() {
for _, v := range waf.ServerOnline {
waf.StartProxyServer(v)
}
waf.EnumAllPortProxyServer()
}
// 罗列端口
func (waf *WafEngine) EnumAllPortProxyServer() {
onlinePorts := ""
for _, v := range waf.ServerOnline {
onlinePorts = strconv.Itoa(v.Port) + "," + onlinePorts
}
global.GWAF_RUNTIME_CURRENT_WEBPORT = onlinePorts
}
func (waf *WafEngine) StartProxyServer(innruntime innerbean.ServerRunTime) {
if innruntime.Status == 0 {
//启动完成的就不进这里了