Skip to content

Integration with OS Running on the Firewall Machine

FirewallFabrik generates firewall scripts for iptables and nftables tailored for integration with modern Linux systems running systemd. The generated script supports command-line arguments start, stop, status, block, reload, interfaces, and test_interfaces. The script can be integrated with systemd using a custom service unit.

The iptables script is assembled from configlets located in resources/configlets/linux24/ (starting from script_skeleton). The nftables script is rendered from the Jinja2 template resources/templates/nftables/script.sh.j2. You can modify both following the instructions in 13 - Configlets.

Activating the Firewall Policy at Boot

The recommended way to activate the FirewallFabrik-generated policy at boot is to create a systemd service unit. This applies to both iptables and nftables firewalls.

Creating a systemd Service Unit

Create a service unit file that runs the generated firewall script at boot. The default output file name is fwf.sh, stored in /etc/. You can change both in the firewall settings dialog (Compiler > Output file name and Installer > Directory on firewall).

sudo $EDITOR /etc/systemd/system/firewallfabrik.service

Add the following content (adjust the script path if you changed the defaults):

[Unit]
Description=FirewallFabrik firewall policy
DefaultDependencies=no
Before=network-pre.target
Wants=network-pre.target
After=local-fs.target

[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/etc/fwf.sh start
ExecStop=/etc/fwf.sh stop
ExecReload=/etc/fwf.sh reload

[Install]
WantedBy=multi-user.target

Enable and start the service:

sudo systemctl daemon-reload
sudo systemctl enable firewallfabrik.service
sudo systemctl start firewallfabrik.service

Disabling Conflicting Services

When using FirewallFabrik to manage the firewall, disable any distribution-provided firewall services to avoid conflicts:

# RHEL / Fedora / CentOS -- disable firewalld
sudo systemctl disable --now firewalld

# Debian / Ubuntu -- disable ufw
sudo systemctl disable --now ufw

# If nftables.service is active and you use FirewallFabrik for nftables
sudo systemctl disable --now nftables

[!NOTE] The script generated by FirewallFabrik does more than just set iptables or nftables rules. It also adds virtual IP addresses to the interfaces of the firewall, configures kernel parameters, and can check whether interfaces are present and up. Distribution-provided iptables-save/iptables-restore services only manage rules; other tasks performed by the FirewallFabrik-generated script will not be done upon reboot if you rely on those services.

Coexistence with Docker, CrowdSec, fail2ban and Other Tools

By default, the generated firewall script flushes all rules before loading the new policy. This guarantees a clean, deterministic firewall state — FirewallFabrik controls the entire host firewall.

On servers running Docker, CrowdSec, fail2ban or similar tools, this full flush destroys the rules those tools manage. To avoid this, disable the "Flush entire ruleset" option in the firewall settings dialog. FirewallFabrik then only manages its own tables and chains, leaving everything else untouched.

Both platforms support this coexistence mode:

  • nftables: FirewallFabrik creates named tables (e.g. fwf_filter, fwf_nat). Only these tables are deleted and recreated. Other tools' tables (e.g. Docker's table ip docker, CrowdSec's table inet crowdsec) remain untouched.
  • iptables: FirewallFabrik creates prefixed chains (e.g. fwf_INPUT, fwf_FORWARD, fwf_OUTPUT) and inserts jump rules into the built-in chains. Only the prefixed chains are flushed on reload.

The table/chain prefix is configurable via "Table name" in the firewall settings dialog (default: fwf).

How nftables Coexistence Works

FirewallFabrik creates two named nftables tables (e.g. fwf_filter and fwf_nat). When "Flush entire ruleset" is disabled, the generated script only deletes and recreates these two tables — other tools' tables are never touched. The nft rules file uses an atomic create-then-delete pattern:

table ip fwf_filter {}
delete table ip fwf_filter

table ip fwf_filter {
    chain input { type filter hook input priority filter; policy drop; ... }
    chain forward { ... }
    chain output { ... }
}

This is safe because nft -f processes the entire file atomically.

How iptables Coexistence Works

Since iptables has no table namespace mechanism, FirewallFabrik uses prefixed user chains. When "Flush entire ruleset" is disabled, the generated script:

  1. Removes any existing fwf_* chains and their jump rules from the built-in chains (reset_fwf_chains).
  2. Creates new prefixed chains (fwf_INPUT, fwf_FORWARD, fwf_OUTPUT) and inserts jump rules at position 1 in the built-in chains (setup_fwf_jumps).
  3. All firewall rules target the prefixed chains instead of the built-in chains.
Chain INPUT (policy DROP)
 fwf_INPUT    all  --  0.0.0.0/0  0.0.0.0/0    ← FWF jump rule
 CROWDSEC_CHAIN ...                               ← other tool
 f2b-sshd ...                                     ← other tool

Chain fwf_INPUT (1 references)
 ... FirewallFabrik rules ...

On stop, only the fwf_* chains and their jump rules are removed. The built-in chain policies remain at DROP, and other tools' chains are untouched.

[!WARNING] Disabling "Flush entire ruleset" means FirewallFabrik no longer controls the entire firewall. Other tools may add rules that bypass your policy. Only disable this if you need coexistence with other tools on the same machine.

Controlling Rule Evaluation Order (iptables)

When multiple tools manage iptables chains, the evaluation order depends on the systemd startup order. Each tool inserts its jump rules at position 1 (top of the built-in chain) using iptables -I. Tools that start later end up at the top, so their rules are evaluated first.

For a typical setup with FirewallFabrik, Docker, CrowdSec and fail2ban, configure the systemd unit to start FirewallFabrik before the other tools:

[Unit]
Description=FirewallFabrik firewall policy
DefaultDependencies=no
Wants=network-pre.target
After=local-fs.target

# Start before network and before other tools so the machine
# is protected as early as possible. Tools that start later
# insert their rules at position 1 (evaluated first).
Before=network-pre.target docker.service crowdsec.service fail2ban.service

[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/etc/fwf.sh start
ExecStop=/etc/fwf.sh stop
ExecReload=/etc/fwf.sh reload

[Install]
WantedBy=multi-user.target

This produces the following evaluation order in the INPUT chain:

Position 1:  -j f2b-sshd         (fail2ban — started last)
Position 2:  -j CROWDSEC_CHAIN   (CrowdSec)
Position 3:  -j fwf_INPUT        (FirewallFabrik — started first)
Built-in policy: DROP

Packets flow top-to-bottom. CrowdSec and fail2ban see each packet before FirewallFabrik's rules. If neither blocks the packet, FirewallFabrik's policy decides. If none of the chains match, the built-in DROP policy applies.

This is typically the desired order: CrowdSec blocklists and fail2ban bans are checked first, then the firewall policy applies.

[!NOTE] nftables uses table priorities instead of insertion order, so the systemd ordering is less critical there. FirewallFabrik's named tables use the default filter priority.

Restarting the Policy when an Interface Address Changes

The firewall policy script generated by FirewallFabrik determines the IP addresses of all dynamic interfaces and assigns them to variables, which it then uses in the policy rules. If an interface's address changes after the policy has been loaded, the firewall script needs to be restarted.

On modern systems using NetworkManager, you can use a dispatcher script:

sudo $EDITOR /etc/NetworkManager/dispatcher.d/99-firewallfabrik

Add the following content:

#!/bin/bash
# Restart FirewallFabrik policy when an interface gets a new address
if [ "$2" = "up" ] || [ "$2" = "dhcp4-change" ] || [ "$2" = "dhcp6-change" ]; then
    systemctl reload firewallfabrik.service 2>/dev/null || true
fi

Make the script executable:

sudo chmod +x /etc/NetworkManager/dispatcher.d/99-firewallfabrik

For systems using systemd-networkd instead of NetworkManager, create a networkd-dispatcher script in /etc/networkd-dispatcher/routable.d/ with similar content.

For PPP connections (e.g. PPPoE), the /etc/ppp/ip-up script can be used to restart the firewall:

#!/bin/bash
systemctl reload firewallfabrik.service

[!NOTE] Replace firewallfabrik.service with the actual service name if you chose a different name for the unit file.

Deploying with Configuration Management

In modern infrastructure, firewall policies are often deployed as part of an automated workflow rather than manually. The generated .fw script fits naturally into configuration management tools.

Ansible

An Ansible playbook to deploy a FirewallFabrik-generated policy might look like this (following the Linuxfabrik Ansible Development Guidelines):

- name: 'Playbook linuxfabrik.lfops.firewallfabrik'
  hosts: 'firewalls'
  become: true

  tasks:

    - name: 'Deploy firewall script'
      ansible.builtin.template:
        src: 'output/{{ inventory_hostname }}.fw'
        dest: '/etc/fwf.sh'
        owner: 'root'
        group: 'root'
        mode: 0o0700
        backup: true
      notify: 'firewallfabrik: activate firewall'

    - name: 'Deploy systemd service'
      ansible.builtin.template:
        src: 'firewallfabrik.service.j2'
        dest: '/etc/systemd/system/firewallfabrik.service'
        owner: 'root'
        group: 'root'
        mode: 0o0644
        backup: true
      notify:
        - 'firewallfabrik: reload systemd'
        - 'firewallfabrik: activate firewall'

    - name: 'Enable firewall service'
      ansible.builtin.service:
        name: 'firewallfabrik'
        enabled: true

  handlers:

    - name: 'firewallfabrik: reload systemd'
      ansible.builtin.systemd:
        daemon_reload: true

    - name: 'firewallfabrik: activate firewall'
      ansible.builtin.service:
        name: 'firewallfabrik'
        state: 'reloaded'

This approach ensures that the firewall script is deployed consistently across all machines and activated in a controlled manner.

CI/CD Integration

For teams using a CI/CD pipeline, the typical workflow is:

  1. Edit the firewall policy — either in the FirewallFabrik GUI or by modifying the .fwf YAML file directly (e.g. via scripts or other automation tools to add rules, objects and attributes).
  2. Save and compile — either via the GUI (Rules > Compile) or on the command line (fwf compile myfile.fwf).
  3. Commit the .fwf source file and the generated .fw script(s) to Git.
  4. A CI/CD pipeline (GitLab CI, GitHub Actions, Jenkins, etc.) picks up the change and runs the deployment playbook or script.

Example GitLab CI stage:

deploy-firewall:
  stage: deploy
  script:
    - ansible-playbook -i inventory deploy-firewall.yml
  only:
    changes:
      - output/*.fw
  when: manual

The when: manual gate ensures that firewall deployments are always explicitly triggered by an operator.

See 10 - Compiling and Installing a Policy for details on compiling policies.