Files
fail2ban-webui/docs/authentication.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

120 lines
5.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Authentication
`AUTH_MODE` selects how users sign in. The container refuses to start without a working
configuration – there is no unauthenticated mode.
| Mode | Use when |
|---|---|
| `local` | one shared admin account is enough |
| `oidc` | you have an identity provider (Authentik, Keycloak, Authelia, Zitadel, …) |
| `local,oidc` | SSO with a local fallback account |
| `proxy` | your reverse proxy already authenticates (forward auth, basic auth, IP allow list) |
Every change (ban/unban) additionally needs the per-session CSRF token and a matching
`Origin` header, and every action is written to the audit log with the user name.
## Local user
```bash
docker compose run --rm -it fail2ban-webui hash-password
```
```ini
AUTH_MODE=local
AUTH_USER=admin
AUTH_PASSWORD_HASH='scrypt:32768:8:1$…'
```
- Passwords need at least 12 characters; the hash is scrypt (Werkzeug).
- After `LOGIN_MAX_ATTEMPTS` (5) failures a client IP is locked for `LOGIN_LOCK_MINUTES` (15).
- Failed logins are logged as `LOGIN_FAILED user=… ip=…` – see
[operations.md](operations.md#protecting-the-login-with-fail2ban) to let fail2ban ban
them at the proxy.
- Set `TRUSTED_PROXY_HOPS=1` behind a reverse proxy, otherwise all clients appear with the
proxy's address and share one lockout counter.
## OpenID Connect
Requirements: `PUBLIC_URL` is set to the external `https://` URL, and the provider allows
the redirect URI **`<PUBLIC_URL>/auth/callback`**.
The UI uses the authorization code flow with PKCE (`S256`) and a confidential client.
User name: `OIDC_USERNAME_CLAIM` (default `preferred_username`); groups:
`OIDC_GROUPS_CLAIM` (default `groups`).
**Always restrict who may sign in** with `OIDC_ALLOWED_GROUPS` and/or
`OIDC_ALLOWED_USERS` – or with an access policy in the provider. Without either, every
account that the provider lets use this client can ban IPs (a warning is logged at start).
### Authentik
1. **Applications → Providers → Create → OAuth2/OpenID Provider**
- Name: `fail2ban-webui`
- Authorization flow: `default-provider-authorization-explicit-consent` (or implicit)
- Client type: **Confidential** – note *Client ID* and *Client Secret*
- Redirect URIs: `strict` · `https://fail2ban.example.org/auth/callback`
- Signing key: any certificate (e.g. `authentik Self-signed Certificate`)
- Advanced protocol settings → Scopes: `openid`, `email`, `profile`
(the default *profile* mapping contains the `groups` claim)
2. **Applications → Applications → Create**
- Name: `fail2ban-webui`, Slug: `fail2ban-webui`, Provider: the one from step 1
3. **Directory → Groups → Create** `fail2ban-admins` and add the users.
4. Optional, recommended: in the application under **Policy / Group / User Bindings**
bind the group `fail2ban-admins`, so nobody else even reaches the consent screen.
5. `.env`:
```ini
PUBLIC_URL=https://fail2ban.example.org
AUTH_MODE=oidc
OIDC_ISSUER=https://auth.example.org/application/o/fail2ban-webui/
OIDC_CLIENT_ID=…
OIDC_CLIENT_SECRET='…'
OIDC_ALLOWED_GROUPS=fail2ban-admins
OIDC_BUTTON_LABEL=Authentik
```
6. `docker compose up -d`, open `PUBLIC_URL`, click **Sign in with Authentik**.
The issuer URL is shown in Authentik on the provider page (*OpenID Configuration Issuer*).
### Other providers
| Provider | `OIDC_ISSUER` | Groups |
|---|---|---|
| Keycloak | `https://kc.example.org/realms/<realm>` | add a *Group Membership* mapper with claim name `groups`, "Full group path" off |
| Authelia | `https://auth.example.org` | `groups` scope; set `OIDC_SCOPES=openid email profile groups` |
| Zitadel | `https://<instance>.zitadel.cloud` | roles instead of groups – use `OIDC_ALLOWED_USERS` or a project role check in Zitadel |
### Sign out
"Sign out" ends the session in the web UI. The session at the provider stays active, so
the next "Sign in with …" usually logs in again without a password.
## Reverse-proxy mode
The proxy does the authentication; the UI only checks where requests come from and,
optionally, which user the proxy reports.
```ini
AUTH_MODE=proxy
TRUSTED_PROXY_HOPS=1
# only accept requests from the proxy (address as seen by the container)
AUTH_PROXY_ALLOWED_IPS=172.23.51.1/32
# optional: user name for the audit log, set by the proxy
AUTH_PROXY_HEADER=X-authentik-username
# optional: logout URL of the proxy/IdP
AUTH_PROXY_LOGOUT_URL=https://fail2ban.example.org/outpost.goauthentik.io/sign_out
```
- `AUTH_PROXY_ALLOWED_IPS` is checked against the **direct** peer of the container, not
against `X-Forwarded-For`. If the proxy runs on the same Docker host and reaches the
container via the published port, that is the network gateway (`172.23.51.1` in the
example compose file); check with `docker compose logs` after a request.
- With `AUTH_PROXY_HEADER` set, requests without the header are rejected (401). Make sure
the proxy **removes** that header from client requests – forward-auth integrations
(Authentik, Authelia) do that.
- Without `AUTH_PROXY_HEADER` the audit log shows the user `proxy`.
Examples for Nginx Proxy Manager, Traefik and Caddy are in
[reverse-proxy.md](reverse-proxy.md).