mirror of
https://gitee.com/samwaf/SamWaf.git
synced 2026-10-31 02:01:44 +08:00
feat: add tunnel protection
#IATZU4
This commit is contained in:
@@ -39,6 +39,7 @@ type APIGroup struct {
|
||||
WafAnalysisApi
|
||||
WafPrivateInfoApi
|
||||
WafCacheRuleApi
|
||||
WafTunnelApi
|
||||
}
|
||||
|
||||
var APIGroupAPP = new(APIGroup)
|
||||
@@ -94,4 +95,5 @@ var (
|
||||
wafPrivateInfoService = waf_service.WafPrivateInfoServiceApp
|
||||
|
||||
wafCacheRuleService = waf_service.WafCacheRuleServiceApp
|
||||
wafTunnelService = waf_service.WafTunnelServiceApp
|
||||
)
|
||||
|
||||
@@ -0,0 +1,260 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"SamWaf/enums"
|
||||
"SamWaf/global"
|
||||
"SamWaf/globalobj"
|
||||
"SamWaf/innerbean"
|
||||
"SamWaf/model/common/response"
|
||||
"SamWaf/model/request"
|
||||
"SamWaf/model/spec"
|
||||
"SamWaf/model/waftunnelmodel"
|
||||
"SamWaf/utils"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/gin-gonic/gin"
|
||||
"gorm.io/gorm"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type WafTunnelApi struct {
|
||||
}
|
||||
|
||||
func (w *WafTunnelApi) AddApi(c *gin.Context) {
|
||||
var req request.WafTunnelAddReq
|
||||
err := c.ShouldBindJSON(&req)
|
||||
if err == nil {
|
||||
|
||||
portStrArray := strings.Split(req.Port, ",")
|
||||
protocol := strings.ToLower(req.Protocol)
|
||||
|
||||
for _, portItem := range portStrArray {
|
||||
portItem = strings.TrimSpace(portItem)
|
||||
if portItem == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
port, err := strconv.Atoi(portItem)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// 根据协议类型检测端口
|
||||
var isUsed bool
|
||||
switch protocol {
|
||||
case "tcp":
|
||||
isUsed = utils.TCPPortCheck(port) // TCP端口检测
|
||||
case "udp":
|
||||
isUsed = utils.UDPPortCheck(port) // UDP端口检测
|
||||
default:
|
||||
response.FailWithMessage("协议不支持", c)
|
||||
return
|
||||
}
|
||||
|
||||
if isUsed {
|
||||
global.GQEQUE_MESSAGE_DB.Enqueue(innerbean.OpResultMessageInfo{
|
||||
BaseMessageInfo: innerbean.BaseMessageInfo{OperaType: "端口告警", Server: global.GWAF_CUSTOM_SERVER_NAME},
|
||||
Msg: fmt.Sprintf("%s端口 %d 已被占用", strings.ToUpper(protocol), port),
|
||||
Success: "true",
|
||||
})
|
||||
response.FailWithMessage(fmt.Sprintf("%s端口 %d 已被占用", strings.ToUpper(protocol), port), c)
|
||||
return
|
||||
}
|
||||
}
|
||||
cnt := wafTunnelService.CheckIsExistApi(req)
|
||||
if cnt == 0 {
|
||||
tunnel, err := wafTunnelService.AddApi(req)
|
||||
|
||||
if err == nil {
|
||||
//发送新增通知信息
|
||||
var chanInfo = spec.ChanCommon{
|
||||
Type: enums.ChanComTypeTunnel,
|
||||
OpType: enums.OP_TYPE_NEW,
|
||||
Content: wafTunnelService.GetDetailByIdApi(tunnel.Id),
|
||||
}
|
||||
global.GWAF_CHAN_COMMON_MSG <- chanInfo
|
||||
response.OkWithMessage("添加成功", c)
|
||||
} else {
|
||||
response.FailWithMessage("添加失败", c)
|
||||
}
|
||||
return
|
||||
} else {
|
||||
response.FailWithMessage("当前记录已经存在", c)
|
||||
return
|
||||
}
|
||||
|
||||
} else {
|
||||
response.FailWithMessage("解析失败", c)
|
||||
}
|
||||
}
|
||||
|
||||
func (w *WafTunnelApi) GetDetailApi(c *gin.Context) {
|
||||
var req request.WafTunnelDetailReq
|
||||
err := c.ShouldBind(&req)
|
||||
if err == nil {
|
||||
bean := wafTunnelService.GetDetailApi(req)
|
||||
response.OkWithDetailed(bean, "获取成功", c)
|
||||
} else {
|
||||
response.FailWithMessage("解析失败", c)
|
||||
}
|
||||
}
|
||||
|
||||
func (w *WafTunnelApi) GetListApi(c *gin.Context) {
|
||||
var req request.WafTunnelSearchReq
|
||||
err := c.ShouldBindJSON(&req)
|
||||
if err == nil {
|
||||
Tunnel, total, _ := wafTunnelService.GetListApi(req)
|
||||
response.OkWithDetailed(response.PageResult{
|
||||
List: Tunnel,
|
||||
Total: total,
|
||||
PageIndex: req.PageIndex,
|
||||
PageSize: req.PageSize,
|
||||
}, "获取成功", c)
|
||||
} else {
|
||||
response.FailWithMessage("解析失败", c)
|
||||
}
|
||||
}
|
||||
|
||||
func (w *WafTunnelApi) DelApi(c *gin.Context) {
|
||||
var req request.WafTunnelDelReq
|
||||
err := c.ShouldBind(&req)
|
||||
if err == nil {
|
||||
tunnel := wafTunnelService.GetDetailByIdApi(req.Id)
|
||||
err = wafTunnelService.DelApi(req)
|
||||
if err != nil && errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
response.FailWithMessage("请检测参数", c)
|
||||
} else if err != nil {
|
||||
response.FailWithMessage("发生错误", c)
|
||||
} else {
|
||||
//发送删除通知信息
|
||||
var chanInfo = spec.ChanCommon{
|
||||
Type: enums.ChanComTypeTunnel,
|
||||
OpType: enums.OP_TYPE_DELETE,
|
||||
OldContent: tunnel,
|
||||
}
|
||||
global.GWAF_CHAN_COMMON_MSG <- chanInfo
|
||||
response.OkWithMessage("删除成功", c)
|
||||
}
|
||||
} else {
|
||||
response.FailWithMessage("解析失败", c)
|
||||
}
|
||||
}
|
||||
|
||||
func (w *WafTunnelApi) ModifyApi(c *gin.Context) {
|
||||
var req request.WafTunnelEditReq
|
||||
err := c.ShouldBindJSON(&req)
|
||||
if err == nil {
|
||||
oldTunnel := wafTunnelService.GetDetailByIdApi(req.Id)
|
||||
err = wafTunnelService.ModifyApi(req)
|
||||
if err != nil {
|
||||
response.FailWithMessage("编辑发生错误"+err.Error(), c)
|
||||
} else {
|
||||
newTunnel := wafTunnelService.GetDetailByIdApi(req.Id)
|
||||
|
||||
//发送修改通知信息
|
||||
var chanInfo = spec.ChanCommon{
|
||||
Type: enums.ChanComTypeTunnel,
|
||||
OpType: enums.OP_TYPE_UPDATE,
|
||||
Content: newTunnel,
|
||||
OldContent: oldTunnel,
|
||||
}
|
||||
global.GWAF_CHAN_COMMON_MSG <- chanInfo
|
||||
response.OkWithMessage("编辑成功", c)
|
||||
}
|
||||
} else {
|
||||
response.FailWithMessage("解析失败", c)
|
||||
}
|
||||
}
|
||||
|
||||
// GetTunnelConnectionsApi 获取隧道连接信息API
|
||||
func (w *WafTunnelApi) GetTunnelConnectionsApi(c *gin.Context) {
|
||||
var req request.WafTunnelConnReq
|
||||
err := c.ShouldBind(&req)
|
||||
if err == nil {
|
||||
tunnel, err := wafTunnelService.GetTunnelConnections(req.ID)
|
||||
if err != nil {
|
||||
response.FailWithMessage("获取隧道连接信息失败: "+err.Error(), c)
|
||||
return
|
||||
}
|
||||
// 获取端口列表
|
||||
portStrArray := strings.Split(tunnel.Port, ",")
|
||||
portInfoList := make([]map[string]interface{}, 0)
|
||||
|
||||
for _, portStr := range portStrArray {
|
||||
portStr = strings.TrimSpace(portStr)
|
||||
if portStr == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
port, err := strconv.Atoi(portStr)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// 从全局变量中获取连接信息
|
||||
tcpSourceCount := 0
|
||||
tcpTargetCount := 0
|
||||
udpSourceCount := 0
|
||||
udpTargetCount := 0
|
||||
|
||||
// TCP来源连接IP列表
|
||||
tcpSourceIPs := []map[string]string{}
|
||||
// UDP来源连接IP列表
|
||||
udpSourceIPs := []map[string]string{}
|
||||
|
||||
// 获取TCP连接数和IP列表
|
||||
if globalobj.GWAF_RUNTIME_OBJ_TUNNEL_ENGINE != nil {
|
||||
tcpSourceCount = globalobj.GWAF_RUNTIME_OBJ_TUNNEL_ENGINE.TCPConnections.GetPortConnsCountByType(port, waftunnelmodel.ConnTypeSource)
|
||||
tcpTargetCount = globalobj.GWAF_RUNTIME_OBJ_TUNNEL_ENGINE.TCPConnections.GetPortConnsCountByType(port, waftunnelmodel.ConnTypeTarget)
|
||||
udpSourceCount = globalobj.GWAF_RUNTIME_OBJ_TUNNEL_ENGINE.UDPConnections.GetPortConnsCountByType(port, waftunnelmodel.ConnTypeSource)
|
||||
udpTargetCount = globalobj.GWAF_RUNTIME_OBJ_TUNNEL_ENGINE.UDPConnections.GetPortConnsCountByType(port, waftunnelmodel.ConnTypeTarget)
|
||||
|
||||
// 获取TCP来源连接IP列表
|
||||
tcpSourceIPList := globalobj.GWAF_RUNTIME_OBJ_TUNNEL_ENGINE.TCPConnections.GetPortConnsInfo(port, waftunnelmodel.ConnTypeSource)
|
||||
for _, ip := range tcpSourceIPList {
|
||||
// 获取IP归属地区
|
||||
region := utils.GetCountry(ip)
|
||||
tcpSourceIPs = append(tcpSourceIPs, map[string]string{
|
||||
"ip": ip,
|
||||
"region": fmt.Sprintf("%v", region),
|
||||
})
|
||||
}
|
||||
|
||||
// 获取UDP来源连接IP列表
|
||||
udpSourceIPList := globalobj.GWAF_RUNTIME_OBJ_TUNNEL_ENGINE.UDPConnections.GetPortConnsInfo(port, waftunnelmodel.ConnTypeSource)
|
||||
for _, ip := range udpSourceIPList {
|
||||
// 获取IP归属地区
|
||||
region := utils.GetCountry(ip)
|
||||
udpSourceIPs = append(udpSourceIPs, map[string]string{
|
||||
"ip": ip,
|
||||
"region": fmt.Sprintf("%v", region),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
portInfo := map[string]interface{}{
|
||||
"port": port,
|
||||
"tcp_source_count": tcpSourceCount,
|
||||
"tcp_target_count": tcpTargetCount,
|
||||
"udp_source_count": udpSourceCount,
|
||||
"udp_target_count": udpTargetCount,
|
||||
"tcp_source_ips": tcpSourceIPs,
|
||||
"udp_source_ips": udpSourceIPs,
|
||||
}
|
||||
|
||||
portInfoList = append(portInfoList, portInfo)
|
||||
}
|
||||
|
||||
// 获取连接的IP列表
|
||||
connInfo := map[string]interface{}{
|
||||
"tunnel_info": tunnel,
|
||||
"port_info": portInfoList,
|
||||
"protocol": tunnel.Protocol,
|
||||
}
|
||||
|
||||
response.OkWithDetailed(connInfo, "获取成功", c)
|
||||
} else {
|
||||
response.FailWithMessage("解析失败", c)
|
||||
}
|
||||
}
|
||||
@@ -9,8 +9,7 @@ import (
|
||||
func TestCodeGeneration(t *testing.T) {
|
||||
// 唯一校验定义字段信息的字符串
|
||||
fieldDefs := []string{
|
||||
"HostCode:host_code:string",
|
||||
"RuleName:rule_name:string",
|
||||
"Name:name:string",
|
||||
}
|
||||
|
||||
// 构造 `uniFields` 列表
|
||||
@@ -28,6 +27,6 @@ func TestCodeGeneration(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
fields := GetStructFields(model.CacheRule{})
|
||||
CodeGeneration("CacheRule", fields, uniFields)
|
||||
fields := GetStructFields(model.Tunnel{})
|
||||
CodeGeneration("Tunnel", fields, uniFields)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
package enums
|
||||
|
||||
const (
|
||||
//隧道
|
||||
ChanComTypeTunnel = iota
|
||||
)
|
||||
@@ -0,0 +1,7 @@
|
||||
package enums
|
||||
|
||||
const (
|
||||
OP_TYPE_NEW = iota
|
||||
OP_TYPE_UPDATE
|
||||
OP_TYPE_DELETE
|
||||
)
|
||||
@@ -43,6 +43,7 @@ var (
|
||||
|
||||
GWAF_RUNTIME_CURRENT_EXEPATH string = "" //当前程序运行路径
|
||||
GWAF_RUNTIME_CURRENT_WEBPORT string = "" //当前程序所占用端口
|
||||
GWAF_RUNTIME_CURRENT_TUNNELPORT string = "" //当前程序所占用隧道端口
|
||||
GWAF_RUNTIME_CURRENT_EXPORT_DB_LOG_FILE_PATH string = "" //生成的日志导出文件路径
|
||||
|
||||
GWAF_RUNTIME_SSL_EXPIRE_CHECK bool = false //SSL过期检测是否正在运行
|
||||
@@ -81,6 +82,7 @@ var (
|
||||
GWAF_CHAN_HOST = make(chan model.Hosts, 10) //主机链
|
||||
GWAF_CHAN_ENGINE = make(chan int, 10) //引擎链
|
||||
GWAF_CHAN_MSG = make(chan spec.ChanCommonHost, 10) //全局通讯包
|
||||
GWAF_CHAN_COMMON_MSG = make(chan spec.ChanCommon, 10) //全局共用通讯包
|
||||
GWAF_CHAN_UPDATE = make(chan int, 10) //升级后处理链
|
||||
GWAF_CHAN_SENSITIVE = make(chan int, 10) //敏感词处理链
|
||||
GWAF_CHAN_SSL = make(chan string, 10) //证书处理链
|
||||
|
||||
@@ -3,13 +3,15 @@ package globalobj
|
||||
import (
|
||||
"SamWaf/wafenginecore"
|
||||
"SamWaf/waftask"
|
||||
"SamWaf/waftunnelengine"
|
||||
)
|
||||
|
||||
var (
|
||||
/***
|
||||
本地对象映射关系
|
||||
*/
|
||||
GWAF_RUNTIME_OBJ_WAF_ENGINE *wafenginecore.WafEngine //当前引擎对象
|
||||
GWAF_RUNTIME_OBJ_WAF_TaskRegistry *waftask.TaskRegistry // 任务执行器
|
||||
GWAF_RUNTIME_OBJ_WAF_TaskScheduler *waftask.TaskScheduler // 任务计划
|
||||
GWAF_RUNTIME_OBJ_WAF_ENGINE *wafenginecore.WafEngine //当前引擎对象
|
||||
GWAF_RUNTIME_OBJ_TUNNEL_ENGINE *waftunnelengine.WafTunnelEngine //当前tunnel引擎对象
|
||||
GWAF_RUNTIME_OBJ_WAF_TaskRegistry *waftask.TaskRegistry // 任务执行器
|
||||
GWAF_RUNTIME_OBJ_WAF_TaskScheduler *waftask.TaskScheduler // 任务计划
|
||||
)
|
||||
|
||||
@@ -25,6 +25,7 @@ import (
|
||||
"SamWaf/wafsnowflake"
|
||||
"SamWaf/wafssl"
|
||||
"SamWaf/waftask"
|
||||
"SamWaf/waftunnelengine"
|
||||
"SamWaf/webplugin"
|
||||
"crypto/tls"
|
||||
"embed"
|
||||
@@ -287,6 +288,9 @@ func (m *wafSystenService) run() {
|
||||
http.Handle("/", globalobj.GWAF_RUNTIME_OBJ_WAF_ENGINE)
|
||||
globalobj.GWAF_RUNTIME_OBJ_WAF_ENGINE.StartWaf()
|
||||
|
||||
//启动隧道
|
||||
globalobj.GWAF_RUNTIME_OBJ_TUNNEL_ENGINE = waftunnelengine.NewWafTunnelEngine()
|
||||
globalobj.GWAF_RUNTIME_OBJ_TUNNEL_ENGINE.StartTunnel()
|
||||
//启动管理界面
|
||||
webmanager = &wafmangeweb.WafWebManager{LogName: "WebManager"}
|
||||
go func() {
|
||||
@@ -538,6 +542,33 @@ func (m *wafSystenService) run() {
|
||||
}
|
||||
}
|
||||
break
|
||||
case common := <-global.GWAF_CHAN_COMMON_MSG:
|
||||
if common.Type == enums.ChanComTypeTunnel {
|
||||
//隧道类型
|
||||
switch common.OpType {
|
||||
case enums.OP_TYPE_NEW:
|
||||
tunnelNew := common.Content.(model.Tunnel)
|
||||
netRunTimes := globalobj.GWAF_RUNTIME_OBJ_TUNNEL_ENGINE.LoadTunnel(tunnelNew)
|
||||
for _, netRunTime := range netRunTimes {
|
||||
globalobj.GWAF_RUNTIME_OBJ_TUNNEL_ENGINE.StartTunnelServer(netRunTime)
|
||||
}
|
||||
break
|
||||
case enums.OP_TYPE_UPDATE:
|
||||
// 获取修改前的隧道信息
|
||||
tunnelOld := common.OldContent.(model.Tunnel)
|
||||
// 获取修改后的隧道信息
|
||||
tunnelNew := common.Content.(model.Tunnel)
|
||||
netRunTimes := globalobj.GWAF_RUNTIME_OBJ_TUNNEL_ENGINE.EditTunnel(tunnelOld, tunnelNew)
|
||||
for _, netRunTime := range netRunTimes {
|
||||
globalobj.GWAF_RUNTIME_OBJ_TUNNEL_ENGINE.StartTunnelServer(netRunTime)
|
||||
}
|
||||
break
|
||||
case enums.OP_TYPE_DELETE:
|
||||
tunnelDelete := common.OldContent.(model.Tunnel)
|
||||
globalobj.GWAF_RUNTIME_OBJ_TUNNEL_ENGINE.RemoveTunnel(tunnelDelete)
|
||||
break
|
||||
}
|
||||
}
|
||||
case engineStatus := <-global.GWAF_CHAN_ENGINE:
|
||||
if engineStatus == 1 {
|
||||
zlog.Info("准备关闭WAF引擎")
|
||||
@@ -545,6 +576,11 @@ func (m *wafSystenService) run() {
|
||||
zlog.Info("准备启动WAF引擎")
|
||||
globalobj.GWAF_RUNTIME_OBJ_WAF_ENGINE.StartWaf()
|
||||
|
||||
zlog.Info("准备关闭隧道引擎")
|
||||
globalobj.GWAF_RUNTIME_OBJ_TUNNEL_ENGINE.CloseTunnel()
|
||||
zlog.Info("准备启动隧道引擎")
|
||||
globalobj.GWAF_RUNTIME_OBJ_TUNNEL_ENGINE.StartTunnel()
|
||||
|
||||
}
|
||||
break
|
||||
case host := <-global.GWAF_CHAN_HOST:
|
||||
@@ -630,6 +666,10 @@ func (m *wafSystenService) stopSamWaf() {
|
||||
globalobj.GWAF_RUNTIME_OBJ_WAF_ENGINE.CloseWaf()
|
||||
zlog.Info("Shutdown SamWaf Engine finished")
|
||||
|
||||
zlog.Info("Shutdown SamWaf Tunnel Engine...")
|
||||
globalobj.GWAF_RUNTIME_OBJ_TUNNEL_ENGINE.CloseTunnel()
|
||||
zlog.Info("Shutdown SamWaf Tunnel Engine finished")
|
||||
|
||||
zlog.Info("Shutdown SamWaf Queue Processors...")
|
||||
// 关闭信号通道,通知所有队列处理协程退出
|
||||
close(global.GWAF_QUEUE_SHUTDOWN_SIGNAL)
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
package request
|
||||
|
||||
import "SamWaf/model/common/request"
|
||||
|
||||
type WafTunnelAddReq struct {
|
||||
Code string `json:"code" form:"code"`
|
||||
Name string `json:"name" form:"name"`
|
||||
Port string `json:"port" form:"port"`
|
||||
Protocol string `json:"protocol" form:"protocol"`
|
||||
RemotePort int `json:"remote_port" form:"remote_port"`
|
||||
RemoteIp string `json:"remote_ip" form:"remote_ip"`
|
||||
AllowIp string `json:"allow_ip" form:"allow_ip"`
|
||||
DenyIp string `json:"deny_ip" form:"deny_ip"`
|
||||
StartStatus int `json:"start_status" form:"start_status"`
|
||||
ConnTimeout int `json:"conn_timeout" form:"conn_timeout"`
|
||||
ReadTimeout int `json:"read_timeout" form:"read_timeout"`
|
||||
WriteTimeout int `json:"write_timeout" form:"write_timeout"`
|
||||
MaxInConnect int `json:"max_in_connect" form:"max_in_connect"`
|
||||
MaxOutConnect int `json:"max_out_connect" form:"max_out_connect"`
|
||||
Remark string `json:"remark" form:"remark"`
|
||||
}
|
||||
type WafTunnelEditReq struct {
|
||||
Id string `json:"id"`
|
||||
|
||||
Code string `json:"code" form:"code"`
|
||||
Name string `json:"name" form:"name"`
|
||||
Port string `json:"port" form:"port"`
|
||||
Protocol string `json:"protocol" form:"protocol"`
|
||||
RemotePort int `json:"remote_port" form:"remote_port"`
|
||||
RemoteIp string `json:"remote_ip" form:"remote_ip"`
|
||||
AllowIp string `json:"allow_ip" form:"allow_ip"`
|
||||
DenyIp string `json:"deny_ip" form:"deny_ip"`
|
||||
StartStatus int `json:"start_status" form:"start_status"`
|
||||
ConnTimeout int `json:"conn_timeout" form:"conn_timeout"`
|
||||
ReadTimeout int `json:"read_timeout" form:"read_timeout"`
|
||||
WriteTimeout int `json:"write_timeout" form:"write_timeout"`
|
||||
MaxInConnect int `json:"max_in_connect" form:"max_in_connect"`
|
||||
MaxOutConnect int `json:"max_out_connect" form:"max_out_connect"`
|
||||
Remark string `json:"remark" form:"remark"`
|
||||
}
|
||||
type WafTunnelDetailReq struct {
|
||||
Id string `json:"id" form:"id"`
|
||||
}
|
||||
type WafTunnelDelReq struct {
|
||||
Id string `json:"id" form:"id"`
|
||||
}
|
||||
type WafTunnelSearchReq struct {
|
||||
request.PageInfo
|
||||
}
|
||||
|
||||
// WafTunnelConnReq 通过ID获取隧道连接请求
|
||||
type WafTunnelConnReq struct {
|
||||
ID string `json:"id" form:"id"`
|
||||
}
|
||||
@@ -6,3 +6,11 @@ type ChanCommonHost struct {
|
||||
Content interface{}
|
||||
OldContent interface{}
|
||||
}
|
||||
|
||||
// ChanCommon 通用通道
|
||||
type ChanCommon struct {
|
||||
Type int //某种类型 1 比如代表隧道
|
||||
OpType int //操作类型 1代表新增 2代表删除 3代表修改
|
||||
Content interface{} //新增的内容
|
||||
OldContent interface{} //修改前的内容
|
||||
}
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"SamWaf/model/baseorm"
|
||||
)
|
||||
|
||||
// Tunnel 隧道
|
||||
type Tunnel struct {
|
||||
baseorm.BaseOrm
|
||||
Code string `json:"code"` //唯一码
|
||||
Name string `json:"name"` //名称
|
||||
Port string `json:"port"` //端口可以多个,号隔开
|
||||
Protocol string `json:"protocol"` //协议 tcp,udp
|
||||
RemotePort int `json:"remote_port"` //远端端口
|
||||
RemoteIp string `json:"remote_ip"` //远端指定IP
|
||||
AllowIp string `json:"allow_ip"` //允许访问的IP ,号隔开
|
||||
DenyIp string `json:"deny_ip"` //拒绝访问的IP ,号隔开
|
||||
StartStatus int `json:"start_status"` //启动状态 如果是1 启动 ; 如果是0 不启动
|
||||
ConnTimeout int `json:"conn_timeout"` // 连接超时时间,单位秒 60s
|
||||
ReadTimeout int `json:"read_timeout"` // 读取超时时间,单位秒 0 表示不限制
|
||||
WriteTimeout int `json:"write_timeout"` // 写入超时时间,单位秒 0 表示不限制
|
||||
MaxInConnect int `json:"max_in_connect"` // 最大入站连接数 0 表示不限制
|
||||
MaxOutConnect int `json:"max_out_connect"` // 最大出站连接数 0 表示不限制
|
||||
Remark string `json:"remark"` //备注
|
||||
}
|
||||
@@ -0,0 +1,313 @@
|
||||
package waftunnelmodel
|
||||
|
||||
import (
|
||||
"net"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// 连接类型常量
|
||||
const (
|
||||
ConnTypeSource = iota // 来源连接(客户端连接)
|
||||
ConnTypeTarget // 目标连接(服务器连接)
|
||||
)
|
||||
|
||||
// ConnInfo 连接信息结构体
|
||||
type ConnInfo struct {
|
||||
ConnType int // 连接类型:0-来源连接,1-目标连接
|
||||
CreateTime time.Time // 连接创建时间
|
||||
}
|
||||
|
||||
// SafeTCPConnMap 线程安全的TCP连接管理Map
|
||||
type SafeTCPConnMap struct {
|
||||
mu sync.RWMutex
|
||||
items map[int]map[net.Conn]ConnInfo // 端口号 -> 连接 -> 连接信息
|
||||
}
|
||||
|
||||
// NewSafeTCPConnMap 创建新的TCP连接安全Map
|
||||
func NewSafeTCPConnMap() *SafeTCPConnMap {
|
||||
return &SafeTCPConnMap{
|
||||
items: make(map[int]map[net.Conn]ConnInfo),
|
||||
}
|
||||
}
|
||||
|
||||
// AddConn 添加连接
|
||||
func (m *SafeTCPConnMap) AddConn(port int, conn net.Conn, connType int) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
|
||||
if _, exists := m.items[port]; !exists {
|
||||
m.items[port] = make(map[net.Conn]ConnInfo)
|
||||
}
|
||||
|
||||
m.items[port][conn] = ConnInfo{
|
||||
ConnType: connType,
|
||||
CreateTime: time.Now(),
|
||||
}
|
||||
}
|
||||
|
||||
// RemoveConn 移除连接
|
||||
func (m *SafeTCPConnMap) RemoveConn(port int, conn net.Conn) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
|
||||
if conns, exists := m.items[port]; exists {
|
||||
delete(conns, conn)
|
||||
}
|
||||
}
|
||||
|
||||
// ClosePortConns 关闭指定端口的所有连接
|
||||
func (m *SafeTCPConnMap) ClosePortConns(port int) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
|
||||
if conns, exists := m.items[port]; exists {
|
||||
for conn := range conns {
|
||||
conn.Close()
|
||||
}
|
||||
delete(m.items, port)
|
||||
}
|
||||
}
|
||||
|
||||
// CloseAllConns 关闭所有连接
|
||||
func (m *SafeTCPConnMap) CloseAllConns() {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
|
||||
for _, conns := range m.items {
|
||||
for conn := range conns {
|
||||
conn.Close()
|
||||
}
|
||||
}
|
||||
|
||||
// 清空map
|
||||
m.items = make(map[int]map[net.Conn]ConnInfo)
|
||||
}
|
||||
|
||||
// GetPortConnsCount 获取指定端口的连接数
|
||||
func (m *SafeTCPConnMap) GetPortConnsCount(port int) int {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
|
||||
if conns, exists := m.items[port]; exists {
|
||||
return len(conns)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// GetPortConnsCountByType 获取指定端口指定类型的连接数
|
||||
func (m *SafeTCPConnMap) GetPortConnsCountByType(port int, connType int) int {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
|
||||
count := 0
|
||||
if conns, exists := m.items[port]; exists {
|
||||
for _, info := range conns {
|
||||
if info.ConnType == connType {
|
||||
count++
|
||||
}
|
||||
}
|
||||
}
|
||||
return count
|
||||
}
|
||||
|
||||
// GetAllConnsCount 获取所有连接数
|
||||
func (m *SafeTCPConnMap) GetAllConnsCount() int {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
|
||||
total := 0
|
||||
for _, conns := range m.items {
|
||||
total += len(conns)
|
||||
}
|
||||
return total
|
||||
}
|
||||
|
||||
// GetAllConnsCountByType 获取所有指定类型的连接数
|
||||
func (m *SafeTCPConnMap) GetAllConnsCountByType(connType int) int {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
|
||||
total := 0
|
||||
for _, conns := range m.items {
|
||||
for _, info := range conns {
|
||||
if info.ConnType == connType {
|
||||
total++
|
||||
}
|
||||
}
|
||||
}
|
||||
return total
|
||||
}
|
||||
|
||||
// SafeUDPConnMap 线程安全的UDP连接管理Map
|
||||
type SafeUDPConnMap struct {
|
||||
mu sync.RWMutex
|
||||
items map[int]map[*net.UDPConn]ConnInfo // 端口号 -> 连接 -> 连接信息
|
||||
}
|
||||
|
||||
// NewSafeUDPConnMap 创建新的UDP连接安全Map
|
||||
func NewSafeUDPConnMap() *SafeUDPConnMap {
|
||||
return &SafeUDPConnMap{
|
||||
items: make(map[int]map[*net.UDPConn]ConnInfo),
|
||||
}
|
||||
}
|
||||
|
||||
// AddConn 添加连接
|
||||
func (m *SafeUDPConnMap) AddConn(port int, conn *net.UDPConn, connType int) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
|
||||
if _, exists := m.items[port]; !exists {
|
||||
m.items[port] = make(map[*net.UDPConn]ConnInfo)
|
||||
}
|
||||
|
||||
m.items[port][conn] = ConnInfo{
|
||||
ConnType: connType,
|
||||
CreateTime: time.Now(),
|
||||
}
|
||||
}
|
||||
|
||||
// RemoveConn 移除连接
|
||||
func (m *SafeUDPConnMap) RemoveConn(port int, conn *net.UDPConn) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
|
||||
if conns, exists := m.items[port]; exists {
|
||||
delete(conns, conn)
|
||||
}
|
||||
}
|
||||
|
||||
// ClosePortConns 关闭指定端口的所有连接
|
||||
func (m *SafeUDPConnMap) ClosePortConns(port int) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
|
||||
if conns, exists := m.items[port]; exists {
|
||||
for conn := range conns {
|
||||
conn.Close()
|
||||
}
|
||||
delete(m.items, port)
|
||||
}
|
||||
}
|
||||
|
||||
// CloseAllConns 关闭所有连接
|
||||
func (m *SafeUDPConnMap) CloseAllConns() {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
|
||||
for _, conns := range m.items {
|
||||
for conn := range conns {
|
||||
conn.Close()
|
||||
}
|
||||
}
|
||||
|
||||
// 清空map
|
||||
m.items = make(map[int]map[*net.UDPConn]ConnInfo)
|
||||
}
|
||||
|
||||
// GetPortConnsCount 获取指定端口的连接数
|
||||
func (m *SafeUDPConnMap) GetPortConnsCount(port int) int {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
|
||||
if conns, exists := m.items[port]; exists {
|
||||
return len(conns)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// GetPortConnsCountByType 获取指定端口指定类型的连接数
|
||||
func (m *SafeUDPConnMap) GetPortConnsCountByType(port int, connType int) int {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
|
||||
count := 0
|
||||
if conns, exists := m.items[port]; exists {
|
||||
for _, info := range conns {
|
||||
if info.ConnType == connType {
|
||||
count++
|
||||
}
|
||||
}
|
||||
}
|
||||
return count
|
||||
}
|
||||
|
||||
// GetAllConnsCount 获取所有连接数
|
||||
func (m *SafeUDPConnMap) GetAllConnsCount() int {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
|
||||
total := 0
|
||||
for _, conns := range m.items {
|
||||
total += len(conns)
|
||||
}
|
||||
return total
|
||||
}
|
||||
|
||||
// GetAllConnsCountByType 获取所有指定类型的连接数
|
||||
func (m *SafeUDPConnMap) GetAllConnsCountByType(connType int) int {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
|
||||
total := 0
|
||||
for _, conns := range m.items {
|
||||
for _, info := range conns {
|
||||
if info.ConnType == connType {
|
||||
total++
|
||||
}
|
||||
}
|
||||
}
|
||||
return total
|
||||
}
|
||||
|
||||
// GetPortConnsInfo 获取指定端口的连接信息
|
||||
func (m *SafeTCPConnMap) GetPortConnsInfo(port int, connType int) []string {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
|
||||
ipList := make([]string, 0)
|
||||
if conns, exists := m.items[port]; exists {
|
||||
for conn, info := range conns {
|
||||
if info.ConnType == connType {
|
||||
// 获取连接的远程地址
|
||||
if remoteAddr := conn.RemoteAddr(); remoteAddr != nil {
|
||||
// 从地址中提取IP(去除端口)
|
||||
host, _, err := net.SplitHostPort(remoteAddr.String())
|
||||
if err == nil {
|
||||
ipList = append(ipList, host)
|
||||
} else {
|
||||
// 如果解析失败,使用原始地址
|
||||
ipList = append(ipList, remoteAddr.String())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return ipList
|
||||
}
|
||||
|
||||
// GetPortConnsInfo 获取指定端口的连接信息
|
||||
func (m *SafeUDPConnMap) GetPortConnsInfo(port int, connType int) []string {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
|
||||
ipList := make([]string, 0)
|
||||
if conns, exists := m.items[port]; exists {
|
||||
for conn, info := range conns {
|
||||
if info.ConnType == connType {
|
||||
// 获取连接的远程地址
|
||||
if remoteAddr := conn.RemoteAddr(); remoteAddr != nil {
|
||||
// 从地址中提取IP(去除端口)
|
||||
host, _, err := net.SplitHostPort(remoteAddr.String())
|
||||
if err == nil {
|
||||
ipList = append(ipList, host)
|
||||
} else {
|
||||
// 如果解析失败,使用原始地址
|
||||
ipList = append(ipList, remoteAddr.String())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return ipList
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
package waftunnelmodel
|
||||
|
||||
import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
// SafeTunnelMap 线程安全的TunnelTarget Map
|
||||
type SafeTunnelMap struct {
|
||||
mu sync.RWMutex
|
||||
items map[string]*TunnelSafe
|
||||
}
|
||||
|
||||
// NewSafeTunnelMap 创建新的安全Map
|
||||
func NewSafeTunnelMap() *SafeTunnelMap {
|
||||
return &SafeTunnelMap{
|
||||
items: make(map[string]*TunnelSafe),
|
||||
}
|
||||
}
|
||||
|
||||
// Get 获取值
|
||||
func (m *SafeTunnelMap) Get(key string) (*TunnelSafe, bool) {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
val, ok := m.items[key]
|
||||
return val, ok
|
||||
}
|
||||
|
||||
// Set 设置值
|
||||
func (m *SafeTunnelMap) Set(key string, value *TunnelSafe) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
m.items[key] = value
|
||||
}
|
||||
|
||||
// Delete 删除值
|
||||
func (m *SafeTunnelMap) Delete(key string) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
delete(m.items, key)
|
||||
}
|
||||
|
||||
// Range 遍历Map
|
||||
func (m *SafeTunnelMap) Range(f func(key string, value *TunnelSafe) bool) {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
for k, v := range m.items {
|
||||
if !f(k, v) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Len 获取Map长度
|
||||
func (m *SafeTunnelMap) Len() int {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
return len(m.items)
|
||||
}
|
||||
|
||||
// GetAll 获取所有值
|
||||
func (m *SafeTunnelMap) GetAll() map[string]*TunnelSafe {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
result := make(map[string]*TunnelSafe, len(m.items))
|
||||
for k, v := range m.items {
|
||||
result[k] = v
|
||||
}
|
||||
return result
|
||||
}
|
||||
func (m *SafeTunnelMap) Clear() {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
m.items = make(map[string]*TunnelSafe)
|
||||
}
|
||||
|
||||
// SafeNetMap 线程安全的NetListerOnline Map
|
||||
type SafeNetMap struct {
|
||||
mu sync.RWMutex
|
||||
items map[string]NetRunTime
|
||||
}
|
||||
|
||||
// NewSafeNetMap 创建新的安全Map
|
||||
func NewSafeNetMap() *SafeNetMap {
|
||||
return &SafeNetMap{
|
||||
items: make(map[string]NetRunTime),
|
||||
}
|
||||
}
|
||||
|
||||
// Get 获取值
|
||||
func (m *SafeNetMap) Get(key string) (NetRunTime, bool) {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
val, ok := m.items[key]
|
||||
return val, ok
|
||||
}
|
||||
|
||||
// Set 设置值
|
||||
func (m *SafeNetMap) Set(key string, value NetRunTime) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
m.items[key] = value
|
||||
}
|
||||
|
||||
// Delete 删除值
|
||||
func (m *SafeNetMap) Delete(key string) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
delete(m.items, key)
|
||||
}
|
||||
|
||||
// Range 遍历Map
|
||||
func (m *SafeNetMap) Range(f func(key string, value NetRunTime) bool) {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
for k, v := range m.items {
|
||||
if !f(k, v) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Len 获取Map长度
|
||||
func (m *SafeNetMap) Len() int {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
return len(m.items)
|
||||
}
|
||||
|
||||
// GetAll 获取所有值
|
||||
func (m *SafeNetMap) GetAll() map[string]NetRunTime {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
result := make(map[string]NetRunTime, len(m.items))
|
||||
for k, v := range m.items {
|
||||
result[k] = v
|
||||
}
|
||||
return result
|
||||
}
|
||||
func (m *SafeNetMap) Clear() {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
m.items = make(map[string]NetRunTime)
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package waftunnelmodel
|
||||
|
||||
type NetRunTime struct {
|
||||
//tcp udp
|
||||
ServerType string
|
||||
Port int
|
||||
Status int // 0 是启动完成 ,1 是新增,2 是编辑 3,是删除
|
||||
Svr interface{} // 支持任何网络连接类型
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package waftunnelmodel
|
||||
|
||||
import (
|
||||
"SamWaf/model"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// TunnelSafe 隧道安全配置
|
||||
type TunnelSafe struct {
|
||||
Mux sync.Mutex //互斥锁
|
||||
Tunnel model.Tunnel
|
||||
}
|
||||
@@ -37,6 +37,7 @@ type ApiGroup struct {
|
||||
AnalysisRouter
|
||||
WafPrivateInfoRouter
|
||||
WafCacheRuleRouter
|
||||
WafTunnelRouter
|
||||
}
|
||||
type PublicApiGroup struct {
|
||||
LoginRouter
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
"SamWaf/api"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type WafTunnelRouter struct {
|
||||
}
|
||||
|
||||
func (receiver *WafTunnelRouter) InitWafTunnelRouter(group *gin.RouterGroup) {
|
||||
api := api.APIGroupAPP.WafTunnelApi
|
||||
router := group.Group("")
|
||||
router.POST("/samwaf/tunnel/tunnel/add", api.AddApi)
|
||||
router.POST("/samwaf/tunnel/tunnel/list", api.GetListApi)
|
||||
router.GET("/samwaf/tunnel/tunnel/detail", api.GetDetailApi)
|
||||
router.POST("/samwaf/tunnel/tunnel/edit", api.ModifyApi)
|
||||
router.GET("/samwaf/tunnel/tunnel/del", api.DelApi)
|
||||
router.GET("/samwaf/tunnel/tunnel/connections", api.GetTunnelConnectionsApi)
|
||||
}
|
||||
@@ -246,6 +246,7 @@ func (receiver *WafStatService) StatHomeRumtimeSysinfo() []response2.WafNameValu
|
||||
data = append(data, response2.WafNameValue{Name: "当前队列数", Value: fmt.Sprintf("主数据:%v 日志数据:%v 统计数据:%v 消息队列:%v", global.GQEQUE_DB.Size(), global.GQEQUE_LOG_DB.Size(), global.GQEQUE_STATS_DB.Size(), global.GQEQUE_MESSAGE_DB.Size())})
|
||||
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)})
|
||||
data = append(data, response2.WafNameValue{Name: "当前隧道端口使用列表", Value: fmt.Sprintf("%v", global.GWAF_RUNTIME_CURRENT_TUNNELPORT)})
|
||||
|
||||
lastTimeStr := time.Unix(0, global.GWAF_LAST_TIME_UNIX*int64(time.Millisecond)).Format("2006-01-02 15:04:05")
|
||||
data = append(data, response2.WafNameValue{Name: "最后统计时间", Value: fmt.Sprintf("%v (%v)", lastTimeStr, global.GWAF_LAST_TIME_UNIX)})
|
||||
|
||||
@@ -0,0 +1,171 @@
|
||||
package waf_service
|
||||
|
||||
import (
|
||||
"SamWaf/common/uuid"
|
||||
"SamWaf/customtype"
|
||||
"SamWaf/global"
|
||||
"SamWaf/model"
|
||||
"SamWaf/model/baseorm"
|
||||
"SamWaf/model/request"
|
||||
"errors"
|
||||
"time"
|
||||
)
|
||||
|
||||
type WafTunnelService struct{}
|
||||
|
||||
var WafTunnelServiceApp = new(WafTunnelService)
|
||||
|
||||
func (receiver *WafTunnelService) AddApi(req request.WafTunnelAddReq) (*model.Tunnel, error) {
|
||||
var bean = &model.Tunnel{
|
||||
BaseOrm: baseorm.BaseOrm{
|
||||
Id: uuid.GenUUID(),
|
||||
USER_CODE: global.GWAF_USER_CODE,
|
||||
Tenant_ID: global.GWAF_TENANT_ID,
|
||||
CREATE_TIME: customtype.JsonTime(time.Now()),
|
||||
UPDATE_TIME: customtype.JsonTime(time.Now()),
|
||||
},
|
||||
|
||||
Code: req.Code,
|
||||
Name: req.Name,
|
||||
Port: req.Port,
|
||||
Protocol: req.Protocol,
|
||||
RemotePort: req.RemotePort,
|
||||
RemoteIp: req.RemoteIp,
|
||||
AllowIp: req.AllowIp,
|
||||
DenyIp: req.DenyIp,
|
||||
StartStatus: req.StartStatus,
|
||||
ConnTimeout: req.ConnTimeout,
|
||||
ReadTimeout: req.ReadTimeout,
|
||||
WriteTimeout: req.WriteTimeout,
|
||||
MaxInConnect: req.MaxInConnect,
|
||||
MaxOutConnect: req.MaxOutConnect,
|
||||
Remark: req.Remark,
|
||||
}
|
||||
if bean.Code == "" {
|
||||
bean.Code = bean.Id
|
||||
}
|
||||
global.GWAF_LOCAL_DB.Create(bean)
|
||||
return bean, nil
|
||||
}
|
||||
|
||||
func (receiver *WafTunnelService) CheckIsExistApi(req request.WafTunnelAddReq) int {
|
||||
var total int64 = 0
|
||||
/*where条件*/
|
||||
var whereField = ""
|
||||
var whereValues []interface{}
|
||||
//where字段
|
||||
whereField = ""
|
||||
|
||||
if len(req.Name) > 0 {
|
||||
if len(whereField) > 0 {
|
||||
whereField = whereField + " and "
|
||||
}
|
||||
whereField = whereField + " name=? "
|
||||
}
|
||||
|
||||
//where字段赋值
|
||||
|
||||
if len(req.Name) > 0 {
|
||||
if len(whereField) > 0 {
|
||||
whereValues = append(whereValues, req.Name)
|
||||
}
|
||||
}
|
||||
|
||||
global.GWAF_LOCAL_DB.Model(&model.Tunnel{}).Where(whereField, whereValues...).Count(&total)
|
||||
return int(total)
|
||||
}
|
||||
|
||||
func (receiver *WafTunnelService) ModifyApi(req request.WafTunnelEditReq) error {
|
||||
// 根据唯一字段生成查询条件(只有在UniFields不为空时才进行存在性检查)
|
||||
|
||||
var total int64 = 0
|
||||
/*where条件*/
|
||||
var whereField = ""
|
||||
var whereValues []interface{}
|
||||
//where字段
|
||||
whereField = ""
|
||||
|
||||
if len(req.Name) > 0 {
|
||||
if len(whereField) > 0 {
|
||||
whereField = whereField + " and "
|
||||
}
|
||||
whereField = whereField + " name=? "
|
||||
}
|
||||
|
||||
//where字段赋值
|
||||
|
||||
if len(req.Name) > 0 {
|
||||
whereValues = append(whereValues, req.Name)
|
||||
}
|
||||
|
||||
global.GWAF_LOCAL_DB.Model(&model.Tunnel{}).Where(whereField, whereValues...).Count(&total)
|
||||
// 查询是否已存在记录
|
||||
var bean model.Tunnel
|
||||
global.GWAF_LOCAL_DB.Model(&model.Tunnel{}).Where(whereField, whereValues...).Limit(1).Find(&bean)
|
||||
|
||||
if int(total) > 0 && bean.Id != "" && bean.Id != req.Id {
|
||||
return errors.New("当前记录已经存在")
|
||||
}
|
||||
|
||||
beanMap := map[string]interface{}{
|
||||
|
||||
"Code": req.Code,
|
||||
"Name": req.Name,
|
||||
"Port": req.Port,
|
||||
"Protocol": req.Protocol,
|
||||
"RemotePort": req.RemotePort,
|
||||
"RemoteIp": req.RemoteIp,
|
||||
"AllowIp": req.AllowIp,
|
||||
"DenyIp": req.DenyIp,
|
||||
"StartStatus": req.StartStatus,
|
||||
"ConnTimeout": req.ConnTimeout,
|
||||
"ReadTimeout": req.ReadTimeout,
|
||||
"WriteTimeout": req.WriteTimeout,
|
||||
"MaxInConnect": req.MaxInConnect,
|
||||
"MaxOutConnect": req.MaxOutConnect,
|
||||
"Remark": req.Remark,
|
||||
|
||||
"UPDATE_TIME": customtype.JsonTime(time.Now()),
|
||||
}
|
||||
err := global.GWAF_LOCAL_DB.Model(model.Tunnel{}).Where("id = ?", req.Id).Updates(beanMap).Error
|
||||
|
||||
return err
|
||||
}
|
||||
func (receiver *WafTunnelService) GetDetailApi(req request.WafTunnelDetailReq) model.Tunnel {
|
||||
var bean model.Tunnel
|
||||
global.GWAF_LOCAL_DB.Where("id=?", req.Id).Find(&bean)
|
||||
return bean
|
||||
}
|
||||
func (receiver *WafTunnelService) GetDetailByIdApi(id string) model.Tunnel {
|
||||
var bean model.Tunnel
|
||||
global.GWAF_LOCAL_DB.Where("id=?", id).Find(&bean)
|
||||
return bean
|
||||
}
|
||||
func (receiver *WafTunnelService) GetListApi(req request.WafTunnelSearchReq) ([]model.Tunnel, int64, error) {
|
||||
var list []model.Tunnel
|
||||
var total int64 = 0
|
||||
global.GWAF_LOCAL_DB.Model(&model.Tunnel{}).Limit(req.PageSize).Offset(req.PageSize * (req.PageIndex - 1)).Find(&list)
|
||||
global.GWAF_LOCAL_DB.Model(&model.Tunnel{}).Count(&total)
|
||||
|
||||
return list, total, nil
|
||||
}
|
||||
func (receiver *WafTunnelService) DelApi(req request.WafTunnelDelReq) error {
|
||||
var bean model.Tunnel
|
||||
err := global.GWAF_LOCAL_DB.Where("id = ?", req.Id).First(&bean).Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = global.GWAF_LOCAL_DB.Where("id = ?", req.Id).Delete(model.Tunnel{}).Error
|
||||
return err
|
||||
}
|
||||
|
||||
// GetTunnelConnections 获取隧道连接信息
|
||||
func (receiver *WafTunnelService) GetTunnelConnections(id string) (model.Tunnel, error) {
|
||||
var tunnel model.Tunnel
|
||||
err := global.GWAF_LOCAL_DB.Where("id = ?", id).First(&tunnel).Error
|
||||
if err != nil {
|
||||
return model.Tunnel{}, err
|
||||
}
|
||||
|
||||
return tunnel, nil
|
||||
}
|
||||
@@ -198,16 +198,6 @@ func CloseIPDatabase() {
|
||||
}
|
||||
}
|
||||
|
||||
// PortCheck 检查端口是否可用,可用-true 不可用-false
|
||||
func PortCheck(port int) bool {
|
||||
conn, err := net.DialTimeout("tcp", fmt.Sprintf(":%d", port), time.Second)
|
||||
if err != nil {
|
||||
return true // Port is available
|
||||
}
|
||||
defer conn.Close()
|
||||
return false // Port is not available
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
通过ip段(CIDR)来查询是否在很多CIDR段内
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
// PortCheck 检查端口是否可用,可用-true 不可用-false
|
||||
func PortCheck(port int) bool {
|
||||
conn, err := net.DialTimeout("tcp", fmt.Sprintf(":%d", port), time.Second)
|
||||
if err != nil {
|
||||
return true // Port is available
|
||||
}
|
||||
defer conn.Close()
|
||||
return false // Port is not available
|
||||
}
|
||||
|
||||
// TCP端口检测
|
||||
func TCPPortCheck(port int) bool {
|
||||
conn, err := net.Listen("tcp", ":"+strconv.Itoa(port))
|
||||
if err != nil {
|
||||
return true
|
||||
}
|
||||
defer conn.Close()
|
||||
return false
|
||||
}
|
||||
|
||||
// UDP端口检测
|
||||
func UDPPortCheck(port int) bool {
|
||||
addr, _ := net.ResolveUDPAddr("udp", ":"+strconv.Itoa(port))
|
||||
conn, err := net.ListenUDP("udp", addr)
|
||||
if err != nil {
|
||||
return true
|
||||
}
|
||||
defer conn.Close()
|
||||
return false
|
||||
}
|
||||
@@ -152,6 +152,9 @@ func InitCoreDb(currentDir string) (bool, error) {
|
||||
//缓存规则
|
||||
db.AutoMigrate(&model.CacheRule{})
|
||||
|
||||
//隧道
|
||||
db.AutoMigrate(&model.Tunnel{})
|
||||
|
||||
global.GWAF_LOCAL_DB.Callback().Query().Before("gorm:query").Register("tenant_plugin:before_query", before_query)
|
||||
global.GWAF_LOCAL_DB.Callback().Query().Before("gorm:update").Register("tenant_plugin:before_update", before_update)
|
||||
|
||||
|
||||
@@ -71,6 +71,7 @@ func (web *WafWebManager) initRouter(r *gin.Engine) {
|
||||
router.ApiGroupApp.InitAnalysisRouter(RouterGroup)
|
||||
router.ApiGroupApp.InitWafPrivateInfoRouter(RouterGroup)
|
||||
router.ApiGroupApp.InitWafCacheRuleRouter(RouterGroup)
|
||||
router.ApiGroupApp.InitWafTunnelRouter(RouterGroup)
|
||||
}
|
||||
|
||||
if global.GWAF_RELEASE == "true" {
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
package waftunnelengine
|
||||
|
||||
import (
|
||||
"SamWaf/common/zlog"
|
||||
"SamWaf/model"
|
||||
"net"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// CheckIPAccess 检查IP是否允许访问
|
||||
// 返回值: true表示允许访问,false表示拒绝访问
|
||||
func CheckIPAccess(clientIP string, tunnel model.Tunnel) bool {
|
||||
// 如果客户端IP为空,拒绝访问
|
||||
if clientIP == "" {
|
||||
zlog.Warn("客户端IP为空,拒绝访问")
|
||||
return false
|
||||
}
|
||||
|
||||
// 处理IP地址,去除端口部分
|
||||
if strings.Contains(clientIP, ":") {
|
||||
host, _, err := net.SplitHostPort(clientIP)
|
||||
if err != nil {
|
||||
zlog.Error("解析客户端IP失败: " + err.Error())
|
||||
return false
|
||||
}
|
||||
clientIP = host
|
||||
}
|
||||
|
||||
// 检查黑名单(优先级高)
|
||||
if tunnel.DenyIp != "" {
|
||||
denyIPs := strings.Split(tunnel.DenyIp, ",")
|
||||
for _, ip := range denyIPs {
|
||||
ip = strings.TrimSpace(ip)
|
||||
if ip == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
// 支持CIDR格式和精确匹配
|
||||
if strings.Contains(ip, "/") {
|
||||
// CIDR格式
|
||||
_, ipNet, err := net.ParseCIDR(ip)
|
||||
if err == nil && ipNet.Contains(net.ParseIP(clientIP)) {
|
||||
zlog.Info("IP " + clientIP + " 在黑名单CIDR " + ip + " 范围内,拒绝访问")
|
||||
return false
|
||||
}
|
||||
} else if ip == clientIP {
|
||||
// 精确匹配
|
||||
zlog.Info("IP " + clientIP + " 在黑名单中,拒绝访问")
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 检查白名单
|
||||
if tunnel.AllowIp != "" {
|
||||
// 白名单不为空,需要在白名单中才允许访问
|
||||
allowIPs := strings.Split(tunnel.AllowIp, ",")
|
||||
for _, ip := range allowIPs {
|
||||
ip = strings.TrimSpace(ip)
|
||||
if ip == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
// 支持CIDR格式和精确匹配
|
||||
if strings.Contains(ip, "/") {
|
||||
// CIDR格式
|
||||
_, ipNet, err := net.ParseCIDR(ip)
|
||||
if err == nil && ipNet.Contains(net.ParseIP(clientIP)) {
|
||||
zlog.Info("IP " + clientIP + " 在白名单CIDR " + ip + " 范围内,允许访问")
|
||||
return true
|
||||
}
|
||||
} else if ip == clientIP {
|
||||
// 精确匹配
|
||||
zlog.Info("IP " + clientIP + " 在白名单中,允许访问")
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// 如果有白名单但IP不在其中,拒绝访问
|
||||
zlog.Info("IP " + clientIP + " 不在白名单中,拒绝访问")
|
||||
return false
|
||||
}
|
||||
|
||||
// 如果没有设置白名单,且不在黑名单中,则允许访问
|
||||
zlog.Info("隧道访问 IP " + clientIP + " 通过访问控制检查,允许访问")
|
||||
return true
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
package waftunnelengine
|
||||
|
||||
import (
|
||||
"SamWaf/common/zlog"
|
||||
"SamWaf/model/waftunnelmodel"
|
||||
"net"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
// startTCPServer 启动TCP服务器
|
||||
func (waf *WafTunnelEngine) startTCPServer(netRuntime waftunnelmodel.NetRunTime) {
|
||||
addr := ":" + strconv.Itoa(netRuntime.Port)
|
||||
listener, err := net.Listen("tcp", addr)
|
||||
if err != nil {
|
||||
zlog.Error("TCP服务器启动失败,端口: " + strconv.Itoa(netRuntime.Port) + ", 错误: " + err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
key := "tcp" + strconv.Itoa(netRuntime.Port)
|
||||
// 更新状态
|
||||
netClone, _ := waf.NetListerOnline.Get(key)
|
||||
netClone.Status = 0
|
||||
netClone.Svr = listener
|
||||
waf.NetListerOnline.Set(key, netClone)
|
||||
|
||||
zlog.Info("启动TCP服务器,端口: " + strconv.Itoa(netRuntime.Port))
|
||||
|
||||
for {
|
||||
conn, err := listener.Accept()
|
||||
if err != nil {
|
||||
zlog.Error("TCP连接接收失败: " + err.Error())
|
||||
break
|
||||
}
|
||||
|
||||
// 获取隧道配置
|
||||
tunnelInfo, ok := waf.TunnelTarget.Get("tcp" + strconv.Itoa(netRuntime.Port))
|
||||
if !ok {
|
||||
zlog.Error("未找到端口对应的隧道配置: " + "tcp" + strconv.Itoa(netRuntime.Port))
|
||||
conn.Close()
|
||||
continue
|
||||
}
|
||||
|
||||
// 检查入站连接数限制
|
||||
if tunnelInfo.Tunnel.MaxInConnect > 0 {
|
||||
inConnCount := waf.TCPConnections.GetPortConnsCountByType(netRuntime.Port, waftunnelmodel.ConnTypeSource)
|
||||
if inConnCount >= tunnelInfo.Tunnel.MaxInConnect {
|
||||
zlog.Warn("TCP入站连接数超过限制,当前连接数: " + strconv.Itoa(inConnCount) +
|
||||
", 最大限制: " + strconv.Itoa(tunnelInfo.Tunnel.MaxInConnect) +
|
||||
", 端口: " + strconv.Itoa(netRuntime.Port))
|
||||
conn.Close()
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// 处理连接
|
||||
go waf.handleTCPConnection(conn, netRuntime.Port)
|
||||
}
|
||||
|
||||
zlog.Info("TCP服务器关闭,端口: " + strconv.Itoa(netRuntime.Port))
|
||||
}
|
||||
|
||||
// handleTCPConnection 处理TCP连接
|
||||
func (waf *WafTunnelEngine) handleTCPConnection(clientConn net.Conn, port int) {
|
||||
// 获取客户端IP
|
||||
clientIP := clientConn.RemoteAddr().String()
|
||||
|
||||
// 获取隧道配置
|
||||
tunnelInfo, ok := waf.TunnelTarget.Get("tcp" + strconv.Itoa(port))
|
||||
if !ok {
|
||||
zlog.Error("未找到端口对应的隧道配置: " + "tcp" + strconv.Itoa(port))
|
||||
clientConn.Close()
|
||||
return
|
||||
}
|
||||
|
||||
// 检查IP访问权限
|
||||
if !CheckIPAccess(clientIP, tunnelInfo.Tunnel) {
|
||||
zlog.Warn("TCP连接被拒绝,客户端IP: " + clientIP + ", 端口: " + strconv.Itoa(port))
|
||||
clientConn.Close()
|
||||
return
|
||||
}
|
||||
|
||||
// 将客户端连接添加到活动连接列表,标记为来源连接
|
||||
waf.TCPConnections.AddConn(port, clientConn, waftunnelmodel.ConnTypeSource)
|
||||
defer func() {
|
||||
clientConn.Close()
|
||||
waf.TCPConnections.RemoveConn(port, clientConn)
|
||||
}()
|
||||
|
||||
// 检查出站连接数限制
|
||||
if tunnelInfo.Tunnel.MaxOutConnect > 0 {
|
||||
outConnCount := waf.TCPConnections.GetPortConnsCountByType(port, waftunnelmodel.ConnTypeTarget)
|
||||
if outConnCount >= tunnelInfo.Tunnel.MaxOutConnect {
|
||||
zlog.Warn("TCP出站连接数超过限制,当前连接数: " + strconv.Itoa(outConnCount) +
|
||||
", 最大限制: " + strconv.Itoa(tunnelInfo.Tunnel.MaxOutConnect) +
|
||||
", 端口: " + strconv.Itoa(port))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// 连接到目标服务器
|
||||
targetAddr := tunnelInfo.Tunnel.RemoteIp + ":" + strconv.Itoa(tunnelInfo.Tunnel.RemotePort)
|
||||
targetConn, err := net.Dial("tcp", targetAddr)
|
||||
if err != nil {
|
||||
zlog.Error("连接目标服务器失败: " + targetAddr + ", 错误: " + err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// 将目标连接也添加到活动连接列表,标记为目标连接
|
||||
waf.TCPConnections.AddConn(port, targetConn, waftunnelmodel.ConnTypeTarget)
|
||||
defer func() {
|
||||
targetConn.Close()
|
||||
waf.TCPConnections.RemoveConn(port, targetConn)
|
||||
}()
|
||||
|
||||
// 设置超时
|
||||
if tunnelInfo.Tunnel.ConnTimeout > 0 {
|
||||
clientConn.SetDeadline(time.Now().Add(time.Duration(tunnelInfo.Tunnel.ConnTimeout) * time.Second))
|
||||
targetConn.SetDeadline(time.Now().Add(time.Duration(tunnelInfo.Tunnel.ConnTimeout) * time.Second))
|
||||
}
|
||||
|
||||
// 设置读取超时
|
||||
if tunnelInfo.Tunnel.ReadTimeout > 0 {
|
||||
clientConn.SetReadDeadline(time.Now().Add(time.Duration(tunnelInfo.Tunnel.ReadTimeout) * time.Second))
|
||||
targetConn.SetReadDeadline(time.Now().Add(time.Duration(tunnelInfo.Tunnel.ReadTimeout) * time.Second))
|
||||
}
|
||||
|
||||
// 设置写入超时
|
||||
if tunnelInfo.Tunnel.WriteTimeout > 0 {
|
||||
clientConn.SetWriteDeadline(time.Now().Add(time.Duration(tunnelInfo.Tunnel.WriteTimeout) * time.Second))
|
||||
targetConn.SetWriteDeadline(time.Now().Add(time.Duration(tunnelInfo.Tunnel.WriteTimeout) * time.Second))
|
||||
}
|
||||
|
||||
// 双向转发数据
|
||||
go func() {
|
||||
buffer := make([]byte, 4096)
|
||||
for {
|
||||
n, err := clientConn.Read(buffer)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
_, err = targetConn.Write(buffer[:n])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
buffer := make([]byte, 4096)
|
||||
for {
|
||||
n, err := targetConn.Read(buffer)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
_, err = clientConn.Write(buffer[:n])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
package waftunnelengine
|
||||
|
||||
import (
|
||||
"SamWaf/common/zlog"
|
||||
"SamWaf/model/waftunnelmodel"
|
||||
"net"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
// startUDPServer 启动UDP服务器
|
||||
func (waf *WafTunnelEngine) startUDPServer(netRuntime waftunnelmodel.NetRunTime) {
|
||||
addr := ":" + strconv.Itoa(netRuntime.Port)
|
||||
udpAddr, err := net.ResolveUDPAddr("udp", addr)
|
||||
if err != nil {
|
||||
zlog.Error("UDP地址解析失败,端口: " + strconv.Itoa(netRuntime.Port) + ", 错误: " + err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
conn, err := net.ListenUDP("udp", udpAddr)
|
||||
if err != nil {
|
||||
zlog.Error("UDP服务器启动失败,端口: " + strconv.Itoa(netRuntime.Port) + ", 错误: " + err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// 将服务器连接添加到活动连接列表,标记为来源连接
|
||||
waf.UDPConnections.AddConn(netRuntime.Port, conn, waftunnelmodel.ConnTypeSource)
|
||||
|
||||
key := "udp" + strconv.Itoa(netRuntime.Port)
|
||||
// 更新状态
|
||||
netClone, _ := waf.NetListerOnline.Get(key)
|
||||
netClone.Status = 0
|
||||
netClone.Svr = conn
|
||||
waf.NetListerOnline.Set(key, netClone)
|
||||
|
||||
zlog.Info("启动UDP服务器,端口: " + strconv.Itoa(netRuntime.Port))
|
||||
|
||||
// 处理UDP数据
|
||||
buffer := make([]byte, 1024)
|
||||
for {
|
||||
n, remoteAddr, err := conn.ReadFromUDP(buffer)
|
||||
if err != nil {
|
||||
zlog.Error("UDP读取失败: " + err.Error())
|
||||
break
|
||||
}
|
||||
|
||||
// 获取隧道配置
|
||||
tunnelInfo, ok := waf.TunnelTarget.Get("udp" + strconv.Itoa(netRuntime.Port))
|
||||
if !ok {
|
||||
zlog.Error("未找到端口对应的隧道配置: " + "udp" + strconv.Itoa(netRuntime.Port))
|
||||
continue
|
||||
}
|
||||
|
||||
// 检查入站连接数限制
|
||||
if tunnelInfo.Tunnel.MaxInConnect > 0 {
|
||||
inConnCount := waf.UDPConnections.GetPortConnsCountByType(netRuntime.Port, waftunnelmodel.ConnTypeSource)
|
||||
if inConnCount >= tunnelInfo.Tunnel.MaxInConnect {
|
||||
zlog.Warn("UDP入站连接数超过限制,当前连接数: " + strconv.Itoa(inConnCount) +
|
||||
", 最大限制: " + strconv.Itoa(tunnelInfo.Tunnel.MaxInConnect) +
|
||||
", 端口: " + strconv.Itoa(netRuntime.Port))
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// 处理UDP数据
|
||||
go waf.handleUDPData(conn, remoteAddr, buffer[:n], netRuntime.Port)
|
||||
}
|
||||
|
||||
zlog.Info("UDP服务器关闭,端口: " + strconv.Itoa(netRuntime.Port))
|
||||
waf.UDPConnections.RemoveConn(netRuntime.Port, conn)
|
||||
}
|
||||
|
||||
// handleUDPData 处理UDP数据
|
||||
func (waf *WafTunnelEngine) handleUDPData(serverConn *net.UDPConn, clientAddr *net.UDPAddr, data []byte, port int) {
|
||||
// 获取客户端IP
|
||||
clientIP := clientAddr.String()
|
||||
|
||||
// 获取隧道配置
|
||||
tunnelInfo, ok := waf.TunnelTarget.Get("udp" + strconv.Itoa(port))
|
||||
if !ok {
|
||||
zlog.Error("未找到端口对应的隧道配置: " + "udp" + strconv.Itoa(port))
|
||||
return
|
||||
}
|
||||
|
||||
// 检查IP访问权限
|
||||
if !CheckIPAccess(clientIP, tunnelInfo.Tunnel) {
|
||||
zlog.Warn("UDP数据被拒绝,客户端IP: " + clientIP + ", 端口: " + strconv.Itoa(port))
|
||||
return
|
||||
}
|
||||
|
||||
// 检查出站连接数限制
|
||||
if tunnelInfo.Tunnel.MaxOutConnect > 0 {
|
||||
outConnCount := waf.UDPConnections.GetPortConnsCountByType(port, waftunnelmodel.ConnTypeTarget)
|
||||
if outConnCount >= tunnelInfo.Tunnel.MaxOutConnect {
|
||||
zlog.Warn("UDP出站连接数超过限制,当前连接数: " + strconv.Itoa(outConnCount) +
|
||||
", 最大限制: " + strconv.Itoa(tunnelInfo.Tunnel.MaxOutConnect) +
|
||||
", 端口: " + strconv.Itoa(port))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// 连接到目标服务器
|
||||
targetAddr := tunnelInfo.Tunnel.RemoteIp + ":" + strconv.Itoa(tunnelInfo.Tunnel.RemotePort)
|
||||
raddr, err := net.ResolveUDPAddr("udp", targetAddr)
|
||||
if err != nil {
|
||||
zlog.Error("解析目标地址失败: " + targetAddr + ", 错误: " + err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// 创建到目标的连接
|
||||
targetConn, err := net.DialUDP("udp", nil, raddr)
|
||||
if err != nil {
|
||||
zlog.Error("连接目标服务器失败: " + targetAddr + ", 错误: " + err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// 将目标连接添加到活动连接列表,标记为目标连接
|
||||
waf.UDPConnections.AddConn(port, targetConn, waftunnelmodel.ConnTypeTarget)
|
||||
defer func() {
|
||||
targetConn.Close()
|
||||
waf.UDPConnections.RemoveConn(port, targetConn)
|
||||
}()
|
||||
|
||||
// 设置超时
|
||||
if tunnelInfo.Tunnel.ConnTimeout > 0 {
|
||||
targetConn.SetDeadline(time.Now().Add(time.Duration(tunnelInfo.Tunnel.ConnTimeout) * time.Second))
|
||||
}
|
||||
|
||||
// 设置读取超时
|
||||
if tunnelInfo.Tunnel.ReadTimeout > 0 {
|
||||
targetConn.SetReadDeadline(time.Now().Add(time.Duration(tunnelInfo.Tunnel.ReadTimeout) * time.Second))
|
||||
}
|
||||
|
||||
// 设置写入超时
|
||||
if tunnelInfo.Tunnel.WriteTimeout > 0 {
|
||||
targetConn.SetWriteDeadline(time.Now().Add(time.Duration(tunnelInfo.Tunnel.WriteTimeout) * time.Second))
|
||||
}
|
||||
|
||||
// 发送数据到目标
|
||||
_, err = targetConn.Write(data)
|
||||
if err != nil {
|
||||
zlog.Error("发送数据到目标失败: " + err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// 接收目标响应
|
||||
buffer := make([]byte, 4096)
|
||||
n, _, err := targetConn.ReadFromUDP(buffer)
|
||||
if err != nil {
|
||||
zlog.Error("从目标接收数据失败: " + err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// 发送响应回客户端
|
||||
_, err = serverConn.WriteToUDP(buffer[:n], clientAddr)
|
||||
if err != nil {
|
||||
zlog.Error("发送响应到客户端失败: " + err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,354 @@
|
||||
package waftunnelengine
|
||||
|
||||
import (
|
||||
"SamWaf/common/zlog"
|
||||
"SamWaf/global"
|
||||
"SamWaf/model"
|
||||
"SamWaf/model/waftunnelmodel"
|
||||
"net"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type WafTunnelEngine struct {
|
||||
//隧道情况(key:协议+端口 : tunnelSafe信息里面详细等)
|
||||
TunnelTarget *waftunnelmodel.SafeTunnelMap
|
||||
//服务在线情况(key:协议+端口,value :连接接入控制器)
|
||||
NetListerOnline *waftunnelmodel.SafeNetMap
|
||||
//TCP连接管理
|
||||
TCPConnections *waftunnelmodel.SafeTCPConnMap
|
||||
//UDP连接管理
|
||||
UDPConnections *waftunnelmodel.SafeUDPConnMap
|
||||
}
|
||||
|
||||
func NewWafTunnelEngine() *WafTunnelEngine {
|
||||
return &WafTunnelEngine{
|
||||
TunnelTarget: waftunnelmodel.NewSafeTunnelMap(),
|
||||
NetListerOnline: waftunnelmodel.NewSafeNetMap(),
|
||||
TCPConnections: waftunnelmodel.NewSafeTCPConnMap(),
|
||||
UDPConnections: waftunnelmodel.NewSafeUDPConnMap(),
|
||||
}
|
||||
}
|
||||
|
||||
// StartTunnel 启动tunnel
|
||||
func (waf *WafTunnelEngine) StartTunnel() {
|
||||
// 启动tunnel
|
||||
waf.LoadAllTunnel()
|
||||
waf.StartAllTunnelServer()
|
||||
}
|
||||
|
||||
// CloseTunnel 关闭tunnel
|
||||
func (waf *WafTunnelEngine) CloseTunnel() {
|
||||
// 关闭tunnel
|
||||
zlog.Info("开始关闭所有隧道服务...")
|
||||
waf.StopAllTunnelServer()
|
||||
// 清理隧道目标信息
|
||||
waf.TunnelTarget.Clear()
|
||||
|
||||
// 清理服务在线情况
|
||||
waf.NetListerOnline.Clear()
|
||||
zlog.Info("所有隧道服务已关闭")
|
||||
}
|
||||
|
||||
// LoadAllTunnel 加载全部tunnel
|
||||
func (waf *WafTunnelEngine) LoadAllTunnel() {
|
||||
//重新查询
|
||||
var tunnels []model.Tunnel
|
||||
global.GWAF_LOCAL_DB.Find(&tunnels)
|
||||
for i := 0; i < len(tunnels); i++ {
|
||||
waf.LoadTunnel(tunnels[i])
|
||||
}
|
||||
}
|
||||
|
||||
// LoadTunnel 加载指定tunnel
|
||||
func (waf *WafTunnelEngine) LoadTunnel(inTunnel model.Tunnel) []waftunnelmodel.NetRunTime {
|
||||
|
||||
netRunTimes := make([]waftunnelmodel.NetRunTime, 0)
|
||||
|
||||
// 先处理端口
|
||||
portStr := inTunnel.Port
|
||||
portStrArray := strings.Split(portStr, ",")
|
||||
portArray := make([]int, 0, len(portStrArray))
|
||||
for _, portItem := range portStrArray {
|
||||
portItem = strings.TrimSpace(portItem)
|
||||
if portItem == "" {
|
||||
continue
|
||||
}
|
||||
port, err := strconv.Atoi(portItem)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
portArray = append(portArray, port)
|
||||
|
||||
key := inTunnel.Protocol + portItem //唯一识别 协议+端口
|
||||
_, ok := waf.NetListerOnline.Get(key)
|
||||
if ok == false {
|
||||
//不存在,创建一个
|
||||
netRuntime := waftunnelmodel.NetRunTime{
|
||||
ServerType: inTunnel.Protocol,
|
||||
Port: port,
|
||||
Status: inTunnel.StartStatus,
|
||||
Svr: nil,
|
||||
}
|
||||
waf.NetListerOnline.Set(key, netRuntime)
|
||||
netRunTimes = append(netRunTimes, netRuntime)
|
||||
}
|
||||
//设置或者重新更新引用隧道的基本信息
|
||||
waf.TunnelTarget.Set(key, &waftunnelmodel.TunnelSafe{
|
||||
Tunnel: inTunnel,
|
||||
})
|
||||
}
|
||||
|
||||
return netRunTimes
|
||||
}
|
||||
|
||||
// EditTunnel 编辑隧道配置
|
||||
func (waf *WafTunnelEngine) EditTunnel(oldTunnel model.Tunnel, newTunnel model.Tunnel) []waftunnelmodel.NetRunTime {
|
||||
// 返回值:新增的端口运行时, 移除的端口运行时
|
||||
addedRunTimes := make([]waftunnelmodel.NetRunTime, 0)
|
||||
|
||||
// 解析旧端口列表
|
||||
oldPortMap := make(map[string]bool)
|
||||
oldPortStrArray := strings.Split(oldTunnel.Port, ",")
|
||||
for _, portItem := range oldPortStrArray {
|
||||
portItem = strings.TrimSpace(portItem)
|
||||
if portItem == "" {
|
||||
continue
|
||||
}
|
||||
oldPortMap[portItem] = true
|
||||
}
|
||||
|
||||
// 解析新端口列表
|
||||
newPortMap := make(map[string]bool)
|
||||
newPortStrArray := strings.Split(newTunnel.Port, ",")
|
||||
for _, portItem := range newPortStrArray {
|
||||
portItem = strings.TrimSpace(portItem)
|
||||
if portItem == "" {
|
||||
continue
|
||||
}
|
||||
newPortMap[portItem] = true
|
||||
}
|
||||
|
||||
// 情况1:端口完全没变,只修改了其他信息
|
||||
if len(oldPortMap) == len(newPortMap) {
|
||||
allSame := true
|
||||
for port := range oldPortMap {
|
||||
if !newPortMap[port] {
|
||||
allSame = false
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
//如果是端口相同,也存在是否存在切换状态
|
||||
if allSame {
|
||||
// 端口完全相同,只需要更新隧道信息
|
||||
for port := range oldPortMap {
|
||||
key := oldTunnel.Protocol + port
|
||||
// 更新隧道目标信息
|
||||
if tunnelSafe, ok := waf.TunnelTarget.Get(key); ok {
|
||||
tunnelSafe.Tunnel = newTunnel
|
||||
waf.TunnelTarget.Set(key, tunnelSafe)
|
||||
}
|
||||
}
|
||||
if oldTunnel.StartStatus != newTunnel.StartStatus {
|
||||
if newTunnel.StartStatus == 0 {
|
||||
waf.RemoveTunnel(newTunnel)
|
||||
} else {
|
||||
netRunTimes := waf.LoadTunnel(newTunnel)
|
||||
addedRunTimes = append(addedRunTimes, netRunTimes...)
|
||||
}
|
||||
}
|
||||
return addedRunTimes
|
||||
}
|
||||
}
|
||||
|
||||
// 情况2和3:端口有变化
|
||||
|
||||
// 处理减少的端口 - 需要移除
|
||||
for port := range oldPortMap {
|
||||
if !newPortMap[port] {
|
||||
// 这个端口在新配置中不存在,需要移除
|
||||
key := oldTunnel.Protocol + port
|
||||
if netRuntime, ok := waf.NetListerOnline.Get(key); ok {
|
||||
// 停止服务
|
||||
waf.StopTunnelServer(netRuntime)
|
||||
// 从在线列表中移除
|
||||
waf.NetListerOnline.Delete(key)
|
||||
// 从隧道目标中移除
|
||||
waf.TunnelTarget.Delete(key)
|
||||
zlog.Info("已移除隧道服务: " + oldTunnel.Protocol + " 端口: " + port)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 处理新增的端口 - 需要添加
|
||||
for port := range newPortMap {
|
||||
if !oldPortMap[port] {
|
||||
// 这个端口在旧配置中不存在,需要添加
|
||||
portInt, err := strconv.Atoi(port)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
key := newTunnel.Protocol + port
|
||||
_, ok := waf.NetListerOnline.Get(key)
|
||||
if !ok {
|
||||
// 不存在,创建一个
|
||||
netRuntime := waftunnelmodel.NetRunTime{
|
||||
ServerType: newTunnel.Protocol,
|
||||
Port: portInt,
|
||||
Status: newTunnel.StartStatus,
|
||||
Svr: nil,
|
||||
}
|
||||
waf.NetListerOnline.Set(key, netRuntime)
|
||||
addedRunTimes = append(addedRunTimes, netRuntime)
|
||||
}
|
||||
// 设置或更新隧道基本信息
|
||||
waf.TunnelTarget.Set(key, &waftunnelmodel.TunnelSafe{
|
||||
Tunnel: newTunnel,
|
||||
})
|
||||
} else {
|
||||
// 端口相同但其他信息可能变了,更新隧道信息
|
||||
key := newTunnel.Protocol + port
|
||||
if tunnelSafe, ok := waf.TunnelTarget.Get(key); ok {
|
||||
tunnelSafe.Tunnel = newTunnel
|
||||
waf.TunnelTarget.Set(key, tunnelSafe)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return addedRunTimes
|
||||
}
|
||||
|
||||
// RemoveTunnel 移除指定tunnel
|
||||
func (waf *WafTunnelEngine) RemoveTunnel(inTunnel model.Tunnel) []waftunnelmodel.NetRunTime {
|
||||
removedRunTimes := make([]waftunnelmodel.NetRunTime, 0)
|
||||
|
||||
// 处理端口
|
||||
portStr := inTunnel.Port
|
||||
portStrArray := strings.Split(portStr, ",")
|
||||
for _, portItem := range portStrArray {
|
||||
portItem = strings.TrimSpace(portItem)
|
||||
if portItem == "" {
|
||||
continue
|
||||
}
|
||||
_, err := strconv.Atoi(portItem)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
key := inTunnel.Protocol + portItem // 唯一识别 协议+端口
|
||||
netRuntime, ok := waf.NetListerOnline.Get(key)
|
||||
if ok {
|
||||
// 存在,需要移除
|
||||
removedRunTimes = append(removedRunTimes, netRuntime)
|
||||
// 停止服务
|
||||
waf.StopTunnelServer(netRuntime)
|
||||
// 从在线列表中移除
|
||||
waf.NetListerOnline.Delete(key)
|
||||
// 从隧道目标中移除
|
||||
waf.TunnelTarget.Delete(key)
|
||||
zlog.Info("已移除隧道服务: " + inTunnel.Protocol + " 端口: " + portItem)
|
||||
}
|
||||
}
|
||||
|
||||
return removedRunTimes
|
||||
}
|
||||
|
||||
// StartAllTunnelServer 开启所有隧道服务
|
||||
func (waf *WafTunnelEngine) StartAllTunnelServer() {
|
||||
netMap := waf.NetListerOnline.GetAll()
|
||||
for _, v := range netMap {
|
||||
waf.StartTunnelServer(v)
|
||||
}
|
||||
waf.EnumAllPortTunnelServer()
|
||||
}
|
||||
|
||||
// EnumAllPortTunnelServer 罗列所有隧道端口
|
||||
func (waf *WafTunnelEngine) EnumAllPortTunnelServer() {
|
||||
onlinePorts := ""
|
||||
netMap := waf.NetListerOnline.GetAll()
|
||||
for _, v := range netMap {
|
||||
onlinePorts = strconv.Itoa(v.Port) + "," + onlinePorts
|
||||
}
|
||||
// 可以将端口信息存储到全局变量中
|
||||
global.GWAF_RUNTIME_CURRENT_TUNNELPORT = onlinePorts
|
||||
}
|
||||
|
||||
// StartTunnelServer 启动指定隧道服务
|
||||
func (waf *WafTunnelEngine) StartTunnelServer(netRuntime waftunnelmodel.NetRunTime) {
|
||||
if netRuntime.Status == 0 {
|
||||
// 已启动完成的就不处理
|
||||
return
|
||||
}
|
||||
if netRuntime.ServerType == "" {
|
||||
// 如果协议类型为空就不处理
|
||||
return
|
||||
}
|
||||
|
||||
go func(netRuntime waftunnelmodel.NetRunTime) {
|
||||
defer func() {
|
||||
e := recover()
|
||||
if e != nil {
|
||||
zlog.Warn("tunnel server recover ", e)
|
||||
}
|
||||
}()
|
||||
|
||||
// 根据协议类型启动不同的服务
|
||||
switch strings.ToLower(netRuntime.ServerType) {
|
||||
case "tcp":
|
||||
waf.startTCPServer(netRuntime)
|
||||
case "udp":
|
||||
waf.startUDPServer(netRuntime)
|
||||
default:
|
||||
zlog.Warn("不支持的协议类型: " + netRuntime.ServerType)
|
||||
}
|
||||
}(netRuntime)
|
||||
}
|
||||
|
||||
// StopAllTunnelServer 关闭所有隧道服务
|
||||
func (waf *WafTunnelEngine) StopAllTunnelServer() {
|
||||
netMap := waf.NetListerOnline.GetAll()
|
||||
for _, v := range netMap {
|
||||
waf.StopTunnelServer(v)
|
||||
}
|
||||
}
|
||||
|
||||
// StopTunnelServer 关闭指定隧道服务
|
||||
func (waf *WafTunnelEngine) StopTunnelServer(netRuntime waftunnelmodel.NetRunTime) {
|
||||
portStr := strconv.Itoa(netRuntime.Port)
|
||||
|
||||
// 关闭服务器
|
||||
if netRuntime.Svr != nil {
|
||||
// 根据不同类型关闭服务
|
||||
switch svr := netRuntime.Svr.(type) {
|
||||
case net.Listener:
|
||||
svr.Close()
|
||||
case *net.UDPConn:
|
||||
svr.Close()
|
||||
}
|
||||
}
|
||||
|
||||
// 获取详细的连接统计
|
||||
tcpSourceCount := waf.TCPConnections.GetPortConnsCountByType(netRuntime.Port, waftunnelmodel.ConnTypeSource)
|
||||
tcpTargetCount := waf.TCPConnections.GetPortConnsCountByType(netRuntime.Port, waftunnelmodel.ConnTypeTarget)
|
||||
udpSourceCount := waf.UDPConnections.GetPortConnsCountByType(netRuntime.Port, waftunnelmodel.ConnTypeSource)
|
||||
udpTargetCount := waf.UDPConnections.GetPortConnsCountByType(netRuntime.Port, waftunnelmodel.ConnTypeTarget)
|
||||
|
||||
// 总连接数
|
||||
tcpCount := tcpSourceCount + tcpTargetCount
|
||||
udpCount := udpSourceCount + udpTargetCount
|
||||
|
||||
if tcpCount > 0 || udpCount > 0 {
|
||||
zlog.Info("正在关闭端口 " + portStr + " 的连接: " +
|
||||
"TCP总计=" + strconv.Itoa(tcpCount) +
|
||||
"(来源=" + strconv.Itoa(tcpSourceCount) +
|
||||
",目标=" + strconv.Itoa(tcpTargetCount) + "), " +
|
||||
"UDP总计=" + strconv.Itoa(udpCount) +
|
||||
"(来源=" + strconv.Itoa(udpSourceCount) +
|
||||
",目标=" + strconv.Itoa(udpTargetCount) + ")")
|
||||
|
||||
waf.TCPConnections.ClosePortConns(netRuntime.Port)
|
||||
waf.UDPConnections.ClosePortConns(netRuntime.Port)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user