mirror of
https://gitee.com/samwaf/SamWaf.git
synced 2026-10-31 02:01:44 +08:00
655 lines
22 KiB
Go
655 lines
22 KiB
Go
package wafenginecore
|
|
|
|
import (
|
|
"SamWaf/global"
|
|
"SamWaf/innerbean"
|
|
"SamWaf/libinjection-go"
|
|
"SamWaf/model"
|
|
"SamWaf/model/wafenginmodel"
|
|
"SamWaf/plugin"
|
|
"SamWaf/utils"
|
|
"SamWaf/utils/zlog"
|
|
"bufio"
|
|
"bytes"
|
|
"compress/flate"
|
|
"compress/gzip"
|
|
"crypto/tls"
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"github.com/lionsoul2014/ip2region/binding/golang/xdb"
|
|
"github.com/satori/go.uuid"
|
|
"github.com/spf13/viper"
|
|
"go.uber.org/zap"
|
|
"golang.org/x/net/html/charset"
|
|
"golang.org/x/text/encoding"
|
|
"golang.org/x/text/encoding/unicode"
|
|
"golang.org/x/text/transform"
|
|
"golang.org/x/time/rate"
|
|
"io"
|
|
"log"
|
|
"net/http"
|
|
"net/http/httputil"
|
|
_ "net/http/pprof"
|
|
"net/url"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
type WafEngine struct {
|
|
//主机情况
|
|
HostTarget map[string]*wafenginmodel.HostSafe
|
|
//主机和code的关系
|
|
HostCode map[string]string
|
|
ServerOnline map[int]innerbean.ServerRunTime
|
|
|
|
//所有证书情况 对应端口 可能多个端口都是https 443,或者其他非标准端口也要实现https证书
|
|
AllCertificate map[int]map[string]*tls.Certificate
|
|
EsHelper utils.EsHelper
|
|
|
|
EngineCurrentStatus int // 当前waf引擎状态
|
|
}
|
|
|
|
func (waf *WafEngine) Error() string {
|
|
fs := "HTTP: %d, HostCode: %d, Message: %s"
|
|
return fmt.Sprintf(fs)
|
|
}
|
|
func (waf *WafEngine) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|
host := r.Host
|
|
if !strings.Contains(host, ":") {
|
|
host = host + ":80"
|
|
}
|
|
defer func() {
|
|
e := recover()
|
|
if e != nil { // 捕获该协程的panic 111111
|
|
fmt.Println("11recover ", e)
|
|
}
|
|
}()
|
|
// 检查域名是否已经注册
|
|
if target, ok := waf.HostTarget[host]; ok {
|
|
|
|
// 获取请求报文的内容长度
|
|
contentLength := r.ContentLength
|
|
|
|
//server_online[8081].Svr.Close()
|
|
var bodyByte []byte
|
|
|
|
// 拷贝一份request的Body ,控制不记录大文件的情况 ,先写死的
|
|
if r.Body != nil && r.Body != http.NoBody && contentLength < (global.GCONFIG_RECORD_MAX_BODY_LENGTH) {
|
|
bodyByte, _ = io.ReadAll(r.Body)
|
|
// 把刚刚读出来的再写进去,不然后面解析表单数据就解析不到了
|
|
r.Body = io.NopCloser(bytes.NewBuffer(bodyByte))
|
|
}
|
|
cookies, _ := json.Marshal(r.Cookies())
|
|
header, _ := json.Marshal(r.Header)
|
|
// 取出客户IP
|
|
ipAndPort := strings.Split(r.RemoteAddr, ":")
|
|
region := utils.GetCountry(ipAndPort[0])
|
|
currentDay, _ := strconv.Atoi(time.Now().Format("20060102"))
|
|
weblogbean := innerbean.WebLog{
|
|
HOST: host,
|
|
URL: r.RequestURI,
|
|
REFERER: r.Referer(),
|
|
USER_AGENT: r.UserAgent(),
|
|
METHOD: r.Method,
|
|
HEADER: string(header),
|
|
COUNTRY: region[0],
|
|
PROVINCE: region[2],
|
|
CITY: region[3],
|
|
SRC_IP: ipAndPort[0],
|
|
SRC_PORT: ipAndPort[1],
|
|
CREATE_TIME: time.Now().Format("2006-01-02 15:04:05"),
|
|
CONTENT_LENGTH: contentLength,
|
|
COOKIES: string(cookies),
|
|
BODY: string(bodyByte),
|
|
REQ_UUID: uuid.NewV4().String(),
|
|
USER_CODE: global.GWAF_USER_CODE,
|
|
HOST_CODE: waf.HostTarget[host].Host.Code,
|
|
TenantId: global.GWAF_TENANT_ID,
|
|
RULE: "",
|
|
ACTION: "通过",
|
|
Day: currentDay,
|
|
}
|
|
|
|
if waf.HostTarget[host].Host.GUARD_STATUS == 1 {
|
|
var jumpGuardFlag = false
|
|
//ip白名单策略(待优化性能)
|
|
if waf.HostTarget[host].IPWhiteLists != nil {
|
|
for i := 0; i < len(waf.HostTarget[host].IPWhiteLists); i++ {
|
|
if waf.HostTarget[host].IPWhiteLists[i].Ip == weblogbean.SRC_IP {
|
|
jumpGuardFlag = true
|
|
break
|
|
}
|
|
}
|
|
}
|
|
//url白名单策略(待优化性能)
|
|
if waf.HostTarget[host].UrlWhiteLists != nil {
|
|
for i := 0; i < len(waf.HostTarget[host].UrlWhiteLists); i++ {
|
|
if (waf.HostTarget[host].UrlWhiteLists[i].CompareType == "等于" && waf.HostTarget[host].UrlWhiteLists[i].Url == weblogbean.URL) ||
|
|
(waf.HostTarget[host].UrlWhiteLists[i].CompareType == "前缀匹配" && strings.HasPrefix(weblogbean.URL, waf.HostTarget[host].UrlWhiteLists[i].Url)) ||
|
|
(waf.HostTarget[host].UrlWhiteLists[i].CompareType == "后缀匹配" && strings.HasSuffix(weblogbean.URL, waf.HostTarget[host].UrlWhiteLists[i].Url)) ||
|
|
(waf.HostTarget[host].UrlWhiteLists[i].CompareType == "包含匹配" && strings.Contains(weblogbean.URL, waf.HostTarget[host].UrlWhiteLists[i].Url)) {
|
|
jumpGuardFlag = true
|
|
break
|
|
}
|
|
}
|
|
}
|
|
//ip黑名单策略(待优化性能)
|
|
if waf.HostTarget[host].IPBlockLists != nil {
|
|
for i := 0; i < len(waf.HostTarget[host].IPBlockLists); i++ {
|
|
if waf.HostTarget[host].IPBlockLists[i].Ip == weblogbean.SRC_IP {
|
|
EchoErrorInfo(w, r, weblogbean, "IP黑名单", "您的访问被阻止了IP限制")
|
|
return
|
|
}
|
|
}
|
|
}
|
|
//url黑名单策略(待优化性能)
|
|
if waf.HostTarget[host].UrlBlockLists != nil {
|
|
for i := 0; i < len(waf.HostTarget[host].UrlBlockLists); i++ {
|
|
if waf.HostTarget[host].UrlBlockLists[i].Url == weblogbean.URL {
|
|
EchoErrorInfo(w, r, weblogbean, "URL黑名单", "您的访问被阻止了URL限制")
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
if jumpGuardFlag == false {
|
|
var sqlFlag = false
|
|
//检测sql注入
|
|
sqliResult, _ := libinjection.IsSQLi(weblogbean.URL)
|
|
if sqliResult {
|
|
sqlFlag = true
|
|
}
|
|
sqliResult, _ = libinjection.IsSQLi(weblogbean.BODY)
|
|
if sqliResult {
|
|
sqlFlag = true
|
|
}
|
|
if sqlFlag == true {
|
|
EchoErrorInfo(w, r, weblogbean, "SQL注入", "请正确访问")
|
|
return
|
|
}
|
|
//检测xss注入
|
|
var xssFlag = false
|
|
sqlixssResult := libinjection.IsXSS(weblogbean.URL)
|
|
if sqlixssResult == true {
|
|
xssFlag = true
|
|
}
|
|
if xssFlag == true {
|
|
EchoErrorInfo(w, r, weblogbean, "XSS跨站注入", "请正确访问")
|
|
return
|
|
}
|
|
//检测xss
|
|
//cc 防护
|
|
if waf.HostTarget[host].PluginIpRateLimiter != nil {
|
|
limiter := waf.HostTarget[host].PluginIpRateLimiter.GetLimiter(weblogbean.SRC_IP)
|
|
if !limiter.Allow() {
|
|
fmt.Println("超量了")
|
|
EchoErrorInfo(w, r, weblogbean, "触发IP频次访问限制1", "您的访问被阻止超量了1")
|
|
return
|
|
}
|
|
}
|
|
if waf.HostTarget[host].Rule != nil {
|
|
ruleMatchs, err := waf.HostTarget[host].Rule.Match("MF", &weblogbean)
|
|
if err == nil {
|
|
if len(ruleMatchs) > 0 {
|
|
|
|
rulestr := ""
|
|
for _, v := range ruleMatchs {
|
|
rulestr = rulestr + v.RuleDescription + ","
|
|
}
|
|
w.Header().Set("WAF", "SAMWAF DROP")
|
|
/*expiration := time.Now()
|
|
expiration = expiration.AddDate(1, 0, 0)
|
|
cookie := http.Cookie{Name: "IDENFY", Value: weblogbean.REQ_UUID, Expires: expiration}
|
|
http.SetCookie(w, &cookie)*/
|
|
//w.Write([]byte("<html><head><title>您的访问被阻止</title></head><body><center><h1>您的访问被阻止触发规则</h1> <br> 访问识别码:<h3>" + weblogbean.REQ_UUID + "</h3></center></body> </html>"))
|
|
EchoErrorInfo(w, r, weblogbean, rulestr, "您的访问被阻止触发规则")
|
|
return
|
|
}
|
|
} else {
|
|
zlog.Debug("规则 ", err)
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
remoteUrl, err := url.Parse(target.TargetHost)
|
|
if err != nil {
|
|
zlog.Debug("target parse fail:", zap.Any("", err))
|
|
return
|
|
}
|
|
|
|
// 直接从缓存取出
|
|
if waf.HostTarget[host].RevProxy != nil {
|
|
waf.HostTarget[host].RevProxy.ServeHTTP(w, r)
|
|
} else {
|
|
proxy := httputil.NewSingleHostReverseProxy(remoteUrl)
|
|
proxy.ModifyResponse = waf.modifyResponse()
|
|
proxy.ErrorHandler = errorHandler()
|
|
waf.HostTarget[host].RevProxy = proxy // 放入缓存
|
|
proxy.ServeHTTP(w, r)
|
|
}
|
|
weblogbean.ACTION = "放行"
|
|
global.GQEQUE_LOG_DB.PushBack(weblogbean)
|
|
return
|
|
} else {
|
|
w.Write([]byte("403: Host forbidden " + host))
|
|
// 获取请求报文的内容长度
|
|
contentLength := r.ContentLength
|
|
|
|
//server_online[8081].Svr.Close()
|
|
var bodyByte []byte
|
|
|
|
// 拷贝一份request的Body ,控制不记录大文件的情况 ,先写死的
|
|
if r.Body != nil && r.Body != http.NoBody && contentLength < (global.GCONFIG_RECORD_MAX_BODY_LENGTH) {
|
|
bodyByte, _ = io.ReadAll(r.Body)
|
|
// 把刚刚读出来的再写进去,不然后面解析表单数据就解析不到了
|
|
r.Body = io.NopCloser(bytes.NewBuffer(bodyByte))
|
|
}
|
|
cookies, _ := json.Marshal(r.Cookies())
|
|
header, _ := json.Marshal(r.Header)
|
|
// 取出客户IP
|
|
ipAndPort := strings.Split(r.RemoteAddr, ":")
|
|
region := utils.GetCountry(ipAndPort[0])
|
|
currentDay, _ := strconv.Atoi(time.Now().Format("20060102"))
|
|
weblogbean := innerbean.WebLog{
|
|
HOST: r.Host,
|
|
URL: r.RequestURI,
|
|
REFERER: r.Referer(),
|
|
USER_AGENT: r.UserAgent(),
|
|
METHOD: r.Method,
|
|
HEADER: string(header),
|
|
COUNTRY: region[0],
|
|
PROVINCE: region[2],
|
|
CITY: region[3],
|
|
SRC_IP: ipAndPort[0],
|
|
SRC_PORT: ipAndPort[1],
|
|
CREATE_TIME: time.Now().Format("2006-01-02 15:04:05"),
|
|
CONTENT_LENGTH: contentLength,
|
|
COOKIES: string(cookies),
|
|
BODY: string(bodyByte),
|
|
REQ_UUID: uuid.NewV4().String(),
|
|
USER_CODE: global.GWAF_USER_CODE,
|
|
HOST_CODE: "",
|
|
TenantId: global.GWAF_TENANT_ID,
|
|
RULE: "",
|
|
ACTION: "通过",
|
|
Day: currentDay,
|
|
}
|
|
weblogbean.ACTION = "禁止"
|
|
global.GQEQUE_LOG_DB.PushBack(weblogbean)
|
|
}
|
|
}
|
|
func EchoErrorInfo(w http.ResponseWriter, r *http.Request, weblogbean innerbean.WebLog, ruleName string, blockInfo string) {
|
|
//通知信息
|
|
noticeStr := fmt.Sprintf("网站域名:%s 访问IP:%s 归属地区:%s 规则:%s 阻止信息:%s", weblogbean.HOST, weblogbean.SRC_IP, utils.GetCountry(weblogbean.SRC_IP), ruleName, blockInfo)
|
|
|
|
zlog.Debug(noticeStr)
|
|
//发送微信推送消息
|
|
global.GQEQUE_MESSAGE_DB.PushBack(innerbean.RuleMessageInfo{
|
|
BaseMessageInfo: innerbean.BaseMessageInfo{OperaType: "命中保护规则", Server: global.GWAF_CUSTOM_SERVER_NAME},
|
|
Domain: weblogbean.HOST,
|
|
RuleInfo: ruleName,
|
|
Ip: fmt.Sprintf("%s (%s)", weblogbean.SRC_IP, utils.GetCountry(weblogbean.SRC_IP)),
|
|
})
|
|
weblogbean.RULE = ruleName
|
|
weblogbean.ACTION = "阻止"
|
|
global.GQEQUE_LOG_DB.PushBack(weblogbean)
|
|
w.Write([]byte("<html><head><title>您的访问被阻止</title></head><body><center><h1>" + blockInfo + "</h1> <br> 访问识别码:<h3>" + weblogbean.REQ_UUID + "</h3></center></body> </html>"))
|
|
zlog.Debug(ruleName)
|
|
}
|
|
func errorHandler() func(http.ResponseWriter, *http.Request, error) {
|
|
return func(w http.ResponseWriter, req *http.Request, err error) {
|
|
zlog.Debug("Got error response:", zap.Any("err", err))
|
|
return
|
|
}
|
|
}
|
|
|
|
func (waf *WafEngine) modifyResponse() func(*http.Response) error {
|
|
return func(resp *http.Response) error {
|
|
resp.Header.Set("WAF", "SamWAF")
|
|
resp.Header.Set("Server", "SamWAF")
|
|
|
|
host := resp.Request.Host
|
|
if !strings.Contains(host, ":") {
|
|
host = host + ":80"
|
|
}
|
|
zlog.Debug("%s %s", resp.Request.Host, resp.Request.RequestURI)
|
|
ldpFlag := false
|
|
//隐私保护(待优化性能)
|
|
for i := 0; i < len(waf.HostTarget[host].LdpUrlLists); i++ {
|
|
if (waf.HostTarget[host].LdpUrlLists[i].CompareType == "等于" && waf.HostTarget[host].LdpUrlLists[i].Url == resp.Request.RequestURI) ||
|
|
(waf.HostTarget[host].LdpUrlLists[i].CompareType == "前缀匹配" && strings.HasPrefix(resp.Request.RequestURI, waf.HostTarget[host].LdpUrlLists[i].Url)) ||
|
|
(waf.HostTarget[host].LdpUrlLists[i].CompareType == "后缀匹配" && strings.HasSuffix(resp.Request.RequestURI, waf.HostTarget[host].LdpUrlLists[i].Url)) ||
|
|
(waf.HostTarget[host].LdpUrlLists[i].CompareType == "包含匹配" && strings.Contains(resp.Request.RequestURI, waf.HostTarget[host].LdpUrlLists[i].Url)) {
|
|
|
|
ldpFlag = true
|
|
break
|
|
}
|
|
}
|
|
if ldpFlag == true {
|
|
//编码转换,自动检测网页编码
|
|
convertCntReader, _ := switchContentEncoding(resp)
|
|
bodyReader := bufio.NewReader(convertCntReader)
|
|
charset := determinePageEncoding(bodyReader)
|
|
|
|
reader := transform.NewReader(bodyReader, charset.NewDecoder())
|
|
oldBytes, err := io.ReadAll(reader)
|
|
if err != nil {
|
|
zlog.Error("error", err)
|
|
return nil
|
|
}
|
|
|
|
// body 追加内容
|
|
//zlog.Debug(string(oldBytes))
|
|
//newPayload := []byte("" + strings.Replace(string(oldBytes), "青岛", "**", -1))
|
|
newPayload := []byte("" + utils.DeSenText(string(oldBytes)))
|
|
|
|
finalBytes, _ := switchReplyContentEncoding(resp, newPayload) //utils.GZipEncode(newPayload)
|
|
//zlog.Debug("转换完",string(finalBytes))
|
|
resp.Body = io.NopCloser(bytes.NewBuffer(finalBytes))
|
|
|
|
// head 修改追加内容
|
|
resp.ContentLength = int64(len(newPayload))
|
|
resp.Header.Set("Content-Length", strconv.FormatInt(int64(len(finalBytes)), 10))
|
|
}
|
|
return nil
|
|
}
|
|
}
|
|
|
|
// 返回是否需要进行压缩
|
|
|
|
func switchReplyContentEncoding(res *http.Response, inputBytes []byte) (respBytes []byte, err error) {
|
|
|
|
switch res.Header.Get("Content-Encoding") {
|
|
case "gzip":
|
|
respBytes, err = utils.GZipEncode(inputBytes)
|
|
case "deflate":
|
|
respBytes, err = utils.DeflateEncode(inputBytes)
|
|
default:
|
|
respBytes = inputBytes
|
|
}
|
|
return
|
|
}
|
|
|
|
// 检测返回的body是否经过压缩,并返回解压的内容
|
|
func switchContentEncoding(res *http.Response) (bodyReader io.Reader, err error) {
|
|
switch res.Header.Get("Content-Encoding") {
|
|
case "gzip":
|
|
bodyReader, err = gzip.NewReader(res.Body)
|
|
case "deflate":
|
|
bodyReader = flate.NewReader(res.Body)
|
|
default:
|
|
bodyReader = res.Body
|
|
}
|
|
return
|
|
}
|
|
|
|
// 检测html页面编码
|
|
func determinePageEncoding(r *bufio.Reader) encoding.Encoding {
|
|
//使用peek读取十分关键,只是偷看一下,不会移动读取位置,否则其他地方就没法读取了
|
|
bytes, err := r.Peek(1024)
|
|
if err != nil {
|
|
log.Printf("Fetcher error: %v\n", err)
|
|
return unicode.UTF8
|
|
}
|
|
e, _, _ := charset.DetermineEncoding(bytes, "")
|
|
return e
|
|
}
|
|
func (waf *WafEngine) Start_WAF() {
|
|
config := viper.New()
|
|
config.AddConfigPath("./conf/") // 文件所在目录
|
|
config.SetConfigName("config") // 文件名
|
|
config.SetConfigType("yml") // 文件类型
|
|
waf.EngineCurrentStatus = 1
|
|
if err := config.ReadInConfig(); err != nil {
|
|
if _, ok := err.(viper.ConfigFileNotFoundError); ok {
|
|
zlog.Error("找不到配置文件..")
|
|
} else {
|
|
zlog.Error("配置文件出错..")
|
|
}
|
|
}
|
|
|
|
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") //读取本地端口
|
|
global.GWAF_CUSTOM_SERVER_NAME = config.GetString("custom_server_name") //本地服务器其定义名称
|
|
zlog.Debug(" load ini: ", global.GWAF_USER_CODE)
|
|
|
|
var hosts []model.Hosts
|
|
|
|
global.GWAF_LOCAL_DB.Where("tenant_id = ? and user_code=? ", global.GWAF_TENANT_ID, global.GWAF_USER_CODE).Where("user_code = ?", global.GWAF_USER_CODE).Find(&hosts)
|
|
|
|
//初始化步骤[加载ip数据库]
|
|
var dbPath = "data/ip2region.xdb"
|
|
// 1、从 dbPath 加载整个 xdb 到内存
|
|
cBuff, err := xdb.LoadContentFromFile(dbPath)
|
|
if err != nil {
|
|
zlog.Info("加载ip库错误")
|
|
zlog.Debug("failed to load content from `%s`: %s\n", dbPath, err)
|
|
return
|
|
}
|
|
global.GCACHE_IP_CBUFF = cBuff
|
|
|
|
//第一步 检测合法性并加入到全局
|
|
for i := 0; i < len(hosts); i++ {
|
|
//检测https
|
|
if hosts[i].Ssl == 1 {
|
|
cert, err := tls.X509KeyPair([]byte(hosts[i].Certfile), []byte(hosts[i].Keyfile))
|
|
if err != nil {
|
|
zlog.Warn("Cannot find %s cert & key file. Error is: %s\n", hosts[i].Host, err)
|
|
continue
|
|
|
|
}
|
|
//all_certificate[hosts[i].Port][hosts[i].Host] = &cert
|
|
mm, ok := waf.AllCertificate[hosts[i].Port] //[hosts[i].Host]
|
|
if !ok {
|
|
mm = make(map[string]*tls.Certificate)
|
|
waf.AllCertificate[hosts[i].Port] = mm
|
|
}
|
|
waf.AllCertificate[hosts[i].Port][hosts[i].Host] = &cert
|
|
}
|
|
_, ok := waf.ServerOnline[hosts[i].Port]
|
|
if ok == false {
|
|
waf.ServerOnline[hosts[i].Port] = innerbean.ServerRunTime{
|
|
ServerType: utils.GetServerByHosts(hosts[i]),
|
|
Port: hosts[i].Port,
|
|
Status: 0,
|
|
}
|
|
}
|
|
|
|
//加载主机对于的规则
|
|
ruleHelper := &utils.RuleHelper{}
|
|
ruleHelper.InitRuleEngine()
|
|
//查询规则
|
|
//TODO 未加租户ID
|
|
var vcnt int
|
|
global.GWAF_LOCAL_DB.Model(&model.Rules{}).Where("host_code = ? and user_code=? ",
|
|
hosts[i].Code, global.GWAF_USER_CODE).Select("sum(rule_version) as vcnt").Row().Scan(&vcnt)
|
|
zlog.Debug("主机host" + hosts[i].Code + " 版本" + strconv.Itoa(vcnt))
|
|
var ruleconfigs []model.Rules
|
|
if vcnt > 0 {
|
|
global.GWAF_LOCAL_DB.Where("host_code = ? and user_code=? ", hosts[i].Code, global.GWAF_USER_CODE).Find(&ruleconfigs)
|
|
ruleHelper.LoadRules(ruleconfigs)
|
|
}
|
|
//查询ip限流(应该针对一个网址只有一个)
|
|
var anticcBean model.AntiCC
|
|
|
|
global.GWAF_LOCAL_DB.Where("host_code=? ", hosts[i].Code).Limit(1).Find(&anticcBean)
|
|
|
|
//初始化插件-ip计数器
|
|
var pluginIpRateLimiter *plugin.IPRateLimiter
|
|
if anticcBean.Id != "" {
|
|
pluginIpRateLimiter = plugin.NewIPRateLimiter(rate.Limit(anticcBean.Rate), anticcBean.Limit)
|
|
}
|
|
|
|
//查询ip白名单
|
|
var ipwhitelist []model.IPWhiteList
|
|
global.GWAF_LOCAL_DB.Where("host_code=? ", hosts[i].Code).Find(&ipwhitelist)
|
|
|
|
//查询url白名单
|
|
var urlwhitelist []model.URLWhiteList
|
|
global.GWAF_LOCAL_DB.Where("host_code=? ", hosts[i].Code).Find(&urlwhitelist)
|
|
|
|
//查询ip黑名单
|
|
var ipblocklist []model.IPBlockList
|
|
global.GWAF_LOCAL_DB.Where("host_code=? ", hosts[i].Code).Find(&ipblocklist)
|
|
|
|
//查询url白名单
|
|
var urlblocklist []model.URLBlockList
|
|
global.GWAF_LOCAL_DB.Where("host_code=? ", hosts[i].Code).Find(&urlblocklist)
|
|
|
|
//查询url隐私保护
|
|
var ldpurls []model.LDPUrl
|
|
global.GWAF_LOCAL_DB.Where("host_code=? ", hosts[i].Code).Find(&ldpurls)
|
|
|
|
//初始化主机host
|
|
hostsafe := &wafenginmodel.HostSafe{
|
|
RevProxy: nil,
|
|
Rule: ruleHelper,
|
|
TargetHost: hosts[i].Remote_host + ":" + strconv.Itoa(hosts[i].Remote_port),
|
|
RuleData: ruleconfigs,
|
|
RuleVersionSum: vcnt,
|
|
Host: hosts[i],
|
|
PluginIpRateLimiter: pluginIpRateLimiter,
|
|
IPWhiteLists: ipwhitelist,
|
|
UrlWhiteLists: urlwhitelist,
|
|
LdpUrlLists: ldpurls,
|
|
IPBlockLists: ipblocklist,
|
|
UrlBlockLists: urlblocklist,
|
|
}
|
|
//赋值到白名单里面
|
|
waf.HostTarget[hosts[i].Host+":"+strconv.Itoa(hosts[i].Port)] = hostsafe
|
|
//赋值到对照表里面
|
|
waf.HostCode[hosts[i].Code] = hosts[i].Host + ":" + strconv.Itoa(hosts[i].Port)
|
|
|
|
}
|
|
|
|
wafSysLog := &model.WafSysLog{
|
|
Id: uuid.NewV4().String(),
|
|
UserCode: global.GWAF_USER_CODE,
|
|
TenantId: global.GWAF_TENANT_ID,
|
|
OpType: "信息",
|
|
OpContent: "WAF启动",
|
|
CreateTime: time.Now(),
|
|
}
|
|
global.GQEQUE_LOG_DB.PushBack(wafSysLog)
|
|
|
|
for _, v := range waf.ServerOnline {
|
|
go func(innruntime innerbean.ServerRunTime) {
|
|
|
|
if (innruntime.ServerType) == "https" {
|
|
|
|
svr := &http.Server{
|
|
Addr: ":" + strconv.Itoa(innruntime.Port),
|
|
Handler: waf,
|
|
TLSConfig: &tls.Config{
|
|
NameToCertificate: make(map[string]*tls.Certificate, 0),
|
|
},
|
|
}
|
|
serclone := waf.ServerOnline[innruntime.Port]
|
|
serclone.Svr = svr
|
|
waf.ServerOnline[innruntime.Port] = serclone
|
|
|
|
svr.TLSConfig.NameToCertificate = waf.AllCertificate[innruntime.Port]
|
|
svr.TLSConfig.GetCertificate = func(clientInfo *tls.ClientHelloInfo) (*tls.Certificate, error) {
|
|
if x509Cert, ok := svr.TLSConfig.NameToCertificate[clientInfo.ServerName]; ok {
|
|
return x509Cert, nil
|
|
}
|
|
return nil, errors.New("config error")
|
|
}
|
|
zlog.Info("启动HTTPS 服务器" + strconv.Itoa(innruntime.Port))
|
|
err = svr.ListenAndServeTLS("", "")
|
|
if err == http.ErrServerClosed {
|
|
zlog.Error("[HTTPServer] https server has been close, cause:[%v]", err)
|
|
} else {
|
|
//TODO 记录如果https 端口被占用的情况 记录日志 且应该推送websocket
|
|
wafSysLog := model.WafSysLog{
|
|
Id: uuid.NewV4().String(),
|
|
UserCode: global.GWAF_USER_CODE,
|
|
TenantId: global.GWAF_TENANT_ID,
|
|
OpType: "系统运行错误",
|
|
OpContent: "HTTPS端口被占用: " + strconv.Itoa(innruntime.Port) + ",请检查",
|
|
CreateTime: time.Time{},
|
|
}
|
|
global.GQEQUE_LOG_DB.PushBack(wafSysLog)
|
|
zlog.Error("[HTTPServer] https server start fail, cause:[%v]", err)
|
|
}
|
|
zlog.Info("server https shutdown")
|
|
|
|
} else {
|
|
defer func() {
|
|
e := recover()
|
|
if e != nil { // 捕获该协程的panic 111111
|
|
zlog.Warn("recover ", e)
|
|
}
|
|
}()
|
|
svr := &http.Server{
|
|
Addr: ":" + strconv.Itoa(innruntime.Port),
|
|
Handler: waf,
|
|
}
|
|
serclone := waf.ServerOnline[innruntime.Port]
|
|
serclone.Svr = svr
|
|
waf.ServerOnline[innruntime.Port] = serclone
|
|
|
|
zlog.Info("启动HTTP 服务器" + strconv.Itoa(innruntime.Port))
|
|
err = svr.ListenAndServe()
|
|
if err == http.ErrServerClosed {
|
|
zlog.Warn("[HTTPServer] http server has been close, cause:[%v]", err)
|
|
} else {
|
|
//TODO 记录如果http 端口被占用的情况 记录日志 且应该推送websocket
|
|
wafSysLog := model.WafSysLog{
|
|
Id: uuid.NewV4().String(),
|
|
UserCode: global.GWAF_USER_CODE,
|
|
TenantId: global.GWAF_TENANT_ID,
|
|
OpType: "系统运行错误",
|
|
OpContent: "HTTP端口被占用: " + strconv.Itoa(innruntime.Port) + ",请检查",
|
|
CreateTime: time.Time{},
|
|
}
|
|
global.GQEQUE_LOG_DB.PushBack(wafSysLog)
|
|
zlog.Error("[HTTPServer] http server start fail, cause:[%v]", err)
|
|
}
|
|
zlog.Info("server http shutdown")
|
|
}
|
|
|
|
}(v)
|
|
|
|
}
|
|
}
|
|
|
|
// 关闭waf
|
|
func (waf *WafEngine) CLoseWAF() {
|
|
defer func() {
|
|
e := recover()
|
|
if e != nil { // 捕获该协程的panic 111111
|
|
zlog.Debug("关闭 recover ", e)
|
|
}
|
|
}()
|
|
wafSysLog := &model.WafSysLog{
|
|
Id: uuid.NewV4().String(),
|
|
UserCode: global.GWAF_USER_CODE,
|
|
TenantId: global.GWAF_TENANT_ID,
|
|
OpType: "信息",
|
|
OpContent: "WAF关闭",
|
|
CreateTime: time.Now(),
|
|
}
|
|
global.GQEQUE_LOG_DB.PushBack(wafSysLog)
|
|
waf.EngineCurrentStatus = 0
|
|
for _, v := range waf.ServerOnline {
|
|
if v.Svr != nil {
|
|
v.Svr.Close()
|
|
}
|
|
}
|
|
|
|
//重置信息
|
|
|
|
waf.HostTarget = map[string]*wafenginmodel.HostSafe{}
|
|
waf.HostCode = map[string]string{}
|
|
waf.ServerOnline = map[int]innerbean.ServerRunTime{}
|
|
waf.AllCertificate = map[int]map[string]*tls.Certificate{}
|
|
|
|
}
|