2023-03-24 23:19:17 +08:00
|
|
|
package client
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
2023-03-30 18:03:21 +08:00
|
|
|
"io/ioutil"
|
|
|
|
"os"
|
2023-03-24 23:19:17 +08:00
|
|
|
"strings"
|
|
|
|
|
2023-03-30 16:07:28 +08:00
|
|
|
"github.com/1Panel-dev/1Panel/backend/constant"
|
2023-03-30 18:03:21 +08:00
|
|
|
"github.com/1Panel-dev/1Panel/backend/utils/cmd"
|
2023-03-24 23:19:17 +08:00
|
|
|
)
|
|
|
|
|
2023-03-30 18:03:21 +08:00
|
|
|
const confPath = "/etc/ufw/sysctl.conf"
|
|
|
|
|
|
|
|
type Ufw struct{}
|
2023-03-24 23:19:17 +08:00
|
|
|
|
|
|
|
func NewUfw() (*Ufw, error) {
|
2023-03-30 18:03:21 +08:00
|
|
|
return &Ufw{}, nil
|
2023-03-24 23:19:17 +08:00
|
|
|
}
|
|
|
|
|
2023-03-28 15:17:13 +08:00
|
|
|
func (f *Ufw) Name() string {
|
|
|
|
return "ufw"
|
|
|
|
}
|
|
|
|
|
2023-03-24 23:19:17 +08:00
|
|
|
func (f *Ufw) Status() (string, error) {
|
2023-03-30 18:03:21 +08:00
|
|
|
stdout, err := cmd.Exec("sudo ufw status | grep Status")
|
2023-03-24 23:19:17 +08:00
|
|
|
if err != nil {
|
|
|
|
return "", fmt.Errorf("load the firewall status failed, err: %s", stdout)
|
|
|
|
}
|
2023-03-30 16:07:28 +08:00
|
|
|
if stdout == "Status: active\n" {
|
2023-03-24 23:19:17 +08:00
|
|
|
return "running", nil
|
|
|
|
}
|
|
|
|
return "not running", nil
|
|
|
|
}
|
|
|
|
|
2023-03-30 16:07:28 +08:00
|
|
|
func (f *Ufw) Version() (string, error) {
|
2023-03-30 18:03:21 +08:00
|
|
|
stdout, err := cmd.Exec("sudo ufw version | grep ufw")
|
2023-03-30 16:07:28 +08:00
|
|
|
if err != nil {
|
|
|
|
return "", fmt.Errorf("load the firewall status failed, err: %s", stdout)
|
|
|
|
}
|
|
|
|
info := strings.ReplaceAll(stdout, "\n", "")
|
|
|
|
return strings.ReplaceAll(info, "ufw ", ""), nil
|
|
|
|
}
|
|
|
|
|
2023-03-24 23:19:17 +08:00
|
|
|
func (f *Ufw) Start() error {
|
2023-03-30 18:03:21 +08:00
|
|
|
stdout, err := cmd.Exec("echo y | sudo ufw enable")
|
2023-03-24 23:19:17 +08:00
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("enable the firewall failed, err: %s", stdout)
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2023-03-30 16:07:28 +08:00
|
|
|
func (f *Ufw) PingStatus() (string, error) {
|
2023-03-30 18:03:21 +08:00
|
|
|
stdout, err := cmd.Exec("cat /etc/ufw/sysctl.conf | grep net/ipv4/icmp_echo_ignore_all= ")
|
2023-03-30 16:07:28 +08:00
|
|
|
if err != nil {
|
2023-03-30 18:03:21 +08:00
|
|
|
return constant.StatusDisable, fmt.Errorf("load firewall ping status failed, err: %s", stdout)
|
2023-03-30 16:07:28 +08:00
|
|
|
}
|
|
|
|
if stdout == "net/ipv4/icmp_echo_ignore_all=1\n" {
|
|
|
|
return constant.StatusEnable, nil
|
|
|
|
}
|
|
|
|
return constant.StatusDisable, nil
|
|
|
|
}
|
|
|
|
|
2023-03-30 18:03:21 +08:00
|
|
|
func (f *Ufw) UpdatePingStatus(enabel string) error {
|
|
|
|
lineBytes, err := ioutil.ReadFile(confPath)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
files := strings.Split(string(lineBytes), "\n")
|
|
|
|
var newFiles []string
|
|
|
|
for _, line := range files {
|
|
|
|
if strings.Contains(line, "net/ipv4/icmp_echo_ignore_all") || strings.HasPrefix(line, "net/ipv4/icmp_echo_ignore_all") {
|
|
|
|
newFiles = append(newFiles, "net/ipv4/icmp_echo_ignore_all="+enabel)
|
|
|
|
} else {
|
|
|
|
newFiles = append(newFiles, line)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
file, err := os.OpenFile(confPath, os.O_WRONLY|os.O_TRUNC, 0666)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
defer file.Close()
|
|
|
|
_, err = file.WriteString(strings.Join(newFiles, "\n"))
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2023-03-24 23:19:17 +08:00
|
|
|
func (f *Ufw) Stop() error {
|
2023-03-30 18:03:21 +08:00
|
|
|
stdout, err := cmd.Exec("sudo ufw disable")
|
2023-03-24 23:19:17 +08:00
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("stop the firewall failed, err: %s", stdout)
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (f *Ufw) Reload() error {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (f *Ufw) ListPort() ([]FireInfo, error) {
|
2023-03-30 18:03:21 +08:00
|
|
|
stdout, err := cmd.Exec("sudo ufw status verbose")
|
2023-03-24 23:19:17 +08:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2023-03-27 23:29:46 +08:00
|
|
|
portInfos := strings.Split(stdout, "\n")
|
2023-03-24 23:19:17 +08:00
|
|
|
var datas []FireInfo
|
2023-03-27 19:02:36 +08:00
|
|
|
isStart := false
|
|
|
|
for _, line := range portInfos {
|
|
|
|
if strings.HasPrefix(line, "--") {
|
|
|
|
isStart = true
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if !isStart {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
itemFire := f.loadInfo(line, "port")
|
2023-03-28 15:17:13 +08:00
|
|
|
if len(itemFire.Port) != 0 && itemFire.Port != "Anywhere" && !strings.Contains(itemFire.Port, ".") {
|
|
|
|
itemFire.Port = strings.ReplaceAll(itemFire.Port, ":", "-")
|
2023-03-27 19:02:36 +08:00
|
|
|
datas = append(datas, itemFire)
|
2023-03-24 23:19:17 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return datas, nil
|
|
|
|
}
|
|
|
|
|
2023-03-27 19:02:36 +08:00
|
|
|
func (f *Ufw) ListAddress() ([]FireInfo, error) {
|
2023-03-30 18:03:21 +08:00
|
|
|
stdout, err := cmd.Exec("sudo ufw status verbose")
|
2023-03-24 23:19:17 +08:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2023-03-27 23:29:46 +08:00
|
|
|
portInfos := strings.Split(stdout, "\n")
|
2023-03-24 23:19:17 +08:00
|
|
|
var datas []FireInfo
|
2023-03-27 19:02:36 +08:00
|
|
|
isStart := false
|
|
|
|
for _, line := range portInfos {
|
|
|
|
if strings.HasPrefix(line, "--") {
|
|
|
|
isStart = true
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if !isStart {
|
2023-03-24 23:19:17 +08:00
|
|
|
continue
|
|
|
|
}
|
2023-03-27 23:29:46 +08:00
|
|
|
if !strings.Contains(line, " IN") {
|
|
|
|
continue
|
|
|
|
}
|
2023-03-27 19:02:36 +08:00
|
|
|
itemFire := f.loadInfo(line, "address")
|
2023-03-28 15:17:13 +08:00
|
|
|
if strings.Contains(itemFire.Port, ".") {
|
|
|
|
itemFire.Address += ("-" + itemFire.Port)
|
|
|
|
itemFire.Port = ""
|
|
|
|
}
|
|
|
|
if len(itemFire.Port) == 0 && len(itemFire.Address) != 0 {
|
2023-03-27 19:02:36 +08:00
|
|
|
datas = append(datas, itemFire)
|
2023-03-24 23:19:17 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return datas, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (f *Ufw) Port(port FireInfo, operation string) error {
|
2023-03-28 15:17:13 +08:00
|
|
|
switch port.Strategy {
|
|
|
|
case "accept":
|
|
|
|
port.Strategy = "allow"
|
|
|
|
case "drop":
|
|
|
|
port.Strategy = "deny"
|
2023-03-24 23:19:17 +08:00
|
|
|
default:
|
2023-03-28 15:17:13 +08:00
|
|
|
return fmt.Errorf("unsupport strategy %s", port.Strategy)
|
2023-03-24 23:19:17 +08:00
|
|
|
}
|
|
|
|
|
2023-03-28 15:17:13 +08:00
|
|
|
command := fmt.Sprintf("sudo ufw %s %s", port.Strategy, port.Port)
|
|
|
|
if operation == "remove" {
|
|
|
|
command = fmt.Sprintf("sudo ufw delete %s %s", port.Strategy, port.Port)
|
|
|
|
}
|
2023-03-24 23:19:17 +08:00
|
|
|
if len(port.Protocol) != 0 {
|
|
|
|
command += fmt.Sprintf("/%s", port.Protocol)
|
|
|
|
}
|
2023-03-30 18:03:21 +08:00
|
|
|
stdout, err := cmd.Exec(command)
|
2023-03-24 23:19:17 +08:00
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("%s port failed, err: %s", operation, stdout)
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (f *Ufw) RichRules(rule FireInfo, operation string) error {
|
2023-03-28 15:17:13 +08:00
|
|
|
switch rule.Strategy {
|
|
|
|
case "accept":
|
|
|
|
rule.Strategy = "allow"
|
|
|
|
case "drop":
|
|
|
|
rule.Strategy = "deny"
|
|
|
|
default:
|
|
|
|
return fmt.Errorf("unsupport strategy %s", rule.Strategy)
|
|
|
|
}
|
|
|
|
|
|
|
|
ruleStr := fmt.Sprintf("sudo ufw %s ", rule.Strategy)
|
|
|
|
if operation == "remove" {
|
|
|
|
ruleStr = fmt.Sprintf("sudo ufw delete %s ", rule.Strategy)
|
|
|
|
}
|
2023-03-27 19:02:36 +08:00
|
|
|
if len(rule.Protocol) != 0 {
|
|
|
|
ruleStr += fmt.Sprintf("proto %s ", rule.Protocol)
|
|
|
|
}
|
2023-03-28 15:17:13 +08:00
|
|
|
if strings.Contains(rule.Address, "-") {
|
|
|
|
ruleStr += fmt.Sprintf("from %s to %s ", strings.Split(rule.Address, "-")[0], strings.Split(rule.Address, "-")[1])
|
|
|
|
} else {
|
2023-03-27 19:02:36 +08:00
|
|
|
ruleStr += fmt.Sprintf("from %s ", rule.Address)
|
2023-03-24 23:19:17 +08:00
|
|
|
}
|
|
|
|
if len(rule.Port) != 0 {
|
2023-03-27 19:02:36 +08:00
|
|
|
ruleStr += fmt.Sprintf("to any port %s ", rule.Port)
|
2023-03-24 23:19:17 +08:00
|
|
|
}
|
|
|
|
|
2023-03-30 18:03:21 +08:00
|
|
|
stdout, err := cmd.Exec(ruleStr)
|
2023-03-24 23:19:17 +08:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
|
2023-03-30 18:03:21 +08:00
|
|
|
stdout, err := cmd.Exec(ruleStr)
|
2023-03-24 23:19:17 +08:00
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("%s port forward failed, err: %s", operation, stdout)
|
|
|
|
}
|
|
|
|
if err := f.Reload(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
2023-03-27 19:02:36 +08:00
|
|
|
|
|
|
|
func (f *Ufw) loadInfo(line string, fireType string) FireInfo {
|
|
|
|
fields := strings.Fields(line)
|
|
|
|
var itemInfo FireInfo
|
|
|
|
if len(fields) < 4 {
|
|
|
|
return itemInfo
|
|
|
|
}
|
2023-03-28 15:17:13 +08:00
|
|
|
if fields[1] == "(v6)" {
|
|
|
|
return itemInfo
|
|
|
|
}
|
2023-03-27 23:29:46 +08:00
|
|
|
if fields[0] == "Anywhere" && fireType != "port" {
|
2023-03-27 19:02:36 +08:00
|
|
|
itemInfo.Strategy = "drop"
|
2023-03-28 15:17:13 +08:00
|
|
|
if fields[1] == "ALLOW" {
|
2023-03-27 19:02:36 +08:00
|
|
|
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"
|
|
|
|
}
|
2023-03-28 15:17:13 +08:00
|
|
|
itemInfo.Family = "ipv4"
|
|
|
|
if fields[1] == "ALLOW" {
|
|
|
|
itemInfo.Strategy = "accept"
|
2023-03-27 19:02:36 +08:00
|
|
|
} else {
|
2023-03-28 15:17:13 +08:00
|
|
|
itemInfo.Strategy = "drop"
|
2023-03-27 19:02:36 +08:00
|
|
|
}
|
2023-03-28 15:17:13 +08:00
|
|
|
itemInfo.Address = fields[3]
|
2023-03-27 19:02:36 +08:00
|
|
|
|
|
|
|
return itemInfo
|
|
|
|
}
|