Update docs and write troubleshooting steps to debug no reciving ban/unban events

This commit is contained in:
2026-02-21 22:31:59 +01:00
parent 3f6f356923
commit 48bcd403ac
5 changed files with 338 additions and 50 deletions

View File

@@ -6,7 +6,7 @@
**Enterprise-Grade Intrusion Detection System Management Platform** **Enterprise-Grade Intrusion Detection System Management Platform**
[![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0) [![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0)
[![Go Version](https://img.shields.io/badge/Go-1.24+-00ADD8?logo=go)](https://golang.org/) [![Go Version](https://img.shields.io/badge/Go-1.25+-00ADD8?logo=go)](https://golang.org/)
[![Platform](https://img.shields.io/badge/Platform-Linux-lightgrey)](https://www.linux.org/) [![Platform](https://img.shields.io/badge/Platform-Linux-lightgrey)](https://www.linux.org/)
*Swissmade open-source solution for centralized Fail2Ban management across distributed infrastructure* *Swissmade open-source solution for centralized Fail2Ban management across distributed infrastructure*
@@ -23,13 +23,15 @@ The project is maintained by Swissmakers GmbH and released under GPL-3.0.
Fail2Ban UI does not replace Fail2Ban. It connects to existing Fail2Ban instances and adds: Fail2Ban UI does not replace Fail2Ban. It connects to existing Fail2Ban instances and adds:
- A Dashboard for active jails and recent ban/unban activity - A Dashboard for active jails and recent ban/unban activity with real-time WebSocket updates
- Server Manager for adding new fail2ban servers to Fail2ban-UI - Server Manager for adding new fail2ban servers to Fail2ban-UI
- Central search and unban across jails and servers - Central search and unban / ban across jails and servers
- Remote editing / creating, of jail/filter configuration (depending on connector) - Remote editing / creating, of jail/filter configuration (depending on connector)
- Filter debug integration and live log-pattern testing - Filter debug integration and live log-pattern testing
- Advanced ban actions for recurring offenders e.g. automatically ban on pfSense and Mikrotik, when threshold is reached. - Ban Insights with an interactive 3D threat globe showing blocks per country
- Optional email alerts with GeoIP/Whois enrichment for selected "alert countries" only. - Advanced ban actions for recurring offenders e.g. automatically ban on pfSense, Mikrotik, or OPNsense when threshold is reached
- Data management possibility for permanent block logs and stored ban events
- Optional email alerts with GeoIP/Whois enrichment for selected "alert countries" only
- Optional OIDC login (Keycloak, Authentik, Pocket-ID) - Optional OIDC login (Keycloak, Authentik, Pocket-ID)
- Least-privilege, SELinux-aware container deployment (policies provided) - Least-privilege, SELinux-aware container deployment (policies provided)
- .. and much more to come. - .. and much more to come.
@@ -170,6 +172,8 @@ Global Fail2Ban settings including default bantime, findtime, maxretry, banactio
* Do not expose the UI directly to the public Internet. Put it behind a reverse proxy, VPN, firewall rules, and/or OIDC. * Do not expose the UI directly to the public Internet. Put it behind a reverse proxy, VPN, firewall rules, and/or OIDC.
* SSH connector should use a dedicated service account with minimal sudo permissions and ACLs. * SSH connector should use a dedicated service account with minimal sudo permissions and ACLs.
* All IP addresses are validated (strict IPv4/IPv6/CIDR parsing) before being passed to any integration or command, preventing command injection.
* WebSocket connections are protected by origin validation (same-origin only) and require authentication when OIDC is enabled.
See [`docs/security.md`](https://github.com/swissmakers/fail2ban-ui/blob/main/docs/security.md) for details. See [`docs/security.md`](https://github.com/swissmakers/fail2ban-ui/blob/main/docs/security.md) for details.

View File

@@ -4,8 +4,12 @@ This is a short index for operators. The UI primarily uses these endpoints. Path
## Authentication ## Authentication
- When OIDC is enabled, most `/api/*` endpoints require an authenticated session. - When OIDC is enabled, all `/api/*` endpoints (including WebSocket) require an authenticated session, except the callback endpoints.
- Callback endpoints are authenticated using `X-Callback-Secret`. - Callback endpoints (`/api/ban`, `/api/unban`) are authenticated using `X-Callback-Secret`.
## Input validation
All endpoints that accept IP addresses validate them server-side using Go's `net.ParseIP` / `net.ParseCIDR`. Requests with invalid IPs receive a `400 Bad Request` response. This applies to ban/unban callbacks, manual ban/unban from the dashboard, and the advanced actions test endpoint.
## Common headers ## Common headers
@@ -14,49 +18,79 @@ This is a short index for operators. The UI primarily uses these endpoints. Path
## Endpoints ## Endpoints
Server management ### Server management
- `GET /api/servers` - `GET /api/servers` -> List configured servers
- `POST /api/servers` - `POST /api/servers` -> Create or update a server
- `DELETE /api/servers/:id` - `DELETE /api/servers/:id` -> Delete a server
- `POST /api/servers/:id/test` - `POST /api/servers/:id/default` -> Set server as default
- `POST /api/servers/:id/test` -> Test server connectivity
- `GET /api/ssh/keys` -> List available SSH keys
Jails and configuration ### Jails and configuration
- `GET /api/summary` - `GET /api/summary` -> Dashboard summary (jails, banned IPs per server)
- `GET /api/jails/manage` - `GET /api/jails/manage` -> List jails with enabled/disabled status
- `POST /api/jails/manage` - `POST /api/jails/manage` -> Update jail enabled/disabled state
- `GET /api/jails/:jail/config` - `POST /api/jails` -> Create a new jail
- `POST /api/jails/:jail/config` - `DELETE /api/jails/:jail` -> Delete a jail
- `POST /api/jails/:jail/unban/:ip` - `GET /api/jails/:jail/config` -> Get jail/filter configuration
- `POST /api/jails/:jail/ban/:ip` - `POST /api/jails/:jail/config` -> Update jail/filter configuration
- `POST /api/jails/:jail/logpath/test` -> Test log path accessibility
- `POST /api/jails/:jail/unban/:ip` -> Unban an IP from a jail
- `POST /api/jails/:jail/ban/:ip` -> Ban an IP in a jail
Events and analytics ### Events and analytics
- `GET /api/events/bans` - `GET /api/events/bans` -> List ban/unban events (paginated, filterable)
- `GET /api/events/bans/stats` - `DELETE /api/events/bans` -> Delete all stored ban events
- `GET /api/events/bans/insights` - `GET /api/events/bans/stats` -> Ban statistics (counts, timeseries)
- `GET /api/events/bans/insights` -> Ban insights (countries, top IPs, top jails)
Settings ### Advanced actions
- `GET /api/settings` - `GET /api/advanced-actions/blocks` -> List permanent block records
- `POST /api/settings` - `DELETE /api/advanced-actions/blocks` -> Delete all permanent block records
- `POST /api/settings/test-email` - `POST /api/advanced-actions/test` -> Manually test block/unblock on configured integration
Filter debugging ### Settings
- `GET /api/filters` - `GET /api/settings` -> Get current application settings
- `POST /api/filters/test` - `POST /api/settings` -> Update application settings
- `POST /api/settings/test-email` -> Send a test email
Service control ### Filter management
- `POST /api/fail2ban/restart` - `GET /api/filters` -> List available filters
- `GET /api/filters/:filter/content` -> Get filter file content
- `POST /api/filters` -> Create a new filter
- `POST /api/filters/test` -> Test filter regex against log lines
- `DELETE /api/filters/:filter` -> Delete a filter
Callbacks (Fail2Ban actions) ### Service control
- `POST /api/ban` - `POST /api/fail2ban/restart` -> Restart / Reloads the Fail2Ban service
- `POST /api/unban`
### Version
- `GET /api/version` -> Get running version and optional update check
### WebSocket
- `GET /api/ws` -> WebSocket endpoint (upgrade)
The WebSocket connection streams, real-time events to the frontend:
- `heartbeat` -> periodic health check (~30s)
- `console_log` -> debug console log lines (when debug mode is enabled)
- `ban_event` -> real-time ban event broadcast
- `unban_event` -> real-time unban event broadcast
The WebSocket enforces same-origin policy via the `Origin` header and requires authentication when OIDC is enabled.
### Callbacks (Fail2Ban actions)
- `POST /api/ban` -> Receive ban notification from Fail2Ban
- `POST /api/unban` -> Receive unban notification from Fail2Ban
Callbacks require: Callbacks require:
- Header: `X-Callback-Secret: <secret>` - Header: `X-Callback-Secret: <secret>`
- JSON body fields (typical): `serverId`, `ip`, `jail`, `hostname`, `failures`, `logs` - JSON body fields (typical): `serverId`, `ip`, `jail`, `hostname`, `failures`, `logs`
Authentication routes (OIDC) All IPs in callback payloads are validated before processing.
- `GET /auth/login`
- `GET /auth/callback` ### Authentication routes (OIDC)
- `GET /auth/logout` - `GET /auth/login` -> Initiate OIDC login flow
- `GET /auth/status` - `GET /auth/callback` -> OIDC provider callback
- `GET /auth/user` - `GET /auth/logout` -> Logout and clear session
- `GET /auth/status` -> Check authentication status
- `GET /auth/user` -> Get current user info

View File

@@ -23,9 +23,11 @@ Fail2Ban UI consists of :
## Components (high level) ## Components (high level)
- REST API: server management, jail/filter config read/write, ban/unban actions, settings - REST API: server management, jail/filter config read/write, ban/unban actions, settings, data management (clear events/blocks)
- WebSocket hub: streams ban/unban events and (optional) debug console logs - WebSocket hub: streams real-time ban/unban events and (optional) debug console logs, protected by origin validation and session auth
- Storage: server definitions, settings, ban history, permanent block records - Storage: server definitions, settings, ban history, permanent block records
- Integrations: MikroTik (SSH), pfSense (REST API), OPNsense (REST API) with input validation on all parameters
- Ban Insights: country-level analytics with interactive 3D threat globe visualization
Additional resources: Additional resources:
- Container deployment guide: `deployment/container/README.md` - Container deployment guide: `deployment/container/README.md`
@@ -62,11 +64,11 @@ Additional resources:
│ │ • /auth/login | /auth/callback | /auth/logout │ │ │ e │ │ • /auth/login | /auth/callback | /auth/logout │ │ │ e
│ │ • /auth/status | /auth/user │ │ │ t │ │ • /auth/status | /auth/user │ │ │ t
│ │ • POST /api/ban | POST /api/unban ← Fail2ban callbacks (a valid Callback │ │ │ │ │ • POST /api/ban | POST /api/unban ← Fail2ban callbacks (a valid Callback │ │ │
│ │ • GET /api/ws (WebSocket) Secret is needed) │ │ │ │ • /static/* | /locales/* Secret is needed) │ │----┘
│ │ • /static/* | /locales/* │ │----┘
│ └───────────────────────────────────────────────────────────────────────────┘ │ │ └───────────────────────────────────────────────────────────────────────────┘ │
│ ┌───────────────────────────────────────────────────────────────────────────┐ │ │ ┌───────────────────────────────────────────────────────────────────────────┐ │
│ │ PROTECTED (when OIDC enabled): GET / | GET and POST to all other /api/* │ │ │ │ PROTECTED (when OIDC enabled): │ │
│ │ GET / | all other /api/* | GET /api/ws (WebSocket, same-origin only) │ │
│ └───────────────────────────────────────────────────────────────────────────┘ │ │ └───────────────────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────────┘ └─────────────────────────────────────────────────────────────────────────────────┘
``` ```
@@ -84,16 +86,19 @@ Additional resources:
│ │ • POST /jails/:jail/unban/:ip • POST /jails/:jail/ban/:ip │ │ │ │ • POST /jails/:jail/unban/:ip • POST /jails/:jail/ban/:ip │ │
│ │ • GET /settings • POST /settings │ │ │ │ • GET /settings • POST /settings │ │
│ │ • GET /events/bans • GET /events/bans/stats | /insights │ │ │ │ • GET /events/bans • GET /events/bans/stats | /insights │ │
│ │ • DELETE /events/bans • DELETE /advanced-actions/blocks │ │
│ │ • GET /version (optional GitHub request if UPDATE_CHECK) │ │ │ │ • GET /version (optional GitHub request if UPDATE_CHECK) │ │
│ │ • GET /servers | POST/DELETE /servers | POST /servers/:id/test │ │ │ │ • GET /servers | POST/DELETE /servers | POST /servers/:id/test │ │
│ │ • GET /filters/* • POST /filters/test | POST/DELETE /filters │ │ │ │ • GET /filters/* • POST /filters/test | POST/DELETE /filters │ │
│ │ • POST /fail2ban/restart • GET/POST /advanced-actions/* │ │ │ │ • POST /fail2ban/restart • GET/POST /advanced-actions/* │ │
│ │ • POST /ban (callback) • POST /unban (callback) │ │ │ │ • POST /ban (callback) • POST /unban (callback) │ │
│ │ All IP inputs validated via net.ParseIP / net.ParseCIDR │ │
│ └────────────────────────────────────────────────────────────────────────────┘ │ │ └────────────────────────────────────────────────────────────────────────────┘ │
│ │ │ │ │ │
│ ┌─────────────────────────────────┴──────────────────────────────────────────┐ │ │ ┌─────────────────────────────────┴──────────────────────────────────────────┐ │
│ │ WebSocket Hub (GET /api/ws) │ │ │ │ WebSocket Hub (GET /api/ws — same-origin, auth required with OIDC) │ │
│ │ • register / unregister clients │ │ │ │ • register / unregister clients │ │
│ │ • Origin header validated against Host (rejects cross-site connections) │ │
│ │ • broadcast to all clients: │ │ │ │ • broadcast to all clients: │ │
│ │ - type: "heartbeat" (every ~30s) │ │ │ │ - type: "heartbeat" (every ~30s) │ │
│ │ - type: "console_log" (debug console lines) │ │ │ │ - type: "console_log" (debug console lines) │ │
@@ -110,8 +115,10 @@ Additional resources:
│ │ Connector Manager │ │ Integrations + Email │ │ │ │ Connector Manager │ │ Integrations + Email │ │
│ │ • Local (fail2ban.sock) │ │ • Mikrotik / pfSense / │ │ │ │ • Local (fail2ban.sock) │ │ • Mikrotik / pfSense / │ │
│ │ • SSH (exec on remote) │ │ OPNsense (block/unblock)│ │ │ │ • SSH (exec on remote) │ │ OPNsense (block/unblock)│ │
│ │ • Agent (HTTP to agent) │ │ • SMTP alert emails │ │ │ │ • Agent (HTTP to agent) │ │ • Input validated (IP + │ │
│ │ • New server init: ensure │ └────────────────────────────┘ │ │ • New server init: ensure │ │ identifiers sanitized) │
│ │ │ │ • SMTP alert emails │ │
│ │ │ └────────────────────────────┘ │
│ │ action.d (ui-custom- │ │ │ │ action.d (ui-custom- │ │
│ │ action.conf) │ │ │ │ action.conf) │ │
│ └────────────────────────────┘ │ │ └────────────────────────────┘ │

View File

@@ -12,6 +12,24 @@ This project can perform security-sensitive operations (bans, configuration chan
If you must publish it, put it behind TLS and an authentication layer, and restrict source IPs. If you must publish it, put it behind TLS and an authentication layer, and restrict source IPs.
## Input validation
All user-supplied IP addresses are validated using Go's `net.ParseIP` and `net.ParseCIDR` before they are passed to any integration, command, or database query. This applies to:
- Ban/Unban callbacks (`/api/ban`, `/api/unban`)
- Manual ban/unban actions from the dashboard
- Advanced action test endpoint (`/api/advanced-actions/test`)
- All integration connectors (MikroTik, pfSense, OPNsense)
Integration-specific identifiers (address list names, alias names) are validated against a strict alphanumeric pattern (`[a-zA-Z0-9._-]`) to prevent injection in both SSH commands and API payloads.
## WebSocket security
The WebSocket endpoint (`/api/ws`) is protected by:
- **Origin validation**: The upgrade handshake verifies that the `Origin` header matches the request's `Host` header (same-origin policy). Cross-origin WebSocket connections are rejected. This prevents cross-site WebSocket hijacking attacks.
- **Authentication**: When OIDC is enabled, the WebSocket endpoint requires a valid session.
## Callback endpoint protection ## Callback endpoint protection
The fail2ban callback endpoints (`/api/ban`, `/api/unban`) are only reachable with a correct `CALLBACK_SECRET`. This secret must be atleast 20 characters long. If not specified a secure secret, will be automatically genereated on first start. It can be further protected by: The fail2ban callback endpoints (`/api/ban`, `/api/unban`) are only reachable with a correct `CALLBACK_SECRET`. This secret must be atleast 20 characters long. If not specified a secure secret, will be automatically genereated on first start. It can be further protected by:
@@ -30,6 +48,14 @@ For SSH-managed hosts:
- Restrict sudo to the minimum set of commands required to operate Fail2Ban (typically `fail2ban-client` and optionally `systemctl restart fail2ban`). - Restrict sudo to the minimum set of commands required to operate Fail2Ban (typically `fail2ban-client` and optionally `systemctl restart fail2ban`).
- Use filesystem ACLs for `/etc/fail2ban` rather than broad permissions to allow full modification capabilities for the specific user. - Use filesystem ACLs for `/etc/fail2ban` rather than broad permissions to allow full modification capabilities for the specific user.
## Integration connector hardening
When using external firewall integrations (MikroTik, pfSense, OPNsense):
- Use a dedicated service account on the firewall device with the minimum permissions needed (address-list management only on MikroTik; alias management only on pfSense/OPNsense).
- For pfSense/OPNsense: use a dedicated API token with limited scope.
- Restrict network access so the Fail2ban-UI host is the only source allowed to reach the firewall management interface.
## Least privilege and file access ## Least privilege and file access
Local connector deployments typically require access to: Local connector deployments typically require access to:

View File

@@ -65,6 +65,195 @@ getfacl /etc/fail2ban
sudo podman exec -it fail2ban-ui ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o BatchMode=yes -i /config/.ssh/id_rsa -p 2222 testuser@127.0.0.1 sudo podman exec -it fail2ban-ui ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o BatchMode=yes -i /config/.ssh/id_rsa -p 2222 testuser@127.0.0.1
``` ```
## Ban/unban notifications not showing up in the UI
This is one of the most common issues. The UI receives ban/unban events from Fail2Ban via HTTP callbacks. If nothing appears in the dashboard or "Recent stored events", the callback chain is broken somewhere. Follow these steps systematically.
### Step 1: Verify the action file exists and is correct
Fail2ban-UI creates a custom action file at `/etc/fail2ban/action.d/ui-custom-action.conf` on each managed host. This file contains `curl` commands that notify the UI when bans/unbans happen.
```bash
# Check if the action file exists:
cat /etc/fail2ban/action.d/ui-custom-action.conf
# You should see actionban and actionunban sections with curl commands pointing
# to your Fail2ban-UI callback URL (e.g. http://10.88.0.1:8080/api/ban)
```
If the file does not exist or looks wrong, go to Settings → Manage Servers in the UI, select the server, and click "Test connection". The UI will re-deploy the action file automatically for local connectors.
### Step 2: Verify jail.local references the action
Fail2ban-UI writes a `jail.local` that uses the custom action. Check that it is in place:
```bash
cat /etc/fail2ban/jail.local | head -30
# Look for the lines like:
# action = %(action_mwlg)s
# and a definition of action_mwlg that references ui-custom-action
```
If your `jail.local` was created manually or by another tool, the `ui-custom-action` might not be referenced. The easiest fix: let the UI manage `jail.local` by removing your manual version and restarting from the UI.
### Step 3: Check network connectivity from Fail2Ban host to the UI
The `curl` command in the action file must be able to reach the UI's callback URL. Test this from the Fail2Ban host (or from inside the container if Fail2Ban runs in one):
```bash
# Replace with your actual Fail2ban-UI address:
curl -s -o /dev/null -w "%{http_code}" http://10.88.0.1:8080/api/version
# Expected: 200
# If you get connection refused, timeout, or another error,
# fix network/firewall rules first.
```
Common issues:
- Container using bridge networking but callback URL points to `127.0.0.1` (use the host IP or `--network=host`)
- Firewall on the UI host blocks the port
### Step 4: Verify the callback secret
Every callback must include the header `X-Callback-Secret`. The value must match what the UI expects. You can find the current secret in Settings → General Settings → Callback Secret (or check the container environment).
```bash
# Check what secret the action file uses:
grep "X-Callback-Secret" /etc/fail2ban/action.d/ui-custom-action.conf
# Compare with the UI's expected secret (from the settings page or env var)
```
If they do not match, re-deploy the action file via "Test connection" from the UI, or manually update the secret in the action file and restart Fail2Ban.
### Step 5: Simulate a ban notification with curl
This is the most direct way to test the full callback chain. Run this from any host that can reach the UI:
```bash
FAIL2BAN_UI_HOST="your_fail2ban_host"
SECRET="your_secret"
curl -v -X POST http://$FAIL2BAN_UI_HOST:8080/api/ban \
-H "Content-Type: application/json" \
-H "X-Callback-Secret: $SECRET" \
-d '{
"serverId": "local",
"ip": "203.0.113.42",
"jail": "sshd",
"hostname": "testhost",
"failures": "5",
"logs": "Jun 15 12:00:00 testhost sshd: Failed password for root from 203.0.113.42"
}'
```
Expected response:
```json
{"message":"Ban notification processed successfully"}
```
If it works, you should immediately see:
- A new entry in "Recent stored events" on the dashboard
- A real-time WebSocket update (the entry appears without refreshing)
Common error responses:
- `401 Unauthorized` with `"Callback secret not configured"` → Secret not set in UI settings
- `401 Unauthorized` with `"Invalid callback secret"` → Secret mismatch
- `400 Bad Request` with `"invalid IP"` → The IP address in the payload is malformed
- `400 Bad Request` with `"Invalid request"` → JSON parsing failed (check `ip` and `jail` fields are present)
To simulate an unban:
```bash
curl -v -X POST http://$FAIL2BAN_UI_HOST:8080/api/unban \
-H "Content-Type: application/json" \
-H "X-Callback-Secret: $SECRET" \
-d '{
"serverId": "local",
"ip": "203.0.113.42",
"jail": "sshd",
"hostname": "testhost"
}'
```
### Step 6: Check what Fail2Ban is actually sending
If the curl test above works but real bans still don't show up, Fail2Ban itself might not be executing the action correctly. Check:
```bash
# Trigger a real ban (use a test jail or ban a test IP):
fail2ban-client set sshd banip 198.51.100.1
# Watch the Fail2Ban log for errors:
tail -f /var/log/fail2ban.log
# Look for lines like:
# ERROR ... Action ... failed
# WARNING ... Command ... failed
```
You can also manually run the exact `curl` command from the action file to see what happens. Extract it from the action file and run it in your shell (replace the Fail2Ban variables like `<ip>`, `<name>`, etc. with real values):
```bash
# Extract and run the actionban command manually:
grep -A5 "actionban" /etc/fail2ban/action.d/ui-custom-action.conf
# Then execute the curl command with real values substituted.
# This reveals whether jq is missing, curl has TLS issues, etc.
```
Common issues at this stage:
- **`jq` not installed**: The action file uses `jq` to build JSON. Install it: `dnf install jq` or `apt install jq`
- **TLS certificate issues**: If the callback URL uses HTTPS with a self-signed cert, the action file needs the `-k` flag (Fail2ban-UI adds this automatically when the callback URL starts with `https://`)
- **Fail2Ban not restarted**: After the action file is deployed, Fail2Ban must be restarted to pick up changes: `systemctl restart fail2ban`
### Step 7: Check the Fail2ban-UI logs
The UI logs every incoming callback with details. Check the container or service logs:
```bash
# Container:
podman logs -f fail2ban-ui
# systemd:
journalctl -u fail2ban-ui -f
# Look for lines like:
# ✅ Parsed ban request - IP: ..., Jail: ...
# ⚠️ Invalid callback secret ...
# ❌ JSON parsing error ...
```
If you enabled debug mode in the UI settings, you will also see the raw JSON body of every incoming callback.
### Step 8: Verify the `serverId` resolves
The callback payload includes a `serverId`. The UI uses this to match the event to a configured server. If neither matches any known server, the UI will reject the callback.
Check that the `serverId` in the action file matches the server ID shown in Settings → Manage Servers. You can see the configured server IDs via:
```bash
curl -s http://$FAIL2BAN_UI_HOST:8080/api/servers \
-H "X-F2B-Server: default" | jq '.servers[] | {id, name, hostname}'
```
### Quick reference: end-to-end callback flow
```
Fail2Ban detects intrusion
→ triggers actionban in ui-custom-action.conf
→ curl POST /api/ban with JSON payload + X-Callback-Secret header
→ Fail2ban-UI validates secret
→ Fail2ban-UI validates IP format
→ Fail2ban-UI resolves server (by serverId)
→ Stores event in SQLite (ban_events table)
→ Broadcasts via WebSocket to all connected browsers
→ Optional: sends email alert, evaluates advanced actions
```
If any step fails, the chain stops and the event will not appear in the UI.
## Bans fail due to firewall backend (nftables / firewalld) ## Bans fail due to firewall backend (nftables / firewalld)
Symptoms often mention `iptables (nf_tables)` or action startup errors. Symptoms often mention `iptables (nf_tables)` or action startup errors.
@@ -92,6 +281,34 @@ podman logs fail2ban-ui
# Also enable debug logging over env or over the webUI # Also enable debug logging over env or over the webUI
``` ```
## WebSocket not connecting
If the real-time dashboard updates (ban/unban events appearing without page refresh) are not working:
Check:
* Browser console for WebSocket errors (F12 → Console tab)
* The WebSocket status indicator in the UI footer
* If using a reverse proxy, ensure it supports WebSocket upgrades
Common issues:
* **Reverse proxy not forwarding WebSocket**: Nginx requires explicit WebSocket upgrade configuration:
```nginx
location /api/ws {
proxy_pass http://127.0.0.1:8080;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
```
* **Origin mismatch**: The WebSocket endpoint validates that the `Origin` header matches the `Host` header. If your reverse proxy rewrites the `Host` header but not the `Origin`, the connection will be rejected. Ensure both headers are consistent.
* **OIDC session expired**: When OIDC is enabled, the WebSocket requires a valid session. If the session expires, the WebSocket connection will fail with a 302 redirect or 401 error. Re-login to the UI to fix this.
## Database issues ## Database issues
Check: Check: