Bound what a request can make the server hold
Cap JSON bodies at 64 KB on every API route except JMAP and uploads, which bound themselves. Sign-in used to read a body of any size before its rate limits ran; the flood ceiling now also runs before the body is read. For sessions whose JMAP requests are checked, lower the read cap from 16 MB to 4 MB, allow four such reads per session at once, and turn requests away with a 503 once 32 MB is held across everyone. Count sign-in limits per /64 for IPv6, since one host holds a whole /64. Bind the compose example to loopback, and run it read-only with no capabilities and no-new-privileges. Keep .env.* out of git and the image build context.
This commit is contained in:
@@ -97,3 +97,21 @@ export function resolveClientIp(peer: string, headers: ForwardHeaders, cfg: Trus
|
||||
const real = headers.realIp?.trim();
|
||||
return real && isIP(real) !== 0 ? real : peer;
|
||||
}
|
||||
|
||||
/**
|
||||
* The key a rate limit counts an address under.
|
||||
*
|
||||
* An IPv4 address is the key as it is. An IPv6 address is cut to its /64: that
|
||||
* is the smallest block an ISP or a VPS hands out, so anyone who holds one
|
||||
* address holds 2^64 of them, and a limit keyed on the full address is no
|
||||
* limit. Everyone behind one /64 shares a budget, which is the same bargain an
|
||||
* IPv4 NAT already makes.
|
||||
*/
|
||||
export function rateLimitKey(ip: string): string {
|
||||
if (isIP(ip) !== 6) return ip;
|
||||
const bits = toBits(ip);
|
||||
if (!bits) return ip;
|
||||
const prefix = bits.value >> 64n;
|
||||
const groups = [48n, 32n, 16n, 0n].map((s) => ((prefix >> s) & 0xffffn).toString(16));
|
||||
return `${groups.join(":")}::/64`;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user