CVE-2021-21273: URL Redirection to Untrusted Site ('Open Redirect') (CWE-601)
value: Synapse is a Matrix reference homeserver written in python (pypi package matrix-synapse). Matrix is an ecosystem for open federated Instant Messaging and VoIP. In Synapse before version 1.25.0, requests to user provided domains were not restricted to external IP addresses when calculating the key validity for third-party invite events and sending push notifications. This could cause Synapse to make requests to internal infrastructure. The type of request was not controlled by the user, although limited modification of request bodies was possible. For the most thorough protection server administrators should remove the deprecated `federation_ip_range_blacklist` from their settings after upgrading to Synapse v1.25.0 which will result in Synapse using the improved default IP address restrictions. See the new `ip_range_blacklist` and `ip_range_whitelist` settings if more specific control is necessary.
CWE description: A web application accepts a user-controlled input that specifies a link to an external site, and uses that link in a Redirect. This simplifies phishing attacks.
CVSS v3 base score: 6.1
Published: 2021-02-26T18:15Z
Repository: https://github.com/matrix-org/synapse
Language: Python
Files changed: changelog.d/8821.bugfix, docs/sample_config.yaml, synapse/app/generic_worker.py, synapse/config/federation.py, synapse/crypto/keyring.py, synapse/federation/federation_server.py, synapse/federation/transport/client.py, synapse/handlers/federation.py, synapse/handlers/identity.py, synapse/http/client.py, synapse/http/federation/matrix_federation_agent.py, synapse/http/matrixfederationclient.py, synapse/push/httppusher.py, synapse/rest/media/v1/media_repository.py, synapse/server.py, tests/api/test_filtering.py, tests/app/test_frontend_proxy.py, tests/app/test_openid_listener.py, tests/crypto/test_keyring.py, tests/handlers/test_device.py, tests/handlers/test_directory.py, tests/handlers/test_federation.py, tests/handlers/test_presence.py, tests/handlers/test_profile.py, tests/handlers/test_typing.py, tests/http/federation/test_matrix_federation_agent.py, tests/push/test_http.py, tests/replication/_base.py, tests/replication/test_federation_sender_shard.py, tests/replication/test_pusher_shard.py, tests/rest/admin/test_admin.py, tests/rest/client/v1/test_presence.py, tests/rest/client/v1/test_profile.py, tests/rest/client/v1/test_rooms.py, tests/rest/client/v1/test_typing.py, tests/rest/key/v2/test_remote_key_resource.py, tests/rest/media/v1/test_media_storage.py, tests/storage/test_e2e_room_keys.py, tests/storage/test_purge.py, tests/storage/test_redaction.py, tests/storage/test_roommember.py, tests/test_federation.py, tests/test_server.py
Commit date: 2020-12-02 11:09:24 -0500
Commit message: Apply an IP range blacklist to push and key revocation requests. (#8821)

Replaces the `federation_ip_range_blacklist` configuration setting with an
`ip_range_blacklist` setting with wider scope. It now applies to:

* Federation
* Identity servers
* Push notifications
* Checking key validitity for third-party invite events

The old `federation_ip_range_blacklist` setting is still honored if present, but
with reduced scope (it only applies to federation and identity servers).
Diff: diff --git a/changelog.d/8821.bugfix b/changelog.d/8821.bugfix
new file mode 100644
index 0000000000..8ddfbf31ce
--- /dev/null
+++ b/changelog.d/8821.bugfix
@@ -0,0 +1 @@
+Apply the `federation_ip_range_blacklist` to push and key revocation requests.
diff --git a/docs/sample_config.yaml b/docs/sample_config.yaml
index 394eb9a3ff..6dbccf5932 100644
--- a/docs/sample_config.yaml
+++ b/docs/sample_config.yaml
@@ -640,21 +640,23 @@ acme:
 #federation_domain_whitelist:
 #  - lon.example.com
 #  - nyc.example.com
 #  - syd.example.com
 
-# Prevent federation requests from being sent to the following
-# blacklist IP address CIDR ranges. If this option is not specified, or
-# specified with an empty list, no ip range blacklist will be enforced.
+# Prevent outgoing requests from being sent to the following blacklisted IP address
+# CIDR ranges. If this option is not specified, or specified with an empty list,
+# no IP range blacklist will be enforced.
 #
-# As of Synapse v1.4.0 this option also affects any outbound requests to identity
-# servers provided by user input.
+# The blacklist applies to the outbound requests for federation, identity servers,
+# push servers, and for checking key validitity for third-party invite events.
 #
 # (0.0.0.0 and :: are always blacklisted, whether or not they are explicitly
 # listed here, since they correspond to unroutable addresses.)
 #
-federation_ip_range_blacklist:
+# This option replaces federation_ip_range_blacklist in Synapse v1.24.0.
+#
+ip_range_blacklist:
   - '127.0.0.0/8'
   - '10.0.0.0/8'
   - '172.16.0.0/12'
   - '192.168.0.0/16'
   - '100.64.0.0/10'
diff --git a/synapse/app/generic_worker.py b/synapse/app/generic_worker.py
index 1b511890aa..aa12c74358 100644
--- a/synapse/app/generic_worker.py
+++ b/synapse/app/generic_worker.py
@@ -264,11 +264,10 @@ UPDATE_SYNCING_USERS_MS = 10 * 1000
 class GenericWorkerPresence(BasePresenceHandler):
     def __init__(self, hs):
         super().__init__(hs)
         self.hs = hs
         self.is_mine_id = hs.is_mine_id
-        self.http_client = hs.get_simple_http_client()
 
         self._presence_enabled = hs.config.use_presence
 
         # The number of ongoing syncs on this process, by user id.
         # Empty if _presence_enabled is false.
diff --git a/synapse/config/federation.py b/synapse/config/federation.py
index ffd8fca54e..27ccf61c3c 100644
--- a/synapse/config/federation.py
+++ b/synapse/config/federation.py
@@ -34,26 +34,34 @@ class FederationConfig(Config):
             self.federation_domain_whitelist = {}
 
             for domain in federation_domain_whitelist:
                 self.federation_domain_whitelist[domain] = True
 
-        self.federation_ip_range_blacklist = config.get(
-            "federation_ip_range_blacklist", []
-        )
+        ip_range_blacklist = config.get("ip_range_blacklist", [])
 
         # Attempt to create an IPSet from the given ranges
         try:
-            self.federation_ip_range_blacklist = IPSet(
-                self.federation_ip_range_blacklist
-            )
-
-            # Always blacklist 0.0.0.0, ::
-            self.federation_ip_range_blacklist.update(["0.0.0.0", "::"])
+            self.ip_range_blacklist = IPSet(ip_range_blacklist)
+        except Exception as e:
+            raise ConfigError("Invalid range(s) provided in ip_range_blacklist: %s" % e)
+        # Always blacklist 0.0.0.0, ::
+        self.ip_range_blacklist.update(["0.0.0.0", "::"])
+
+        # The federation_ip_range_blacklist is used for backwards-compatibility
+        # and only applies to federation and identity servers. If it is not given,
+        # default to ip_range_blacklist.
+        federation_ip_range_blacklist = config.get(
+            "federation_ip_range_blacklist", ip_range_blacklist
+        )
+        try:
+            self.federation_ip_range_blacklist = IPSet(federation_ip_range_blacklist)
         except Exception as e:
             raise ConfigError(
                 "Invalid range(s) provided in federation_ip_range_blacklist: %s" % e
             )
+        # Always blacklist 0.0.0.0, ::
+        self.federation_ip_range_blacklist.update(["0.0.0.0", "::"])
 
         federation_metrics_domains = config.get("federation_metrics_domains") or []
         validate_config(
             _METRICS_FOR_DOMAINS_SCHEMA,
             federation_metrics_domains,
@@ -74,21 +82,23 @@ class FederationConfig(Config):
         #federation_domain_whitelist:
         #  - lon.example.com
         #  - nyc.example.com
         #  - syd.example.com
 
-        # Prevent federation requests from being sent to the following
-        # blacklist IP address CIDR ranges. If this option is not specified, or
-        # specified with an empty list, no ip range blacklist will be enforced.
+        # Prevent outgoing requests from being sent to the following blacklisted IP address
+        # CIDR ranges. If this option is not specified, or specified with an empty list,
+        # no IP range blacklist will be enforced.
         #
-        # As of Synapse v1.4.0 this option also affects any outbound requests to identity
-        # servers provided by user input.
+        # The blacklist applies to the outbound requests for federation, identity servers,
+        # push servers, and for checking key validitity for third-party invite events.
         #
         # (0.0.0.0 and :: are always blacklisted, whether or not they are explicitly
         # listed here, since they correspond to unroutable addresses.)
         #
-        federation_ip_range_blacklist:
+        # This option replaces federation_ip_range_blacklist in Synapse v1.24.0.
+        #
+        ip_range_blacklist:
           - '127.0.0.0/8'
           - '10.0.0.0/8'
           - '172.16.0.0/12'
           - '192.168.0.0/16'
           - '100.64.0.0/10'
diff --git a/synapse/crypto/keyring.py b/synapse/crypto/keyring.py
index c04ad77cf9..f23eacc0d7 100644
--- a/synapse/crypto/keyring.py
+++ b/synapse/crypto/keyring.py
@@ -576,11 +576,11 @@ class PerspectivesKeyFetcher(BaseV2KeyFetcher):
     """KeyFetcher impl which fetches keys from the "perspectives" servers"""
 
     def __init__(self, hs):
         super().__init__(hs)
         self.clock = hs.get_clock()
-        self.client = hs.get_http_client()
+        self.client = hs.get_federation_http_client()
         self.key_servers = self.config.key_servers
 
     async def get_keys(self, keys_to_fetch):
         """see KeyFetcher.get_keys"""
 
@@ -746,11 +746,11 @@ class ServerKeyFetcher(BaseV2KeyFetcher):
     """KeyFetcher impl which fetches keys from the origin servers"""
 
     def __init__(self, hs):
         super().__init__(hs)
         self.clock = hs.get_clock()
-        self.client = hs.get_http_client()
+        self.client = hs.get_federation_http_client()
 
     async def get_keys(self, keys_to_fetch):
         """
         Args:
             keys_to_fetch (dict[str, iterable[str]]):
diff --git a/synapse/federation/federation_server.py b/synapse/federation/federation_server.py
index 4b6ab470d0..35e345ce70 100644
--- a/synapse/federation/federation_server.py
+++ b/synapse/federation/federation_server.py
@@ -843,11 +843,10 @@ class FederationHandlerRegistry:
     query type for incoming federation traffic.
     """
 
     def __init__(self, hs: "HomeServer"):
         self.config = hs.config
-        self.http_client = hs.get_simple_http_client()
         self.clock = hs.get_clock()
         self._instance_name = hs.get_instance_name()
 
         # These are safe to load in monolith mode, but will explode if we try
         # and use them. However we have guards before we use them to ensure that
diff --git a/synapse/federation/transport/client.py b/synapse/federation/transport/client.py
index 17a10f622e..abe9168c78 100644
--- a/synapse/federation/transport/client.py
+++ b/synapse/federation/transport/client.py
@@ -33,11 +33,11 @@ logger = logging.getLogger(__name__)
 class TransportLayerClient:
     """Sends federation HTTP requests to other servers"""
 
     def __init__(self, hs):
         self.server_name = hs.hostname
-        self.client = hs.get_http_client()
+        self.client = hs.get_federation_http_client()
 
     @log_function
     def get_room_state_ids(self, destination, room_id, event_id):
         """ Requests all state for a given room from the given server at the
         given event. Returns the state's event_id's
diff --git a/synapse/handlers/federation.py b/synapse/handlers/federation.py
index b9799090f7..df82e60b33 100644
--- a/synapse/handlers/federation.py
+++ b/synapse/handlers/federation.py
@@ -138,11 +138,11 @@ class FederationHandler(BaseHandler):
         self.spam_checker = hs.get_spam_checker()
         self.event_creation_handler = hs.get_event_creation_handler()
         self._message_handler = hs.get_message_handler()
         self._server_notices_mxid = hs.config.server_notices_mxid
         self.config = hs.config
-        self.http_client = hs.get_simple_http_client()
+        self.http_client = hs.get_proxied_blacklisted_http_client()
         self._instance_name = hs.get_instance_name()
         self._replication = hs.get_replication_data_handler()
 
         self._send_events = ReplicationFederationSendEventsRestServlet.make_client(hs)
         self._clean_room_for_join_client = ReplicationCleanRoomRestServlet.make_client(
diff --git a/synapse/handlers/identity.py b/synapse/handlers/identity.py
index 9b3c6b4551..7301c24710 100644
--- a/synapse/handlers/identity.py
+++ b/synapse/handlers/identity.py
@@ -44,17 +44,17 @@ id_server_scheme = "https://"
 
 class IdentityHandler(BaseHandler):
     def __init__(self, hs):
         super().__init__(hs)
 
+        # An HTTP client for contacting trusted URLs.
         self.http_client = SimpleHttpClient(hs)
-        # We create a blacklisting instance of SimpleHttpClient for contacting identity
-        # servers specified by clients
+        # An HTTP client for contacting identity servers specified by clients.
         self.blacklisting_http_client = SimpleHttpClient(
             hs, ip_blacklist=hs.config.federation_ip_range_blacklist
         )
-        self.federation_http_client = hs.get_http_client()
+        self.federation_http_client = hs.get_federation_http_client()
         self.hs = hs
 
     async def threepid_from_creds(
         self, id_server: str, creds: Dict[str, str]
     ) -> Optional[JsonDict]:
diff --git a/synapse/http/client.py b/synapse/http/client.py
index e5b13593f2..df7730078f 100644
--- a/synapse/http/client.py
+++ b/synapse/http/client.py
@@ -123,11 +123,11 @@ def _make_scheduler(reactor):
         return reactor.callLater(_EPSILON, x)
 
     return _scheduler
 
 
-class IPBlacklistingResolver:
+class _IPBlacklistingResolver:
     """
     A proxy for reactor.nameResolver which only produces non-blacklisted IP
     addresses, preventing DNS rebinding attacks on URL preview.
     """
 
@@ -197,10 +197,39 @@ class IPBlacklistingResolver:
         )
 
         return r
 
 
+@implementer(IReactorPluggableNameResolver)
+class BlacklistingReactorWrapper:
+    """
+    A Reactor wrapper which will prevent DNS resolution to blacklisted IP
+    addresses, to prevent DNS rebinding.
+    """
+
+    def __init__(
+        self,
+        reactor: IReactorPluggableNameResolver,
+        ip_whitelist: Optional[IPSet],
+        ip_blacklist: IPSet,
+    ):
+        self._reactor = reactor
+
+        # We need to use a DNS resolver which filters out blacklisted IP
+        # addresses, to prevent DNS rebinding.
+        self._nameResolver = _IPBlacklistingResolver(
+            self._reactor, ip_whitelist, ip_blacklist
+        )
+
+    def __getattr__(self, attr: str) -> Any:
+        # Passthrough to the real reactor except for the DNS resolver.
+        if attr == "nameResolver":
+            return self._nameResolver
+        else:
+            return getattr(self._reactor, attr)
+
+
 class BlacklistingAgentWrapper(Agent):
     """
     An Agent wrapper which will prevent access to IP addresses being accessed
     directly (without an IP address lookup).
     """
@@ -290,26 +319,15 @@ class SimpleHttpClient:
         self._cooperator = Cooperator(scheduler=_make_scheduler(hs.get_reactor()))
 
         self.user_agent = self.user_agent.encode("ascii")
 
         if self._ip_blacklist:
-            real_reactor = hs.get_reactor()
             # If we have an IP blacklist, we need to use a DNS resolver which
             # filters out blacklisted IP addresses, to prevent DNS rebinding.
-            nameResolver = IPBlacklistingResolver(
-                real_reactor, self._ip_whitelist, self._ip_blacklist
+            self.reactor = BlacklistingReactorWrapper(
+                hs.get_reactor(), self._ip_whitelist, self._ip_blacklist
             )
-
-            @implementer(IReactorPluggableNameResolver)
-            class Reactor:
-                def __getattr__(_self, attr):
-                    if attr == "nameResolver":
-                        return nameResolver
-                    else:
-                        return getattr(real_reactor, attr)
-
-            self.reactor = Reactor()
         else:
             self.reactor = hs.get_reactor()
 
         # the pusher makes lots of concurrent SSL connections to sygnal, and
         # tends to do so in batches, so we need to allow the pool to keep
diff --git a/synapse/http/federation/matrix_federation_agent.py b/synapse/http/federation/matrix_federation_agent.py
index e77f9587d0..3b756a7dc2 100644
--- a/synapse/http/federation/matrix_federation_agent.py
+++ b/synapse/http/federation/matrix_federation_agent.py
@@ -14,11 +14,11 @@
 # limitations under the License.
 import logging
 import urllib.parse
 from typing import List, Optional
 
-from netaddr import AddrFormatError, IPAddress
+from netaddr import AddrFormatError, IPAddress, IPSet
 from zope.interface import implementer
 
 from twisted.internet import defer
 from twisted.internet.endpoints import HostnameEndpoint, wrapClientTLS
 from twisted.internet.interfaces import (
@@ -29,10 +29,11 @@ from twisted.internet.interfaces import (
 from twisted.web.client import URI, Agent, HTTPConnectionPool
 from twisted.web.http_headers import Headers
 from twisted.web.iweb import IAgent, IAgentEndpointFactory, IBodyProducer
 
 from synapse.crypto.context_factory import FederationPolicyForHTTPS
+from synapse.http.client import BlacklistingAgentWrapper
 from synapse.http.federation.srv_resolver import Server, SrvResolver
 from synapse.http.federation.well_known_resolver import WellKnownResolver
 from synapse.logging.context import make_deferred_yieldable, run_in_background
 from synapse.util import Clock
 
@@ -68,10 +69,11 @@ class MatrixFederationAgent:
     def __init__(
         self,
         reactor: IReactorCore,
         tls_client_options_factory: Optional[FederationPolicyForHTTPS],
         user_agent: bytes,
+        ip_blacklist: IPSet,
         _srv_resolver: Optional[SrvResolver] = None,
         _well_known_resolver: Optional[WellKnownResolver] = None,
     ):
         self._reactor = reactor
         self._clock = Clock(reactor)
@@ -88,16 +90,22 @@ class MatrixFederationAgent:
             pool=self._pool,
         )
         self.user_agent = user_agent
 
         if _well_known_resolver is None:
+            # Note that the name resolver has already been wrapped in a
+            # IPBlacklistingResolver by MatrixFederationHttpClient.
             _well_known_resolver = WellKnownResolver(
                 self._reactor,
-                agent=Agent(
+                agent=BlacklistingAgentWrapper(
+                    Agent(
+                        self._reactor,
+                        pool=self._pool,
+                        contextFactory=tls_client_options_factory,
+                    ),
                     self._reactor,
-                    pool=self._pool,
-                    contextFactory=tls_client_options_factory,
+                    ip_blacklist=ip_blacklist,
                 ),
                 user_agent=self.user_agent,
             )
 
         self._well_known_resolver = _well_known_resolver
diff --git a/synapse/http/matrixfederationclient.py b/synapse/http/matrixfederationclient.py
index 4e27f93b7a..c962994727 100644
--- a/synapse/http/matrixfederationclient.py
+++ b/synapse/http/matrixfederationclient.py
@@ -24,15 +24,14 @@ from typing import Callable, Dict, List, Optional, Tuple, Union
 import attr
 import treq
 from canonicaljson import encode_canonical_json
 from prometheus_client import Counter
 from signedjson.sign import sign_json
-from zope.interface import implementer
 
 from twisted.internet import defer
 from twisted.internet.error import DNSLookupError
-from twisted.internet.interfaces import IReactorPluggableNameResolver, IReactorTime
+from twisted.internet.interfaces import IReactorTime
 from twisted.internet.task import _EPSILON, Cooperator
 from twisted.web.http_headers import Headers
 from twisted.web.iweb import IBodyProducer, IResponse
 
 import synapse.metrics
@@ -43,11 +42,11 @@ from synapse.api.errors import (
     RequestSendFailed,
 )
 from synapse.http import QuieterFileBodyProducer
 from synapse.http.client import (
     BlacklistingAgentWrapper,
-    IPBlacklistingResolver,
+    BlacklistingReactorWrapper,
     encode_query_args,
     readBodyToFile,
 )
 from synapse.http.federation.matrix_federation_agent import MatrixFederationAgent
 from synapse.logging.context import make_deferred_yieldable
@@ -219,35 +218,26 @@ class MatrixFederationHttpClient:
     def __init__(self, hs, tls_client_options_factory):
         self.hs = hs
         self.signing_key = hs.signing_key
         self.server_name = hs.hostname
 
-        real_reactor = hs.get_reactor()
-
         # We need to use a DNS resolver which filters out blacklisted IP
         # addresses, to prevent DNS rebinding.
-        nameResolver = IPBlacklistingResolver(
-            real_reactor, None, hs.config.federation_ip_range_blacklist
+        self.reactor = BlacklistingReactorWrapper(
+            hs.get_reactor(), None, hs.config.federation_ip_range_blacklist
         )
 
-        @implementer(IReactorPluggableNameResolver)
-        class Reactor:
-            def __getattr__(_self, attr):
-                if attr == "nameResolver":
-                    return nameResolver
-                else:
-                    return getattr(real_reactor, attr)
-
-        self.reactor = Reactor()
-
         user_agent = hs.version_string
         if hs.config.user_agent_suffix:
             user_agent = "%s %s" % (user_agent, hs.config.user_agent_suffix)
         user_agent = user_agent.encode("ascii")
 
         self.agent = MatrixFederationAgent(
-            self.reactor, tls_client_options_factory, user_agent
+            self.reactor,
+            tls_client_options_factory,
+            user_agent,
+            hs.config.federation_ip_range_blacklist,
         )
 
         # Use a BlacklistingAgentWrapper to prevent circumventing the IP
         # blacklist via IP literals in server names
         self.agent = BlacklistingAgentWrapper(
diff --git a/synapse/push/httppusher.py b/synapse/push/httppusher.py
index eff0975b6a..0e845212a9 100644
--- a/synapse/push/httppusher.py
+++ b/synapse/push/httppusher.py
@@ -98,11 +98,11 @@ class HttpPusher:
             raise PusherConfigException("data can not be null for HTTP pusher")
 
         if "url" not in self.data:
             raise PusherConfigException("'url' required in data for HTTP pusher")
         self.url = self.data["url"]
-        self.http_client = hs.get_proxied_http_client()
+        self.http_client = hs.get_proxied_blacklisted_http_client()
         self.data_minus_url = {}
         self.data_minus_url.update(self.data)
         del self.data_minus_url["url"]
 
     def on_started(self, should_check_for_notifs):
diff --git a/synapse/rest/media/v1/media_repository.py b/synapse/rest/media/v1/media_repository.py
index 9cac74ebd8..83beb02b05 100644
--- a/synapse/rest/media/v1/media_repository.py
+++ b/synapse/rest/media/v1/media_repository.py
@@ -64,11 +64,11 @@ UPDATE_RECENTLY_ACCESSED_TS = 60 * 1000
 
 class MediaRepository:
     def __init__(self, hs):
         self.hs = hs
         self.auth = hs.get_auth()
-        self.client = hs.get_http_client()
+        self.client = hs.get_federation_http_client()
         self.clock = hs.get_clock()
         self.server_name = hs.hostname
         self.store = hs.get_datastore()
         self.max_upload_size = hs.config.max_upload_size
         self.max_image_pixels = hs.config.max_image_pixels
diff --git a/synapse/server.py b/synapse/server.py
index b017e3489f..9af759626e 100644
--- a/synapse/server.py
+++ b/synapse/server.py
@@ -348,20 +348,49 @@ class HomeServer(metaclass=abc.ABCMeta):
             else RegularPolicyForHTTPS()
         )
 
     @cache_in_self
     def get_simple_http_client(self) -> SimpleHttpClient:
+        """
+        An HTTP client with no special configuration.
+        """
         return SimpleHttpClient(self)
 
     @cache_in_self
     def get_proxied_http_client(self) -> SimpleHttpClient:
+        """
+        An HTTP client that uses configured HTTP(S) proxies.
+        """
+        return SimpleHttpClient(
+            self,
+            http_proxy=os.getenvb(b"http_proxy"),
+            https_proxy=os.getenvb(b"HTTPS_PROXY"),
+        )
+
+    @cache_in_self
+    def get_proxied_blacklisted_http_client(self) -> SimpleHttpClient:
+        """
+        An HTTP client that uses configured HTTP(S) proxies and blacklists IPs
+        based on the IP range blacklist.
+        """
         return SimpleHttpClient(
             self,
+            ip_blacklist=self.config.ip_range_blacklist,
             http_proxy=os.getenvb(b"http_proxy"),
             https_proxy=os.getenvb(b"HTTPS_PROXY"),
         )
 
+    @cache_in_self
+    def get_federation_http_client(self) -> MatrixFederationHttpClient:
+        """
+        An HTTP client for federation.
+        """
+        tls_client_options_factory = context_factory.FederationPolicyForHTTPS(
+            self.config
+        )
+        return MatrixFederationHttpClient(self, tls_client_options_factory)
+
     @cache_in_self
     def get_room_creation_handler(self) -> RoomCreationHandler:
         return RoomCreationHandler(self)
 
     @cache_in_self
@@ -512,17 +541,10 @@ class HomeServer(metaclass=abc.ABCMeta):
 
     @cache_in_self
     def get_pusherpool(self) -> PusherPool:
         return PusherPool(self)
 
-    @cache_in_self
-    def get_http_client(self) -> MatrixFederationHttpClient:
-        tls_client_options_factory = context_factory.FederationPolicyForHTTPS(
-            self.config
-        )
-        return MatrixFederationHttpClient(self, tls_client_options_factory)
-
     @cache_in_self
     def get_media_repository_resource(self) -> MediaRepositoryResource:
         # build the media repo resource. This indirects through the HomeServer
         # to ensure that we only have a single instance of
         return MediaRepositoryResource(self)
diff --git a/tests/api/test_filtering.py b/tests/api/test_filtering.py
index c98ae75974..bcf1a8010e 100644
--- a/tests/api/test_filtering.py
+++ b/tests/api/test_filtering.py
@@ -48,11 +48,13 @@ class FilteringTestCase(unittest.TestCase):
 
         self.mock_http_client = Mock(spec=[])
         self.mock_http_client.put_json = DeferredMockCallable()
 
         hs = yield setup_test_homeserver(
-            self.addCleanup, http_client=self.mock_http_client, keyring=Mock(),
+            self.addCleanup,
+            federation_http_client=self.mock_http_client,
+            keyring=Mock(),
         )
 
         self.filtering = hs.get_filtering()
 
         self.datastore = hs.get_datastore()
diff --git a/tests/app/test_frontend_proxy.py b/tests/app/test_frontend_proxy.py
index 40abe9d72d..43fef5d64a 100644
--- a/tests/app/test_frontend_proxy.py
+++ b/tests/app/test_frontend_proxy.py
@@ -21,11 +21,11 @@ from tests.unittest import HomeserverTestCase
 
 class FrontendProxyTests(HomeserverTestCase):
     def make_homeserver(self, reactor, clock):
 
         hs = self.setup_test_homeserver(
-            http_client=None, homeserver_to_use=GenericWorkerServer
+            federation_http_client=None, homeserver_to_use=GenericWorkerServer
         )
 
         return hs
 
     def default_config(self):
diff --git a/tests/app/test_openid_listener.py b/tests/app/test_openid_listener.py
index ea3be95cf1..b260ab734d 100644
--- a/tests/app/test_openid_listener.py
+++ b/tests/app/test_openid_listener.py
@@ -25,11 +25,11 @@ from tests.unittest import HomeserverTestCase
 
 
 class FederationReaderOpenIDListenerTests(HomeserverTestCase):
     def make_homeserver(self, reactor, clock):
         hs = self.setup_test_homeserver(
-            http_client=None, homeserver_to_use=GenericWorkerServer
+            federation_http_client=None, homeserver_to_use=GenericWorkerServer
         )
         return hs
 
     def default_config(self):
         conf = super().default_config()
@@ -82,11 +82,11 @@ class FederationReaderOpenIDListenerTests(HomeserverTestCase):
 
 @patch("synapse.app.homeserver.KeyApiV2Resource", new=Mock())
 class SynapseHomeserverOpenIDListenerTests(HomeserverTestCase):
     def make_homeserver(self, reactor, clock):
         hs = self.setup_test_homeserver(
-            http_client=None, homeserver_to_use=SynapseHomeServer
+            federation_http_client=None, homeserver_to_use=SynapseHomeServer
         )
         return hs
 
     @parameterized.expand(
         [
diff --git a/tests/crypto/test_keyring.py b/tests/crypto/test_keyring.py
index 697916a019..d146f2254f 100644
--- a/tests/crypto/test_keyring.py
+++ b/tests/crypto/test_keyring.py
@@ -313,11 +313,11 @@ class KeyringTestCase(unittest.HomeserverTestCase):
 
 @logcontext_clean
 class ServerKeyFetcherTestCase(unittest.HomeserverTestCase):
     def make_homeserver(self, reactor, clock):
         self.http_client = Mock()
-        hs = self.setup_test_homeserver(http_client=self.http_client)
+        hs = self.setup_test_homeserver(federation_http_client=self.http_client)
         return hs
 
     def test_get_keys_from_server(self):
         # arbitrarily advance the clock a bit
         self.reactor.advance(100)
@@ -393,11 +393,13 @@ class PerspectivesKeyFetcherTestCase(unittest.HomeserverTestCase):
                 "server_name": self.mock_perspective_server.server_name,
                 "verify_keys": self.mock_perspective_server.get_verify_keys(),
             }
         ]
 
-        return self.setup_test_homeserver(http_client=self.http_client, config=config)
+        return self.setup_test_homeserver(
+            federation_http_client=self.http_client, config=config
+        )
 
     def build_perspectives_response(
         self, server_name: str, signing_key: SigningKey, valid_until_ts: int,
     ) -> dict:
         """
diff --git a/tests/handlers/test_device.py b/tests/handlers/test_device.py
index 875aaec2c6..5dfeccfeb6 100644
--- a/tests/handlers/test_device.py
+++ b/tests/handlers/test_device.py
@@ -25,11 +25,11 @@ user1 = "@boris:aaa"
 user2 = "@theresa:bbb"
 
 
 class DeviceTestCase(unittest.HomeserverTestCase):
     def make_homeserver(self, reactor, clock):
-        hs = self.setup_test_homeserver("server", http_client=None)
+        hs = self.setup_test_homeserver("server", federation_http_client=None)
         self.handler = hs.get_device_handler()
         self.store = hs.get_datastore()
         return hs
 
     def prepare(self, reactor, clock, hs):
@@ -227,11 +227,11 @@ class DeviceTestCase(unittest.HomeserverTestCase):
             self.reactor.advance(1000)
 
 
 class DehydrationTestCase(unittest.HomeserverTestCase):
     def make_homeserver(self, reactor, clock):
-        hs = self.setup_test_homeserver("server", http_client=None)
+        hs = self.setup_test_homeserver("server", federation_http_client=None)
         self.handler = hs.get_device_handler()
         self.registration = hs.get_registration_handler()
         self.auth = hs.get_auth()
         self.store = hs.get_datastore()
         return hs
diff --git a/tests/handlers/test_directory.py b/tests/handlers/test_directory.py
index ee6ef5e6fa..2f1f2a5517 100644
--- a/tests/handlers/test_directory.py
+++ b/tests/handlers/test_directory.py
@@ -40,11 +40,11 @@ class DirectoryTestCase(unittest.HomeserverTestCase):
             self.query_handlers[query_type] = handler
 
         self.mock_registry.register_query_handler = register_query_handler
 
         hs = self.setup_test_homeserver(
-            http_client=None,
+            federation_http_client=None,
             resource_for_federation=Mock(),
             federation_client=self.mock_federation,
             federation_registry=self.mock_registry,
         )
 
diff --git a/tests/handlers/test_federation.py b/tests/handlers/test_federation.py
index bf866dacf3..d0452e1490 100644
--- a/tests/handlers/test_federation.py
+++ b/tests/handlers/test_federation.py
@@ -35,11 +35,11 @@ class FederationTestCase(unittest.HomeserverTestCase):
         login.register_servlets,
         room.register_servlets,
     ]
 
     def make_homeserver(self, reactor, clock):
-        hs = self.setup_test_homeserver(http_client=None)
+        hs = self.setup_test_homeserver(federation_http_client=None)
         self.handler = hs.get_federation_handler()
         self.store = hs.get_datastore()
         return hs
 
     def test_exchange_revoked_invite(self):
diff --git a/tests/handlers/test_presence.py b/tests/handlers/test_presence.py
index 8ed67640f8..0794b32c9c 100644
--- a/tests/handlers/test_presence.py
+++ b/tests/handlers/test_presence.py
@@ -461,11 +461,11 @@ class PresenceJoinTestCase(unittest.HomeserverTestCase):
 
     servlets = [room.register_servlets]
 
     def make_homeserver(self, reactor, clock):
         hs = self.setup_test_homeserver(
-            "server", http_client=None, federation_sender=Mock()
+            "server", federation_http_client=None, federation_sender=Mock()
         )
         return hs
 
     def prepare(self, reactor, clock, hs):
         self.federation_sender = hs.get_federation_sender()
diff --git a/tests/handlers/test_profile.py b/tests/handlers/test_profile.py
index a69fa28b41..ea2bcf2655 100644
--- a/tests/handlers/test_profile.py
+++ b/tests/handlers/test_profile.py
@@ -42,11 +42,11 @@ class ProfileTestCase(unittest.TestCase):
 
         self.mock_registry.register_query_handler = register_query_handler
 
         hs = yield setup_test_homeserver(
             self.addCleanup,
-            http_client=None,
+            federation_http_client=None,
             resource_for_federation=Mock(),
             federation_client=self.mock_federation,
             federation_server=Mock(),
             federation_registry=self.mock_registry,
         )
diff --git a/tests/handlers/test_typing.py b/tests/handlers/test_typing.py
index abbdf2d524..36086ca836 100644
--- a/tests/handlers/test_typing.py
+++ b/tests/handlers/test_typing.py
@@ -68,11 +68,11 @@ class TypingNotificationsTestCase(unittest.HomeserverTestCase):
         # the tests assume that we are starting at unix time 1000
         reactor.pump((1000,))
 
         hs = self.setup_test_homeserver(
             notifier=Mock(),
-            http_client=mock_federation_client,
+            federation_http_client=mock_federation_client,
             keyring=mock_keyring,
             replication_streams={},
         )
 
         return hs
@@ -190,11 +190,11 @@ class TypingNotificationsTestCase(unittest.HomeserverTestCase):
                 room_id=ROOM_ID,
                 timeout=20000,
             )
         )
 
