1Panel/backend/utils/firewall/client/ufw.go
2023-04-06 09:01:57 +08:00

210 lines
4.7 KiB
Go

package client
import (
"fmt"
"strings"
"github.com/1Panel-dev/1Panel/backend/utils/ssh"
)
type Ufw struct {
Client ssh.ConnInfo
}
func NewUfw() (*Ufw, error) {
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 := f.Client.Run("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 := f.Client.Run("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 := f.Client.Run("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 := f.Client.Run("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 := f.Client.Run("sudo ufw status verbose")
if err != nil {
return nil, err
}
portInfos := strings.Split(stdout, "\n")
var datas []FireInfo
isStart := false
for _, line := range portInfos {
if strings.HasPrefix(line, "--") {
isStart = true
continue
}
if !isStart {
continue
}
itemFire := f.loadInfo(line, "port")
if len(itemFire.Port) != 0 {
datas = append(datas, itemFire)
}
}
return datas, nil
}
func (f *Ufw) ListAddress() ([]FireInfo, error) {
stdout, err := f.Client.Run("sudo ufw status verbose")
if err != nil {
return nil, err
}
portInfos := strings.Split(stdout, "\n")
var datas []FireInfo
isStart := false
for _, line := range portInfos {
if strings.HasPrefix(line, "--") {
isStart = true
continue
}
if !isStart {
continue
}
if !strings.Contains(line, " IN") {
continue
}
itemFire := f.loadInfo(line, "address")
if len(itemFire.Port) == 0 {
datas = append(datas, itemFire)
}
}
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("sudo ufw %s %s", operation, port.Port)
if len(port.Protocol) != 0 {
command += fmt.Sprintf("/%s", port.Protocol)
}
stdout, err := f.Client.Run(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 := "sudo ufw "
if len(rule.Protocol) != 0 {
ruleStr += fmt.Sprintf("proto %s ", rule.Protocol)
}
if len(rule.Address) != 0 {
ruleStr += fmt.Sprintf("from %s ", rule.Address)
}
if len(rule.Port) != 0 {
ruleStr += fmt.Sprintf("to any port %s ", rule.Port)
}
stdout, err := f.Client.Run(ruleStr)
if err != nil {
return fmt.Errorf("%s rich rules failed, err: %s", operation, stdout)
}
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 := f.Client.Run(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
}
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
}