1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
|
"""
Rate limits are per client, not per proxy.
meshbay.org's hub sits behind Caddy on the same host, so every request's TCP peer
is loopback. The limiter was keyed on that peer (slowapi's get_remote_address)
while `client_ip` -- written "for the audit log and rate limiting" -- went
unused by it, so each limit was one bucket for the whole internet: ten node
sign-ins a minute shared by every node. A node that started while others signed
in got 429, sat in `waiting_for_hub`, and the desktop app took that for no node
at all. Every other test runs with the limiter disabled, which is how it hid.
"""
import pytest
from meshbay_hub.api.middleware import limiter
from meshbay_hub.api.netutil import client_ip
@pytest.fixture
def limits_on():
limiter.reset()
limiter.enabled = True
yield
limiter.enabled = False
limiter.reset()
def _auth(client, ip):
# The ASGI test transport's peer is 127.0.0.1 -- a trusted proxy, as Caddy is.
return client.post("/v1/nodes/auth",
json={"username": "nobody", "timestamp": 0, "signature": "AAAA"},
headers={"X-Forwarded-For": ip})
async def test_one_client_is_limited(client, limits_on):
for _ in range(10):
assert (await _auth(client, "203.0.113.1")).status_code != 429
assert (await _auth(client, "203.0.113.1")).status_code == 429
async def test_another_client_behind_the_same_proxy_is_not(client, limits_on):
for _ in range(11):
await _auth(client, "203.0.113.1")
assert (await _auth(client, "203.0.113.2")).status_code != 429, (
"a second client behind the proxy shared the first one's bucket")
def test_the_limiter_resolves_clients_the_way_the_audit_log_does():
assert limiter._key_func is client_ip
|