mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2025-01-19 00:09:16 +08:00
feat(iptables): Support forwarding for complex networks (#7598)
* feat(iptables): 支持对复杂网络的转发 * refactor(iptables): 提供更完善的接口和报错
This commit is contained in:
parent
7c46f54e15
commit
55bb997d51
@ -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)
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
@ -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"`
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user