fix(settings): trust reverse proxy for client IP in rate limiting#1813
Open
miaulalala wants to merge 1 commit into
Open
fix(settings): trust reverse proxy for client IP in rate limiting#1813miaulalala wants to merge 1 commit into
miaulalala wants to merge 1 commit into
Conversation
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>
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