Files
2026-08-24 13:39:05 +08:00

394 lines
20 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package global
import (
"SamWaf/cache"
"SamWaf/common/gwebsocket"
"SamWaf/common/queue"
"SamWaf/iplocation"
"SamWaf/model"
"SamWaf/model/spec"
"SamWaf/wafai"
"SamWaf/wafnotify"
"SamWaf/wafowasp"
"SamWaf/wafsnowflake"
"strconv"
"sync/atomic"
"time"
"github.com/bytedance/godlp/dlpheader"
"github.com/lionsoul2014/ip2region/binding/golang/xdb"
"github.com/oschwald/geoip2-golang"
"gorm.io/gorm"
)
const (
GWAF_NAME = "SamWaf"
Version_num = 1
)
var (
/*本机信息**/
GWAF_RUNTIME_IP string = "127.0.0.1" //本机当前外网IP
GWAF_RUNTIME_AREA string = "" //本机当前所在区域
GWAF_RUNTIME_SERVER_TYPE bool = false //当前是是否以服务形式启动
GWAF_RUNTIME_IS_TAKEOVER bool = false //当前 Worker 是否以升级接管(takeover)方式启动:与旧 Worker 并存,需跳过整库备份等仅首启动作
// 调试:在响应头 X-SamWaf-Worker 标记处理请求的 Worker 进程,用于验证升级时新旧 Worker 交替。
// 仅当环境变量 SAMWAF_WORKER_HEADER=1 时开启(默认关闭,避免对外暴露 PID)。
GWAF_DEBUG_WORKER_HEADER bool = false
GWAF_WORKER_TAG string = "" //当前进程标识(pid+启动时间)
GWAF_RUNTIME_NEW_VERSION string = "" //最新版本号
GWAF_RUNTIME_NEW_VERSION_DESC string = "" //最新版本描述
GWAF_RUNTIME_WIN7_VERSION string = "false" //是否是win7内部版本
GWAF_RUNTIME_QPS uint64 = 0 //当前qps (累积值)
GWAF_RUNTIME_LOG_PROCESS uint64 = 0 //log 处理速度 (累积值)
// QPS实时计算相关变量 (用于差分计算)
GWAF_LAST_QPS_VALUE uint64 = 0 //上次QPS计数值
GWAF_LAST_LOG_QPS_VALUE uint64 = 0 //上次日志QPS计数值
GWAF_LAST_QPS_TIME int64 = 0 //上次QPS计算时间
GWAF_CURRENT_REALTIME_QPS uint64 = 0 //当前实时QPS
GWAF_CURRENT_REALTIME_LOG_QPS uint64 = 0 //当前实时日志QPS
GWAF_RUNTIME_DNS_SERVER string = "119.29.29.29" //反向查询DNS的IP
GWAF_RUNTIME_DNS_TIMEOUT int64 = 500 // DNS 查询超时时间 单位毫秒
GWAF_RUNTIME_RECORD_LOG_TYPE string = "all" // 记录日志形式: 全部(all),非正常(abnormal)
GWAF_RUNTIME_IS_UPDATETING bool = false //是否正在升级中
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_PROCESS_START_TIME time.Time = time.Now() //当前进程启动时间(包加载即赋值)
GWAF_RUNTIME_SSL_EXPIRE_CHECK bool = false //SSL过期检测是否正在运行
GWAF_RUNTIME_SSL_SYNC_HOST bool = false //主机同步信息到过期检测是否正在运行
/**
遥测数据
*/
GWAF_MEASURE_PROCESS_DEQUEENGINE cache.WafOnlyLockWriteData //遥测数据-队列处理时间
GWAF_GLOBAL_HOST_NAME string = "全局网站:0" //全局网站
GWAF_GLOBAL_HOST_CODE string = "0" //全局网站代码
GWAF_LOCAL_DB *gorm.DB //通用本地数据库,尊重用户隐私
GWAF_LOCAL_LOG_DB *gorm.DB //通用本地数据库存日志数据,尊重用户隐私
GWAF_LOCAL_STATS_DB *gorm.DB //通用本地数据库存放统计数据,尊重用户隐私
GWAF_REMOTE_DB *gorm.DB //仅当用户使用云数据库
GWAF_LOCAL_SERVER_PORT int = 26666 // 本地local端口
GWAF_LOCAL_INDEX_HTML string //本地首页HTML信息
GWAF_USER_CODE string // 当前识别号
GWAF_CUSTOM_SERVER_NAME string // 当前服务器自定义名称
GWAF_TENANT_ID string = "SamWafCom" // 当前租户ID
//管理端访问控制
GWAF_IP_WHITELIST string = "0.0.0.0/0,::/0" //IP白名单 后台默认放行所有
GWAF_DOMAIN_WHITELIST string = "" //域名白名单,为空时不限制,多个用逗号分隔
GWAF_SSL_ENABLE bool = false //是否启用SSL证书
GWAF_SSL_FORCE_HTTPS bool = false //管理端是否仅允许HTTPS访问(开启后纯HTTP请求301跳转到HTTPS),默认HTTP/HTTPS都允许
GWAF_SSL_BIND_CERT_ID string = "" //管理端证书绑定的证书夹(SslConfig)ID,为空表示未绑定(使用手动上传的证书)
GWAF_SECURITY_ENTRY_ENABLE bool = false //是否启用安全路径入口
GWAF_SECURITY_ENTRY_PATH string = "" //安全路径(18位随机码)
GWAF_SECURITY_EMERGENCY_PATH string = "" //应急恢复路径(随机生成,首次启动自动写入 conf/config.yml
//zlog 日志相关信息
GWAF_LOG_OUTPUT_FORMAT string = "console" //zlog输出格式 控制台格式console,json格式
GWAF_LOG_DEBUG_ENABLE bool = false //是否开启debug日志,默认关闭
GWAF_LOG_DEBUG_DB_ENABLE bool = false //是否开启数据库日志,默认关闭
GWAF_RELEASE string = "false" // 当前是否为发行版
GWAF_RELEASE_VERSION_NAME string = "20241028" // 发行版的版本号名称
GWAF_RELEASE_VERSION string = "v1.0.0" // 发行版的版本号
GWAF_LAST_UPDATE_TIME time.Time // 上次时间
GWAF_NOTICE_ENABLE bool = false // 是否开启通知
GWAF_NOTICE_TITLE string // 通知消息标题前缀(用于区分多实例,默认使用 custom_server_name
GWAF_CAN_EXPORT_DOWNLOAD_LOG bool = false //是否可以导出下载日志
GWAF_CAN_APP_MANAGE bool = false //是否启用应用管理功能(默认关闭,需服务端配置才能开启)
GWAF_APP_ALLOW_DIRS string = "data/applications" //应用允许的工作目录(逗号分隔多路径)
GWAF_APP_OP_PASSWORD string //应用操作密码(高危操作二次确认,自动生成存 config.yml)
GWAF_DLP dlpheader.EngineAPI // 脱敏引擎
GWAF_DLP_CONFIG string // 脱敏引擎配置数据
GWAF_AI_DETECTOR *wafai.Detector // AI智能检测器(持有当前模型,失败安全)
GWAF_OWASP *wafowasp.WafOWASP //owasp引擎(兼容保留:由 GWAF_OWASP_MANAGER.Current() 更新;请勿直接赋值新实例,否则热重载会失效)
GWAF_OWASP_MANAGER *wafowasp.OwaspManager //owasp 管理器(支持热重载)
/**链聚合**/
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) //证书处理链
GWAF_CHAN_SSLOrder = make(chan spec.ChanSslOrder, 10) //SSL证书申请
GWAF_CHAN_SSL_EXPIRE_CHECK = make(chan int, 10) //SSL证书到期检测
GWAF_CHAN_SYNC_HOST_TO_SSL_EXPIRE = make(chan int, 10) //同步已存在主机到SSL证书检测任务里
GWAF_CHAN_TASK = make(chan string, 10) //手工执行任务
GWAF_CHAN_TASK_RELOAD = make(chan model.Task, 10) //任务重新加载
GWAF_CHAN_CLEAR_CC_WINDOWS = make(chan int, 10) //清除cc缓存信息
GWAF_CHAN_CLEAR_CC_IP = make(chan string, 10) //清除cc缓存信息IP
GWAF_QUEUE_SHUTDOWN_SIGNAL chan struct{} = make(chan struct{}) // 队列关闭信号
GWAF_CHAN_MANAGER_RESTART = make(chan int, 1) // 管理端重启信号
GWAF_CHAN_HTTP3 = make(chan int, 1) // HTTP/3 开关变更信号(配置热生效)
GWAF_SHUTDOWN_SIGNAL bool = false // 系统关闭信号
/*****CACHE相关*********/
GCACHE_WAFCACHE cache.CacheStore //cache
GCACHE_WECHAT_ACCESS string = "" //微信访问密钥
// Cache后端配置
GCACHE_TYPE string = "memory" // 缓存类型: memory(默认)| redis
GCACHE_REDIS_HOST string = "127.0.0.1"
GCACHE_REDIS_PORT int = 6379
GCACHE_REDIS_PASSWORD string = ""
GCACHE_REDIS_DB int = 0
/*********HTTP相关**************/
GWAF_HTTP_SENSITIVE_REPLACE_STRING = "**" //HTTP 敏感内容替换成
/*********IP相关**************/
GCACHE_IP_CBUFF []byte // IP相关缓存 (已废弃,由 GIPLOCATION_MANAGER 管理)
GCACHE_IP_V6_COUNTRY_CBUFF []byte // IPv6国家相关缓存 (已废弃,由 GIPLOCATION_MANAGER 管理)
GCACHE_IPV4_SEARCHER *xdb.Searcher //IPV4得查询器 (已废弃,由 GIPLOCATION_MANAGER 管理)
GCACHE_IPV6_SEARCHER *geoip2.Reader // IPV6得查询器 (已废弃,由 GIPLOCATION_MANAGER 管理)
// IP 数据库配置
GCONFIG_IP_V4_SOURCE string = "ip2region" // IPv4 数据来源: ip2region / geolite2ip2region.xdb 随程序内置)
GCONFIG_IP_V6_SOURCE string = "ip2region" // IPv6 数据来源: ip2region / geolite2
// 注:IPv6 默认自 1.3.24-beta.6 由 geolite2 改为 ip2region —— GeoLite2 受 MaxMind 再分发授权限制,
// 已不再随程序内置。ip2region_v6.xdb 需在【IP库管理】在线下载或自行放入 data/。
// 只影响新装;老用户 sys_config 里已有的值不动,由 ReloadFromConfig 做运行时降级。
GCONFIG_IP_V4_FORMAT string = "legacy" // IPv4 xdb 字段格式
GCONFIG_IP_V6_FORMAT string = "opensource" // IPv6 xdb 字段格式(仅 ip2region 时有效)
// IP Location Manager 全局实例
GIPLOCATION_MANAGER *iplocation.Manager
GDATA_DELETE_INTERVAL int64 = 180 // 删除180天前的数据
/****队列相关*****/
GQEQUE_DB *queue.Queue //正常DB队列
GQEQUE_UPDATE_DB *queue.Queue //正常DB 更新队列
GQEQUE_LOG_DB *queue.Queue //日志DB队列
GQEQUE_STATS_DB *queue.Queue //统计DB队列
GQEQUE_STATS_UPDATE_DB *queue.Queue //统计更新DB队列
GQEQUE_MESSAGE_DB *queue.Queue //发送消息队列
/*******通知相关*************/
GNOTIFY_KAKFA_SERVICE *wafnotify.WafNotifyService //通知服务
GNOTIFY_LOG_FILE_WRITER *wafnotify.WafNotifyService //日志文件写入服务
GNOTIFY_SEND_MAX_LIMIT_MINTUTES = time.Duration(5) * time.Minute // 规则相关信息最大发送抑止 默认5分钟
/*******日志记录相关*************/
GWEBLOG_VERSION = 20250112 //weblog 日志版本号
/*********SSL相关*************/
GSSL_HTTP_CHANGLE_PATH string = "/.well-known/acme-challenge/" // http01证书验证路径
/******数据库处理参数*****/
GDATA_BATCH_INSERT int64 = 100 //最大批量插入数量
GDATA_SHARE_DB_SIZE int64 = 100 * 10000 //100w 进行分库 100*10000
GDATA_SHARE_DB_FILE_SIZE int64 = 1024 //1024M 进行分库
GDATA_CURRENT_CHANGE bool = false //当前是否正在切换
GDATA_IP_TAG_DB int64 = 0 //IP Tag 存放位置 0 是主库 1是读取 stat库
// GCONFIG_ATTACK_TAG_EXCLUDE 风险日志里不算风险的标签(逗号分隔)。"正常"永远排除,不用写这里。
// 这些标签既不出现在规则筛选列表里,也不计入「阻止数量」,而是算作放行。
GCONFIG_ATTACK_TAG_EXCLUDE string = "ACME证书校验,静态文件访问成功"
GDATA_CURRENT_LOG_DB_MAP map[string]*gorm.DB //当前自定义的数据库连接 TODO 如果用户打开了多个 会不会影响内存
/******WebSocket*********/
GWebSocket *gwebsocket.WebSocketOnline
//升级相关
GUPDATE_VERSION_URL string = "https://update.samwaf.com/" // 官方下载
GUPDATE_GITHUB_VERSION_URL string = "https://update.samwaf.com/samwaf_beta_update/latest_release.json" //从gitHub自动下载镜像
//GUPDATE_GITHUB_VERSION_URL string = "http://10.0.2.2:8111/beta_update_linux/latest_release.json"
// http://127.0.0.1:8111/beta_update/latest_release.json
// http://10.0.2.2:8111/beta_update_linux/latest_release.json
GWAF_SNOWFLAKE_GEN *wafsnowflake.Snowflake //雪花算法
//任务开关信息
GWAF_SWITCH_TASK_COUNTER bool
//数据密码
GWAF_PWD_COREDB = "3Y)(27EtO^tK8Bj~CORE" //加密
GWAF_PWD_STATDB = "3Y)(27EtO^tK8Bj~STAT" //加密
GWAF_PWD_LOGDB = "3Y)(27EtO^tK8Bj~LOG" //加密
// Database driver: "sqlite" | "mysql" | "postgres" (default: sqlite)
GWAF_DB_DRIVER string = "sqlite"
// MySQL connection config (populated from conf/config.yml database.mysql.*)
GWAF_MYSQL_HOST string = "127.0.0.1"
GWAF_MYSQL_PORT int = 3306
GWAF_MYSQL_USER string = "samwaf"
GWAF_MYSQL_PASSWORD string = ""
GWAF_MYSQL_CHARSET string = "utf8mb4"
GWAF_MYSQL_CORE_DB string = "samwaf_core"
GWAF_MYSQL_LOG_DB string = "samwaf_log"
GWAF_MYSQL_STATS_DB string = "samwaf_stats"
GWAF_MYSQL_MAX_OPEN_CONNS int = 50
GWAF_MYSQL_MAX_IDLE_CONNS int = 10
GWAF_MYSQL_CONN_MAX_LIFETIME_MINUTES int = 60
// PostgreSQL connection config (populated from conf/config.yml database.postgres.*)
GWAF_PG_HOST string = "127.0.0.1"
GWAF_PG_PORT int = 5432
GWAF_PG_USER string = "postgres"
GWAF_PG_PASSWORD string = ""
GWAF_PG_SSLMODE string = "disable"
// GWAF_PG_TIMEZONE pins the session time zone. It is load-bearing, not cosmetic:
// timestamptz values come back in the session zone and customtype.JsonTime renders
// whatever Location it is handed, so a wrong value silently shifts every timestamp
// shown in the UI. Overridable via database.postgres.timezone.
GWAF_PG_TIMEZONE string = "Asia/Shanghai"
// GWAF_PG_MAINTENANCE_DB is the database connected to in order to CREATE DATABASE
// (PostgreSQL cannot create a database from a connection to that same database).
GWAF_PG_MAINTENANCE_DB string = "postgres"
GWAF_PG_CORE_DB string = "samwaf_core"
GWAF_PG_LOG_DB string = "samwaf_log"
GWAF_PG_STATS_DB string = "samwaf_stats"
GWAF_PG_MAX_OPEN_CONNS int = 50
GWAF_PG_MAX_IDLE_CONNS int = 10
GWAF_PG_CONN_MAX_LIFETIME_MINUTES int = 60
//默认创建的账户和密码
GWAF_DEFAULT_ACCOUNT string = "admin" //默认创建的账户
GWAF_DEFAULT_ACCOUNT_PWD string = "admin868" //历史默认口令:新装已改为随机口令(见 InitDefaultAccount),此值仅作随机生成失败兜底与存量识别
GWAF_DEFAULT_ACCOUNT_SALT string = "%@311*abDop#*" //盐值(仅用于校验/识别存量 MD5,bcrypt 迁移后新账户不再使用)
//通讯加密
GWAF_COMMUNICATION_KEY = []byte("7E@u*has$d*@s5YX") //通讯加密密钥
//资源下载
GWAF_BOT_IP_URL_MAIN string = "https://raw.githubusercontent.com/samwafgo/SamWafBotIPDatabase/main/allowlist/index.json"
/**
授权注册信息部分
*/
GWAF_REG_INFO model.RegistrationInfo //当前注册信息
GWAF_REG_VERSION = "v1" //注册信息版本
GWAF_REG_KEY = []byte("5F!vion$k@a7QZ&)") //注册信息加密密钥
GWAF_REG_PUBLIC_KEY string = "" //注册信息加密公钥
GWAF_REG_TMP_REG []byte //用户传来的信息
)
func GetCurrentVersionInt() int {
version, _ := strconv.Atoi(GWAF_RELEASE_VERSION)
return version
}
// GetRealtimeQPS 获取实时QPS (基于差分计算)
func GetRealtimeQPS() uint64 {
return atomic.LoadUint64(&GWAF_CURRENT_REALTIME_QPS)
}
// GetRealtimeLogQPS 获取实时日志处理QPS (基于差分计算)
func GetRealtimeLogQPS() uint64 {
return atomic.LoadUint64(&GWAF_CURRENT_REALTIME_LOG_QPS)
}
// GetCumulativeQPS 获取累积QPS (用于统计计算)
func GetCumulativeQPS() uint64 {
return atomic.LoadUint64(&GWAF_RUNTIME_QPS)
}
// GetCumulativeLogQPS 获取累积日志处理QPS (用于统计计算)
func GetCumulativeLogQPS() uint64 {
return atomic.LoadUint64(&GWAF_RUNTIME_LOG_PROCESS)
}
// IncrementQPS 增加QPS计数 (只增加累积计数)
func IncrementQPS() {
atomic.AddUint64(&GWAF_RUNTIME_QPS, 1)
}
// IncrementLogQPS 增加日志处理QPS计数 (只增加累积计数)
func IncrementLogQPS() {
atomic.AddUint64(&GWAF_RUNTIME_LOG_PROCESS, 1)
}
// NotifyHTTP3ConfigChanged 投递一次 HTTP/3 配置变更信号,让引擎重新对齐 QUIC 监听。
//
// cap=1 + select/default:连续多次改动只留一次待处理信号(reconcile 本身幂等)
// 且在引擎/主循环尚未就绪时(启动期加载配置)也绝不会阻塞调用方 ——
// 信号会留在缓冲里,等主循环起来后被消费。
func NotifyHTTP3ConfigChanged() {
select {
case GWAF_CHAN_HTTP3 <- 1:
default:
}
}
// UpdateRealtimeQPS 更新实时QPS (基于差分计算,定时调用)
func UpdateRealtimeQPS() {
currentTime := time.Now().Unix()
currentQPS := atomic.LoadUint64(&GWAF_RUNTIME_QPS)
currentLogQPS := atomic.LoadUint64(&GWAF_RUNTIME_LOG_PROCESS)
lastTime := atomic.LoadInt64(&GWAF_LAST_QPS_TIME)
lastQPS := atomic.LoadUint64(&GWAF_LAST_QPS_VALUE)
lastLogQPS := atomic.LoadUint64(&GWAF_LAST_LOG_QPS_VALUE)
// 边界检查和安全计算
if lastTime > 0 && currentTime > lastTime {
timeDiff := currentTime - lastTime
// 防止时间差过小导致的计算不准确
if timeDiff >= 1 {
var realQPS, realLogQPS uint64
// 安全的差值计算,处理可能的溢出情况
if currentQPS >= lastQPS {
qpsDiff := currentQPS - lastQPS
realQPS = qpsDiff / uint64(timeDiff)
} else {
// 处理uint64溢出的情况
// 当计数器溢出时,使用当前值作为差值的近似
realQPS = currentQPS / uint64(timeDiff)
}
if currentLogQPS >= lastLogQPS {
logQpsDiff := currentLogQPS - lastLogQPS
realLogQPS = logQpsDiff / uint64(timeDiff)
} else {
// 处理uint64溢出的情况
realLogQPS = currentLogQPS / uint64(timeDiff)
}
// 设置合理的QPS上限,防止异常值
const MaxReasonableQps = 10000000 // 1000万QPS上限
if realQPS > MaxReasonableQps {
realQPS = MaxReasonableQps
}
if realLogQPS > MaxReasonableQps {
realLogQPS = MaxReasonableQps
}
atomic.StoreUint64(&GWAF_CURRENT_REALTIME_QPS, realQPS)
atomic.StoreUint64(&GWAF_CURRENT_REALTIME_LOG_QPS, realLogQPS)
// 留一份采样历史,首页QPS卡片的趋势图直接读内存,不落库
AppendQPSSample(currentTime, realQPS)
}
// 如果时间差小于1秒,保持之前的QPS值不变
} else {
// 初始化情况或时间异常,设置QPS为0
atomic.StoreUint64(&GWAF_CURRENT_REALTIME_QPS, 0)
atomic.StoreUint64(&GWAF_CURRENT_REALTIME_LOG_QPS, 0)
}
// 更新记录值
atomic.StoreUint64(&GWAF_LAST_QPS_VALUE, currentQPS)
atomic.StoreUint64(&GWAF_LAST_LOG_QPS_VALUE, currentLogQPS)
atomic.StoreInt64(&GWAF_LAST_QPS_TIME, currentTime)
}
// GetIPTagDB 根据配置获取IP标签数据库连接
func GetIPTagDB() *gorm.DB {
if GDATA_IP_TAG_DB == 1 {
return GWAF_LOCAL_STATS_DB // 使用统计数据库
}
return GWAF_LOCAL_DB // 默认使用主数据库
}