Skip to content
This repository has been archived by the owner on Sep 16, 2022. It is now read-only.

Commit

Permalink
Merge pull request #214 from GreatFruitOmsk/issue_210
Browse files Browse the repository at this point in the history
Fix issue with IPv6 and blocking
  • Loading branch information
vpetersson authored May 28, 2019
2 parents 1ad0929 + 2855ee0 commit 445c561
Show file tree
Hide file tree
Showing 6 changed files with 94 additions and 33 deletions.
2 changes: 1 addition & 1 deletion backend/backend/settings/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ def check_ip_range(ipr):
with open(spam_networks_list_path) as f:
read_data = f.read()
spam_networks_list = re.findall(r'^(\d{1,3}\.\d{1,3}\.\d{1,3}\.0/\d{1,2}).*', read_data, re.MULTILINE)
SPAM_NETWORKS = list(filter(check_ip_range, spam_networks_list))
SPAM_NETWORKS = [[addr, False] for addr in filter(check_ip_range, spam_networks_list)]

# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/2.1/howto/deployment/checklist/
Expand Down
6 changes: 6 additions & 0 deletions backend/device_registry/api_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
from rest_framework.decorators import api_view, renderer_classes, permission_classes
from rest_framework.renderers import JSONRenderer
from rest_framework.response import Response
from netaddr import IPAddress

from device_registry import ca_helper
from device_registry.serializers import DeviceInfoSerializer
Expand Down Expand Up @@ -265,6 +266,11 @@ def mtls_ping_view(request, format=None):
scan_info = request.data.get('scan_info', [])
if isinstance(scan_info, str):
scan_info = json.loads(scan_info)
# Add missing IP protocol version info.
for record in scan_info:
if 'ip_version' not in record:
ipaddr = IPAddress(record['host'])
record['ip_version'] = ipaddr.version
portscan_object.scan_info = scan_info
portscan_object.netstat = request.data.get('netstat', [])
block_networks = portscan_object.block_networks.copy()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# Generated by Django 2.1.7 on 2019-05-27 10:29

from django.db import migrations

from netaddr import IPAddress


def fix_missing_protocol_version(apps, schema_editor):
PortScan = apps.get_model('device_registry', 'PortScan')
for instance in PortScan.objects.all():
# Fix `block_ports`.
block_ports = instance.block_ports
for record in block_ports:
ipaddr = IPAddress(record[0])
# Check if it's IPv6 address or not.
is_v6 = ipaddr.version == 6
if len(record) == 3:
record.append(is_v6)
# Fix `scan_info`.
scan_info = instance.scan_info
for record in scan_info:
if 'ip_version' not in record:
ipaddr = IPAddress(record['host'])
record['ip_version'] = ipaddr.version
# Fix `block_networks`.
block_networks = instance.block_networks
block_networks_list = []
for record in block_networks:
if not isinstance(record, list):
ipaddr = IPAddress(record)
block_networks_list.append([record, ipaddr.version == 6])
else:
block_networks_list.append(record)
instance.block_networks = block_networks_list

instance.save(update_fields=['block_ports', 'scan_info', 'block_networks'])


class Migration(migrations.Migration):
dependencies = [
('device_registry', '0034_device_name'),
]

operations = [
migrations.RunPython(fix_missing_protocol_version),
]
40 changes: 23 additions & 17 deletions backend/device_registry/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -273,29 +273,32 @@ def ports_form_data(self):
Build 3 lists:
1) list of choices for the ports form
(gonna be split in a template by '/' separator):
[[0, '192.168.1.178/22/TCP'], [0, '192.168.1.178/33/UDP']]
[[0, '::ffff:192.168.1.178/22/TCP/6'], [0, '192.168.1.178/33/UDP/4']]
2) list of initial values for the ports form:
[0, 1]
3) list of choices for saving to the block list:
[['192.168.1.178', 22, 'tcp'], ['192.168.1.178', 33, 'udp']]
[['::ffff:192.168.1.178', 22, 'tcp', True], ['192.168.1.178', 33, 'udp', False]]
"""
initial_data = []
choices_data = []
ports_data = []
port_record_index = 0
# 1st - take ports from the block list.
for port_record in self.block_ports:
choices_data.append((port_record_index, '%s/%s/%s' % (
port_record[0], port_record[2], port_record[1].upper())))
choices_data.append((port_record_index, '%s/%s/%s/%d' % (
port_record[0], port_record[2], port_record[1].upper(), 6 if port_record[3] else 4)))
ports_data.append(port_record)
initial_data.append(port_record_index)
port_record_index += 1
# 2nd - take ports from the open ports list (only the ones missing in the block list).
for port_record in self.scan_info:
if [port_record['host'], port_record['proto'], port_record['port']] not in self.block_ports:
choices_data.append((port_record_index, '%s/%s/%s' % (
port_record['host'], port_record['port'], port_record['proto'].upper())))
ports_data.append([port_record['host'], port_record['proto'], port_record['port']])
if [port_record['host'], port_record['proto'], port_record['port'], port_record['ip_version'] == 6] \
not in self.block_ports:
choices_data.append((port_record_index, '%s/%s/%s/%d' % (
port_record['host'], port_record['port'], port_record['proto'].upper(),
port_record['ip_version'])))
ports_data.append([port_record['host'], port_record['proto'], port_record['port'],
port_record['ip_version'] == 6])
port_record_index += 1
return choices_data, initial_data, ports_data

Expand All @@ -304,11 +307,11 @@ def connections_form_data(self):
Build 3 lists:
1) list of choices for the open connections form
(gonna be split in a template by '/' separator)::
[[0, '192.168.1.20/4567/192.168.1.178/80/v4/TCP/open/3425']]
[[0, '192.168.1.20/4567/192.168.1.178/80/4/TCP/open/3425']]
2) list of initial values for the open connections form:
[0]
3) list of choices for saving to the block list:
['192.168.1.20']
[['192.168.1.20', False], ['::ffff:192.168.1.25', True]]
"""
initial_data = []
choices_data = []
Expand All @@ -318,25 +321,28 @@ def connections_form_data(self):

