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>
242 lines
6.6 KiB
Markdown
242 lines
6.6 KiB
Markdown
# 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](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](reverse-proxy.md))
|
||
|
||
Check that the subnet used in the example (`172.23.51.0/24`) is free:
|
||
|
||
```bash
|
||
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
|
||
|
||
```bash
|
||
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
|
||
|
||
```bash
|
||
nano docker-compose.yaml
|
||
```
|
||
|
||
```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](configuration.md) and in [`examples/.env.example`](../examples/.env.example).
|
||
|
||
```bash
|
||
nano .env
|
||
chmod 600 .env
|
||
```
|
||
|
||
```ini
|
||
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
|
||
|
||
```bash
|
||
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](authentication.md).
|
||
|
||
## 5. SSH key
|
||
|
||
```bash
|
||
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](agent.md). In short:
|
||
|
||
```bash
|
||
# 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
|
||
|
||
```bash
|
||
nano config/hosts.yaml
|
||
```
|
||
|
||
```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
|
||
|
||
```bash
|
||
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:
|
||
|
||
```bash
|
||
# on the fail2ban host
|
||
ssh-keygen -lf /etc/ssh/ssh_host_ed25519_key.pub
|
||
```
|
||
|
||
Then test every agent:
|
||
|
||
```bash
|
||
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](operations.md#troubleshooting) otherwise.
|
||
|
||
## 9. Start
|
||
|
||
```bash
|
||
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:
|
||
|
||
```bash
|
||
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](reverse-proxy.md). Then open `PUBLIC_URL` and sign in.
|
||
|
||
## Next steps
|
||
|
||
- [Protect the login with fail2ban](operations.md#protecting-the-login-with-fail2ban)
|
||
- [Updates and backups](operations.md)
|
||
- Optional: [Loki history and Grafana links](configuration.md#loki-and-grafana)
|