Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions synapse/api/auth/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,22 @@ def get_access_token_from_request(request: Request) -> str:
request
"""

@staticmethod
def get_ip_address_from_request(request: Request) -> str:
"""
Extract the IPv4 or IPv6 address from a client request.

Args:
request: The request to process.

Returns:
The IPv4 or IPv6 address of the client.

Raises:
SynapseError: If an IP address could not be extracted from the
request.
"""

async def check_user_in_room_or_world_readable(
self, room_id: str, requester: Requester, allow_departed_users: bool = False
) -> Tuple[str, Optional[str]]:
Expand Down
37 changes: 36 additions & 1 deletion synapse/api/auth/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,12 @@
#
#
import logging
from http import HTTPStatus
from typing import TYPE_CHECKING, Optional, Tuple

from netaddr import IPAddress

from twisted.internet.address import IPv4Address, IPv6Address
from twisted.web.server import Request

from synapse import event_auth
Expand All @@ -31,6 +33,7 @@
AuthError,
Codes,
MissingClientTokenError,
SynapseError,
UnstableSpecAuthError,
)
from synapse.appservice import ApplicationService
Expand Down Expand Up @@ -291,6 +294,37 @@ def get_access_token_from_request(request: Request) -> str:

return query_params[0].decode("ascii")

@staticmethod
def get_ip_address_from_request(request: Request) -> str:
"""
Extract the IPv4 or IPv6 address from a client request.

Args:
request: The request to process.

Returns:
The IPv4 or IPv6 address of the client.

Raises:
SynapseError: If an IP address could not be extracted from the
request.
"""
client_address = request.getClientAddress()
if not isinstance(client_address, IPv4Address) and not isinstance(
client_address, IPv6Address
):
logger.error(
"Unable to view IP address of the requester. " \
"Check that you are setting the X-Forwarded-For header correctly in your reverse proxy."
Comment on lines +317 to +318
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was the fix in #18940, though I don't know whether there are any other potential causes that a Twisted wouldn't be able to get an IP address for the client?

)
raise SynapseError(
HTTPStatus.INTERNAL_SERVER_ERROR,
"Unable to read client IP address",
Codes.UNKNOWN,
)

return client_address.host

@cancellable
async def get_appservice_user(
self, request: Request, access_token: str
Expand Down Expand Up @@ -326,7 +360,8 @@ async def get_appservice_user(
return None

if app_service.ip_range_whitelist:
ip_address = IPAddress(request.getClientAddress().host)
ip_address_str = self.get_ip_address_from_request(request)
ip_address = IPAddress(ip_address_str)
if ip_address not in app_service.ip_range_whitelist:
return None

Expand Down
2 changes: 1 addition & 1 deletion synapse/handlers/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -567,7 +567,7 @@ async def check_ui_auth(
await self.store.set_ui_auth_clientdict(sid, clientdict)

user_agent = get_request_user_agent(request)
clientip = request.getClientAddress().host
clientip = self.auth.get_ip_address_from_request(request)

await self.store.add_user_agent_ip_to_ui_auth_session(
session.session_id, user_agent, clientip
Expand Down
6 changes: 3 additions & 3 deletions synapse/handlers/identity.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@

class IdentityHandler:
def __init__(self, hs: "HomeServer"):
self._auth = hs.get_auth()
self.store = hs.get_datastores().main
# An HTTP client for contacting trusted URLs.
self.http_client = SimpleHttpClient(hs)
Expand Down Expand Up @@ -97,9 +98,8 @@ async def ratelimit_request_token_requests(
address: The actual threepid ID, e.g. the phone number or email address
"""

await self._3pid_validation_ratelimiter_ip.ratelimit(
None, (medium, request.getClientAddress().host)
)
ip_address = self._auth.get_ip_address_from_request(request)
await self._3pid_validation_ratelimiter_ip.ratelimit(None, (medium, ip_address))
await self._3pid_validation_ratelimiter_address.ratelimit(
None, (medium, address)
)
Expand Down
8 changes: 6 additions & 2 deletions synapse/handlers/sso.py
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,7 @@ def __init__(self, hs: "HomeServer"):
self.server_name = hs.hostname
self._is_mine_server_name = hs.is_mine_server_name
self._registration_handler = hs.get_registration_handler()
self._auth = hs.get_auth()
self._auth_handler = hs.get_auth_handler()
self._device_handler = hs.get_device_handler()
self._error_template = hs.config.sso.sso_error_template
Expand Down Expand Up @@ -505,12 +506,13 @@ async def complete_sso_login_request(
auth_provider_session_id,
)