-        put_json = self.hs.get_http_client().put_json
+        put_json = self.hs.get_federation_http_client().put_json
         put_json.assert_called_once_with(
             "farm",
             path="/_matrix/federation/v1/send/1000000",
             data=_expect_edu_transaction(
                 "m.typing",
@@ -268,11 +268,11 @@ class TypingNotificationsTestCase(unittest.HomeserverTestCase):
             )
         )
 
         self.on_new_event.assert_has_calls([call("typing_key", 1, rooms=[ROOM_ID])])
 
-        put_json = self.hs.get_http_client().put_json
+        put_json = self.hs.get_federation_http_client().put_json
         put_json.assert_called_once_with(
             "farm",
             path="/_matrix/federation/v1/send/1000000",
             data=_expect_edu_transaction(
                 "m.typing",
diff --git a/tests/http/federation/test_matrix_federation_agent.py b/tests/http/federation/test_matrix_federation_agent.py
index 8b5ad4574f..626acdcaa3 100644
--- a/tests/http/federation/test_matrix_federation_agent.py
+++ b/tests/http/federation/test_matrix_federation_agent.py
@@ -15,10 +15,11 @@
 import logging
 
 from mock import Mock
 
 import treq
+from netaddr import IPSet
 from service_identity import VerificationError
 from zope.interface import implementer
 
 from twisted.internet import defer
 from twisted.internet._sslverify import ClientTLSOptions, OpenSSLCertificateOptions
@@ -101,10 +102,11 @@ class MatrixFederationAgentTests(unittest.TestCase):
 
         self.agent = MatrixFederationAgent(
             reactor=self.reactor,
             tls_client_options_factory=self.tls_factory,
             user_agent="test-agent",  # Note that this is unused since _well_known_resolver is provided.
+            ip_blacklist=IPSet(),
             _srv_resolver=self.mock_resolver,
             _well_known_resolver=self.well_known_resolver,
         )
 
     def _make_connection(self, client_factory, expected_sni):
@@ -734,10 +736,11 @@ class MatrixFederationAgentTests(unittest.TestCase):
         tls_factory = FederationPolicyForHTTPS(config)
         agent = MatrixFederationAgent(
             reactor=self.reactor,
             tls_client_options_factory=tls_factory,
             user_agent=b"test-agent",  # This is unused since _well_known_resolver is passed below.
+            ip_blacklist=IPSet(),
             _srv_resolver=self.mock_resolver,
             _well_known_resolver=WellKnownResolver(
                 self.reactor,
                 Agent(self.reactor, contextFactory=tls_factory),
                 b"test-agent",
diff --git a/tests/push/test_http.py b/tests/push/test_http.py
index f118430309..e8cea39c83 100644
--- a/tests/push/test_http.py
+++ b/tests/push/test_http.py
@@ -47,11 +47,13 @@ class HTTPPusherTests(HomeserverTestCase):
         m.post_json_get_json = post_json_get_json
 
         config = self.default_config()
         config["start_pushers"] = True
 
-        hs = self.setup_test_homeserver(config=config, proxied_http_client=m)
+        hs = self.setup_test_homeserver(
+            config=config, proxied_blacklisted_http_client=m
+        )
 
         return hs
 
     def test_sends_http(self):
         """
diff --git a/tests/replication/_base.py b/tests/replication/_base.py
index 295c5d58a6..728de28277 100644
--- a/tests/replication/_base.py
+++ b/tests/replication/_base.py
@@ -65,11 +65,11 @@ class BaseStreamTestCase(unittest.HomeserverTestCase):
         self.server = server_factory.buildProtocol(None)
 
         # Make a new HomeServer object for the worker
         self.reactor.lookups["testserv"] = "1.2.3.4"
         self.worker_hs = self.setup_test_homeserver(
-            http_client=None,
+            federation_http_client=None,
             homeserver_to_use=GenericWorkerServer,
             config=self._get_worker_hs_config(),
             reactor=self.reactor,
         )
 
@@ -262,11 +262,11 @@ class BaseMultiWorkerStreamTestCase(unittest.HomeserverTestCase):
 
         Args:
             worker_app: Type of worker, e.g. `synapse.app.federation_sender`.
             extra_config: Any extra config to use for this instances.
             **kwargs: Options that get passed to `self.setup_test_homeserver`,
-                useful to e.g. pass some mocks for things like `http_client`
+                useful to e.g. pass some mocks for things like `federation_http_client`
 
         Returns:
             The new worker HomeServer instance.
         """
 
diff --git a/tests/replication/test_federation_sender_shard.py b/tests/replication/test_federation_sender_shard.py
index 779745ae9d..fffdb742c8 100644
--- a/tests/replication/test_federation_sender_shard.py
+++ b/tests/replication/test_federation_sender_shard.py
@@ -48,11 +48,11 @@ class FederationSenderTestCase(BaseMultiWorkerStreamTestCase):
         mock_client.put_json.return_value = make_awaitable({})
 
         self.make_worker_hs(
             "synapse.app.federation_sender",
             {"send_federation": True},
-            http_client=mock_client,
+            federation_http_client=mock_client,
         )
 
         user = self.register_user("user", "pass")
         token = self.login("user", "pass")
 
@@ -79,11 +79,11 @@ class FederationSenderTestCase(BaseMultiWorkerStreamTestCase):
             {
                 "send_federation": True,
                 "worker_name": "sender1",
                 "federation_sender_instances": ["sender1", "sender2"],
             },
-            http_client=mock_client1,
+            federation_http_client=mock_client1,
         )
 
         mock_client2 = Mock(spec=["put_json"])
         mock_client2.put_json.return_value = make_awaitable({})
         self.make_worker_hs(
@@ -91,11 +91,11 @@ class FederationSenderTestCase(BaseMultiWorkerStreamTestCase):
             {
                 "send_federation": True,
                 "worker_name": "sender2",
                 "federation_sender_instances": ["sender1", "sender2"],
             },
-            http_client=mock_client2,
+            federation_http_client=mock_client2,
         )
 
         user = self.register_user("user2", "pass")
         token = self.login("user2", "pass")
 
@@ -142,11 +142,11 @@ class FederationSenderTestCase(BaseMultiWorkerStreamTestCase):
             {
                 "send_federation": True,
                 "worker_name": "sender1",
                 "federation_sender_instances": ["sender1", "sender2"],
             },
-            http_client=mock_client1,
+            federation_http_client=mock_client1,
         )
 
         mock_client2 = Mock(spec=["put_json"])
         mock_client2.put_json.return_value = make_awaitable({})
         self.make_worker_hs(
@@ -154,11 +154,11 @@ class FederationSenderTestCase(BaseMultiWorkerStreamTestCase):
             {
                 "send_federation": True,
                 "worker_name": "sender2",
                 "federation_sender_instances": ["sender1", "sender2"],
             },
-            http_client=mock_client2,
+            federation_http_client=mock_client2,
         )
 
         user = self.register_user("user3", "pass")
         token = self.login("user3", "pass")
 
diff --git a/tests/replication/test_pusher_shard.py b/tests/replication/test_pusher_shard.py
index 67c27a089f..f894bcd6e7 100644
--- a/tests/replication/test_pusher_shard.py
+++ b/tests/replication/test_pusher_shard.py
@@ -96,11 +96,11 @@ class PusherShardTestCase(BaseMultiWorkerStreamTestCase):
         )
 
         self.make_worker_hs(
             "synapse.app.pusher",
             {"start_pushers": True},
-            proxied_http_client=http_client_mock,
+            proxied_blacklisted_http_client=http_client_mock,
         )
 
         event_id = self._create_pusher_and_send_msg("user")
 
         # Advance time a bit, so the pusher will register something has happened
@@ -131,11 +131,11 @@ class PusherShardTestCase(BaseMultiWorkerStreamTestCase):
             {
                 "start_pushers": True,
                 "worker_name": "pusher1",
                 "pusher_instances": ["pusher1", "pusher2"],
             },
-            proxied_http_client=http_client_mock1,
+            proxied_blacklisted_http_client=http_client_mock1,
         )
 
         http_client_mock2 = Mock(spec_set=["post_json_get_json"])
         http_client_mock2.post_json_get_json.side_effect = lambda *_, **__: defer.succeed(
             {}
@@ -146,11 +146,11 @@ class PusherShardTestCase(BaseMultiWorkerStreamTestCase):
             {
                 "start_pushers": True,
                 "worker_name": "pusher2",
                 "pusher_instances": ["pusher1", "pusher2"],
             },
-            proxied_http_client=http_client_mock2,
+            proxied_blacklisted_http_client=http_client_mock2,
         )
 
         # We choose a user name that we know should go to pusher1.
         event_id = self._create_pusher_and_send_msg("user2")
 
