test_firewall.py 12.2 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 periodic_task, 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 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104


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)
        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()

    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)

    def test_all_addr_in_use_w_ipv6(self):
        Host(hostname='h-x', mac='01:02:03:04:05:06',
             ipv4='10.0.0.6', ipv6='2001:738:2001:4031:0:0:2:0',
             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 111 112 113 114 115 116 117 118 119 120 121 122 123


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,
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


Bach Dániel committed
192
class ReloadTestCase(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)
Bach Dániel committed
197
        self.vlan = Vlan(vid=1, name='test', network4='10.0.0.0/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()
Bach Dániel committed
203 204 205 206 207 208 209 210 211 212 213
        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')
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 317 318 319 320 321

    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
322
        # TODO
Bach Dániel committed
323 324 325 326 327 328
        with patch('firewall.tasks.local_tasks.cache') as cache:
            self.test_host_add_port()
            self.test_host_add_port2()
            periodic_task()
            reloadtask()
            assert cache.delete.called