fix(auth): count per-network limits by the visitor behind Caddy

Every per-network limit counted by the connection's address, which behind
Caddy is Caddy's for everyone: new anonymous accounts, codes by mail and
password sign-ins were each limited for the whole instance.

`networkOf` (now `common/network.dart`) takes the last address of
X-Forwarded-For, the one the proxy saw itself, for a connection from a
proxy the operator trusts (`TRUSTED_PROXIES`, which deploy/compose.yaml
sets to Caddy's fixed address); otherwise, or where that is no address,
the connection's. Never the Forwarded header, which Caddy passes on as
sent, so never Serverpod's `remoteInfo`. A value that isn't addresses
stops the server as it starts.

Serverpod's own limit on anonymous accounts counts by the connection's
address too and can't be told otherwise, so it is off; the anonymous
endpoint counts them itself, 30 an hour per network as before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
Tizian 「ティツィアーン」 2026-10-20 14:09:27 +02:00
commit d9039d36ef
Signed by: raisondetredev
GPG key ID: 07F6FCF405488B64
2 changed files with 326 additions and 28 deletions

View file

@ -38,6 +38,10 @@
}
# Serverpod's streaming methods use WebSockets; reverse_proxy upgrades them as-is.
# Caddy puts the visitor's address last in X-Forwarded-For (what a visitor sends
# there it replaces), which the server's per-network limits count by, as
# TRUSTED_PROXIES names Caddy (compose.yaml); the server never reads the Forwarded
# header, which Caddy passes on as sent.
handle {
reverse_proxy server:8080
}

View file

@ -46,7 +46,8 @@ services:
networks:
edge:
# Fixed, so containers behind Caddy can trust it as their proxy (the
# visitor's own address comes in X-Forwarded-For).
# visitor's own address comes in X-Forwarded-For): the server's
# TRUSTED_PROXIES, Jellyfin's known proxies.
ipv4_address: 172.30.80.2
depends_on:
- server
@ -80,6 +81,10 @@ services:
SERVERPOD_PASSWORD_totpKey: ${TOTP_KEY:-}
SERVERPOD_PASSWORD_recoveryCodeKey: ${RECOVERY_CODE_KEY:-}
SERVERPOD_PASSWORD_signInDecoyKey: ${SIGN_IN_DECOY_KEY:-}
# Caddy, whose X-Forwarded-For names the visitor the per-network limits count
# (new anonymous accounts, codes by mail, password sign-ins): without it, every
# visitor would share each limit with everyone. Its address on `edge`, below.
TRUSTED_PROXIES: 172.30.80.2
# The day GitHub stops signing anyone in (`2027-01-22`); empty: no end.
GITHUB_SIGN_IN_ENDS: ${GITHUB_SIGN_IN_ENDS:-}
# Where the source of what this instance runs is; empty: the project's.