diff --git a/tests/rest/admin/test_admin.py b/tests/rest/admin/test_admin.py
index 4f76f8f768..67d8878395 100644
--- a/tests/rest/admin/test_admin.py
+++ b/tests/rest/admin/test_admin.py
@@ -208,11 +208,11 @@ class QuarantineMediaTestCase(unittest.HomeserverTestCase):
             "store_remote": True,
             "config": {"directory": self.storage_path},
         }
         config["media_storage_providers"] = [provider_config]
 
-        hs = self.setup_test_homeserver(config=config, http_client=client)
+        hs = self.setup_test_homeserver(config=config, federation_http_client=client)
 
         return hs
 
     def _ensure_quarantined(self, admin_user_tok, server_and_media_id):
         """Ensure a piece of media is quarantined when trying to access it."""
diff --git a/tests/rest/client/v1/test_presence.py b/tests/rest/client/v1/test_presence.py
index 5d5c24d01c..11cd8efe21 100644
--- a/tests/rest/client/v1/test_presence.py
+++ b/tests/rest/client/v1/test_presence.py
@@ -36,11 +36,11 @@ class PresenceTestCase(unittest.HomeserverTestCase):
         presence_handler = Mock()
         presence_handler.set_state.return_value = defer.succeed(None)
 
         hs = self.setup_test_homeserver(
             "red",
-            http_client=None,
+            federation_http_client=None,
             federation_client=Mock(),
             presence_handler=presence_handler,
         )
 
         return hs
