增加网站防护基本编辑,删除

This commit is contained in:
samwaf
2022-10-08 11:30:21 +08:00
parent 5864e457fc
commit 4fda55103c
7 changed files with 420 additions and 59 deletions
+3
View File
@@ -11,4 +11,7 @@ var (
GWAF_LOCAL_DB *gorm.DB //通用本地数据库,尊重用户隐私
GWAF_REMOTE_DB *gorm.DB //仅当用户使用云数据库
GWAF_LOCAL_SERVER_PORT int = 0 // 本地local端口
GWAF_USER_CODE string // 当前识别号
GWAF_TENANT_ID string // 当前租户ID
)
+152
View File
@@ -6,10 +6,14 @@ import (
"SamWaf/model"
"SamWaf/model/common/response"
"SamWaf/model/waflog/request"
"errors"
"github.com/gin-gonic/gin"
uuid "github.com/satori/go.uuid"
"gorm.io/gorm"
"log"
"net/http"
"strconv"
"time"
)
func Cors() gin.HandlerFunc {
@@ -120,6 +124,154 @@ func StartLocalServer() {
})
}
})
var waf_host_add_req request.WafHostAddReq
r.POST("/samwaf/wafhost/host/add", func(c *gin.Context) {
err := c.ShouldBind(&waf_host_add_req)
if err == nil {
if (!errors.Is(global.GWAF_LOCAL_DB.First(&model.Hosts{}, "host = ? and port= ?", waf_host_add_req.Host, waf_host_add_req.Port).Error, gorm.ErrRecordNotFound)) {
c.JSON(http.StatusOK, response.Response{
Code: 404,
Msg: "当前网站和端口已经存在", //可以后续考虑再次加入已存在的host的返回,前台进行编辑
})
return
}
var waf_host = &model.Hosts{
USER_CODE: global.GWAF_USER_CODE,
Tenant_id: global.GWAF_TENANT_ID,
Code: uuid.NewV4().String(),
Host: waf_host_add_req.Host,
Port: waf_host_add_req.Port,
Ssl: waf_host_add_req.Ssl,
GUARD_STATUS: 0,
REMOTE_SYSTEM: waf_host_add_req.REMOTE_SYSTEM,
REMOTE_APP: waf_host_add_req.REMOTE_APP,
Remote_host: waf_host_add_req.Remote_host,
Remote_port: waf_host_add_req.Remote_port,
REMARKS: waf_host_add_req.REMARKS,
CREATE_TIME: time.Now(),
UPDATE_TIME: time.Now(),
}
//waf_host_add_req.USER_CODE =
global.GWAF_LOCAL_DB.Debug().Create(waf_host)
c.JSON(http.StatusOK, response.Response{
Code: 200,
Data: "",
Msg: "添加成功",
})
} else {
log.Println("添加解析失败")
c.JSON(http.StatusOK, response.Response{
Code: -1,
Data: err.Error(),
Msg: "添加失败",
})
return
}
})
var waf_host_del_req request.WafHostDelReq
r.GET("/samwaf/wafhost/host/del", func(c *gin.Context) {
err := c.ShouldBind(&waf_host_del_req)
if err == nil {
var webhost model.Hosts
err = global.GWAF_LOCAL_DB.Where("CODE = ?", waf_host_del_req.CODE).First(&webhost).Error
if err != nil && errors.Is(err, gorm.ErrRecordNotFound) {
c.JSON(http.StatusOK, response.Response{
Code: -1,
Data: err.Error(),
Msg: "请检测参数",
})
return
}
if err != nil {
c.JSON(http.StatusOK, response.Response{
Code: -1,
Data: err.Error(),
Msg: "发生错误",
})
return
}
err = global.GWAF_LOCAL_DB.Where("CODE = ?", waf_host_del_req.CODE).Delete(model.Hosts{}).Error
if err != nil {
c.JSON(http.StatusOK, response.Response{
Code: -1,
Data: err.Error(),
Msg: "删除失败",
})
return
}
c.JSON(http.StatusOK, response.Response{
Code: 200,
Data: "",
Msg: "删除成功",
})
}
})
var waf_host_edit_req request.WafHostEditReq
r.POST("/samwaf/wafhost/host/edit", func(c *gin.Context) {
err := c.ShouldBind(&waf_host_edit_req)
if err == nil {
var webhost model.Hosts
global.GWAF_LOCAL_DB.Debug().Where("host = ? and port= ?", waf_host_edit_req.Host, waf_host_edit_req.Port).Find(&webhost)
if webhost.Id != 0 && webhost.Code != waf_host_edit_req.CODE {
c.JSON(http.StatusOK, response.Response{
Code: 404,
Msg: "当前网站和端口已经存在", //可以后续考虑再次加入已存在的host的返回,前台进行编辑
})
return
}
hostMap := map[string]interface{}{
"Host": waf_host_edit_req.Host,
"Port": waf_host_edit_req.Port,
"Ssl": waf_host_edit_req.Ssl,
"GUARD_STATUS": 0,
"REMOTE_SYSTEM": waf_host_edit_req.REMOTE_SYSTEM,
"REMOTE_APP": waf_host_edit_req.REMOTE_APP,
"Remote_host": waf_host_edit_req.Remote_host,
"Remote_port": waf_host_edit_req.Remote_port,
"REMARKS": waf_host_edit_req.REMARKS,
"UPDATE_TIME": time.Now(),
}
//var edit_waf_host model.Hosts
//global.GWAF_LOCAL_DB.Debug().Where("CODE=?", waf_host_edit_req.CODE).Find(edit_waf_host)
err = global.GWAF_LOCAL_DB.Debug().Model(model.Hosts{}).Where("CODE=?", waf_host_edit_req.CODE).Updates(hostMap).Error
if err != nil {
c.JSON(http.StatusOK, response.Response{
Code: 200,
Data: err.Error(),
Msg: "编辑失败",
})
} else {
c.JSON(http.StatusOK, response.Response{
Code: 200,
Data: "",
Msg: "编辑成功",
})
}
} else {
log.Println("添加解析失败")
c.JSON(http.StatusOK, response.Response{
Code: -1,
Data: err.Error(),
Msg: "编辑失败",
})
return
}
})
r.Run(":" + strconv.Itoa(global.GWAF_LOCAL_SERVER_PORT)) // listen and serve on 0.0.0.0:8080 (for windows "localhost:8080")
log.Println("本地 port:%d", global.GWAF_LOCAL_SERVER_PORT)
+222 -46
View File
@@ -55,6 +55,7 @@
<template #op="slotProps">
<a class="t-button-link" @click="handleClickDetail(slotProps)">详情</a>
<a class="t-button-link" @click="handleClickEdit(slotProps)">编辑</a>
<a class="t-button-link" @click="handleClickDelete(slotProps)">删除</a>
</template>
</t-table>
@@ -78,16 +79,27 @@
<t-radio value="1">加密证书(需上传证书)</t-radio>
</t-radio-group>
</t-form-item>
<t-form-item label="后端域名" name="remote_host">
<t-input :style="{ width: '480px' }" v-model="formData.remote_host" placeholder="请输入产品描述"></t-input>
</t-form-item>
<t-form-item label="产品类型" name="type">
<t-select v-model="formData.type" clearable :style="{ width: '480px' }">
<t-option v-for="(item, index) in options" :value="item.value" :label="item.label" :key="index">
<t-form-item label="后端系统类型" name="remote_system">
<t-select v-model="formData.remote_system" clearable :style="{ width: '480px' }">
<t-option v-for="(item, index) in remote_system_options" :value="item.value" :label="item.label" :key="index">
{{ item.label }}
</t-option>
</t-select>
</t-form-item>
<t-form-item label="后端应用类型" name="remote_app">
<t-select v-model="formData.remote_app" clearable :style="{ width: '480px' }">
<t-option v-for="(item, index) in remote_app_options" :value="item.value" :label="item.label" :key="index">
{{ item.label }}
</t-option>
</t-select>
</t-form-item>
<t-form-item label="后端域名" name="remote_host">
<t-input :style="{ width: '480px' }" v-model="formData.remote_host" placeholder="请输入后端域名"></t-input>
</t-form-item>
<t-form-item label="后端端口" name="remote_port">
<t-input-number :style="{ width: '150px' }" v-model="formData.remote_port" placeholder="请输入网站的端口一般是80/443"></t-input-number>
</t-form-item>
<t-form-item label="备注" name="mark">
<t-textarea :style="{ width: '480px' }" v-model="textareaValue" placeholder="请输入内容" name="description">
</t-textarea>
@@ -100,8 +112,58 @@
</div>
</t-dialog>
<!-- 编辑网站防御弹窗 -->
<t-dialog header="编辑网站防御" :visible.sync="editFormVisible" :width="680" :footer="false">
<div slot="body">
<!-- 表单内容 -->
<t-form :data="formEditData" ref="form" :rules="rules" @submit="onSubmitEdit" :labelWidth="100">
<t-form-item label="网站" name="host">
<t-input :style="{ width: '480px' }" v-model="formEditData.host" placeholder="请输入网站的网址"></t-input>
</t-form-item>
<t-form-item label="端口" name="port">
<t-input-number :style="{ width: '150px' }" v-model="formEditData.port" placeholder="请输入网站的端口一般是80/443"></t-input-number>
</t-form-item>
<t-form-item label="加密证书" name="ssl">
<t-radio-group v-model="formEditData.ssl">
<t-radio value="0">非加密</t-radio>
<t-radio value="1">加密证书(需上传证书)</t-radio>
</t-radio-group>
</t-form-item>
<t-form-item label="后端系统类型" name="remote_system">
<t-select v-model="formEditData.remote_system" clearable :style="{ width: '480px' }">
<t-option v-for="(item, index) in remote_system_options" :value="item.value" :label="item.label" :key="index">
{{ item.label }}
</t-option>
</t-select>
</t-form-item>
<t-form-item label="后端应用类型" name="remote_app">
<t-select v-model="formEditData.remote_app" clearable :style="{ width: '480px' }">
<t-option v-for="(item, index) in remote_app_options" :value="item.value" :label="item.label" :key="index">
{{ item.label }}
</t-option>
</t-select>
</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="后端端口" name="remote_port">
<t-input-number :style="{ width: '150px' }" v-model="formEditData.remote_port" placeholder="请输入网站的端口一般是80/443"></t-input-number>
</t-form-item>
<t-form-item label="备注" name="mark">
<t-textarea :style="{ width: '480px' }" v-model="textareaValue" placeholder="请输入内容" name="description">
</t-textarea>
</t-form-item>
<t-form-item style="float: right">
<t-button variant="outline" @click="onClickCloseEditBtn">取消</t-button>
<t-button theme="primary" type="submit">确定</t-button>
</t-form-item>
</t-form>
</div>
</t-dialog>
<t-dialog
header="确认删除当前所选合同?"
header="确认删除当前所选网站?"
:body="confirmBody"
:visible.sync="confirmVisible"
@confirm="onConfirmDelete"
@@ -120,12 +182,15 @@ import {attacklogList} from '@/apis/waflog/attacklog';
import { CONTRACT_STATUS, CONTRACT_STATUS_OPTIONS, CONTRACT_TYPES, CONTRACT_PAYMENT_TYPES } from '@/constants';
const INITIAL_DATA = {
name: '',
status: '',
description: '',
type: '',
mark: '',
amount: 0,
host: 'baidu.com',
port: 80,
remote_host: 'baidu2.com',
remote_port: 80,
ssl:0,
remote_system:"宝塔",
remote_app:"API业务系统",
guard_status: '',
remarks: '',
};
export default Vue.extend({
name: 'ListBase',
@@ -136,15 +201,22 @@ export default Vue.extend({
data() {
return {
addFormVisible:false,
editFormVisible:false,
formData: { ...INITIAL_DATA },
formEditData: { ...INITIAL_DATA },
rules: {
name: [{ required: true, message: '请输入网站名称', type: 'error' }],
host: [{ required: true, message: '请输入网站名称', type: 'error' }],
},
textareaValue: '',
options: [
{ label: '网关', value: '1' },
{ label: '人工智能', value: '2' },
{ label: 'CVM', value: '3' },
remote_system_options: [
{ label: '宝塔', value: '1' },
{ label: '小皮面板(phpstudy)', value: '2' },
{ label: 'PHPnow', value: '3' },
],
remote_app_options: [
{ label: '纯网站', value: '1' },
{ label: 'API业务系统', value: '2' },
{ label: '业务加管理', value: '3' },
],
CONTRACT_STATUS,
CONTRACT_STATUS_OPTIONS,
@@ -152,8 +224,9 @@ export default Vue.extend({
CONTRACT_PAYMENT_TYPES,
prefix,
dataLoading: false,
data: [],
selectedRowKeys: [1, 2],
data: [], //列表数据信息
detail_data:[],//加载详情信息用于编辑
selectedRowKeys: [],
value: 'first',
columns: [
{ colKey: 'row-select', type: 'multiple', width: 64, fixed: 'left' },
@@ -199,7 +272,7 @@ export default Vue.extend({
title: '操作',
},
],
rowKey: 'CODE',
rowKey: 'code',
tableLayout: 'auto',
verticalAlign: 'top',
hover: true,
@@ -218,8 +291,8 @@ export default Vue.extend({
computed: {
confirmBody() {
if (this.deleteIdx > -1) {
const { name } = this.data?.[this.deleteIdx];
return `删除后,${name}的所有合同信息将被清空,且无法恢复`;
const { host } = this.data?.[this.deleteIdx];
return `删除后,${host}的所有网站信息和规则将被清空,且无法恢复`;
}
return '';
},
@@ -285,51 +358,133 @@ export default Vue.extend({
console.log(e)
const { code } = e.row
console.log(code)
/* this.$router.push(
{name:'WafAttackLogDetail',params: {
req_uuid: req_uuid,
},
}, */
this.$router.push(
{
path:'/waf-host/wafhostdetail',
query: {
code: code,
{
path:'/waf-host/wafhostdetail',
query: {
code: code,
},
},
},
);
);
},
handleClickEdit(e) {
console.log(e)
const { code } = e.row
console.log(code)
this.editFormVisible = true
this.getDetail(code)
},
handleAddHost() {
//添加host
this.addFormVisible = true
},
onSubmit({ result, firstError }): void {
let that = this
if (!firstError) {
this.$message.success('提交成功');
this.addFormVisible = false;
let postdata = {...that.formData}
postdata['ssl'] = Number(postdata['ssl'])
this.$request
.post('/wafhost/host/add', {
...postdata
})
.then((res) => {
let resdata = res.data
console.log(resdata)
if (resdata.code === 200) {
that.$message.success(resdata.msg);
that.addFormVisible = false;
that.pagination.current = 1
that.getList("")
}else{
that.$message.warning(resdata.msg);
}
})
.catch((e: Error) => {
console.log(e);
})
.finally(() => {
});
} else {
console.log('Errors: ', result);
this.$message.warning(firstError);
that.$message.warning(firstError);
}
},
onSubmitEdit({ result, firstError }): void {
let that = this
if (!firstError) {
let postdata = {...that.formEditData}
postdata['ssl'] = Number(postdata['ssl'])
this.$request
.post('/wafhost/host/edit', {
...postdata
})
.then((res) => {
let resdata = res.data
console.log(resdata)
if (resdata.code === 200) {
that.$message.success(resdata.msg);
that.editFormVisible = false;
that.pagination.current = 1
that.getList("")
}else{
that.$message.warning(resdata.msg);
}
})
.catch((e: Error) => {
console.log(e);
})
.finally(() => {
});
} else {
console.log('Errors: ', result);
that.$message.warning(firstError);
}
},
onClickCloseBtn(): void {
this.formVisible = false;
this.formData = {};
},
handleClickDelete(row: { rowIndex: any }) {
onClickCloseEditBtn(): void {
this.editFormVisible = false;
this.formEditData = {};
},
handleClickDelete(row) {
console.log(row)
this.deleteIdx = row.rowIndex;
this.confirmVisible = true;
},
onConfirmDelete() {
// 真实业务请发起请求
this.data.splice(this.deleteIdx, 1);
this.pagination.total = this.data.length;
const selectedIdx = this.selectedRowKeys.indexOf(this.deleteIdx);
if (selectedIdx > -1) {
this.selectedRowKeys.splice(selectedIdx, 1);
}
this.confirmVisible = false;
this.$message.success('删除成功');
console.log('delete',this.data)
console.log('delete',this.data[this.deleteIdx])
let {code} = this.data[this.deleteIdx]
let that = this
this.$request
.get('/wafhost/host/del', {
params: {
CODE: code,
}
})
.then((res) => {
let resdata = res.data
console.log(resdata)
if (resdata.code === 200) {
that.pagination.current = 1
that.getList("")
that.$message.success(resdata.msg);
}else{
that.$message.warning(resdata.msg);
}
})
.catch((e: Error) => {
console.log(e);
})
.finally(() => {});
this.resetIdx();
},
onCancel() {
@@ -338,6 +493,27 @@ export default Vue.extend({
resetIdx() {
this.deleteIdx = -1;
},
getDetail(id) {
let that = this
this.$request
.get('/wafhost/host/detail', {
params: {
CODE: id,
}
})
.then((res) => {
let resdata = res.data
console.log(resdata)
if (resdata.code === 200) {
that.detail_data = resdata.data;
that.formEditData = {...that.detail_data}
}
})
.catch((e: Error) => {
console.log(e);
})
.finally(() => {});
},
},
});
</script>
+13 -13
View File
@@ -37,7 +37,6 @@ type HostSafe struct {
}
var (
user_code string
//主机情况
hostTarget = map[string]*HostSafe{}
//主机和code的关系
@@ -142,10 +141,10 @@ func (h *baseHandle) ServeHTTP(w http.ResponseWriter, r *http.Request) {
COOKIES: string(cookies),
BODY: string(bodyByte),
REQ_UUID: uuid.NewV4().String(),
USER_CODE: user_code,
USER_CODE: global.GWAF_USER_CODE,
}
global.GWAF_LOCAL_DB.Create(weblogbean)
esHelper.BatchInsert("full_log", weblogbean)
//esHelper.BatchInsert("full_log", weblogbean)
rule := &innerbean.WAF_REQUEST_FULL{
SRC_INFO: weblogbean,
ExecResult: 0,
@@ -188,14 +187,14 @@ func (h *baseHandle) ServeHTTP(w http.ResponseWriter, r *http.Request) {
proxy.ServeHTTP(w, r)
return
} else {
waflogbean := innerbean.WAFLog{
/*waflogbean := innerbean.WAFLog{
CREATE_TIME: time.Now().Format("2006-01-02 15:04:05"),
RULE: hostTarget[host].RuleData.Rulename,
ACTION: "FORBIDDEN",
REQ_UUID: uuid.NewV4().String(),
USER_CODE: user_code,
}
esHelper.BatchInsertWAF("web_log", waflogbean)
}*/
//esHelper.BatchInsertWAF("web_log", waflogbean)
w.Write([]byte("403: Host forbidden " + host))
}
}
@@ -226,9 +225,10 @@ func Start_WAF() {
}
}
user_code = config.GetString("user_code") // 读取配置
global.GWAF_USER_CODE = config.GetString("user_code") // 读取配置
global.GWAF_TENANT_ID = global.GWAF_USER_CODE
global.GWAF_LOCAL_SERVER_PORT = config.GetInt("local_port") //读取本地端口
fmt.Println(" load ini: ", user_code)
fmt.Println(" load ini: ", global.GWAF_USER_CODE)
//数据库连接
newLogger := logger.New(
@@ -254,13 +254,13 @@ func Start_WAF() {
var base_config model.Base_config
db.Debug().Where("name = ?", "esurl").Find(&base_config)
esHelper.Init(base_config.Value)
//esHelper.Init(base_config.Value) todo 暂时屏蔽es
fmt.Printf("current_version %s \r\n", global.Version_name)
var hosts []model.Hosts
db.Where("user_code = ?", user_code).Find(&hosts)
db.Where("user_code = ?", global.GWAF_USER_CODE).Find(&hosts)
//初始化步骤[加载ip数据库]
var dbPath = "data/ip2region.xdb"
@@ -280,7 +280,7 @@ func Start_WAF() {
if hosts[i].Ssl == 1 {
//查询ssl证书
var sslconfig model.Sslconfig
db.Debug().Where("code = ? and user_code=? ", hosts[i].Code, user_code).Find(&sslconfig)
db.Debug().Where("code = ? and user_code=? ", hosts[i].Code, global.GWAF_USER_CODE).Find(&sslconfig)
// 第一个域名:example.com
cert, err := tls.LoadX509KeyPair(sslconfig.Certfile, sslconfig.Keyfile)
if err != nil {
@@ -311,7 +311,7 @@ func Start_WAF() {
//查询规则
var ruleconfig model.Rules
db.Debug().Where("code = ? and user_code=? ", hosts[i].Code, user_code).Find(&ruleconfig)
db.Debug().Where("code = ? and user_code=? ", hosts[i].Code, global.GWAF_USER_CODE).Find(&ruleconfig)
ruleHelper.LoadRule(ruleconfig)
hostsafe := &HostSafe{
@@ -393,7 +393,7 @@ func Start_WAF() {
for code, host := range hostCode {
//hostTarget[host].Rule
var ruleconfig model.Rules
db.Debug().Where("code = ? and user_code=? ", code, user_code).Find(&ruleconfig)
db.Debug().Where("code = ? and user_code=? ", code, global.GWAF_USER_CODE).Find(&ruleconfig)
if ruleconfig.Ruleversion > hostTarget[host].RuleData.Ruleversion {
//说明该code有更新
hostRuleChan <- ruleconfig
+12
View File
@@ -0,0 +1,12 @@
package request
type WafHostAddReq struct {
Host string `json:"host"` //域名
Port int `json:"port"` //端口
Ssl int `json:"ssl"` //是否是ssl
REMOTE_SYSTEM string `json:"remote_system"` //是宝塔 phpstudy等
REMOTE_APP string `json:"remote_app"` //是什么类型的应用
Remote_host string `json:"remote_host"` //远端域名
Remote_port int `json:"remote_port"` //远端端口
REMARKS string `json:"remarks"` //备注
}
+5
View File
@@ -0,0 +1,5 @@
package request
type WafHostDelReq struct {
CODE string `json:"code"`
}
+13
View File
@@ -0,0 +1,13 @@
package request
type WafHostEditReq struct {
CODE string `json:"code"`
Host string `json:"host"` //域名
Port int `json:"port"` //端口
Ssl int `json:"ssl"` //是否是ssl
REMOTE_SYSTEM string `json:"remote_system"` //是宝塔 phpstudy等
REMOTE_APP string `json:"remote_app"` //是什么类型的应用
Remote_host string `json:"remote_host"` //远端域名
Remote_port int `json:"remote_port"` //远端端口
REMARKS string `json:"remarks"` //备注
}