# fail2ban with firewalld

> fail2ban with firewalld: rich rules vs ipset vs multiport, zones, the fail2ban-firewalld package on Fedora and RHEL, listing bans, and firewalld is not running.
>
> Source: https://fail2ban.dev/guides/firewalld/ · Updated 2026-10-25 · Checked against fail2ban 1.1.1
> fail2ban.dev is an independent guide, not the official fail2ban project.

To make fail2ban ban through firewalld, set `banaction = firewallcmd-rich-rules` and `banaction_allports = firewallcmd-rich-rules` in `/etc/fail2ban/jail.local`. On Fedora and on RHEL, Rocky or AlmaLinux with EPEL, the `fail2ban-firewalld` package already sets this in `/etc/fail2ban/jail.d/00-firewalld.conf`. Each ban becomes a rich rule in the `public` zone, visible with `sudo firewall-cmd --list-rich-rules`.

```ini
[DEFAULT]
banaction          = firewallcmd-rich-rules
banaction_allports = firewallcmd-rich-rules
```

On Fedora and EPEL (Rocky, AlmaLinux, RHEL 8, 9 and 10), fail2ban is split into sub-packages. The `fail2ban` meta package pulls in `fail2ban-server`, `fail2ban-sendmail` and **`fail2ban-firewalld`**, which installs:

```ini
[DEFAULT]
banaction = firewallcmd-rich-rules
banaction_allports = firewallcmd-rich-rules
```

```bash
sudo dnf install fail2ban fail2ban-systemd
sudo systemctl enable --now firewalld fail2ban
```

### fail2ban without firewalld

If you run plain nftables instead, install only `fail2ban-server` (and `fail2ban-systemd`) and choose the action yourself:

```ini
[DEFAULT]
banaction          = nftables
banaction_allports = nftables[type=allports]
```

## The firewalld actions compared

| Action | How it bans | Visible with |
|---|---|---|
| [`firewallcmd-rich-rules`](/actions/#action-firewallcmd-rich-rules) | One runtime rich rule per IP and port in the zone | `firewall-cmd --list-rich-rules` |
| `firewallcmd-rich-logging` | Same, plus a rate-limited `log prefix='f2b-<jail>'` | `firewall-cmd --list-rich-rules`, kernel log |
| [`firewallcmd-ipset`](/actions/#action-firewallcmd-ipset) | One direct rule matching an ipset `f2b-<jail>`; bans are set entries | `ipset list f2b-<jail>`, `firewall-cmd --direct --get-all-rules` |
| `firewallcmd-multiport` / `firewallcmd-new` | Direct-interface chain `f2b-<jail>` jumped to from `INPUT_direct` for the jail's ports | `firewall-cmd --direct --get-all-rules` |
| `firewallcmd-allports` | Same chain, jumped to for all traffic | `firewall-cmd --direct --get-all-rules` |

**Rich rules** are the default and the most "firewalld-native": they live in a zone and show up in normal firewalld tooling. Each ban adds one rule per port, which is fine for tens or hundreds of bans and gets slow with thousands.

**ipset** scales better: a single rule matches the whole set and each ban is a set entry. The `ipset` command must be installed. In fail2ban 1.1.1 the option that chooses who manages the set was renamed from `ipsettype` to `ipsetbackend` (`ipset` or `firewalld`). `ipsettype` now sets the set type (`hash:ip`, `hash:net`).

```ini
[DEFAULT]
banaction          = firewallcmd-ipset
banaction_allports = firewallcmd-allports
```

### Port ranges and recidive

Jails that ban all ports pass `port = 0:65535` to the action. firewalld writes ranges as `0-65535`. Fedora's package patches the rich-rule action to translate `:` to `-`, while upstream fail2ban doesn't. On Debian or a source install with firewalld, use `banaction_allports = firewallcmd-allports` for `recidive` and other all-ports jails, or the rich rule will be rejected.

## Zones: which zone gets the bans?

`firewallcmd-rich-rules` adds its rules to `zone = public` by default. If your internet-facing interface is in another zone, the bans exist but never see the traffic. Check:

```bash
sudo firewall-cmd --get-default-zone
```

```ini
[DEFAULT]
banaction          = firewallcmd-rich-rules[zone=external]
banaction_allports = firewallcmd-rich-rules[zone=external]
```

## View and verify bans

```bash
sudo firewall-cmd --zone=public --list-rich-rules

# is one address banned?
sudo firewall-cmd --zone=public --list-rich-rules | grep 203.0.113.42

# ipset action
sudo ipset list f2b-sshd

# direct-interface actions
sudo firewall-cmd --direct --get-all-rules
```

```text
$ sudo fail2ban-client banned 203.0.113.42
[['sshd']]
```

```bash
sudo firewall-cmd --list-rich-rules | grep 192.0.2.15
sudo fail2ban-client set sshd unbanip 192.0.2.15
```

fail2ban adds its rich rules (and direct rules) to the **runtime** configuration only, never with `--permanent`. `firewall-cmd --reload` replaces the runtime configuration with the permanent one, so every ban disappears while fail2ban still lists the addresses as banned. After reloading firewalld, restart fail2ban so it re-applies current bans from its database:

```bash
```

## Trap 2: "FirewallD is not running"

If firewalld is stopped, every `firewall-cmd` call fails with `FirewallD is not running`, and fail2ban can't ban anything. `/var/log/fail2ban.log` shows the failing command with a non-zero return code. Fix it by starting firewalld:

```bash
sudo systemctl restart fail2ban
```

## firewalld on Debian and Ubuntu

Debian 13 and Ubuntu 24.04 default fail2ban to the `nftables` action even if firewalld is installed. That works: firewalld's nftables backend keeps its rules in its own table, and fail2ban uses `inet f2b-table`. A drop or reject in either one blocks the packet, so you don't have to switch. Bans just won't appear in `firewall-cmd` output. If you want them in firewalld, set the rich-rules action in `jail.local` (with `firewallcmd-allports` for all-ports jails, see above) and install `ipset` if you choose the ipset action.

## FAQ

### Does fail2ban work with firewalld?

Yes. fail2ban ships several firewalld actions, and on Fedora and RHEL-family systems the `fail2ban-firewalld` package makes `firewallcmd-rich-rules` the default. Each ban becomes a runtime rich rule in the `public` zone. firewalld must be running, or bans fail.

### How do I set the fail2ban banaction for firewalld?

Put `banaction = firewallcmd-rich-rules` and `banaction_allports = firewallcmd-rich-rules` under `[DEFAULT]` in `/etc/fail2ban/jail.local`, or install `fail2ban-firewalld`, which sets them in `jail.d/00-firewalld.conf`. Use `firewallcmd-rich-rules[zone=<zone>]` if your public interface isn't in the `public` zone.

### Should I use firewallcmd-ipset or rich rules?

Rich rules are the default and integrate with firewalld zones. They're fine for up to a few hundred bans. `firewallcmd-ipset` puts banned addresses in one ipset matched by a single rule, which scales to thousands, but it relies on firewalld's direct interface and the `ipset` tool.

### Why did my fail2ban bans disappear after firewall-cmd --reload?

fail2ban adds rules to firewalld's runtime configuration only. `firewall-cmd --reload` resets the runtime to the permanent configuration and drops them. fail2ban doesn't notice. Run `systemctl restart fail2ban` after every firewalld reload so it re-applies the active bans from its database.

### What does "FirewallD is not running" mean in fail2ban?

fail2ban is set to a `firewallcmd-*` action, but the firewalld service is stopped, so every ban command fails. Start it with `systemctl enable --now firewalld`, or switch `banaction` to `nftables` if you don't use firewalld.