# 1st - take addresses from the block list.
for connection_record in self.block_networks:
if connection_record not in unique_addresses:
unique_addresses.add(connection_record)
choices_data.append((connection_record_index, '%s////v4///' % connection_record))
if tuple(connection_record) not in unique_addresses:
unique_addresses.add(tuple(connection_record))
choices_data.append((connection_record_index, '%s////%d///' % (connection_record[0],
6 if connection_record[1] else 4)))
connections_data.append(connection_record)
initial_data.append(connection_record_index)
connection_record_index += 1

# 2nd - take addresses from the open connections list (only the ones missing in the block list).
for connection_record in self.netstat:
if connection_record['remote_address'] and connection_record['remote_address'][0] not in unique_addresses:
unique_addresses.add(connection_record['remote_address'][0])
if connection_record['remote_address'] and (connection_record['remote_address'][0],
connection_record['ip_version'] == 6) not in unique_addresses:
unique_addresses.add((connection_record['remote_address'][0], connection_record['ip_version'] == 6))
choices_data.append((
connection_record_index, '%s/%s/%s/%s/v%s/%s/%s/%s' %
connection_record_index, '%s/%s/%s/%s/%d/%s/%s/%s' %
(connection_record['remote_address'][0], connection_record['remote_address'][1],
connection_record['local_address'][0] if connection_record['local_address'] else '',
connection_record['local_address'][1] if connection_record['local_address'] else '',
connection_record['ip_version'], connection_record['type'].upper(),
connection_record['status'], connection_record['pid'])))
connections_data.append(connection_record['remote_address'][0])
connections_data.append([connection_record['remote_address'][0],
connection_record['ip_version'] == 6])
connection_record_index += 1
return choices_data, initial_data, connections_data

Expand Down
6 changes: 4 additions & 2 deletions backend/device_registry/templates/device_info_security.html
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ <h4 class="tab-title">Security</h4>
<thead class="thead-light">
<tr>
<th scope="col" width="5%">Blocked</th>
<th scope="col">IP</th>
<th scope="col">Address</th>
<th scope="col">Port</th>
<th scope="col">Proto</th>
Expand All @@ -147,6 +148,7 @@ <h4 class="tab-title">Security</h4>
{% for checkbox in ports_form.open_ports %}
<tr>
<td>{{ checkbox.tag }}</td>
<td>v{{ checkbox.choice_label|split_index:"/,3" }}</td>
<td>{{ checkbox.choice_label|split_index:"/,0" }}</td>
<td>{{ checkbox.choice_label|split_index:"/,1" }}</td>
<td>{{ checkbox.choice_label|split_index:"/,2" }}</td>
Expand All @@ -173,11 +175,11 @@ <h4 class="tab-title">Security</h4>
<thead class="thead-light">
<tr>
<th scope="col" width="5%">Blocked</th>
<th scope="col">IP</th>
<th scope="col">Rem addr</th>
<th scope="col">Rem port</th>
<th scope="col">Loc addr</th>
<th scope="col">Loc port</th>
<th scope="col">IP</th>
<th scope="col">Type</th>
<th scope="col">Status</th>
<th scope="col">PID</th>
Expand All @@ -186,11 +188,11 @@ <h4 class="tab-title">Security</h4>
{% for checkbox in connections_form.open_connections %}
<tr>
<td>{{ checkbox.tag }}</td>
<td>v{{ checkbox.choice_label|split_index:"/,4" }}</td>
<td>{{ checkbox.choice_label|split_index:"/,0" }}</td>
<td>{{ checkbox.choice_label|split_index:"/,1" }}</td>
<td>{{ checkbox.choice_label|split_index:"/,2" }}</td>
<td>{{ checkbox.choice_label|split_index:"/,3" }}</td>
<td>{{ checkbox.choice_label|split_index:"/,4" }}</td>
<td>{{ checkbox.choice_label|split_index:"/,5" }}</td>
<td>{{ checkbox.choice_label|split_index:"/,6" }}</td>
<td>{{ checkbox.choice_label|split_index:"/,7" }}</td>
Expand Down
27 changes: 14 additions & 13 deletions backend/device_registry/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,8 +114,8 @@ def test_common_name_does_not_match_valid_domain(self):
TEST_RULES = {'INPUT': [{'src': '15.15.15.50/32', 'target': 'DROP'}, {'src': '15.15.15.51/32', 'target': 'DROP'}],
'OUTPUT': [], 'FORWARD': []}

