fix(settings): trust reverse proxy for client IP in rate limiting - #1813
Merged
Conversation
miaulalala
requested review from
SystemKeeper,
edward-ly,
marcelklehr and
oleksandr-nc
July 7, 2026 07:21
edward-ly
approved these changes
Jul 15, 2026
edward-ly
force-pushed
the
fix/login-ratelimit-trusted-proxy
branch
from
July 15, 2026 04:17
06a7bfb to
f103e4c
Compare
Apache/mod_wsgi logs show every request's remote address as 127.0.0.1, meaning a reverse proxy sits in front of it. allauth's rate limiter (used by ACCOUNT_RATE_LIMITS["login_failed"]) only reads the real client IP from X-Forwarded-For when ALLAUTH_TRUSTED_PROXY_COUNT > 0; left unset, it fell back to REMOTE_ADDR, i.e. the proxy's own address for every visitor. That collapsed the per-IP login_failed limit into a single site-wide bucket of 10 failed attempts per hour shared by every user, so once exhausted (typos, scanners, credential stuffing), regular username/password login was rejected for everyone until the hour rolled over -- while GitHub OAuth, a separate code path with no rate limiter, kept working. This matches reports of 'GitHub login works but regular login doesn't'. Set ALLAUTH_TRUSTED_PROXY_COUNT = 1 so allauth resolves the real client IP from the last hop of X-Forwarded-For instead of REMOTE_ADDR, restoring per-visitor rate limiting. Pending sysadmin confirmation that there is exactly one reverse proxy hop in front of Apache, that it sets X-Forwarded-For itself (rather than forwarding a client-supplied value), and that Apache is never reachable bypassing that proxy -- required for this setting to be safe. Signed-off-by: Anna Larch <anna@nextcloud.com>
edward-ly
force-pushed
the
fix/login-ratelimit-trusted-proxy
branch
from
July 15, 2026 04:26
f103e4c to
90401db
Compare
2 tasks
pull Bot
pushed a commit
to Zezo-Ai/Zezo-Ai-next-appstore
that referenced
this pull request
Jul 21, 2026
copy-webpack-plugin@14 requires Node >= 20.9.0 (uses Array.prototype.toSorted() internally), but the production server runs Node 18.19.1, so the production build fails with "TypeError: [...].toSorted is not a function". This blocks deployment of nextcloud#1813, which fixes app store login/password-reset. Signed-off-by: Anna Larch <anna@nextcloud.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
mod_wsgilogs show every request'sREMOTE_ADDRas127.0.0.1— there's a reverse proxy in front of Apache that isn't being accounted for by the app.django-allauth's rate limiter (backingACCOUNT_RATE_LIMITS["login_failed"]) resolves the client IP viaallauth.core.internal.httpkit.get_client_ip(), which only trustsX-Forwarded-ForwhenALLAUTH_TRUSTED_PROXY_COUNT > 0. That setting wasn't set anywhere, so it fell back toREMOTE_ADDR— i.e. the proxy's own address for every visitor.login_failed: "10/h/ip"limit collapsed into a single site-wide bucket of 10 failed password attempts per hour, shared by every user. Once exhausted (typos, scanners, credential stuffing — trivial to trigger), every regular username/password login got rejected with 429 until the hour rolled over, while GitHub OAuth — a separate code path with no rate limiter — kept working. This matches reports of "GitHub login works, regular login doesn't."X-Forwarded-Foris reliably populated with the real client IP in this deployment (an existing ops-side logging filter forbad_requests.log/fail2ban already depends on it).ALLAUTH_TRUSTED_PROXY_COUNT = 1so allauth resolves the real client IP from the trusted hop ofX-Forwarded-Forinstead ofREMOTE_ADDR, restoring per-visitor rate limiting.Test plan
python -c "... from allauth import app_settings; print(app_settings.TRUSTED_PROXY_COUNT)"→ confirms1is picked up from settings.🤖 Generated with Claude Code