package setup import ( "encoding/json" "strconv" "strings" "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" ) 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, }, } // 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 { // 清空域名统计,已使用分表代替 // 因为可能有权限问题,所以我们忽略错误 _, _ = 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请求数Code _, 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 { // 修改所有edgeNodeGrants中的su为0 _, 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 }