test_firewall.py 12.7 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
# Copyright 2014 Budapest University of Technology and Economics (BME IK)
#
# This file is part of CIRCLE Cloud.
#
# CIRCLE is free software: you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation, either version 3 of the License, or (at your option)
# any later version.
#
# CIRCLE is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License along
# with CIRCLE.  If not, see <http://www.gnu.org/licenses/>.

18
from mock import patch
Bach Dániel committed
19
from netaddr import IPSet, AddrFormatError
20

21
import django.conf
22
from django.contrib.auth.models import User
23 24 25 26 27 28 29
from django.forms import ValidationError
from django.test import TestCase

from common.tests.celery_mock import MockCeleryMixin
from firewall.admin import HostAdmin
from firewall.fw import dns, ipv6_to_octal
from firewall.iptables import IptRule, IptChain, InvalidRuleExcepion
Bach Dániel committed
30 31
from firewall.models import (Vlan, Domain, Record, Host, VlanGroup, Group,
                             Rule, Firewall)
Bach Dániel committed
32
from firewall.tasks.local_tasks import reloadtask_worker, reloadtask
Bach Dániel committed
33 34

settings = django.conf.settings.FIREWALL_SETTINGS
35

36

37 38 39 40
class MockInstance:
    def __init__(self, groups):
        self.groups = MockGroups(groups)

41

42 43 44 45
class MockGroup:
    def __init__(self, name):
        self.name = name

46

47 48 49 50 51 52 53
class MockGroups:
    def __init__(self, groups):
        self.groups = groups

    def all(self):
        return self.groups

54

55 56 57 58 59 60 61 62 63 64 65 66 67
class HostAdminTestCase(TestCase):
    def test_no_groups(self):
        instance = MockInstance([])
        l = HostAdmin.list_groups(instance)
        self.assertEqual(l, "")

    def test_sigle_group(self):
        instance = MockInstance([MockGroup("alma")])
        l = HostAdmin.list_groups(instance)
        self.assertEqual(l, "alma")

    def test_multiple_groups(self):
        instance = MockInstance([MockGroup("alma"),
68
                                 MockGroup("korte"), MockGroup("szilva")])
69 70
        l = HostAdmin.list_groups(instance)
        self.assertEqual(l, "alma, korte, szilva")
71 72


73
class GetNewAddressTestCase(MockCeleryMixin, TestCase):
74 75 76 77 78 79
    def setUp(self):
        self.u1 = User.objects.create(username='user1')
        self.u1.save()
        d = Domain(name='example.org', owner=self.u1)
        d.save()
        # /29 = .1-.6 =  6 hosts/subnet + broadcast + network id
80
        self.vlan = Vlan(vid=1, name='test', network4='10.0.0.1/29',
81 82
                         network6='2001:738:2001:4031::/80', domain=d,
                         owner=self.u1)
Őry Máté committed
83
        self.vlan.clean()
84 85
        self.vlan.save()
        self.vlan.host_set.all().delete()
86
        for i in range(3, 6):
87 88 89 90
            Host(hostname='h-%d' % i, mac='01:02:03:04:05:%02d' % i,
                 ipv4='10.0.0.%d' % i, vlan=self.vlan,
                 owner=self.u1).save()

Őry Máté committed
91 92 93
    def tearDown(self):
        self.vlan.delete()

94 95 96 97 98 99 100 101 102 103 104
    def test_new_addr_w_empty_vlan(self):
        self.vlan.host_set.all().delete()
        self.vlan.get_new_address()

    def test_all_addr_in_use(self):
        for i in (2, 6):
            Host(hostname='h-%d' % i, mac='01:02:03:04:05:%02d' % i,
                 ipv4='10.0.0.%d' % i, vlan=self.vlan,
                 owner=self.u1).save()
        self.assertRaises(ValidationError, self.vlan.get_new_address)

105 106 107
    def test_new_addr(self):
        used_v4 = IPSet(self.vlan.host_set.values_list('ipv4', flat=True))
        assert self.vlan.get_new_address()['ipv4'] not in used_v4
108 109


110
class HostGetHostnameTestCase(MockCeleryMixin, TestCase):
111 112 113 114 115 116
    def setUp(self):
        self.u1 = User.objects.create(username='user1')
        self.u1.save()
        self.d = Domain(name='example.org', owner=self.u1)
        self.d.save()
        Record.objects.all().delete()
117
        self.vlan = Vlan(vid=1, name='test', network4='10.0.0.1/24',
118 119 120 121 122 123
                         network6='2001:738:2001:4031::/80', domain=self.d,
                         owner=self.u1, network_type='portforward',
                         snat_ip='10.1.1.1')
        self.vlan.save()
        self.h = Host(hostname='h', mac='01:02:03:04:05:00', ipv4='10.0.0.1',
                      vlan=self.vlan, owner=self.u1, shared_ip=True,
124
                      external_ipv4=self.vlan.snat_ip)