ip_address = self._auth.get_ip_address_from_request(request)
user_id = await self._register_mapped_user(
attributes,
auth_provider_id,
remote_user_id,
get_request_user_agent(request),
request.getClientAddress().host,
ip_address,
)
new_user = True
elif self._sso_update_profile_information:
Expand Down Expand Up @@ -1080,14 +1082,16 @@ async def register_sso_user(self, request: Request, session_id: str) -> None:
if session.use_avatar:
attributes.picture = session.avatar_url

ip_address = self._auth.get_ip_address_from_request(request)

# the following will raise a 400 error if the username has been taken in the
# meantime.
user_id = await self._register_mapped_user(
attributes,
session.auth_provider_id,
session.remote_user_id,
get_request_user_agent(request),
request.getClientAddress().host,
ip_address,
)

logger.info(
Expand Down
12 changes: 9 additions & 3 deletions synapse/rest/client/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,8 @@ async def on_POST(self, request: Request, stagetype: str) -> None:
if not session:
raise SynapseError(400, "No session supplied")

ip_address = self.auth.get_ip_address_from_request(request)

if stagetype == LoginType.RECAPTCHA:
response = parse_string(request, "g-recaptcha-response")

Expand All @@ -144,7 +146,9 @@ async def on_POST(self, request: Request, stagetype: str) -> None:

try:
await self.auth_handler.add_oob_auth(
LoginType.RECAPTCHA, authdict, request.getClientAddress().host
LoginType.RECAPTCHA,
authdict,
ip_address,
)
except LoginError as e:
# Authentication failed, let user try again
Expand All @@ -164,7 +168,9 @@ async def on_POST(self, request: Request, stagetype: str) -> None:

try:
await self.auth_handler.add_oob_auth(
LoginType.TERMS, authdict, request.getClientAddress().host
LoginType.TERMS,
authdict,
ip_address,
)
except LoginError as e:
# Authentication failed, let user try again
Expand Down Expand Up @@ -195,7 +201,7 @@ async def on_POST(self, request: Request, stagetype: str) -> None:
await self.auth_handler.add_oob_auth(
LoginType.REGISTRATION_TOKEN,
authdict,
request.getClientAddress().host,
ip_address,
)
except LoginError as e:
html = self.registration_token_template.render(
Expand Down
17 changes: 5 additions & 12 deletions synapse/rest/client/login.py
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,7 @@ async def on_POST(self, request: SynapseRequest) -> Tuple[int, LoginResponse]:
)

request_info = request.request_info()
ip_address = self.auth.get_ip_address_from_request(request)

try:
if login_submission["type"] == LoginRestServlet.APPSERVICE_TYPE:
Expand All @@ -224,9 +225,7 @@ async def on_POST(self, request: SynapseRequest) -> Tuple[int, LoginResponse]:
)

if appservice.is_rate_limited():
await self._address_ratelimiter.ratelimit(
None, request.getClientAddress().host
)
await self._address_ratelimiter.ratelimit(None, ip_address)

