Commit 5a892e40 by Dudás Ádám

firewall: moar readability

parent a47e41cb
...@@ -200,7 +200,6 @@ store_settings = { ...@@ -200,7 +200,6 @@ store_settings = {
"store_public": "store.ik.bme.hu", "store_public": "store.ik.bme.hu",
} }
firewall_settings = { firewall_settings = {
"default_vlangroup": "publikus", "default_vlangroup": "publikus",
"reload_sleep": "10", "reload_sleep": "10",
......
...@@ -13,7 +13,7 @@ class RecordInline(contrib.admin.TabularInline): ...@@ -13,7 +13,7 @@ class RecordInline(contrib.admin.TabularInline):
class HostAdmin(admin.ModelAdmin): class HostAdmin(admin.ModelAdmin):
list_display = ('hostname', 'vlan', 'ipv4', 'ipv6', 'pub_ipv4', 'mac', list_display = ('hostname', 'vlan', 'ipv4', 'ipv6', 'pub_ipv4', 'mac',
'shared_ip', 'owner', 'description', 'reverse', 'groups_l') 'shared_ip', 'owner', 'description', 'reverse', 'list_groups')
ordering = ('hostname', ) ordering = ('hostname', )
list_filter = ('owner', 'vlan', 'groups') list_filter = ('owner', 'vlan', 'groups')
search_fields = ('hostname', 'description', 'ipv4', 'ipv6', 'mac') search_fields = ('hostname', 'description', 'ipv4', 'ipv6', 'mac')
...@@ -21,7 +21,7 @@ class HostAdmin(admin.ModelAdmin): ...@@ -21,7 +21,7 @@ class HostAdmin(admin.ModelAdmin):
inlines = (RuleInline, RecordInline) inlines = (RuleInline, RecordInline)
@staticmethod @staticmethod
def groups_l(instance): def list_groups(instance):
"""Returns instance's groups' names as a comma-separated list.""" """Returns instance's groups' names as a comma-separated list."""
names = [group.name for group in instance.groups.all()] names = [group.name for group in instance.groups.all()]
return u', '.join(names) return u', '.join(names)
...@@ -43,36 +43,39 @@ class RuleAdmin(admin.ModelAdmin): ...@@ -43,36 +43,39 @@ class RuleAdmin(admin.ModelAdmin):
list_filter = ('r_type', 'vlan', 'owner', 'direction', 'accept', list_filter = ('r_type', 'vlan', 'owner', 'direction', 'accept',
'proto', 'nat') 'proto', 'nat')
def color_desc(self, instance): @staticmethod
def color_desc(instance):
"""Returns a colorful description of the instance.""" """Returns a colorful description of the instance."""
para = '</span>' return (u'<span style="color: #FF0000;">[%(type)s]</span> '
if instance.dport: u'%(src)s<span style="color: #0000FF;"> ▸ </span>%(dst)s '
para = 'dport=%s %s' % (instance.dport, para) u'%(para)s %(desc)s') % {
if instance.sport: 'type': instance.r_type,
para = 'sport=%s %s' % (instance.sport, para) 'src': (instance.foreign_network.name
if instance.proto: if instance.direction == '1' else instance.r_type),
para = 'proto=%s %s' % (instance.proto, para) 'dst': (instance.r_type if instance.direction == '1'
para = u'<span style="color: #00FF00;">' + para else instance.foreign_network.name),
return ( 'para': (u'<span style="color: #00FF00;">' +
u'<span style="color: #FF0000;">[%s]</span> ' % instance.r_type + (('proto=%s ' % instance.proto)
(u'%s<span style="color: #0000FF;"> ▸ </span>%s' % if instance.proto else '') +
((instance.foreign_network.name, instance.r_type) (('sport=%s ' % instance.sport)
if instance.direction == '1' else if instance.sport else '') +
(instance.r_type, instance.foreign_network.name))) + (('dport=%s ' % instance.dport)
' ' + para + ' ' + instance.description) if instance.dport else '') +
'</span>'),
'desc': instance.description}
color_desc.allow_tags = True color_desc.allow_tags = True
def vlan_l(self, instance): @staticmethod
def vlan_l(instance):
"""Returns instance's VLANs' names as a comma-separated list.""" """Returns instance's VLANs' names as a comma-separated list."""
retval = [] names = [vlan.name for vlan in instance.foreign_network.vlans.all()]
for vlan in instance.foreign_network.vlans.all(): return u', '.join(names)
retval.append(vlan.name)
return u', '.join(retval)
def used_in(self, instance): @staticmethod
def used_in(instance):
for field in [instance.vlan, instance.vlangroup, instance.host, for field in [instance.vlan, instance.vlangroup, instance.host,
instance.hostgroup, instance.firewall]: instance.hostgroup, instance.firewall]:
if field is not None: if field:
return unicode(field) + ' ' + field._meta.object_name return unicode(field) + ' ' + field._meta.object_name
...@@ -92,15 +95,15 @@ class DomainAdmin(admin.ModelAdmin): ...@@ -92,15 +95,15 @@ class DomainAdmin(admin.ModelAdmin):
class RecordAdmin(admin.ModelAdmin): class RecordAdmin(admin.ModelAdmin):
list_display = ('name_', 'type', 'address_', 'ttl', 'host', 'owner') list_display = ('name_', 'type', 'address_', 'ttl', 'host', 'owner')
def address_(self, instance): @staticmethod
def address_(instance):
a = instance.get_data() a = instance.get_data()
if a: return a['address'] if a else None
return a['address']
def name_(self, instance): @staticmethod
def name_(instance):
a = instance.get_data() a = instance.get_data()
if a: return a['name'] if a else None
return a['name']
class BlacklistAdmin(admin.ModelAdmin): class BlacklistAdmin(admin.ModelAdmin):
list_display = ('ipv4', 'reason', 'created_at', 'modified_at') list_display = ('ipv4', 'reason', 'created_at', 'modified_at')
......
...@@ -2,6 +2,7 @@ from django.core.exceptions import ValidationError ...@@ -2,6 +2,7 @@ from django.core.exceptions import ValidationError
from django.forms import fields from django.forms import fields
from django.db import models from django.db import models
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from django.utils.ipv6 import is_valid_ipv6_address
from south.modelsinspector import add_introspection_rules from south.modelsinspector import add_introspection_rules
import re import re
...@@ -35,26 +36,46 @@ class MACAddressField(models.Field): ...@@ -35,26 +36,46 @@ class MACAddressField(models.Field):
add_introspection_rules([], ["firewall\.fields\.MACAddressField"]) add_introspection_rules([], ["firewall\.fields\.MACAddressField"])
def val_alfanum(value): def val_alfanum(value):
"""Check whether the parameter is a valid alphanumeric value.""" """Validate whether the parameter is a valid alphanumeric value."""
if alfanum_re.search(value) is None: if alfanum_re.match(value) is None:
raise ValidationError( raise ValidationError(_(u'%s - only letters, numbers, underscores '
_(u'%s - only letters, numbers, underscores and hyphens are ' 'and hyphens are allowed!') % value)
'allowed!') % value)
def is_valid_domain(value):
"""Check whether the parameter is a valid domain name."""
return domain_re.match(value) is not None
def val_domain(value): def val_domain(value):
"""Check wheter the parameter is a valid domin.""" """Validate whether the parameter is a valid domin name."""
if domain_re.search(value) is None: if not is_valid_domain(value):
raise ValidationError(_(u'%s - invalid domain') % value) raise ValidationError(_(u'%s - invalid domain name') % value)
def is_valid_reverse_domain(value):
"""Check whether the parameter is a valid reverse domain name."""
return reverse_domain_re.match(value) is not None
def val_reverse_domain(value): def val_reverse_domain(value):
"""Check whether the parameter is a valid reverse domain.""" """Validate whether the parameter is a valid reverse domain name."""
if not reverse_domain_re.search(value): if not is_valid_reverse_domain(value):
raise ValidationError(u'%s - reverse domain' % value) raise ValidationError(u'%s - invalid reverse domain name' % value)
def is_valid_ipv4_address(value):
"""Check whether the parameter is a valid IPv4 address."""
return ipv4_re.match(value) is not None
def val_ipv4(value):
"""Validate whether the parameter is a valid IPv4 address."""
if not is_valid_ipv4_address(value):
raise ValidationError(_(u'%s - not an IPv4 address') % value)
def val_ipv6(value):
"""Validate whether the parameter is a valid IPv6 address."""
if not is_valid_ipv6_address(value):
raise ValidationError(_(u'%s - not an IPv6 address') % value)
def ipv4_2_ipv6(ipv4): def ipv4_2_ipv6(ipv4):
"""Convert IPv4 address string to IPv6 address string.""" """Convert IPv4 address string to IPv6 address string."""
val_ipv4(ipv4)
m = ipv4_re.match(ipv4) m = ipv4_re.match(ipv4)
if m is None:
raise ValidationError(_(u'%s - not an IPv4 address') % ipv4)
return ("2001:738:2001:4031:%s:%s:%s:0" % return ("2001:738:2001:4031:%s:%s:%s:0" %
(m.group(1), m.group(2), m.group(3))) (m.group(1), m.group(2), m.group(3)))
...@@ -36,10 +36,11 @@ class firewall: ...@@ -36,10 +36,11 @@ class firewall:
def iptables(self, s): def iptables(self, s):
"""Append rule.""" """Append rule to filter table."""
self.RULES.append(s) self.RULES.append(s)
def iptablesnat(self, s): def iptablesnat(self, s):
"""Append rule to NAT table."""
self.RULES_NAT.append(s) self.RULES_NAT.append(s)
def host2vlan(self, host, rule): def host2vlan(self, host, rule):
......
...@@ -8,7 +8,6 @@ from firewall.fields import * ...@@ -8,7 +8,6 @@ from firewall.fields import *
from south.modelsinspector import add_introspection_rules from south.modelsinspector import add_introspection_rules
from django.core.validators import MinValueValidator, MaxValueValidator from django.core.validators import MinValueValidator, MaxValueValidator
from cloud.settings import firewall_settings as settings from cloud.settings import firewall_settings as settings
from django.utils.ipv6 import is_valid_ipv6_address
from django.db.models.signals import post_save from django.db.models.signals import post_save
import re import re
...@@ -54,27 +53,23 @@ class Rule(models.Model): ...@@ -54,27 +53,23 @@ class Rule(models.Model):
return self.desc() return self.desc()
def clean(self): def clean(self):
count = 0 fields = [self.vlan, self.vlangroup, self.host, self.hostgroup,
for field in [self.vlan, self.vlangroup, self.host, self.hostgroup, self.firewall]
self.firewall]: selected_fields = [field for field in fields if field]
if field is None: if len(selected_fields) > 1:
count = count + 1 raise ValidationError(_('Only one field can be selected.'))
if count != 4:
raise ValidationError('jaj')
def desc(self): def desc(self):
para = u"" return u'[%(type)s] %(src)s ▸ %(dst)s %(para)s %(desc)s' % {
if(self.dport): 'type': self.r_type,
para = "dport=%s %s" % (self.dport, para) 'src': (unicode(self.foreign_network) if self.direction == '1'
if(self.sport): else self.r_type),
para = "sport=%s %s" % (self.sport, para) 'dst': (self.r_type if self.direction == '1'
if(self.proto): else unicode(self.foreign_network)),
para = "proto=%s %s" % (self.proto, para) 'para': ((("proto=%s " % self.proto) if self.proto else '') +
return (u'[' + self.r_type + u'] ' + (("sport=%s " % self.sport) if self.sport else '') +
(unicode(self.foreign_network) + u' ▸ ' + self.r_type (("dport=%s " % self.dport) if self.dport else '')),
if self.direction == '1' else self.r_type + u' ▸ ' + 'desc': self.description}
unicode(self.foreign_network)) + u' ' + para + u' ' +
self.description)
class Vlan(models.Model): class Vlan(models.Model):
vid = models.IntegerField(unique=True) vid = models.IntegerField(unique=True)
...@@ -170,17 +165,15 @@ class Host(models.Model): ...@@ -170,17 +165,15 @@ class Host(models.Model):
self.full_clean() self.full_clean()
super(Host, self).save(*args, **kwargs) super(Host, self).save(*args, **kwargs)
if id is None: if id is None:
Record(domain=self.vlan.domain, host=self, type='A', t = 'A' if self.ipv6 else 'AAAA'
owner=self.owner).save() Record(domain=self.vlan.domain, host=self, type=t,
if self.ipv6:
Record(domain=self.vlan.domain, host=self, type='AAAA',
owner=self.owner).save() owner=self.owner).save()
def enable_net(self): def enable_net(self):
self.groups.add(Group.objects.get(name="netezhet")) self.groups.add(Group.objects.get(name="netezhet"))
def add_port(self, proto, public, private = 0): def add_port(self, proto, public, private = 0):
proto = "tcp" if (proto == "tcp") else "udp" proto = "tcp" if proto == "tcp" else "udp"
if self.shared_ip: if self.shared_ip:
if public < 1024: if public < 1024:
raise ValidationError(_("Only ports above 1024 can be used.")) raise ValidationError(_("Only ports above 1024 can be used."))
...@@ -197,8 +190,9 @@ class Host(models.Model): ...@@ -197,8 +190,9 @@ class Host(models.Model):
raise ValidationError(_("Port %s %s is already in use.") % raise ValidationError(_("Port %s %s is already in use.") %
(proto, public)) (proto, public))
rule = Rule(direction='1', owner=self.owner, dport=public, rule = Rule(direction='1', owner=self.owner, dport=public,
proto=proto, nat=False, accept=True, r_type="host", host=self, proto=proto, nat=False, accept=True, r_type="host",
foreign_network=VlanGroup.objects.get(name=settings["default_vlangroup"])) host=self, foreign_network=VlanGroup.objects
.get(name=settings["default_vlangroup"]))
rule.full_clean() rule.full_clean()
rule.save() rule.save()
...@@ -208,11 +202,10 @@ class Host(models.Model): ...@@ -208,11 +202,10 @@ class Host(models.Model):
dport=public).delete() dport=public).delete()
def list_ports(self): def list_ports(self):
retval = [] return [{'proto': rule.proto,
for rule in self.rules.filter(owner=self.owner): 'public': rule.dport,
retval.append({'proto': rule.proto, 'public': rule.dport, 'private': rule.nat_dport} for rule in
'private': rule.nat_dport}) self.rules.filter(owner=self.owner)]
return retval
def get_fqdn(self): def get_fqdn(self):
return self.hostname + u'.' + unicode(self.vlan.domain) return self.hostname + u'.' + unicode(self.vlan.domain)
...@@ -255,75 +248,90 @@ class Record(models.Model): ...@@ -255,75 +248,90 @@ class Record(models.Model):
def desc(self): def desc(self):
a = self.get_data() a = self.get_data()
if a: return (u' '.join([a['name'], a['type'], a['address']])
return a['name'] + u' ' + a['type'] + u' ' + a['address'] if a else _('(empty)'))
return '(empty)'
def save(self, *args, **kwargs): def save(self, *args, **kwargs):
self.full_clean() self.full_clean()
super(Record, self).save(*args, **kwargs) super(Record, self).save(*args, **kwargs)
def clean(self): def clean(self):
if self.name and self.name.endswith(u'.'): if self.name:
raise ValidationError(_("Domain can't be terminated with a dot.")) self.name = self.name.rstrip(".") # remove trailing dots
if self.host and self.type in ['CNAME', 'A', 'AAAA']: if self.host:
if self.type == 'CNAME': if self.type in ['A', 'AAAA']:
if not self.name or self.address: if self.address:
raise ValidationError(_("Only the 'name' field should " raise ValidationError(_("Can't specify address for "
"be filled with a CNAME record if a host is " "A or AAAA records if host is set!"))
"set.")) if self.name:
elif self.name or self.address: raise ValidationError(_("Can't specify name for "
raise ValidationError(_("'name' and 'address' can't be " "A or AAAA records if host is set!"))
"specified with an A or AAAA record if a host is " elif self.type == 'CNAME':
"set.")) if self.name is None:
else: raise ValidationError(_("Name must be specified for "
if not self.address: "CNAME records if host is set!"))
raise ValidationError(_("'address' field must be filled.")) if self.address:
raise ValidationError(_("Can't specify address for "
"CNAME records if host is set!"))
else: # if self.host is None
if self.address is None:
raise ValidationError(_("Address must be specified!"))
if self.type == 'A': if self.type == 'A':
if not ipv4_re.match(self.address): val_ipv4(self.address)
raise ValidationError(_("Not a valid IPv4 address."))
elif self.type in ['CNAME', 'NS', 'PTR', 'TXT']:
if not domain_re.match(self.address):
raise ValidationError(_("Not a valid domain."))
elif self.type == 'AAAA': elif self.type == 'AAAA':
if not is_valid_ipv6_address(self.address): val_ipv6(self.address)
raise ValidationError(_("Not a valid IPv6 address.")) elif self.type in ['CNAME', 'NS', 'PTR', 'TXT']:
val_domain(self.address)
elif self.type == 'MX': elif self.type == 'MX':
mx = self.address.split(':', 1) mx = self.address.split(':', 1)
if not (len(mx) == 2 and mx[0].isdigit() and if not (len(mx) == 2 and mx[0].isdigit() and
domain_re.match(mx[1])): domain_re.match(mx[1])):
raise ValidationError(_("Invalid address. " raise ValidationError(_("Bad address format. "
"Valid format: <priority>:<hostname>")) "Should be: <priority>:<hostname>"))
else: else:
raise ValidationError(_("Unknown record.")) raise ValidationError(_("Unknown record type."))
def get_data(self): def __get_name(self):
retval = { 'name': self.name, 'type': self.type, 'ttl': self.ttl, if self.host:
'address': self.address } if self.type in ['A', 'AAAA']:
if self.host and self.type in ['CNAME', 'A', 'AAAA']: return self.host.get_fqdn()
elif self.type == 'CNAME':
return self.name + '.' + unicode(self.domain)
else:
return self.name
else: # if self.host is None
if self.name is None:
return unicode(self.domain)
else:
return self.name + '.' + unicode(self.domain)
def __get_address(self):
if self.host:
if self.type == 'A': if self.type == 'A':
retval['address'] = (self.host.pub_ipv4 return (self.host.pub_ipv4
if self.host.pub_ipv4 and not self.host.shared_ip if self.host.pub_ipv4 and not self.host.shared_ip
else self.host.ipv4) else self.host.ipv4)
retval['name'] = self.host.get_fqdn()
elif self.type == 'AAAA': elif self.type == 'AAAA':
if not self.host.ipv6: return self.host.ipv6
return None
retval['address'] = self.host.ipv6
retval['name'] = self.host.get_fqdn()
elif self.type == 'CNAME': elif self.type == 'CNAME':
retval['address'] = self.host.get_fqdn() return self.host.get_fqdn()
retval['name'] = self.name + u'.' + unicode(self.domain) # otherwise:
else: return self.address
if not self.name:
retval['name'] = unicode(self.domain) def get_data(self):
else: name = __get_name()
retval['name'] = self.name + u'.' + unicode(self.domain) address = __get_address()
if not (retval['address'] and retval['name']): if self.host and self.type == 'AAAA' and not self.host.ipv6:
return None return None
return retval elif address is None or name is None:
return None
else:
return {'name': name,
'type': self.type,
'ttl': self.ttl,
'address': address}
class Blacklist(models.Model): class Blacklist(models.Model):
CHOICES_type = (('permban', 'permanent ban'), ('tempban', 'temporary ban'), ('whitelist', 'whitelist')) CHOICES_type = (('permban', 'permanent ban'), ('tempban', 'temporary ban'), ('whitelist', 'whitelist'))
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment