Skip to content

refactor: firewall provider handling and rule synchronization - #13430

Open
HynoR wants to merge 6 commits into
1Panel-dev:dev-v2from
HynoR:pr/fw02
Open

refactor: firewall provider handling and rule synchronization#13430
HynoR wants to merge 6 commits into
1Panel-dev:dev-v2from
HynoR:pr/fw02

Conversation

@HynoR

@HynoR HynoR commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

What this PR does / why we need it?

把防火墙实施拆分和隔离,化adaptor

Summary of your change

Please indicate you've done the following:

  • Made sure tests are passing and test coverage is added if needed.
  • Made sure commit message follow the rule of Conventional Commits specification.
  • Considered the docs impact and opened a new docs issue or PR with docs changes if needed.

HynoR added 6 commits July 30, 2026 16:24
Three callers only need the provider name: the boot replay, the historical
firewall_type migration and the forwarding adapter factory. They each built a
full filter client and read Name() off it.

DetectProvider reports the name using the same selection order NewFirewallClient
uses (firewalld+ufw conflict, then firewalld, ufw, iptables) without
constructing anything, and NewFirewallClient is now that detection plus a
name-to-client switch, so an unsupported provider is rejected in one place.
One logical rule from the API can mean several native operations, and how it
splits depends entirely on the provider: ufw applies a port list or range with
":" but records it with "-", drops the protocol so tcp/udp stays a single rule
and shows an empty source as "Anywhere"; firewalld and iptables multiply
protocols by ports by sources; only iptables owns a chain. Until now the
service switched on client.Name() to decide all of that.

FilterClient gains ExpandPortRule/ApplyPortUnit and ExpandAddressRule/
ApplyAddressUnit. Expansion is a pure function that runs no command and returns
the operations in order, each carrying what to apply, what to record and which
chain owns it - the applied and the recorded shape are not always the same. The
service now runs one loop over those units for every provider and no longer
knows what a rich rule is, so RichRules leaves the interface. The command
argument construction inside each client is factored into pure builders so the
argv can be asserted without running anything.

Address expansion is the same for all three providers apart from the chain, so
it stays a single shared helper.
The port whitelist was the last provider switch left in the service. What each
provider does with it is genuinely different: ufw and firewalld only act while
the service is running and add or remove ports through the same native port
command as user rules, while iptables acts only once its chains are
initialized and writes required ports into 1PANEL_BASIC_BEFORE and configured
ports into 1PANEL_BASIC, then persists both chains.

FilterClient gains AddPortWhiteList, which re-adds everything after the
provider was started, and SyncPortWhiteList, which applies the difference
against the previously configured list. The service resolves the whitelist
state - configured entries, the required 1Panel and SSH ports, and the
previous configured entries - and hands that struct down; deciding what to do
with it belongs to the provider. The iptables chain writing moves next to the
iptables client, and the advanced init path calls it directly with persistence
turned off because it persists the chains itself once initialization finishes.

The whitelist state is now resolved before the provider decides whether it can
act, so on a stopped ufw/firewalld or an uninitialized iptables an unreadable
1Panel service port surfaces as an error instead of silently doing nothing.
Two layers, so that a future change to one provider cannot quietly change
another.

Expansion layer: a table of rules - single port, range, colon range, comma
list, tcp/udp, empty/single/CIDR/multiple/trailing-comma/Anywhere sources,
drop, explicit chain - run through all three real ExpandPortRule and
ExpandAddressRule implementations. Expansion runs no command, so the real
clients are used directly. The expected output records what is applied, what
is recorded, which chain owns it and whether the unit takes the rich rule
path, which is the whole per-provider decision in one place.

Flow layer: the service loop driven by the real expansion with a recorded
apply step and a recorded firewall record repository, so the order of native
operations and the interleaving of record writes are both pinned.

Alongside these, the command builders extracted in the previous commits get
argv assertions, including that no ufw or firewalld command ever mentions a
1PANEL chain.

One recorded difference against dev-v2: ufw returned before Reload for port
rules while the shared flow always reloads. Ufw.Reload is a no-op, so the
trailing reload changes nothing on a real host; the flow test comment says so.
Removed the ForwardRuleSearch struct and replaced it with a more generic RuleSearch in the forwarding service. Updated related search functions and tests to accommodate this change, streamlining the search process for forwarding rules. Additionally, refactored the handling of protocol loading in iptables and improved the readability of the code by renaming functions for clarity.
@HynoR
HynoR marked this pull request as ready for review August 3, 2026 05:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant