Files
fail2ban-webui/docs/agent.md
T
schiwagoaandClaude Opus 5.5 2e3f79236c Initial version of fail2ban-webui
Web interface to view, ban and unban fail2ban IPs on several hosts via
SSH, generalised from the private f2b-web tool.

- Web UI (Flask/Waitress): hosts.yaml, local/OIDC/proxy authentication,
  English/German UI, origin map, optional Loki/Grafana/GeoIP, CLI helpers
- Agent f2b-webctl with install.sh/uninstall.sh (dedicated user + sudo,
  from= restriction, protected networks, manual jail)
- Hardened Docker image, examples, documentation, smoke test, testbed

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 15:38:17 +01:00

9.7 KiB
Raw Blame History

Agent – f2b-webctl

Every fail2ban host runs the agent f2b-webctl: a small Python script (standard library only) started as SSH forced command of the web UI's key. The web UI cannot run anything else on the host.

Command (sent by the web UI) Effect
list all jails with their banned IPs (JSON)
ban <ip> ban permanently in the manual jail (manual-block)
unban <jail> <ip> unban in one jail
unban-all <ip> unban in all jails
version agent version

Never banned, whatever the web UI sends:

  • private, loopback, link-local, CGNAT, multicast and reserved ranges (10/8, 172.16/12, 192.168/16, 100.64/10, 127/8, 169.254/16, fc00::/7, fe80::/10, …)
  • all addresses of the host itself and the address the web UI connects from
  • everything listed in PROTECTED in /etc/f2b-webctl.conf (e.g. your home IP)

Every action is logged to syslog (facility auth, tag f2b-webctl), including rejected ones.

Requirements

fail2ban ≥ 0.11, running (fail2ban-client ping) – older versions without --with-time work, but without start/end times
python3 ≥ 3.8
sshd reachable from the web UI host
sudo for the default user f2bweb (apt install sudo); not needed with --user root
bash, ssh-keygen for the installer

Works on Debian, Ubuntu, Proxmox VE and other systemd or non-systemd Linux systems – bare metal, VMs, LXC containers (privileged or unprivileged) and Docker hosts.

Installation

You need the public key printed by fail2ban-webui keygen and the address the web UI connects from (the public IP of the Docker host, or its LAN address).

# as root on the fail2ban host
git clone https://git.retronerd.at/schiwagoa/fail2ban-webui.git /tmp/fail2ban-webui
cd /tmp/fail2ban-webui/agent

echo 'ssh-ed25519 AAAA… fail2ban-webui' > /tmp/webui.pub

# see what would happen
bash install.sh --key-file /tmp/webui.pub --from 198.51.100.10 --dry-run
# install
bash install.sh --key-file /tmp/webui.pub --from 198.51.100.10

Without git: download the three files f2b-webctl, install.sh and uninstall.sh from the agent/ directory into one directory and run install.sh from there.

The installer shows a summary and asks before it changes anything:

fail2ban-webui agent 1.0.0  –  fail2ban 1.1.0  –  web01

  script         /usr/local/sbin/f2b-webctl
  config         /etc/f2b-webctl.conf
  SSH user       f2bweb (will be created)
  sudo rule      /etc/sudoers.d/f2b-webctl
  key file       /var/lib/f2bweb/.ssh/authorized_keys
  key            256 SHA256:zfwh… (ED25519)
  allowed from   198.51.100.10
  manual jail    manual-block (will be created)
  backups        /var/backups/fail2ban-webui-agent

What it does:

  1. backs up every file it is going to change to --backup-dir
  2. installs /usr/local/sbin/f2b-webctl (root:root 0750)
  3. writes /etc/f2b-webctl.conf (kept if it already exists)
  4. creates the system user f2bweb (home /var/lib/f2bweb, no password) and the sudo rule
    Defaults!/usr/local/sbin/f2b-webctl env_keep += "SSH_ORIGINAL_COMMAND SSH_CLIENT"
    f2bweb ALL=(root) NOPASSWD: /usr/local/sbin/f2b-webctl
    
  5. adds the key with all restrictions:
    restrict,from="198.51.100.10",command="sudo -n /usr/local/sbin/f2b-webctl" ssh-ed25519 AAAA… fail2ban-webui
    
  6. creates the jail manual-block if it does not exist (see below)
  7. runs a self test through the same sudo path the web UI uses

Running the installer again is safe: it updates the script, replaces the key entry and keeps your configuration.

Options

Option Default Meaning
--key-file FILE / --key "KEY" – public key of the web UI (required)
--from ADDRESSES any only accept the key from these IPs/networks (comma separated). Strongly recommended.
--user NAME f2bweb SSH user; root works without sudo
--authorized-keys-file PATH ~USER/.ssh/authorized_keys where the key entry goes – see managed keys
--manual-jail NAME manual-block jail for manual bans
--banaction ACTION from jail.local banaction of a newly created manual jail
--protect NETWORKS – additional protected addresses (only when the config file is written)
--no-jail – do not create the manual jail
--force-config – overwrite an existing /etc/f2b-webctl.conf
--backup-dir DIR /var/backups/fail2ban-webui-agent backups of changed files
--dry-run – only show what would be done
-y, --yes – do not ask

