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>
6.6 KiB
Installation – web UI
This guide sets up the web UI on a Docker host, step by step. The agents on your fail2ban hosts are covered in agent.md; you need both.
- Source: https://git.retronerd.at/schiwagoa/fail2ban-webui
- Image:
git.retronerd.at/schiwagoa/fail2ban-webui(amd64,arm64) - Tags:
1.0.0(exact),1(latest 1.x),latest
1. Prerequisites
- Docker Engine with Compose v2 (
docker compose version) - Outgoing SSH (TCP 22 or your port) from the Docker host to every fail2ban host
- A DNS name and a reverse proxy with TLS for the UI (see reverse-proxy.md)
Check that the subnet used in the example (172.23.51.0/24) is free:
docker network inspect $(docker network ls -q) --format '{{.Name}} {{range .IPAM.Config}}{{.Subnet}}{{end}}' | grep 172.23.51 \
|| echo "172.23.51.0/24 is free"
If it is taken, pick another /24 and change it in step 3.
2. Directories
mkdir -p /opt/fail2ban-webui/config /opt/fail2ban-webui/data
cd /opt/fail2ban-webui
# the container runs as UID 10050 and needs to write to data/
chown -R 10050:10050 data
chmod 700 data
| Path | Content |
|---|---|
docker-compose.yaml |
the service |
.env |
settings and secrets (password hash, OIDC secret) |
config/hosts.yaml |
the fail2ban hosts (mounted read-only) |
data/ |
SSH key, known_hosts, session secret, audit log, GeoIP database |
3. docker-compose.yaml
nano docker-compose.yaml
# =====================================================================
# fail2ban-webui – example docker-compose.yaml
#
# mkdir -p fail2ban-webui/{config,data} && cd fail2ban-webui
# (copy this file, .env.example as .env and hosts.yaml.example as config/hosts.yaml)
# chown -R 10050:10050 data
#
# The UI listens on 127.0.0.1:8080 only – put a reverse proxy with TLS in front of it.
# The network uses its own /24 subnet; change it if it collides with an existing one
# (docker network inspect $(docker network ls -q) | grep Subnet).
# =====================================================================
name: fail2ban-webui
services:
fail2ban-webui:
image: git.retronerd.at/schiwagoa/fail2ban-webui:latest
# build: { context: .., dockerfile: docker/Dockerfile } # build from the repository instead
container_name: fail2ban-webui
restart: unless-stopped
env_file: .env
environment:
TZ: ${TZ:-Europe/Vienna}
read_only: true
tmpfs:
- /tmp
cap_drop:
- ALL
security_opt:
- no-new-privileges:true
volumes:
- ./config:/config:ro
- ./data:/data
ports:
- "127.0.0.1:8080:8080"
extra_hosts:
# lets the container reach an agent on the Docker host itself (address: host.docker.internal)
- "host.docker.internal:host-gateway"
deploy:
resources:
limits:
memory: 256M
networks:
fail2ban-webui.dockernetwork.local:
ipv4_address: 172.23.51.10
networks:
fail2ban-webui.dockernetwork.local:
name: fail2ban-webui.dockernetwork.local
driver: bridge
ipam:
config:
- subnet: 172.23.51.0/24
gateway: 172.23.51.1
ip_range: 172.23.51.128/25
If the reverse proxy runs on another machine, bind the port to the LAN address instead,
e.g. "192.168.1.10:8080:8080" – never to 0.0.0.0 without a firewall in front.
4. .env
Start with this minimal file; every option is described in
configuration.md and in examples/.env.example.
nano .env
chmod 600 .env
PUBLIC_URL=https://fail2ban.example.org
DEFAULT_LANG=en
TRUSTED_PROXY_HOPS=1
AUTH_MODE=local
AUTH_USER=admin
AUTH_PASSWORD_HASH='set in the next step'
SSH_USER=f2bweb
MANUAL_JAIL=manual-block
GEOIP_AUTO_DOWNLOAD=1
Password hash
docker compose run --rm -it fail2ban-webui hash-password
Copy the printed line (AUTH_PASSWORD_HASH='scrypt:…') into .env and replace the
placeholder. Keep the single quotes – the hash contains $ signs that Docker Compose
would otherwise try to expand. For single sign-on instead see
authentication.md.
5. SSH key
docker compose run --rm fail2ban-webui keygen
The command creates data/ssh/id_ed25519 and prints the public key
(ssh-ed25519 AAAA… fail2ban-webui). Copy it – you need it for the agent installer.
6. Agents
Install the agent on every fail2ban host as described in agent.md. In short:
# on each fail2ban host, as root
git clone https://git.retronerd.at/schiwagoa/fail2ban-webui.git /tmp/fail2ban-webui
echo 'ssh-ed25519 AAAA… fail2ban-webui' > /tmp/webui.pub
bash /tmp/fail2ban-webui/agent/install.sh --key-file /tmp/webui.pub --from <public IP of the web UI host>
7. config/hosts.yaml
nano config/hosts.yaml
hosts:
- name: web01
address: web01.example.org
- name: lxc-proxy
address: 192.168.1.20
port: 2222
# fail2ban on the Docker host itself
- name: dockerhost
address: host.docker.internal
user defaults to SSH_USER (f2bweb). Set user: root for hosts where the agent was
installed with --user root.
8. Host keys and connection test
docker compose run --rm -it fail2ban-webui keyscan
keyscan shows the fingerprint of every host and asks before writing
data/ssh/known_hosts. Compare the fingerprints with the hosts:
# on the fail2ban host
ssh-keygen -lf /etc/ssh/ssh_host_ed25519_key.pub
Then test every agent:
docker compose run --rm fail2ban-webui check
OK web01 agent 1.0.0, 3 jails, 12 bans
OK lxc-proxy agent 1.0.0, 2 jails, 0 bans
OK dockerhost agent 1.0.0, 4 jails, 7 bans
Every host must show OK – see operations.md otherwise.
9. Start
docker compose up -d
docker compose ps # STATUS: Up … (healthy)
docker compose logs -f fail2ban-webui # Ctrl+C to leave
The log shows a summary like
fail2ban-webui 1.0.0 – hosts: web01, lxc-proxy, dockerhost – auth: local – loki: off – geoip: on.
Local test without the proxy:
curl -s http://127.0.0.1:8080/healthz # ok
10. Reverse proxy
Publish http://127.0.0.1:8080 (or the LAN address from step 3) under your DNS name with
TLS – examples for Nginx Proxy Manager, Caddy, Traefik and nginx are in
reverse-proxy.md. Then open PUBLIC_URL and sign in.