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

5.1 KiB
Raw Blame History

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

docker compose run --rm -it fail2ban-webui hash-password
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 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:

    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.

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.