From 6c2301733a36574ed1c7d694009d2fe2b7fe171d Mon Sep 17 00:00:00 2001 From: GoEdgeLab Date: Wed, 18 May 2022 21:02:47 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=9E=E7=8E=B0=E5=9F=BA=E7=A1=80=E7=9A=84DD?= =?UTF-8?q?oS=E9=98=B2=E6=8A=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../actions/default/clusters/cluster/init.go | 3 + .../clusters/cluster/node/nodeutils/utils.go | 27 +++- .../cluster/node/nodeutils/utils_ext.go | 3 +- .../node/settings/ddos-protection/index.go | 133 ++++++++++++++++++ .../node/settings/ddos-protection/status.go | 27 ++++ .../cluster/node/settings/ssh/index.go | 4 + .../cluster/settings/ddos-protection/index.go | 106 ++++++++++++++ .../settings/ddos-protection/status.go | 71 ++++++++++ .../clusters/cluster/settings/index.go | 44 +++--- .../default/clusters/cluster/settings/init.go | 8 ++ .../clusters/clusterutils/cluster_helper.go | 12 ++ .../ddos-protection-ip-list-config-box.js | 112 +++++++++++++++ .../ddos-protection-ports-config-box.js | 115 +++++++++++++++ .../node-ddos-protection-config-box.js | 110 +++++++++++++++ web/public/js/components/common/inputs.js | 63 +++++++++ .../components/dns/dns-resolver-config-box.js | 2 +- .../js/components/server/prior-checkbox.js | 11 +- .../node/settings/ddos-protection/index.html | 17 +++ .../node/settings/ddos-protection/index.js | 15 ++ .../cluster/node/settings/ssh/index.html | 2 +- .../settings/ddos-protection/index.html | 19 +++ .../cluster/settings/ddos-protection/index.js | 3 + .../settings/ddos-protection/status.html | 44 ++++++ .../settings/ddos-protection/status.js | 20 +++ .../clusters/cluster/settings/index.html | 7 - web/views/@default/clusters/logs/index.html | 4 +- .../@default/clusters/tasks/listPopup.html | 1 + .../servers/components/waf/policy.html | 2 +- .../servers/components/waf/update.html | 2 +- 29 files changed, 939 insertions(+), 48 deletions(-) create mode 100644 internal/web/actions/default/clusters/cluster/node/settings/ddos-protection/index.go create mode 100644 internal/web/actions/default/clusters/cluster/node/settings/ddos-protection/status.go create mode 100644 internal/web/actions/default/clusters/cluster/settings/ddos-protection/index.go create mode 100644 internal/web/actions/default/clusters/cluster/settings/ddos-protection/status.go create mode 100644 web/public/js/components/cluster/ddos-protection-ip-list-config-box.js create mode 100644 web/public/js/components/cluster/ddos-protection-ports-config-box.js create mode 100644 web/public/js/components/cluster/node-ddos-protection-config-box.js create mode 100644 web/public/js/components/common/inputs.js create mode 100644 web/views/@default/clusters/cluster/node/settings/ddos-protection/index.html create mode 100644 web/views/@default/clusters/cluster/node/settings/ddos-protection/index.js create mode 100644 web/views/@default/clusters/cluster/settings/ddos-protection/index.html create mode 100644 web/views/@default/clusters/cluster/settings/ddos-protection/index.js create mode 100644 web/views/@default/clusters/cluster/settings/ddos-protection/status.html create mode 100644 web/views/@default/clusters/cluster/settings/ddos-protection/status.js diff --git a/internal/web/actions/default/clusters/cluster/init.go b/internal/web/actions/default/clusters/cluster/init.go index f28e9997..866dd67d 100644 --- a/internal/web/actions/default/clusters/cluster/init.go +++ b/internal/web/actions/default/clusters/cluster/init.go @@ -5,6 +5,7 @@ import ( "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/groups" "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/node" "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/node/settings/cache" + ddosProtection "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/node/settings/ddos-protection" "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/node/settings/dns" "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/node/settings/ssh" "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/node/settings/system" @@ -56,6 +57,8 @@ func init() { GetPost("/settings/ssh", new(ssh.IndexAction)). GetPost("/settings/ssh/test", new(ssh.TestAction)). GetPost("/settings/thresholds", new(thresholds.IndexAction)). + GetPost("/settings/ddos-protection", new(ddosProtection.IndexAction)). + Post("/settings/ddos-protection/status", new(ddosProtection.StatusAction)). // 分组相关 Prefix("/clusters/cluster/groups"). diff --git a/internal/web/actions/default/clusters/cluster/node/nodeutils/utils.go b/internal/web/actions/default/clusters/cluster/node/nodeutils/utils.go index 977a5262..bb2496f4 100644 --- a/internal/web/actions/default/clusters/cluster/node/nodeutils/utils.go +++ b/internal/web/actions/default/clusters/cluster/node/nodeutils/utils.go @@ -23,6 +23,11 @@ func InitNodeInfo(parentAction *actionutils.ParentAction, nodeId int64) (*pb.Nod } var node = nodeResp.Node + info, err := parentAction.RPC().NodeRPC().FindEnabledNodeConfigInfo(parentAction.AdminContext(), &pb.FindEnabledNodeConfigInfoRequest{NodeId: nodeId}) + if err != nil { + return nil, err + } + var groupMap maps.Map if node.NodeGroup != nil { groupMap = maps.Map{ @@ -60,30 +65,38 @@ func InitNodeInfo(parentAction *actionutils.ParentAction, nodeId int64) (*pb.Nod "name": "DNS设置", "url": prefix + "/settings/dns?" + query, "isActive": menuItem == "dns", - "isOn": len(node.DnsRoutes) > 0, + "isOn": info.HasDNSInfo, }, { "name": "缓存设置", "url": prefix + "/settings/cache?" + query, "isActive": menuItem == "cache", - "isOn": len(node.CacheDiskDir) > 0 || - (node.MaxCacheDiskCapacity != nil && node.MaxCacheDiskCapacity.Count > 0) || - (node.MaxCacheMemoryCapacity != nil && node.MaxCacheMemoryCapacity.Count > 0), + "isOn": info.HasCacheInfo, + }, + { + "name": "DDOS防护", + "url": prefix + "/settings/ddos-protection?" + query, + "isActive": menuItem == "ddosProtection", + "isOn": info.HasDDoSProtection, + }, + { + "name": "-", + "url": "", }, } - menuItems = filterMenuItems(menuItems, menuItem, prefix, query) + menuItems = filterMenuItems(menuItems, menuItem, prefix, query, info) menuItems = append(menuItems, []maps.Map{ { "name": "SSH设置", "url": prefix + "/settings/ssh?" + query, "isActive": menuItem == "ssh", - "isOn": node.NodeLogin != nil, + "isOn": info.HasSSH, }, { "name": "系统设置", "url": prefix + "/settings/system?" + query, "isActive": menuItem == "system", - "isOn": node.MaxCPU > 0, + "isOn": info.HasSystemSettings, }, }...) parentAction.Data["leftMenuItems"] = menuItems diff --git a/internal/web/actions/default/clusters/cluster/node/nodeutils/utils_ext.go b/internal/web/actions/default/clusters/cluster/node/nodeutils/utils_ext.go index 5e18eac7..560c9d08 100644 --- a/internal/web/actions/default/clusters/cluster/node/nodeutils/utils_ext.go +++ b/internal/web/actions/default/clusters/cluster/node/nodeutils/utils_ext.go @@ -5,9 +5,10 @@ package nodeutils import ( + "github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb" "github.com/iwind/TeaGo/maps" ) -func filterMenuItems(menuItems []maps.Map, menuItem string, prefix string, query string) []maps.Map { +func filterMenuItems(menuItems []maps.Map, menuItem string, prefix string, query string, info *pb.FindEnabledNodeConfigInfoResponse) []maps.Map { return menuItems } diff --git a/internal/web/actions/default/clusters/cluster/node/settings/ddos-protection/index.go b/internal/web/actions/default/clusters/cluster/node/settings/ddos-protection/index.go new file mode 100644 index 00000000..e35771bd --- /dev/null +++ b/internal/web/actions/default/clusters/cluster/node/settings/ddos-protection/index.go @@ -0,0 +1,133 @@ +// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. + +package ddosProtection + +import ( + "encoding/json" + "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils" + "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/node/nodeutils" + "github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs" + "github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb" + "github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/ddosconfigs" + "github.com/iwind/TeaGo/actions" + "github.com/iwind/TeaGo/types" + "net" +) + +type IndexAction struct { + actionutils.ParentAction +} + +func (this *IndexAction) Init() { + this.Nav("", "node", "update") + this.SecondMenu("ddosProtection") +} + +func (this *IndexAction) RunGet(params struct { + ClusterId int64 + NodeId int64 +}) { + _, err := nodeutils.InitNodeInfo(this.Parent(), params.NodeId) + if err != nil { + this.ErrorPage(err) + return + } + + this.Data["nodeId"] = params.NodeId + + // 集群设置 + clusterProtectionResp, err := this.RPC().NodeClusterRPC().FindNodeClusterDDoSProtection(this.AdminContext(), &pb.FindNodeClusterDDoSProtectionRequest{NodeClusterId: params.ClusterId}) + if err != nil { + this.ErrorPage(err) + return + } + var clusterDDoSProtectionIsOn = false + if len(clusterProtectionResp.DdosProtectionJSON) > 0 { + var clusterDDoSProtection = &ddosconfigs.ProtectionConfig{} + err = json.Unmarshal(clusterProtectionResp.DdosProtectionJSON, clusterDDoSProtection) + if err != nil { + this.ErrorPage(err) + return + } + clusterDDoSProtectionIsOn = clusterDDoSProtection.IsOn() + } + + this.Data["clusterDDoSProtectionIsOn"] = clusterDDoSProtectionIsOn + + // 节点设置 + ddosProtectionResp, err := this.RPC().NodeRPC().FindNodeDDoSProtection(this.AdminContext(), &pb.FindNodeDDoSProtectionRequest{NodeId: params.NodeId}) + if err != nil { + this.ErrorPage(err) + return + } + var ddosProtectionConfig = ddosconfigs.DefaultProtectionConfig() + if len(ddosProtectionResp.DdosProtectionJSON) > 0 { + err = json.Unmarshal(ddosProtectionResp.DdosProtectionJSON, ddosProtectionConfig) + if err != nil { + this.ErrorPage(err) + return + } + } + this.Data["config"] = ddosProtectionConfig + this.Data["defaultConfigs"] = nodeconfigs.DefaultConfigs + + this.Show() +} + +func (this *IndexAction) RunPost(params struct { + NodeId int64 + DdosProtectionJSON []byte + + Must *actions.Must + CSRF *actionutils.CSRF +}) { + defer this.CreateLogInfo("修改节点 %d 的DDOS防护设置", params.NodeId) + + var ddosProtectionConfig = &ddosconfigs.ProtectionConfig{} + err := json.Unmarshal(params.DdosProtectionJSON, ddosProtectionConfig) + if err != nil { + this.ErrorPage(err) + return + } + + err = ddosProtectionConfig.Init() + if err != nil { + this.Fail("配置校验失败:" + err.Error()) + } + + // 校验参数 + if ddosProtectionConfig.TCP != nil { + var tcpConfig = ddosProtectionConfig.TCP + if tcpConfig.MaxConnectionsPerIP > 0 && tcpConfig.MaxConnectionsPerIP < nodeconfigs.DefaultTCPMinConnectionsPerIP { + this.FailField("tcpMaxConnectionsPerIP", "TCP: 单IP TCP最大连接数不能小于"+types.String(nodeconfigs.DefaultTCPMinConnectionsPerIP)) + } + + if tcpConfig.NewConnectionsRate > 0 && tcpConfig.NewConnectionsRate < nodeconfigs.DefaultTCPNewConnectionsMinRate { + this.FailField("tcpNewConnectionsRate", "TCP: 单IP连接速率不能小于"+types.String(nodeconfigs.DefaultTCPNewConnectionsMinRate)) + } + + // Port + for _, portConfig := range tcpConfig.Ports { + if portConfig.Port > 65535 { + this.Fail("端口号" + types.String(portConfig.Port) + "不能大于65535") + } + } + + // IP + for _, ipConfig := range tcpConfig.AllowIPList { + if net.ParseIP(ipConfig.IP) == nil { + this.Fail("白名单IP '" + ipConfig.IP + "' 格式错误") + } + } + } + + _, err = this.RPC().NodeRPC().UpdateNodeDDoSProtection(this.AdminContext(), &pb.UpdateNodeDDoSProtectionRequest{ + NodeId: params.NodeId, + DdosProtectionJSON: params.DdosProtectionJSON, + }) + if err != nil { + this.ErrorPage(err) + return + } + this.Success() +} diff --git a/internal/web/actions/default/clusters/cluster/node/settings/ddos-protection/status.go b/internal/web/actions/default/clusters/cluster/node/settings/ddos-protection/status.go new file mode 100644 index 00000000..fa5d4d4e --- /dev/null +++ b/internal/web/actions/default/clusters/cluster/node/settings/ddos-protection/status.go @@ -0,0 +1,27 @@ +// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. + +package ddosProtection + +import ( + "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils" + "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/nodes/nodeutils" + "github.com/TeaOSLab/EdgeCommon/pkg/messageconfigs" +) + +type StatusAction struct { + actionutils.ParentAction +} + +func (this *StatusAction) RunPost(params struct { + NodeId int64 +}) { + results, err := nodeutils.SendMessageToNodeIds(this.AdminContext(), []int64{params.NodeId}, messageconfigs.MessageCodeCheckLocalFirewall, &messageconfigs.CheckLocalFirewallMessage{ + Name: "nftables", + }, 10) + if err != nil { + this.ErrorPage(err) + return + } + this.Data["results"] = results + this.Success() +} diff --git a/internal/web/actions/default/clusters/cluster/node/settings/ssh/index.go b/internal/web/actions/default/clusters/cluster/node/settings/ssh/index.go index 2df68f54..892db726 100644 --- a/internal/web/actions/default/clusters/cluster/node/settings/ssh/index.go +++ b/internal/web/actions/default/clusters/cluster/node/settings/ssh/index.go @@ -32,6 +32,8 @@ func (this *IndexAction) RunGet(params struct { return } + this.Data["hostIsAutoFilled"] = false + // 登录信息 var loginMap maps.Map = nil if node.NodeLogin != nil { @@ -79,6 +81,7 @@ func (this *IndexAction) RunGet(params struct { return } if len(addressesResp.NodeIPAddresses) > 0 { + this.Data["hostIsAutoFilled"] = true loginMap = maps.Map{ "id": 0, "name": "", @@ -100,6 +103,7 @@ func (this *IndexAction) RunGet(params struct { return } if len(addressesResp.NodeIPAddresses) > 0 { + this.Data["hostIsAutoFilled"] = true loginParams["host"] = addressesResp.NodeIPAddresses[0].Ip } } diff --git a/internal/web/actions/default/clusters/cluster/settings/ddos-protection/index.go b/internal/web/actions/default/clusters/cluster/settings/ddos-protection/index.go new file mode 100644 index 00000000..161e3bfd --- /dev/null +++ b/internal/web/actions/default/clusters/cluster/settings/ddos-protection/index.go @@ -0,0 +1,106 @@ +// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. + +package ddosProtection + +import ( + "encoding/json" + "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils" + "github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs" + "github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb" + "github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/ddosconfigs" + "github.com/iwind/TeaGo/actions" + "github.com/iwind/TeaGo/types" + "net" +) + +type IndexAction struct { + actionutils.ParentAction +} + +func (this *IndexAction) Init() { + this.Nav("", "setting", "") + this.SecondMenu("ddosProtection") +} + +func (this *IndexAction) RunGet(params struct { + ClusterId int64 +}) { + this.Data["clusterId"] = params.ClusterId + + protectionResp, err := this.RPC().NodeClusterRPC().FindNodeClusterDDoSProtection(this.AdminContext(), &pb.FindNodeClusterDDoSProtectionRequest{NodeClusterId: params.ClusterId}) + if err != nil { + this.ErrorPage(err) + return + } + + var ddosProtectionConfig = ddosconfigs.DefaultProtectionConfig() + if len(protectionResp.DdosProtectionJSON) > 0 { + err = json.Unmarshal(protectionResp.DdosProtectionJSON, ddosProtectionConfig) + if err != nil { + this.ErrorPage(err) + return + } + } + this.Data["config"] = ddosProtectionConfig + this.Data["defaultConfigs"] = nodeconfigs.DefaultConfigs + + this.Show() +} + +func (this *IndexAction) RunPost(params struct { + ClusterId int64 + DdosProtectionJSON []byte + + Must *actions.Must + CSRF *actionutils.CSRF +}) { + defer this.CreateLogInfo("修改集群 %d 的DDOS防护设置", params.ClusterId) + + var ddosProtectionConfig = &ddosconfigs.ProtectionConfig{} + err := json.Unmarshal(params.DdosProtectionJSON, ddosProtectionConfig) + if err != nil { + this.ErrorPage(err) + return + } + + err = ddosProtectionConfig.Init() + if err != nil { + this.Fail("配置校验失败:" + err.Error()) + } + + // 校验参数 + if ddosProtectionConfig.TCP != nil { + var tcpConfig = ddosProtectionConfig.TCP + if tcpConfig.MaxConnectionsPerIP > 0 && tcpConfig.MaxConnectionsPerIP < nodeconfigs.DefaultTCPMinConnectionsPerIP { + this.FailField("tcpMaxConnectionsPerIP", "TCP: 单IP TCP最大连接数不能小于"+types.String(nodeconfigs.DefaultTCPMinConnectionsPerIP)) + } + + if tcpConfig.NewConnectionsRate > 0 && tcpConfig.NewConnectionsRate < nodeconfigs.DefaultTCPNewConnectionsMinRate { + this.FailField("tcpNewConnectionsRate", "TCP: 单IP连接速率不能小于"+types.String(nodeconfigs.DefaultTCPNewConnectionsMinRate)) + } + + // Port + for _, portConfig := range tcpConfig.Ports { + if portConfig.Port > 65535 { + this.Fail("端口号" + types.String(portConfig.Port) + "不能大于65535") + } + } + + // IP + for _, ipConfig := range tcpConfig.AllowIPList { + if net.ParseIP(ipConfig.IP) == nil { + this.Fail("白名单IP '" + ipConfig.IP + "' 格式错误") + } + } + } + + _, err = this.RPC().NodeClusterRPC().UpdateNodeClusterDDoSProtection(this.AdminContext(), &pb.UpdateNodeClusterDDoSProtectionRequest{ + NodeClusterId: params.ClusterId, + DdosProtectionJSON: params.DdosProtectionJSON, + }) + if err != nil { + this.ErrorPage(err) + return + } + this.Success() +} diff --git a/internal/web/actions/default/clusters/cluster/settings/ddos-protection/status.go b/internal/web/actions/default/clusters/cluster/settings/ddos-protection/status.go new file mode 100644 index 00000000..194dad27 --- /dev/null +++ b/internal/web/actions/default/clusters/cluster/settings/ddos-protection/status.go @@ -0,0 +1,71 @@ +// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. + +package ddosProtection + +import ( + "encoding/json" + "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils" + "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/nodes/nodeutils" + "github.com/TeaOSLab/EdgeCommon/pkg/messageconfigs" + "github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb" + "github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/ddosconfigs" + "github.com/iwind/TeaGo/maps" +) + +type StatusAction struct { + actionutils.ParentAction +} + +func (this *StatusAction) Init() { + this.Nav("", "setting", "") + this.SecondMenu("ddosProtection") +} + +func (this *StatusAction) RunGet(params struct { + ClusterId int64 +}) { + this.Data["clusterId"] = params.ClusterId + + this.Show() +} + +func (this *StatusAction) RunPost(params struct { + ClusterId int64 +}) { + results, err := nodeutils.SendMessageToCluster(this.AdminContext(), params.ClusterId, messageconfigs.MessageCodeCheckLocalFirewall, &messageconfigs.CheckLocalFirewallMessage{ + Name: "nftables", + }, 10) + if err != nil { + this.ErrorPage(err) + return + } + + var resultMaps = []maps.Map{} + for _, result := range results { + var resultMap = maps.Map{ + "isOk": result.IsOK, + "message": result.Message, + "nodeId": result.NodeId, + "nodeName": result.NodeName, + } + + nodeResp, err := this.RPC().NodeRPC().FindNodeDDoSProtection(this.AdminContext(), &pb.FindNodeDDoSProtectionRequest{NodeId: result.NodeId}) + if err != nil { + this.ErrorPage(err) + return + } + if len(nodeResp.DdosProtectionJSON) > 0 { + var ddosProtection = ddosconfigs.DefaultProtectionConfig() + err = json.Unmarshal(nodeResp.DdosProtectionJSON, ddosProtection) + if err != nil { + this.ErrorPage(err) + return + } + resultMap["isPrior"] = !ddosProtection.IsPriorEmpty() + } + resultMaps = append(resultMaps, resultMap) + } + + this.Data["results"] = resultMaps + this.Success() +} diff --git a/internal/web/actions/default/clusters/cluster/settings/index.go b/internal/web/actions/default/clusters/cluster/settings/index.go index ef96319c..4cc5aa04 100644 --- a/internal/web/actions/default/clusters/cluster/settings/index.go +++ b/internal/web/actions/default/clusters/cluster/settings/index.go @@ -66,34 +66,31 @@ func (this *IndexAction) RunGet(params struct { this.Data["timeZoneLocation"] = nodeconfigs.FindTimeZoneLocation(cluster.TimeZone) this.Data["cluster"] = maps.Map{ - "id": cluster.Id, - "name": cluster.Name, - "installDir": cluster.InstallDir, - "timeZone": cluster.TimeZone, - "nodeMaxThreads": cluster.NodeMaxThreads, - "nodeTCPMaxConnections": cluster.NodeTCPMaxConnections, - "autoOpenPorts": cluster.AutoOpenPorts, + "id": cluster.Id, + "name": cluster.Name, + "installDir": cluster.InstallDir, + "timeZone": cluster.TimeZone, + "nodeMaxThreads": cluster.NodeMaxThreads, + "autoOpenPorts": cluster.AutoOpenPorts, } // 默认值 this.Data["defaultNodeMaxThreads"] = nodeconfigs.DefaultMaxThreads this.Data["defaultNodeMaxThreadsMin"] = nodeconfigs.DefaultMaxThreadsMin this.Data["defaultNodeMaxThreadsMax"] = nodeconfigs.DefaultMaxThreadsMax - this.Data["defaultNodeTCPMaxConnections"] = nodeconfigs.DefaultTCPMaxConnections this.Show() } // RunPost 保存设置 func (this *IndexAction) RunPost(params struct { - ClusterId int64 - Name string - GrantId int64 - InstallDir string - TimeZone string - NodeMaxThreads int32 - NodeTCPMaxConnections int32 - AutoOpenPorts bool + ClusterId int64 + Name string + GrantId int64 + InstallDir string + TimeZone string + NodeMaxThreads int32 + AutoOpenPorts bool Must *actions.Must }) { @@ -112,14 +109,13 @@ func (this *IndexAction) RunPost(params struct { } _, err := this.RPC().NodeClusterRPC().UpdateNodeCluster(this.AdminContext(), &pb.UpdateNodeClusterRequest{ - NodeClusterId: params.ClusterId, - Name: params.Name, - NodeGrantId: params.GrantId, - InstallDir: params.InstallDir, - TimeZone: params.TimeZone, - NodeMaxThreads: params.NodeMaxThreads, - NodeTCPMaxConnections: params.NodeTCPMaxConnections, - AutoOpenPorts: params.AutoOpenPorts, + NodeClusterId: params.ClusterId, + Name: params.Name, + NodeGrantId: params.GrantId, + InstallDir: params.InstallDir, + TimeZone: params.TimeZone, + NodeMaxThreads: params.NodeMaxThreads, + AutoOpenPorts: params.AutoOpenPorts, }) if err != nil { this.ErrorPage(err) diff --git a/internal/web/actions/default/clusters/cluster/settings/init.go b/internal/web/actions/default/clusters/cluster/settings/init.go index c58f58bc..1b498f83 100644 --- a/internal/web/actions/default/clusters/cluster/settings/init.go +++ b/internal/web/actions/default/clusters/cluster/settings/init.go @@ -3,6 +3,7 @@ package settings import ( "github.com/TeaOSLab/EdgeAdmin/internal/configloaders" "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/settings/cache" + ddosProtection "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/settings/ddos-protection" "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/settings/dns" firewallActions "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/settings/firewall-actions" "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/settings/health" @@ -79,6 +80,13 @@ func init() { // WebP Prefix("/clusters/cluster/settings/webp"). GetPost("", new(webp.IndexAction)). + + // DDOS Protection + Prefix("/clusters/cluster/settings/ddos-protection"). + GetPost("", new(ddosProtection.IndexAction)). + GetPost("/status", new(ddosProtection.StatusAction)). + + // EndAll() }) } diff --git a/internal/web/actions/default/clusters/clusterutils/cluster_helper.go b/internal/web/actions/default/clusters/clusterutils/cluster_helper.go index 23614752..8f508b49 100644 --- a/internal/web/actions/default/clusters/clusterutils/cluster_helper.go +++ b/internal/web/actions/default/clusters/clusterutils/cluster_helper.go @@ -117,6 +117,13 @@ func (this *ClusterHelper) createSettingMenu(cluster *pb.NodeCluster, info *pb.F "isOn": info != nil && info.HasFirewallActions, }) + items = append(items, maps.Map{ + "name": "DDOS防护", + "url": "/clusters/cluster/settings/ddos-protection?clusterId=" + clusterId, + "isActive": selectedItem == "ddosProtection", + "isOn": info != nil && info.HasDDoSProtection, + }) + items = append(items, maps.Map{ "name": "健康检查", "url": "/clusters/cluster/settings/health?clusterId=" + clusterId, @@ -136,6 +143,11 @@ func (this *ClusterHelper) createSettingMenu(cluster *pb.NodeCluster, info *pb.F "isActive": selectedItem == "webp", "isOn": info != nil && info.WebpIsOn, }) + + items = append(items, maps.Map{ + "name": "-", + }) + items = append(items, maps.Map{ "name": "统计指标", "url": "/clusters/cluster/settings/metrics?clusterId=" + clusterId, diff --git a/web/public/js/components/cluster/ddos-protection-ip-list-config-box.js b/web/public/js/components/cluster/ddos-protection-ip-list-config-box.js new file mode 100644 index 00000000..699e0db9 --- /dev/null +++ b/web/public/js/components/cluster/ddos-protection-ip-list-config-box.js @@ -0,0 +1,112 @@ +Vue.component("ddos-protection-ip-list-config-box", { + props: ["v-ip-list"], + data: function () { + let list = this.vIpList + if (list == null) { + list = [] + } + return { + list: list, + isAdding: false, + addingIP: { + ip: "", + description: "" + } + } + }, + methods: { + add: function () { + this.isAdding = true + let that = this + setTimeout(function () { + that.$refs.addingIPInput.focus() + }) + }, + confirm: function () { + let ip = this.addingIP.ip + if (ip.length == 0) { + this.warn("请输入IP") + return + } + + let exists = false + this.list.forEach(function (v) { + if (v.ip == ip) { + exists = true + } + }) + if (exists) { + this.warn("IP '" + ip + "'已经存在") + return + } + + let that = this + Tea.Vue.$post("/ui/validateIPs") + .params({ + ips: [ip] + }) + .success(function () { + that.list.push({ + ip: ip, + description: that.addingIP.description + }) + that.notifyChange() + that.cancel() + }) + .fail(function () { + that.warn("请输入正确的IP") + }) + }, + cancel: function () { + this.isAdding = false + this.addingIP = { + ip: "", + description: "" + } + }, + remove: function (index) { + this.list.$remove(index) + this.notifyChange() + }, + warn: function (message) { + let that = this + teaweb.warn(message, function () { + that.$refs.addingIPInput.focus() + }) + }, + notifyChange: function () { + this.$emit("change", this.list) + } + }, + template: `
+
+
+ {{ipConfig.ip}} ({{ipConfig.description}}) +
+
+
+
+
+
+
+ IP + +
+
+
+
+ 备注 + +
+
+
+ +  取消 +
+
+
+
+ +
+
` +}) \ No newline at end of file diff --git a/web/public/js/components/cluster/ddos-protection-ports-config-box.js b/web/public/js/components/cluster/ddos-protection-ports-config-box.js new file mode 100644 index 00000000..30892a50 --- /dev/null +++ b/web/public/js/components/cluster/ddos-protection-ports-config-box.js @@ -0,0 +1,115 @@ +Vue.component("ddos-protection-ports-config-box", { + props: ["v-ports"], + data: function () { + let ports = this.vPorts + if (ports == null) { + ports = [] + } + return { + ports: ports, + isAdding: false, + addingPort: { + port: "", + description: "" + } + } + }, + methods: { + add: function () { + this.isAdding = true + let that = this + setTimeout(function () { + that.$refs.addingPortInput.focus() + }) + }, + confirm: function () { + let portString = this.addingPort.port + if (portString.length == 0) { + this.warn("请输入端口号") + return + } + if (!/^\d+$/.test(portString)) { + this.warn("请输入正确的端口号") + return + } + let port = parseInt(portString, 10) + if (port <= 0) { + this.warn("请输入正确的端口号") + return + } + if (port > 65535) { + this.warn("请输入正确的端口号") + return + } + + let exists = false + this.ports.forEach(function (v) { + if (v.port == port) { + exists = true + } + }) + if (exists) { + this.warn("端口号已经存在") + return + } + + this.ports.push({ + port: port, + description: this.addingPort.description + }) + this.notifyChange() + this.cancel() + }, + cancel: function () { + this.isAdding = false + this.addingPort = { + port: "", + description: "" + } + }, + remove: function (index) { + this.ports.$remove(index) + this.notifyChange() + }, + warn: function (message) { + let that = this + teaweb.warn(message, function () { + that.$refs.addingPortInput.focus() + }) + }, + notifyChange: function () { + this.$emit("change", this.ports) + } + }, + template: `
+
+
+ {{portConfig.port}} ({{portConfig.description}}) +
+
+
+
+
+
+
+ 端口 + +
+
+
+
+ 备注 + +
+
+
+ +  取消 +
+
+
+
+ +
+
` +}) \ No newline at end of file diff --git a/web/public/js/components/cluster/node-ddos-protection-config-box.js b/web/public/js/components/cluster/node-ddos-protection-config-box.js new file mode 100644 index 00000000..3bb13c7e --- /dev/null +++ b/web/public/js/components/cluster/node-ddos-protection-config-box.js @@ -0,0 +1,110 @@ +Vue.component("node-ddos-protection-config-box", { + props: ["v-ddos-protection-config", "v-default-configs", "v-is-node", "v-cluster-is-on"], + data: function () { + let config = this.vDdosProtectionConfig + if (config == null) { + config = { + tcp: { + isPrior: false, + isOn: false, + maxConnections: 0, + maxConnectionsPerIP: 0, + newConnectionsRate: 0, + allowIPList: [], + ports: [] + } + } + } + + // initialize + if (config.tcp == null) { + config.tcp = { + isPrior: false, + isOn: false, + maxConnections: 0, + maxConnectionsPerIP: 0, + newConnectionsRate: 0, + allowIPList: [], + ports: [] + } + } + + + return { + config: config, + defaultConfigs: this.vDefaultConfigs, + isNode: this.vIsNode, + + isAddingPort: false + } + }, + methods: { + changeTCPPorts: function (ports) { + this.config.tcp.ports = ports + }, + changeTCPAllowIPList: function (ipList) { + this.config.tcp.allowIPList = ipList + } + }, + template: `
+ + +

功能说明:此功能为试验性质,目前仅能防御简单的DDoS攻击,试验期间建议仅在被攻击时启用,仅支持已安装nftables v0.9以上的Linux系统。

+ +
当前节点所在集群已设置DDoS防护。
+ +

TCP设置

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
启用 + +
单节点TCP最大连接数 + +

单个节点可以接受的TCP最大连接数。如果为0,则默认为{{defaultConfigs.tcpMaxConnections}}。

+
单IP TCP最大连接数 + +

单个IP可以连接到节点的TCP最大连接数。如果为0,则默认为{{defaultConfigs.tcpMaxConnectionsPerIP}};最小值为{{defaultConfigs.tcpMinConnectionsPerIP}}。

+
单IP TCP新连接速率 +
+ + 个新连接/每分钟 +
+

单个IP可以创建TCP新连接的速率。如果为0,则默认为{{defaultConfigs.tcpNewConnectionsRate}};最小值为{{defaultConfigs.tcpNewConnectionsMinRate}}。

+
TCP端口列表 + +

默认为80和443两个端口。

+
IP白名单 + +

在白名单中的IP不受当前设置的限制。

+
+
+
` +}) \ No newline at end of file diff --git a/web/public/js/components/common/inputs.js b/web/public/js/components/common/inputs.js new file mode 100644 index 00000000..52ff7533 --- /dev/null +++ b/web/public/js/components/common/inputs.js @@ -0,0 +1,63 @@ +Vue.component("digit-input", { + props: ["value", "maxlength", "size", "min", "max", "required", "placeholder"], + mounted: function () { + let that = this + setTimeout(function () { + that.check() + }) + }, + data: function () { + let realMaxLength = this.maxlength + if (realMaxLength == null) { + realMaxLength = 20 + } + + let realSize = this.size + if (realSize == null) { + realSize = 6 + } + + return { + realValue: this.value, + realMaxLength: realMaxLength, + realSize: realSize, + isValid: true + } + }, + watch: { + realValue: function (v) { + this.notifyChange() + } + }, + methods: { + notifyChange: function () { + let v = parseInt(this.realValue.toString(), 10) + if (isNaN(v)) { + v = 0 + } + this.check() + this.$emit("input", v) + }, + check: function () { + if (this.realValue == null) { + return + } + let s = this.realValue.toString() + if (!/^\d+$/.test(s)) { + this.isValid = false + return + } + let v = parseInt(s, 10) + if (isNaN(v)) { + this.isValid = false + } else { + if (this.required) { + this.isValid = (this.min == null || this.min <= v) && (this.max == null || this.max >= v) + } else { + this.isValid = (v == 0 || (this.min == null || this.min <= v) && (this.max == null || this.max >= v)) + } + } + } + }, + template: `` +}) \ No newline at end of file diff --git a/web/public/js/components/dns/dns-resolver-config-box.js b/web/public/js/components/dns/dns-resolver-config-box.js index b158cdd5..1b188a9f 100644 --- a/web/public/js/components/dns/dns-resolver-config-box.js +++ b/web/public/js/components/dns/dns-resolver-config-box.js @@ -34,7 +34,7 @@ Vue.component("dns-resolver-config-box", { -

修改此项配置后,需要重启节点进程才会生效。

+

边缘节点使用的DNS解析库。修改此项配置后,需要重启节点进程才会生效。

diff --git a/web/public/js/components/server/prior-checkbox.js b/web/public/js/components/server/prior-checkbox.js index 427cddb9..de0cdd86 100644 --- a/web/public/js/components/server/prior-checkbox.js +++ b/web/public/js/components/server/prior-checkbox.js @@ -1,8 +1,13 @@ Vue.component("prior-checkbox", { - props: ["v-config"], + props: ["v-config", "description"], data: function () { + let description = this.description + if (description == null) { + description = "打开后可以覆盖父级或子级配置" + } return { - isPrior: this.vConfig.isPrior + isPrior: this.vConfig.isPrior, + realDescription: description } }, watch: { @@ -18,7 +23,7 @@ Vue.component("prior-checkbox", { -

[已打开] 打开后可以覆盖父级或子级配置。

+

[已打开] {{realDescription}}。

` diff --git a/web/views/@default/clusters/cluster/node/settings/ddos-protection/index.html b/web/views/@default/clusters/cluster/node/settings/ddos-protection/index.html new file mode 100644 index 00000000..c4bef9bb --- /dev/null +++ b/web/views/@default/clusters/cluster/node/settings/ddos-protection/index.html @@ -0,0 +1,17 @@ +{$layout} +{$template "/clusters/cluster/node/node_menu"} +{$template "/left_menu_with_menu"} + +
+
+ + + + + +

节点检查结果:{{checkResult.message}}

+

节点检查结果:有错误发生:{{checkResult.message}}

+ + +
+
\ No newline at end of file diff --git a/web/views/@default/clusters/cluster/node/settings/ddos-protection/index.js b/web/views/@default/clusters/cluster/node/settings/ddos-protection/index.js new file mode 100644 index 00000000..04112cb4 --- /dev/null +++ b/web/views/@default/clusters/cluster/node/settings/ddos-protection/index.js @@ -0,0 +1,15 @@ +Tea.context(function () { + this.success = NotifyReloadSuccess("保存成功") + this.checkResult = null + + this.$post(".status") + .params({ + nodeId: this.nodeId + }) + .success(function (resp) { + let results = resp.data.results + if (results.length > 0) { + this.checkResult = results[0] + } + }) +}) \ No newline at end of file diff --git a/web/views/@default/clusters/cluster/node/settings/ssh/index.html b/web/views/@default/clusters/cluster/node/settings/ssh/index.html index 2cfe75bc..bcf72628 100644 --- a/web/views/@default/clusters/cluster/node/settings/ssh/index.html +++ b/web/views/@default/clusters/cluster/node/settings/ssh/index.html @@ -13,7 +13,7 @@ SSH主机地址 -

比如192.168.1.100

+

已自动填充,需要保存比如192.168.1.100。

diff --git a/web/views/@default/clusters/cluster/settings/ddos-protection/index.html b/web/views/@default/clusters/cluster/settings/ddos-protection/index.html new file mode 100644 index 00000000..fa4079f5 --- /dev/null +++ b/web/views/@default/clusters/cluster/settings/ddos-protection/index.html @@ -0,0 +1,19 @@ +{$layout} +{$template "../menu"} +{$template "/left_menu_with_menu"} + +
+ + 设置 + 状态 + + +
+ + + + + + +
+
\ No newline at end of file diff --git a/web/views/@default/clusters/cluster/settings/ddos-protection/index.js b/web/views/@default/clusters/cluster/settings/ddos-protection/index.js new file mode 100644 index 00000000..295a9aaf --- /dev/null +++ b/web/views/@default/clusters/cluster/settings/ddos-protection/index.js @@ -0,0 +1,3 @@ +Tea.context(function () { + this.success = NotifyReloadSuccess("保存成功") +}) \ No newline at end of file diff --git a/web/views/@default/clusters/cluster/settings/ddos-protection/status.html b/web/views/@default/clusters/cluster/settings/ddos-protection/status.html new file mode 100644 index 00000000..1270a893 --- /dev/null +++ b/web/views/@default/clusters/cluster/settings/ddos-protection/status.html @@ -0,0 +1,44 @@ +{$layout} +{$template "../menu"} +{$template "/left_menu_with_menu"} + +
+ + 设置 + 状态 + + + 检查中,请耐心等待完成... + +

当前集群下暂时还没有节点。

+ + + + + + + + + + + + + + + + +
节点名称检查结果独立设置
+ {{result.nodeName}} +
+ 定制 +
+
+ {{result.message}} + {{result.message}} + + 设置 +
+ +
+ [刷新] +
\ No newline at end of file diff --git a/web/views/@default/clusters/cluster/settings/ddos-protection/status.js b/web/views/@default/clusters/cluster/settings/ddos-protection/status.js new file mode 100644 index 00000000..fd35c0e2 --- /dev/null +++ b/web/views/@default/clusters/cluster/settings/ddos-protection/status.js @@ -0,0 +1,20 @@ +Tea.context(function () { + this.isLoading = true + this.results = [] + + this.$delay(function () { + this.reload() + }) + + this.reload = function () { + this.isLoading = true + this.$post("$") + .params({ clusterId: this.clusterId }) + .success(function (resp) { + this.results = resp.data.results + }) + .done(function () { + this.isLoading = false + }) + } +}) \ No newline at end of file diff --git a/web/views/@default/clusters/cluster/settings/index.html b/web/views/@default/clusters/cluster/settings/index.html index b6c6b27f..8b3cf8ca 100644 --- a/web/views/@default/clusters/cluster/settings/index.html +++ b/web/views/@default/clusters/cluster/settings/index.html @@ -53,13 +53,6 @@

取值在{{defaultNodeMaxThreadsMin}}和{{defaultNodeMaxThreadsMax}}之间;如果为0,则默认为{{defaultNodeMaxThreads}}。

- - 单节点TCP最大连接数 - - -

如果为0,则默认为{{defaultNodeTCPMaxConnections}}。

- - 自动开放端口 diff --git a/web/views/@default/clusters/logs/index.html b/web/views/@default/clusters/logs/index.html index 5e2738e2..d4e89cd3 100644 --- a/web/views/@default/clusters/logs/index.html +++ b/web/views/@default/clusters/logs/index.html @@ -70,8 +70,8 @@ - - + + diff --git a/web/views/@default/clusters/tasks/listPopup.html b/web/views/@default/clusters/tasks/listPopup.html index 7415eeb3..4500b779 100644 --- a/web/views/@default/clusters/tasks/listPopup.html +++ b/web/views/@default/clusters/tasks/listPopup.html @@ -31,6 +31,7 @@ 同步IP名单同步脚本同步L2节点 + DDoS配置 - + - +
集群节点集群节点 信息 操作
{{task.error}} diff --git a/web/views/@default/servers/components/waf/policy.html b/web/views/@default/servers/components/waf/policy.html index d9995e38..fcaf873f 100644 --- a/web/views/@default/servers/components/waf/policy.html +++ b/web/views/@default/servers/components/waf/policy.html @@ -93,7 +93,7 @@
记录日志记录访问日志 默认 开启 diff --git a/web/views/@default/servers/components/waf/update.html b/web/views/@default/servers/components/waf/update.html index 1b8e594b..994cba9a 100644 --- a/web/views/@default/servers/components/waf/update.html +++ b/web/views/@default/servers/components/waf/update.html @@ -51,7 +51,7 @@
记录日志记录访问日志