fix(firewall): preserve whitelist priority and rule ordering (#13845)

This commit is contained in:
ssongliu
2026-09-17 13:02:08 +08:00
committed by GitHub
parent 673ffac516
commit e833787020
10 changed files with 234 additions and 131 deletions

View File

@@ -1015,7 +1015,9 @@ func (s *FirewallService) Create(
return dto.FirewallRuleCreateResponse{}, err
}
taskItem.AddSubTaskWithOps(i18n.GetMsgByKey("FirewallCreateRulesStep"), func(t *task.Task) error {
_, err := s.runCreateTask(t, request)
firewallRuleMutationMu.Lock()
defer firewallRuleMutationMu.Unlock()
_, err := s.createRules(t.TaskCtx, request, t)
return err
}, nil, 0, 0)
if err := repo.NewITaskRepo().Save(context.Background(), taskItem.Task); err != nil {
@@ -1031,12 +1033,6 @@ func (s *FirewallService) Create(
return dto.FirewallRuleCreateResponse{TaskID: taskItem.TaskID, Queued: true}, nil
}
func (s *FirewallService) runCreateTask(t *task.Task, request dto.FirewallRuleCreate) (dto.FirewallRuleCreateResponse, error) {
firewallRuleMutationMu.Lock()
defer firewallRuleMutationMu.Unlock()
return s.createRules(t.TaskCtx, request, t)
}
func (s *FirewallService) createRules(ctx context.Context, request dto.FirewallRuleCreate, t *task.Task) (result dto.FirewallRuleCreateResponse, taskErr error) {
var firstFailure error
defer func() {
@@ -1412,7 +1408,13 @@ func (s *FirewallService) Delete(ctx context.Context, request dto.FirewallRuleDe
return dto.FirewallRuleDeleteResponse{}, err
}
taskItem.AddSubTaskWithOps(taskItem.Name, func(t *task.Task) error {
_, err := s.runDeleteTask(t, request)
t.Logf("rules=%d", len(request.UUIDs)+len(request.BeforeRules))
firewallRuleMutationMu.Lock()
defer firewallRuleMutationMu.Unlock()
if err := t.TaskCtx.Err(); err != nil {
return err
}
_, err := s.deleteRules(t.TaskCtx, request, t)
return err
}, nil, 0, 0)
if err := repo.NewITaskRepo().Save(context.Background(), taskItem.Task); err != nil {
@@ -1428,16 +1430,6 @@ func (s *FirewallService) Delete(ctx context.Context, request dto.FirewallRuleDe
return dto.FirewallRuleDeleteResponse{TaskID: taskItem.TaskID, Queued: true}, nil
}
func (s *FirewallService) runDeleteTask(t *task.Task, request dto.FirewallRuleDelete) (dto.FirewallRuleDeleteResponse, error) {
t.Logf("rules=%d", len(request.UUIDs)+len(request.BeforeRules))
firewallRuleMutationMu.Lock()
defer firewallRuleMutationMu.Unlock()
if err := t.TaskCtx.Err(); err != nil {
return dto.FirewallRuleDeleteResponse{}, err
}
return s.deleteRules(t.TaskCtx, request, t)
}
func (s *FirewallService) deleteRules(ctx context.Context, request dto.FirewallRuleDelete, t *task.Task) (result dto.FirewallRuleDeleteResponse, taskErr error) {
var firstFailure error
defer func() {
@@ -1619,7 +1611,7 @@ func (s *FirewallService) prepareDelete(
}
return preparedFirewallRuleDelete{}, err
}
if err := checkFirewallRuleWhitelistProtection(ctx, stored); err != nil {
if err := checkFirewallRuleWhitelistProtection(selectedProvider, stored); err != nil {
return preparedFirewallRuleDelete{}, err
}
if stored.Origin != constant.FirewallRuleOriginCreated && stored.Origin != constant.FirewallRuleOriginAdopted {
@@ -1754,7 +1746,11 @@ func (s *FirewallService) updateRuleDescription(ctx context.Context, ruleUUID, d
if err != nil {
return err
}
if err := checkFirewallRuleWhitelistProtection(ctx, stored); err != nil {
selected, err := s.selectedProviderForStoredRule(ctx, stored)
if err != nil {
return err
}
if err := checkFirewallRuleWhitelistProtection(selected, stored); err != nil {
return err
}
if stored.Origin != constant.FirewallRuleOriginCreated && stored.Origin != constant.FirewallRuleOriginAdopted {
@@ -1900,7 +1896,7 @@ func (s *FirewallService) deleteRule(ctx context.Context, ruleUUID string) error
if err != nil {
return err
}
if err := checkFirewallRuleWhitelistProtection(ctx, stored); err != nil {
if err := checkFirewallRuleWhitelistProtection(selected, stored); err != nil {
return err
}
type appliedDelete struct {
@@ -2212,9 +2208,6 @@ func (s *FirewallService) loadManagedMutation(
if err != nil {
return model.FirewallRule{}, filter.DesiredRule{}, filter.Snapshot{}, filter.ObservedRule{}, nil, err
}
if err := checkFirewallRuleWhitelistProtection(ctx, stored); err != nil {
return model.FirewallRule{}, filter.DesiredRule{}, filter.Snapshot{}, filter.ObservedRule{}, nil, err
}
if stored.Origin != constant.FirewallRuleOriginCreated && stored.Origin != constant.FirewallRuleOriginAdopted {
return model.FirewallRule{}, filter.DesiredRule{}, filter.Snapshot{}, filter.ObservedRule{}, nil,
fmt.Errorf("%w: only created or adopted rules can be changed", filter.ErrInvalidRule)
@@ -2223,6 +2216,9 @@ func (s *FirewallService) loadManagedMutation(
if err != nil {
return model.FirewallRule{}, filter.DesiredRule{}, filter.Snapshot{}, filter.ObservedRule{}, nil, err
}
if err := checkFirewallRuleWhitelistProtection(selected, stored); err != nil {
return model.FirewallRule{}, filter.DesiredRule{}, filter.Snapshot{}, filter.ObservedRule{}, nil, err
}
desiredRules, err := s.compileStoredFirewallRules(ctx, stored, selected)
if err != nil {
return model.FirewallRule{}, filter.DesiredRule{}, filter.Snapshot{}, filter.ObservedRule{}, nil, err
@@ -2617,16 +2613,27 @@ func (s *FirewallService) sequenceForFirewallRulePosition(
byUUID := make(map[string]model.FirewallRule, len(stored))
for _, record := range stored {
byUUID[record.UUID] = record
compiled, err := s.compileStoredFirewallRules(ctx, record, snapshot.Scope.Provider)
if err != nil {
if isFirewallPolicyIncompatible(err) {
continue
}
return 0, err
}
for _, desired := range compiled {
if desired.Rule.Scope.Key() == snapshot.Scope.Key() {
byUUID[desired.Rule.UUID] = record
}
}
}
var previous, next *model.FirewallRule
needsRebalance := false
for _, observed := range snapshot.Rules {
uuid := strings.TrimPrefix(observed.Marker, "1panel-rule:")
if observed.Marker == uuid || uuid == excludedUUID || observed.Locator.Position == nil {
continue
}
record, exists := byUUID[uuid]
if !exists {
if !exists || record.UUID == excludedUUID {
continue
}
position := *observed.Locator.Position
@@ -2640,30 +2647,24 @@ func (s *FirewallService) sequenceForFirewallRulePosition(
}
}
if previous != nil && previous.Sequence == nil || next != nil && next.Sequence == nil {
needsRebalance = true
return s.rebalanceFirewallRuleSequences(ctx, snapshot, targetPosition, excludedUUID, byUUID)
}
if !needsRebalance && current != nil &&
if current != nil &&
(previous == nil || *previous.Sequence < *current) && (next == nil || *current < *next.Sequence) {
return *current, nil
}
if !needsRebalance {
switch {
case previous == nil && next == nil:
return model.FirewallRuleSequenceStep, nil
case previous == nil:
return *next.Sequence - model.FirewallRuleSequenceStep, nil
case next == nil:
return *previous.Sequence + model.FirewallRuleSequenceStep, nil
case *next.Sequence-*previous.Sequence > 1:
return *previous.Sequence + (*next.Sequence-*previous.Sequence)/2, nil
default:
needsRebalance = true
}
}
if needsRebalance {
switch {
case previous == nil && next == nil:
return model.FirewallRuleSequenceStep, nil
case previous == nil:
return *next.Sequence - model.FirewallRuleSequenceStep, nil
case next == nil:
return *previous.Sequence + model.FirewallRuleSequenceStep, nil
case *next.Sequence-*previous.Sequence > 1:
return *previous.Sequence + (*next.Sequence-*previous.Sequence)/2, nil
default:
return s.rebalanceFirewallRuleSequences(ctx, snapshot, targetPosition, excludedUUID, byUUID)
}
return 0, fmt.Errorf("%w: cannot allocate firewall rule sequence", filter.ErrRuleOperation)
}
func (s *FirewallService) rebalanceFirewallRuleSequences(
@@ -2674,6 +2675,7 @@ func (s *FirewallService) rebalanceFirewallRuleSequences(
byUUID map[string]model.FirewallRule,
) (int64, error) {
targetSequence := int64(targetPosition) * model.FirewallRuleSequenceStep
updated := make(map[string]bool)
for _, observed := range snapshot.Rules {
if observed.Locator.Position == nil {
continue
@@ -2683,9 +2685,10 @@ func (s *FirewallService) rebalanceFirewallRuleSequences(
continue
}
record, exists := byUUID[uuid]
if !exists {
if !exists || record.UUID == excludedUUID || updated[record.UUID] {
continue
}
updated[record.UUID] = true
position := *observed.Locator.Position
if excludedUUID == "" && position >= targetPosition {
position++
@@ -2825,7 +2828,11 @@ func firewallRuleSnapshotPolicy(ctx context.Context, snapshot filter.Snapshot) (
}
func firewallRuleSelectedProvider(context.Context) (filter.Provider, error) {
return selectedRuleProvider()
provider, err := selectedSystemFirewallProvider()
if err != nil {
return "", fmt.Errorf("%w: %v", filter.ErrProviderUnavailable, err)
}
return filter.Provider(provider), nil
}
func rollbackFirewallPlan(ctx context.Context, runtime *filterruntime.Engine, plan filter.BackendPlan, cause error) error {
@@ -2838,14 +2845,6 @@ func rollbackFirewallPlan(ctx context.Context, runtime *filterruntime.Engine, pl
return cause
}
func selectedRuleProvider() (filter.Provider, error) {
provider, err := selectedSystemFirewallProvider()
if err != nil {
return "", fmt.Errorf("%w: %v", filter.ErrProviderUnavailable, err)
}
return filter.Provider(provider), nil
}
func ensureFirewallPorts(ports []int) error {
filterruntime.InvalidateInventory()
defer filterruntime.InvalidateInventory()
@@ -2858,7 +2857,7 @@ func ensureFirewallPorts(ports []int) error {
return err
}
if state.Name == constant.FirewallProviderIptables || state.Name == constant.FirewallProviderNftables {
isInit, _, err := loadDirectFirewallInitStatus(state.Name)
isInit, _, err := loadFirewallInitStatus(state.Name, "base")
if err != nil {
return err
}
@@ -2934,10 +2933,6 @@ func newNftablesHelperManager() *nftables_helper.Manager {
}
}
func loadDirectFirewallInitStatus(provider string) (bool, bool, error) {
return loadFirewallInitStatus(provider, "base")
}
func loadFirewallInitStatus(provider, tab string) (bool, bool, error) {
switch provider {
case constant.FirewallProviderNftables:
@@ -2968,7 +2963,7 @@ func (s *FirewallService) restoreFirewallAfterStart(client lifecycle.Client) err
}
}
if provider == filter.ProviderIptables || provider == filter.ProviderNftables {
isInit, _, err := loadDirectFirewallInitStatus(string(provider))
isInit, _, err := loadFirewallInitStatus(string(provider), "base")
if err != nil {
recordFailure("load managed chain status", err)
return errors.Join(recoveryErrors...)

View File

@@ -137,12 +137,12 @@ func savePortWhitelist(ctx context.Context, change func([]firewall.PortWhitelist
})
}
func checkFirewallRuleWhitelistProtection(ctx context.Context, record model.FirewallRule) error {
func checkFirewallRuleWhitelistProtection(provider filter.Provider, record model.FirewallRule) error {
ports, err := loadFirewallPortWhiteList()
if err != nil {
return err
}
rules, err := record.RulesForProvider(filter.ProviderIptables)
rules, err := record.RulesForProvider(provider)
if err != nil {
return err
}

View File

@@ -106,6 +106,7 @@ func (s *FirewallService) loadFirewallSyncRules(ctx context.Context, request dto
}
for _, native := range desired {
rule := native.Rule
native.Protected = filter.RuleMatchesPortWhitelist(rule, ports)
rules = append(rules, &firewallSyncRule{desired: native, FirewallRuleSyncItem: dto.FirewallRuleSyncItem{SourceUUID: record.UUID, Rule: &rule}})
}
}
@@ -210,7 +211,7 @@ func (s *FirewallService) loadFirewallSyncRules(ctx context.Context, request dto
}
drifted := firewallsync.RuleOrder(snapshot, ordered)
for _, rule := range scoped {
if !drifted[rule.desired.Marker] {
if rule.desired.Protected || !drifted[rule.desired.Marker] {
continue
}
if rule.Status == firewallsync.StatusExisting {
@@ -586,7 +587,12 @@ func (s *FirewallService) syncSystemRules(
return dto.FirewallRuleSyncResult{}, err
}
if running.Executing {
return runningFirewallRuleSyncResult(request, running.TaskID), nil
return dto.FirewallRuleSyncResult{
Subsystem: firewallSyncSubsystem(request.Subsystem),
TargetProvider: request.TargetProvider,
TaskID: running.TaskID,
Queued: true,
}, nil
}
if firewallSyncSubsystem(request.Subsystem) != "system" {
return dto.FirewallRuleSyncResult{}, fmt.Errorf("%w: firewall synchronization tasks are only available for the system firewall", filter.ErrInvalidRule)
@@ -660,15 +666,6 @@ func currentFirewallRuleSyncTaskLocked() (dto.FirewallRuleSyncTask, error) {
return dto.FirewallRuleSyncTask{TaskID: record.ID, Executing: true}, nil
}
func runningFirewallRuleSyncResult(request dto.FirewallRuleSyncRequest, taskID string) dto.FirewallRuleSyncResult {
return dto.FirewallRuleSyncResult{
Subsystem: firewallSyncSubsystem(request.Subsystem),
TargetProvider: request.TargetProvider,
TaskID: taskID,
Queued: true,
}
}
func whitelistRules(provider filter.Provider, ports, required []firewall.SystemPort) []filter.FirewallRule {
rules := make([]filter.FirewallRule, 0, len(ports)+len(required))
for _, port := range required {
@@ -747,6 +744,10 @@ func (s *FirewallService) SyncPortWhitelist(ctx context.Context) error {
if len(failures) > 0 {
return errors.Join(failures...)
}
if provider == filter.ProviderUFW && len(prepared) > 0 {
return s.syncUFWPortWhitelist(ctx, prepared)
}
for _, item := range prepared {
if err := ctx.Err(); err != nil {
return err
@@ -759,6 +760,55 @@ func (s *FirewallService) SyncPortWhitelist(ctx context.Context) error {
return errors.Join(failures...)
}
func (s *FirewallService) syncUFWPortWhitelist(ctx context.Context, prepared []preparedFirewallRuleCreate) error {
firewallRuleMutationMu.Lock()
defer firewallRuleMutationMu.Unlock()
runtime := prepared[0].runtime
snapshots, err := runtime.ObserveScopes(ctx, filter.ManagedInputScopes(filter.ProviderUFW))
if err != nil {
return err
}
for itemIndex, item := range prepared {
if err := ctx.Err(); err != nil {
return err
}
rule := item.request.Rule
var snapshot *filter.Snapshot
for index := range snapshots {
if snapshots[index].Scope.Key() == rule.Scope.Key() {
snapshot = &snapshots[index]
break
}
}
if snapshot == nil {
return fmt.Errorf("%w: missing UFW whitelist scope %s", filter.ErrInventoryUnavailable, rule.Scope.Key())
}
for _, notice := range snapshot.Notices {
if notice.Code == filter.ScopeNoticeManagedScopeInactive || notice.Code == filter.ScopeNoticeManagedScopeMissing {
return filter.ErrProviderUnavailable
}
}
added, err := s.addWhitelistRuleFromSnapshot(ctx, item, *snapshot)
if err != nil {
return fmt.Errorf("add whitelist rule %s %s/%s [%s]: %w", rule.Scope.Family, rule.DestinationPort, rule.Protocol, rule.SourceAddress, err)
}
if !added {
continue
}
if itemIndex+1 < len(prepared) {
if err := ctx.Err(); err != nil {
return err
}
snapshots, err = runtime.ObserveScopes(ctx, filter.ManagedInputScopes(filter.ProviderUFW))
if err != nil {
return err
}
}
}
return nil
}
func (s *FirewallService) addWhitelistRule(ctx context.Context, prepared preparedFirewallRuleCreate) error {
firewallRuleMutationMu.Lock()
defer firewallRuleMutationMu.Unlock()
@@ -770,38 +820,85 @@ func (s *FirewallService) addWhitelistRule(ctx context.Context, prepared prepare
if err != nil {
return err
}
_, err = s.addWhitelistRuleFromSnapshot(ctx, prepared, snapshot)
return err
}
func (s *FirewallService) addWhitelistRuleFromSnapshot(ctx context.Context, prepared preparedFirewallRuleCreate, snapshot filter.Snapshot) (bool, error) {
rule, runtime := prepared.request.Rule, prepared.runtime
for _, observed := range snapshot.Rules {
if observed.ParseStatus == filter.ParseStatusSupported {
if same, err := filter.SameRuleContent(rule, observed.Rule); err == nil && same {
return nil
return false, nil
}
}
}
if err := filter.CheckObservedRuleCollisions(snapshot, rule, nil); err != nil {
return err
return false, err
}
if rule.Scope.Chain == filter.BasicBeforeChain {
position := int64(1)
rule.OrderIndex = &position
rule.UUID = uuid.NewString()
return runtime.ExecuteCreate(ctx, snapshot, []filter.DesiredChange{{Operation: filter.ChangeCreate, After: &rule}})
return true, runtime.ExecuteCreate(ctx, snapshot, []filter.DesiredChange{{Operation: filter.ChangeCreate, After: &rule}})
}
stored, err := s.rules.List(ctx)
if err != nil {
return err
return false, err
}
model.SortFirewallRules(stored, rule.Scope.Provider)
markers := make([]string, 0, len(stored))
var existing *filter.DesiredRule
var firstSequence *int64
for _, record := range stored {
compiled, err := s.compileStoredFirewallRules(ctx, record, rule.Scope.Provider)
if err != nil {
continue
if isFirewallPolicyIncompatible(err) {
continue
}
return false, err
}
for _, candidate := range compiled {
if candidate.Rule.Scope.Key() == rule.Scope.Key() && candidate.Marker != "" {
markers = append(markers, candidate.Marker)
if firstSequence == nil && record.Sequence != nil {
firstSequence = record.Sequence
}
}
if err := filter.CheckRuleCollision(rule, candidate.Rule); errors.Is(err, filter.ErrRuleOperation) {
return runtime.ExecuteCreate(ctx, snapshot, []filter.DesiredChange{{Operation: filter.ChangeCreate, After: &candidate.Rule}})
copy := candidate
existing = &copy
} else if err != nil {
return err
return false, err
}
}
}
return s.applyCreateRules(ctx, runtime, snapshot, stored, []preparedFirewallRuleCreate{prepared})[0]
if existing != nil {
existing.Rule.OrderIndex = firewallsync.InsertionPosition(snapshot, markers, existing.Marker)
return true, runtime.ExecuteCreate(ctx, snapshot, []filter.DesiredChange{{
Operation: filter.ChangeCreate, After: &existing.Rule,
Append: rule.Scope.Provider == filter.ProviderUFW && existing.Rule.OrderIndex == nil,
}})
}
if rule.Scope.Provider != filter.ProviderFirewalld {
position := int64(1)
rule.OrderIndex = &position
record, err := firewallRuleModelForCreate(rule, prepared.request, constant.FirewallRuleOriginCreated)
if err != nil {
return false, err
}
sequence := model.FirewallRuleSequenceStep
if firstSequence != nil {
sequence = *firstSequence - model.FirewallRuleSequenceStep
}
record.UUID, record.Sequence = uuid.NewString(), &sequence
rule.UUID = record.UUID
if err := runtime.ExecuteCreate(ctx, snapshot, []filter.DesiredChange{{Operation: filter.ChangeCreate, After: &rule}}); err != nil {
return false, firewallCreateExecutionError(err)
}
return true, s.saveFirewallRule(ctx, &record)
}
return true, s.applyCreateRules(ctx, runtime, snapshot, stored, []preparedFirewallRuleCreate{prepared})[0]
}
type forwardingRuleSyncCandidate struct {
@@ -813,7 +910,7 @@ func (s *ForwardingService) loadRuleSyncCandidates(
ctx context.Context,
targetProvider filter.Provider,
) (*forwarding.Manager, []forwardingRuleSyncCandidate, []forwarding.Rule, bool, error) {
target, err := s.manager()
target, err := s.managerFactory()
if err != nil {
return nil, nil, nil, false, err
}

View File

@@ -75,7 +75,7 @@ func (s *ForwardingService) LoadBaseInfo() (dto.FirewallSubsystemStatus, error)
baseInfo := dto.FirewallSubsystemStatus{
Version: "-", Name: forwardingDisplayName(selected), Backend: selected, SyncError: lastForwardingSyncError(),
}
manager, err := s.manager()
manager, err := s.managerFactory()
if err != nil {
if errors.Is(err, errForwardingBackendUnavailable) {
baseInfo.Reason = constant.FirewallBackendNotInstalled
@@ -124,7 +124,7 @@ func (s *ForwardingService) SearchRules(request dto.ForwardRuleSearch) (int64, [
if err != nil {
return 0, nil, err
}
manager, err := s.manager()
manager, err := s.managerFactory()
if err != nil {
return 0, nil, err
}
@@ -244,7 +244,7 @@ func (s *ForwardingService) operateRules(ctx context.Context, request dto.Forwar
func (s *ForwardingService) Enable() error {
forwardingMutationMu.Lock()
defer forwardingMutationMu.Unlock()
manager, err := s.manager()
manager, err := s.managerFactory()
if err != nil {
recordForwardingSyncError(err)
return err
@@ -283,7 +283,7 @@ func (s *ForwardingService) QueueInitialization(
forwardingMutationMu.Lock()
defer forwardingMutationMu.Unlock()
var err error
manager, err = s.manager()
manager, err = s.managerFactory()
if err != nil {
recordForwardingSyncError(err)
return err
@@ -329,7 +329,7 @@ func (s *ForwardingService) Restore(ctx context.Context) error {
}
return err
}
manager, err := s.manager()
manager, err := s.managerFactory()
if err != nil {
recordForwardingSyncError(err)
return err
@@ -349,7 +349,7 @@ func (s *ForwardingService) Restore(ctx context.Context) error {
}
func (s *ForwardingService) reconcile(rules []forwarding.Rule) error {
manager, err := s.manager()
manager, err := s.managerFactory()
if err != nil {
return err
}
@@ -548,10 +548,6 @@ func forwardingOperationsOnlyRemove(operations []dto.ForwardRuleOperation) bool
return true
}
func (s *ForwardingService) manager() (*forwarding.Manager, error) {
return s.managerFactory()
}
func newForwardingManager() (*forwarding.Manager, error) {
return newForwardingManagerFor(configuredForwardingBackend())
}

View File

@@ -336,8 +336,12 @@ func compileChange(snapshot filter.Snapshot, change filter.DesiredChange) (filte
return filter.NativeRulePlan{}, fmt.Errorf("%w: create target is out of range", filter.ErrInvalidRule)
}
command := insertCommand(position, normalized, marker)
if change.Append || position == maximumObservedPosition(snapshot)+1 {
if !change.Append && normalized.OrderIndex != nil && position == 1 {
command = filter.NativeCommand{Executable: "ufw", Args: append([]string{"prepend"}, compileRuleArgs(normalized, marker)...)}
} else if change.Append || position == maximumObservedPosition(snapshot)+1 {
command = commentCommand(normalized, marker)
}
if command.Args[0] != "insert" {
plan.Expected.Locator.NativeID = ""
plan.Expected.Locator.Position = nil
}
@@ -740,7 +744,7 @@ func validateCommand(command filter.NativeCommand) error {
return nil
}
switch first {
case "insert", "allow", "deny", "reject":
case "insert", "prepend", "allow", "deny", "reject":
return nil
default:
return fmt.Errorf("%w: unsupported ufw command %q", filter.ErrInvalidRule, first)

View File

@@ -42,6 +42,10 @@ func RuleMatchesPortWhitelist(rule FirewallRule, ports []PortWhitelist) bool {
if err != nil || rule.Action != ActionAccept || rule.SourcePort != "" || rule.DestinationAddress != "" || rule.Interface != "" || len(rule.ConnectionStates) != 0 {
return false
}
if rule.Scope.Provider == ProviderFirewalld && (rule.NativeKind == NativeKindZonePort ||
(rule.NativeKind == NativeKindRule && rule.Scope.Family == FamilyInet && rule.Priority == nil)) {
return false
}
families := []Family{rule.Scope.Family}
if rule.Scope.Family == FamilyInet {
families = []Family{FamilyIPv4, FamilyIPv6}

View File

@@ -61,7 +61,7 @@ func (m *Manager) Operate(operation firewall.BaseOperation) error {
func (m *Manager) enableBase(prepare bool) error {
if prepare {
if err := ensureBaseChains(); err != nil {
if err := ensureBaseChainsFamily(false); err != nil {
return err
}
if err := m.initPreRules(); err != nil {
@@ -71,7 +71,7 @@ func (m *Manager) enableBase(prepare bool) error {
return err
}
}
if err := bindBaseChains(); err != nil {
if err := setBaseChainBindings(false, true); err != nil {
return err
}
if prepare {
@@ -97,10 +97,6 @@ func (m *Manager) disableBase() error {
return m.updateSetting("IptablesStatus", constant.StatusDisable)
}
func ensureBaseChains() error {
return ensureBaseChainsFamily(false)
}
func ensureBaseChainsFamily(ipv6 bool) error {
commands, err := lifecycle.ResolveIptablesCommands()
if err != nil {
@@ -169,10 +165,6 @@ func cleanupBaseChains(ipv6 bool) error {
return nil
}
func bindBaseChains() error {
return setBaseChainBindings(false, true)
}
func setBaseChainBindings(ipv6, bind bool) error {
commands, err := lifecycle.ResolveIptablesCommands()
if err != nil {
@@ -250,7 +242,7 @@ func RestoreBaseChains(requiredPorts []firewall.PortWhitelist) error {
if err != nil {
return err
}
if err := ensureBaseChains(); err != nil {
if err := ensureBaseChainsFamily(false); err != nil {
return err
}
input, err := buildBaseChainsRestoreScript(global.Dir.FirewallDir, false, requiredPorts...)
@@ -320,6 +312,9 @@ func buildBaseChainsRestoreScript(firewallDir string, ipv6 bool, requiredPorts .
}
for _, rule := range defaults {
if !containsIptablesRule(script.String(), rule) {
if strings.HasPrefix(rule, "-A "+BasicBeforeChain+" ") && strings.Contains(rule, " --dport ") {
rule = strings.Replace(rule, "-A "+BasicBeforeChain+" ", "-I "+BasicBeforeChain+" 1 ", 1)
}
script.WriteString(rule + "\n")
}
}
@@ -442,7 +437,7 @@ func buildRequiredPortsRestoreScript(
for _, rule := range desired {
line := iptablesSystemPortRuleLine(rule)
if rule.Family == family && !containsIptablesRule(beforeRaw, line) {
commands = append(commands, line)
commands = append(commands, strings.Replace(line, "-A "+BasicBeforeChain+" ", "-I "+BasicBeforeChain+" 1 ", 1))
beforeRaw += "\n" + line
}
}

View File

@@ -7,6 +7,8 @@ import (
"net/netip"
"os"
"path/filepath"
"slices"
"strconv"
"strings"
"github.com/1Panel-dev/1Panel/agent/constant"
@@ -113,7 +115,7 @@ func (m *Manager) ensureBaseChains() error {
func requiredPortCommand(tableFamily string, rule firewall.SystemPort) []string {
command := []string{
"add", "rule", tableFamily, TableName, BasicBeforeChain,
"insert", "rule", tableFamily, TableName, BasicBeforeChain,
}
if rule.SourceAddress != "" {
command = append(command, tableFamily, "saddr", rule.SourceAddress)
@@ -174,6 +176,29 @@ func containsRequiredPortRule(output, expression string) bool {
line = strings.NewReplacer("{", "", "}", "", ", ", ",", " ,", ",").Replace(line)
fields := strings.Fields(line)
for index, field := range fields {
if index >= 2 && fields[index-2] == "ct" && fields[index-1] == "state" {
states := strings.Split(field, ",")
for i, state := range states {
value, err := strconv.ParseUint(state, 0, 64)
if err != nil {
continue
}
switch value {
case 1:
states[i] = "invalid"
case 2:
states[i] = "established"
case 4:
states[i] = "related"
case 8:
states[i] = "new"
case 64:
states[i] = "untracked"
}
}
slices.Sort(states)
fields[index] = strings.Join(states, ",")
}
if prefix, err := netip.ParsePrefix(field); err == nil {
prefix = prefix.Masked()
fields[index] = prefix.String()

View File

@@ -871,12 +871,8 @@ const loadAllInventoryItems = async () => {
return response.data.items || [];
};
const resetPagination = () => {
paginationConfig.currentPage = 1;
};
const searchWithReset = () => {
resetPagination();
paginationConfig.currentPage = 1;
return searchPage();
};

View File

@@ -246,7 +246,6 @@ const familyRetrying = ref(false);
const taskLogRef = ref();
const lifecycleSubmitting = ref(false);
const lifecycleBusy = computed(() => lifecycleSubmitting.value || !!baseInfo.value.lifecycleTaskID);
let lifecycleTimer: ReturnType<typeof setTimeout>;
let disposed = false;
let baseInfoRequestID = 0;
const backendName = computed(() => baseInfo.value.backend || baseInfo.value.name);
@@ -306,7 +305,7 @@ const initActionHelper = computed(() => {
});
const acceptParams = (): void => {
loadBaseInfo(true);
loadBaseInfo();
loadDocker();
};
const emit = defineEmits([
@@ -319,14 +318,12 @@ const emit = defineEmits([
'update:version',
]);
const loadBaseInfo = async (search: boolean) => {
const loadBaseInfo = async () => {
if (disposed) return;
const requestID = ++baseInfoRequestID;
clearTimeout(lifecycleTimer);
try {
const res = await (props.currentTab === 'forward' ? loadForwardBaseInfo() : loadFireBaseInfo(props.currentTab));
if (disposed || requestID !== baseInfoRequestID) return;
const lifecycleCompleted = Boolean(baseInfo.value.lifecycleTaskID) && !res.data.lifecycleTaskID;
baseInfo.value = {
...res.data,
ipv4: res.data.ipv4 || { available: true, initialized: res.data.isInit, bound: res.data.isBind },
@@ -347,7 +344,7 @@ const loadBaseInfo = async (search: boolean) => {
emit('update:is-bind', isDirectManaged.value ? anyFamilyBound.value : baseInfo.value.isBind);
emit('update:version', baseInfo.value.version);
if ((search || lifecycleCompleted) && !baseInfo.value.lifecycleTaskID) {
if (!baseInfo.value.lifecycleTaskID) {
await nextTick();
emit('search');
} else {
@@ -359,10 +356,6 @@ const loadBaseInfo = async (search: boolean) => {
emit('update:is-init', false);
emit('update:name', '-');
emit('update:version', '');
} finally {
if (!disposed && requestID === baseInfoRequestID && baseInfo.value.lifecycleTaskID) {
lifecycleTimer = setTimeout(() => loadBaseInfo(false), 3000);
}
}
};
@@ -413,11 +406,11 @@ const onInit = async () => {
}
}
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
await loadBaseInfo(true);
await loadBaseInfo();
};
const handleInitializationTaskClose = () => {
loadBaseInfo(true);
loadBaseInfo();
};
const onBind = async () => {
@@ -431,7 +424,7 @@ const onBind = async () => {
}
await operateFilterChain('1PANEL_BASIC', 'bind-base');
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
await loadBaseInfo(true);
await loadBaseInfo();
};
const onRetryFamilyIssues = async () => {
@@ -448,7 +441,7 @@ const onRetryFamilyIssues = async () => {
await operateFilterChain('1PANEL_BASIC', 'bind-base');
}
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
await loadBaseInfo(true);
await loadBaseInfo();
} finally {
familyRetrying.value = false;
}
@@ -465,7 +458,7 @@ const onUnBind = async () => {
}
await operateFilterChain('1PANEL_BASIC', 'unbind-base');
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
await loadBaseInfo(true);
await loadBaseInfo();
};
const onOperate = async (op: string) => {
@@ -499,13 +492,12 @@ const submitLifecycleOperation = async (restartDocker: boolean) => {
baseInfo.value.lifecycleTaskID = result.taskID;
emit('update:loading', false);
openLifecycleTask();
await loadBaseInfo(false);
return;
}
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
await loadBaseInfo(true);
await loadBaseInfo();
} catch {
if (!disposed) await loadBaseInfo(true);
if (!disposed) await loadBaseInfo();
} finally {
lifecycleSubmitting.value = false;
}
@@ -513,7 +505,6 @@ const submitLifecycleOperation = async (restartDocker: boolean) => {
onBeforeUnmount(() => {
disposed = true;
clearTimeout(lifecycleTimer);
});
defineExpose({