125 126 127 128
        self.h.save()

    def test_issue_93_wo_record(self):
        self.assertEqual(self.h.get_hostname(proto='ipv4', public=True),
129
                         unicode(self.h.external_ipv4))
130 131 132 133 134 135 136

    def test_issue_93_w_record(self):
        self.r = Record(name='vm', type='A', domain=self.d, owner=self.u1,
                        address=self.vlan.snat_ip)
        self.r.save()
        self.assertEqual(self.h.get_hostname(proto='ipv4', public=True),
                         self.r.fqdn)
Bach Dániel committed
137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169


class IptablesTestCase(TestCase):
    def setUp(self):
        self.r = [IptRule(priority=4, action='ACCEPT',
                          src=('127.0.0.4', None)),
                  IptRule(priority=4, action='ACCEPT',
                          src=('127.0.0.4', None)),
                  IptRule(priority=2, action='ACCEPT',
                          dst=('127.0.0.2', None),
                          extra='-p icmp'),
                  IptRule(priority=6, action='ACCEPT',
                          dst=('127.0.0.6', None),
                          proto='tcp', dport=80),
                  IptRule(priority=1, action='ACCEPT',
                          dst=('127.0.0.1', None),
                          proto='udp', dport=53),
                  IptRule(priority=5, action='ACCEPT',
                          dst=('127.0.0.5', None),
                          proto='tcp', dport=443),
                  IptRule(priority=2, action='ACCEPT',
                          dst=('127.0.0.2', None),
                          proto='icmp'),
                  IptRule(priority=6, action='ACCEPT',
                          dst=('127.0.0.6', None),
                          proto='tcp', dport='1337')]

    def test_chain_add(self):
        ch = IptChain(name='test')
        ch.add(*self.r)
        self.assertEqual(len(ch), len(self.r) - 1)

    def test_rule_compile_ok(self):
Bach Dániel committed
170
        assert unicode(self.r[5])
Bach Dániel committed
171 172 173 174 175
        self.assertEqual(self.r[5].compile(),
                         '-d 127.0.0.5 -p tcp --dport 443 -g ACCEPT')

    def test_rule_compile_fail(self):
        self.assertRaises(InvalidRuleExcepion,
Bach Dániel committed
176 177
                          IptRule, **{'proto': 'test'})
        self.assertRaises(InvalidRuleExcepion,
Bach Dániel committed
178 179 180 181 182 183 184 185
                          IptRule, **{'priority': 5, 'action': 'ACCEPT',
                                      'dst': '127.0.0.5',
                                      'proto': 'icmp', 'dport': 443})

    def test_chain_compile(self):
        ch = IptChain(name='test')
        ch.add(*self.r)
        compiled = ch.compile()
Bach Dániel committed
186 187
        compiled_v6 = ch.compile_v6()
        assert unicode(ch)
Bach Dániel committed
188
        self.assertEqual(len(compiled.splitlines()), len(ch))
Bach Dániel committed
189
        self.assertEqual(len(compiled_v6.splitlines()), 0)
Bach Dániel committed
190 191


192
class ReloadTestCase(MockCeleryMixin, TestCase):
Bach Dániel committed
193 194 195
    def setUp(self):
        self.u1 = User.objects.create(username='user1')
        self.u1.save()
Bach Dániel committed
196
        d = Domain.objects.create(name='example.org', owner=self.u1)
197
        self.vlan = Vlan(vid=1, name='test', network4='10.0.0.1/29',
Bach Dániel committed
198
                         snat_ip='152.66.243.99',
Bach Dániel committed
199
                         network6='2001:738:2001:4031::/80', domain=d,
Bach Dániel committed
200 201
                         owner=self.u1, network_type='portforward',
                         dhcp_pool='manual')
Bach Dániel committed
202
        self.vlan.save()
203
        self.vlan2 = Vlan(vid=2, name='pub', network4='10.1.0.1/29',
Bach Dániel committed
204 205 206 207 208 209 210 211 212 213
                          network6='2001:738:2001:4032::/80', domain=d,
                          owner=self.u1, network_type='public')
        self.vlan2.save()
        self.vlan.snat_to.add(self.vlan2)

        settings["default_vlangroup"] = 'public'
        settings["default_host_groups"] = ['netezhet']
        vlg = VlanGroup.objects.create(name='public')
        vlg.vlans.add(self.vlan, self.vlan2)
        self.hg = Group.objects.create(name='netezhet')
214
        Rule.objects.create(action='accept', hostgroup=self.hg,
Bach Dániel committed
215 216 217
                            foreign_network=vlg)

        firewall = Firewall.objects.create(name='fw')
218
        Rule.objects.create(action='accept', firewall=firewall,
Bach Dániel committed
219 220
                            foreign_network=vlg)

Bach Dániel committed
221
        for i in range(1, 6):
Bach Dániel committed
222 223 224 225 226 227 228 229 230 231 232 233
            h = Host.objects.create(hostname='h-%d' % i, vlan=self.vlan,
                                    mac='01:02:03:04:05:%02d' % i,
                                    ipv4='10.0.0.%d' % i, owner=self.u1)
            h.enable_net()
            h.groups.add(self.hg)
            if i == 5:
                h.vlan = self.vlan2
                h.save()
                self.h5 = h
            if i == 1:
                self.h1 = h

