diff --git a/backend/init/app/app.go b/backend/init/app/app.go index f89f8b60f..a7152f301 100644 --- a/backend/init/app/app.go +++ b/backend/init/app/app.go @@ -35,7 +35,9 @@ func Init() { _ = docker.CreateDefaultDockerNetwork() if f, err := firewall.NewFirewallClient(); err == nil { - _ = f.EnableForward() + if err = f.EnableForward(); err != nil { + global.LOG.Errorf("init port forward failed, err: %v", err) + } } }() } diff --git a/backend/utils/firewall/client/info.go b/backend/utils/firewall/client/info.go index 3503c25ed..f88bd155b 100644 --- a/backend/utils/firewall/client/info.go +++ b/backend/utils/firewall/client/info.go @@ -33,3 +33,12 @@ type IptablesNatInfo struct { SrcPort string `json:"srcPort"` DestPort string `json:"destPort"` } + +type IptablesFilterInfo struct { + Num string `json:"num"` + Target string `json:"target"` + Protocol string `json:"protocol"` + Opt string `json:"opt"` + Source string `json:"source"` + Destination string `json:"destination"` +} diff --git a/backend/utils/firewall/client/iptables.go b/backend/utils/firewall/client/iptables.go index 1348c021d..e299c89f4 100644 --- a/backend/utils/firewall/client/iptables.go +++ b/backend/utils/firewall/client/iptables.go @@ -10,9 +10,20 @@ import ( "github.com/1Panel-dev/1Panel/backend/utils/cmd" ) -const NatChain = "1PANEL" +const ( + PreRoutingChain = "1PANEL_PREROUTING" + PostRoutingChain = "1PANEL_POSTROUTING" + ForwardChain = "1PANEL_FORWARD" +) -var NatListRegex = regexp.MustCompile(`^(\d+)\s+(.+?)\s+(.+?)\s+(.+?)\s+(.+?)\s+(.+?)(?:\s+(.+?) .+?:(\d{1,5}(?::\d+)?).+?[ :](.+-.+|(?:.+:)?\d{1,5}(?:-\d{1,5})?))?$`) +const ( + FilterTab = "filter" + NatTab = "nat" +) + +var ( + natListRegex = regexp.MustCompile(`^(\d+)\s+(.+?)\s+(.+?)\s+(.+?)\s+(.+?)\s+(.+?)(?:\s+(.+?) .+?:(\d{1,5}(?::\d+)?).+?[ :](.+-.+|(?:.+:)?\d{1,5}(?:-\d{1,5})?))?$`) +) type Iptables struct { CmdStr string @@ -27,8 +38,16 @@ func NewIptables() (*Iptables, error) { return iptables, nil } -func (iptables *Iptables) runf(rule string, a ...any) error { - stdout, err := cmd.Execf("%s iptables -t nat %s", iptables.CmdStr, fmt.Sprintf(rule, a...)) +func (iptables *Iptables) outf(tab, rule string, a ...any) (stdout string, err error) { + stdout, err = cmd.Execf("%s iptables -t %s %s", iptables.CmdStr, tab, fmt.Sprintf(rule, a...)) + if err != nil && stdout != "" { + global.LOG.Errorf("iptables failed, err: %s", stdout) + } + return +} + +func (iptables *Iptables) runf(tab, rule string, a ...any) error { + stdout, err := iptables.outf(tab, rule, a...) if err != nil { return fmt.Errorf("%s, %s", err, stdout) } @@ -45,26 +64,30 @@ func (iptables *Iptables) Check() error { return fmt.Errorf("%s, %s", err, stdout) } if stdout == "0" { - return fmt.Errorf("disable") + return fmt.Errorf("ipv4 forward disable") + } + + chain, _ := iptables.outf(NatTab, "-L -n | grep 'Chain %s'", PreRoutingChain) + if len(strings.ReplaceAll(chain, "\n", "")) != 0 { + return fmt.Errorf("chain enabled") } return nil } -func (iptables *Iptables) NatNewChain() error { - return iptables.runf("-N %s", NatChain) +func (iptables *Iptables) NewChain(tab, chain string) error { + return iptables.runf(tab, "-N %s", chain) } -func (iptables *Iptables) NatAppendChain() error { - return iptables.runf("-A PREROUTING -j %s", NatChain) +func (iptables *Iptables) AppendChain(tab string, chain, chain1 string) error { + return iptables.runf(tab, "-A %s -j %s", chain, chain1) } func (iptables *Iptables) NatList(chain ...string) ([]IptablesNatInfo, error) { - rule := fmt.Sprintf("%s iptables -t nat -nL %s --line", iptables.CmdStr, NatChain) - if len(chain) == 1 { - rule = fmt.Sprintf("%s iptables -t nat -nL %s --line", iptables.CmdStr, chain[0]) + if len(chain) == 0 { + chain = append(chain, PreRoutingChain) } - stdout, err := cmd.Exec(rule) + stdout, err := iptables.outf(NatTab, "-nL %s --line", chain[0]) if err != nil { return nil, err } @@ -74,8 +97,8 @@ func (iptables *Iptables) NatList(chain ...string) ([]IptablesNatInfo, error) { line = strings.TrimFunc(line, func(r rune) bool { return r <= 32 }) - if NatListRegex.MatchString(line) { - match := NatListRegex.FindStringSubmatch(line) + if natListRegex.MatchString(line) { + match := natListRegex.FindStringSubmatch(line) if !strings.Contains(match[9], ":") { match[9] = fmt.Sprintf(":%s", match[9]) } @@ -95,43 +118,127 @@ func (iptables *Iptables) NatList(chain ...string) ([]IptablesNatInfo, error) { return forwardList, nil } -func (iptables *Iptables) NatAdd(protocol, src, destIp, destPort string, save bool) error { - rule := fmt.Sprintf("-A %s -p %s --dport %s -j REDIRECT --to-port %s", NatChain, protocol, src, destPort) - if destIp != "" && destIp != "127.0.0.1" && destIp != "localhost" { - rule = fmt.Sprintf("-A %s -p %s --dport %s -j DNAT --to-destination %s:%s", NatChain, protocol, src, destIp, destPort) - } - if err := iptables.runf(rule); err != nil { - return err +func (iptables *Iptables) NatAdd(protocol, srcPort, dest, destPort string, save bool) error { + if dest != "" && dest != "127.0.0.1" && dest != "localhost" { + if err := iptables.runf(NatTab, fmt.Sprintf( + "-A %s -p %s --dport %s -j DNAT --to-destination %s:%s", + PreRoutingChain, + protocol, + srcPort, + dest, + destPort, + )); err != nil { + return err + } + + // 非本机转发, 按公网流程走 + if err := iptables.runf(NatTab, fmt.Sprintf( + "-A %s -p %s -d %s --dport %s -j MASQUERADE", + PostRoutingChain, + protocol, + dest, + destPort, + )); err != nil { + return err + } + + if err := iptables.runf(FilterTab, fmt.Sprintf( + "-A %s -d %s -p %s --dport %s -j ACCEPT", + ForwardChain, + dest, + protocol, + destPort, + )); err != nil { + return err + } + + if err := iptables.runf(FilterTab, fmt.Sprintf( + "-A %s -s %s -p %s --sport %s -j ACCEPT", + ForwardChain, + dest, + protocol, + destPort, + )); err != nil { + return err + } + } else { + if err := iptables.runf(NatTab, fmt.Sprintf( + "-A %s -p %s --dport %s -j REDIRECT --to-port %s", + PreRoutingChain, + protocol, + srcPort, + destPort, + )); err != nil { + return err + } } if save { return global.DB.Save(&model.Forward{ Protocol: protocol, - Port: src, - TargetIP: destIp, + Port: srcPort, + TargetIP: dest, TargetPort: destPort, }).Error } return nil } -func (iptables *Iptables) NatRemove(num string, protocol, src, destIp, destPort string) error { - if err := iptables.runf("-D %s %s", NatChain, num); err != nil { +func (iptables *Iptables) NatRemove(num string, protocol, srcPort, dest, destPort string) error { + if err := iptables.runf(NatTab, "-D %s %s", PreRoutingChain, num); err != nil { return err } + // 删除公网转发规则 + if dest != "" && dest != "127.0.0.1" && dest != "localhost" { + if err := iptables.runf(NatTab, fmt.Sprintf( + "-D %s -p %s --dport %s -j DNAT MASQUERADE", + PostRoutingChain, + protocol, + destPort, + )); err != nil { + return err + } + + if err := iptables.runf(FilterTab, fmt.Sprintf( + "-D %s -d %s -p %s --dport %s -j ACCEPT", + ForwardChain, + dest, + protocol, + destPort, + )); err != nil { + return err + } + + if err := iptables.runf(FilterTab, fmt.Sprintf( + "-D %s -s %s -p %s --sport %s -j ACCEPT", + ForwardChain, + dest, + protocol, + destPort, + )); err != nil { + return err + } + } + global.DB.Where( "protocol = ? AND port = ? AND target_ip = ? AND target_port = ?", protocol, - src, - destIp, + srcPort, + dest, destPort, ).Delete(&model.Forward{}) return nil } func (iptables *Iptables) Reload() error { - if err := iptables.runf("-F %s", NatChain); err != nil { + if err := iptables.runf(NatTab, "-F %s", PreRoutingChain); err != nil { + return err + } + if err := iptables.runf(NatTab, "-F %s", PostRoutingChain); err != nil { + return err + } + if err := iptables.runf(FilterTab, "-F %s", ForwardChain); err != nil { return err } diff --git a/backend/utils/firewall/client/ufw.go b/backend/utils/firewall/client/ufw.go index e82e29ada..0cc30aebd 100644 --- a/backend/utils/firewall/client/ufw.go +++ b/backend/utils/firewall/client/ufw.go @@ -6,7 +6,6 @@ import ( "github.com/1Panel-dev/1Panel/backend/buserr" "github.com/1Panel-dev/1Panel/backend/constant" - "github.com/1Panel-dev/1Panel/backend/global" "github.com/1Panel-dev/1Panel/backend/utils/cmd" ) @@ -101,16 +100,12 @@ func (f *Ufw) ListPort() ([]FireInfo, error) { } func (f *Ufw) ListForward() ([]FireInfo, error) { + _ = f.EnableForward() iptables, err := NewIptables() if err != nil { return nil, err } - panelChian, _ := cmd.Execf("%s iptables -t nat -L -n | grep 'Chain 1PANEL'", iptables.CmdStr) - if len(strings.ReplaceAll(panelChian, "\n", "")) == 0 { - if err := f.EnableForward(); err != nil { - global.LOG.Errorf("init port forward failed, err: %v", err) - } - } + rules, err := iptables.NatList() if err != nil { return nil, err @@ -305,21 +300,34 @@ func (f *Ufw) EnableForward() error { if err != nil { return err } - _ = iptables.NatNewChain() + if err = iptables.Check(); err != nil { + return err + } + + _ = iptables.NewChain(NatTab, PreRoutingChain) + _ = iptables.NewChain(NatTab, PostRoutingChain) + _ = iptables.NewChain(FilterTab, ForwardChain) + + if err = f.enableChain(iptables); err != nil { + return err + } + return iptables.Reload() +} + +func (f *Ufw) enableChain(iptables *Iptables) error { rules, err := iptables.NatList("PREROUTING") if err != nil { return err } for _, rule := range rules { - if rule.Target == NatChain { - goto reload + if rule.Target == PreRoutingChain { + return nil } } - if err = iptables.NatAppendChain(); err != nil { - return err - } -reload: - return iptables.Reload() + _ = iptables.AppendChain(NatTab, "PREROUTING", PreRoutingChain) + _ = iptables.AppendChain(NatTab, "POSTROUTING", PostRoutingChain) + _ = iptables.AppendChain(FilterTab, "FORWARD", ForwardChain) + return nil }