mirror of
				https://github.com/1Panel-dev/1Panel.git
				synced 2025-11-01 03:37:19 +08:00 
			
		
		
		
	feat: 同步修改防火墙端口
This commit is contained in:
		
							parent
							
								
									1c5d01b11c
								
							
						
					
					
						commit
						8902fdc78a
					
				
					 18 changed files with 363 additions and 121 deletions
				
			
		|  | @ -112,6 +112,7 @@ type RootInfo struct { | |||
| 	ID            uint   `json:"id"` | ||||
| 	Name          string `json:"name"` | ||||
| 	Port          int64  `json:"port"` | ||||
| 	HttpsPort     int64  `json:"httpsPort"` | ||||
| 	Password      string `json:"password"` | ||||
| 	UserPassword  string `json:"userPassword"` | ||||
| 	ContainerName string `json:"containerName"` | ||||
|  | @ -152,6 +153,7 @@ func (a *AppInstallRepo) LoadBaseInfo(key string, name string) (*RootInfo, error | |||
| 		info.UserPassword = userPassword | ||||
| 	} | ||||
| 	info.Port = int64(appInstall.HttpPort) | ||||
| 	info.HttpsPort = int64(appInstall.HttpsPort) | ||||
| 	info.ID = appInstall.ID | ||||
| 	info.ContainerName = appInstall.ContainerName | ||||
| 	info.Name = appInstall.Name | ||||
|  |  | |||
|  | @ -387,6 +387,10 @@ func (a *AppInstallService) ChangeAppPort(req request.PortUpdate) error { | |||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if err := OperateFirewallPort([]int{int(appInstall.Port)}, []int{int(req.Port)}); err != nil { | ||||
| 		global.LOG.Errorf("allow firewall failed, err: %v", err) | ||||
| 	} | ||||
| 
 | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -2,9 +2,12 @@ package service | |||
| 
 | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"strconv" | ||||
| 	"strings" | ||||
| 
 | ||||
| 	"github.com/1Panel-dev/1Panel/backend/app/dto" | ||||
| 	"github.com/1Panel-dev/1Panel/backend/utils/cmd" | ||||
| 	"github.com/1Panel-dev/1Panel/backend/utils/common" | ||||
| 	"github.com/1Panel-dev/1Panel/backend/utils/firewall" | ||||
| 	fireClient "github.com/1Panel-dev/1Panel/backend/utils/firewall/client" | ||||
| 	"github.com/jinzhu/copier" | ||||
|  | @ -101,6 +104,22 @@ func (u *FirewallService) SearchWithPage(req dto.RuleSearch) (int64, interface{} | |||
| 		backDatas = datas[start:end] | ||||
| 	} | ||||
| 
 | ||||
| 	if req.Type == "port" { | ||||
| 		apps := u.loadPortByApp() | ||||
| 		for i := 0; i < len(backDatas); i++ { | ||||
| 			backDatas[i].IsUsed = common.ScanPortWithProtocol(backDatas[i].Port, backDatas[i].Protocol) | ||||
| 			if backDatas[i].Protocol == "udp" { | ||||
| 				continue | ||||
| 			} | ||||
| 			for _, app := range apps { | ||||
| 				if app.HttpPort == backDatas[i].Port || app.HttpsPort == backDatas[i].Port { | ||||
| 					backDatas[i].APPName = app.AppName | ||||
| 					break | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return int64(total), backDatas, nil | ||||
| } | ||||
| 
 | ||||
|  | @ -111,9 +130,24 @@ func (u *FirewallService) OperateFirewall(operation string) error { | |||
| 	} | ||||
| 	switch operation { | ||||
| 	case "start": | ||||
| 		return client.Start() | ||||
| 		if err := client.Start(); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		serverPort, err := settingRepo.Get(settingRepo.WithByKey("ServerPort")) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		if err := client.Port(fireClient.FireInfo{Port: serverPort.Value, Protocol: "tcp", Strategy: "accept"}, "add"); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		_, _ = cmd.Exec("systemctl restart docker") | ||||
| 		return nil | ||||
| 	case "stop": | ||||
| 		return client.Stop() | ||||
| 		if err := client.Stop(); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		_, _ = cmd.Exec("systemctl restart docker") | ||||
| 		return nil | ||||
| 	case "disablePing": | ||||
| 		return client.UpdatePingStatus("0") | ||||
| 	case "enablePing": | ||||
|  | @ -134,16 +168,47 @@ func (u *FirewallService) OperatePortRule(req dto.PortRuleOperate, reload bool) | |||
| 			return u.operatePort(client, req) | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if req.Protocol == "tcp/udp" { | ||||
| 		req.Protocol = "tcp" | ||||
| 		if err := u.operatePort(client, req); err != nil { | ||||
| 			return err | ||||
| 		if client.Name() == "firewalld" && strings.Contains(req.Port, ",") { | ||||
| 			ports := strings.Split(req.Port, ",") | ||||
| 			for _, port := range ports { | ||||
| 				if len(port) == 0 { | ||||
| 					continue | ||||
| 				} | ||||
| 				req.Port = port | ||||
| 				req.Protocol = "tcp" | ||||
| 				if err := u.operatePort(client, req); err != nil { | ||||
| 					return err | ||||
| 				} | ||||
| 				req.Protocol = "udp" | ||||
| 				if err := u.operatePort(client, req); err != nil { | ||||
| 					return err | ||||
| 				} | ||||
| 			} | ||||
| 		} else { | ||||
| 			req.Protocol = "tcp" | ||||
| 			if err := u.operatePort(client, req); err != nil { | ||||
| 				return err | ||||
| 			} | ||||
| 			req.Protocol = "udp" | ||||
| 			if err := u.operatePort(client, req); err != nil { | ||||
| 				return err | ||||
| 			} | ||||
| 		} | ||||
| 	} else { | ||||
| 		if strings.Contains(req.Port, ",") { | ||||
| 			ports := strings.Split(req.Port, ",") | ||||
| 			for _, port := range ports { | ||||
| 				req.Port = port | ||||
| 				if err := u.operatePort(client, req); err != nil { | ||||
| 					return err | ||||
| 				} | ||||
| 			} | ||||
| 		} else { | ||||
| 			if err := u.operatePort(client, req); err != nil { | ||||
| 				return err | ||||
| 			} | ||||
| 		} | ||||
| 		req.Protocol = "udp" | ||||
| 	} | ||||
| 	if err := u.operatePort(client, req); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	if reload { | ||||
| 		return client.Reload() | ||||
|  | @ -228,6 +293,26 @@ func (u *FirewallService) BacthOperateRule(req dto.BatchRuleOperate) error { | |||
| 	return client.Reload() | ||||
| } | ||||
| 
 | ||||
| func OperateFirewallPort(oldPorts, newPorts []int) error { | ||||
| 	fmt.Printf("old: %v, new: %v \n", oldPorts, newPorts) | ||||
| 	client, err := firewall.NewFirewallClient() | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	for _, port := range newPorts { | ||||
| 
 | ||||
| 		if err := client.Port(fireClient.FireInfo{Port: strconv.Itoa(port), Protocol: "tcp", Strategy: "accept"}, "add"); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
| 	for _, port := range oldPorts { | ||||
| 		if err := client.Port(fireClient.FireInfo{Port: strconv.Itoa(port), Protocol: "tcp", Strategy: "accept"}, "remove"); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
| 	return client.Reload() | ||||
| } | ||||
| 
 | ||||
| func (u *FirewallService) operatePort(client firewall.FirewallClient, req dto.PortRuleOperate) error { | ||||
| 	var fireInfo fireClient.FireInfo | ||||
| 	if err := copier.Copy(&fireInfo, &req); err != nil { | ||||
|  | @ -246,3 +331,31 @@ func (u *FirewallService) operatePort(client firewall.FirewallClient, req dto.Po | |||
| 	} | ||||
| 	return client.Port(fireInfo, req.Operation) | ||||
| } | ||||
| 
 | ||||
| type portOfApp struct { | ||||
| 	AppName   string | ||||
| 	HttpPort  string | ||||
| 	HttpsPort string | ||||
| } | ||||
| 
 | ||||
| func (u *FirewallService) loadPortByApp() []portOfApp { | ||||
| 	var datas []portOfApp | ||||
| 	apps, err := appInstallRepo.ListBy() | ||||
| 	if err != nil { | ||||
| 		return datas | ||||
| 	} | ||||
| 	for i := 0; i < len(apps); i++ { | ||||
| 		datas = append(datas, portOfApp{ | ||||
| 			AppName:   apps[i].App.Key, | ||||
| 			HttpPort:  strconv.Itoa(apps[i].HttpPort), | ||||
| 			HttpsPort: strconv.Itoa(apps[i].HttpsPort), | ||||
| 		}) | ||||
| 	} | ||||
| 	systemPort, err := settingRepo.Get(settingRepo.WithByKey("ServerPort")) | ||||
| 	if err != nil { | ||||
| 		return datas | ||||
| 	} | ||||
| 	datas = append(datas, portOfApp{AppName: "1panel", HttpPort: systemPort.Value}) | ||||
| 
 | ||||
| 	return datas | ||||
| } | ||||
|  |  | |||
|  | @ -70,7 +70,14 @@ func (u *SettingService) UpdatePort(port uint) error { | |||
| 	if common.ScanPort(int(port)) { | ||||
| 		return buserr.WithDetail(constant.ErrPortInUsed, port, nil) | ||||
| 	} | ||||
| 
 | ||||
| 	serverPort, err := settingRepo.Get(settingRepo.WithByKey("ServerPort")) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	portValue, _ := strconv.Atoi(serverPort.Value) | ||||
| 	if err := OperateFirewallPort([]int{portValue}, []int{int(port)}); err != nil { | ||||
| 		global.LOG.Errorf("set system firewall ports failed, err: %v", err) | ||||
| 	} | ||||
| 	if err := settingRepo.Update("ServerPort", strconv.Itoa(int(port))); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  |  | |||
|  | @ -11,6 +11,8 @@ import ( | |||
| 	"sort" | ||||
| 	"strconv" | ||||
| 	"strings" | ||||
| 
 | ||||
| 	"github.com/1Panel-dev/1Panel/backend/utils/cmd" | ||||
| ) | ||||
| 
 | ||||
| func CompareVersion(version1 string, version2 string) bool { | ||||
|  | @ -92,6 +94,27 @@ func ScanPort(port int) bool { | |||
| 	return false | ||||
| } | ||||
| 
 | ||||
| func ScanPortWithProtocol(port, Protocol string) bool { | ||||
| 	command := "netstat -ntpl" | ||||
| 	if Protocol == "udp" { | ||||
| 		command = "netstat -nupl" | ||||
| 	} | ||||
| 	stdout, err := cmd.Execf("%s | awk '{print $4}' ", command) | ||||
| 	if err != nil { | ||||
| 		return false | ||||
| 	} | ||||
| 	lines := strings.Split(stdout, "\n") | ||||
| 	if len(lines) == 0 { | ||||
| 		return false | ||||
| 	} | ||||
| 	for _, line := range lines { | ||||
| 		if strings.HasSuffix(line, ":"+port) { | ||||
| 			return true | ||||
| 		} | ||||
| 	} | ||||
| 	return false | ||||
| } | ||||
| 
 | ||||
| func ExistWithStrArray(str string, arr []string) bool { | ||||
| 	for _, a := range arr { | ||||
| 		if strings.Contains(a, str) { | ||||
|  |  | |||
|  | @ -20,7 +20,10 @@ func (f *Firewall) Name() string { | |||
| 
 | ||||
| func (f *Firewall) Status() (string, error) { | ||||
| 	stdout, _ := cmd.Exec("firewall-cmd --state") | ||||
| 	return strings.ReplaceAll(stdout, "\n", ""), nil | ||||
| 	if stdout == "running\n" { | ||||
| 		return "running", nil | ||||
| 	} | ||||
| 	return "not running", nil | ||||
| } | ||||
| 
 | ||||
| func (f *Firewall) Version() (string, error) { | ||||
|  | @ -83,6 +86,9 @@ func (f *Firewall) ListPort() ([]FireInfo, error) { | |||
| 	ports := strings.Split(strings.ReplaceAll(stdout, "\n", ""), " ") | ||||
| 	var datas []FireInfo | ||||
| 	for _, port := range ports { | ||||
| 		if len(port) == 0 { | ||||
| 			continue | ||||
| 		} | ||||
| 		var itemPort FireInfo | ||||
| 		if strings.Contains(port, "/") { | ||||
| 			itemPort.Port = strings.Split(port, "/")[0] | ||||
|  | @ -121,7 +127,7 @@ func (f *Firewall) ListAddress() ([]FireInfo, error) { | |||
| 			continue | ||||
| 		} | ||||
| 		itemRule := f.loadInfo(rule) | ||||
| 		if len(itemRule.Port) == 0 { | ||||
| 		if len(itemRule.Port) == 0 && len(itemRule.Address) != 0 { | ||||
| 			datas = append(datas, itemRule) | ||||
| 		} | ||||
| 	} | ||||
|  | @ -137,18 +143,22 @@ func (f *Firewall) Port(port FireInfo, operation string) error { | |||
| } | ||||
| 
 | ||||
| func (f *Firewall) RichRules(rule FireInfo, operation string) error { | ||||
| 	ruleStr := "rule family=ipv4 " | ||||
| 	if len(rule.Address) != 0 { | ||||
| 		ruleStr += fmt.Sprintf("source address=%s ", rule.Address) | ||||
| 	ruleStr := "" | ||||
| 	if strings.Contains(rule.Address, "-") { | ||||
| 		ruleStr = fmt.Sprintf("rule source ipset=%s %s", rule.Address, rule.Strategy) | ||||
| 	} else { | ||||
| 		ruleStr = "rule family=ipv4 " | ||||
| 		if len(rule.Address) != 0 { | ||||
| 			ruleStr += fmt.Sprintf("source address=%s ", rule.Address) | ||||
| 		} | ||||
| 		if len(rule.Port) != 0 { | ||||
| 			ruleStr += fmt.Sprintf("port port=%s ", rule.Port) | ||||
| 		} | ||||
| 		if len(rule.Protocol) != 0 { | ||||
| 			ruleStr += fmt.Sprintf("protocol=%s ", rule.Protocol) | ||||
| 		} | ||||
| 		ruleStr += rule.Strategy | ||||
| 	} | ||||
| 	if len(rule.Port) != 0 { | ||||
| 		ruleStr += fmt.Sprintf("port port=%s ", rule.Port) | ||||
| 	} | ||||
| 	if len(rule.Protocol) != 0 { | ||||
| 		ruleStr += fmt.Sprintf("protocol=%s ", rule.Protocol) | ||||
| 	} | ||||
| 	ruleStr += rule.Strategy | ||||
| 
 | ||||
| 	stdout, err := cmd.Execf("firewall-cmd --zone=public --%s-rich-rule '%s' --permanent", operation, ruleStr) | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("%s rich rules failed, err: %s", operation, stdout) | ||||
|  | @ -176,13 +186,15 @@ func (f *Firewall) loadInfo(line string) FireInfo { | |||
| 		switch { | ||||
| 		case strings.Contains(item, "family="): | ||||
| 			itemRule.Family = strings.ReplaceAll(item, "family=", "") | ||||
| 		case strings.Contains(item, "ipset="): | ||||
| 			itemRule.Address = strings.ReplaceAll(item, "ipset=", "") | ||||
| 		case strings.Contains(item, "address="): | ||||
| 			itemRule.Address = strings.ReplaceAll(item, "address=", "") | ||||
| 		case strings.Contains(item, "port="): | ||||
| 			itemRule.Port = strings.ReplaceAll(item, "port=", "") | ||||
| 		case strings.Contains(item, "protocol="): | ||||
| 			itemRule.Protocol = strings.ReplaceAll(item, "protocol=", "") | ||||
| 		case item == "accept" || item == "drop": | ||||
| 		case item == "accept" || item == "drop" || item == "reject": | ||||
| 			itemRule.Strategy = item | ||||
| 		} | ||||
| 	} | ||||
|  |  | |||
|  | @ -1,37 +0,0 @@ | |||
| package client | ||||
| 
 | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"strings" | ||||
| 	"testing" | ||||
| 
 | ||||
| 	"github.com/1Panel-dev/1Panel/backend/utils/ssh" | ||||
| ) | ||||
| 
 | ||||
| func TestFire(t *testing.T) { | ||||
| 	ConnInfo := ssh.ConnInfo{ | ||||
| 		Addr:     "172.16.10.234", | ||||
| 		User:     "ubuntu", | ||||
| 		AuthMode: "password", | ||||
| 		Port:     22, | ||||
| 	} | ||||
| 	output, err := ConnInfo.Run("sudo ufw status verbose") | ||||
| 	if err != nil { | ||||
| 		fmt.Println(err) | ||||
| 	} | ||||
| 
 | ||||
| 	lines := strings.Split(string(output), "\n") | ||||
| 	var datas []FireInfo | ||||
| 	isStart := false | ||||
| 	for _, line := range lines { | ||||
| 		if strings.HasPrefix(line, "--") { | ||||
| 			isStart = true | ||||
| 			continue | ||||
| 		} | ||||
| 		if !isStart { | ||||
| 			continue | ||||
| 		} | ||||
| 
 | ||||
| 	} | ||||
| 	fmt.Println(datas) | ||||
| } | ||||
|  | @ -6,6 +6,9 @@ type FireInfo struct { | |||
| 	Port     string `json:"port"` | ||||
| 	Protocol string `json:"protocol"` // tcp udp tcp/udp | ||||
| 	Strategy string `json:"strategy"` // accept drop | ||||
| 
 | ||||
| 	APPName string `json:"appName"` | ||||
| 	IsUsed  bool   `json:"isUsed"` | ||||
| } | ||||
| 
 | ||||
| type Forward struct { | ||||
|  |  | |||
|  | @ -85,6 +85,11 @@ func (f *Ufw) UpdatePingStatus(enabel string) error { | |||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	stdout, err := cmd.Exec("sudo ufw reload") | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("reload ufw setting failed, err: %v", stdout) | ||||
| 	} | ||||
| 
 | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -69,6 +69,8 @@ export namespace Host { | |||
|         port: string; | ||||
|         protocol: string; | ||||
|         strategy: string; | ||||
|         appName: string; | ||||
|         isUsed: boolean; | ||||
|     } | ||||
|     export interface RulePort { | ||||
|         operation: string; | ||||
|  |  | |||
|  | @ -1185,19 +1185,33 @@ const message = { | |||
|         cookieBlockList: 'Cookie Blacklist', | ||||
| 
 | ||||
|         firewall: 'Firewall', | ||||
|         used: 'Used', | ||||
|         unUsed: 'Unused', | ||||
|         firewallHelper: '{0} System firewall', | ||||
|         firewallNotStart: 'The firewall service is not enabled at present, please enable it first!', | ||||
|         stopFirewallHelper: | ||||
|             'If the firewall is disabled, the server loses security protection. Do you want to continue?', | ||||
|             'After the system firewall is disabled, the server loses security protection. Do you want to continue?', | ||||
|         startFirewallHelper: | ||||
|             'After the firewall is enabled, the current server security can be better protected. Do you want to continue?', | ||||
|             'After the firewall is enabled, the server security can be better protected. Do you want to continue?', | ||||
|         noPing: 'Disable ping', | ||||
|         noPingHelper: | ||||
|             'If the ping function is disabled, the server cannot be pinged. Do you want to continue the operation?', | ||||
|         noPingTitle: 'Disable ping', | ||||
|         noPingHelper: 'If the ping function is disabled, the server cannot be pinged. Do you want to continue?', | ||||
|         onPingHelper: 'If you disable ping, hackers may discover your server. Do you want to continue?', | ||||
|         protocol: 'Protocol', | ||||
|         port: 'Port', | ||||
|         changeStrategy: 'Change the {0} strategy', | ||||
|         changeStrategyIPHelper1: | ||||
|             'Change the IP address strategy to [deny]. After the IP address is set, access to the server is prohibited. Do you want to continue?', | ||||
|         changeStrategyIPHelper2: | ||||
|             'Change the IP address strategy to [allow]. After the IP address is set, normal access is restored. Do you want to continue?', | ||||
|         changeStrategyPortHelper1: | ||||
|             'Change the port policy to [drop]. After the port policy is set, external access is denied. Do you want to continue?', | ||||
|         changeStrategyPortHelper2: | ||||
|             'Change the port policy to [accept]. After the policy is set, normal port access will be restored. Do you want to continue?', | ||||
|         stop: 'Stop', | ||||
|         portFormatError: 'Please enter the correct port information!', | ||||
|         portHelper1: 'Multiple ports, such as 8080 and 8081', | ||||
|         portHelper2: 'Range port, such as 8080-8089', | ||||
|         changeStrategyHelper: | ||||
|             'Change [{1}] {0} strategy to [{2}]. After setting, {0} will access {2} externally. Do you want to continue?', | ||||
|         portHelper: 'Multiple ports can be entered, such as 80,81, or range ports, such as 80-88', | ||||
|  | @ -1209,9 +1223,10 @@ const message = { | |||
|         address: 'Specified IP', | ||||
|         allow: 'Allow', | ||||
|         deny: 'Deny', | ||||
|         addressHelper1: 'Support for multiple IP, such as 172.16.10.11 172.16.10.99', | ||||
|         addressHelper2: 'You can enter an IP address segment, for example, 172.16.10.0/24', | ||||
|         addressHelper3: 'You can enter an IP address range, such as 172.16.10.11-172.16.10.99', | ||||
|         addressFormatError: 'Please enter a valid ip address!', | ||||
|         addressHelper1: 'Multiple IP please separated with ",", such as 172.16.10.11, 172.16.10.99', | ||||
|         addressHelper2: 'IP segment, such as 172.16.10.0/24', | ||||
|         addressHelper3: 'IP address range, such as 172.16.10.11-172.16.10.99', | ||||
|         allIP: 'All IP', | ||||
|         portRule: 'Port rule', | ||||
|         ipRule: 'IP rule', | ||||
|  |  | |||
|  | @ -807,6 +807,8 @@ const message = { | |||
|         LOCAL: '服务器磁盘', | ||||
|         currentPath: '当前路径', | ||||
|         OSS: '阿里云 OSS', | ||||
|         COS: '腾讯云 cos browser', | ||||
|         KODO: '七牛云 Kodo', | ||||
|         S3: '亚马逊 S3 云存储', | ||||
|         MINIO: 'MINIO', | ||||
|         SFTP: 'SFTP', | ||||
|  | @ -1184,18 +1186,27 @@ const message = { | |||
|         cookieBlockList: 'Cookie 黑名单', | ||||
| 
 | ||||
|         firewall: '防火墙', | ||||
|         used: '已使用', | ||||
|         unUsed: '未使用', | ||||
|         firewallHelper: '{0}系统防火墙', | ||||
|         firewallNotStart: '当前未开启防火墙服务,请先开启!', | ||||
|         stopFirewallHelper: '停用系统防火墙,服务器将失去安全防护,是否继续操作?', | ||||
|         startFirewallHelper: '启用系统防火墙后,可以更好的防护当前的服务器安全,是否继续操作?', | ||||
|         stopFirewallHelper: '系统防火墙关闭后,服务器将失去安全防护,是否继续?', | ||||
|         startFirewallHelper: '系统防火墙开启后,可以更好的防护服务器安全,是否继续?', | ||||
|         noPing: '禁 ping', | ||||
|         noPingHelper: '禁 ping 后不影响服务器正常使用,但无法 ping 通服务器,是否继续操作?', | ||||
|         onPingHelper: '解除禁 ping 状态可能会被黑客发现您的服务器,是否继续操作?', | ||||
|         noPingTitle: '是否禁 ping', | ||||
|         noPingHelper: '禁 ping 后将无法 ping 通服务器,是否继续?', | ||||
|         onPingHelper: '解除禁 ping 后您的服务器可能会被黑客发现,是否继续?', | ||||
|         protocol: '协议', | ||||
|         port: '端口', | ||||
|         changeStrategy: '修改{0}策略', | ||||
|         changeStrategyHelper: '修改 [{1}] {0}策略为 [{2}],设置后该{0}将{2}外部访问,是否继续操作?', | ||||
|         portHelper: '支持输入多个端口,如 80,81 或者范围端口,如 80-88', | ||||
|         changeStrategyIPHelper1: 'IP 策略修改为【屏蔽】,设置后该 IP 将禁止访问服务器,是否继续?', | ||||
|         changeStrategyIPHelper2: 'IP 策略修改为【放行】,设置后该 IP 将恢复正常访问,是否继续?', | ||||
|         changeStrategyPortHelper1: '端口策略修改为【拒绝】,设置后端口将拒绝外部访问,是否继续?', | ||||
|         changeStrategyPortHelper2: '端口策略为【允许】,设置后端口将恢复正常访问,是否继续?', | ||||
|         stop: '禁止', | ||||
|         portFormatError: '请输入正确的端口信息!', | ||||
|         portHelper1: '多个端口,如:8080,8081', | ||||
|         portHelper2: '范围端口,如:8080-8089', | ||||
|         strategy: '策略', | ||||
|         accept: '允许', | ||||
|         drop: '拒绝', | ||||
|  | @ -1204,9 +1215,10 @@ const message = { | |||
|         address: '指定 IP', | ||||
|         allow: '放行', | ||||
|         deny: '屏蔽', | ||||
|         addressHelper1: '支持输入多个 IP ,如 172.16.10.11,172.16.10.99', | ||||
|         addressHelper2: '支持输入 IP 段,如 172.16.10.0/24', | ||||
|         addressHelper3: '支持输入 IP 范围,如 172.16.10.11-172.16.10.99', | ||||
|         addressFormatError: '请输入合法的 ip 地址!', | ||||
|         addressHelper1: '多个 IP 请用 "," 隔开:172.16.10.11,172.16.10.99', | ||||
|         addressHelper2: 'IP 段:172.16.0.0/24', | ||||
|         addressHelper3: 'IP 范围:172.16.10.10-172.16.10.19(暂不支持跨网段范围)', | ||||
|         allIP: '所有 IP', | ||||
|         portRule: '端口规则', | ||||
|         ipRule: 'IP 规则', | ||||
|  |  | |||
|  | @ -167,6 +167,15 @@ export function checkIp(value: string): boolean { | |||
|     } | ||||
| } | ||||
| 
 | ||||
| export function checkPort(value: string): boolean { | ||||
|     const reg = /^([0-9]|[1-9]\d{1,3}|[1-5]\d{4}|6[0-5]{2}[0-3][0-5])$/; | ||||
|     if (!reg.test(value) && value !== '') { | ||||
|         return true; | ||||
|     } else { | ||||
|         return false; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| export function getProvider(provider: string): string { | ||||
|     switch (provider) { | ||||
|         case 'dnsAccount': | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| <template> | ||||
|     <div v-loading="loading"> | ||||
|     <div v-loading="loading" style="position: relative"> | ||||
|         <FireRouter /> | ||||
|         <FireStatus ref="fireStatuRef" @search="search" v-model:loading="loading" v-model:status="fireStatus" /> | ||||
| 
 | ||||
|  | @ -108,6 +108,12 @@ const paginationConfig = reactive({ | |||
| }); | ||||
| 
 | ||||
| const search = async () => { | ||||
|     if (fireStatus.value !== 'running') { | ||||
|         loading.value = false; | ||||
|         data.value = []; | ||||
|         paginationConfig.total = 0; | ||||
|         return; | ||||
|     } | ||||
|     let params = { | ||||
|         type: activeTag.value, | ||||
|         info: searchName.value, | ||||
|  | @ -141,15 +147,14 @@ const onOpenDialog = async ( | |||
| }; | ||||
| 
 | ||||
| const onChangeStatus = async (row: Host.RuleInfo, status: string) => { | ||||
|     let operation = status === 'accept' ? i18n.global.t('firewall.allow') : i18n.global.t('firewall.deny'); | ||||
|     ElMessageBox.confirm( | ||||
|         i18n.global.t('firewall.changeStrategyHelper', ['IP', row.address, operation]), | ||||
|         i18n.global.t('firewall.changeStrategy', ['IP']), | ||||
|         { | ||||
|             confirmButtonText: i18n.global.t('commons.button.confirm'), | ||||
|             cancelButtonText: i18n.global.t('commons.button.cancel'), | ||||
|         }, | ||||
|     ).then(async () => { | ||||
|     let operation = | ||||
|         status === 'accept' | ||||
|             ? i18n.global.t('firewall.changeStrategyIPHelper2') | ||||
|             : i18n.global.t('firewall.changeStrategyIPHelper1'); | ||||
|     ElMessageBox.confirm(operation, i18n.global.t('firewall.changeStrategy', [' IP ']), { | ||||
|         confirmButtonText: i18n.global.t('commons.button.confirm'), | ||||
|         cancelButtonText: i18n.global.t('commons.button.cancel'), | ||||
|     }).then(async () => { | ||||
|         let params = { | ||||
|             oldRule: { | ||||
|                 operation: 'remove', | ||||
|  |  | |||
|  | @ -9,10 +9,11 @@ | |||
|                     <el-col :span="22"> | ||||
|                         <el-form-item :label="$t('firewall.address')" prop="address"> | ||||
|                             <el-input | ||||
|                                 :disabled="dialogData.title === 'edit'" | ||||
|                                 :autosize="{ minRows: 3, maxRows: 6 }" | ||||
|                                 type="textarea" | ||||
|                                 clearable | ||||
|                                 v-model="dialogData.rowData!.address" | ||||
|                                 v-model.trim="dialogData.rowData!.address" | ||||
|                             /> | ||||
|                             <span class="input-help">{{ $t('firewall.addressHelper1') }}</span> | ||||
|                             <span class="input-help">{{ $t('firewall.addressHelper2') }}</span> | ||||
|  | @ -45,10 +46,10 @@ import { Rules } from '@/global/form-rules'; | |||
| import i18n from '@/lang'; | ||||
| import { ElForm } from 'element-plus'; | ||||
| import DrawerHeader from '@/components/drawer-header/index.vue'; | ||||
| import { MsgSuccess } from '@/utils/message'; | ||||
| import { MsgError, MsgSuccess } from '@/utils/message'; | ||||
| import { Host } from '@/api/interface/host'; | ||||
| import { operateIPRule, updateAddrRule } from '@/api/modules/host'; | ||||
| import { deepCopy } from '@/utils/util'; | ||||
| import { checkIp, deepCopy } from '@/utils/util'; | ||||
| 
 | ||||
| const loading = ref(); | ||||
| const oldRule = ref<Host.RuleIP>(); | ||||
|  | @ -88,9 +89,25 @@ const onSubmit = async (formEl: FormInstance | undefined) => { | |||
|     if (!formEl) return; | ||||
|     formEl.validate(async (valid) => { | ||||
|         if (!valid) return; | ||||
|         loading.value = true; | ||||
|         dialogData.value.rowData.operation = 'add'; | ||||
|         if (!dialogData.value.rowData) return; | ||||
|         let ips = []; | ||||
|         if (dialogData.value.rowData.address.indexOf('-') !== -1) { | ||||
|             ips = dialogData.value.rowData.address.split('-'); | ||||
|         } else if (dialogData.value.rowData.address.indexOf(',') !== -1) { | ||||
|             ips = dialogData.value.rowData.address.split(','); | ||||
|         } else if (dialogData.value.rowData.address.indexOf('/') !== -1) { | ||||
|             ips.push(dialogData.value.rowData.address.split('/')[0]); | ||||
|         } else { | ||||
|             ips.push(dialogData.value.rowData.address); | ||||
|         } | ||||
|         for (const ip of ips) { | ||||
|             if (checkIp(ip)) { | ||||
|                 MsgError(i18n.global.t('firewall.addressFormatError')); | ||||
|                 return; | ||||
|             } | ||||
|         } | ||||
|         loading.value = true; | ||||
|         if (dialogData.value.title === 'create') { | ||||
|             await operateIPRule(dialogData.value.rowData) | ||||
|                 .then(() => { | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| <template> | ||||
|     <div v-loading="loading"> | ||||
|     <div v-loading="loading" style="position: relative"> | ||||
|         <FireRouter /> | ||||
| 
 | ||||
|         <FireStatus ref="fireStatuRef" @search="search" v-model:loading="loading" v-model:status="fireStatus" /> | ||||
|  | @ -42,13 +42,24 @@ | |||
|                     @search="search" | ||||
|                     :data="data" | ||||
|                 > | ||||
|                     <el-table-column type="selection" fix /> | ||||
|                     <el-table-column type="selection" :selectable="selectable" fix /> | ||||
|                     <el-table-column :label="$t('firewall.protocol')" :min-width="90" prop="protocol" /> | ||||
|                     <el-table-column :label="$t('firewall.port')" :min-width="120" prop="port" /> | ||||
|                     <el-table-column :label="$t('commons.table.status')" :min-width="120"> | ||||
|                         <template #default="{ row }"> | ||||
|                             <el-tag type="info" v-if="row.isUsed"> | ||||
|                                 {{ | ||||
|                                     row.appName ? $t('firewall.used') + ' ( ' + row.appName + ' )' : $t('firewall.used') | ||||
|                                 }} | ||||
|                             </el-tag> | ||||
|                             <el-tag type="success" v-else>{{ $t('firewall.unUsed') }}</el-tag> | ||||
|                         </template> | ||||
|                     </el-table-column> | ||||
|                     <el-table-column :min-width="80" :label="$t('firewall.strategy')" prop="strategy"> | ||||
|                         <template #default="{ row }"> | ||||
|                             <el-button | ||||
|                                 v-if="row.strategy === 'accept'" | ||||
|                                 :disabled="row.appName === '1panel'" | ||||
|                                 @click="onChangeStatus(row, 'drop')" | ||||
|                                 link | ||||
|                                 type="success" | ||||
|  | @ -111,6 +122,12 @@ const paginationConfig = reactive({ | |||
| }); | ||||
| 
 | ||||
| const search = async () => { | ||||
|     if (fireStatus.value !== 'running') { | ||||
|         loading.value = false; | ||||
|         data.value = []; | ||||
|         paginationConfig.total = 0; | ||||
|         return; | ||||
|     } | ||||
|     let params = { | ||||
|         type: activeTag.value, | ||||
|         info: searchName.value, | ||||
|  | @ -146,15 +163,14 @@ const onOpenDialog = async ( | |||
| }; | ||||
| 
 | ||||
| const onChangeStatus = async (row: Host.RuleInfo, status: string) => { | ||||
|     let operation = i18n.global.t('firewall.' + status); | ||||
|     ElMessageBox.confirm( | ||||
|         i18n.global.t('firewall.changeStrategyHelper', [i18n.global.t('firewall.port'), row.port, operation]), | ||||
|         i18n.global.t('firewall.changeStrategy', [i18n.global.t('firewall.port')]), | ||||
|         { | ||||
|             confirmButtonText: i18n.global.t('commons.button.confirm'), | ||||
|             cancelButtonText: i18n.global.t('commons.button.cancel'), | ||||
|         }, | ||||
|     ).then(async () => { | ||||
|     let operation = | ||||
|         status === 'accept' | ||||
|             ? i18n.global.t('firewall.changeStrategyPortHelper2') | ||||
|             : i18n.global.t('firewall.changeStrategyPortHelper1'); | ||||
|     ElMessageBox.confirm(operation, i18n.global.t('firewall.changeStrategy', [i18n.global.t('firewall.port')]), { | ||||
|         confirmButtonText: i18n.global.t('commons.button.confirm'), | ||||
|         cancelButtonText: i18n.global.t('commons.button.cancel'), | ||||
|     }).then(async () => { | ||||
|         let params = { | ||||
|             oldRule: { | ||||
|                 operation: 'remove', | ||||
|  | @ -227,18 +243,28 @@ const onDelete = async (row: Host.RuleInfo | null) => { | |||
|     }); | ||||
| }; | ||||
| 
 | ||||
| function selectable(row) { | ||||
|     return row.appName !== '1panel'; | ||||
| } | ||||
| 
 | ||||
| const buttons = [ | ||||
|     { | ||||
|         label: i18n.global.t('commons.button.edit'), | ||||
|         click: (row: Host.RulePort) => { | ||||
|             onOpenDialog('edit', row); | ||||
|         }, | ||||
|         disabled: (row: any) => { | ||||
|             return row.appName === '1panel'; | ||||
|         }, | ||||
|     }, | ||||
|     { | ||||
|         label: i18n.global.t('commons.button.delete'), | ||||
|         click: (row: Host.RuleInfo) => { | ||||
|             onDelete(row); | ||||
|         }, | ||||
|         disabled: (row: any) => { | ||||
|             return row.appName === '1panel'; | ||||
|         }, | ||||
|     }, | ||||
| ]; | ||||
| 
 | ||||
|  |  | |||
|  | @ -16,8 +16,13 @@ | |||
|                         </el-form-item> | ||||
| 
 | ||||
|                         <el-form-item :label="$t('firewall.port')" prop="port"> | ||||
|                             <el-input clearable v-model.trim="dialogData.rowData!.port" /> | ||||
|                             <span class="input-help">{{ $t('firewall.portHelper') }}</span> | ||||
|                             <el-input | ||||
|                                 :disabled="dialogData.title === 'edit'" | ||||
|                                 clearable | ||||
|                                 v-model.trim="dialogData.rowData!.port" | ||||
|                             /> | ||||
|                             <span class="input-help">{{ $t('firewall.portHelper1') }}</span> | ||||
|                             <span class="input-help">{{ $t('firewall.portHelper2') }}</span> | ||||
|                         </el-form-item> | ||||
| 
 | ||||
|                         <el-form-item :label="$t('firewall.source')" prop="source"> | ||||
|  | @ -62,10 +67,10 @@ import { Rules } from '@/global/form-rules'; | |||
| import i18n from '@/lang'; | ||||
| import { ElForm } from 'element-plus'; | ||||
| import DrawerHeader from '@/components/drawer-header/index.vue'; | ||||
| import { MsgSuccess } from '@/utils/message'; | ||||
| import { MsgError, MsgSuccess } from '@/utils/message'; | ||||
| import { Host } from '@/api/interface/host'; | ||||
| import { operatePortRule, updatePortRule } from '@/api/modules/host'; | ||||
| import { deepCopy } from '@/utils/util'; | ||||
| import { checkPort, deepCopy } from '@/utils/util'; | ||||
| 
 | ||||
| const loading = ref(); | ||||
| const oldRule = ref<Host.RulePort>(); | ||||
|  | @ -102,7 +107,7 @@ const handleClose = () => { | |||
| const rules = reactive({ | ||||
|     protocol: [Rules.requiredSelect], | ||||
|     port: [Rules.requiredInput], | ||||
|     address: [Rules.requiredInput], | ||||
|     address: [Rules.ip], | ||||
| }); | ||||
| 
 | ||||
| type FormInstance = InstanceType<typeof ElForm>; | ||||
|  | @ -114,6 +119,23 @@ const onSubmit = async (formEl: FormInstance | undefined) => { | |||
|         if (!valid) return; | ||||
|         dialogData.value.rowData.operation = 'add'; | ||||
|         if (!dialogData.value.rowData) return; | ||||
|         if (dialogData.value.rowData.source === 'anyWhere') { | ||||
|             dialogData.value.rowData.address = ''; | ||||
|         } | ||||
|         let ports = []; | ||||
|         if (dialogData.value.rowData.port.indexOf('-') !== -1) { | ||||
|             ports = dialogData.value.rowData.port.split('-'); | ||||
|         } else if (dialogData.value.rowData.port.indexOf(',') !== -1) { | ||||
|             ports = dialogData.value.rowData.port.split(','); | ||||
|         } else { | ||||
|             ports.push(dialogData.value.rowData.port); | ||||
|         } | ||||
|         for (const port of ports) { | ||||
|             if (checkPort(port)) { | ||||
|                 MsgError(i18n.global.t('firewall.portFormatError')); | ||||
|                 return; | ||||
|             } | ||||
|         } | ||||
|         loading.value = true; | ||||
|         if (dialogData.value.title === 'create') { | ||||
|             await operatePortRule(dialogData.value.rowData) | ||||
|  |  | |||
|  | @ -16,22 +16,21 @@ | |||
|                         <el-button type="primary" @click="onOperate('stop')" link> | ||||
|                             {{ $t('commons.button.stop') }} | ||||
|                         </el-button> | ||||
|                         <el-divider direction="vertical" /> | ||||
|                         <el-button type="primary" link>{{ $t('firewall.noPing') }}</el-button> | ||||
|                         <el-switch | ||||
|                             style="margin-left: 10px" | ||||
|                             inactive-value="Disable" | ||||
|                             active-value="Enable" | ||||
|                             @change="onPingOperate(baseInfo.pingStatus)" | ||||
|                             v-model="onPing" | ||||
|                         /> | ||||
|                     </span> | ||||
| 
 | ||||
|                     <span v-if="baseInfo.status === 'not running'" class="buttons"> | ||||
|                         <el-button type="primary" @click="onOperate('start')" link> | ||||
|                             {{ $t('commons.button.start') }} | ||||
|                         </el-button> | ||||
|                     </span> | ||||
|                     <el-divider direction="vertical" /> | ||||
|                     <el-button type="primary" link>{{ $t('firewall.noPing') }}</el-button> | ||||
|                     <el-switch | ||||
|                         style="margin-left: 10px" | ||||
|                         inactive-value="Disable" | ||||
|                         active-value="Enable" | ||||
|                         @change="onPingOperate(baseInfo.pingStatus)" | ||||
|                         v-model="onPing" | ||||
|                     /> | ||||
|                 </div> | ||||
|             </el-card> | ||||
|         </div> | ||||
|  | @ -42,11 +41,12 @@ | |||
| import { Host } from '@/api/interface/host'; | ||||
| import { loadFireBaseInfo, operateFire } from '@/api/modules/host'; | ||||
| import i18n from '@/lang'; | ||||
| import { MsgSuccess } from '@/utils/message'; | ||||
| import { ElMessageBox } from 'element-plus'; | ||||
| import { ref } from 'vue'; | ||||
| 
 | ||||
| const baseInfo = ref<Host.FirewallBase>({ status: '', name: '', version: '', pingStatus: '' }); | ||||
| const onPing = ref(); | ||||
| const onPing = ref('Disable'); | ||||
| 
 | ||||
| const acceptParams = (): void => { | ||||
|     loadBaseInfo(true); | ||||
|  | @ -59,7 +59,7 @@ const loadBaseInfo = async (search: boolean) => { | |||
|             baseInfo.value = res.data; | ||||
|             onPing.value = baseInfo.value.pingStatus; | ||||
|             emit('update:status', baseInfo.value.status); | ||||
|             if (baseInfo.value.status === 'running' && search) { | ||||
|             if (search) { | ||||
|                 emit('search'); | ||||
|             } else { | ||||
|                 emit('update:loading', false); | ||||
|  | @ -81,6 +81,7 @@ const onOperate = async (operation: string) => { | |||
|         emit('update:status', 'running'); | ||||
|         await operateFire(operation) | ||||
|             .then(() => { | ||||
|                 MsgSuccess(i18n.global.t('commons.msg.operationSuccess')); | ||||
|                 loadBaseInfo(true); | ||||
|             }) | ||||
|             .catch(() => { | ||||
|  | @ -92,7 +93,7 @@ const onOperate = async (operation: string) => { | |||
| const onPingOperate = async (operation: string) => { | ||||
|     let operationHelper = | ||||
|         operation === 'Enabel' ? i18n.global.t('firewall.noPingHelper') : i18n.global.t('firewall.onPingHelper'); | ||||
|     ElMessageBox.confirm(operationHelper, i18n.global.t('firewall.noPing'), { | ||||
|     ElMessageBox.confirm(operationHelper, i18n.global.t('firewall.noPingTitle'), { | ||||
|         confirmButtonText: i18n.global.t('commons.button.confirm'), | ||||
|         cancelButtonText: i18n.global.t('commons.button.cancel'), | ||||
|     }) | ||||
|  | @ -102,6 +103,7 @@ const onPingOperate = async (operation: string) => { | |||
|             operation = operation === 'Disable' ? 'enablePing' : 'disablePing'; | ||||
|             await operateFire(operation) | ||||
|                 .then(() => { | ||||
|                     MsgSuccess(i18n.global.t('commons.msg.operationSuccess')); | ||||
|                     loadBaseInfo(false); | ||||
|                 }) | ||||
|                 .catch(() => { | ||||
|  |  | |||
		Loading…
	
	Add table
		
		Reference in a new issue