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

Commit

Permalink
add SpamChecker callback for hard rejecting federated events
Browse files Browse the repository at this point in the history
Signed-off-by: jesopo <github@lolnerd.net>
  • Loading branch information
jesopo committed May 16, 2022
1 parent b4eb163 commit 5121095
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 3 deletions.
35 changes: 35 additions & 0 deletions synapse/events/spamcheck.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ def load_legacy_spam_checkers(hs: "synapse.server.HomeServer") -> None:
# which name appears in this set, we'll want to register it.
spam_checker_methods = {
"check_event_for_spam",
"reject_federated_spam_event",
"user_may_invite",
"user_may_create_room",
"user_may_create_room_alias",
Expand Down Expand Up @@ -168,6 +169,9 @@ def __init__(self, hs: "synapse.server.HomeServer") -> None:
self.clock = hs.get_clock()

self._check_event_for_spam_callbacks: List[CHECK_EVENT_FOR_SPAM_CALLBACK] = []
self._reject_federated_spam_event_callbacks: List[
CHECK_EVENT_FOR_SPAM_CALLBACK
] = []
self._user_may_join_room_callbacks: List[USER_MAY_JOIN_ROOM_CALLBACK] = []
self._user_may_invite_callbacks: List[USER_MAY_INVITE_CALLBACK] = []
self._user_may_send_3pid_invite_callbacks: List[
Expand All @@ -191,6 +195,7 @@ def __init__(self, hs: "synapse.server.HomeServer") -> None:
def register_callbacks(
self,
check_event_for_spam: Optional[CHECK_EVENT_FOR_SPAM_CALLBACK] = None,
reject_federated_spam_event: Optional[CHECK_EVENT_FOR_SPAM_CALLBACK] = None,
user_may_join_room: Optional[USER_MAY_JOIN_ROOM_CALLBACK] = None,
user_may_invite: Optional[USER_MAY_INVITE_CALLBACK] = None,
user_may_send_3pid_invite: Optional[USER_MAY_SEND_3PID_INVITE_CALLBACK] = None,
Expand All @@ -209,6 +214,11 @@ def register_callbacks(
if check_event_for_spam is not None:
self._check_event_for_spam_callbacks.append(check_event_for_spam)

if reject_federated_spam_event is not None:
self._reject_federated_spam_event_callbacks.append(
reject_federated_spam_event
)

if user_may_join_room is not None:
self._user_may_join_room_callbacks.append(user_may_join_room)

Expand Down Expand Up @@ -268,6 +278,31 @@ async def check_event_for_spam(

return False

async def reject_federated_spam_event(
self, event: "synapse.events.EventBase"
) -> Union[bool, str]:
"""Checks if a given federated event is considered "spammy" by this
server.
If the server considers an event spammy, it will be hard rejected, and
in doing so will split-brain our view of the room's DAG.
Args:
event: the event to be checked
Returns:
True if the event should be hard rejected
"""
for callback in self._reject_federated_spam_event_callbacks:
with Measure(
self.clock, "{}.{}".format(callback.__module__, callback.__qualname__)
):
res: Union[bool, str] = await delay_cancellation(callback(event))
if res:
return res

return False

async def user_may_join_room(
self, user_id: str, room_id: str, is_invited: bool
) -> bool:
Expand Down
11 changes: 8 additions & 3 deletions synapse/federation/federation_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
from typing import TYPE_CHECKING

from synapse.api.constants import MAX_DEPTH, EventContentFields, EventTypes, Membership
from synapse.api.errors import Codes, SynapseError
from synapse.api.errors import Codes, FederationDeniedError, SynapseError
from synapse.api.room_versions import EventFormatVersions, RoomVersion
from synapse.crypto.event_signing import check_event_content_hash
from synapse.crypto.keyring import Keyring
Expand Down Expand Up @@ -98,9 +98,14 @@ async def _check_sigs_and_hash(
)
return redacted_event

result = await self.spam_checker.check_event_for_spam(pdu)
reject_hard = await self.spam_checker.reject_federated_spam_event(pdu)
if reject_hard:
logger.warning("Event contains spam, hard-failing %s", pdu.event_id)
raise FederationDeniedError(get_domain_from_id(pdu.sender))

if result:
reject_soft = await self.spam_checker.check_event_for_spam(pdu)

if reject_soft:
logger.warning("Event contains spam, soft-failing %s", pdu.event_id)
# we redact (to save disk space) as well as soft-failing (to stop
# using the event in prev_events).
Expand Down
2 changes: 2 additions & 0 deletions synapse/module_api/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,7 @@ def register_spam_checker_callbacks(
self,
*,
check_event_for_spam: Optional[CHECK_EVENT_FOR_SPAM_CALLBACK] = None,
reject_federated_spam_event: Optional[CHECK_EVENT_FOR_SPAM_CALLBACK] = None,
user_may_join_room: Optional[USER_MAY_JOIN_ROOM_CALLBACK] = None,
user_may_invite: Optional[USER_MAY_INVITE_CALLBACK] = None,
user_may_send_3pid_invite: Optional[USER_MAY_SEND_3PID_INVITE_CALLBACK] = None,
Expand All @@ -254,6 +255,7 @@ def register_spam_checker_callbacks(
"""
return self._spam_checker.register_callbacks(
check_event_for_spam=check_event_for_spam,
reject_federated_spam_event=reject_federated_spam_event,
user_may_join_room=user_may_join_room,
user_may_invite=user_may_invite,
user_may_send_3pid_invite=user_may_send_3pid_invite,
Expand Down

0 comments on commit 5121095

Please sign in to comment.