mirror of
https://mau.dev/maunium/synapse.git
synced 2024-10-01 01:36:05 -04:00
Make all the rate limiting options more consistent (#5181)
This commit is contained in:
parent
5f027a315f
commit
f1e5b41388
1
changelog.d/5181.feature
Normal file
1
changelog.d/5181.feature
Normal file
@ -0,0 +1 @@
|
|||||||
|
Ratelimiting configuration for clients sending messages and the federation server has been altered to match login ratelimiting. The old configuration names will continue working. Check the sample config for details of the new names.
|
@ -446,21 +446,15 @@ log_config: "CONFDIR/SERVERNAME.log.config"
|
|||||||
|
|
||||||
## Ratelimiting ##
|
## Ratelimiting ##
|
||||||
|
|
||||||
# Number of messages a client can send per second
|
# Ratelimiting settings for client actions (registration, login, messaging).
|
||||||
#
|
|
||||||
#rc_messages_per_second: 0.2
|
|
||||||
|
|
||||||
# Number of message a client can send before being throttled
|
|
||||||
#
|
|
||||||
#rc_message_burst_count: 10.0
|
|
||||||
|
|
||||||
# Ratelimiting settings for registration and login.
|
|
||||||
#
|
#
|
||||||
# Each ratelimiting configuration is made of two parameters:
|
# Each ratelimiting configuration is made of two parameters:
|
||||||
# - per_second: number of requests a client can send per second.
|
# - per_second: number of requests a client can send per second.
|
||||||
# - burst_count: number of requests a client can send before being throttled.
|
# - burst_count: number of requests a client can send before being throttled.
|
||||||
#
|
#
|
||||||
# Synapse currently uses the following configurations:
|
# Synapse currently uses the following configurations:
|
||||||
|
# - one for messages that ratelimits sending based on the account the client
|
||||||
|
# is using
|
||||||
# - one for registration that ratelimits registration requests based on the
|
# - one for registration that ratelimits registration requests based on the
|
||||||
# client's IP address.
|
# client's IP address.
|
||||||
# - one for login that ratelimits login requests based on the client's IP
|
# - one for login that ratelimits login requests based on the client's IP
|
||||||
@ -473,6 +467,10 @@ log_config: "CONFDIR/SERVERNAME.log.config"
|
|||||||
#
|
#
|
||||||
# The defaults are as shown below.
|
# The defaults are as shown below.
|
||||||
#
|
#
|
||||||
|
#rc_message:
|
||||||
|
# per_second: 0.2
|
||||||
|
# burst_count: 10
|
||||||
|
#
|
||||||
#rc_registration:
|
#rc_registration:
|
||||||
# per_second: 0.17
|
# per_second: 0.17
|
||||||
# burst_count: 3
|
# burst_count: 3
|
||||||
@ -488,29 +486,28 @@ log_config: "CONFDIR/SERVERNAME.log.config"
|
|||||||
# per_second: 0.17
|
# per_second: 0.17
|
||||||
# burst_count: 3
|
# burst_count: 3
|
||||||
|
|
||||||
# The federation window size in milliseconds
|
|
||||||
#
|
|
||||||
#federation_rc_window_size: 1000
|
|
||||||
|
|
||||||
# The number of federation requests from a single server in a window
|
# Ratelimiting settings for incoming federation
|
||||||
# before the server will delay processing the request.
|
|
||||||
#
|
#
|
||||||
#federation_rc_sleep_limit: 10
|
# The rc_federation configuration is made up of the following settings:
|
||||||
|
# - window_size: window size in milliseconds
|
||||||
# The duration in milliseconds to delay processing events from
|
# - sleep_limit: number of federation requests from a single server in
|
||||||
# remote servers by if they go over the sleep limit.
|
# a window before the server will delay processing the request.
|
||||||
|
# - sleep_delay: duration in milliseconds to delay processing events
|
||||||
|
# from remote servers by if they go over the sleep limit.
|
||||||
|
# - reject_limit: maximum number of concurrent federation requests
|
||||||
|
# allowed from a single server
|
||||||
|
# - concurrent: number of federation requests to concurrently process
|
||||||
|
# from a single server
|
||||||
#
|
#
|
||||||
#federation_rc_sleep_delay: 500
|
# The defaults are as shown below.
|
||||||
|
|
||||||
# The maximum number of concurrent federation requests allowed
|
|
||||||
# from a single server
|
|
||||||
#
|
#
|
||||||
#federation_rc_reject_limit: 50
|
#rc_federation:
|
||||||
|
# window_size: 1000
|
||||||
# The number of federation requests to concurrently process from a
|
# sleep_limit: 10
|
||||||
# single server
|
# sleep_delay: 500
|
||||||
#
|
# reject_limit: 50
|
||||||
#federation_rc_concurrent: 3
|
# concurrent: 3
|
||||||
|
|
||||||
# Target outgoing federation transaction frequency for sending read-receipts,
|
# Target outgoing federation transaction frequency for sending read-receipts,
|
||||||
# per-room.
|
# per-room.
|
||||||
|
@ -16,16 +16,56 @@ from ._base import Config
|
|||||||
|
|
||||||
|
|
||||||
class RateLimitConfig(object):
|
class RateLimitConfig(object):
|
||||||
def __init__(self, config):
|
def __init__(self, config, defaults={"per_second": 0.17, "burst_count": 3.0}):
|
||||||
self.per_second = config.get("per_second", 0.17)
|
self.per_second = config.get("per_second", defaults["per_second"])
|
||||||
self.burst_count = config.get("burst_count", 3.0)
|
self.burst_count = config.get("burst_count", defaults["burst_count"])
|
||||||
|
|
||||||
|
|
||||||
|
class FederationRateLimitConfig(object):
|
||||||
|
_items_and_default = {
|
||||||
|
"window_size": 10000,
|
||||||
|
"sleep_limit": 10,
|
||||||
|
"sleep_delay": 500,
|
||||||
|
"reject_limit": 50,
|
||||||
|
"concurrent": 3,
|
||||||
|
}
|
||||||
|
|
||||||
|
def __init__(self, **kwargs):
|
||||||
|
for i in self._items_and_default.keys():
|
||||||
|
setattr(self, i, kwargs.get(i) or self._items_and_default[i])
|
||||||
|
|
||||||
|
|
||||||
class RatelimitConfig(Config):
|
class RatelimitConfig(Config):
|
||||||
|
|
||||||
def read_config(self, config):
|
def read_config(self, config):
|
||||||
self.rc_messages_per_second = config.get("rc_messages_per_second", 0.2)
|
|
||||||
self.rc_message_burst_count = config.get("rc_message_burst_count", 10.0)
|
# Load the new-style messages config if it exists. Otherwise fall back
|
||||||
|
# to the old method.
|
||||||
|
if "rc_message" in config:
|
||||||
|
self.rc_message = RateLimitConfig(
|
||||||
|
config["rc_message"], defaults={"per_second": 0.2, "burst_count": 10.0}
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
self.rc_message = RateLimitConfig(
|
||||||
|
{
|
||||||
|
"per_second": config.get("rc_messages_per_second", 0.2),
|
||||||
|
"burst_count": config.get("rc_message_burst_count", 10.0),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
# Load the new-style federation config, if it exists. Otherwise, fall
|
||||||
|
# back to the old method.
|
||||||
|
if "federation_rc" in config:
|
||||||
|
self.rc_federation = FederationRateLimitConfig(**config["rc_federation"])
|
||||||
|
else:
|
||||||
|
self.rc_federation = FederationRateLimitConfig(
|
||||||
|
**{
|
||||||
|
"window_size": config.get("federation_rc_window_size"),
|
||||||
|
"sleep_limit": config.get("federation_rc_sleep_limit"),
|
||||||
|
"sleep_delay": config.get("federation_rc_sleep_delay"),
|
||||||
|
"reject_limit": config.get("federation_rc_reject_limit"),
|
||||||
|
"concurrent": config.get("federation_rc_concurrent"),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
self.rc_registration = RateLimitConfig(config.get("rc_registration", {}))
|
self.rc_registration = RateLimitConfig(config.get("rc_registration", {}))
|
||||||
|
|
||||||
@ -33,38 +73,26 @@ class RatelimitConfig(Config):
|
|||||||
self.rc_login_address = RateLimitConfig(rc_login_config.get("address", {}))
|
self.rc_login_address = RateLimitConfig(rc_login_config.get("address", {}))
|
||||||
self.rc_login_account = RateLimitConfig(rc_login_config.get("account", {}))
|
self.rc_login_account = RateLimitConfig(rc_login_config.get("account", {}))
|
||||||
self.rc_login_failed_attempts = RateLimitConfig(
|
self.rc_login_failed_attempts = RateLimitConfig(
|
||||||
rc_login_config.get("failed_attempts", {}),
|
rc_login_config.get("failed_attempts", {})
|
||||||
)
|
)
|
||||||
|
|
||||||
self.federation_rc_window_size = config.get("federation_rc_window_size", 1000)
|
|
||||||
self.federation_rc_sleep_limit = config.get("federation_rc_sleep_limit", 10)
|
|
||||||
self.federation_rc_sleep_delay = config.get("federation_rc_sleep_delay", 500)
|
|
||||||
self.federation_rc_reject_limit = config.get("federation_rc_reject_limit", 50)
|
|
||||||
self.federation_rc_concurrent = config.get("federation_rc_concurrent", 3)
|
|
||||||
|
|
||||||
self.federation_rr_transactions_per_room_per_second = config.get(
|
self.federation_rr_transactions_per_room_per_second = config.get(
|
||||||
"federation_rr_transactions_per_room_per_second", 50,
|
"federation_rr_transactions_per_room_per_second", 50
|
||||||
)
|
)
|
||||||
|
|
||||||
def default_config(self, **kwargs):
|
def default_config(self, **kwargs):
|
||||||
return """\
|
return """\
|
||||||
## Ratelimiting ##
|
## Ratelimiting ##
|
||||||
|
|
||||||
# Number of messages a client can send per second
|
# Ratelimiting settings for client actions (registration, login, messaging).
|
||||||
#
|
|
||||||
#rc_messages_per_second: 0.2
|
|
||||||
|
|
||||||
# Number of message a client can send before being throttled
|
|
||||||
#
|
|
||||||
#rc_message_burst_count: 10.0
|
|
||||||
|
|
||||||
# Ratelimiting settings for registration and login.
|
|
||||||
#
|
#
|
||||||
# Each ratelimiting configuration is made of two parameters:
|
# Each ratelimiting configuration is made of two parameters:
|
||||||
# - per_second: number of requests a client can send per second.
|
# - per_second: number of requests a client can send per second.
|
||||||
# - burst_count: number of requests a client can send before being throttled.
|
# - burst_count: number of requests a client can send before being throttled.
|
||||||
#
|
#
|
||||||
# Synapse currently uses the following configurations:
|
# Synapse currently uses the following configurations:
|
||||||
|
# - one for messages that ratelimits sending based on the account the client
|
||||||
|
# is using
|
||||||
# - one for registration that ratelimits registration requests based on the
|
# - one for registration that ratelimits registration requests based on the
|
||||||
# client's IP address.
|
# client's IP address.
|
||||||
# - one for login that ratelimits login requests based on the client's IP
|
# - one for login that ratelimits login requests based on the client's IP
|
||||||
@ -77,6 +105,10 @@ class RatelimitConfig(Config):
|
|||||||
#
|
#
|
||||||
# The defaults are as shown below.
|
# The defaults are as shown below.
|
||||||
#
|
#
|
||||||
|
#rc_message:
|
||||||
|
# per_second: 0.2
|
||||||
|
# burst_count: 10
|
||||||
|
#
|
||||||
#rc_registration:
|
#rc_registration:
|
||||||
# per_second: 0.17
|
# per_second: 0.17
|
||||||
# burst_count: 3
|
# burst_count: 3
|
||||||
@ -92,29 +124,28 @@ class RatelimitConfig(Config):
|
|||||||
# per_second: 0.17
|
# per_second: 0.17
|
||||||
# burst_count: 3
|
# burst_count: 3
|
||||||
|
|
||||||
# The federation window size in milliseconds
|
|
||||||
#
|
|
||||||
#federation_rc_window_size: 1000
|
|
||||||
|
|
||||||
# The number of federation requests from a single server in a window
|
# Ratelimiting settings for incoming federation
|
||||||
# before the server will delay processing the request.
|
|
||||||
#
|
#
|
||||||
#federation_rc_sleep_limit: 10
|
# The rc_federation configuration is made up of the following settings:
|
||||||
|
# - window_size: window size in milliseconds
|
||||||
# The duration in milliseconds to delay processing events from
|
# - sleep_limit: number of federation requests from a single server in
|
||||||
# remote servers by if they go over the sleep limit.
|
# a window before the server will delay processing the request.
|
||||||
|
# - sleep_delay: duration in milliseconds to delay processing events
|
||||||
|
# from remote servers by if they go over the sleep limit.
|
||||||
|
# - reject_limit: maximum number of concurrent federation requests
|
||||||
|
# allowed from a single server
|
||||||
|
# - concurrent: number of federation requests to concurrently process
|
||||||
|
# from a single server
|
||||||
#
|
#
|
||||||
#federation_rc_sleep_delay: 500
|
# The defaults are as shown below.
|
||||||
|
|
||||||
# The maximum number of concurrent federation requests allowed
|
|
||||||
# from a single server
|
|
||||||
#
|
#
|
||||||
#federation_rc_reject_limit: 50
|
#rc_federation:
|
||||||
|
# window_size: 1000
|
||||||
# The number of federation requests to concurrently process from a
|
# sleep_limit: 10
|
||||||
# single server
|
# sleep_delay: 500
|
||||||
#
|
# reject_limit: 50
|
||||||
#federation_rc_concurrent: 3
|
# concurrent: 3
|
||||||
|
|
||||||
# Target outgoing federation transaction frequency for sending read-receipts,
|
# Target outgoing federation transaction frequency for sending read-receipts,
|
||||||
# per-room.
|
# per-room.
|
||||||
|
@ -63,11 +63,7 @@ class TransportLayerServer(JsonResource):
|
|||||||
self.authenticator = Authenticator(hs)
|
self.authenticator = Authenticator(hs)
|
||||||
self.ratelimiter = FederationRateLimiter(
|
self.ratelimiter = FederationRateLimiter(
|
||||||
self.clock,
|
self.clock,
|
||||||
window_size=hs.config.federation_rc_window_size,
|
config=hs.config.rc_federation,
|
||||||
sleep_limit=hs.config.federation_rc_sleep_limit,
|
|
||||||
sleep_msec=hs.config.federation_rc_sleep_delay,
|
|
||||||
reject_limit=hs.config.federation_rc_reject_limit,
|
|
||||||
concurrent_requests=hs.config.federation_rc_concurrent,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
self.register_servlets()
|
self.register_servlets()
|
||||||
|
@ -90,8 +90,8 @@ class BaseHandler(object):
|
|||||||
messages_per_second = override.messages_per_second
|
messages_per_second = override.messages_per_second
|
||||||
burst_count = override.burst_count
|
burst_count = override.burst_count
|
||||||
else:
|
else:
|
||||||
messages_per_second = self.hs.config.rc_messages_per_second
|
messages_per_second = self.hs.config.rc_message.per_second
|
||||||
burst_count = self.hs.config.rc_message_burst_count
|
burst_count = self.hs.config.rc_message.burst_count
|
||||||
|
|
||||||
allowed, time_allowed = self.ratelimiter.can_do_action(
|
allowed, time_allowed = self.ratelimiter.can_do_action(
|
||||||
user_id, time_now,
|
user_id, time_now,
|
||||||
|
@ -31,6 +31,7 @@ from synapse.api.errors import (
|
|||||||
SynapseError,
|
SynapseError,
|
||||||
UnrecognizedRequestError,
|
UnrecognizedRequestError,
|
||||||
)
|
)
|
||||||
|
from synapse.config.ratelimiting import FederationRateLimitConfig
|
||||||
from synapse.config.server import is_threepid_reserved
|
from synapse.config.server import is_threepid_reserved
|
||||||
from synapse.http.servlet import (
|
from synapse.http.servlet import (
|
||||||
RestServlet,
|
RestServlet,
|
||||||
@ -153,16 +154,18 @@ class UsernameAvailabilityRestServlet(RestServlet):
|
|||||||
self.registration_handler = hs.get_registration_handler()
|
self.registration_handler = hs.get_registration_handler()
|
||||||
self.ratelimiter = FederationRateLimiter(
|
self.ratelimiter = FederationRateLimiter(
|
||||||
hs.get_clock(),
|
hs.get_clock(),
|
||||||
# Time window of 2s
|
FederationRateLimitConfig(
|
||||||
window_size=2000,
|
# Time window of 2s
|
||||||
# Artificially delay requests if rate > sleep_limit/window_size
|
window_size=2000,
|
||||||
sleep_limit=1,
|
# Artificially delay requests if rate > sleep_limit/window_size
|
||||||
# Amount of artificial delay to apply
|
sleep_limit=1,
|
||||||
sleep_msec=1000,
|
# Amount of artificial delay to apply
|
||||||
# Error with 429 if more than reject_limit requests are queued
|
sleep_msec=1000,
|
||||||
reject_limit=1,
|
# Error with 429 if more than reject_limit requests are queued
|
||||||
# Allow 1 request at a time
|
reject_limit=1,
|
||||||
concurrent_requests=1,
|
# Allow 1 request at a time
|
||||||
|
concurrent_requests=1,
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
|
@ -30,31 +30,14 @@ logger = logging.getLogger(__name__)
|
|||||||
|
|
||||||
|
|
||||||
class FederationRateLimiter(object):
|
class FederationRateLimiter(object):
|
||||||
def __init__(self, clock, window_size, sleep_limit, sleep_msec,
|
def __init__(self, clock, config):
|
||||||
reject_limit, concurrent_requests):
|
|
||||||
"""
|
"""
|
||||||
Args:
|
Args:
|
||||||
clock (Clock)
|
clock (Clock)
|
||||||
window_size (int): The window size in milliseconds.
|
config (FederationRateLimitConfig)
|
||||||
sleep_limit (int): The number of requests received in the last
|
|
||||||
`window_size` milliseconds before we artificially start
|
|
||||||
delaying processing of requests.
|
|
||||||
sleep_msec (int): The number of milliseconds to delay processing
|
|
||||||
of incoming requests by.
|
|
||||||
reject_limit (int): The maximum number of requests that are can be
|
|
||||||
queued for processing before we start rejecting requests with
|
|
||||||
a 429 Too Many Requests response.
|
|
||||||
concurrent_requests (int): The number of concurrent requests to
|
|
||||||
process.
|
|
||||||
"""
|
"""
|
||||||
self.clock = clock
|
self.clock = clock
|
||||||
|
self._config = config
|
||||||
self.window_size = window_size
|
|
||||||
self.sleep_limit = sleep_limit
|
|
||||||
self.sleep_msec = sleep_msec
|
|
||||||
self.reject_limit = reject_limit
|
|
||||||
self.concurrent_requests = concurrent_requests
|
|
||||||
|
|
||||||
self.ratelimiters = {}
|
self.ratelimiters = {}
|
||||||
|
|
||||||
def ratelimit(self, host):
|
def ratelimit(self, host):
|
||||||
@ -76,25 +59,25 @@ class FederationRateLimiter(object):
|
|||||||
host,
|
host,
|
||||||
_PerHostRatelimiter(
|
_PerHostRatelimiter(
|
||||||
clock=self.clock,
|
clock=self.clock,
|
||||||
window_size=self.window_size,
|
config=self._config,
|
||||||
sleep_limit=self.sleep_limit,
|
|
||||||
sleep_msec=self.sleep_msec,
|
|
||||||
reject_limit=self.reject_limit,
|
|
||||||
concurrent_requests=self.concurrent_requests,
|
|
||||||
)
|
)
|
||||||
).ratelimit()
|
).ratelimit()
|
||||||
|
|
||||||
|
|
||||||
class _PerHostRatelimiter(object):
|
class _PerHostRatelimiter(object):
|
||||||
def __init__(self, clock, window_size, sleep_limit, sleep_msec,
|
def __init__(self, clock, config):
|
||||||
reject_limit, concurrent_requests):
|
"""
|
||||||
|
Args:
|
||||||
|
clock (Clock)
|
||||||
|
config (FederationRateLimitConfig)
|
||||||
|
"""
|
||||||
self.clock = clock
|
self.clock = clock
|
||||||
|
|
||||||
self.window_size = window_size
|
self.window_size = config.window_size
|
||||||
self.sleep_limit = sleep_limit
|
self.sleep_limit = config.sleep_limit
|
||||||
self.sleep_sec = sleep_msec / 1000.0
|
self.sleep_sec = config.sleep_delay / 1000.0
|
||||||
self.reject_limit = reject_limit
|
self.reject_limit = config.reject_limit
|
||||||
self.concurrent_requests = concurrent_requests
|
self.concurrent_requests = config.concurrent
|
||||||
|
|
||||||
# request_id objects for requests which have been slept
|
# request_id objects for requests which have been slept
|
||||||
self.sleeping_requests = set()
|
self.sleeping_requests = set()
|
||||||
|
@ -134,10 +134,6 @@ def default_config(name, parse=False):
|
|||||||
"email_enable_notifs": False,
|
"email_enable_notifs": False,
|
||||||
"block_non_admin_invites": False,
|
"block_non_admin_invites": False,
|
||||||
"federation_domain_whitelist": None,
|
"federation_domain_whitelist": None,
|
||||||
"federation_rc_reject_limit": 10,
|
|
||||||
"federation_rc_sleep_limit": 10,
|
|
||||||
"federation_rc_sleep_delay": 100,
|
|
||||||
"federation_rc_concurrent": 10,
|
|
||||||
"filter_timeline_limit": 5000,
|
"filter_timeline_limit": 5000,
|
||||||
"user_directory_search_all_users": False,
|
"user_directory_search_all_users": False,
|
||||||
"user_consent_server_notice_content": None,
|
"user_consent_server_notice_content": None,
|
||||||
@ -156,8 +152,13 @@ def default_config(name, parse=False):
|
|||||||
"mau_stats_only": False,
|
"mau_stats_only": False,
|
||||||
"mau_limits_reserved_threepids": [],
|
"mau_limits_reserved_threepids": [],
|
||||||
"admin_contact": None,
|
"admin_contact": None,
|
||||||
"rc_messages_per_second": 10000,
|
"rc_federation": {
|
||||||
"rc_message_burst_count": 10000,
|
"reject_limit": 10,
|
||||||
|
"sleep_limit": 10,
|
||||||
|
"sleep_delay": 10,
|
||||||
|
"concurrent": 10,
|
||||||
|
},
|
||||||
|
"rc_message": {"per_second": 10000, "burst_count": 10000},
|
||||||
"rc_registration": {"per_second": 10000, "burst_count": 10000},
|
"rc_registration": {"per_second": 10000, "burst_count": 10000},
|
||||||
"rc_login": {
|
"rc_login": {
|
||||||
"address": {"per_second": 10000, "burst_count": 10000},
|
"address": {"per_second": 10000, "burst_count": 10000},
|
||||||
@ -375,12 +376,7 @@ def register_federation_servlets(hs, resource):
|
|||||||
resource=resource,
|
resource=resource,
|
||||||
authenticator=federation_server.Authenticator(hs),
|
authenticator=federation_server.Authenticator(hs),
|
||||||
ratelimiter=FederationRateLimiter(
|
ratelimiter=FederationRateLimiter(
|
||||||
hs.get_clock(),
|
hs.get_clock(), config=hs.config.rc_federation
|
||||||
window_size=hs.config.federation_rc_window_size,
|
|
||||||
sleep_limit=hs.config.federation_rc_sleep_limit,
|
|
||||||
sleep_msec=hs.config.federation_rc_sleep_delay,
|
|
||||||
reject_limit=hs.config.federation_rc_reject_limit,
|
|
||||||
concurrent_requests=hs.config.federation_rc_concurrent,
|
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user