forked from MirrorHub/synapse
Merge pull request #3465 from matrix-org/matthew/as_ip_lock
add ip_range_whitelist parameter to limit where ASes can connect from
This commit is contained in:
commit
fc0e17b3e5
5 changed files with 69 additions and 5 deletions
1
changelog.d/3465.feature
Normal file
1
changelog.d/3465.feature
Normal file
|
@ -0,0 +1 @@
|
||||||
|
Add optional ip_range_whitelist param to AS registration files to lock AS IP access
|
|
@ -19,6 +19,7 @@ from six import itervalues
|
||||||
|
|
||||||
import pymacaroons
|
import pymacaroons
|
||||||
from twisted.internet import defer
|
from twisted.internet import defer
|
||||||
|
from netaddr import IPAddress
|
||||||
|
|
||||||
import synapse.types
|
import synapse.types
|
||||||
from synapse import event_auth
|
from synapse import event_auth
|
||||||
|
@ -244,6 +245,11 @@ class Auth(object):
|
||||||
if app_service is None:
|
if app_service is None:
|
||||||
defer.returnValue((None, None))
|
defer.returnValue((None, None))
|
||||||
|
|
||||||
|
if app_service.ip_range_whitelist:
|
||||||
|
ip_address = IPAddress(self.hs.get_ip_from_request(request))
|
||||||
|
if ip_address not in app_service.ip_range_whitelist:
|
||||||
|
defer.returnValue((None, None))
|
||||||
|
|
||||||
if "user_id" not in request.args:
|
if "user_id" not in request.args:
|
||||||
defer.returnValue((app_service.sender, app_service))
|
defer.returnValue((app_service.sender, app_service))
|
||||||
|
|
||||||
|
|
|
@ -85,7 +85,8 @@ class ApplicationService(object):
|
||||||
NS_LIST = [NS_USERS, NS_ALIASES, NS_ROOMS]
|
NS_LIST = [NS_USERS, NS_ALIASES, NS_ROOMS]
|
||||||
|
|
||||||
def __init__(self, token, hostname, url=None, namespaces=None, hs_token=None,
|
def __init__(self, token, hostname, url=None, namespaces=None, hs_token=None,
|
||||||
sender=None, id=None, protocols=None, rate_limited=True):
|
sender=None, id=None, protocols=None, rate_limited=True,
|
||||||
|
ip_range_whitelist=None):
|
||||||
self.token = token
|
self.token = token
|
||||||
self.url = url
|
self.url = url
|
||||||
self.hs_token = hs_token
|
self.hs_token = hs_token
|
||||||
|
@ -93,6 +94,7 @@ class ApplicationService(object):
|
||||||
self.server_name = hostname
|
self.server_name = hostname
|
||||||
self.namespaces = self._check_namespaces(namespaces)
|
self.namespaces = self._check_namespaces(namespaces)
|
||||||
self.id = id
|
self.id = id
|
||||||
|
self.ip_range_whitelist = ip_range_whitelist
|
||||||
|
|
||||||
if "|" in self.id:
|
if "|" in self.id:
|
||||||
raise Exception("application service ID cannot contain '|' character")
|
raise Exception("application service ID cannot contain '|' character")
|
||||||
|
|
|
@ -17,6 +17,8 @@ from ._base import Config, ConfigError
|
||||||
from synapse.appservice import ApplicationService
|
from synapse.appservice import ApplicationService
|
||||||
from synapse.types import UserID
|
from synapse.types import UserID
|
||||||
|
|
||||||
|
from netaddr import IPSet
|
||||||
|
|
||||||
import yaml
|
import yaml
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
@ -154,6 +156,13 @@ def _load_appservice(hostname, as_info, config_filename):
|
||||||
" will not receive events or queries.",
|
" will not receive events or queries.",
|
||||||
config_filename,
|
config_filename,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
ip_range_whitelist = None
|
||||||
|
if as_info.get('ip_range_whitelist'):
|
||||||
|
ip_range_whitelist = IPSet(
|
||||||
|
as_info.get('ip_range_whitelist')
|
||||||
|
)
|
||||||
|
|
||||||
return ApplicationService(
|
return ApplicationService(
|
||||||
token=as_info["as_token"],
|
token=as_info["as_token"],
|
||||||
hostname=hostname,
|
hostname=hostname,
|
||||||
|
@ -163,5 +172,6 @@ def _load_appservice(hostname, as_info, config_filename):
|
||||||
sender=user_id,
|
sender=user_id,
|
||||||
id=as_info["id"],
|
id=as_info["id"],
|
||||||
protocols=protocols,
|
protocols=protocols,
|
||||||
rate_limited=rate_limited
|
rate_limited=rate_limited,
|
||||||
|
ip_range_whitelist=ip_range_whitelist,
|
||||||
)
|
)
|
||||||
|
|
|
@ -86,16 +86,53 @@ class AuthTestCase(unittest.TestCase):
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def test_get_user_by_req_appservice_valid_token(self):
|
def test_get_user_by_req_appservice_valid_token(self):
|
||||||
app_service = Mock(token="foobar", url="a_url", sender=self.test_user)
|
app_service = Mock(
|
||||||
|
token="foobar", url="a_url", sender=self.test_user,
|
||||||
|
ip_range_whitelist=None,
|
||||||
|
)
|
||||||
self.store.get_app_service_by_token = Mock(return_value=app_service)
|
self.store.get_app_service_by_token = Mock(return_value=app_service)
|
||||||
self.store.get_user_by_access_token = Mock(return_value=None)
|
self.store.get_user_by_access_token = Mock(return_value=None)
|
||||||
|
|
||||||
request = Mock(args={})
|
request = Mock(args={})
|
||||||
|
request.getClientIP.return_value = "127.0.0.1"
|
||||||
request.args["access_token"] = [self.test_token]
|
request.args["access_token"] = [self.test_token]
|
||||||
request.requestHeaders.getRawHeaders = mock_getRawHeaders()
|
request.requestHeaders.getRawHeaders = mock_getRawHeaders()
|
||||||
requester = yield self.auth.get_user_by_req(request)
|
requester = yield self.auth.get_user_by_req(request)
|
||||||
self.assertEquals(requester.user.to_string(), self.test_user)
|
self.assertEquals(requester.user.to_string(), self.test_user)
|
||||||
|
|
||||||
|
@defer.inlineCallbacks
|
||||||
|
def test_get_user_by_req_appservice_valid_token_good_ip(self):
|
||||||
|
from netaddr import IPSet
|
||||||
|
app_service = Mock(
|
||||||
|
token="foobar", url="a_url", sender=self.test_user,
|
||||||
|
ip_range_whitelist=IPSet(["192.168/16"]),
|
||||||
|
)
|
||||||
|
self.store.get_app_service_by_token = Mock(return_value=app_service)
|
||||||
|
self.store.get_user_by_access_token = Mock(return_value=None)
|
||||||
|
|
||||||
|
request = Mock(args={})
|
||||||
|
request.getClientIP.return_value = "192.168.10.10"
|
||||||
|
request.args["access_token"] = [self.test_token]
|
||||||
|
request.requestHeaders.getRawHeaders = mock_getRawHeaders()
|
||||||
|
requester = yield self.auth.get_user_by_req(request)
|
||||||
|
self.assertEquals(requester.user.to_string(), self.test_user)
|
||||||
|
|
||||||
|
def test_get_user_by_req_appservice_valid_token_bad_ip(self):
|
||||||
|
from netaddr import IPSet
|
||||||
|
app_service = Mock(
|
||||||
|
token="foobar", url="a_url", sender=self.test_user,
|
||||||
|
ip_range_whitelist=IPSet(["192.168/16"]),
|
||||||
|
)
|
||||||
|
self.store.get_app_service_by_token = Mock(return_value=app_service)
|
||||||
|
self.store.get_user_by_access_token = Mock(return_value=None)
|
||||||
|
|
||||||
|
request = Mock(args={})
|
||||||
|
request.getClientIP.return_value = "131.111.8.42"
|
||||||
|
request.args["access_token"] = [self.test_token]
|
||||||
|
request.requestHeaders.getRawHeaders = mock_getRawHeaders()
|
||||||
|
d = self.auth.get_user_by_req(request)
|
||||||
|
self.failureResultOf(d, AuthError)
|
||||||
|
|
||||||
def test_get_user_by_req_appservice_bad_token(self):
|
def test_get_user_by_req_appservice_bad_token(self):
|
||||||
self.store.get_app_service_by_token = Mock(return_value=None)
|
self.store.get_app_service_by_token = Mock(return_value=None)
|
||||||
self.store.get_user_by_access_token = Mock(return_value=None)
|
self.store.get_user_by_access_token = Mock(return_value=None)
|
||||||
|
@ -119,12 +156,16 @@ class AuthTestCase(unittest.TestCase):
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def test_get_user_by_req_appservice_valid_token_valid_user_id(self):
|
def test_get_user_by_req_appservice_valid_token_valid_user_id(self):
|
||||||
masquerading_user_id = "@doppelganger:matrix.org"
|
masquerading_user_id = "@doppelganger:matrix.org"
|
||||||
app_service = Mock(token="foobar", url="a_url", sender=self.test_user)
|
app_service = Mock(
|
||||||
|
token="foobar", url="a_url", sender=self.test_user,
|
||||||
|
ip_range_whitelist=None,
|
||||||
|
)
|
||||||
app_service.is_interested_in_user = Mock(return_value=True)
|
app_service.is_interested_in_user = Mock(return_value=True)
|
||||||
self.store.get_app_service_by_token = Mock(return_value=app_service)
|
self.store.get_app_service_by_token = Mock(return_value=app_service)
|
||||||
self.store.get_user_by_access_token = Mock(return_value=None)
|
self.store.get_user_by_access_token = Mock(return_value=None)
|
||||||
|
|
||||||
request = Mock(args={})
|
request = Mock(args={})
|
||||||
|
request.getClientIP.return_value = "127.0.0.1"
|
||||||
request.args["access_token"] = [self.test_token]
|
request.args["access_token"] = [self.test_token]
|
||||||
request.args["user_id"] = [masquerading_user_id]
|
request.args["user_id"] = [masquerading_user_id]
|
||||||
request.requestHeaders.getRawHeaders = mock_getRawHeaders()
|
request.requestHeaders.getRawHeaders = mock_getRawHeaders()
|
||||||
|
@ -133,12 +174,16 @@ class AuthTestCase(unittest.TestCase):
|
||||||
|
|
||||||
def test_get_user_by_req_appservice_valid_token_bad_user_id(self):
|
def test_get_user_by_req_appservice_valid_token_bad_user_id(self):
|
||||||
masquerading_user_id = "@doppelganger:matrix.org"
|
masquerading_user_id = "@doppelganger:matrix.org"
|
||||||
app_service = Mock(token="foobar", url="a_url", sender=self.test_user)
|
app_service = Mock(
|
||||||
|
token="foobar", url="a_url", sender=self.test_user,
|
||||||
|
ip_range_whitelist=None,
|
||||||
|
)
|
||||||
app_service.is_interested_in_user = Mock(return_value=False)
|
app_service.is_interested_in_user = Mock(return_value=False)
|
||||||
self.store.get_app_service_by_token = Mock(return_value=app_service)
|
self.store.get_app_service_by_token = Mock(return_value=app_service)
|
||||||
self.store.get_user_by_access_token = Mock(return_value=None)
|
self.store.get_user_by_access_token = Mock(return_value=None)
|
||||||
|
|
||||||
request = Mock(args={})
|
request = Mock(args={})
|
||||||
|
request.getClientIP.return_value = "127.0.0.1"
|
||||||
request.args["access_token"] = [self.test_token]
|
request.args["access_token"] = [self.test_token]
|
||||||
request.args["user_id"] = [masquerading_user_id]
|
request.args["user_id"] = [masquerading_user_id]
|
||||||
request.requestHeaders.getRawHeaders = mock_getRawHeaders()
|
request.requestHeaders.getRawHeaders = mock_getRawHeaders()
|
||||||
|
|
Loading…
Reference in a new issue