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>
This commit is contained in:
commit
2e3f79236c
44 files changed
+4280
No files matched your search
+269
@@ -0,0 +1,269 @@
|
||||
# Agent – f2b-webctl
|
||||
|
||||
Every fail2ban host runs the agent [`f2b-webctl`](../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).
|
||||
|
||||
```bash
|
||||
# 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](#hosts-with-managed-authorized_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`
|
||||
|
||||
```ini
|
||||
# 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`
|
||||
|
||||
```ini
|
||||
# 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
|
||||
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:
|
||||
|
||||
```bash
|
||||
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:
|
||||
|
||||
```bash
|
||||
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:
|
||||
|
||||
```bash
|
||||
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:
|
||||
|
||||
```bash
|
||||
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
|
||||
|
||||
```ini
|
||||
# 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
|
||||
|
||||
```bash
|
||||
# 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
|
||||
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:
|
||||
|
||||
```bash
|
||||
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
|
||||
```
|
||||
Reference in new issue
Block a user