Bach Dániel committed
234 235 236 237 238 239
        self.r1 = Record(name='tst', type='A', address='127.0.0.1',
                         domain=d, owner=self.u1)
        self.rb = Record(name='tst', type='AAAA', address='1.0.0.1',
                         domain=d, owner=self.u1)
        self.r2 = Record(name='ts', type='AAAA', address='2001:123:45::6',
                         domain=d, owner=self.u1)
Bach Dániel committed
240 241 242 243
        self.rm = Record(name='asd', type='MX', address='10:teszthu',
                         domain=d, owner=self.u1)
        self.rt = Record(name='asd', type='TXT', address='ASD',
                         domain=d, owner=self.u1)
Bach Dániel committed
244 245
        self.r1.save()
        self.r2.save()
Bach Dániel committed
246 247 248 249
        with patch('firewall.models.Record.clean'):
            self.rb.save()
        self.rm.save()
        self.rt.save()
Bach Dániel committed
250

Bach Dániel committed
251 252 253
    def tearDown(self):
        settings["default_host_groups"] = []

Bach Dániel committed
254 255 256 257 258 259 260 261 262
    def test_bad_aaaa_record(self):
        self.assertRaises(AddrFormatError, ipv6_to_octal, self.rb.address)

    def test_good_aaaa_record(self):
        ipv6_to_octal(self.r2.address)

    def test_dns_func(self):
        records = dns()
        self.assertEqual(Host.objects.count() * 2 +         # soa
Bach Dániel committed
263
                         len((self.r1, self.r2, self.rm, self.rt)) + 1,
Bach Dániel committed
264
                         len(records))
Bach Dániel committed
265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316

    def test_host_add_port(self):
        h = self.h1
        h.ipv6 = '2001:2:3:4::0'
        assert h.behind_nat
        h.save()
        old_rules = h.rules.count()
        h.add_port('tcp', private=22)
        new_rules = h.rules.count()
        self.assertEqual(new_rules, old_rules + 1)
        self.assertEqual(len(h.list_ports()), old_rules + 1)
        endp = h.get_public_endpoints(22)
        self.assertEqual(endp['ipv4'][0], h.ipv4)
        assert int(endp['ipv4'][1])
        self.assertEqual(endp['ipv6'][0], h.ipv6)
        assert int(endp['ipv6'][1])

    def test_host_add_port2(self):
        h = self.h5
        h.ipv6 = '2001:2:3:4::1'
        h.save()
        assert not h.behind_nat
        old_rules = h.rules.count()
        h.add_port('tcp', private=22)
        new_rules = h.rules.count()
        self.assertEqual(new_rules, old_rules + 1)
        self.assertEqual(len(h.list_ports()), old_rules + 1)
        endp = h.get_public_endpoints(22)
        self.assertEqual(endp['ipv4'][0], h.ipv4)
        assert int(endp['ipv4'][1])
        self.assertEqual(endp['ipv6'][0], h.ipv6)
        assert int(endp['ipv6'][1])

    def test_host_del_port(self):
        h = self.h1
        h.ipv6 = '2001:2:3:4::0'
        h.save()
        h.add_port('tcp', private=22)
        old_rules = h.rules.count()
        h.del_port('tcp', private=22)
        new_rules = h.rules.count()
        self.assertEqual(new_rules, old_rules - 1)

    def test_host_add_port_wo_vlangroup(self):
        VlanGroup.objects.filter(name='public').delete()
        h = self.h1
        old_rules = h.rules.count()
        h.add_port('tcp', private=22)
        new_rules = h.rules.count()
        self.assertEqual(new_rules, old_rules)

    def test_periodic_task(self):
Bach Dániel committed
317
        # TODO
318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333
        cache = patch('firewall.tasks.local_tasks.cache')
        grqn = patch('firewall.models.Firewall.get_remote_queue_name',
                     return_value='fw.firewall')
        worker = patch(
            'firewall.tasks.local_tasks.reloadtask_worker.apply_async')

        dns = patch('firewall.tasks.remote_tasks.reload_dns.apply_async')
        fw = patch(
            'firewall.tasks.remote_tasks.reload_firewall.apply_async')
        fw_vlan = patch(
            'firewall.tasks.remote_tasks.reload_firewall_vlan.apply_async')
        blacklist = patch(
            'firewall.tasks.remote_tasks.reload_blacklist.apply_async')
        dhcp = patch('firewall.tasks.remote_tasks.reload_dhcp.apply_async')

        with cache as cache, grqn, dns, fw, fw_vlan, blacklist, dhcp, worker:
Bach Dániel committed
334 335
            self.test_host_add_port()
            self.test_host_add_port2()
Bach Dániel committed
336
            reloadtask_worker()
Bach Dániel committed
337 338
            reloadtask()
            assert cache.delete.called