Skip to content

added firewall-reload command #1301

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged

Conversation

Rishikpulhani
Copy link
Contributor

@Rishikpulhani Rishikpulhani commented Aug 7, 2025

On systems using nftables directly without firewalld, restarting the nftables.service would flush all of Netavark's rules, breaking container networking.

This introduces a new "oneshot" command, netavark firewall-reload, which reads the container network state from /run/containers/netavark/ and re-applies all necessary firewall rules.

To automate this, a new systemd service, netavark-nftables-reload.service, is added. This service is procedurally linked to nftables.service and triggers the reload command automatically whenever the main nftables service is started or reloaded.

Fixes: #1258

Summary by Sourcery

Add a oneshot firewall-reload mechanism for nftables-only systems to restore container networking after nftables service restarts

New Features:

  • Introduce a ‘firewall-reload’ subcommand to reapply firewall rules from Netavark’s container network state
  • Add netavark-nftables-reload.service systemd unit to trigger the reload command on nftables.service start or reload

Enhancements:

  • Update Makefile and RPM spec to include and install the new nftables reload service unit

Copy link

sourcery-ai bot commented Aug 7, 2025

Reviewer's Guide

This PR introduces a new firewall-reload subcommand and corresponding systemd service to reapply nftables-based firewall rules by reading container network state, and integrates it into the build, packaging, and runtime hooks.

Sequence diagram for systemd-triggered nftables firewall reload

sequenceDiagram
    participant systemd as systemd
    participant nftables as nftables.service
    participant reload as netavark-nftables-reload.service
    participant netavark as netavark (firewall-reload)

    systemd->>nftables: Start or reload nftables.service
    nftables-->>reload: Triggers netavark-nftables-reload.service
    reload->>netavark: Execute 'netavark firewall-reload'
    netavark->>netavark: Read /run/containers/netavark/
    netavark->>netavark: Re-apply firewall rules for all networks
    netavark-->>reload: Exit
    reload-->>nftables: Complete
    nftables-->>systemd: Complete
Loading

Class diagram for new firewall-reload command integration

classDiagram
    class Main {
        +main()
    }
    class SubCommand {
        +FirewallReload
    }
    class firewall_reload {
        +firewall_reload(config_dir: Option<OsString>) NetavarkResult<()>
    }
    class firewalld_reload {
        +listen(config_dir: Option<OsString>) NetavarkResult<()>
    }
    Main --> SubCommand
    SubCommand --> firewall_reload : FirewallReload variant
    Main --> firewall_reload : calls firewall_reload()
    SubCommand --> firewalld_reload : FirewallDReload variant
    Main --> firewalld_reload : calls listen()
Loading

File-Level Changes

Change Details Files
Add new CLI subcommand for firewall reload
  • Declare FirewallReload variant in the SubCommand enum
  • Wire firewall_reload handler into main dispatch
  • Expose firewall_reload module in commands mod
src/main.rs
src/commands/mod.rs
Implement oneshot reload logic
  • Create new firewall_reload.rs with entrypoint and error wrapper
  • Copy and adapt reload_rules_inner from firewalld_reload (remove D-Bus)
  • Invoke read_fw_config and driver methods to restore rules
src/commands/firewall_reload.rs
Integrate service into build and packaging
  • Add nftables-reload unit to Makefile installation list
  • Include service hooks and file listing in RPM spec
  • Add systemd template netavark-nftables-reload.service.in
Makefile
rpm/netavark.spec
contrib/systemd/system/netavark-nftables-reload.service.in

Possibly linked issues


Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @Rishikpulhani - I've reviewed your changes and they look great!

Prompt for AI Agents
Please address the comments from this code review:
## Individual Comments

### Comment 1
<location> `src/commands/firewall_reload.rs:29` </location>
<code_context>
+
+    // Call the reload logic. We pass `None` for the D-Bus connection
+    // because we are on an nftables system and don't need it.
+    reload_rules(config_dir);
+
+    Ok(())
</code_context>

<issue_to_address>
The return value of reload_rules is ignored, which may hide errors.

If systemd relies on exit codes to detect failures, propagate errors from reload_rules to ensure systemd can respond appropriately.
</issue_to_address>

<suggested_fix>
<<<<<<< SEARCH
    // Call the reload logic. We pass `None` for the D-Bus connection
    // because we are on an nftables system and don't need it.
    reload_rules(config_dir);

    Ok(())
}

 // This function is copied directly from firewalld_reload.rs.
 // It's a simple wrapper to handle and log any errors.
 fn reload_rules(config_dir: &Path) {
     if let Err(e) = reload_rules_inner(config_dir) {
         log::error!("failed to reload firewall rules: {e}");
     }
 }
=======
    // Call the reload logic. We pass `None` for the D-Bus connection
    // because we are on an nftables system and don't need it.
    reload_rules(config_dir)?;

    Ok(())
}

// This function is copied directly from firewalld_reload.rs.
// It's a simple wrapper to handle and log any errors.
fn reload_rules(config_dir: &Path) -> Result<(), Error> {
    reload_rules_inner(config_dir).map_err(|e| {
        log::error!("failed to reload firewall rules: {e}");
        e
    })
}
>>>>>>> REPLACE

</suggested_fix>