diff --git a/tests/rest/client/v1/test_profile.py b/tests/rest/client/v1/test_profile.py
index 383a9eafac..2a3b483eaf 100644
--- a/tests/rest/client/v1/test_profile.py
+++ b/tests/rest/client/v1/test_profile.py
@@ -61,11 +61,11 @@ class MockHandlerProfileTestCase(unittest.TestCase):
         )
 
         hs = yield setup_test_homeserver(
             self.addCleanup,
             "test",
-            http_client=None,
+            federation_http_client=None,
             resource_for_client=self.mock_resource,
             federation=Mock(),
             federation_client=Mock(),
             profile_handler=self.mock_handler,
         )
diff --git a/tests/rest/client/v1/test_rooms.py b/tests/rest/client/v1/test_rooms.py
index 49f1073c88..e67de41c18 100644
--- a/tests/rest/client/v1/test_rooms.py
+++ b/tests/rest/client/v1/test_rooms.py
@@ -43,11 +43,11 @@ class RoomBase(unittest.HomeserverTestCase):
     servlets = [room.register_servlets, room.register_deprecated_servlets]
 
     def make_homeserver(self, reactor, clock):
 
         self.hs = self.setup_test_homeserver(
-            "red", http_client=None, federation_client=Mock(),
+            "red", federation_http_client=None, federation_client=Mock(),
         )
 
         self.hs.get_federation_handler = Mock()
         self.hs.get_federation_handler.return_value.maybe_backfill = Mock(
             return_value=make_awaitable(None)
diff --git a/tests/rest/client/v1/test_typing.py b/tests/rest/client/v1/test_typing.py
index bbd30f594b..ae0207366b 100644
--- a/tests/rest/client/v1/test_typing.py
+++ b/tests/rest/client/v1/test_typing.py
@@ -37,11 +37,11 @@ class RoomTypingTestCase(unittest.HomeserverTestCase):
     servlets = [room.register_servlets]
 
     def make_homeserver(self, reactor, clock):
 
         hs = self.setup_test_homeserver(
-            "red", http_client=None, federation_client=Mock(),
+            "red", federation_http_client=None, federation_client=Mock(),
         )
 
         self.event_source = hs.get_event_sources().sources["typing"]
 
         hs.get_federation_handler = Mock()
diff --git a/tests/rest/key/v2/test_remote_key_resource.py b/tests/rest/key/v2/test_remote_key_resource.py
index fbcf8d5b86..5e90d656f7 100644
--- a/tests/rest/key/v2/test_remote_key_resource.py
+++ b/tests/rest/key/v2/test_remote_key_resource.py
@@ -37,11 +37,11 @@ from tests.utils import default_config
 
 
 class BaseRemoteKeyResourceTestCase(unittest.HomeserverTestCase):
     def make_homeserver(self, reactor, clock):
         self.http_client = Mock()
-        return self.setup_test_homeserver(http_client=self.http_client)
+        return self.setup_test_homeserver(federation_http_client=self.http_client)
 
     def create_test_resource(self):
         return create_resource_tree(
             {"/_matrix/key/v2": KeyApiV2Resource(self.hs)}, root_resource=NoResource()
         )
@@ -170,11 +170,11 @@ class EndToEndPerspectivesTests(BaseRemoteKeyResourceTestCase):
                     )
                 },
             }
         ]
         self.hs2 = self.setup_test_homeserver(
-            http_client=self.http_client2, config=config
+            federation_http_client=self.http_client2, config=config
         )
 
         # wire up outbound POST /key/v2/query requests from hs2 so that they
         # will be forwarded to hs1
         async def post_json(destination, path, data):
