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

Commit e385c8b

Browse files
authored
Don't apply the IP range blacklist to proxy connections (#9084)
It is expected that the proxy would be on a private IP address so the configured proxy should be connected to regardless of the IP range blacklist.
1 parent fa6deb2 commit e385c8b

File tree

4 files changed

+145
-3
lines changed

4 files changed

+145
-3
lines changed

changelog.d/9084.bugfix

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Don't blacklist connections to the configured proxy. Contributed by @Bubu.

synapse/http/client.py

+1
Original file line numberDiff line numberDiff line change
@@ -341,6 +341,7 @@ def __init__(
341341

342342
self.agent = ProxyAgent(
343343
self.reactor,
344+
hs.get_reactor(),
344345
connectTimeout=15,
345346
contextFactory=self.hs.get_http_client_context_factory(),
346347
pool=pool,

synapse/http/proxyagent.py

+13-3
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,10 @@ class ProxyAgent(_AgentBase):
3939
reactor: twisted reactor to place outgoing
4040
connections.
4141
42+
proxy_reactor: twisted reactor to use for connections to the proxy server
43+
reactor might have some blacklisting applied (i.e. for DNS queries),
44+
but we need unblocked access to the proxy.
45+
4246
contextFactory (IPolicyForHTTPS): A factory for TLS contexts, to control the
4347
verification parameters of OpenSSL. The default is to use a
4448
`BrowserLikePolicyForHTTPS`, so unless you have special
@@ -59,6 +63,7 @@ class ProxyAgent(_AgentBase):
5963
def __init__(
6064
self,
6165
reactor,
66+
proxy_reactor=None,
6267
contextFactory=BrowserLikePolicyForHTTPS(),
6368
connectTimeout=None,
6469
bindAddress=None,
@@ -68,18 +73,23 @@ def __init__(
6873
):
6974
_AgentBase.__init__(self, reactor, pool)
7075

76+
if proxy_reactor is None:
77+
self.proxy_reactor = reactor
78+
else:
79+
self.proxy_reactor = proxy_reactor
80+
7181
self._endpoint_kwargs = {}
7282
if connectTimeout is not None:
7383
self._endpoint_kwargs["timeout"] = connectTimeout
7484
if bindAddress is not None:
7585
self._endpoint_kwargs["bindAddress"] = bindAddress
7686

7787
self.http_proxy_endpoint = _http_proxy_endpoint(
78-
http_proxy, reactor, **self._endpoint_kwargs
88+
http_proxy, self.proxy_reactor, **self._endpoint_kwargs
7989
)
8090

8191
self.https_proxy_endpoint = _http_proxy_endpoint(
82-
https_proxy, reactor, **self._endpoint_kwargs
92+
https_proxy, self.proxy_reactor, **self._endpoint_kwargs
8393
)
8494

8595
self._policy_for_https = contextFactory
@@ -137,7 +147,7 @@ def request(self, method, uri, headers=None, bodyProducer=None):
137147
request_path = uri
138148
elif parsed_uri.scheme == b"https" and self.https_proxy_endpoint:
139149
endpoint = HTTPConnectProxyEndpoint(
140-
self._reactor,
150+
self.proxy_reactor,
141151
self.https_proxy_endpoint,
142152
parsed_uri.host,
143153
parsed_uri.port,

tests/http/test_proxyagent.py

+130
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,14 @@
1515
import logging
1616

1717
import treq
18+
from netaddr import IPSet
1819

1920
from twisted.internet import interfaces # noqa: F401
2021
from twisted.internet.protocol import Factory
2122
from twisted.protocols.tls import TLSMemoryBIOFactory
2223
from twisted.web.http import HTTPChannel
2324

25+
from synapse.http.client import BlacklistingReactorWrapper
2426
from synapse.http.proxyagent import ProxyAgent
2527

2628
from tests.http import TestServerTLSConnectionFactory, get_test_https_policy
@@ -292,6 +294,134 @@ def test_https_request_via_proxy(self):
292294
body = self.successResultOf(treq.content(resp))
293295
self.assertEqual(body, b"result")
294296

297+
def test_http_request_via_proxy_with_blacklist(self):
298+
# The blacklist includes the configured proxy IP.
299+
agent = ProxyAgent(
300+
BlacklistingReactorWrapper(
301+
self.reactor, ip_whitelist=None, ip_blacklist=IPSet(["1.0.0.0/8"])
302+
),
303+
self.reactor,
304+
http_proxy=b"proxy.com:8888",
305+
)
306+
307+
self.reactor.lookups["proxy.com"] = "1.2.3.5"
308+
d = agent.request(b"GET", b"http://test.com")
309+
310+
# there should be a pending TCP connection
311+
clients = self.reactor.tcpClients
312+
self.assertEqual(len(clients), 1)
313+
(host, port, client_factory, _timeout, _bindAddress) = clients[0]
314+
self.assertEqual(host, "1.2.3.5")
315+
self.assertEqual(port, 8888)
316+
317+
# make a test server, and wire up the client
318+
http_server = self._make_connection(
319+
client_factory, _get_test_protocol_factory()
320+
)
321+
322+
# the FakeTransport is async, so we need to pump the reactor
323+
self.reactor.advance(0)
324+
325+
# now there should be a pending request
326+
self.assertEqual(len(http_server.requests), 1)
327+
328+
request = http_server.requests[0]
329+
self.assertEqual(request.method, b"GET")
330+
self.assertEqual(request.path, b"http://test.com")
331+
self.assertEqual(request.requestHeaders.getRawHeaders(b"host"), [b"test.com"])
332+
request.write(b"result")
333+
request.finish()
334+
335+
self.reactor.advance(0)
336+
337+
resp = self.successResultOf(d)
338+
body = self.successResultOf(treq.content(resp))
339+
self.assertEqual(body, b"result")
340+
341+
def test_https_request_via_proxy_with_blacklist(self):
342+
# The blacklist includes the configured proxy IP.
343+
agent = ProxyAgent(
344+
BlacklistingReactorWrapper(
345+
self.reactor, ip_whitelist=None, ip_blacklist=IPSet(["1.0.0.0/8"])
346+
),
347+
self.reactor,
348+
contextFactory=get_test_https_policy(),
349+
https_proxy=b"proxy.com",
350+
)
351+
352+
self.reactor.lookups["proxy.com"] = "1.2.3.5"
353+
d = agent.request(b"GET", b"https://test.com/abc")
354+
355+
# there should be a pending TCP connection
356+
clients = self.reactor.tcpClients
357+
self.assertEqual(len(clients), 1)
358+
(host, port, client_factory, _timeout, _bindAddress) = clients[0]
359+
self.assertEqual(host, "1.2.3.5")
360+
self.assertEqual(port, 1080)
361+
362+
# make a test HTTP server, and wire up the client
363+
proxy_server = self._make_connection(
364+
client_factory, _get_test_protocol_factory()
365+
)
366+
367+
# fish the transports back out so that we can do the old switcheroo
368+
s2c_transport = proxy_server.transport
369+
client_protocol = s2c_transport.other
370+
c2s_transport = client_protocol.transport
371+
372+
# the FakeTransport is async, so we need to pump the reactor
373+
self.reactor.advance(0)
374+
375+
# now there should be a pending CONNECT request
376+
self.assertEqual(len(proxy_server.requests), 1)
377+
378+
request = proxy_server.requests[0]
379+
self.assertEqual(request.method, b"CONNECT")
380+
self.assertEqual(request.path, b"test.com:443")
381+
382+
# tell the proxy server not to close the connection
383+
proxy_server.persistent = True
384+
385+
# this just stops the http Request trying to do a chunked response
386+
# request.setHeader(b"Content-Length", b"0")
387+
request.finish()
388+
389+
# now we can replace the proxy channel with a new, SSL-wrapped HTTP channel
390+
ssl_factory = _wrap_server_factory_for_tls(_get_test_protocol_factory())
391+
ssl_protocol = ssl_factory.buildProtocol(None)
392+
http_server = ssl_protocol.wrappedProtocol
393+
394+
ssl_protocol.makeConnection(
395+
FakeTransport(client_protocol, self.reactor, ssl_protocol)
396+
)
397+
c2s_transport.other = ssl_protocol
398+
399+
self.reactor.advance(0)
400+
401+
server_name = ssl_protocol._tlsConnection.get_servername()
402+
expected_sni = b"test.com"
403+
self.assertEqual(
404+
server_name,
405+
expected_sni,
406+
"Expected SNI %s but got %s" % (expected_sni, server_name),
407+
)
408+
409+
# now there should be a pending request
410+
self.assertEqual(len(http_server.requests), 1)
411+
412+
request = http_server.requests[0]
413+
self.assertEqual(request.method, b"GET")
414+
self.assertEqual(request.path, b"/abc")
415+
self.assertEqual(request.requestHeaders.getRawHeaders(b"host"), [b"test.com"])
416+
request.write(b"result")
417+
request.finish()
418+
419+
self.reactor.advance(0)
420+
421+
resp = self.successResultOf(d)
422+
body = self.successResultOf(treq.content(resp))
423+
self.assertEqual(body, b"result")
424+
295425

296426
def _wrap_server_factory_for_tls(factory, sanlist=None):
297427
"""Wrap an existing Protocol Factory with a test TLSMemoryBIOFactory

0 commit comments

Comments
 (0)