result = await self._do_appservice_login(
login_submission,
Expand All @@ -238,27 +237,21 @@ async def on_POST(self, request: SynapseRequest) -> Tuple[int, LoginResponse]:
self.jwt_enabled
and login_submission["type"] == LoginRestServlet.JWT_TYPE
):
await self._address_ratelimiter.ratelimit(
None, request.getClientAddress().host
)
await self._address_ratelimiter.ratelimit(None, ip_address)
result = await self._do_jwt_login(
login_submission,
should_issue_refresh_token=should_issue_refresh_token,
request_info=request_info,
)
elif login_submission["type"] == LoginRestServlet.TOKEN_TYPE:
await self._address_ratelimiter.ratelimit(
None, request.getClientAddress().host
)
await self._address_ratelimiter.ratelimit(None, ip_address)
result = await self._do_token_login(
login_submission,
should_issue_refresh_token=should_issue_refresh_token,
request_info=request_info,
)
else:
await self._address_ratelimiter.ratelimit(
None, request.getClientAddress().host
)
await self._address_ratelimiter.ratelimit(None, ip_address)
result = await self._do_other_login(
login_submission,
should_issue_refresh_token=should_issue_refresh_token,
Expand Down
6 changes: 4 additions & 2 deletions synapse/rest/client/media.py
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,8 @@ async def on_GET(
respond_404(request)
return

ip_address = request.getClientAddress().host
ip_address = self.auth.get_ip_address_from_request(request)

remote_resp_function = (
self.thumbnailer.select_or_generate_remote_thumbnail
if self.dynamic_thumbnails
Expand Down Expand Up @@ -263,7 +264,8 @@ async def on_GET(
request, media_id, file_name, max_timeout_ms
)
else:
ip_address = request.getClientAddress().host
ip_address = self.auth.get_ip_address_from_request(request)

await self.media_repo.get_remote_media(
request,
server_name,
Expand Down
11 changes: 7 additions & 4 deletions synapse/rest/client/register.py
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,7 @@ class UsernameAvailabilityRestServlet(RestServlet):
def __init__(self, hs: "HomeServer"):
super().__init__()
self.hs = hs
self._auth = hs.get_auth()
self.server_name = hs.hostname
self.registration_handler = hs.get_registration_handler()
self.ratelimiter = FederationRateLimiter(
Expand Down Expand Up @@ -361,7 +362,7 @@ async def on_GET(self, request: Request) -> Tuple[int, JsonDict]:
if self.inhibit_user_in_use_error:
return 200, {"available": True}

ip = request.getClientAddress().host
ip = self._auth.get_ip_address_from_request(request)
with self.ratelimiter.ratelimit(ip) as wait_deferred:
await wait_deferred

Expand Down Expand Up @@ -395,6 +396,7 @@ class RegistrationTokenValidityRestServlet(RestServlet):
def __init__(self, hs: "HomeServer"):
super().__init__()
self.hs = hs
self._auth = hs.get_auth()
self.store = hs.get_datastores().main
self.ratelimiter = Ratelimiter(
store=self.store,
Expand All @@ -403,7 +405,8 @@ def __init__(self, hs: "HomeServer"):
)

async def on_GET(self, request: Request) -> Tuple[int, JsonDict]:
await self.ratelimiter.ratelimit(None, (request.getClientAddress().host,))
ip_address = self._auth.get_ip_address_from_request(request)
await self.ratelimiter.ratelimit(None, (ip_address,))

if not self.hs.config.registration.enable_registration:
raise SynapseError(
Expand Down Expand Up @@ -456,7 +459,7 @@ def __init__(self, hs: "HomeServer"):
async def on_POST(self, request: SynapseRequest) -> Tuple[int, JsonDict]:
body = parse_json_object_from_request(request)

client_addr = request.getClientAddress().host
client_addr = self.auth.get_ip_address_from_request(request)

await self.ratelimiter.ratelimit(None, client_addr, update=False)

Expand Down Expand Up @@ -916,7 +919,7 @@ def __init__(self, hs: "HomeServer"):
async def on_POST(self, request: SynapseRequest) -> Tuple[int, JsonDict]:
body = parse_json_object_from_request(request)

client_addr = request.getClientAddress().host
client_addr = self.auth.get_ip_address_from_request(request)

await self.ratelimiter.ratelimit(None, client_addr, update=False)

Expand Down
3 changes: 2 additions & 1 deletion synapse/rest/media/download_resource.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ class DownloadResource(RestServlet):

def __init__(self, hs: "HomeServer", media_repo: "MediaRepository"):
super().__init__()
self._auth = hs.get_auth()
self.media_repo = media_repo
self._is_mine_server_name = hs.is_mine_server_name

Expand Down Expand Up @@ -97,7 +98,7 @@ async def on_GET(
respond_404(request)
return

ip_address = request.getClientAddress().host
ip_address = self._auth.get_ip_address_from_request(request)
await self.media_repo.get_remote_media(
request,
server_name,
Expand Down
3 changes: 2 additions & 1 deletion synapse/rest/media/thumbnail_resource.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ def __init__(
):
super().__init__()

self._auth = hs.get_auth()
self.store = hs.get_datastores().main
self.media_repo = media_repo
self.media_storage = media_storage
Expand Down Expand Up @@ -120,7 +121,7 @@ async def on_GET(
respond_404(request)
return

ip_address = request.getClientAddress().host
ip_address = self._auth.get_ip_address_from_request(request)
remote_resp_function = (
self.thumbnail_provider.select_or_generate_remote_thumbnail
if self.dynamic_thumbnails
Expand Down
Loading
Loading