mirror of
https://hubproxy.babadafafafafa.cn/https://github.com/1Panel-dev/1Panel.git
synced 2026-09-21 00:24:12 +08:00
Port forwarding no longer shares the filter client. FilterClient keeps only
filter capabilities, and forwarding gets its own adapter, service and boot
replay:
- utils/firewall/forwarding holds the provider adapters. firewalld uses native
forward-port, ufw and iptables share the NAT implementation moved out of
client/iptables/forward.go.
- service/forwarding.go owns base info, search, operate, enable and replay.
The API keeps its routes and dispatches on name/type/operate.
- init/firewall replays forwarding through that service instead of loading NAT
rule files inline.
Also adds 1PANEL_FORWARD to the IptablesOp name enum: the frontend already
sends {"name":"1PANEL_FORWARD","operate":"init-forward"} and the validator
rejected it with 400 before reaching the service. Besides that, the only
observable difference is that a forward-tab search no longer triggers the
port/address record cleanup goroutine on the side.
283 lines
6.6 KiB
Go
283 lines
6.6 KiB
Go
package client
|
|
|
|
import (
|
|
"fmt"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/1Panel-dev/1Panel/agent/buserr"
|
|
"github.com/1Panel-dev/1Panel/agent/global"
|
|
"github.com/1Panel-dev/1Panel/agent/utils/cmd"
|
|
)
|
|
|
|
type Ufw struct {
|
|
CmdStr string
|
|
}
|
|
|
|
func NewUfw() (*Ufw, error) {
|
|
var ufw Ufw
|
|
ufw.CmdStr = fmt.Sprintf("LANGUAGE=en_US:en %s ufw", cmd.SudoHandleCmd())
|
|
return &ufw, nil
|
|
}
|
|
|
|
func (f *Ufw) Name() string {
|
|
return "ufw"
|
|
}
|
|
|
|
func (f *Ufw) Status() (bool, error) {
|
|
stdout, _ := f.runWithStdout("status")
|
|
if strings.Contains(stdout, "Status: active") {
|
|
return true, nil
|
|
}
|
|
if strings.Contains(stdout, "状态: 激活") {
|
|
return true, nil
|
|
}
|
|
return false, nil
|
|
}
|
|
|
|
func (f *Ufw) Version() (string, error) {
|
|
stdout, err := f.runWithStdout("version")
|
|
if err != nil {
|
|
return "", fmt.Errorf("load the firewall status failed, %v", err)
|
|
}
|
|
info := strings.Split(strings.TrimSpace(stdout), "\n")[0]
|
|
return strings.ReplaceAll(info, "ufw ", ""), nil
|
|
}
|
|
|
|
func (f *Ufw) Start() error {
|
|
if err := f.run("--force", "enable"); err != nil {
|
|
return fmt.Errorf("enable the firewall failed, %v", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (f *Ufw) Stop() error {
|
|
if err := f.run("disable"); err != nil {
|
|
return fmt.Errorf("stop the firewall failed, %v", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (f *Ufw) Restart() error {
|
|
if err := f.Stop(); err != nil {
|
|
return err
|
|
}
|
|
if err := f.Start(); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (f *Ufw) Reload() error {
|
|
return nil
|
|
}
|
|
|
|
func (f *Ufw) ListPort() ([]FireInfo, error) {
|
|
stdout, err := f.runWithStdout("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 && itemFire.Port != "Anywhere" && !strings.Contains(itemFire.Port, ".") {
|
|
itemFire.Port = strings.ReplaceAll(itemFire.Port, ":", "-")
|
|
datas = append(datas, itemFire)
|
|
}
|
|
}
|
|
return datas, nil
|
|
}
|
|
|
|
func (f *Ufw) ListAddress() ([]FireInfo, error) {
|
|
stdout, err := f.runWithStdout("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 strings.Contains(itemFire.Port, ".") {
|
|
itemFire.Address += ("-" + itemFire.Port)
|
|
itemFire.Port = ""
|
|
}
|
|
if len(itemFire.Port) == 0 && len(itemFire.Address) != 0 {
|
|
datas = append(datas, itemFire)
|
|
}
|
|
}
|
|
return datas, nil
|
|
}
|
|
|
|
func (f *Ufw) Port(port FireInfo, operation string) error {
|
|
switch port.Strategy {
|
|
case "accept":
|
|
port.Strategy = "allow"
|
|
case "drop":
|
|
port.Strategy = "deny"
|
|
default:
|
|
return fmt.Errorf("unsupported strategy %s", port.Strategy)
|
|
}
|
|
if cmd.CheckIllegal(port.Protocol, port.Port) {
|
|
return buserr.New("ErrCmdIllegal")
|
|
}
|
|
|
|
args := []string{port.Strategy, port.Port}
|
|
if operation == "remove" {
|
|
args = []string{"delete", port.Strategy, port.Port}
|
|
}
|
|
if len(port.Protocol) != 0 {
|
|
args[len(args)-1] += "/" + port.Protocol
|
|
}
|
|
if err := f.run(args...); err != nil {
|
|
return fmt.Errorf("%s (%s) failed, %v", operation, strings.Join(args, " "), err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (f *Ufw) RichRules(rule FireInfo, operation string) error {
|
|
switch rule.Strategy {
|
|
case "accept":
|
|
rule.Strategy = "allow"
|
|
case "drop":
|
|
rule.Strategy = "deny"
|
|
default:
|
|
return fmt.Errorf("unsupported strategy %s", rule.Strategy)
|
|
}
|
|
|
|
if cmd.CheckIllegal(operation, rule.Protocol, rule.Address, rule.Port) {
|
|
return buserr.New("ErrCmdIllegal")
|
|
}
|
|
|
|
insertNum := f.loadInsertNum(rule, operation)
|
|
args := []string{"insert", strconv.Itoa(insertNum), rule.Strategy}
|
|
if operation == "remove" {
|
|
args = []string{"delete", rule.Strategy}
|
|
}
|
|
if len(rule.Protocol) != 0 {
|
|
args = append(args, "proto", rule.Protocol)
|
|
}
|
|
if strings.Contains(rule.Address, "-") {
|
|
parts := strings.Split(rule.Address, "-")
|
|
args = append(args, "from", parts[0], "to", parts[1])
|
|
} else {
|
|
args = append(args, "from", rule.Address)
|
|
}
|
|
if len(rule.Port) != 0 {
|
|
args = append(args, "to", "any", "port", rule.Port)
|
|
}
|
|
|
|
stdout, err := f.runWithStdout(args...)
|
|
if err != nil {
|
|
if strings.Contains(stdout, "ERROR: Invalid position") || strings.Contains(stdout, "ERROR: 无效位置") {
|
|
fallbackArgs := args
|
|
if len(args) >= 2 && args[0] == "insert" {
|
|
fallbackArgs = append([]string{}, args[2:]...)
|
|
}
|
|
if err := f.run(fallbackArgs...); err != nil {
|
|
return fmt.Errorf("%s rich rules (%s), failed, %v", operation, strings.Join(args, " "), err)
|
|
}
|
|
return nil
|
|
}
|
|
return fmt.Errorf("%s rich rules (%s), failed, %v", operation, strings.Join(args, " "), err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (f *Ufw) loadInfo(line string, fireType string) FireInfo {
|
|
fields := strings.Fields(line)
|
|
var itemInfo FireInfo
|
|
if strings.Contains(line, "LIMIT") || strings.Contains(line, "ALLOW FWD") {
|
|
return itemInfo
|
|
}
|
|
if len(fields) < 4 {
|
|
return itemInfo
|
|
}
|
|
if fields[1] == "(v6)" && fireType == "port" {
|
|
return itemInfo
|
|
}
|
|
if fields[0] == "Anywhere" && fireType != "port" {
|
|
itemInfo.Strategy = "drop"
|
|
if fields[1] == "ALLOW" {
|
|
itemInfo.Strategy = "accept"
|
|
}
|
|
if fields[1] == "(v6)" {
|
|
if fields[2] == "ALLOW" {
|
|
itemInfo.Strategy = "accept"
|
|
}
|
|
itemInfo.Address = fields[4]
|
|
} else {
|
|
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"
|
|
}
|
|
itemInfo.Family = "ipv4"
|
|
if fields[1] == "ALLOW" {
|
|
itemInfo.Strategy = "accept"
|
|
} else {
|
|
itemInfo.Strategy = "drop"
|
|
}
|
|
itemInfo.Address = fields[3]
|
|
|
|
return itemInfo
|
|
}
|
|
|
|
func (f *Ufw) loadInsertNum(rule FireInfo, operation string) int {
|
|
if !strings.Contains(rule.Address, ":") || operation == "remove" {
|
|
return 1
|
|
}
|
|
rules, err := f.runWithStdout("status", "numbered")
|
|
if err != nil {
|
|
global.LOG.Errorf("load ufw rules failed, err: %v", err)
|
|
return 1
|
|
}
|
|
lines := strings.Split(rules, "\n")
|
|
i := 1
|
|
for _, item := range lines {
|
|
fields := strings.Fields(item)
|
|
if len(fields) < 4 {
|
|
continue
|
|
}
|
|
if !strings.Contains(item, "(v6)") {
|
|
i++
|
|
}
|
|
}
|
|
return i
|
|
}
|
|
|
|
func (f *Ufw) run(args ...string) error {
|
|
_, err := f.runWithStdout(args...)
|
|
return err
|
|
}
|
|
|
|
func (f *Ufw) runWithStdout(args ...string) (string, error) {
|
|
cmdMgr := cmd.NewCommandMgr(cmd.WithEnv("LANGUAGE=en_US:en"))
|
|
return cmdMgr.RunWithOptionalSudoAndStdout("ufw", args...)
|
|
}
|