Files
waf-platform/EdgeAPI/internal/setup/sql_upgrade.go
2026-02-27 10:35:22 +08:00

1285 lines
39 KiB
Go
Raw 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 setup
import (
"encoding/json"
"github.com/TeaOSLab/EdgeAPI/internal/acme"
"github.com/TeaOSLab/EdgeAPI/internal/db/models"
"github.com/TeaOSLab/EdgeAPI/internal/db/models/stats"
"github.com/TeaOSLab/EdgeAPI/internal/errors"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/firewallconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/shared"
"github.com/TeaOSLab/EdgeCommon/pkg/systemconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/userconfigs"
"github.com/iwind/TeaGo/dbs"
"github.com/iwind/TeaGo/lists"
"github.com/iwind/TeaGo/maps"
"github.com/iwind/TeaGo/rands"
"github.com/iwind/TeaGo/types"
"strconv"
"strings"
)
type upgradeVersion struct {
version string
f func(db *dbs.DB) error
}
var upgradeFuncs = []*upgradeVersion{
{
"0.0.3", upgradeV0_0_3,
},
{
"0.0.5", upgradeV0_0_5,
},
{
"0.0.6", upgradeV0_0_6,
},
{
"0.0.9", upgradeV0_0_9,
},
{
"0.0.10", upgradeV0_0_10,
},
{
"0.2.5", upgradeV0_2_5,
},
{
"0.2.8.1", upgradeV0_2_8_1,
},
{
"0.3.0", upgradeV0_3_0,
},
{
"0.3.1", upgradeV0_3_1,
},
{
"0.3.2", upgradeV0_3_2,
},
{
"0.3.3", upgradeV0_3_3,
},
{
"0.3.7", upgradeV0_3_7,
},
{
"0.4.0", upgradeV0_4_0,
},
{
"0.4.1", upgradeV0_4_1,
},
{
"0.4.5", upgradeV0_4_5,
},
{
"0.4.7", upgradeV0_4_7,
},
{
"0.4.8", upgradeV0_4_8,
},
{
"0.4.9", upgradeV0_4_9,
},
{
"0.4.11", upgradeV0_4_11,
},
{
"0.5.3", upgradeV0_5_3,
},
{
"0.5.6", upgradeV0_5_6,
},
{
"0.5.8", upgradeV0_5_8,
},
{
"1.2.1", upgradeV1_2_1,
},
{
"1.2.9", upgradeV1_2_9,
},
{
"1.2.10", upgradeV1_2_10,
},
{
"1.3.2", upgradeV1_3_2,
},
{
"1.3.4", upgradeV1_3_4,
},
{
"1.4.4", upgradeV1_4_4,
},
{
"1.4.8", upgradeV1_4_8,
},
}
// UpgradeSQLData 升级SQL数据
func UpgradeSQLData(db *dbs.DB) error {
version, err := db.FindCol(0, "SELECT version FROM edgeVersions")
if err != nil {
return err
}
var versionString = types.String(version)
if len(versionString) > 0 {
for _, f := range upgradeFuncs {
if CompareVersion(versionString, f.version) >= 0 {
continue
}
err = f.f(db)
if err != nil {
return err
}
}
}
return nil
}
// v0.0.3
func upgradeV0_0_3(db *dbs.DB) error {
// 获取第一个管理员
adminIdCol, err := db.FindCol(0, "SELECT id FROM edgeAdmins ORDER BY id ASC LIMIT 1")
if err != nil {
return err
}
adminId := types.Int64(adminIdCol)
if adminId <= 0 {
return errors.New("'edgeAdmins' table should not be empty")
}
// 升级edgeDNSProviders
_, err = db.Exec("UPDATE edgeDNSProviders SET adminId=? WHERE adminId=0 AND userId=0", adminId)
if err != nil {
return err
}
// 升级edgeDNSDomains
_, err = db.Exec("UPDATE edgeDNSDomains SET adminId=? WHERE adminId=0 AND userId=0", adminId)
if err != nil {
return err
}
// 升级edgeSSLCerts
_, err = db.Exec("UPDATE edgeSSLCerts SET adminId=? WHERE adminId=0 AND userId=0", adminId)
if err != nil {
return err
}
// 升级edgeNodeClusters
_, err = db.Exec("UPDATE edgeNodeClusters SET adminId=? WHERE adminId=0 AND userId=0", adminId)
if err != nil {
return err
}
// 升级edgeNodes
_, err = db.Exec("UPDATE edgeNodes SET adminId=? WHERE adminId=0 AND userId=0", adminId)
if err != nil {
return err
}
// 升级edgeNodeGrants
_, err = db.Exec("UPDATE edgeNodeGrants SET adminId=? WHERE adminId=0", adminId)
if err != nil {
return err
}
return nil
}
// v0.0.5
func upgradeV0_0_5(db *dbs.DB) error {
// 升级edgeACMETasks
_, err := db.Exec("UPDATE edgeACMETasks SET authType=? WHERE authType IS NULL OR LENGTH(authType)=0", acme.AuthTypeDNS)
if err != nil {
return err
}
return nil
}
// v0.0.6
func upgradeV0_0_6(db *dbs.DB) error {
stmt, err := db.Prepare("SELECT COUNT(*) FROM edgeAPITokens WHERE role='user'")
if err != nil {
return err
}
defer func() {
_ = stmt.Close()
}()
col, err := stmt.FindCol(0)
if err != nil {
return err
}
count := types.Int(col)
if count > 0 {
return nil
}
nodeId := rands.HexString(32)
secret := rands.String(32)
_, err = db.Exec("INSERT INTO edgeAPITokens (nodeId, secret, role) VALUES (?, ?, ?)", nodeId, secret, "user")
if err != nil {
return err
}
return nil
}
// v0.0.9
func upgradeV0_0_9(db *dbs.DB) error {
// firewall policies
var tx *dbs.Tx
dbs.NotifyReady()
policies, err := models.NewHTTPFirewallPolicyDAO().FindAllEnabledFirewallPolicies(tx)
if err != nil {
return err
}
for _, policy := range policies {
if policy.ServerId > 0 {
continue
}
policyId := int64(policy.Id)
webIds, err := models.NewHTTPWebDAO().FindAllWebIdsWithHTTPFirewallPolicyId(tx, policyId)
if err != nil {
return err
}
serverIds := []int64{}
for _, webId := range webIds {
serverId, err := models.NewServerDAO().FindEnabledServerIdWithWebId(tx, webId)
if err != nil {
return err
}
if serverId > 0 && !lists.ContainsInt64(serverIds, serverId) {
serverIds = append(serverIds, serverId)
}
}
if len(serverIds) == 1 {
err = models.NewHTTPFirewallPolicyDAO().UpdateFirewallPolicyServerId(tx, policyId, serverIds[0])
if err != nil {
return err
}
}
}
return nil
}
// v0.0.10
func upgradeV0_0_10(db *dbs.DB) error {
return nil
}
// v0.2.5
func upgradeV0_2_5(db *dbs.DB) error {
// 鏇存柊鐢ㄦ埛
_, err := db.Exec("UPDATE edgeUsers SET day=FROM_UNIXTIME(createdAt,'%Y%m%d') WHERE day IS NULL OR LENGTH(day)=0")
if err != nil {
return err
}
// 更新防火墙规则
ones, _, err := db.FindOnes("SELECT id, actions, action, actionOptions FROM edgeHTTPFirewallRuleSets WHERE actions IS NULL OR LENGTH(actions)=0")
if err != nil {
return err
}
for _, one := range ones {
oneId := one.GetInt64("id")
action := one.GetString("action")
options := one.GetString("actionOptions")
var optionsMap = maps.Map{}
if len(options) > 0 {
_ = json.Unmarshal([]byte(options), &optionsMap)
}
var actions = []*firewallconfigs.HTTPFirewallActionConfig{
{
Code: action,
Options: optionsMap,
},
}
actionsJSON, err := json.Marshal(actions)
if err != nil {
return err
}
_, err = db.Exec("UPDATE edgeHTTPFirewallRuleSets SET actions=? WHERE id=?", string(actionsJSON), oneId)
if err != nil {
return err
}
}
return nil
}
// v0.3.0
func upgradeV0_3_0(db *dbs.DB) error {
// 升级健康检查
ones, _, err := db.FindOnes("SELECT id,healthCheck FROM edgeNodeClusters WHERE state=1")
if err != nil {
return err
}
for _, one := range ones {
var clusterId = one.GetInt64("id")
var healthCheck = one.GetString("healthCheck")
if len(healthCheck) == 0 {
continue
}
var config = &serverconfigs.HealthCheckConfig{}
err = json.Unmarshal([]byte(healthCheck), config)
if err != nil {
continue
}
if config.CountDown <= 1 {
config.CountDown = 3
configJSON, err := json.Marshal(config)
if err != nil {
continue
}
_, err = db.Exec("UPDATE edgeNodeClusters SET healthCheck=? WHERE id=?", string(configJSON), clusterId)
if err != nil {
return err
}
}
}
return nil
}
// v0.3.1
func upgradeV0_3_1(db *dbs.DB) error {
// 娓呯┖鍩熷悕缁熻锛屽凡浣跨敤鍒嗚〃浠f浛
// 鍥犱负鍙兘鏈夋潈闄愰棶棰橈紝鎵€浠ユ垜浠拷鐣ラ敊璇? _, _ = db.Exec("TRUNCATE table edgeServerDomainHourlyStats")
// 鍗囩骇APIToken
ones, _, err := db.FindOnes("SELECT uniqueId,secret FROM edgeNodeClusters")
if err != nil {
return err
}
for _, one := range ones {
var uniqueId = one.GetString("uniqueId")
var secret = one.GetString("secret")
tokenOne, err := db.FindOne("SELECT id FROM edgeAPITokens WHERE nodeId=? LIMIT 1", uniqueId)
if err != nil {
return err
}
if len(tokenOne) == 0 {
_, err = db.Exec("INSERT INTO edgeAPITokens (nodeId, secret, role, state) VALUES (?, ?, 'cluster', 1)", uniqueId, secret)
if err != nil {
return err
}
}
}
return nil
}
// v0.3.2
func upgradeV0_3_2(db *dbs.DB) error {
// gzip => compression
type HTTPGzipRef struct {
IsPrior bool `yaml:"isPrior" json:"isPrior"` // 鏄惁瑕嗙洊
IsOn bool `yaml:"isOn" json:"isOn"` // 是否开启
GzipId int64 `yaml:"gzipId" json:"gzipId"` // 使用的配置ID
}
webOnes, _, err := db.FindOnes("SELECT id, gzip FROM edgeHTTPWebs WHERE gzip IS NOT NULL AND compression IS NULL")
if err != nil {
return err
}
for _, webOne := range webOnes {
var gzipRef = &HTTPGzipRef{}
err = json.Unmarshal([]byte(webOne.GetString("gzip")), gzipRef)
if err != nil {
continue
}
if gzipRef == nil || gzipRef.GzipId <= 0 {
continue
}
var webId = webOne.GetInt("id")
var compressionConfig = &serverconfigs.HTTPCompressionConfig{
UseDefaultTypes: true,
}
compressionConfig.IsPrior = gzipRef.IsPrior
compressionConfig.IsOn = gzipRef.IsOn
gzipOne, err := db.FindOne("SELECT * FROM edgeHTTPGzips WHERE id=?", gzipRef.GzipId)
if err != nil {
return err
}
if len(gzipOne) == 0 {
continue
}
level := gzipOne.GetInt("level")
if level <= 0 {
continue
}
if level > 0 && level <= 10 {
compressionConfig.Level = types.Int8(level)
} else if level > 10 {
compressionConfig.Level = 10
}
var minLengthBytes = []byte(gzipOne.GetString("minLength"))
if len(minLengthBytes) > 0 {
var sizeCapacity = &shared.SizeCapacity{}
err = json.Unmarshal(minLengthBytes, sizeCapacity)
if err != nil {
continue
}
compressionConfig.MinLength = sizeCapacity
}
var maxLengthBytes = []byte(gzipOne.GetString("maxLength"))
if len(maxLengthBytes) > 0 {
var sizeCapacity = &shared.SizeCapacity{}
err = json.Unmarshal(maxLengthBytes, sizeCapacity)
if err != nil {
continue
}
compressionConfig.MaxLength = sizeCapacity
}
var condsBytes = []byte(gzipOne.GetString("conds"))
if len(condsBytes) > 0 {
var conds = &shared.HTTPRequestCondsConfig{}
err = json.Unmarshal(condsBytes, conds)
if err != nil {
continue
}
compressionConfig.Conds = conds
}
configJSON, err := json.Marshal(compressionConfig)
if err != nil {
return err
}
_, err = db.Exec("UPDATE edgeHTTPWebs SET compression=? WHERE id=?", string(configJSON), webId)
if err != nil {
return err
}
}
// 鏇存柊鏈嶅姟绔彛
var serverDAO = models.NewServerDAO()
ones, err := serverDAO.Query(nil).
ResultPk().
FindAll()
if err != nil {
return err
}
for _, one := range ones {
var serverId = int64(one.(*models.Server).Id)
err = serverDAO.NotifyServerPortsUpdate(nil, serverId)
if err != nil {
return err
}
}
return nil
}
// v0.3.3
func upgradeV0_3_3(db *dbs.DB) error {
// 鍗囩骇CC璇锋眰鏁癈ode
_, err := db.Exec("UPDATE edgeHTTPFirewallRuleSets SET code='8002' WHERE name='CC璇锋眰鏁? AND code='8001'")
if err != nil {
return err
}
// 娓呴櫎鑺傜偣
// 鍒犻櫎7澶╀互鍓嶇殑info鏃ュ織
err = models.NewNodeLogDAO().DeleteExpiredLogsWithLevel(nil, "info", 7)
if err != nil {
return err
}
return nil
}
// v0.3.7
func upgradeV0_3_7(db *dbs.DB) error {
// 淇敼鎵€鏈塭dgeNodeGrants涓殑su涓?
_, err := db.Exec("UPDATE edgeNodeGrants SET su=0 WHERE su=1")
if err != nil {
return err
}
// WAF棰勭疆鍒嗙粍
_, err = db.Exec("UPDATE edgeHTTPFirewallRuleGroups SET isTemplate=1 WHERE LENGTH(code)>0")
if err != nil {
return err
}
return nil
}
// v0.4.0
func upgradeV0_4_0(db *dbs.DB) error {
// 鍗囩骇SYN Flood閰嶇疆
synFloodJSON, err := json.Marshal(firewallconfigs.NewSYNFloodConfig())
if err == nil {
_, err := db.Exec("UPDATE edgeHTTPFirewallPolicies SET synFlood=? WHERE synFlood IS NULL AND state=1", string(synFloodJSON))
if err != nil {
return err
}
}
return nil
}
// v0.4.1
func upgradeV0_4_1(db *dbs.DB) error {
// 鍗囩骇 servers.lastUserPlanId
_, err := db.Exec("UPDATE edgeServers SET lastUserPlanId=userPlanId WHERE userPlanId>0")
if err != nil {
return err
}
// 鎵ц鍩熷悕缁熻娓呯悊
err = stats.NewServerDomainHourlyStatDAO().CleanDays(nil, 7)
if err != nil {
return err
}
return nil
}
// v0.4.5
func upgradeV0_4_5(db *dbs.DB) error {
// 鍗囩骇璁块棶鏃ュ織鑷姩鍒嗚〃
{
var dao = models.NewSysSettingDAO()
valueJSON, err := dao.ReadSetting(nil, systemconfigs.SettingCodeAccessLogQueue)
if err != nil {
return err
}
if len(valueJSON) > 0 {
var config = &serverconfigs.AccessLogQueueConfig{}
err = json.Unmarshal(valueJSON, config)
if err == nil && config.RowsPerTable == 0 {
config.EnableAutoPartial = true
config.RowsPerTable = 500_000
configJSON, err := json.Marshal(config)
if err == nil {
err = dao.UpdateSetting(nil, systemconfigs.SettingCodeAccessLogQueue, configJSON)
if err != nil {
return err
}
}
}
}
}
// 鍗囩骇涓€涓槻SQL娉ㄥ叆瑙勫垯
{
ones, _, err := db.FindOnes(`SELECT id FROM edgeHTTPFirewallRules WHERE value=?`, "(updatexml|extractvalue|ascii|ord|char|chr|count|concat|rand|floor|substr|length|len|user|database|benchmark|analyse)\\s*\\(")
if err != nil {
return err
}
for _, one := range ones {
var ruleId = one.GetInt64("id")
_, err = db.Exec(`UPDATE edgeHTTPFirewallRules SET value=? WHERE id=? LIMIT 1`, `\b(updatexml|extractvalue|ascii|ord|char|chr|count|concat|rand|floor|substr|length|len|user|database|benchmark|analyse)\s*\(.*\)`, ruleId)
if err != nil {
return err
}
}
}
return nil
}
// v0.4.7
func upgradeV0_4_7(db *dbs.DB) error {
// 鍗囩骇 edgeServers 涓殑 plainServerNames
{
ones, _, err := db.FindOnes("SELECT id,serverNames FROM edgeServers WHERE state=1")
if err != nil {
return err
}
for _, one := range ones {
var serverId = one.GetInt64("id")
var serverNamesJSON = one.GetBytes("serverNames")
if len(serverNamesJSON) > 0 {
var serverNames = []*serverconfigs.ServerNameConfig{}
err = json.Unmarshal(serverNamesJSON, &serverNames)
if err != nil {
return err
}
plainServerNamesJSON, err := json.Marshal(serverconfigs.PlainServerNames(serverNames))
if err != nil {
return err
}
_, err = db.Exec("UPDATE edgeServers SET plainServerNames=? WHERE id=?", plainServerNamesJSON, serverId)
if err != nil {
return err
}
}
}
}
return nil
}
// v0.4.8
func upgradeV0_4_8(db *dbs.DB) error {
// 璁剧疆edgeIPLists涓殑serverId
{
firewallPolicyOnes, _, err := db.FindOnes("SELECT inbound,serverId FROM edgeHTTPFirewallPolicies WHERE serverId>0")
if err != nil {
return err
}
for _, one := range firewallPolicyOnes {
var inboundBytes = one.GetBytes("inbound")
var serverId = one.GetInt64("serverId")
var listIds = []int64{}
if len(inboundBytes) > 0 {
var inbound = &firewallconfigs.HTTPFirewallInboundConfig{}
err = json.Unmarshal(inboundBytes, inbound)
if err == nil { // we ignore errors
if inbound.AllowListRef != nil && inbound.AllowListRef.ListId > 0 {
listIds = append(listIds, inbound.AllowListRef.ListId)
}
if inbound.DenyListRef != nil && inbound.DenyListRef.ListId > 0 {
listIds = append(listIds, inbound.DenyListRef.ListId)
}
if inbound.GreyListRef != nil && inbound.GreyListRef.ListId > 0 {
listIds = append(listIds, inbound.GreyListRef.ListId)
}
}
}
if len(listIds) == 0 {
continue
}
for _, listId := range listIds {
isPublicCol, err := db.FindCol(0, "SELECT isPublic FROM edgeIPLists WHERE id=? LIMIT 1", listId)
if err != nil {
return err
}
var isPublic = types.Bool(isPublicCol)
if !isPublic {
_, err = db.Exec("UPDATE edgeIPLists SET serverId=? WHERE id=?", serverId, listId)
if err != nil {
return err
}
}
}
}
}
return nil
}
// v0.4.11
func upgradeV0_4_11(db *dbs.DB) error {
// 鍗囩骇ns绔彛
{
// TCP
{
var config = &serverconfigs.TCPProtocolConfig{}
config.IsOn = true
config.Listen = []*serverconfigs.NetworkAddressConfig{
{
Protocol: serverconfigs.ProtocolTCP,
PortRange: "53",
},
}
configJSON, err := json.Marshal(config)
if err != nil {
return err
}
_, err = db.Exec("UPDATE edgeNSClusters SET tcp=? WHERE tcp IS NULL", configJSON)
if err != nil {
return err
}
}
// UDP
{
var config = &serverconfigs.UDPProtocolConfig{}
config.IsOn = true
config.Listen = []*serverconfigs.NetworkAddressConfig{
{
Protocol: serverconfigs.ProtocolUDP,
PortRange: "53",
},
}
configJSON, err := json.Marshal(config)
if err != nil {
return err
}
_, err = db.Exec("UPDATE edgeNSClusters SET udp=? WHERE udp IS NULL", configJSON)
if err != nil {
return err
}
}
}
return nil
}
// v1.2.1
func upgradeV1_2_1(db *dbs.DB) error {
// upgrade generated USER-xxx in old versions
ones, _, err := db.FindOnes("SELECT id, username, clusterId FROM edgeUsers WHERE username LIKE 'USER-%'")
if err != nil {
return err
}
for _, one := range ones {
var userId = one.GetInt64("id")
var clusterId = one.GetInt64("clusterId")
var username = one.GetString("username")
if clusterId <= 0 {
defaultClusterIdValue, err := db.FindCol(0, "SELECT id FROM edgeNodeClusters WHERE state=1 ORDER BY id ASC LIMIT 1")
if err != nil {
return err
}
var defaultClusterId = types.Int64(defaultClusterIdValue)
if defaultClusterId > 0 {
_, err = db.Exec("UPDATE edgeUsers SET username=?, clusterId=? WHERE id=?", strings.ReplaceAll(username, "-", "_"), defaultClusterId, userId)
if err != nil {
return err
}
}
}
}
return nil
}
// 1.2.10
func upgradeV1_2_10(db *dbs.DB) error {
{
type OldGlobalConfig struct {
// HTTP & HTTPS鐩稿叧閰嶇疆
HTTPAll struct {
DomainAuditingIsOn bool `yaml:"domainAuditingIsOn" json:"domainAuditingIsOn"` // 域名是否需要审核
DomainAuditingPrompt string `yaml:"domainAuditingPrompt" json:"domainAuditingPrompt"` // 域名审核提示
} `yaml:"httpAll" json:"httpAll"`
TCPAll struct {
PortRangeMin int `yaml:"portRangeMin" json:"portRangeMin"` // 最小端口
PortRangeMax int `yaml:"portRangeMax" json:"portRangeMax"` // 最大端口
DenyPorts []int `yaml:"denyPorts" json:"denyPorts"` // 禁止端口
} `yaml:"tcpAll" json:"tcpAll"`
}
globalConfigValue, err := db.FindCol(0, "SELECT value FROM edgeSysSettings WHERE code='serverGlobalConfig'")
if err != nil {
return err
}
var globalConfigString = types.String(globalConfigValue)
if len(globalConfigString) > 0 {
var oldGlobalConfig = &OldGlobalConfig{}
err = json.Unmarshal([]byte(globalConfigString), oldGlobalConfig)
if err == nil { // we ignore error
ones, _, err := db.FindOnes("SELECT id,globalServerConfig FROM edgeNodeClusters")
if err != nil {
return err
}
for _, one := range ones {
var id = one.GetInt64("id")
var globalServerConfigData = []byte(one.GetString("globalServerConfig"))
if len(globalServerConfigData) > 32 {
var globalServerConfig = &serverconfigs.GlobalServerConfig{}
err = json.Unmarshal(globalServerConfigData, globalServerConfig)
if err != nil {
return err
}
globalServerConfig.HTTPAll.DomainAuditingIsOn = oldGlobalConfig.HTTPAll.DomainAuditingIsOn
globalServerConfig.HTTPAll.DomainAuditingPrompt = oldGlobalConfig.HTTPAll.DomainAuditingPrompt
globalServerConfig.TCPAll.DenyPorts = oldGlobalConfig.TCPAll.DenyPorts
globalServerConfig.TCPAll.PortRangeMin = oldGlobalConfig.TCPAll.PortRangeMin
globalServerConfig.TCPAll.PortRangeMax = oldGlobalConfig.TCPAll.PortRangeMax
globalServerConfigJSON, err := json.Marshal(globalServerConfig)
if err != nil {
return err
}
_, err = db.Exec("UPDATE edgeNodeClusters SET globalServerConfig=? WHERE id=?", globalServerConfigJSON, id)
if err != nil {
return err
}
}
}
}
}
}
return nil
}
// 1.3.2
func upgradeV1_3_2(db *dbs.DB) error {
// waf
{
var disableSet = func(setId int64) error {
_, err := db.Exec("UPDATE edgeHTTPFirewallRuleSets SET state=0 WHERE id=?", setId)
return err
}
var addRuleToGroup = func(groupId int64, setCode string, setName string, actions []*firewallconfigs.HTTPFirewallActionConfig, ruleParam string, ruleOperator string, value string) error {
actionsJSON, err := json.Marshal(actions)
if err != nil {
return err
}
// rule
ruleResult, err := db.Exec("INSERT INTO edgeHTTPFirewallRules (isOn, param, operator, value, isCaseInsensitive, state) VALUES (1, ?, ?, ?, 0, 1)", ruleParam, ruleOperator, value)
if err != nil {
return err
}
ruleId, err := ruleResult.LastInsertId()
if err != nil {
return err
}
var ruleRefs = []*firewallconfigs.HTTPFirewallRuleRef{
{
IsOn: true,
RuleId: ruleId,
},
}
ruleRefsJSON, err := json.Marshal(ruleRefs)
if err != nil {
return err
}
// set
setResult, err := db.Exec("INSERT INTO edgeHTTPFirewallRuleSets (isOn, code, name, rules, connector, state, actions) VALUES (1, ?, ?, ?, 'or', 1, ?)", setCode, setName, ruleRefsJSON, actionsJSON)
if err != nil {
return err
}
setId, err := setResult.LastInsertId()
if err != nil {
return err
}
var setRefs = []*firewallconfigs.HTTPFirewallRuleSetRef{
{
IsOn: true,
SetId: setId,
},
}
setRefsJSON, err := json.Marshal(setRefs)
if err != nil {
return err
}
// group
_, err = db.Exec("UPDATE edgeHTTPFirewallRuleGroups SET sets=? WHERE id=?", setRefsJSON, groupId)
if err != nil {
return err
}
return nil
}
// sql injection
{
ruleGroups, _, err := db.FindOnes("SELECT id, sets FROM edgeHTTPFirewallRuleGroups WHERE code='sqlInjection' AND state=1")
if err != nil {
return err
}
for _, ruleGroup := range ruleGroups {
var setsJSON = ruleGroup.GetBytes("sets")
if len(setsJSON) == 0 {
continue
}
var setRefs = []*firewallconfigs.HTTPFirewallRuleSetRef{}
err = json.Unmarshal(setsJSON, &setRefs)
if err != nil {
continue
}
if len(setRefs) != 5 {
continue
}
var isChanged = false
for setIndex, setRef := range setRefs {
set, setErr := db.FindOne("SELECT id, rules, isOn, actions FROM edgeHTTPFirewallRuleSets WHERE id=? AND state=1", setRef.SetId)
if setErr != nil {
return setErr
}
if set == nil {
isChanged = true
break
}
var rulesJSON = set.GetBytes("rules")
if len(rulesJSON) == 0 {
isChanged = true
break
}
var ruleRefs = []*firewallconfigs.HTTPFirewallRuleRef{}
err = json.Unmarshal(rulesJSON, &ruleRefs)
if err != nil {
return err
}
if len(ruleRefs) < 1 {
isChanged = true
break
}
var actionsJSON = set.GetBytes("actions")
if len(actionsJSON) == 0 {
isChanged = true
break
}
var actions = []*firewallconfigs.HTTPFirewallActionConfig{}
err = json.Unmarshal(actionsJSON, &actions)
if err != nil {
return err
}
if !(len(actions) == 1 && actions[0].Code == firewallconfigs.HTTPFirewallActionBlock) {
isChanged = true
break
}
var rules = []maps.Map{}
for _, ruleRef := range ruleRefs {
rule, ruleErr := db.FindOne("SELECT * FROM edgeHTTPFirewallRules WHERE id=? AND state=1", ruleRef.RuleId)
if ruleErr != nil {
return ruleErr
}
if rule == nil {
isChanged = true
break
}
rules = append(rules, rule)
}
if isChanged {
break
}
if len(rules) < 1 {
isChanged = true
break
}
switch setIndex {
case 0:
var rule = rules[0]
if !(rule.GetString("param") == "${requestAll}" && rule.GetString("operator") == "match" && rule.GetString("value") == `union[\s/\*]+select`) {
isChanged = true
}
case 1:
var rule = rules[0]
if !(rule.GetString("param") == "${requestAll}" && rule.GetString("operator") == "match" && rule.GetString("value") == `/\*(!|\x00)`) {
isChanged = true
}
case 2:
if len(rules) != 4 {
isChanged = true
} else {
{
var rule = rules[0]
if !(rule.GetString("param") == "${requestAll}" && rule.GetString("operator") == "match" && rule.GetString("value") == `\s(and|or|rlike)\s+(if|updatexml)\s*\(`) {
isChanged = true
}
}
{
var rule = rules[1]
if !(rule.GetString("param") == "${requestAll}" && rule.GetString("operator") == "match" && rule.GetString("value") == `\s+(and|or|rlike)\s+(select|case)\s+`) {
isChanged = true
}
}
{
var rule = rules[2]
if !(rule.GetString("param") == "${requestAll}" && rule.GetString("operator") == "match" && rule.GetString("value") == `\s+(and|or|procedure)\s+[\w\p{L}]+\s*=\s*[\w\p{L}]+(\s|$|--|#)`) {
isChanged = true
}
}
{
var rule = rules[3]
if !(rule.GetString("param") == "${requestAll}" && rule.GetString("operator") == "match" && rule.GetString("value") == `\(\s*case\s+when\s+[\w\p{L}]+\s*=\s*[\w\p{L}]+\s+then\s+`) {
isChanged = true
}
}
}
case 3:
var rule = rules[0]
if !(rule.GetString("param") == "${requestAll}" && rule.GetString("operator") == "match" && rule.GetString("value") == `\b(updatexml|extractvalue|ascii|ord|char|chr|count|concat|rand|floor|substr|length|len|user|database|benchmark|analyse)\s*\(.*\)`) {
isChanged = true
}
case 4:
var rule = rules[0]
if !(rule.GetString("param") == "${requestAll}" && rule.GetString("operator") == "match" && rule.GetString("value") == `;\s*(declare|use|drop|create|exec|delete|update|insert)\s`) {
isChanged = true
}
}
}
if isChanged {
continue
}
for _, setRef := range setRefs {
err = disableSet(setRef.SetId)
if err != nil {
return err
}
}
err = addRuleToGroup(ruleGroup.GetInt64("id"), "7010", "SQL注入检测", []*firewallconfigs.HTTPFirewallActionConfig{
{
Code: firewallconfigs.HTTPFirewallActionPage,
Options: maps.Map{"status": 403, "body": ""},
},
}, "${requestAll}", firewallconfigs.HTTPFirewallRuleOperatorContainsSQLInjection, "")
if err != nil {
return err
}
}
}
// xss
{
ruleGroups, _, err := db.FindOnes("SELECT id, sets FROM edgeHTTPFirewallRuleGroups WHERE code='xss' AND state=1")
if err != nil {
return err
}
for _, ruleGroup := range ruleGroups {
var setsJSON = ruleGroup.GetBytes("sets")
if len(setsJSON) == 0 {
continue
}
var setRefs = []*firewallconfigs.HTTPFirewallRuleSetRef{}
err = json.Unmarshal(setsJSON, &setRefs)
if err != nil {
continue
}
if len(setRefs) != 3 {
continue
}
var isChanged = false
for setIndex, setRef := range setRefs {
set, setErr := db.FindOne("SELECT id, rules, isOn, actions FROM edgeHTTPFirewallRuleSets WHERE id=? AND state=1", setRef.SetId)
if setErr != nil {
return setErr
}
if set == nil {
isChanged = true
break
}
var rulesJSON = set.GetBytes("rules")
if len(rulesJSON) == 0 {
isChanged = true
break
}
var ruleRefs = []*firewallconfigs.HTTPFirewallRuleRef{}
err = json.Unmarshal(rulesJSON, &ruleRefs)
if err != nil {
return err
}
if len(ruleRefs) != 1 {
isChanged = true
break
}
var actionsJSON = set.GetBytes("actions")
if len(actionsJSON) == 0 {
isChanged = true
break
}
var actions = []*firewallconfigs.HTTPFirewallActionConfig{}
err = json.Unmarshal(actionsJSON, &actions)
if err != nil {
return err
}
if !(len(actions) == 1 && actions[0].Code == firewallconfigs.HTTPFirewallActionBlock) {
isChanged = true
break
}
rule, ruleErr := db.FindOne("SELECT * FROM edgeHTTPFirewallRules WHERE id=? AND state=1", ruleRefs[0].RuleId)
if ruleErr != nil {
return ruleErr
}
if rule == nil {
isChanged = true
break
}
switch setIndex {
case 0:
if !(rule.GetString("param") == "${requestURI}" && rule.GetString("operator") == "match" && rule.GetString("value") == `(onmouseover|onmousemove|onmousedown|onmouseup|onerror|onload|onclick|ondblclick|onkeydown|onkeyup|onkeypress)\s*=`) {
isChanged = true
}
case 1:
if !(rule.GetString("param") == "${requestURI}" && rule.GetString("operator") == "match" && rule.GetString("value") == `(alert|eval|prompt|confirm)\s*\(`) {
isChanged = true
}
case 2:
if !(rule.GetString("param") == "${requestURI}" && rule.GetString("operator") == "match" && rule.GetString("value") == `<(script|iframe|link)`) {
isChanged = true
}
}
}
if isChanged {
continue
}
for _, setRef := range setRefs {
err = disableSet(setRef.SetId)
if err != nil {
return err
}
}
err = addRuleToGroup(ruleGroup.GetInt64("id"), "1010", "XSS攻击检测", []*firewallconfigs.HTTPFirewallActionConfig{
{
Code: firewallconfigs.HTTPFirewallActionPage,
Options: maps.Map{"status": 403, "body": ""},
},
}, "${requestAll}", firewallconfigs.HTTPFirewallRuleOperatorContainsXSS, "")
if err != nil {
return err
}
}
}
}
// user register config
var newAddedFeatureCodes = []string{
userconfigs.UserFeatureCodeServerOptimization,
userconfigs.UserFeatureCodeServerAuth,
userconfigs.UserFeatureCodeServerWebsocket,
userconfigs.UserFeatureCodeServerHTTP3,
userconfigs.UserFeatureCodeServerCC,
userconfigs.UserFeatureCodeServerReferers,
userconfigs.UserFeatureCodeServerUserAgent,
userconfigs.UserFeatureCodeServerRequestLimit,
userconfigs.UserFeatureCodeServerCompression,
userconfigs.UserFeatureCodeServerRewriteRules,
userconfigs.UserFeatureCodeServerHostRedirects,
userconfigs.UserFeatureCodeServerHTTPHeaders,
userconfigs.UserFeatureCodeServerPages,
}
{
value, err := db.FindCol(0, "SELECT value FROM edgeSysSettings WHERE code=?", systemconfigs.SettingCodeUserRegisterConfig)
if err != nil {
return err
}
if value != nil {
var valueString = types.String(value)
if valueString != "null" && len(valueString) > 0 {
var registerConfig = &userconfigs.UserRegisterConfig{}
err = json.Unmarshal([]byte(valueString), registerConfig)
if err != nil {
return err
}
if len(registerConfig.Features) > 0 {
var newFeatureCodes = registerConfig.Features
var changed = false
for _, featureCode := range newAddedFeatureCodes {
if !lists.ContainsString(newFeatureCodes, featureCode) {
newFeatureCodes = append(newFeatureCodes, featureCode)
changed = true
}
}
if changed {
registerConfig.Features = newFeatureCodes
registerConfigJSON, err := json.Marshal(registerConfig)
if err != nil {
return err
}
_, err = db.Exec("UPDATE edgeSysSettings SET value=? WHERE code=?", registerConfigJSON, systemconfigs.SettingCodeUserRegisterConfig)
if err != nil {
return err
}
}
}
}
}
}
// user features
{
var sqlPieces []string
for _, featureCode := range newAddedFeatureCodes {
if strings.Contains(featureCode, "'") {
continue
}
sqlPieces = append(sqlPieces, "'$', '"+featureCode+"'")
}
_, err := db.Exec("UPDATE edgeUsers SET features=JSON_ARRAY_APPEND(features," + strings.Join(sqlPieces, ",") + ") WHERE features IS NOT NULL AND JSON_LENGTH(features)>0 AND NOT JSON_CONTAINS(features, '" + strconv.Quote(newAddedFeatureCodes[0]) + "')")
if err != nil {
return err
}
}
return nil
}
// 1.3.4
func upgradeV1_3_4(db *dbs.DB) error {
_, err := db.Exec("DELETE FROM edgeLoginSessions WHERE adminId>0")
if err != nil {
return err
}
return nil
}
// 1.4.4
func upgradeV1_4_4(db *dbs.DB) error {
// 检查 encryption 字段是否已存在
col, err := db.FindCol(0, "SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='edgeHTTPWebs' AND COLUMN_NAME='encryption'")
if err != nil {
return err
}
var count = types.Int(col)
if count > 0 {
// 字段已存在,跳过
return nil
}
// 添加 encryption 字段到 edgeHTTPWebs 表
_, err = db.Exec("ALTER TABLE edgeHTTPWebs ADD COLUMN `encryption` json DEFAULT NULL COMMENT '页面动态加密配置' AFTER `optimization`")
if err != nil {
return err
}
return nil
}
// 1.4.8
func upgradeV1_4_8(db *dbs.DB) error {
return createHTTPDNSTables(db)
}
func createHTTPDNSTables(db *dbs.DB) error {
sqls := []string{
"CREATE TABLE IF NOT EXISTS `edgeHTTPDNSClusters` (`id` bigint unsigned NOT NULL AUTO_INCREMENT,`name` varchar(255) DEFAULT NULL,`isOn` tinyint unsigned DEFAULT '1',`isDefault` tinyint unsigned DEFAULT '0',`serviceDomain` varchar(255) DEFAULT NULL,`defaultTTL` int unsigned DEFAULT '30',`fallbackTimeoutMs` int unsigned DEFAULT '300',`installDir` varchar(255) DEFAULT '/opt/edge-httpdns',`tlsPolicy` json DEFAULT NULL,`createdAt` bigint unsigned DEFAULT '0',`updatedAt` bigint unsigned DEFAULT '0',`state` tinyint unsigned DEFAULT '1',PRIMARY KEY (`id`),KEY `name` (`name`),KEY `isDefault` (`isDefault`),KEY `state` (`state`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='HTTPDNS集群配置表默认TTL、回退超时、服务域名等'",
"CREATE TABLE IF NOT EXISTS `edgeHTTPDNSNodes` (`id` bigint unsigned NOT NULL AUTO_INCREMENT,`clusterId` bigint unsigned DEFAULT '0',`name` varchar(255) DEFAULT NULL,`isOn` tinyint unsigned DEFAULT '1',`isUp` tinyint unsigned DEFAULT '0',`isInstalled` tinyint unsigned DEFAULT '0',`isActive` tinyint unsigned DEFAULT '0',`uniqueId` varchar(64) DEFAULT NULL,`secret` varchar(64) DEFAULT NULL,`installDir` varchar(255) DEFAULT '/opt/edge-httpdns',`status` json DEFAULT NULL,`installStatus` json DEFAULT NULL,`createdAt` bigint unsigned DEFAULT '0',`updatedAt` bigint unsigned DEFAULT '0',`state` tinyint unsigned DEFAULT '1',PRIMARY KEY (`id`),UNIQUE KEY `uniqueId` (`uniqueId`),KEY `clusterId` (`clusterId`),KEY `state` (`state`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='HTTPDNS节点表节点基础信息与运行状态'",
"CREATE TABLE IF NOT EXISTS `edgeHTTPDNSApps` (`id` bigint unsigned NOT NULL AUTO_INCREMENT,`name` varchar(255) DEFAULT NULL,`appId` varchar(64) DEFAULT NULL,`isOn` tinyint unsigned DEFAULT '1',`primaryClusterId` bigint unsigned DEFAULT '0',`backupClusterId` bigint unsigned DEFAULT '0',`sniMode` varchar(64) DEFAULT 'fixed_hide',`userId` bigint unsigned DEFAULT '0',`createdAt` bigint unsigned DEFAULT '0',`updatedAt` bigint unsigned DEFAULT '0',`state` tinyint unsigned DEFAULT '1',PRIMARY KEY (`id`),UNIQUE KEY `appId` (`appId`),KEY `name` (`name`),KEY `state` (`state`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='HTTPDNS应用表应用与主备集群绑定关系'",
"CREATE TABLE IF NOT EXISTS `edgeHTTPDNSAppSecrets` (`id` bigint unsigned NOT NULL AUTO_INCREMENT,`appId` bigint unsigned DEFAULT '0',`signEnabled` tinyint unsigned DEFAULT '0',`signSecret` varchar(255) DEFAULT NULL,`signUpdatedAt` bigint unsigned DEFAULT '0',`updatedAt` bigint unsigned DEFAULT '0',`state` tinyint unsigned DEFAULT '1',PRIMARY KEY (`id`),UNIQUE KEY `appId` (`appId`),KEY `state` (`state`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='HTTPDNS应用密钥表请求验签开关与加签Secret'",
"CREATE TABLE IF NOT EXISTS `edgeHTTPDNSDomains` (`id` bigint unsigned NOT NULL AUTO_INCREMENT,`appId` bigint unsigned DEFAULT '0',`domain` varchar(255) DEFAULT NULL,`isOn` tinyint unsigned DEFAULT '1',`createdAt` bigint unsigned DEFAULT '0',`updatedAt` bigint unsigned DEFAULT '0',`state` tinyint unsigned DEFAULT '1',PRIMARY KEY (`id`),UNIQUE KEY `appId_domain` (`appId`,`domain`),KEY `domain` (`domain`),KEY `state` (`state`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='HTTPDNS应用域名表应用绑定的业务域名'",
"CREATE TABLE IF NOT EXISTS `edgeHTTPDNSCustomRules` (`id` bigint unsigned NOT NULL AUTO_INCREMENT,`appId` bigint unsigned DEFAULT '0',`domainId` bigint unsigned DEFAULT '0',`ruleName` varchar(255) DEFAULT NULL,`lineScope` varchar(64) DEFAULT NULL,`lineCarrier` varchar(64) DEFAULT NULL,`lineRegion` varchar(64) DEFAULT NULL,`lineProvince` varchar(64) DEFAULT NULL,`lineContinent` varchar(64) DEFAULT NULL,`lineCountry` varchar(128) DEFAULT NULL,`ttl` int unsigned DEFAULT '30',`isOn` tinyint unsigned DEFAULT '1',`priority` int unsigned DEFAULT '0',`updatedAt` bigint unsigned DEFAULT '0',`state` tinyint unsigned DEFAULT '1',PRIMARY KEY (`id`),KEY `domainId_isOn_priority` (`domainId`,`isOn`,`priority`),KEY `state` (`state`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='HTTPDNS自定义解析规则表按线路/地域匹配)'",
"CREATE TABLE IF NOT EXISTS `edgeHTTPDNSCustomRuleRecords` (`id` bigint unsigned NOT NULL AUTO_INCREMENT,`ruleId` bigint unsigned DEFAULT '0',`recordType` varchar(32) DEFAULT NULL,`recordValue` varchar(255) DEFAULT NULL,`weight` int unsigned DEFAULT '0',`sort` int unsigned DEFAULT '0',`state` tinyint unsigned DEFAULT '1',PRIMARY KEY (`id`),KEY `ruleId` (`ruleId`),KEY `state` (`state`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='HTTPDNS自定义规则记录值表A/AAAA及权重'",
"CREATE TABLE IF NOT EXISTS `edgeHTTPDNSAccessLogs` (`id` bigint unsigned NOT NULL AUTO_INCREMENT,`requestId` varchar(128) DEFAULT NULL,`clusterId` bigint unsigned DEFAULT '0',`nodeId` bigint unsigned DEFAULT '0',`appId` varchar(64) DEFAULT NULL,`appName` varchar(255) DEFAULT NULL,`domain` varchar(255) DEFAULT NULL,`qtype` varchar(16) DEFAULT NULL,`clientIP` varchar(64) DEFAULT NULL,`clientRegion` varchar(255) DEFAULT NULL,`carrier` varchar(128) DEFAULT NULL,`sdkVersion` varchar(64) DEFAULT NULL,`os` varchar(64) DEFAULT NULL,`resultIPs` text,`status` varchar(32) DEFAULT NULL,`errorCode` varchar(64) DEFAULT NULL,`costMs` int unsigned DEFAULT '0',`createdAt` bigint unsigned DEFAULT '0',`day` varchar(8) DEFAULT NULL,`summary` text,PRIMARY KEY (`id`),UNIQUE KEY `requestId_nodeId` (`requestId`,`nodeId`),KEY `day_cluster_node_domain_status_createdAt` (`day`,`clusterId`,`nodeId`,`domain`,`status`,`createdAt`),KEY `appId` (`appId`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='HTTPDNS访问日志表解析请求与结果'",
"CREATE TABLE IF NOT EXISTS `edgeHTTPDNSRuntimeLogs` (`id` bigint unsigned NOT NULL AUTO_INCREMENT,`clusterId` bigint unsigned DEFAULT '0',`nodeId` bigint unsigned DEFAULT '0',`level` varchar(32) DEFAULT NULL,`type` varchar(64) DEFAULT NULL,`module` varchar(64) DEFAULT NULL,`description` text,`count` bigint unsigned DEFAULT '1',`requestId` varchar(128) DEFAULT NULL,`createdAt` bigint unsigned DEFAULT '0',`day` varchar(8) DEFAULT NULL,PRIMARY KEY (`id`),KEY `day_cluster_node_level_createdAt` (`day`,`clusterId`,`nodeId`,`level`,`createdAt`),KEY `requestId` (`requestId`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='HTTPDNS运行日志表节点运行与异常日志'",
}
for _, sql := range sqls {
if _, err := db.Exec(sql); err != nil {
return err
}
}
return nil
}