Skip to content
This repository was archived by the owner on Apr 26, 2024. It is now read-only.

Commit 394be6a

Browse files
authored
Merge pull request #8008 from matrix-org/erikj/add_rate_limiting_to_joins
Add ratelimiting on joins
2 parents e2a4ba6 + faba873 commit 394be6a

File tree

5 files changed

+73
-2
lines changed

5 files changed

+73
-2
lines changed

changelog.d/8008.feature

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Add rate limiting to users joining rooms.

docs/sample_config.yaml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -746,6 +746,10 @@ log_config: "CONFDIR/SERVERNAME.log.config"
746746
# - one for ratelimiting redactions by room admins. If this is not explicitly
747747
# set then it uses the same ratelimiting as per rc_message. This is useful
748748
# to allow room admins to deal with abuse quickly.
749+
# - two for ratelimiting number of rooms a user can join, "local" for when
750+
# users are joining rooms the server is already in (this is cheap) vs
751+
# "remote" for when users are trying to join rooms not on the server (which
752+
# can be more expensive)
749753
#
750754
# The defaults are as shown below.
751755
#
@@ -771,6 +775,14 @@ log_config: "CONFDIR/SERVERNAME.log.config"
771775
#rc_admin_redaction:
772776
# per_second: 1
773777
# burst_count: 50
778+
#
779+
#rc_joins:
780+
# local:
781+
# per_second: 0.1
782+
# burst_count: 3
783+
# remote:
784+
# per_second: 0.01
785+
# burst_count: 3
774786

775787

776788
# Ratelimiting settings for incoming federation

synapse/config/ratelimiting.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,15 @@ def read_config(self, config, **kwargs):
9393
if rc_admin_redaction:
9494
self.rc_admin_redaction = RateLimitConfig(rc_admin_redaction)
9595

96+
self.rc_joins_local = RateLimitConfig(
97+
config.get("rc_joins", {}).get("local", {}),
98+
defaults={"per_second": 0.1, "burst_count": 3},
99+
)
100+
self.rc_joins_remote = RateLimitConfig(
101+
config.get("rc_joins", {}).get("remote", {}),
102+
defaults={"per_second": 0.01, "burst_count": 3},
103+
)
104+
96105
def generate_config_section(self, **kwargs):
97106
return """\
98107
## Ratelimiting ##
@@ -118,6 +127,10 @@ def generate_config_section(self, **kwargs):
118127
# - one for ratelimiting redactions by room admins. If this is not explicitly
119128
# set then it uses the same ratelimiting as per rc_message. This is useful
120129
# to allow room admins to deal with abuse quickly.
130+
# - two for ratelimiting number of rooms a user can join, "local" for when
131+
# users are joining rooms the server is already in (this is cheap) vs
132+
# "remote" for when users are trying to join rooms not on the server (which
133+
# can be more expensive)
121134
#
122135
# The defaults are as shown below.
123136
#
@@ -143,6 +156,14 @@ def generate_config_section(self, **kwargs):
143156
#rc_admin_redaction:
144157
# per_second: 1
145158
# burst_count: 50
159+
#
160+
#rc_joins:
161+
# local:
162+
# per_second: 0.1
163+
# burst_count: 3
164+
# remote:
165+
# per_second: 0.01
166+
# burst_count: 3
146167
147168
148169
# Ratelimiting settings for incoming federation

synapse/handlers/room_member.py

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@
2222

2323
from synapse import types
2424
from synapse.api.constants import MAX_DEPTH, EventTypes, Membership
25-
from synapse.api.errors import AuthError, Codes, SynapseError
25+
from synapse.api.errors import AuthError, Codes, LimitExceededError, SynapseError
26+
from synapse.api.ratelimiting import Ratelimiter
2627
from synapse.api.room_versions import EventFormatVersions
2728
from synapse.crypto.event_signing import compute_event_reference_hash
2829
from synapse.events import EventBase
@@ -77,6 +78,17 @@ def __init__(self, hs):
7778
if self._is_on_event_persistence_instance:
7879
self.persist_event_storage = hs.get_storage().persistence
7980

81+
self._join_rate_limiter_local = Ratelimiter(
82+
clock=self.clock,
83+
rate_hz=hs.config.ratelimiting.rc_joins_local.per_second,
84+
burst_count=hs.config.ratelimiting.rc_joins_local.burst_count,
85+
)
86+
self._join_rate_limiter_remote = Ratelimiter(
87+
clock=self.clock,
88+
rate_hz=hs.config.ratelimiting.rc_joins_remote.per_second,
89+
burst_count=hs.config.ratelimiting.rc_joins_remote.burst_count,
90+
)
91+
8092
# This is only used to get at ratelimit function, and
8193
# maybe_kick_guest_users. It's fine there are multiple of these as
8294
# it doesn't store state.
@@ -441,7 +453,28 @@ async def _update_membership(
441453
# so don't really fit into the general auth process.
442454
raise AuthError(403, "Guest access not allowed")
443455

444-
if not is_host_in_room:
456+
if is_host_in_room:
457+
time_now_s = self.clock.time()
458+
allowed, time_allowed = self._join_rate_limiter_local.can_do_action(
459+
requester.user.to_string(),
460+
)
461+
462+
if not allowed:
463+
raise LimitExceededError(
464+
retry_after_ms=int(1000 * (time_allowed - time_now_s))
465+
)
466+
467+
else:
468+
time_now_s = self.clock.time()
469+
allowed, time_allowed = self._join_rate_limiter_remote.can_do_action(
470+
requester.user.to_string(),
471+
)
472+
473+
if not allowed:
474+
raise LimitExceededError(
475+
retry_after_ms=int(1000 * (time_allowed - time_now_s))
476+
)
477+
445478
inviter = await self._get_inviter(target.to_string(), room_id)
446479
if inviter and not self.hs.is_mine(inviter):
447480
remote_room_hosts.append(inviter.domain)

tests/utils.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,10 @@ def default_config(name, parse=False):
154154
"account": {"per_second": 10000, "burst_count": 10000},
155155
"failed_attempts": {"per_second": 10000, "burst_count": 10000},
156156
},
157+
"rc_joins": {
158+
"local": {"per_second": 10000, "burst_count": 10000},
159+
"remote": {"per_second": 10000, "burst_count": 10000},
160+
},
157161
"saml2_enabled": False,
158162
"public_baseurl": None,
159163
"default_identity_server": None,

0 commit comments

Comments
 (0)