feat(6866): the proxy hop count is an admin setting, and the server shows what each count reads
CI & Build / Plugin hooks (push) Successful in 12s
CI & Build / Python lint (push) Successful in 2s
CI & Build / TypeScript typecheck (push) Successful in 32s
CI & Build / Python tests (push) Successful in 1m47s
CI & Build / integration (push) Successful in 2m1s
CI & Build / Build & push image (push) Successful in 20s
CI & Build / Plugin hooks (push) Successful in 12s
CI & Build / Python lint (push) Successful in 2s
CI & Build / TypeScript typecheck (push) Successful in 32s
CI & Build / Python tests (push) Successful in 1m47s
CI & Build / integration (push) Successful in 2m1s
CI & Build / Build & push image (push) Successful in 20s
An admin can save how many reverse proxies stand in front of Grimoire. A saved count wins over TRUSTED_PROXY_HOPS, which stays the starting value. Each worker loads it at startup (under a deadline) and re-reads it every 30s; the worker that saves applies it at once. GET/PUT /api/admin/proxy returns the count, where it came from, and a readout of the admin's own request: the socket peer, the forwarding chains, what counts 0-3 would read, and the count that reads Cloudflare's visitor when Cloudflare's headers are present. Saves are audit-logged. A "too many attempts" reply now says where to look when everyone is being blocked. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -2,15 +2,23 @@
|
||||
|
||||
IMPORTANT — deployment note:
|
||||
Rate limit counters are stored in memory and are lost on process restart.
|
||||
The bucket key is the client address from grimoire/proxy.py, read by
|
||||
TRUSTED_PROXY_HOPS. A hop count that is too low keys every caller on the
|
||||
proxy's address, so they all share one bucket.
|
||||
The bucket key is the client address from grimoire/proxy.py, read by the
|
||||
proxy hop count (Settings → Config). A count that is too low keys every
|
||||
caller on the proxy's address, so they all share one bucket.
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import time
|
||||
from collections import defaultdict
|
||||
|
||||
# Said beside every "too many attempts" reply, because the one case a person
|
||||
# can't diagnose on their own is everybody being blocked at once: the proxy
|
||||
# count is too low, so every visitor reads as the proxy and shares one bucket.
|
||||
SHARED_ADDRESS_HINT = (
|
||||
"If everyone is being blocked, an admin should check the reverse proxy count "
|
||||
"in Settings → Config: every visitor may be reading as the proxy's address."
|
||||
)
|
||||
|
||||
_buckets: dict[str, list[float]] = defaultdict(list)
|
||||
_lock = asyncio.Lock()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user