-
-
Notifications
You must be signed in to change notification settings - Fork 2.1k
support federation queries through http connect proxy #10475
Changes from 21 commits
85081fa
7115567
ef8792a
781da36
196af98
86b3623
3c7c3b6
cee7f65
e8cb087
73c5ff5
4cd4a48
1595da4
5acd7ce
3342bda
0bf5ac8
b81eaaf
08b19a6
70a8b11
b68a14d
b766a0f
eab6c74
73a7380
007ea8a
258fab8
08823be
67522c3
512166b
ab2067d
e8fd305
d0933e5
c953242
2720c88
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| Add support for sending federation requests through a proxy. Contributed by @Bubu and @dklimpel. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -14,6 +14,10 @@ | |
| import logging | ||
| import urllib.parse | ||
| from typing import Any, Generator, List, Optional | ||
| from urllib.request import ( # type: ignore[attr-defined] | ||
| getproxies_environment, | ||
| proxy_bypass_environment, | ||
| ) | ||
|
|
||
| from netaddr import AddrFormatError, IPAddress, IPSet | ||
| from zope.interface import implementer | ||
|
|
@@ -25,14 +29,17 @@ | |
| IReactorCore, | ||
| IStreamClientEndpoint, | ||
| ) | ||
| from twisted.web.client import URI, Agent, HTTPConnectionPool | ||
| from twisted.web.client import URI, Agent, BrowserLikePolicyForHTTPS, HTTPConnectionPool | ||
| from twisted.web.http_headers import Headers | ||
| from twisted.web.iweb import IAgent, IAgentEndpointFactory, IBodyProducer, IResponse | ||
|
|
||
| from synapse.crypto.context_factory import FederationPolicyForHTTPS | ||
| from synapse.http import proxyagent | ||
| from synapse.http.client import BlacklistingAgentWrapper | ||
| from synapse.http.connectproxyclient import HTTPConnectProxyEndpoint | ||
| from synapse.http.federation.srv_resolver import Server, SrvResolver | ||
| from synapse.http.federation.well_known_resolver import WellKnownResolver | ||
| from synapse.http.proxyagent import ProxyAgent | ||
| from synapse.logging.context import make_deferred_yieldable, run_in_background | ||
| from synapse.types import ISynapseReactor | ||
| from synapse.util import Clock | ||
|
|
@@ -57,6 +64,12 @@ class MatrixFederationAgent: | |
| user_agent: | ||
| The user agent header to use for federation requests. | ||
|
|
||
| ip_blacklist: Disallowed IP addresses. | ||
|
|
||
| proxy_reactor: twisted reactor to use for connections to the proxy server | ||
| reactor might have some blacklisting applied (i.e. for DNS queries), | ||
| but we need unblocked access to the proxy. | ||
|
|
||
| _srv_resolver: | ||
| SrvResolver implementation to use for looking up SRV records. None | ||
| to use a default implementation. | ||
|
|
@@ -72,6 +85,7 @@ def __init__( | |
| tls_client_options_factory: Optional[FederationPolicyForHTTPS], | ||
| user_agent: bytes, | ||
| ip_blacklist: IPSet, | ||
| proxy_reactor: Optional[ISynapseReactor] = None, | ||
| _srv_resolver: Optional[SrvResolver] = None, | ||
| _well_known_resolver: Optional[WellKnownResolver] = None, | ||
| ): | ||
|
|
@@ -82,10 +96,18 @@ def __init__( | |
| self._pool.maxPersistentPerHost = 5 | ||
| self._pool.cachedConnectionTimeout = 2 * 60 | ||
|
|
||
| if proxy_reactor is None: | ||
| self.proxy_reactor = reactor | ||
| else: | ||
| self.proxy_reactor = proxy_reactor | ||
|
|
||
| self._agent = Agent.usingEndpointFactory( | ||
| self._reactor, | ||
| MatrixHostnameEndpointFactory( | ||
| reactor, tls_client_options_factory, _srv_resolver | ||
| reactor, | ||
| self.proxy_reactor, | ||
| tls_client_options_factory, | ||
| _srv_resolver, | ||
| ), | ||
| pool=self._pool, | ||
| ) | ||
|
|
@@ -97,10 +119,12 @@ def __init__( | |
| _well_known_resolver = WellKnownResolver( | ||
| self._reactor, | ||
| agent=BlacklistingAgentWrapper( | ||
| Agent( | ||
| ProxyAgent( | ||
| self._reactor, | ||
| self.proxy_reactor, | ||
| pool=self._pool, | ||
| contextFactory=tls_client_options_factory, | ||
| use_proxy=True, | ||
| ), | ||
| ip_blacklist=ip_blacklist, | ||
| ), | ||
|
|
@@ -200,20 +224,23 @@ class MatrixHostnameEndpointFactory: | |
| def __init__( | ||
| self, | ||
| reactor: IReactorCore, | ||
| proxy_reactor: IReactorCore, | ||
| tls_client_options_factory: Optional[FederationPolicyForHTTPS], | ||
| srv_resolver: Optional[SrvResolver], | ||
| ): | ||
| self._reactor = reactor | ||
| self._proxy_reactor = proxy_reactor | ||
| self._tls_client_options_factory = tls_client_options_factory | ||
|
|
||
| if srv_resolver is None: | ||
| srv_resolver = SrvResolver() | ||
|
|
||
| self._srv_resolver = srv_resolver | ||
|
|
||
| def endpointForURI(self, parsed_uri): | ||
| def endpointForURI(self, parsed_uri: URI): | ||
| return MatrixHostnameEndpoint( | ||
| self._reactor, | ||
| self._proxy_reactor, | ||
| self._tls_client_options_factory, | ||
| self._srv_resolver, | ||
| parsed_uri, | ||
|
|
@@ -227,6 +254,9 @@ class MatrixHostnameEndpoint: | |
|
|
||
| Args: | ||
| reactor: twisted reactor to use for underlying requests | ||
| proxy_reactor: twisted reactor to use for connections to the proxy server | ||
| reactor might have some blacklisting applied (i.e. for DNS queries), | ||
| but we need unblocked access to the proxy. | ||
| tls_client_options_factory: | ||
| factory to use for fetching client tls options, or none to disable TLS. | ||
| srv_resolver: The SRV resolver to use | ||
|
|
@@ -236,14 +266,28 @@ class MatrixHostnameEndpoint: | |
| def __init__( | ||
| self, | ||
| reactor: IReactorCore, | ||
| proxy_reactor: IReactorCore, | ||
| tls_client_options_factory: Optional[FederationPolicyForHTTPS], | ||
| srv_resolver: SrvResolver, | ||
| parsed_uri: URI, | ||
| ): | ||
| self._reactor = reactor | ||
|
|
||
| self._parsed_uri = parsed_uri | ||
|
|
||
| # http_proxy is not needed because federation is always over TLS | ||
| proxies = getproxies_environment() | ||
| https_proxy = proxies["https"].encode() if "https" in proxies else None | ||
| self.no_proxy = proxies["no"] if "no" in proxies else None | ||
|
|
||
| ( | ||
| self.https_proxy_endpoint, | ||
| self.https_proxy_creds, | ||
| ) = proxyagent.http_proxy_endpoint( | ||
dklimpel marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| https_proxy, | ||
| proxy_reactor, | ||
| tls_client_options_factory or BrowserLikePolicyForHTTPS(), | ||
|
||
| ) | ||
|
|
||
| # set up the TLS connection params | ||
| # | ||
| # XXX disabling TLS is really only supported here for the benefit of the | ||
|
|
@@ -273,9 +317,42 @@ async def _do_connect(self, protocol_factory: IProtocolFactory) -> None: | |
| host = server.host | ||
| port = server.port | ||
|
|
||
| should_skip_proxy = False | ||
| if self.no_proxy is not None: | ||
| should_skip_proxy = proxy_bypass_environment( | ||
| host.decode(), | ||
| proxies={"no": self.no_proxy}, | ||
| ) | ||
|
|
||
| endpoint: IStreamClientEndpoint | ||
| try: | ||
| logger.debug("Connecting to %s:%i", host.decode("ascii"), port) | ||
| endpoint = HostnameEndpoint(self._reactor, host, port) | ||
| if self.https_proxy_endpoint and not should_skip_proxy: | ||
| logger.debug( | ||
| "Connecting to %s:%i via %s", | ||
| host.decode("ascii"), | ||
| port, | ||
| self.https_proxy_endpoint, | ||
| ) | ||
| connect_headers = Headers() | ||
| # Determine whether we need to set Proxy-Authorization headers | ||
| if self.https_proxy_creds: | ||
| # Set a Proxy-Authorization header | ||
| connect_headers.addRawHeader( | ||
| b"Proxy-Authorization", | ||
| self.https_proxy_creds.as_proxy_authorization_value(), | ||
| ) | ||
|
||
|
|
||
| endpoint = HTTPConnectProxyEndpoint( | ||
| self._reactor, | ||
| self.https_proxy_endpoint, | ||
| host, | ||
| port, | ||
| headers=connect_headers, | ||
| ) | ||
| else: | ||
| logger.debug("Connecting to %s:%i", host.decode("ascii"), port) | ||
| # not using a proxy | ||
| endpoint = HostnameEndpoint(self._reactor, host, port) | ||
| if self._tls_options: | ||
| endpoint = wrapClientTLS(self._tls_options, endpoint) | ||
| result = await make_deferred_yieldable( | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -343,6 +343,7 @@ def __init__(self, hs, tls_client_options_factory): | |
| tls_client_options_factory, | ||
| user_agent, | ||
| hs.config.federation_ip_range_blacklist, | ||
| proxy_reactor=hs.get_reactor(), | ||
|
||
| ) | ||
|
|
||
| # Use a BlacklistingAgentWrapper to prevent circumventing the IP | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.