From 3a64e27dc09c8fdd3a8da43eebee055b933fbcf0 Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Mon, 26 Oct 2026 20:10:36 +0100 Subject: [PATCH] docs: describe the Stalwart settings that make it read the forwarded client address --- lib/auth/client-address.ts | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/lib/auth/client-address.ts b/lib/auth/client-address.ts index a27856950..d3eac4793 100644 --- a/lib/auth/client-address.ts +++ b/lib/auth/client-address.ts @@ -6,10 +6,15 @@ import { getClientIP } from '@/lib/admin/session'; * host's address. Stalwart counts failed logins per address (fail2ban) and * bans the address once the allowance is spent, so wrong passwords typed by * or fired at the webmail would eventually lock the webmail itself out for - * everyone. Stalwart reads `X-Forwarded-For` from connections that come from - * a configured trusted network (`server.proxy.trusted-networks`), so handing - * it the real client address lets it attribute the failure to that client - * instead. A server that does not trust this host ignores the header. + * everyone. Handing Stalwart the real client address lets it attribute the + * failure to that client instead, once the admin tells it to read the header: + * on 0.16 `Http.useXForwarded` (it then prefers a `Forwarded` header and + * otherwise takes the first `X-Forwarded-For` entry), on 1.0 + * `Http.clientIpSource` = `xForwardedFor` with `forwardedHops` = 1 (the last + * entry; more when proxies between the webmail and Stalwart append their + * own). Neither checks where the connection comes from, so only enable it + * when every HTTP client of Stalwart goes through the webmail or a proxy + * that sets the header. Left at its default, Stalwart ignores the header. * * Only a syntactically valid address is forwarded: the client-facing value * comes from the request's own `X-Forwarded-For` when no reverse proxy