From a2fcdabb7b81c516becca47eb7d034788107fc9c Mon Sep 17 00:00:00 2001 From: ssongliu Date: Fri, 24 Mar 2023 23:19:17 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=B0=81=E8=A3=85=E9=98=B2=E7=81=AB?= =?UTF-8?q?=E5=A2=99=20firewalld=20=E7=9B=B8=E5=85=B3=E6=93=8D=E4=BD=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/utils/firewall/client.go | 31 ++++ backend/utils/firewall/client/firewalld.go | 146 ++++++++++++++++ .../utils/firewall/client/firewalld_test.go | 51 ++++++ backend/utils/firewall/client/info.go | 16 ++ backend/utils/firewall/client/ufw.go | 159 ++++++++++++++++++ 5 files changed, 403 insertions(+) create mode 100644 backend/utils/firewall/client.go create mode 100644 backend/utils/firewall/client/firewalld.go create mode 100644 backend/utils/firewall/client/firewalld_test.go create mode 100644 backend/utils/firewall/client/info.go create mode 100644 backend/utils/firewall/client/ufw.go diff --git a/backend/utils/firewall/client.go b/backend/utils/firewall/client.go new file mode 100644 index 000000000..4ad32b2c3 --- /dev/null +++ b/backend/utils/firewall/client.go @@ -0,0 +1,31 @@ +package firewall + +import ( + "errors" + "os" + + "github.com/1Panel-dev/1Panel/backend/utils/firewall/client" +) + +type FirewallClient interface { + Start() error + Stop() error + Reload() error + Status() (string, error) + ListPort() ([]client.FireInfo, error) + ListRichRules() ([]client.FireInfo, error) + + Port(port client.FireInfo, operation string) error + RichRules(rule client.FireInfo, operation string) error + PortForward(info client.Forward, operation string) error +} + +func NewFirewallClient() (FirewallClient, error) { + 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") +} diff --git a/backend/utils/firewall/client/firewalld.go b/backend/utils/firewall/client/firewalld.go new file mode 100644 index 000000000..5cae11626 --- /dev/null +++ b/backend/utils/firewall/client/firewalld.go @@ -0,0 +1,146 @@ +package client + +import ( + "fmt" + "strings" + + "github.com/1Panel-dev/1Panel/backend/utils/cmd" +) + +type Firewall struct{} + +func NewFirewalld() (*Firewall, error) { + return &Firewall{}, nil +} + +func (f *Firewall) Status() (string, error) { + stdout, err := cmd.Exec("firewall-cmd --state") + if err != nil { + return "", fmt.Errorf("load the firewall status failed, err: %s", stdout) + } + return strings.ReplaceAll(stdout, "\n", ""), nil +} + +func (f *Firewall) Start() error { + stdout, err := cmd.Exec("systemctl start firewalld") + if err != nil { + return fmt.Errorf("enable the firewall failed, err: %s", stdout) + } + return nil +} + +func (f *Firewall) Stop() error { + stdout, err := cmd.Exec("systemctl stop firewalld") + if err != nil { + return fmt.Errorf("stop the firewall failed, err: %s", stdout) + } + return nil +} + +func (f *Firewall) Reload() error { + stdout, err := cmd.Exec("firewall-cmd --reload") + if err != nil { + return fmt.Errorf("reload firewall failed, err: %s", stdout) + } + return nil +} + +func (f *Firewall) ListPort() ([]FireInfo, error) { + stdout, err := cmd.Exec("firewall-cmd --zone=public --list-ports") + if err != nil { + return nil, err + } + ports := 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] + } + datas = append(datas, itemPort) + } + return datas, nil +} + +func (f *Firewall) ListRichRules() ([]FireInfo, error) { + stdout, err := cmd.Exec("firewall-cmd --zone=public --list-rich-rules") + if err != nil { + return nil, err + } + var datas []FireInfo + rules := strings.Split(stdout, "\n") + for _, rule := range rules { + 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 + } + } + 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) + if err != nil { + return fmt.Errorf("%s port failed, err: %s", operation, stdout) + } + if err := f.Reload(); err != nil { + return err + } + return nil +} + +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) + } + 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) + } + if err := f.Reload(); err != nil { + return err + } + return nil +} + +func (f *Firewall) PortForward(info Forward, operation string) error { + ruleStr := fmt.Sprintf("firewall-cmd --%s-forward-port=port=%s:proto=%s:toport=%s --permanent", operation, info.Port, info.Protocol, info.Target) + if len(info.Address) != 0 { + 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) + if err != nil { + return fmt.Errorf("%s port forward failed, err: %s", operation, stdout) + } + if err := f.Reload(); err != nil { + return err + } + return nil +} diff --git a/backend/utils/firewall/client/firewalld_test.go b/backend/utils/firewall/client/firewalld_test.go new file mode 100644 index 000000000..19b0f4e46 --- /dev/null +++ b/backend/utils/firewall/client/firewalld_test.go @@ -0,0 +1,51 @@ +package client + +import ( + "encoding/json" + "fmt" + "strings" + "testing" + + "github.com/1Panel-dev/1Panel/backend/utils/ssh" +) + +func TestFire(t *testing.T) { + ConnInfo := ssh.ConnInfo{ + User: "ubuntu", + AuthMode: "password", + Port: 22, + } + output, err := ConnInfo.Run("sudo ufw status numbered") + if err != nil { + fmt.Println(err) + } + + lines := strings.Split(string(output), "\n") + var rules []UfwRule + for _, line := range lines { + if line == "" || !strings.HasPrefix(line, "[") { + 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"` +} diff --git a/backend/utils/firewall/client/info.go b/backend/utils/firewall/client/info.go new file mode 100644 index 000000000..ae44bf527 --- /dev/null +++ b/backend/utils/firewall/client/info.go @@ -0,0 +1,16 @@ +package client + +type FireInfo struct { + Family string + Address string + Port string + Protocol string + Strategy string +} + +type Forward struct { + Protocol string + Address string + Port string + Target string +} diff --git a/backend/utils/firewall/client/ufw.go b/backend/utils/firewall/client/ufw.go new file mode 100644 index 000000000..095eaffb5 --- /dev/null +++ b/backend/utils/firewall/client/ufw.go @@ -0,0 +1,159 @@ +package client + +import ( + "fmt" + "strings" + + "github.com/1Panel-dev/1Panel/backend/utils/cmd" +) + +type Ufw struct{} + +func NewUfw() (*Ufw, error) { + return &Ufw{}, nil +} + +func (f *Ufw) Status() (string, error) { + stdout, err := cmd.Exec("sudo ufw status") + if err != nil { + return "", fmt.Errorf("load the firewall status failed, err: %s", stdout) + } + if stdout == "Status: inactive\n" { + return "running", nil + } + return "not running", nil +} + +func (f *Ufw) Start() error { + stdout, err := cmd.Exec("sudo ufw enable") + if err != nil { + return fmt.Errorf("enable the firewall failed, err: %s", stdout) + } + return nil +} + +func (f *Ufw) Stop() error { + stdout, err := cmd.Exec("sudo ufw disable") + if err != nil { + return fmt.Errorf("stop the firewall failed, err: %s", stdout) + } + return nil +} + +func (f *Ufw) Reload() error { + stdout, err := cmd.Exec("sudo ufw reload") + if err != nil { + return fmt.Errorf("reload firewall failed, err: %s", stdout) + } + return nil +} + +func (f *Ufw) ListPort() ([]FireInfo, error) { + stdout, err := cmd.Exec("firewall-cmd --zone=public --list-ports") + if err != nil { + return nil, err + } + ports := 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] + } + datas = append(datas, itemPort) + } + return datas, nil +} + +func (f *Ufw) ListRichRules() ([]FireInfo, error) { + stdout, err := cmd.Exec("firewall-cmd --zone=public --list-rich-rules") + if err != nil { + return nil, err + } + var datas []FireInfo + rules := strings.Split(stdout, "\n") + for _, rule := range rules { + 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 + } + } + datas = append(datas, itemRule) + } + return datas, nil +} + +func (f *Ufw) Port(port FireInfo, operation string) error { + switch operation { + case "add": + operation = "allow" + case "remove": + operation = "deny" + default: + return fmt.Errorf("unsupport operation %s", operation) + } + + command := fmt.Sprintf("ufw %s %s", operation, port.Port) + if len(port.Protocol) != 0 { + command += fmt.Sprintf("/%s", port.Protocol) + } + stdout, err := cmd.Exec(command) + if err != nil { + return fmt.Errorf("%s port failed, err: %s", operation, stdout) + } + return nil +} + +func (f *Ufw) RichRules(rule FireInfo, operation string) error { + 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 + + 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) + } + if err := f.Reload(); err != nil { + return err + } + return nil +} + +func (f *Ufw) PortForward(info Forward, operation string) error { + ruleStr := fmt.Sprintf("firewall-cmd --%s-forward-port=port=%s:proto=%s:toport=%s --permanent", operation, info.Port, info.Protocol, info.Target) + if len(info.Address) != 0 { + 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) + if err != nil { + return fmt.Errorf("%s port forward failed, err: %s", operation, stdout) + } + if err := f.Reload(); err != nil { + return err + } + return nil +}