diff --git a/tests/rest/media/v1/test_media_storage.py b/tests/rest/media/v1/test_media_storage.py
index 2a3b2a8f27..4c749f1a61 100644
--- a/tests/rest/media/v1/test_media_storage.py
+++ b/tests/rest/media/v1/test_media_storage.py
@@ -212,11 +212,11 @@ class MediaRepoTests(unittest.HomeserverTestCase):
             "store_remote": True,
             "config": {"directory": self.storage_path},
         }
         config["media_storage_providers"] = [provider_config]
 
-        hs = self.setup_test_homeserver(config=config, http_client=client)
+        hs = self.setup_test_homeserver(config=config, federation_http_client=client)
 
         return hs
 
     def prepare(self, reactor, clock, hs):
 
diff --git a/tests/storage/test_e2e_room_keys.py b/tests/storage/test_e2e_room_keys.py
index 35dafbb904..3d7760d5d9 100644
--- a/tests/storage/test_e2e_room_keys.py
+++ b/tests/storage/test_e2e_room_keys.py
@@ -24,11 +24,11 @@ room_key = {
 }
 
 
 class E2eRoomKeysHandlerTestCase(unittest.HomeserverTestCase):
     def make_homeserver(self, reactor, clock):
-        hs = self.setup_test_homeserver("server", http_client=None)
+        hs = self.setup_test_homeserver("server", federation_http_client=None)
         self.store = hs.get_datastore()
         return hs
 
     def test_room_keys_version_delete(self):
         # test that deleting a room key backup deletes the keys