OPEN_PORTS_INFO = [{"host": "192.168.1.178", "port": 22, "proto": "tcp", "state": "open"}]
OPEN_PORTS_INFO_TELNET = [{"host": "192.168.1.178", "port": 23, "proto": "tcp", "state": "open"}]
OPEN_PORTS_INFO = [{"host": "192.168.1.178", "port": 22, "proto": "tcp", "state": "open", "ip_version": 4}]
OPEN_PORTS_INFO_TELNET = [{"host": "192.168.1.178", "port": 23, "proto": "tcp", "state": "open", "ip_version": 4}]

OPEN_CONNECTIONS_INFO = [
{'ip_version': 4, 'type': 'tcp', 'local_address': ['192.168.1.178', 4567],
Expand Down Expand Up @@ -167,8 +167,8 @@ def test_pong_data(self):
self.assertEqual(response.status_code, 200)
self.assertDictEqual(response.data, {'block_ports': [], 'block_networks': settings.SPAM_NETWORKS})
# 2nd request
self.device0.portscan.block_ports = [['192.168.1.178', 'tcp', 22]]
self.device0.portscan.block_networks = ['192.168.1.177']
self.device0.portscan.block_ports = [['192.168.1.178', 'tcp', 22, False]]
self.device0.portscan.block_networks = [['192.168.1.177', False]]
self.device0.portscan.save(update_fields=['block_ports', 'block_networks'])
request = self.api.post(
'/v0.2/ping/',
Expand All @@ -178,8 +178,8 @@ def test_pong_data(self):
)
response = mtls_ping_view(request)
self.assertEqual(response.status_code, 200)
self.assertDictEqual(response.data, {'block_ports': [['192.168.1.178', 'tcp', 22]],
'block_networks': ['192.168.1.177'] + settings.SPAM_NETWORKS})
self.assertDictEqual(response.data, {'block_ports': [['192.168.1.178', 'tcp', 22, False]],
'block_networks': [['192.168.1.177', False]] + settings.SPAM_NETWORKS})

def test_ping_creates_models(self):
request = self.api.post(
Expand Down Expand Up @@ -272,7 +272,8 @@ def test_ping_converts_json(self):
"host": "localhost",
"port": 22,
"proto": "tcp",
"state": "open"
"state": "open",
"ip_version": 4
}]
firewall_rules = {'INPUT': [], 'OUTPUT': [], 'FORWARD': []}
ping_payload = {
Expand Down Expand Up @@ -363,12 +364,12 @@ def setUp(self):
logins={'pi': {'failed': 1, 'success': 1}}
)
portscan0 = [
{"host": "192.168.1.178", "port": 22, "proto": "tcp", "state": "open"},
{"host": "192.168.1.178", "port": 25, "proto": "tcp", "state": "open"}
{"host": "192.168.1.178", "port": 22, "proto": "tcp", "state": "open", "ip_version": 4},
{"host": "192.168.1.178", "port": 25, "proto": "tcp", "state": "open", "ip_version": 4}
]
portscan1 = [
{"host": "192.168.1.178", "port": 80, "proto": "tcp", "state": "open"},
{"host": "192.168.1.178", "port": 110, "proto": "tcp", "state": "open"}
{"host": "192.168.1.178", "port": 80, "proto": "tcp", "state": "open", "ip_version": 4},
{"host": "192.168.1.178", "port": 110, "proto": "tcp", "state": "open", "ip_version": 4}
]
self.portscan0 = PortScan.objects.create(device=self.device0, scan_info=portscan0)
self.portscan1 = PortScan.objects.create(device=self.device1, scan_info=portscan1)
Expand Down Expand Up @@ -707,14 +708,14 @@ def test_open_ports(self):
form_data = {'is_ports_form': 'true', 'open_ports': ['0']}
self.client.post(self.url2, form_data)
portscan = PortScan.objects.get(pk=self.portscan.pk)
self.assertListEqual(portscan.block_ports, [['192.168.1.178', 'tcp', 22]])
self.assertListEqual(portscan.block_ports, [['192.168.1.178', 'tcp', 22, False]])

def test_open_connections(self):
self.client.login(username='test', password='123')
form_data = {'is_connections_form': 'true', 'open_connections': ['0']}
self.client.post(self.url2, form_data)
portscan = PortScan.objects.get(pk=self.portscan.pk)
self.assertListEqual(portscan.block_networks, ['192.168.1.177'])
self.assertListEqual(portscan.block_networks, [['192.168.1.177', False]])

def test_no_logins(self):
self.client.login(username='test', password='123')
Expand Down

0 comments on commit 445c561

Please sign in to comment.