The installer warns when sshd would not read the chosen key file (AuthorizedKeysFile), when AllowUsers/AllowGroups are set, or when --user root meets PermitRootLogin no.

The manual jail

Bans from the web UI go into one jail, manual-block by default, with bantime = -1 (permanent). If the jail does not exist, the installer creates:

/etc/fail2ban/filter.d/manual-block.conf

# fail2ban-webui: never matches – the jail is only filled via
# "fail2ban-client set manual-block banip <ip>"
[Definition]
failregex = ^FAIL2BAN-WEBUI-NEVER-MATCHES <HOST>$
ignoreregex =

/etc/fail2ban/jail.d/manual-block.local

# fail2ban-webui: permanent manual bans from the web UI
[manual-block]
enabled  = true
filter   = manual-block
backend  = polling
logpath  = /var/log/fail2ban-manual-block.log
maxretry = 1
bantime  = -1

The jail uses your default banaction from jail.local. Bans survive restarts through the fail2ban database.

An existing jail with that name is never touched – if you already have a jail for manual bans, pass its name with --manual-jail.

Docker hosts

fail2ban's default actions block in the INPUT chain. Traffic to published Docker ports does not pass INPUT but FORWARD → DOCKER-USER, so those bans would not affect your containers. For a Docker host, create the manual jail with an action that uses the DOCKER-USER chain, e.g. with iptables:

bash install.sh --key-file /tmp/webui.pub --from 198.51.100.10 \
  --banaction "iptables-allports[chain=DOCKER-USER]"

Check the result after the first ban:

iptables -S DOCKER-USER | head

If the web UI runs on the same Docker host, use address: host.docker.internal in hosts.yaml and allow SSH from the Docker network if a firewall (e.g. UFW) blocks it:

ufw allow from 172.23.51.0/24 to any port 22 proto tcp comment 'fail2ban-webui'

and install the agent with --from 172.23.51.10.

LXC containers

The agent runs inside the container where fail2ban runs – nothing special is needed. In unprivileged containers fail2ban itself must be able to change the container's firewall (nftables/iptables inside the CT); test with a manual ban:

fail2ban-client set manual-block banip 203.0.113.1 && nft list ruleset | grep 203.0.113.1
fail2ban-client set manual-block unbanip 203.0.113.1

Hosts with managed authorized_keys

Some tools (Ansible, Keywarden, cloud-init, …) overwrite ~/.ssh/authorized_keys. Put the key into a file they do not manage and make sure sshd reads it:

grep -i '^AuthorizedKeysFile' /etc/ssh/sshd_config /etc/ssh/sshd_config.d/*.conf
# e.g. AuthorizedKeysFile .ssh/authorized_keys .ssh/authorized_keys2

bash install.sh --key-file /tmp/webui.pub --from 198.51.100.10 \
  --user root --authorized-keys-file /root/.ssh/authorized_keys2

Configuration – /etc/f2b-webctl.conf

# fail2ban-webui agent (f2b-webctl) – settings
# Jail used for manual bans from the web UI
MANUAL_JAIL=manual-block
# Addresses/networks that must never be banned (comma separated), e.g. your home IP.
# Private, loopback, link-local and CGNAT ranges are always protected.
PROTECTED=203.0.113.25,2001:db8:1234::/48
# Also protect all addresses of this host and the address of the web UI (yes/no)
PROTECT_OWN_ADDRESSES=yes
PROTECT_CLIENT=yes

Changes take effect immediately – the agent reads the file on every call.

Testing on the host

# as root, directly
f2b-webctl list | python3 -m json.tool | head -30
f2b-webctl ban 10.0.0.1          # → rejected, protected

# the same path as the web UI (user f2bweb through sudo)
runuser -u f2bweb -- env SSH_ORIGINAL_COMMAND=version sudo -n /usr/local/sbin/f2b-webctl

# what the agent did
journalctl -t f2b-webctl --since today      # or: grep f2b-webctl /var/log/auth.log

Update

Run the installer of the new version again with the same options. Your configuration and the jail are kept; the script and the key entry are replaced.

Uninstall

bash uninstall.sh --dry-run
bash uninstall.sh
Option Meaning
--user NAME agent user (default f2bweb)
--authorized-keys-file PATH file with the key entry
--remove-user also delete the user and its home
--remove-jail also delete the manual jail – lifts all manual bans (they are listed first)
--backup-dir DIR, --dry-run, --yes as for the installer

Manual installation

If you prefer to do it by hand, these are all the steps the installer performs:

install -o root -g root -m 0750 f2b-webctl /usr/local/sbin/f2b-webctl
nano /etc/f2b-webctl.conf                          # content see above

useradd --system --create-home --home-dir /var/lib/f2bweb --shell /bin/sh f2bweb
usermod -p '*' f2bweb
visudo -f /etc/sudoers.d/f2b-webctl                # content see "What it does", step 4

install -d -o f2bweb -g f2bweb -m 700 /var/lib/f2bweb/.ssh
nano /var/lib/f2bweb/.ssh/authorized_keys          # line see step 5
chown f2bweb: /var/lib/f2bweb/.ssh/authorized_keys
chmod 600 /var/lib/f2bweb/.ssh/authorized_keys

# manual jail: the two files from "The manual jail", then
touch /var/log/fail2ban-manual-block.log
fail2ban-client reload