diff --git a/tests/storage/test_purge.py b/tests/storage/test_purge.py
index cc1f3c53c5..a06ad2c03e 100644
--- a/tests/storage/test_purge.py
+++ b/tests/storage/test_purge.py
@@ -25,11 +25,11 @@ class PurgeTests(HomeserverTestCase):
 
     user_id = "@red:server"
     servlets = [room.register_servlets]
 
     def make_homeserver(self, reactor, clock):
-        hs = self.setup_test_homeserver("server", http_client=None)
+        hs = self.setup_test_homeserver("server", federation_http_client=None)
         return hs
 
     def prepare(self, reactor, clock, hs):
         self.room_id = self.helper.create_room_as(self.user_id)
 
diff --git a/tests/storage/test_redaction.py b/tests/storage/test_redaction.py
index d4f9e809db..fd0add5db3 100644
--- a/tests/storage/test_redaction.py
+++ b/tests/storage/test_redaction.py
@@ -32,11 +32,11 @@ from tests.utils import create_room
 class RedactionTestCase(unittest.HomeserverTestCase):
     def make_homeserver(self, reactor, clock):
         config = self.default_config()
         config["redaction_retention_period"] = "30d"
         return self.setup_test_homeserver(
-            resource_for_federation=Mock(), http_client=None, config=config
+            resource_for_federation=Mock(), federation_http_client=None, config=config
         )
 
     def prepare(self, reactor, clock, hs):
         self.store = hs.get_datastore()
         self.storage = hs.get_storage()
