def save_if_hidden_ip(remote_addr: str, server: Server): """ :param remote_addr: remote_addr to save :param server: server who has the remote_addr :return: """ ip = ipaddress.ip_address(remote_addr) if not ip.is_loopback: def get_ip(dns: str): if dns is None: return else: try: return ipaddress.ip_address(socket.gethostbyname(dns)) except: return gate = [gate for gate in server.gates if ip in (gate.ip, get_ip(gate.dns))] if not gate: try: with lock_scope(Scope.CATALOG): for port in set([gate.port for gate in server.gates]): gate = server.add_new_gate(dns_or_ip=remote_addr, port=port, hidden=True) db.session.add(gate) db.session.commit() except errors.NoServerToLock as e: def background_new_gate(): resp = None gates = [dict(dns_or_ip=remote_addr, port=port, hidden=True) for port in set([gate.port for gate in server.gates])] for port in set([gate.port for gate in server.gates]): resp = ntwrk.patch(f"{remote_addr}:{port}", 'api_1_0.serverresource', view_data=dict(server_id=server.id), json=dict(gates=gates), auth=get_root_auth(), timeout=30) if resp.ok: break else: logger.error(f"Unable to create external gate {server}->{remote_addr}." f" Reason: {resp}" if resp else "") # create local to be able to contact node in case needed # with bypass_datamark_update(): # for g in gates: # gate = server.add_new_gate(**g) # gate.last_modified_at = defaults.INITIAL_DATEMARK # db.session.commit() executor.submit(background_new_gate) except errors.LockerError as e: dm_logger.error(f"Unable to lock catalog for saving {remote_addr} from {server}. Reason: {e}") except Exception: dm_logger.exception(f"Unable to save {remote_addr} from {server}")
def test_sqlalchemy_entity_events_server(self, mock_now): mock_now.return_value = dt.datetime(2019, 4, 1, tzinfo=dt.timezone.utc) s = Server('source', port=5000) d = Server('destination', port=5000) db.session.add_all([s, d]) db.session.commit() mock_now.return_value = dt.datetime(2019, 4, 2, tzinfo=dt.timezone.utc) s.route = Route(destination=d, proxy_server_or_gate=d.gates[0], cost=0) db.session.commit() self.assertEqual(dt.datetime(2019, 4, 1, tzinfo=dt.timezone.utc), s.last_modified_at) g = s.add_new_gate(dns_or_ip='dns', port=5000) db.session.add(g) db.session.commit() self.assertEqual(dt.datetime(2019, 4, 1, tzinfo=dt.timezone.utc), s.last_modified_at)
class TestForwardOrDispatch(TestCase, ValidateResponseMixin): # def run(self, result=None): # with patch('dimensigon.web.scopefunc') as mock_scopefunc: # mock_scopefunc.side_effect = lambda: threading.get_ident() # super().run(result) def setUp(self): """Create and configure a new self.app instance for each test.""" # create a temporary file to isolate the database for each test # create the self.app with common test config self.app = Flask(__name__) @self.app.route('/', methods=['GET', 'POST']) @forward_or_dispatch() def hello(): return {'msg': 'default response'} self.app_context = self.app.app_context() self.app_context.push() self.client = self.app.test_client() db.init_app(self.app) db.create_all() self.srv1 = Server(id='bbbbbbbb-1234-5678-1234-56781234bbb1', name='server1', dns_or_ip='192.168.1.9', port=7123) Route(self.srv1, cost=0) self.srv2 = Server(id='bbbbbbbb-1234-5678-1234-56781234bbb2', name='server2', dns_or_ip='192.168.1.10', port=7124) Route(self.srv2, self.srv1, cost=1) db.session.add_all([self.srv1, self.srv2]) db.session.commit() def tearDown(self) -> None: db.session.remove() db.drop_all() self.app_context.pop() @patch('dimensigon.web.decorators.g') @responses.activate def test_forward_or_dispatch_in_content(self, mock_g): mock_g.server = MagicMock(id=self.srv1.id) responses.add(responses.POST, 'https://192.168.1.9:7123/', json={'msg': 'response'}) # check if request is forwarded to the server response = self.client.post('/', json={'destination': self.srv2.id, 'data': None}) self.assertEqual({'msg': 'response'}, response.json) self.assertEqual(1, len(responses.calls)) self.assertEqual('https://192.168.1.9:7123/', responses.calls[0].request.url) # check if request is forwarded to the server response = self.client.post('/', json={'destination': self.srv1.id, 'data': None}) self.assertEqual({'msg': 'default response'}, response.json) self.assertEqual(1, len(responses.calls)) @patch('dimensigon.web.decorators.g') @responses.activate def test_forward_or_dispatch(self, mock_g): mock_g.server = MagicMock(id=self.srv1.id) responses.add(responses.POST, 'https://192.168.1.9:7123/', json={'msg': 'response'}) # check if request is forwarded to the server response = self.client.post('/', json={'data': None}, headers={'D-Destination': self.srv2.id}) self.assertEqual({'msg': 'response'}, response.json) self.assertEqual(1, len(responses.calls)) self.assertEqual('https://192.168.1.9:7123/', responses.calls[0].request.url) # check if request is forwarded to the server response = self.client.post('/', json={'data': None}, headers={'D-Destination': self.srv1.id}) self.assertEqual({'msg': 'default response'}, response.json) self.assertEqual(1, len(responses.calls)) # @patch('dimensigon.web.decorators.g') # def test_forward_or_dispatch_hidden_ip(self, mock_g): # mock_g.server = MagicMock(id=self.srv1.id) # # response = self.client.post('/', json={'data': None}, # headers={'D-Destination': self.srv1.id, # 'D-Source': self.srv2.id}, # environ_base={'REMOTE_ADDR': '10.1.2.3'}) # self.assertEqual({'msg': 'default response'}, response.json) # # self.assertEqual(1, len(self.srv2.hidden_gates)) # hg = self.srv2.hidden_gates[0] # self.assertEqual('10.1.2.3', str(hg.ip)) # self.assertEqual(7124, hg.port) # # response = self.client.post('/', json={'data': None}, # headers={'D-Destination': self.srv1.id, # 'D-Source': self.srv2.id}, # environ_base={'REMOTE_ADDR': '10.1.2.3'}) # self.assertEqual({'msg': 'default response'}, response.json) # # self.assertEqual(1, len(self.srv2.hidden_gates)) # self.assertEqual('10.1.2.3', str(hg.ip)) # self.assertEqual(7124, hg.port) # # response = self.client.post('/', json={'data': None}, # headers={'D-Destination': self.srv1.id, # 'D-Source': self.srv2.id}, # environ_base={'REMOTE_ADDR': '10.1.2.4'}) # self.assertEqual({'msg': 'default response'}, response.json) # # db.session.refresh(self.srv2) # self.assertEqual(1, len(self.srv2.hidden_gates)) # self.assertEqual('10.1.2.4', str(hg.ip)) # self.assertEqual(7124, hg.port) # @patch('dimensigon.web.decorators.g') # def test_forward_or_dispatch_hidden_ip_multiple_ports(self, mock_g): # mock_g.server = MagicMock(id=self.srv1.id) # # g = self.srv2.add_new_gate('127.0.0.1', 7125) # # db.session.add(g) # db.session.commit() # # response = self.client.post('/', json={'data': None}, # headers={'D-Destination': self.srv1.id, # 'D-Source': self.srv2.id}, # environ_base={'REMOTE_ADDR': '10.1.2.3'}) # # self.assertEqual({'msg': 'default response'}, response.json) # # db.session.refresh(self.srv2) # # self.assertEqual(2, len(self.srv2.hidden_gates)) # hg = self.srv2.hidden_gates # self.assertEqual('10.1.2.3', str(hg[0].ip)) # self.assertEqual(7124, hg[0].port) # self.assertEqual('10.1.2.3', str(hg[1].ip)) # self.assertEqual(7125, hg[1].port) # # response = self.client.post('/', json={'data': None}, # headers={'D-Destination': self.srv1.id, # 'D-Source': self.srv2.id}, # environ_base={'REMOTE_ADDR': '10.1.2.3'}) # self.assertEqual({'msg': 'default response'}, response.json) # # db.session.refresh(self.srv2) # # self.assertEqual(2, len(self.srv2.hidden_gates)) # hg = self.srv2.hidden_gates # self.assertEqual('10.1.2.3', str(hg[0].ip)) # self.assertEqual(7124, hg[0].port) # self.assertEqual('10.1.2.3', str(hg[1].ip)) # self.assertEqual(7125, hg[1].port) # # response = self.client.post('/', json={'data': None}, # headers={'D-Destination': self.srv1.id, # 'D-Source': self.srv2.id}, # environ_base={'REMOTE_ADDR': '10.1.2.4'}) # # self.assertEqual({'msg': 'default response'}, response.json) # # db.session.refresh(self.srv2) # # self.assertEqual(2, len(self.srv2.hidden_gates)) # hg = self.srv2.hidden_gates # self.assertEqual('10.1.2.4', str(hg[0].ip)) # self.assertIn(hg[0].port, (7124, 7125)) # self.assertEqual('10.1.2.4', str(hg[1].ip)) # self.assertIn(hg[1].port, (7124, 7125)) # @patch('dimensigon.web.decorators.g') # def test_forward_or_dispatch_using_proxy_server(self, mock_g): # me = Server(id='bbbbbbbb-1234-5678-1234-56781234bbb0', name='server0', # dns_or_ip='192.168.1.8', port=7123, me=True) # db.session.add(me) # db.session.commit() # mock_g.server = MagicMock(id='bbbbbbbb-1234-5678-1234-56781234bbb0') # # response = self.client.post('/', json={'data': None}, # headers={'D-Destination': 'bbbbbbbb-1234-5678-1234-56781234bbb0', # 'D-Source': 'bbbbbbbb-1234-5678-1234-56781234bbb2:bbbbbbbb-1234-5678-1234-56781234bbb1'}, # environ_base={'REMOTE_ADDR': '192.168.1.9'}) # self.assertEqual({'msg': 'default response'}, response.json) # # db.session.refresh(self.srv2) # self.assertEqual(0, len(self.srv2.hidden_gates)) # # response = self.client.post('/', json={'data': None}, # headers={'D-Destination': 'bbbbbbbb-1234-5678-1234-56781234bbb0', # 'D-Source': 'bbbbbbbb-1234-5678-1234-56781234bbb2:bbbbbbbb-1234-5678-1234-56781234bbb1'}, # environ_base={'REMOTE_ADDR': '10.1.2.3'}) # self.assertEqual({'msg': 'default response'}, response.json) # # db.session.refresh(self.srv1) # self.assertEqual(1, len(self.srv1.hidden_gates)) # hg = self.srv1.hidden_gates[0] # self.assertEqual('10.1.2.3', str(hg.ip)) # self.assertEqual(7123, hg.port) @patch('dimensigon.web.decorators.g') def test_server_not_found(self, mock_g): mock_g.server = MagicMock(id=self.srv1.id) resp = self.client.post('/', json={'data': None}, headers={'D-Destination': 'bbbbbbbb-1234-5678-1234-56781234bbb5'}) self.validate_error_response(resp, errors.EntityNotFound('Server', 'bbbbbbbb-1234-5678-1234-56781234bbb5')) @patch('dimensigon.web.decorators.socket.gethostbyname') @patch('dimensigon.web.decorators.g') def test_forward_or_dispatch_dns(self, mock_g, mock_gethostbyname): mock_g.server = MagicMock(id=self.srv1.id) def gethostbyname(dns: str): if dns == 'server2': return '10.1.2.3' else: return None mock_gethostbyname.side_effect = gethostbyname self.srv2.add_new_gate('server2', 7124) db.session.commit() response = self.client.post('/', json={'data': None}, headers={'D-Destination': self.srv1.id, 'D-Source': self.srv2.id}, environ_base={'REMOTE_ADDR': '10.1.2.3'}) self.assertEqual({'msg': 'default response'}, response.json) self.assertEqual(0, len(self.srv2.hidden_gates)) @patch('dimensigon.web.decorators.requests.request', autospec=True) @patch('dimensigon.web.decorators.g') def test_forward_or_dispatch_proxy_request(self, mock_g, mock_request): mock_g.server = MagicMock(id=self.srv1.id) def request(*args, **kwargs): r = requests.Response() r._content = kwargs['headers'].get('d-source') r.status_code = 222 return r mock_request.side_effect = request # check if request is forwarded to the server without d-source header resp = self.client.post('/', json={'data': None}, headers={'D-Destination': self.srv2.id}) self.assertEqual(':bbbbbbbb-1234-5678-1234-56781234bbb1', resp.get_data(True)) # check if request is forwarded to the server with d-source header resp = self.client.post('/', json={'data': None}, headers={'D-Destination': self.srv2.id, 'D-Source': 'bbbbbbbb-1234-5678-1234-56781234bbb0'}) self.assertEqual('bbbbbbbb-1234-5678-1234-56781234bbb0:bbbbbbbb-1234-5678-1234-56781234bbb1', resp.get_data(True)) @patch('dimensigon.web.decorators.g') def test_forward_or_dispatch_unreachable_destination(self, mock_g): type(mock_g).server = mock.PropertyMock(return_value=self.srv1) self.srv2.set_route(None, None, None) resp = self.client.post('/', json={'data': None}, headers={'D-Destination': self.srv2.id}) self.validate_error_response(resp, errors.UnreachableDestination(self.srv2, self.srv1)) @patch('dimensigon.web.decorators.requests.request', autospec=True) @patch('dimensigon.web.decorators.g') def test_forward_or_dispatch_error_proxying(self, mock_g, mock_request): mock_g.server = MagicMock(id=self.srv1.id) mock_request.side_effect = requests.exceptions.ConnectionError('error') # check if request is forwarded to the server resp = self.client.post('/', json={'data': None}, headers={'D-Destination': self.srv2.id}) self.validate_error_response(resp, errors.ProxyForwardingError(self.srv2, requests.exceptions.ConnectionError('error')))