diff --git a/agent/app/service/firewall_utils.go b/agent/app/service/firewall_utils.go index 6a4c754e5..4d40f4675 100644 --- a/agent/app/service/firewall_utils.go +++ b/agent/app/service/firewall_utils.go @@ -582,6 +582,18 @@ func (s *FirewallService) createRules(ctx context.Context, request dto.FirewallR if err != nil { return result, err } + importPrioritySupported := true + if adapter, ok := runtime.(*filterfirewalld.Adapter); ok && slices.ContainsFunc(request.Items, func(item dto.FirewallRuleCreateItem) bool { + return item.SourceKind == constant.FirewallRuleSourceImported + }) { + importPrioritySupported, err = adapter.SupportsRichRulePriority(ctx) + if err != nil { + return result, err + } + if !importPrioritySupported && t != nil { + t.Log("firewalld does not support rich rule priority; ignoring priority for imported rules, original rule ordering is not preserved") + } + } preparedItems := make([]*createItem, 0, len(request.Items)) var stop error for index, item := range request.Items { @@ -593,6 +605,16 @@ func (s *FirewallService) createRules(ctx context.Context, request dto.FirewallR continue } item.Rule.OrderIndex = nil + if item.SourceKind == constant.FirewallRuleSourceImported && !importPrioritySupported { + item.Rule.Priority = nil + if item.Rule.Scope.Provider == filter.ProviderFirewalld && item.Raw != "" && item.ParseStatus != "" && item.ParseStatus != filter.ParseStatusSupported && item.Rule.NativeKind != filter.NativeKindZoneService { + item.Raw, err = filterfirewalld.RemoveRichRulePriority(item.Raw) + if err != nil { + record(createItem{index: index, request: item}, "skipped", err) + continue + } + } + } rules, err := expandFirewallCreateRule(item, selected) if err != nil { status := "failed" @@ -693,7 +715,7 @@ func (s *FirewallService) createRules(ctx context.Context, request dto.FirewallR record(*item, "skipped", buserr.New("ErrRecordExist")) continue } - if converted { + if converted && importPrioritySupported { priority++ } if existing[key] { @@ -713,7 +735,7 @@ func (s *FirewallService) createRules(ctx context.Context, request dto.FirewallR } ufwActions[matchKey] = map[filter.Action]bool{item.request.Rule.Action: true} } - if converted { + if converted && importPrioritySupported { if priority > 32767 { return result, fmt.Errorf("%w: firewalld import order exceeds the priority range", filter.ErrExpansionLimit) } @@ -729,18 +751,19 @@ func (s *FirewallService) createRules(ctx context.Context, request dto.FirewallR continue } item.request = prepared - actualKey, err := firewallRuleDuplicateKey(filter.ObservedRule{Rule: prepared.Rule, ParseStatus: prepared.ParseStatus}, false) + } + pending[key] = true + if converted { + actualKey, err := firewallRuleDuplicateKey(filter.ObservedRule{Rule: item.request.Rule, ParseStatus: item.request.ParseStatus}, false) if err != nil { return result, err } - pending[key] = true if existing[actualKey] || pending[actualKey] { record(*item, "skipped", buserr.New("ErrRecordExist")) continue } pending[actualKey] = true } - pending[key] = true mergedItems = append(mergedItems, item) } for _, item := range mergedItems { diff --git a/agent/utils/firewall/docker_guard/policy.go b/agent/utils/firewall/docker_guard/policy.go index f47efdd88..24a1e30c1 100644 --- a/agent/utils/firewall/docker_guard/policy.go +++ b/agent/utils/firewall/docker_guard/policy.go @@ -92,6 +92,9 @@ func dockerPolicyRuleKey(tokens []string, backend string) string { if address, err := netip.ParseAddr(value); err == nil { value = netip.PrefixFrom(address, address.BitLen()).String() } else if prefix, err := netip.ParsePrefix(value); err == nil { + if prefix.Bits() == 0 { + continue + } value = prefix.Masked().String() } } @@ -156,7 +159,13 @@ func parseDockerGuardPolicies(output, family string) (PolicyInventory, error) { group.acceptedSource = append(group.acceptedSource, source) case action == "accept": group.acceptAll = true - case action == "return" && source != "": + case action == "return": + if source == "" { + source = "0.0.0.0/0" + if family == FamilyIPv6 { + source = "::/0" + } + } group.allowedSource = append(group.allowedSource, source) case action == "drop" && source != "": group.droppedSource = append(group.droppedSource, source) diff --git a/agent/utils/firewall/filter/providers/firewalld/adapter.go b/agent/utils/firewall/filter/providers/firewalld/adapter.go index 0f6d56fcc..93c4c3c51 100644 --- a/agent/utils/firewall/filter/providers/firewalld/adapter.go +++ b/agent/utils/firewall/filter/providers/firewalld/adapter.go @@ -50,7 +50,7 @@ func (a *Adapter) CheckRule(ctx context.Context, rule filter.FirewallRule) error if rule.Priority == nil || *rule.Priority == 0 { return nil } - supported, err := a.supportsRichRulePriority(ctx) + supported, err := a.SupportsRichRulePriority(ctx) if err != nil { return err } @@ -60,7 +60,7 @@ func (a *Adapter) CheckRule(ctx context.Context, rule filter.FirewallRule) error return nil } -func (a *Adapter) supportsRichRulePriority(ctx context.Context) (bool, error) { +func (a *Adapter) SupportsRichRulePriority(ctx context.Context) (bool, error) { a.prioritySupportMu.Lock() defer a.prioritySupportMu.Unlock() if a.richRulePrioritySupported != nil { @@ -82,6 +82,52 @@ func (a *Adapter) supportsRichRulePriority(ctx context.Context) (bool, error) { return supported, nil } +func RemoveRichRulePriority(raw string) (string, error) { + if strings.ContainsAny(raw, "\r\n\x00") { + return "", filter.ErrInvalidRule + } + parts := make([]string, 0) + removed := false + for start := 0; start < len(raw); { + if raw[start] == ' ' || raw[start] == '\t' { + start++ + continue + } + end, quote := start, byte(0) + for end < len(raw) { + char := raw[end] + if char == '\\' && quote != '\'' { + end += 2 + continue + } + if quote != 0 { + if char == quote { + quote = 0 + } + } else if char == '\'' || char == '"' { + quote = char + } else if char == ' ' || char == '\t' { + break + } + end++ + } + if quote != 0 || end > len(raw) { + return "", filter.ErrInvalidRule + } + token := raw[start:end] + if strings.HasPrefix(token, "priority=") { + removed = true + } else { + parts = append(parts, token) + } + start = end + } + if !removed || len(parts) == 0 || parts[0] != "rule" { + return raw, nil + } + return strings.Join(parts, " "), nil +} + func parseFirewalldVersion(output string) (int, int, error) { version := strings.TrimSpace(output) parts := strings.Split(version, ".")