diff --git a/tests/storage/test_roommember.py b/tests/storage/test_roommember.py
index ff972daeaa..5ba1db2332 100644
--- a/tests/storage/test_roommember.py
+++ b/tests/storage/test_roommember.py
@@ -34,11 +34,11 @@ class RoomMemberStoreTestCase(unittest.HomeserverTestCase):
         room.register_servlets,
     ]
 
     def make_homeserver(self, reactor, clock):
         hs = self.setup_test_homeserver(
-            resource_for_federation=Mock(), http_client=None
+            resource_for_federation=Mock(), federation_http_client=None
         )
         return hs
 
     def prepare(self, reactor, clock, hs: TestHomeServer):
 
diff --git a/tests/test_federation.py b/tests/test_federation.py
index 1ce4ea3a01..fa45f8b3b7 100644
--- a/tests/test_federation.py
+++ b/tests/test_federation.py
@@ -35,11 +35,11 @@ class MessageAcceptTests(unittest.HomeserverTestCase):
         self.http_client = Mock()
         self.reactor = ThreadedMemoryReactorClock()
         self.hs_clock = Clock(self.reactor)
         self.homeserver = setup_test_homeserver(
             self.addCleanup,
-            http_client=self.http_client,
+            federation_http_client=self.http_client,
             clock=self.hs_clock,
             reactor=self.reactor,
         )
 
         user_id = UserID("us", "test")