### Comment 2
<location> `src/commands/firewall_reload.rs:43` </location>
<code_context>
+}
+
+// This is the core logic, also copied directly.
+// We keep the `conn` parameter for compatibility with the firewall driver trait,
+// but it will be `None` and won't be used by the nftables driver.
+fn reload_rules_inner(config_dir: &Path) -> NetavarkResult<()> {
+    // read_fw_config reads all the JSON files from `/run/containers/netavark/`
+    let conf = read_fw_config(config_dir).wrap("read firewall config")?;
</code_context>

<issue_to_address>
The comment about the unused `conn` parameter is outdated.

The function no longer has a `conn` parameter, so the comment should be updated to reflect the current signature.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment on lines 43 to 39
// We keep the `conn` parameter for compatibility with the firewall driver trait,
// but it will be `None` and won't be used by the nftables driver.
fn reload_rules_inner(config_dir: &Path) -> NetavarkResult<()> {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nitpick: The comment about the unused conn parameter is outdated.

The function no longer has a conn parameter, so the comment should be updated to reflect the current signature.

@Rishikpulhani Rishikpulhani force-pushed the fix-nftables-reload branch 2 times, most recently from 7e16902 to 0ec3879 Compare August 8, 2025 08:56
Copy link

Ephemeral COPR build failed. @containers/packit-build please check.

Copy link
Member

@Luap99 Luap99 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you remove the extra merge commit and rebase instead to keep a clean git history please.
Also this is going to need a test under test/
For example in in test/250-bridge-nftables.bats you could do a normal setup like most other test, then run_in_host_netns nft flush ruleset (or how the command is called to clean all rules). Then call netavark firewall-reload and then check that the nft rules exist again.

};

// This is our new main entry point. It's a "oneshot" function.
// It will be called once by systemd, do its job, and then exit.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The fact that it is called by systemd should not matter here, any user can call this.


[Install]
# This tells systemd that this service should be enabled when nftables is enabled.
WantedBy=nftables.service
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh and missing newline here'

Did you test that the unit dependencies and such actually work correctly?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Luap99
Yes, I tested the unit dependencies and they are working correctly.

@Rishikpulhani
Copy link
Contributor Author

Thank you for the feedback, @Luap99. I will make the suggested changes and add the tests shortly.

@Rishikpulhani Rishikpulhani force-pushed the fix-nftables-reload branch 5 times, most recently from 61caae2 to 4c78798 Compare August 15, 2025 20:28
@Rishikpulhani Rishikpulhani requested a review from Luap99 August 15, 2025 20:49
Comment on lines 995 to 997
# check that the firewall config files exist
run_helper ls -A "$NETAVARK_TMPDIR/config/firewall/networks/"
assert "$output" != "" "network config file should exist"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

a positive match is almost always better, the file name should be based on the id so you should be able to lookup the full file path and ensure it exists instead of allowing for any path.

Comment on lines 45 to 51
// Get the appropriate firewall driver (it will auto-detect nftables).
let fw_driver = get_supported_firewall_driver(Some(conf.driver))?;

// Loop through each network configuration and restore its rules.
for net in conf.net_confs {
fw_driver.setup_network(net, &None)?;
}
// Loop through each container's port mappings and restore them.
for port in &conf.port_confs {
fw_driver.setup_port_forward(port.into(), &None)?;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it doesn't auto detect nftables so the comment is misleading, nftables is one driver it might use, but the code is generic so it will work for any driver.

And passing None is not right, we should call let conn = Connection::system().ok() and then pass this, that is needed to ensure it correctly adds the rule to the trusted zone in firewalld.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ahh actually while making this PR I had in mind the case where this code would only be used when firewalld is absent from the system, so I thought it would be fine to just pass None directly.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is most likely correct for the majority of users but it cost us nothing to pass the real connection in case it is needed so doing that is fine.

@Rishikpulhani Rishikpulhani force-pushed the fix-nftables-reload branch 2 times, most recently from 05ecade to 5ab3192 Compare August 18, 2025 14:13
On systems using nftables directly without firewalld, restarting the
nftables.service would flush all of Netavark's rules, breaking container
networking.

This introduces a new "oneshot" command, `netavark firewall-reload`,
which reads the container network state from /run/containers/netavark/
and re-applies all necessary firewall rules.

To automate this, a new systemd service, `netavark-nftables-reload.service`,
is added. This service is procedurally linked to `nftables.service` and
triggers the reload command automatically whenever the main nftables
service is started or reloaded.

Fixes: containers#1258

Signed-off-by: Rishikpulhani <[email protected]>
@Rishikpulhani
Copy link
Contributor Author

@Luap99 I’ve made the requested changes. Could you please review it again ?

@Rishikpulhani Rishikpulhani requested a review from Luap99 August 18, 2025 14:41
Copy link
Member

@Luap99 Luap99 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, @mheon PTAL

Copy link
Contributor

openshift-ci bot commented Aug 18, 2025

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: Luap99, Rishikpulhani, sourcery-ai[bot]

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@mheon
Copy link
Member

mheon commented Aug 18, 2025

/lgtm

@openshift-ci openshift-ci bot added the lgtm label Aug 18, 2025
@openshift-merge-bot openshift-merge-bot bot merged commit e012a77 into containers:main Aug 18, 2025
28 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Handle nftables reload/restart
3 participants