test_firewall.py 12 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/>.

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

20
from django.test import TestCase
21 22
from django.contrib.auth.models import User
from ..admin import HostAdmin
Bach Dániel committed
23 24
from firewall.models import (Vlan, Domain, Record, Host, VlanGroup, Group,
                             Rule, Firewall)
Bach Dániel committed
25
from firewall.fw import dns, ipv6_to_octal
Bach Dániel committed
26
from firewall.tasks.local_tasks import reloadtask_worker, reloadtask
27
from django.forms import ValidationError
Bach Dániel committed
28
from ..iptables import IptRule, IptChain, InvalidRuleExcepion
Bach Dániel committed
29 30 31 32
from mock import patch

import django.conf
settings = django.conf.settings.FIREWALL_SETTINGS
33

34

35 36 37 38
class MockInstance:
    def __init__(self, groups):
        self.groups = MockGroups(groups)

39

40 41 42 43
class MockGroup:
    def __init__(self, name):
        self.name = name

44

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

    def all(self):
        return self.groups

52

53 54 55 56 57 58 59 60 61 62 63 64 65
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"),
66
                                 MockGroup("korte"), MockGroup("szilva")])
67 68
        l = HostAdmin.list_groups(instance)
        self.assertEqual(l, "alma, korte, szilva")
69 70 71 72 73 74 75 76 77 78 79 80


class GetNewAddressTestCase(TestCase):
    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
        self.vlan = Vlan(vid=1, name='test', network4='10.0.0.0/29',
                         network6='2001:738:2001:4031::/80', domain=d,
                         owner=self.u1)
Őry Máté committed
81
        self.vlan.clean()
82 83 84 85 86 87 88
        self.vlan.save()
        self.vlan.host_set.all().delete()
        for i in [1] + range(3, 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()

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

92 93 94 95 96 97 98 99 100 101 102
    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)

103 104 105
    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
106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121


class HostGetHostnameTestCase(TestCase):
    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()
        self.vlan = Vlan(vid=1, name='test', network4='10.0.0.0/24',
                         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,
122
                      external_ipv4=self.vlan.snat_ip)
123 124 125 126
        self.h.save()

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

    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
135 136 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


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
168
        assert unicode(self.r[5])
Bach Dániel committed
169 170 171 172 173
        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
174 175
                          IptRule, **{'proto': 'test'})
        self.assertRaises(InvalidRuleExcepion,
Bach Dániel committed
176 177 178 179 180 181 182 183
                          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
184 185
        compiled_v6 = ch.compile_v6()
        assert unicode(ch)
Bach Dániel committed
186
        self.assertEqual(len(compiled.splitlines()), len(ch))
Bach Dániel committed
187
        self.assertEqual(len(compiled_v6.splitlines()), 0)
Bach Dániel committed
188 189


Bach Dániel committed
190
class ReloadTestCase(TestCase):
Bach Dániel committed
191 192 193
    def setUp(self):
        self.u1 = User.objects.create(username='user1')
        self.u1.save()
Bach Dániel committed
194
        d = Domain.objects.create(name='example.org', owner=self.u1)
Bach Dániel committed
195
        self.vlan = Vlan(vid=1, name='test', network4='10.0.0.0/29',
Bach Dániel committed
196
                         snat_ip='152.66.243.99',
Bach Dániel committed
197
                         network6='2001:738:2001:4031::/80', domain=d,
Bach Dániel committed
198 199
                         owner=self.u1, network_type='portforward',
                         dhcp_pool='manual')
Bach Dániel committed
200
        self.vlan.save()
Bach Dániel committed
201 202 203 204 205 206 207 208 209 210 211
        self.vlan2 = Vlan(vid=2, name='pub', network4='10.1.0.0/29',
                          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')
212
        Rule.objects.create(action='accept', hostgroup=self.hg,
Bach Dániel committed
213 214 215
                            foreign_network=vlg)

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

Bach Dániel committed
219
        for i in range(1, 6):
Bach Dániel committed
220 221 222 223 224 225 226 227 228 229 230 231
            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
232 233 234 235 236 237
        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
238 239 240 241
        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
242 243
        self.r1.save()
        self.r2.save()
Bach Dániel committed
244 245 246 247
        with patch('firewall.models.Record.clean'):
            self.rb.save()
        self.rm.save()
        self.rt.save()
Bach Dániel committed
248

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

Bach Dániel committed
252 253 254 255 256 257 258 259 260
    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
261
                         len((self.r1, self.r2, self.rm, self.rt)) + 1,
Bach Dániel committed
262
                         len(records))
Bach Dániel committed
263 264 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 317 318 319

    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_host_add_port_w_validationerror(self):
        h = self.h1
        self.assertRaises(ValidationError, h.add_port,
                          'tcp', public=1000, private=22)

    def test_periodic_task(self):
Bach Dániel committed
320
        # TODO
Bach Dániel committed
321 322 323
        with patch('firewall.tasks.local_tasks.cache') as cache:
            self.test_host_add_port()
            self.test_host_add_port2()
Bach Dániel committed
324
            reloadtask_worker()
Bach Dániel committed
325 326
            reloadtask()
            assert cache.delete.called