Add rate limit conf to user directory endpoint (#19291)
Some checks are pending
Build docker images / Build and push image for linux/arm64 (push) Waiting to run
Build docker images / Push merged images to docker.io/matrixdotorg/synapse (push) Blocked by required conditions
Deploy the documentation / Calculate variables for GitHub Pages deployment (push) Waiting to run
Deploy the documentation / GitHub Pages (push) Blocked by required conditions
Build release artifacts / Calculate list of debian distros (push) Waiting to run
Build release artifacts / Build .deb packages (push) Blocked by required conditions
Build release artifacts / Build sdist (push) Waiting to run
Schema / Ensure Synapse config schema is valid (push) Waiting to run
Schema / Ensure generated documentation is up-to-date (push) Waiting to run
Tests / lint-clippy (push) Blocked by required conditions
Tests / lint-clippy-nightly (push) Blocked by required conditions
Tests / cargo-bench (push) Blocked by required conditions
Tests / tests-done (push) Blocked by required conditions
Tests / check-sampleconfig (push) Blocked by required conditions
Tests / check-schema-delta (push) Blocked by required conditions
Tests / check-lockfile (push) Waiting to run
Tests / lint-rustfmt (push) Blocked by required conditions
Tests / linting-done (push) Blocked by required conditions
Tests / calculate-test-jobs (push) Blocked by required conditions
Tests / trial-olddeps (push) Blocked by required conditions
Tests / trial-pypy (all, pypy-3.10) (push) Blocked by required conditions
Tests / sytest (push) Blocked by required conditions
Build docker images / Build and push image for linux/amd64 (push) Waiting to run
Build docker images / Push merged images to ghcr.io/element-hq/synapse (push) Blocked by required conditions
Build release artifacts / Build wheels on ubuntu-24.04 (push) Waiting to run
Build release artifacts / Build wheels on ubuntu-24.04-arm (push) Waiting to run
Build release artifacts / Attach assets to release (push) Blocked by required conditions
Tests / cargo-test (push) Blocked by required conditions
Tests / changes (push) Waiting to run
Tests / lint (push) Blocked by required conditions
Tests / Typechecking (push) Blocked by required conditions
Tests / lint-crlf (push) Waiting to run
Tests / lint-newsfile (push) Waiting to run
Tests / lint-rust (push) Blocked by required conditions
Tests / lint-readme (push) Blocked by required conditions
Tests / trial (push) Blocked by required conditions
Tests / portdb (14, 3.10) (push) Blocked by required conditions
Tests / portdb (17, 3.14) (push) Blocked by required conditions
Tests / complement (monolith, Postgres) (push) Blocked by required conditions
Tests / complement (monolith, SQLite) (push) Blocked by required conditions
Tests / export-data (push) Blocked by required conditions
Tests / complement (workers, Postgres) (push) Blocked by required conditions

The goal is to avoid that an user could scrape the user directory too
quickly.
This commit is contained in:
Mathieu Velten 2026-01-05 20:35:11 +01:00 committed by GitHub
parent 6b755f964b
commit 444bc56cda
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 55 additions and 0 deletions

1
changelog.d/19291.misc Normal file
View file

@ -0,0 +1 @@
Add a config to be able to rate limit search in the user directory.

View file

@ -145,6 +145,12 @@ for port in 8080 8081 8082; do
rc_delayed_event_mgmt:
per_second: 1000
burst_count: 1000
rc_room_creation:
per_second: 1000
burst_count: 1000
rc_user_directory:
per_second: 1000
burst_count: 1000
RC
)
echo "${ratelimiting}" >> "$port.config"

View file

@ -102,6 +102,10 @@ rc_room_creation:
per_second: 9999
burst_count: 9999
rc_user_directory:
per_second: 9999
burst_count: 9999
federation_rr_transactions_per_room_per_second: 9999
allow_device_name_lookup_over_federation: true

View file

@ -2041,6 +2041,25 @@ rc_room_creation:
burst_count: 5.0
```
---
### `rc_user_directory`
*(object)* This option allows admins to ratelimit searches in the user directory.
_Added in Synapse 1.145.0._
This setting has the following sub-options:
* `per_second` (number): Maximum number of requests a client can send per second.
* `burst_count` (number): Maximum number of requests a client can send before being throttled.
Default configuration:
```yaml
rc_user_directory:
per_second: 0.016
burst_count: 200.0
```
---
### `federation_rr_transactions_per_room_per_second`
*(integer)* Sets outgoing federation transaction frequency for sending read-receipts, per-room.

View file

@ -2274,6 +2274,16 @@ properties:
examples:
- per_second: 1.0
burst_count: 5.0
rc_user_directory:
$ref: "#/$defs/rc"
description: >-
This option allows admins to ratelimit searches in the user directory.
_Added in Synapse 1.145.0._
default:
per_second: 0.016
burst_count: 200.0
federation_rr_transactions_per_room_per_second:
type: integer
description: >-

View file

@ -252,3 +252,9 @@ class RatelimitConfig(Config):
"rc_reports",
defaults={"per_second": 1, "burst_count": 5},
)
self.rc_user_directory = RatelimitSettings.parse(
config,
"rc_user_directory",
defaults={"per_second": 0.016, "burst_count": 200},
)

View file

@ -23,6 +23,7 @@ import logging
from typing import TYPE_CHECKING
from synapse.api.errors import SynapseError
from synapse.api.ratelimiting import Ratelimiter
from synapse.http.server import HttpServer
from synapse.http.servlet import RestServlet, parse_json_object_from_request
from synapse.http.site import SynapseRequest
@ -46,6 +47,12 @@ class UserDirectorySearchRestServlet(RestServlet):
self.auth = hs.get_auth()
self.user_directory_handler = hs.get_user_directory_handler()
self._per_user_limiter = Ratelimiter(
store=hs.get_datastores().main,
clock=hs.get_clock(),
cfg=hs.config.ratelimiting.rc_user_directory,
)
async def on_POST(self, request: SynapseRequest) -> tuple[int, JsonMapping]:
"""Searches for users in directory
@ -69,6 +76,8 @@ class UserDirectorySearchRestServlet(RestServlet):
if not self.hs.config.userdirectory.user_directory_search_enabled:
return 200, {"limited": False, "results": []}
await self._per_user_limiter.ratelimit(requester)
body = parse_json_object_from_request(request)
limit = int(body.get("limit", 10))