From d5f446d7cf24caa645fddb50b2a4774283eb80cb Mon Sep 17 00:00:00 2001 From: ssongliu Date: Mon, 27 Mar 2023 19:02:36 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=AE=8C=E6=88=90=E5=89=8D=E7=AB=AF?= =?UTF-8?q?=E7=AB=AF=E5=8F=A3=E8=A7=84=E5=88=99=E8=AE=BE=E7=BD=AE=E5=8A=9F?= =?UTF-8?q?=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/app/api/v1/entry.go | 7 +- backend/app/api/v1/firewall.go | 101 ++++++++++++ backend/app/dto/firewall.go | 20 +++ backend/app/service/entry.go | 1 + backend/app/service/firewall.go | 85 ++++++++++ backend/router/ro_host.go | 4 + backend/utils/firewall/client.go | 13 +- backend/utils/firewall/client/firewalld.go | 89 +++++++---- .../utils/firewall/client/firewalld_test.go | 36 ++--- backend/utils/firewall/client/info.go | 18 +-- backend/utils/firewall/client/ufw.go | 148 ++++++++++++------ frontend/src/api/interface/host.ts | 25 +++ frontend/src/api/modules/host.ts | 11 ++ frontend/src/lang/modules/zh.ts | 14 +- frontend/src/routers/modules/host.ts | 19 +++ frontend/src/views/host/firewall/index.vue | 25 +++ frontend/src/views/host/firewall/ip/index.vue | 101 ++++++++++++ .../views/host/firewall/port/create/index.vue | 114 ++++++++++++++ .../src/views/host/firewall/port/index.vue | 141 +++++++++++++++++ frontend/src/views/host/security/index.vue | 7 - 20 files changed, 847 insertions(+), 132 deletions(-) create mode 100644 backend/app/api/v1/firewall.go create mode 100644 backend/app/dto/firewall.go create mode 100644 backend/app/service/firewall.go create mode 100644 frontend/src/views/host/firewall/index.vue create mode 100644 frontend/src/views/host/firewall/ip/index.vue create mode 100644 frontend/src/views/host/firewall/port/create/index.vue create mode 100644 frontend/src/views/host/firewall/port/index.vue delete mode 100644 frontend/src/views/host/security/index.vue diff --git a/backend/app/api/v1/entry.go b/backend/app/api/v1/entry.go index 1274ab4cf..5824be5a2 100644 --- a/backend/app/api/v1/entry.go +++ b/backend/app/api/v1/entry.go @@ -26,9 +26,10 @@ var ( cronjobService = service.ServiceGroupApp.CronjobService - hostService = service.ServiceGroupApp.HostService - groupService = service.ServiceGroupApp.GroupService - fileService = service.ServiceGroupApp.FileService + hostService = service.ServiceGroupApp.HostService + groupService = service.ServiceGroupApp.GroupService + fileService = service.ServiceGroupApp.FileService + firewallService = service.NewIFirewallService() settingService = service.ServiceGroupApp.SettingService backupService = service.ServiceGroupApp.BackupService diff --git a/backend/app/api/v1/firewall.go b/backend/app/api/v1/firewall.go new file mode 100644 index 000000000..b2429068e --- /dev/null +++ b/backend/app/api/v1/firewall.go @@ -0,0 +1,101 @@ +package v1 + +import ( + "github.com/1Panel-dev/1Panel/backend/app/api/v1/helper" + "github.com/1Panel-dev/1Panel/backend/app/dto" + "github.com/1Panel-dev/1Panel/backend/constant" + "github.com/1Panel-dev/1Panel/backend/global" + "github.com/gin-gonic/gin" +) + +// @Tags Firewall +// @Summary Page firewall rules +// @Description 获取防火墙规则列表分页 +// @Accept json +// @Param request body dto.SearchWithPage true "request" +// @Success 200 {object} dto.PageResult +// @Security ApiKeyAuth +// @Router /hosts/firewall/search [post] +func (b *BaseApi) SearchFirewallRule(c *gin.Context) { + var req dto.RuleSearch + if err := c.ShouldBindJSON(&req); err != nil { + helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err) + return + } + + total, list, err := firewallService.SearchWithPage(req) + if err != nil { + helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err) + return + } + + helper.SuccessWithData(c, dto.PageResult{ + Items: list, + Total: total, + }) +} + +// @Tags Firewall +// @Summary Create group +// @Description 创建防火墙端口规则 +// @Accept json +// @Param request body dto.PortRuleOperate true "request" +// @Success 200 +// @Security ApiKeyAuth +// @Router /hosts/firewall/port [post] +// @x-panel-log {"bodyKeys":["port","strategy"],"paramKeys":[],"BeforeFuntions":[],"formatZH":"添加端口规则 {[strategy] [port]}","formatEN":"create port rules {[strategy][port]}"} +func (b *BaseApi) OperatePortRule(c *gin.Context) { + var req dto.PortRuleOperate + if err := c.ShouldBindJSON(&req); err != nil { + helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err) + return + } + if err := global.VALID.Struct(req); err != nil { + helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err) + return + } + if req.Protocol == "tcp/udp" { + req.Protocol = "tcp" + if err := firewallService.OperatePortRule(req); err != nil { + helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err) + return + } + req.Protocol = "udp" + if err := firewallService.OperatePortRule(req); err != nil { + helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err) + return + } + helper.SuccessWithData(c, nil) + } + if err := firewallService.OperatePortRule(req); err != nil { + helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err) + return + } + helper.SuccessWithData(c, nil) +} + +// @Tags Firewall +// @Summary Create group +// @Description 创建防火墙 IP 规则 +// @Accept json +// @Param request body dto.AddressCreate true "request" +// @Success 200 +// @Security ApiKeyAuth +// @Router /hosts/firewall/ip [post] +// @x-panel-log {"bodyKeys":["strategy","address"],"paramKeys":[],"BeforeFuntions":[],"formatZH":"添加 ip 规则 {[strategy] [address]}","formatEN":"create address rules {[strategy][address]}"} +func (b *BaseApi) OperateIPRule(c *gin.Context) { + var req dto.AddrRuleOperate + if err := c.ShouldBindJSON(&req); err != nil { + helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err) + return + } + if err := global.VALID.Struct(req); err != nil { + helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err) + return + } + if err := firewallService.OperateAddressRule(req); err != nil { + helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err) + return + } + helper.SuccessWithData(c, nil) +} diff --git a/backend/app/dto/firewall.go b/backend/app/dto/firewall.go new file mode 100644 index 000000000..047b6c154 --- /dev/null +++ b/backend/app/dto/firewall.go @@ -0,0 +1,20 @@ +package dto + +type RuleSearch struct { + PageInfo + Type string `json:"type" validate:"required"` +} + +type PortRuleOperate struct { + Operation string `json:"operation" validate:"required,oneof=add remove"` + Address string `json:"address"` + Port string `json:"port" validate:"required"` + Protocol string `json:"protocol" validate:"required,oneof=tcp udp tcp/upd"` + Strategy string `json:"strategy" validate:"required,oneof=accept drop"` +} + +type AddrRuleOperate struct { + Operation string `json:"operation" validate:"required,oneof=add remove"` + Address string `json:"address" validate:"required"` + Strategy string `json:"strategy" validate:"required,oneof=accept drop"` +} diff --git a/backend/app/service/entry.go b/backend/app/service/entry.go index a70e3809d..98f216c72 100644 --- a/backend/app/service/entry.go +++ b/backend/app/service/entry.go @@ -24,6 +24,7 @@ type ServiceGroup struct { GroupService CommandService FileService + FirewallService SettingService BackupService diff --git a/backend/app/service/firewall.go b/backend/app/service/firewall.go new file mode 100644 index 000000000..a7d01176a --- /dev/null +++ b/backend/app/service/firewall.go @@ -0,0 +1,85 @@ +package service + +import ( + "github.com/1Panel-dev/1Panel/backend/app/dto" + "github.com/1Panel-dev/1Panel/backend/utils/firewall" + fireClient "github.com/1Panel-dev/1Panel/backend/utils/firewall/client" + "github.com/jinzhu/copier" +) + +type FirewallService struct{} + +type IFirewallService interface { + SearchWithPage(search dto.RuleSearch) (int64, interface{}, error) + OperatePortRule(req dto.PortRuleOperate) error + OperateAddressRule(req dto.AddrRuleOperate) error +} + +func NewIFirewallService() IFirewallService { + return &FirewallService{} +} + +func (u *FirewallService) SearchWithPage(req dto.RuleSearch) (int64, interface{}, error) { + var ( + datas []fireClient.FireInfo + backDatas []fireClient.FireInfo + ) + client, err := firewall.NewFirewallClient() + if err != nil { + return 0, nil, err + } + if req.Type == "port" { + ports, err := client.ListPort() + if err != nil { + return 0, nil, err + } + datas = ports + } else { + address, err := client.ListAddress() + if err != nil { + return 0, nil, err + } + datas = address + } + total, start, end := len(datas), (req.Page-1)*req.PageSize, req.Page*req.PageSize + if start > total { + backDatas = make([]fireClient.FireInfo, 0) + } else { + if end >= total { + end = total + } + backDatas = datas[start:end] + } + + return int64(total), backDatas, nil +} + +func (u *FirewallService) OperatePortRule(req dto.PortRuleOperate) error { + client, err := firewall.NewFirewallClient() + if err != nil { + return err + } + + var fireInfo fireClient.FireInfo + if err := copier.Copy(&fireInfo, &req); err != nil { + return err + } + + if len(fireInfo.Address) != 0 || fireInfo.Strategy == "drop" { + return client.RichRules(fireInfo, req.Operation) + } + return client.Port(fireInfo, req.Operation) +} + +func (u *FirewallService) OperateAddressRule(req dto.AddrRuleOperate) error { + client, err := firewall.NewFirewallClient() + if err != nil { + return err + } + + var fireInfo fireClient.FireInfo + if err := copier.Copy(&fireInfo, &req); err != nil { + return err + } + return client.RichRules(fireInfo, req.Operation) +} diff --git a/backend/router/ro_host.go b/backend/router/ro_host.go index 17edc59f1..7e987d445 100644 --- a/backend/router/ro_host.go +++ b/backend/router/ro_host.go @@ -26,6 +26,10 @@ func (s *HostRouter) InitHostRouter(Router *gin.RouterGroup) { hostRouter.POST("/test/byid/:id", baseApi.TestByID) hostRouter.GET(":id", baseApi.GetHostInfo) + hostRouter.POST("/firewall/search", baseApi.SearchFirewallRule) + hostRouter.POST("/firewall/port", baseApi.OperatePortRule) + hostRouter.POST("/firewall/ip", baseApi.OperateIPRule) + hostRouter.GET("/command", baseApi.ListCommand) hostRouter.POST("/command", baseApi.CreateCommand) hostRouter.POST("/command/del", baseApi.DeleteCommand) diff --git a/backend/utils/firewall/client.go b/backend/utils/firewall/client.go index 4ad32b2c3..2dc599f11 100644 --- a/backend/utils/firewall/client.go +++ b/backend/utils/firewall/client.go @@ -1,9 +1,6 @@ package firewall import ( - "errors" - "os" - "github.com/1Panel-dev/1Panel/backend/utils/firewall/client" ) @@ -13,7 +10,7 @@ type FirewallClient interface { Reload() error Status() (string, error) ListPort() ([]client.FireInfo, error) - ListRichRules() ([]client.FireInfo, error) + ListAddress() ([]client.FireInfo, error) Port(port client.FireInfo, operation string) error RichRules(rule client.FireInfo, operation string) error @@ -21,11 +18,11 @@ type FirewallClient interface { } func NewFirewallClient() (FirewallClient, error) { - if _, err := os.Stat("/usr/sbin/firewalld"); err == nil { - return client.NewFirewalld() - } + // if _, err := os.Stat("/usr/sbin/firewalld"); err == nil { + return client.NewFirewalld() + // } // if _, err := os.Stat("/usr/sbin/ufw"); err == nil { // return client.NewUfw() // } - return nil, errors.New("no such type") + // return nil, errors.New("no such type") } diff --git a/backend/utils/firewall/client/firewalld.go b/backend/utils/firewall/client/firewalld.go index 5cae11626..34cca4cf4 100644 --- a/backend/utils/firewall/client/firewalld.go +++ b/backend/utils/firewall/client/firewalld.go @@ -4,17 +4,25 @@ import ( "fmt" "strings" - "github.com/1Panel-dev/1Panel/backend/utils/cmd" + "github.com/1Panel-dev/1Panel/backend/utils/ssh" ) -type Firewall struct{} +type Firewall struct { + Client ssh.ConnInfo +} func NewFirewalld() (*Firewall, error) { - return &Firewall{}, nil + ConnInfo := ssh.ConnInfo{ + Addr: "172.16.10.143", + User: "root", + AuthMode: "password", + Port: 22, + } + return &Firewall{Client: ConnInfo}, nil } func (f *Firewall) Status() (string, error) { - stdout, err := cmd.Exec("firewall-cmd --state") + stdout, err := f.Client.Run("firewall-cmd --state") if err != nil { return "", fmt.Errorf("load the firewall status failed, err: %s", stdout) } @@ -22,7 +30,7 @@ func (f *Firewall) Status() (string, error) { } func (f *Firewall) Start() error { - stdout, err := cmd.Exec("systemctl start firewalld") + stdout, err := f.Client.Run("systemctl start firewalld") if err != nil { return fmt.Errorf("enable the firewall failed, err: %s", stdout) } @@ -30,7 +38,7 @@ func (f *Firewall) Start() error { } func (f *Firewall) Stop() error { - stdout, err := cmd.Exec("systemctl stop firewalld") + stdout, err := f.Client.Run("systemctl stop firewalld") if err != nil { return fmt.Errorf("stop the firewall failed, err: %s", stdout) } @@ -38,7 +46,7 @@ func (f *Firewall) Stop() error { } func (f *Firewall) Reload() error { - stdout, err := cmd.Exec("firewall-cmd --reload") + stdout, err := f.Client.Run("firewall-cmd --reload") if err != nil { return fmt.Errorf("reload firewall failed, err: %s", stdout) } @@ -46,7 +54,7 @@ func (f *Firewall) Reload() error { } func (f *Firewall) ListPort() ([]FireInfo, error) { - stdout, err := cmd.Exec("firewall-cmd --zone=public --list-ports") + stdout, err := f.Client.Run("firewall-cmd --zone=public --list-ports") if err != nil { return nil, err } @@ -58,13 +66,29 @@ func (f *Firewall) ListPort() ([]FireInfo, error) { itemPort.Port = strings.Split(port, "/")[0] itemPort.Protocol = strings.Split(port, "/")[1] } + itemPort.Strategy = "accept" datas = append(datas, itemPort) } + + stdout1, err := f.Client.Run("firewall-cmd --zone=public --list-rich-rules") + if err != nil { + return nil, err + } + rules := strings.Split(stdout1, "\n") + for _, rule := range rules { + if len(rule) == 0 { + continue + } + itemRule := f.loadInfo(rule) + if len(itemRule.Port) != 0 { + datas = append(datas, itemRule) + } + } return datas, nil } -func (f *Firewall) ListRichRules() ([]FireInfo, error) { - stdout, err := cmd.Exec("firewall-cmd --zone=public --list-rich-rules") +func (f *Firewall) ListAddress() ([]FireInfo, error) { + stdout, err := f.Client.Run("firewall-cmd --zone=public --list-rich-rules") if err != nil { return nil, err } @@ -74,29 +98,16 @@ func (f *Firewall) ListRichRules() ([]FireInfo, error) { if len(rule) == 0 { continue } - var itemRule FireInfo - ruleInfo := strings.Split(strings.ReplaceAll(rule, "\"", ""), " ") - for _, item := range ruleInfo { - switch { - case strings.Contains(item, "family="): - itemRule.Family = strings.ReplaceAll(item, "family=", "") - 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": - itemRule.Strategy = item - } + itemRule := f.loadInfo(rule) + if len(itemRule.Port) == 0 { + datas = append(datas, itemRule) } - datas = append(datas, itemRule) } return datas, nil } func (f *Firewall) Port(port FireInfo, operation string) error { - stdout, err := cmd.Execf("firewall-cmd --zone=public --%s-port=%s/%s --permanent", operation, port.Port, port.Protocol) + stdout, err := f.Client.Run(fmt.Sprintf("firewall-cmd --zone=public --%s-port=%s/%s --permanent", operation, port.Port, port.Protocol)) if err != nil { return fmt.Errorf("%s port failed, err: %s", operation, stdout) } @@ -119,7 +130,7 @@ func (f *Firewall) RichRules(rule FireInfo, operation string) error { } ruleStr += rule.Strategy - stdout, err := cmd.Execf("firewall-cmd --zone=public --%s-rich-rule '%s' --permanent", operation, ruleStr) + stdout, err := f.Client.Run(fmt.Sprintf("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) } @@ -135,7 +146,7 @@ func (f *Firewall) PortForward(info Forward, operation string) error { ruleStr = fmt.Sprintf("firewall-cmd --%s-forward-port=port=%s:proto=%s:toaddr=%s:toport=%s --permanent", operation, info.Port, info.Protocol, info.Address, info.Target) } - stdout, err := cmd.Exec(ruleStr) + stdout, err := f.Client.Run(ruleStr) if err != nil { return fmt.Errorf("%s port forward failed, err: %s", operation, stdout) } @@ -144,3 +155,23 @@ func (f *Firewall) PortForward(info Forward, operation string) error { } return nil } + +func (f *Firewall) loadInfo(line string) FireInfo { + var itemRule FireInfo + ruleInfo := strings.Split(strings.ReplaceAll(line, "\"", ""), " ") + for _, item := range ruleInfo { + switch { + case strings.Contains(item, "family="): + itemRule.Family = strings.ReplaceAll(item, "family=", "") + 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": + itemRule.Strategy = item + } + } + return itemRule +} diff --git a/backend/utils/firewall/client/firewalld_test.go b/backend/utils/firewall/client/firewalld_test.go index 19b0f4e46..854ddbca4 100644 --- a/backend/utils/firewall/client/firewalld_test.go +++ b/backend/utils/firewall/client/firewalld_test.go @@ -1,7 +1,6 @@ package client import ( - "encoding/json" "fmt" "strings" "testing" @@ -11,41 +10,28 @@ import ( 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 numbered") + output, err := ConnInfo.Run("sudo ufw status verbose") if err != nil { fmt.Println(err) } lines := strings.Split(string(output), "\n") - var rules []UfwRule + var datas []FireInfo + isStart := false for _, line := range lines { - if line == "" || !strings.HasPrefix(line, "[") { + if strings.HasPrefix(line, "--") { + isStart = true + continue + } + if !isStart { continue } - fields := strings.Fields(line) - rule := UfwRule{Status: fields[0], From: fields[1], To: fields[2], Proto: fields[3], Comment: strings.Join(fields[4:], " ")} - rules = append(rules, rule) - } - ufwStatus := UfwStatus{Rules: rules} - ufwStatusJSON, err := json.MarshalIndent(ufwStatus, "", " ") - if err != nil { - fmt.Println("Error:", err) - } - fmt.Println(string(ufwStatusJSON)) -} - -type UfwRule struct { - Status string `json:"status"` - From string `json:"from"` - To string `json:"to"` - Proto string `json:"proto"` - Comment string `json:"comment"` -} -type UfwStatus struct { - Rules []UfwRule `json:"rules"` + } + fmt.Println(datas) } diff --git a/backend/utils/firewall/client/info.go b/backend/utils/firewall/client/info.go index ae44bf527..9f9a0a9f6 100644 --- a/backend/utils/firewall/client/info.go +++ b/backend/utils/firewall/client/info.go @@ -1,16 +1,16 @@ package client type FireInfo struct { - Family string - Address string - Port string - Protocol string - Strategy string + Family string `json:"family"` // ipv4 ipv6 + Address string `json:"address"` // Anywhere + Port string `json:"port"` + Protocol string `json:"protocol"` // tcp udp tcp/upd + Strategy string `json:"strategy"` // accept drop } type Forward struct { - Protocol string - Address string - Port string - Target string + Protocol string `json:"protocol"` + Address string `json:"address"` + Port string `json:"port"` + Target string `json:"target"` } diff --git a/backend/utils/firewall/client/ufw.go b/backend/utils/firewall/client/ufw.go index 095eaffb5..0ff449ff0 100644 --- a/backend/utils/firewall/client/ufw.go +++ b/backend/utils/firewall/client/ufw.go @@ -4,17 +4,25 @@ import ( "fmt" "strings" - "github.com/1Panel-dev/1Panel/backend/utils/cmd" + "github.com/1Panel-dev/1Panel/backend/utils/ssh" ) -type Ufw struct{} +type Ufw struct { + Client ssh.ConnInfo +} func NewUfw() (*Ufw, error) { - return &Ufw{}, nil + ConnInfo := ssh.ConnInfo{ + Addr: "172.16.10.234", + User: "ubuntu", + AuthMode: "password", + Port: 22, + } + return &Ufw{Client: ConnInfo}, nil } func (f *Ufw) Status() (string, error) { - stdout, err := cmd.Exec("sudo ufw status") + stdout, err := f.Client.Run("sudo ufw status") if err != nil { return "", fmt.Errorf("load the firewall status failed, err: %s", stdout) } @@ -25,7 +33,7 @@ func (f *Ufw) Status() (string, error) { } func (f *Ufw) Start() error { - stdout, err := cmd.Exec("sudo ufw enable") + stdout, err := f.Client.Run("sudo ufw enable") if err != nil { return fmt.Errorf("enable the firewall failed, err: %s", stdout) } @@ -33,7 +41,7 @@ func (f *Ufw) Start() error { } func (f *Ufw) Stop() error { - stdout, err := cmd.Exec("sudo ufw disable") + stdout, err := f.Client.Run("sudo ufw disable") if err != nil { return fmt.Errorf("stop the firewall failed, err: %s", stdout) } @@ -41,7 +49,7 @@ func (f *Ufw) Stop() error { } func (f *Ufw) Reload() error { - stdout, err := cmd.Exec("sudo ufw reload") + stdout, err := f.Client.Run("sudo ufw reload") if err != nil { return fmt.Errorf("reload firewall failed, err: %s", stdout) } @@ -49,51 +57,49 @@ func (f *Ufw) Reload() error { } func (f *Ufw) ListPort() ([]FireInfo, error) { - stdout, err := cmd.Exec("firewall-cmd --zone=public --list-ports") + stdout, err := f.Client.Run("sudo ufw status verbose") if err != nil { return nil, err } - ports := strings.Split(strings.ReplaceAll(stdout, "\n", ""), " ") + portInfos := strings.Split(strings.ReplaceAll(stdout, "\n", ""), " ") var datas []FireInfo - for _, port := range ports { - var itemPort FireInfo - if strings.Contains(port, "/") { - itemPort.Port = strings.Split(port, "/")[0] - itemPort.Protocol = strings.Split(port, "/")[1] + isStart := false + for _, line := range portInfos { + if strings.HasPrefix(line, "--") { + isStart = true + continue + } + if !isStart { + continue + } + itemFire := f.loadInfo(line, "port") + if len(itemFire.Address) != 0 { + datas = append(datas, itemFire) } - datas = append(datas, itemPort) } return datas, nil } -func (f *Ufw) ListRichRules() ([]FireInfo, error) { - stdout, err := cmd.Exec("firewall-cmd --zone=public --list-rich-rules") +func (f *Ufw) ListAddress() ([]FireInfo, error) { + stdout, err := f.Client.Run("sudo ufw status verbose") if err != nil { return nil, err } + portInfos := strings.Split(strings.ReplaceAll(stdout, "\n", ""), " ") var datas []FireInfo - rules := strings.Split(stdout, "\n") - for _, rule := range rules { - if len(rule) == 0 { + isStart := false + for _, line := range portInfos { + if strings.HasPrefix(line, "--") { + isStart = true continue } - var itemRule FireInfo - ruleInfo := strings.Split(strings.ReplaceAll(rule, "\"", ""), " ") - for _, item := range ruleInfo { - switch { - case strings.Contains(item, "family="): - itemRule.Family = strings.ReplaceAll(item, "family=", "") - 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": - itemRule.Strategy = item - } + if !isStart { + continue + } + itemFire := f.loadInfo(line, "address") + if len(itemFire.Address) != 0 { + datas = append(datas, itemFire) } - datas = append(datas, itemRule) } return datas, nil } @@ -108,11 +114,11 @@ func (f *Ufw) Port(port FireInfo, operation string) error { return fmt.Errorf("unsupport operation %s", operation) } - command := fmt.Sprintf("ufw %s %s", operation, port.Port) + command := fmt.Sprintf("sudo ufw %s %s", operation, port.Port) if len(port.Protocol) != 0 { command += fmt.Sprintf("/%s", port.Protocol) } - stdout, err := cmd.Exec(command) + stdout, err := f.Client.Run(command) if err != nil { return fmt.Errorf("%s port failed, err: %s", operation, stdout) } @@ -120,25 +126,21 @@ func (f *Ufw) Port(port FireInfo, operation string) error { } func (f *Ufw) RichRules(rule FireInfo, operation string) error { - ruleStr := "rule family=ipv4 " + ruleStr := "sudo ufw " + if len(rule.Protocol) != 0 { + ruleStr += fmt.Sprintf("proto %s ", rule.Protocol) + } if len(rule.Address) != 0 { - ruleStr += fmt.Sprintf("source address=%s ", rule.Address) + ruleStr += fmt.Sprintf("from %s ", rule.Address) } if len(rule.Port) != 0 { - ruleStr += fmt.Sprintf("port port=%s ", rule.Port) + ruleStr += fmt.Sprintf("to any 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) + stdout, err := f.Client.Run(ruleStr) if err != nil { return fmt.Errorf("%s rich rules failed, err: %s", operation, stdout) } - if err := f.Reload(); err != nil { - return err - } return nil } @@ -148,7 +150,7 @@ func (f *Ufw) PortForward(info Forward, operation string) error { ruleStr = fmt.Sprintf("firewall-cmd --%s-forward-port=port=%s:proto=%s:toaddr=%s:toport=%s --permanent", operation, info.Port, info.Protocol, info.Address, info.Target) } - stdout, err := cmd.Exec(ruleStr) + stdout, err := f.Client.Run(ruleStr) if err != nil { return fmt.Errorf("%s port forward failed, err: %s", operation, stdout) } @@ -157,3 +159,49 @@ func (f *Ufw) PortForward(info Forward, operation string) error { } return nil } + +func (f *Ufw) loadInfo(line string, fireType string) FireInfo { + fields := strings.Fields(line) + var itemInfo FireInfo + if len(fields) < 4 { + return itemInfo + } + if fields[0] == "Anywhere" && fireType == "port" { + itemInfo.Strategy = "drop" + if fields[2] == "ALLOW" { + itemInfo.Strategy = "accept" + } + itemInfo.Address = fields[3] + return itemInfo + } + if strings.Contains(fields[0], "/") { + itemInfo.Port = strings.Split(fields[0], "/")[0] + itemInfo.Protocol = strings.Split(fields[0], "/")[1] + } else { + itemInfo.Port = fields[0] + itemInfo.Protocol = "tcp/udp" + } + + if fields[1] == "(v6)" { + if len(fields) < 5 { + return itemInfo + } + itemInfo.Family = "ipv6" + if fields[2] == "ALLOW" { + itemInfo.Strategy = "accept" + } else { + itemInfo.Strategy = "drop" + } + itemInfo.Address = fields[4] + } else { + itemInfo.Family = "ipv4" + if fields[1] == "ALLOW" { + itemInfo.Strategy = "accept" + } else { + itemInfo.Strategy = "drop" + } + itemInfo.Address = fields[3] + } + + return itemInfo +} diff --git a/frontend/src/api/interface/host.ts b/frontend/src/api/interface/host.ts index a81d6061a..6aa1b60d7 100644 --- a/frontend/src/api/interface/host.ts +++ b/frontend/src/api/interface/host.ts @@ -52,4 +52,29 @@ export namespace Host { groupID: number; info?: string; } + + export interface RuleSearch extends ReqPage { + info: string; + type: string; + } + export interface RuleInfo extends ReqPage { + family: string; + address: string; + port: string; + protocol: string; + strategy: string; + } + export interface RulePort { + operation: string; + address: string; + port: string; + source: string; + protocol: string; + strategy: string; + } + export interface RuleIP { + operation: string; + address: string; + strategy: string; + } } diff --git a/frontend/src/api/modules/host.ts b/frontend/src/api/modules/host.ts index 6fa7d0235..05cd78068 100644 --- a/frontend/src/api/modules/host.ts +++ b/frontend/src/api/modules/host.ts @@ -70,3 +70,14 @@ export const editCommand = (params: Command.CommandOperate) => { export const deleteCommand = (params: { ids: number[] }) => { return http.post(`/hosts/command/del`, params); }; + +// firewall +export const searchFireRule = (params: Host.RuleSearch) => { + return http.post>(`/hosts/firewall/search`, params); +}; +export const operatePortRule = (params: Host.RulePort) => { + return http.post(`/hosts/firewall/port`, params); +}; +export const operateIPRule = (params: Host.RuleIP) => { + return http.post(`/hosts/firewall/ip`, params); +}; diff --git a/frontend/src/lang/modules/zh.ts b/frontend/src/lang/modules/zh.ts index 1d9c2bf5d..7fbc47426 100644 --- a/frontend/src/lang/modules/zh.ts +++ b/frontend/src/lang/modules/zh.ts @@ -215,7 +215,6 @@ const message = { container: '容器', cronjob: '计划任务', host: '主机', - security: '安全', files: '文件', monitor: '监控', terminal: '终端', @@ -1181,6 +1180,19 @@ const message = { argsCheck: 'GET 参数校验', postCheck: 'POST 参数校验', cookieBlockList: 'Cookie 黑名单', + + firewall: '防火墙', + protocol: '协议', + port: '端口', + strategy: '策略', + accept: '允许', + drop: '拒绝', + source: '来源', + anyWhere: '所有 IP', + address: '指定 IP', + allIP: '所有 IP', + portRule: '端口规则', + ipRule: 'IP 规则', }, }; export default { diff --git a/frontend/src/routers/modules/host.ts b/frontend/src/routers/modules/host.ts index 8ca957b7d..022217cbd 100644 --- a/frontend/src/routers/modules/host.ts +++ b/frontend/src/routers/modules/host.ts @@ -29,6 +29,25 @@ const hostRouter = { requiresAuth: false, }, }, + { + path: '/hosts/firewall/port', + name: 'FirewallPort', + component: () => import('@/views/host/firewall/port/index.vue'), + meta: { + title: 'menu.firewall', + requiresAuth: false, + }, + }, + { + path: '/hosts/firewall/ip', + name: 'FirewallIP', + component: () => import('@/views/host/firewall/ip/index.vue'), + hidden: true, + meta: { + title: 'menu.toolbox', + requiresAuth: false, + }, + }, { path: '/hosts/terminal', name: 'Terminal', diff --git a/frontend/src/views/host/firewall/index.vue b/frontend/src/views/host/firewall/index.vue new file mode 100644 index 000000000..70fc325cd --- /dev/null +++ b/frontend/src/views/host/firewall/index.vue @@ -0,0 +1,25 @@ + + + diff --git a/frontend/src/views/host/firewall/ip/index.vue b/frontend/src/views/host/firewall/ip/index.vue new file mode 100644 index 000000000..5fc8bcd77 --- /dev/null +++ b/frontend/src/views/host/firewall/ip/index.vue @@ -0,0 +1,101 @@ + + + diff --git a/frontend/src/views/host/firewall/port/create/index.vue b/frontend/src/views/host/firewall/port/create/index.vue new file mode 100644 index 000000000..632e6c9b5 --- /dev/null +++ b/frontend/src/views/host/firewall/port/create/index.vue @@ -0,0 +1,114 @@ + + + diff --git a/frontend/src/views/host/firewall/port/index.vue b/frontend/src/views/host/firewall/port/index.vue new file mode 100644 index 000000000..91695b00b --- /dev/null +++ b/frontend/src/views/host/firewall/port/index.vue @@ -0,0 +1,141 @@ + + + diff --git a/frontend/src/views/host/security/index.vue b/frontend/src/views/host/security/index.vue deleted file mode 100644 index cdfc73be6..000000000 --- a/frontend/src/views/host/security/index.vue +++ /dev/null @@ -1,7 +0,0 @@ - - -