mirror of
https://hubproxy.babadafafafafa.cn/https://github.com/1Panel-dev/1Panel.git
synced 2026-09-20 08:03:55 +08:00
fix(firewall): preserve whitelist priority and rule ordering (#13845)
This commit is contained in:
@@ -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,13 +2647,12 @@ 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
|
||||
@@ -2657,13 +2663,8 @@ func (s *FirewallService) sequenceForFirewallRulePosition(
|
||||
case *next.Sequence-*previous.Sequence > 1:
|
||||
return *previous.Sequence + (*next.Sequence-*previous.Sequence)/2, nil
|
||||
default:
|
||||
needsRebalance = true
|
||||
}
|
||||
}
|
||||
if needsRebalance {
|
||||
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...)
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
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 = ©
|
||||
} 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
|
||||
}
|
||||
|
||||
@@ -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())
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -871,12 +871,8 @@ const loadAllInventoryItems = async () => {
|
||||
return response.data.items || [];
|
||||
};
|
||||
|
||||
const resetPagination = () => {
|
||||
paginationConfig.currentPage = 1;
|
||||
};
|
||||
|
||||
const searchWithReset = () => {
|
||||
resetPagination();
|
||||
paginationConfig.currentPage = 1;
|
||||
return searchPage();
|
||||
};
|
||||
|
||||
|
||||
@@ -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({
|
||||
|
||||
Reference in New Issue
Block a user