diff --git a/tests/test_server.py b/tests/test_server.py
index c387a85f2e..6b2d2f0401 100644
--- a/tests/test_server.py
+++ b/tests/test_server.py
@@ -36,11 +36,14 @@ from tests.server import (
 class JsonResourceTests(unittest.TestCase):
     def setUp(self):
         self.reactor = ThreadedMemoryReactorClock()
         self.hs_clock = Clock(self.reactor)
         self.homeserver = setup_test_homeserver(
-            self.addCleanup, http_client=None, clock=self.hs_clock, reactor=self.reactor
+            self.addCleanup,
+            federation_http_client=None,
+            clock=self.hs_clock,
+            reactor=self.reactor,
         )
 
     def test_handler_for_request(self):
         """
         JsonResource.handler_for_request gives correctly decoded URL args to

Vulnerable code: # Prevent federation requests from being sent to the following
# blacklist IP address CIDR ranges. If this option is not specified, or
# specified with an empty list, no ip range blacklist will be enforced.
# As of Synapse v1.4.0 this option also affects any outbound requests to identity
# servers provided by user input.
federation_ip_range_blacklist:
        self.http_client = hs.get_simple_http_client()
        self.federation_ip_range_blacklist = config.get(
            "federation_ip_range_blacklist", []
        )
            self.federation_ip_range_blacklist = IPSet(
                self.federation_ip_range_blacklist
            )

            # Always blacklist 0.0.0.0, ::
            self.federation_ip_range_blacklist.update(["0.0.0.0", "::"])
        # Prevent federation requests from being sent to the following
        # blacklist IP address CIDR ranges. If this option is not specified, or
        # specified with an empty list, no ip range blacklist will be enforced.
        # As of Synapse v1.4.0 this option also affects any outbound requests to identity
        # servers provided by user input.
        federation_ip_range_blacklist:
        self.client = hs.get_http_client()
        self.client = hs.get_http_client()
        self.http_client = hs.get_simple_http_client()
        self.client = hs.get_http_client()
        self.http_client = hs.get_simple_http_client()
        # We create a blacklisting instance of SimpleHttpClient for contacting identity
        # servers specified by clients
        self.federation_http_client = hs.get_http_client()
class IPBlacklistingResolver:
            real_reactor = hs.get_reactor()
            nameResolver = IPBlacklistingResolver(
                real_reactor, self._ip_whitelist, self._ip_blacklist

            @implementer(IReactorPluggableNameResolver)
            class Reactor:
                def __getattr__(_self, attr):
                    if attr == "nameResolver":
                        return nameResolver
                    else:
                        return getattr(real_reactor, attr)

            self.reactor = Reactor()
from netaddr import AddrFormatError, IPAddress
                agent=Agent(
                    pool=self._pool,
                    contextFactory=tls_client_options_factory,
from zope.interface import implementer
from twisted.internet.interfaces import IReactorPluggableNameResolver, IReactorTime
    IPBlacklistingResolver,
        real_reactor = hs.get_reactor()

        nameResolver = IPBlacklistingResolver(
            real_reactor, None, hs.config.federation_ip_range_blacklist
        @implementer(IReactorPluggableNameResolver)
        class Reactor:
            def __getattr__(_self, attr):
                if attr == "nameResolver":
                    return nameResolver
                else:
                    return getattr(real_reactor, attr)

        self.reactor = Reactor()

            self.reactor, tls_client_options_factory, user_agent
        self.http_client = hs.get_proxied_http_client()
        self.client = hs.get_http_client()
    @cache_in_self
    def get_http_client(self) -> MatrixFederationHttpClient:
        tls_client_options_factory = context_factory.FederationPolicyForHTTPS(
            self.config
        )
        return MatrixFederationHttpClient(self, tls_client_options_factory)

            self.addCleanup, http_client=self.mock_http_client, keyring=Mock(),
            http_client=None, homeserver_to_use=GenericWorkerServer
            http_client=None, homeserver_to_use=GenericWorkerServer
            http_client=None, homeserver_to_use=SynapseHomeServer
        hs = self.setup_test_homeserver(http_client=self.http_client)
        return self.setup_test_homeserver(http_client=self.http_client, config=config)
        hs = self.setup_test_homeserver("server", http_client=None)
        hs = self.setup_test_homeserver("server", http_client=None)
            http_client=None,
        hs = self.setup_test_homeserver(http_client=None)
            "server", http_client=None, federation_sender=Mock()
            http_client=None,
            http_client=mock_federation_client,
        put_json = self.hs.get_http_client().put_json
        put_json = self.hs.get_http_client().put_json
        hs = self.setup_test_homeserver(config=config, proxied_http_client=m)
            http_client=None,
                useful to e.g. pass some mocks for things like `http_client`
            http_client=mock_client,
            http_client=mock_client1,
            http_client=mock_client2,
            http_client=mock_client1,
            http_client=mock_client2,
            proxied_http_client=http_client_mock,
            proxied_http_client=http_client_mock1,
            proxied_http_client=http_client_mock2,
        hs = self.setup_test_homeserver(config=config, http_client=client)
            http_client=None,
            http_client=None,
            "red", http_client=None, federation_client=Mock(),
            "red", http_client=None, federation_client=Mock(),
        return self.setup_test_homeserver(http_client=self.http_client)
            http_client=self.http_client2, config=config
        hs = self.setup_test_homeserver(config=config, http_client=client)
        hs = self.setup_test_homeserver("server", http_client=None)
        hs = self.setup_test_homeserver("server", http_client=None)
            resource_for_federation=Mock(), http_client=None, config=config
            resource_for_federation=Mock(), http_client=None
            http_client=self.http_client,
            self.addCleanup, http_client=None, clock=self.hs_clock, reactor=self.reactor
Fixed code: Apply the `federation_ip_range_blacklist` to push and key revocation requests.
# Prevent outgoing requests from being sent to the following blacklisted IP address
# CIDR ranges. If this option is not specified, or specified with an empty list,
# no IP range blacklist will be enforced.
# The blacklist applies to the outbound requests for federation, identity servers,
# push servers, and for checking key validitity for third-party invite events.
# This option replaces federation_ip_range_blacklist in Synapse v1.24.0.
#
ip_range_blacklist:
        ip_range_blacklist = config.get("ip_range_blacklist", [])
            self.ip_range_blacklist = IPSet(ip_range_blacklist)
        except Exception as e:
            raise ConfigError("Invalid range(s) provided in ip_range_blacklist: %s" % e)
        # Always blacklist 0.0.0.0, ::
        self.ip_range_blacklist.update(["0.0.0.0", "::"])

        # The federation_ip_range_blacklist is used for backwards-compatibility
        # and only applies to federation and identity servers. If it is not given,
        # default to ip_range_blacklist.
        federation_ip_range_blacklist = config.get(
            "federation_ip_range_blacklist", ip_range_blacklist
        )
        try:
            self.federation_ip_range_blacklist = IPSet(federation_ip_range_blacklist)
        # Always blacklist 0.0.0.0, ::
        self.federation_ip_range_blacklist.update(["0.0.0.0", "::"])
        # Prevent outgoing requests from being sent to the following blacklisted IP address
        # CIDR ranges. If this option is not specified, or specified with an empty list,
        # no IP range blacklist will be enforced.
        # The blacklist applies to the outbound requests for federation, identity servers,
        # push servers, and for checking key validitity for third-party invite events.
        # This option replaces federation_ip_range_blacklist in Synapse v1.24.0.
        #
        ip_range_blacklist:
        self.client = hs.get_federation_http_client()
        self.client = hs.get_federation_http_client()
        self.client = hs.get_federation_http_client()
        self.http_client = hs.get_proxied_blacklisted_http_client()
        # An HTTP client for contacting trusted URLs.
        # An HTTP client for contacting identity servers specified by clients.
        self.federation_http_client = hs.get_federation_http_client()
class _IPBlacklistingResolver:
@implementer(IReactorPluggableNameResolver)
class BlacklistingReactorWrapper:
    """
    A Reactor wrapper which will prevent DNS resolution to blacklisted IP
    addresses, to prevent DNS rebinding.
    """

    def __init__(
        self,
        reactor: IReactorPluggableNameResolver,
        ip_whitelist: Optional[IPSet],
        ip_blacklist: IPSet,
    ):
        self._reactor = reactor

        # We need to use a DNS resolver which filters out blacklisted IP
        # addresses, to prevent DNS rebinding.
        self._nameResolver = _IPBlacklistingResolver(
            self._reactor, ip_whitelist, ip_blacklist
        )

    def __getattr__(self, attr: str) -> Any:
        # Passthrough to the real reactor except for the DNS resolver.
        if attr == "nameResolver":
            return self._nameResolver
        else:
            return getattr(self._reactor, attr)


            self.reactor = BlacklistingReactorWrapper(
                hs.get_reactor(), self._ip_whitelist, self._ip_blacklist
from netaddr import AddrFormatError, IPAddress, IPSet
from synapse.http.client import BlacklistingAgentWrapper
        ip_blacklist: IPSet,
            # Note that the name resolver has already been wrapped in a
            # IPBlacklistingResolver by MatrixFederationHttpClient.
                agent=BlacklistingAgentWrapper(
                    Agent(
                        self._reactor,
                        pool=self._pool,
                        contextFactory=tls_client_options_factory,
                    ),
                    ip_blacklist=ip_blacklist,
from twisted.internet.interfaces import IReactorTime
    BlacklistingReactorWrapper,
        self.reactor = BlacklistingReactorWrapper(
            hs.get_reactor(), None, hs.config.federation_ip_range_blacklist
            self.reactor,
            tls_client_options_factory,
            user_agent,
            hs.config.federation_ip_range_blacklist,
        self.http_client = hs.get_proxied_blacklisted_http_client()
        self.client = hs.get_federation_http_client()
        """
        An HTTP client with no special configuration.
        """
        """
        An HTTP client that uses configured HTTP(S) proxies.
        """
        return SimpleHttpClient(
            self,
            http_proxy=os.getenvb(b"http_proxy"),
            https_proxy=os.getenvb(b"HTTPS_PROXY"),
        )

    @cache_in_self
    def get_proxied_blacklisted_http_client(self) -> SimpleHttpClient:
        """
        An HTTP client that uses configured HTTP(S) proxies and blacklists IPs
        based on the IP range blacklist.
        """
            ip_blacklist=self.config.ip_range_blacklist,
    @cache_in_self
    def get_federation_http_client(self) -> MatrixFederationHttpClient:
        """
        An HTTP client for federation.
        """
        tls_client_options_factory = context_factory.FederationPolicyForHTTPS(
            self.config
        )
        return MatrixFederationHttpClient(self, tls_client_options_factory)

            self.addCleanup,
            federation_http_client=self.mock_http_client,
            keyring=Mock(),
            federation_http_client=None, homeserver_to_use=GenericWorkerServer
            federation_http_client=None, homeserver_to_use=GenericWorkerServer
            federation_http_client=None, homeserver_to_use=SynapseHomeServer
        hs = self.setup_test_homeserver(federation_http_client=self.http_client)
        return self.setup_test_homeserver(
            federation_http_client=self.http_client, config=config
        )
        hs = self.setup_test_homeserver("server", federation_http_client=None)
        hs = self.setup_test_homeserver("server", federation_http_client=None)
            federation_http_client=None,
        hs = self.setup_test_homeserver(federation_http_client=None)
            "server", federation_http_client=None, federation_sender=Mock()
            federation_http_client=None,
            federation_http_client=mock_federation_client,
        put_json = self.hs.get_federation_http_client().put_json
        put_json = self.hs.get_federation_http_client().put_json
from netaddr import IPSet
            ip_blacklist=IPSet(),
            ip_blacklist=IPSet(),
        hs = self.setup_test_homeserver(
            config=config, proxied_blacklisted_http_client=m
        )
            federation_http_client=None,
                useful to e.g. pass some mocks for things like `federation_http_client`
            federation_http_client=mock_client,
            federation_http_client=mock_client1,
            federation_http_client=mock_client2,
            federation_http_client=mock_client1,
            federation_http_client=mock_client2,
            proxied_blacklisted_http_client=http_client_mock,
            proxied_blacklisted_http_client=http_client_mock1,
            proxied_blacklisted_http_client=http_client_mock2,
        hs = self.setup_test_homeserver(config=config, federation_http_client=client)
            federation_http_client=None,
            federation_http_client=None,
            "red", federation_http_client=None, federation_client=Mock(),
            "red", federation_http_client=None, federation_client=Mock(),
        return self.setup_test_homeserver(federation_http_client=self.http_client)
            federation_http_client=self.http_client2, config=config
        hs = self.setup_test_homeserver(config=config, federation_http_client=client)
        hs = self.setup_test_homeserver("server", federation_http_client=None)
        hs = self.setup_test_homeserver("server", federation_http_client=None)
            resource_for_federation=Mock(), federation_http_client=None, config=config
            resource_for_federation=Mock(), federation_http_client=None
            federation_http_client=self.http_client,
            self.addCleanup,
            federation_http_client=None,
            clock=self.hs_clock,
            reactor=self.reactor,