From 496c0b50b42e8f908888919349e407f79e8ad796 Mon Sep 17 00:00:00 2001 From: ssongliu <73214554+ssongliu@users.noreply.github.com> Date: Mon, 24 Nov 2025 22:33:15 +0800 Subject: [PATCH] fix: Fix the issue of abnormal iptables rule persistence (#11056) Refs #11027 --- agent/app/api/v2/firewall.go | 2 +- agent/app/service/iptables.go | 51 ++++++++---- agent/init/firewall/firwall.go | 77 +++++++++++++++++-- agent/utils/firewall/client/iptables.go | 12 ++- .../firewall/client/iptables/persistence.go | 50 +++++------- 5 files changed, 139 insertions(+), 53 deletions(-) diff --git a/agent/app/api/v2/firewall.go b/agent/app/api/v2/firewall.go index fda0bcebb..b36ddb871 100644 --- a/agent/app/api/v2/firewall.go +++ b/agent/app/api/v2/firewall.go @@ -269,7 +269,7 @@ func (b *BaseApi) OperateFilterRule(c *gin.Context) { if err := helper.CheckBindAndValidate(&req, c); err != nil { return } - if err := iptablesService.OperateRule(req); err != nil { + if err := iptablesService.OperateRule(req, true); err != nil { helper.InternalServer(c, err) return } diff --git a/agent/app/service/iptables.go b/agent/app/service/iptables.go index 56f3e952e..615277a57 100644 --- a/agent/app/service/iptables.go +++ b/agent/app/service/iptables.go @@ -16,7 +16,7 @@ import ( type IIptablesService interface { Search(req dto.SearchPageWithType) (int64, interface{}, error) - OperateRule(req dto.IptablesRuleOp) error + OperateRule(req dto.IptablesRuleOp, withSave bool) error BatchOperate(req dto.IptablesBatchOperate) error LoadChainStatus(req dto.OperationWithName) dto.IptablesChainStatus @@ -63,7 +63,7 @@ func (s *IptablesService) Search(req dto.SearchPageWithType) (int64, interface{} return int64(total), records, nil } -func (s *IptablesService) OperateRule(req dto.IptablesRuleOp) error { +func (s *IptablesService) OperateRule(req dto.IptablesRuleOp, withSave bool) error { action := "ACCEPT" if req.Strategy == "drop" { action = "DROP" @@ -122,6 +122,9 @@ func (s *IptablesService) OperateRule(req dto.IptablesRuleOp) error { } } + if !withSave { + return nil + } if err := iptables.SaveRulesToFile(iptables.FilterTab, req.Chain, name); err != nil { global.LOG.Errorf("persistence for %s failed, err: %v", iptables.Chain1PanelBasic, err) } @@ -129,11 +132,23 @@ func (s *IptablesService) OperateRule(req dto.IptablesRuleOp) error { } func (s *IptablesService) BatchOperate(req dto.IptablesBatchOperate) error { + if len(req.Rules) == 0 { + return errors.New("no rules to operate") + } for _, rule := range req.Rules { - if err := s.OperateRule(rule); err != nil { + if err := s.OperateRule(rule, false); err != nil { return err } } + chain := iptables.Chain1PanelInput + fileName := iptables.InputFileName + if req.Rules[0].Chain == iptables.Chain1PanelOutput { + chain = iptables.Chain1PanelOutput + fileName = iptables.OutputFileName + } + if err := iptables.SaveRulesToFile(iptables.FilterTab, chain, fileName); err != nil { + global.LOG.Errorf("persistence for %s failed, err: %v", iptables.Chain1PanelBasic, err) + } return nil } @@ -168,6 +183,12 @@ func (s *IptablesService) Operate(req dto.IptablesOp) error { if err := iptables.BindChain(iptables.FilterTab, iptables.ChainInput, iptables.Chain1PanelBasicAfter, 3); err != nil { return err } + if err := iptables.SaveRulesToFile(iptables.FilterTab, iptables.Chain1PanelBasicBefore, iptables.BasicBeforeFileName); err != nil { + return err + } + if err := iptables.SaveRulesToFile(iptables.FilterTab, iptables.Chain1PanelBasicAfter, iptables.BasicAfterFileName); err != nil { + return err + } return nil case "init-forward": return client.EnableIptablesForward() @@ -310,16 +331,7 @@ func initPreRules() error { if err := iptables.AddRule(iptables.FilterTab, iptables.Chain1PanelBasicBefore, iptables.EstablishedRule); err != nil { return err } - panelPort := "" - if !global.IsMaster { - panelPort = global.CONF.Base.Port - } else { - var portSetting model.Setting - _ = global.CoreDB.Where("key = ?", "ServerPort").First(&portSetting).Error - if len(portSetting.Value) != 0 { - panelPort = portSetting.Value - } - } + panelPort := LoadPanelPort() if len(panelPort) == 0 { return errors.New("find 1panel service port failed") } @@ -337,3 +349,16 @@ func initPreRules() error { } return nil } + +func LoadPanelPort() string { + if !global.IsMaster { + return global.CONF.Base.Port + } else { + var portSetting model.Setting + _ = global.CoreDB.Where("key = ?", "ServerPort").First(&portSetting).Error + if len(portSetting.Value) != 0 { + return portSetting.Value + } + } + return "" +} diff --git a/agent/init/firewall/firwall.go b/agent/init/firewall/firwall.go index e1d1ab9e9..fdb695b5d 100644 --- a/agent/init/firewall/firwall.go +++ b/agent/init/firewall/firwall.go @@ -1,6 +1,11 @@ package firewall import ( + "fmt" + + "github.com/1Panel-dev/1Panel/agent/app/dto" + "github.com/1Panel-dev/1Panel/agent/app/service" + "github.com/1Panel-dev/1Panel/agent/global" "github.com/1Panel-dev/1Panel/agent/utils/firewall" "github.com/1Panel-dev/1Panel/agent/utils/firewall/client/iptables" ) @@ -12,13 +17,73 @@ func Init() { } clientName := client.Name() if clientName == "ufw" || clientName == "iptables" { - _ = iptables.LoadRulesFromFile(iptables.FilterTab, iptables.Chain1PanelForward, iptables.ForwardFileName) - _ = iptables.LoadRulesFromFile(iptables.NatTab, iptables.Chain1PanelPreRouting, iptables.ForwardFileName1) - _ = iptables.LoadRulesFromFile(iptables.NatTab, iptables.Chain1PanelPostRouting, iptables.ForwardFileName2) + if err := iptables.LoadRulesFromFile(iptables.FilterTab, iptables.Chain1PanelForward, iptables.ForwardFileName); err != nil { + global.LOG.Errorf("load forward rules from file failed, err: %v", err) + return + } + if err := iptables.LoadRulesFromFile(iptables.NatTab, iptables.Chain1PanelPreRouting, iptables.ForwardFileName1); err != nil { + global.LOG.Errorf("load prerouting rules from file failed, err: %v", err) + return + } + if err := iptables.LoadRulesFromFile(iptables.NatTab, iptables.Chain1PanelPostRouting, iptables.ForwardFileName2); err != nil { + global.LOG.Errorf("load postrouting rules from file failed, err: %v", err) + return + } + global.LOG.Infof("loaded iptables rules for forward from file successfully") + } + if clientName == "ufw" { + _ = iptables.UnbindChain(iptables.FilterTab, iptables.ChainInput, iptables.Chain1PanelBasicAfter) + _ = iptables.UnbindChain(iptables.FilterTab, iptables.ChainInput, iptables.Chain1PanelBasicBefore) + _ = iptables.UnbindChain(iptables.FilterTab, iptables.ChainInput, iptables.Chain1PanelBasic) + _ = iptables.UnbindChain(iptables.FilterTab, iptables.ChainInput, iptables.Chain1PanelInput) + _ = iptables.UnbindChain(iptables.FilterTab, iptables.ChainOutput, iptables.Chain1PanelOutput) } if clientName == "iptables" { - _ = iptables.LoadRulesFromFile(iptables.FilterTab, iptables.Chain1PanelBasic, iptables.BasicFileName) - _ = iptables.LoadRulesFromFile(iptables.FilterTab, iptables.Chain1PanelInput, iptables.InputFileName) - _ = iptables.LoadRulesFromFile(iptables.FilterTab, iptables.Chain1PanelOutput, iptables.OutputFileName) + if err := iptables.LoadRulesFromFile(iptables.FilterTab, iptables.Chain1PanelBasicBefore, iptables.BasicBeforeFileName); err != nil { + global.LOG.Errorf("load basic before rules from file failed, err: %v", err) + return + } + if err := iptables.LoadRulesFromFile(iptables.FilterTab, iptables.Chain1PanelBasic, iptables.BasicFileName); err != nil { + global.LOG.Errorf("load basic rules from file failed, err: %v", err) + return + } + if err := iptables.LoadRulesFromFile(iptables.FilterTab, iptables.Chain1PanelBasicAfter, iptables.BasicAfterFileName); err != nil { + global.LOG.Errorf("load basic after rules from file failed, err: %v", err) + return + } + if err := iptables.LoadRulesFromFile(iptables.FilterTab, iptables.Chain1PanelInput, iptables.InputFileName); err != nil { + global.LOG.Errorf("load input rules from file failed, err: %v", err) + return + } + if err := iptables.LoadRulesFromFile(iptables.FilterTab, iptables.Chain1PanelOutput, iptables.OutputFileName); err != nil { + global.LOG.Errorf("load output rules from file failed, err: %v", err) + return + } + global.LOG.Infof("loaded iptables rules for basic, input and output from file successfully") + + panelPort := service.LoadPanelPort() + if len(panelPort) == 0 { + global.LOG.Errorf("find 1panel service port failed") + return + } + if err := iptables.AddRule(iptables.FilterTab, iptables.Chain1PanelBasicBefore, fmt.Sprintf("-p tcp -m tcp --dport %v -j ACCEPT", panelPort)); err != nil { + global.LOG.Errorf("add port accept rule %v failed, err: %v", panelPort, err) + return + } + + iptablesService := service.IptablesService{} + if err := iptablesService.Operate(dto.IptablesOp{Operate: "bind-base"}); err != nil { + global.LOG.Errorf("bind base chains failed, err: %v", err) + return + } + if err := iptablesService.Operate(dto.IptablesOp{Name: iptables.Chain1PanelOutput, Operate: "bind"}); err != nil { + global.LOG.Errorf("bind output chains failed, err: %v", err) + return + } + if err := iptablesService.Operate(dto.IptablesOp{Name: iptables.Chain1PanelInput, Operate: "bind"}); err != nil { + global.LOG.Errorf("bind input chains failed, err: %v", err) + return + } } + } diff --git a/agent/utils/firewall/client/iptables.go b/agent/utils/firewall/client/iptables.go index c847e2c6c..c957d08a0 100644 --- a/agent/utils/firewall/client/iptables.go +++ b/agent/utils/firewall/client/iptables.go @@ -144,8 +144,12 @@ func (i *Iptables) Port(port FireInfo, operation string) error { } } + name := iptables.BasicFileName + if port.Chain == iptables.Chain1PanelBasicBefore { + name = iptables.BasicBeforeFileName + } if port.Chain == iptables.Chain1PanelBasic { - if err := iptables.SaveRulesToFile(iptables.FilterTab, iptables.Chain1PanelBasic, iptables.BasicFileName); err != nil { + if err := iptables.SaveRulesToFile(iptables.FilterTab, port.Chain, name); err != nil { global.LOG.Errorf("persistence for %s failed, err: %v", iptables.Chain1PanelBasic, err) } } @@ -207,8 +211,12 @@ func (i *Iptables) RichRules(rule FireInfo, operation string) error { } } + name := iptables.BasicFileName + if rule.Chain == iptables.Chain1PanelBasicBefore { + name = iptables.BasicBeforeFileName + } if rule.Chain == iptables.Chain1PanelBasic { - if err := iptables.SaveRulesToFile(iptables.FilterTab, iptables.Chain1PanelBasic, iptables.BasicFileName); err != nil { + if err := iptables.SaveRulesToFile(iptables.FilterTab, rule.Chain, name); err != nil { global.LOG.Errorf("persistence for %s failed, err: %v", iptables.Chain1PanelBasic, err) } } diff --git a/agent/utils/firewall/client/iptables/persistence.go b/agent/utils/firewall/client/iptables/persistence.go index c2f0b7f2e..d5380794f 100644 --- a/agent/utils/firewall/client/iptables/persistence.go +++ b/agent/utils/firewall/client/iptables/persistence.go @@ -11,12 +11,14 @@ import ( ) const ( - BasicFileName = "1panel_basic.rules" - InputFileName = "1panel_input.rules" - OutputFileName = "1panel_out.rules" - ForwardFileName = "1panel_forward.rules" - ForwardFileName1 = "1panel_forward_pre.rules" - ForwardFileName2 = "1panel_forward_post.rules" + BasicBeforeFileName = "1panel_basic_before.rules" + BasicFileName = "1panel_basic.rules" + BasicAfterFileName = "1panel_basic_after.rules" + InputFileName = "1panel_input.rules" + OutputFileName = "1panel_out.rules" + ForwardFileName = "1panel_forward.rules" + ForwardFileName1 = "1panel_forward_pre.rules" + ForwardFileName2 = "1panel_forward_post.rules" ) func SaveRulesToFile(tab, chain, fileName string) error { @@ -63,39 +65,25 @@ func LoadRulesFromFile(tab, chain, fileName string) error { return nil } - file, err := os.Open(rulesFile) + if err := AddChain(tab, chain); err != nil { + global.LOG.Errorf("create chain %s failed: %v", chain, err) + return err + } + data, err := os.ReadFile(rulesFile) if err != nil { - return fmt.Errorf("failed to open rules file: %w", err) + global.LOG.Errorf("read rules from file %s failed, err: %v", rulesFile, err) + return err } - defer file.Close() - - var rules []string - scanner := bufio.NewScanner(file) - for scanner.Scan() { - line := strings.TrimSpace(scanner.Text()) - if line == "" || strings.HasPrefix(line, "#") { - continue - } - rules = append(rules, line) - } - - if err := scanner.Err(); err != nil { - return fmt.Errorf("failed to read rules file: %w", err) - } - + rules := strings.Split(string(data), "\n") if err := ClearChain(tab, chain); err != nil { - global.LOG.Warnf("Failed to clear existing rules from %s: %v", chain, err) + global.LOG.Warnf("clear existing rules from %s failed, err: %v", chain, err) } - appliedCount := 0 for _, rule := range rules { if strings.HasPrefix(rule, fmt.Sprintf("-A %s", chain)) { - ruleArgs := strings.TrimPrefix(rule, "-A ") - if err := Run(tab, "-A "+ruleArgs); err != nil { - global.LOG.Errorf("Failed to apply rule '%s': %v", rule, err) - continue + if err := Run(tab, rule); err != nil { + global.LOG.Errorf("apply rule '%s' failed, err: %v", rule, err) } - appliedCount++ } }