Commit 43391a3d by Őry Máté

firewall: merge from network-gui

parent a8c081dc
# -*- coding: utf8 -*- # -*- coding: utf-8 -*-
from django.contrib import admin from django.contrib import admin
from firewall.models import * from firewall.models import (Rule, Host, Vlan, Group, VlanGroup, Firewall,
Domain, Record, Blacklist)
from django import contrib from django import contrib
class RuleInline(contrib.admin.TabularInline): class RuleInline(contrib.admin.TabularInline):
model = Rule model = Rule
class RecordInline(contrib.admin.TabularInline): class RecordInline(contrib.admin.TabularInline):
model = Record model = Record
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', 'list_groups') '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')
...@@ -26,20 +30,24 @@ class HostAdmin(admin.ModelAdmin): ...@@ -26,20 +30,24 @@ class HostAdmin(admin.ModelAdmin):
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)
class HostInline(contrib.admin.TabularInline): class HostInline(contrib.admin.TabularInline):
model = Host model = Host
fields = ('hostname', 'ipv4', 'ipv6', 'pub_ipv4', 'mac', 'shared_ip', fields = ('hostname', 'ipv4', 'ipv6', 'pub_ipv4', 'mac', 'shared_ip',
'owner', 'reverse') 'owner', 'reverse')
class VlanAdmin(admin.ModelAdmin): class VlanAdmin(admin.ModelAdmin):
list_display = ('vid', 'name', 'ipv4', 'net_ipv4', 'ipv6', 'net_ipv6', list_display = ('vid', 'name', 'ipv4', 'net_ipv4', 'ipv6', 'net_ipv6',
'description', 'domain', 'snat_ip', ) 'description', 'domain', 'snat_ip', )
ordering = ('vid', ) ordering = ('vid', )
inlines = (RuleInline, ) inlines = (RuleInline, )
class RuleAdmin(admin.ModelAdmin): class RuleAdmin(admin.ModelAdmin):
list_display = ('r_type', 'color_desc', 'owner', 'extra', 'direction', list_display = ('r_type', 'color_desc', 'owner', 'extra', 'direction',
'accept', 'proto', 'sport', 'dport', 'nat', 'nat_dport', 'used_in') 'accept', 'proto', 'sport', 'dport', 'nat',
'nat_dport', 'used_in')
list_filter = ('r_type', 'vlan', 'owner', 'direction', 'accept', list_filter = ('r_type', 'vlan', 'owner', 'direction', 'accept',
'proto', 'nat') 'proto', 'nat')
...@@ -81,16 +89,20 @@ class RuleAdmin(admin.ModelAdmin): ...@@ -81,16 +89,20 @@ class RuleAdmin(admin.ModelAdmin):
class AliasAdmin(admin.ModelAdmin): class AliasAdmin(admin.ModelAdmin):
list_display = ('alias', 'host') list_display = ('alias', 'host')
class GroupAdmin(admin.ModelAdmin): class GroupAdmin(admin.ModelAdmin):
list_display = ('name', 'owner', 'description') list_display = ('name', 'owner', 'description')
inlines = (RuleInline, ) inlines = (RuleInline, )
class FirewallAdmin(admin.ModelAdmin): class FirewallAdmin(admin.ModelAdmin):
inlines = (RuleInline, ) inlines = (RuleInline, )
class DomainAdmin(admin.ModelAdmin): class DomainAdmin(admin.ModelAdmin):
list_display = ('name', 'owner') list_display = ('name', 'owner')
class RecordAdmin(admin.ModelAdmin): class RecordAdmin(admin.ModelAdmin):
list_display = ('name_', 'type', 'address_', 'ttl', 'host', 'owner') list_display = ('name_', 'type', 'address_', 'ttl', 'host', 'owner')
...@@ -104,6 +116,7 @@ class RecordAdmin(admin.ModelAdmin): ...@@ -104,6 +116,7 @@ class RecordAdmin(admin.ModelAdmin):
a = instance.get_data() a = instance.get_data()
return a['name'] if a else None return a['name'] if a else None
class BlacklistAdmin(admin.ModelAdmin): class BlacklistAdmin(admin.ModelAdmin):
list_display = ('ipv4', 'reason', 'created_at', 'modified_at') list_display = ('ipv4', 'reason', 'created_at', 'modified_at')
...@@ -116,4 +129,3 @@ admin.site.register(Firewall, FirewallAdmin) ...@@ -116,4 +129,3 @@ admin.site.register(Firewall, FirewallAdmin)
admin.site.register(Domain, DomainAdmin) admin.site.register(Domain, DomainAdmin)
admin.site.register(Record, RecordAdmin) admin.site.register(Record, RecordAdmin)
admin.site.register(Blacklist, BlacklistAdmin) admin.site.register(Blacklist, BlacklistAdmin)
...@@ -6,12 +6,14 @@ from django.utils.ipv6 import is_valid_ipv6_address ...@@ -6,12 +6,14 @@ 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
mac_re = re.compile(r'^([0-9a-fA-F]{2}([:-]?|$)){6}$')
mac_re = re.compile(r'^([0-9a-fA-F]{2}(:|$)){6}$')
alfanum_re = re.compile(r'^[A-Za-z0-9_-]+$') alfanum_re = re.compile(r'^[A-Za-z0-9_-]+$')
domain_re = re.compile(r'^([A-Za-z0-9_-]\.?)+$') domain_re = re.compile(r'^([A-Za-z0-9_-]\.?)+$')
ipv4_re = re.compile('^[0-9]+\.([0-9]+)\.([0-9]+)\.([0-9]+)$') ipv4_re = re.compile('^[0-9]+\.([0-9]+)\.([0-9]+)\.([0-9]+)$')
reverse_domain_re = re.compile(r'^(%\([abcd]\)d|[a-z0-9.-])+$') reverse_domain_re = re.compile(r'^(%\([abcd]\)d|[a-z0-9.-])+$')
class MACAddressFormField(fields.RegexField): class MACAddressFormField(fields.RegexField):
default_error_messages = { default_error_messages = {
'invalid': _(u'Enter a valid MAC address.'), 'invalid': _(u'Enter a valid MAC address.'),
...@@ -20,8 +22,10 @@ class MACAddressFormField(fields.RegexField): ...@@ -20,8 +22,10 @@ class MACAddressFormField(fields.RegexField):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super(MACAddressFormField, self).__init__(mac_re, *args, **kwargs) super(MACAddressFormField, self).__init__(mac_re, *args, **kwargs)
class MACAddressField(models.Field): class MACAddressField(models.Field):
empty_strings_allowed = False empty_strings_allowed = False
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
kwargs['max_length'] = 17 kwargs['max_length'] = 17
super(MACAddressField, self).__init__(*args, **kwargs) super(MACAddressField, self).__init__(*args, **kwargs)
...@@ -35,44 +39,65 @@ class MACAddressField(models.Field): ...@@ -35,44 +39,65 @@ class MACAddressField(models.Field):
return super(MACAddressField, self).formfield(**defaults) return super(MACAddressField, self).formfield(**defaults)
add_introspection_rules([], ["firewall\.fields\.MACAddressField"]) add_introspection_rules([], ["firewall\.fields\.MACAddressField"])
def val_alfanum(value): def val_alfanum(value):
"""Validate whether the parameter is a valid alphanumeric value.""" """Validate whether the parameter is a valid alphanumeric value."""
if not alfanum_re.match(value): if not alfanum_re.match(value):
raise ValidationError(_(u'%s - only letters, numbers, underscores ' raise ValidationError(_(u'%s - only letters, numbers, underscores '
'and hyphens are allowed!') % value) 'and hyphens are allowed!') % value)
def is_valid_domain(value): def is_valid_domain(value):
"""Check whether the parameter is a valid domain name.""" """Check whether the parameter is a valid domain name."""
return domain_re.match(value) is not None return domain_re.match(value) is not None
def val_domain(value): def val_domain(value):
"""Validate whether the parameter is a valid domin name.""" """Validate whether the parameter is a valid domin name."""
if not is_valid_domain(value): if not is_valid_domain(value):
raise ValidationError(_(u'%s - invalid domain name') % value) raise ValidationError(_(u'%s - invalid domain name') % value)
def is_valid_reverse_domain(value): def is_valid_reverse_domain(value):
"""Check whether the parameter is a valid reverse domain name.""" """Check whether the parameter is a valid reverse domain name."""
return reverse_domain_re.match(value) is not None return reverse_domain_re.match(value) is not None
def val_reverse_domain(value): def val_reverse_domain(value):
"""Validate whether the parameter is a valid reverse domain name.""" """Validate whether the parameter is a valid reverse domain name."""
if not is_valid_reverse_domain(value): if not is_valid_reverse_domain(value):
raise ValidationError(u'%s - invalid reverse domain name' % value) raise ValidationError(u'%s - invalid reverse domain name' % value)
def is_valid_ipv4_address(value): def is_valid_ipv4_address(value):
"""Check whether the parameter is a valid IPv4 address.""" """Check whether the parameter is a valid IPv4 address."""
return ipv4_re.match(value) is not None return ipv4_re.match(value) is not None
def val_ipv4(value): def val_ipv4(value):
"""Validate whether the parameter is a valid IPv4 address.""" """Validate whether the parameter is a valid IPv4 address."""
if not is_valid_ipv4_address(value): if not is_valid_ipv4_address(value):
raise ValidationError(_(u'%s - not an IPv4 address') % value) raise ValidationError(_(u'%s - not an IPv4 address') % value)
def val_ipv6(value): def val_ipv6(value):
"""Validate whether the parameter is a valid IPv6 address.""" """Validate whether the parameter is a valid IPv6 address."""
if not is_valid_ipv6_address(value): if not is_valid_ipv6_address(value):
raise ValidationError(_(u'%s - not an IPv6 address') % value) raise ValidationError(_(u'%s - not an IPv6 address') % value)
def val_mx(value):
"""Validate whether the parameter is a valid MX address definition.
Expected form is <priority>:<hostname>.
"""
mx = value.split(':', 1)
if not (len(mx) == 2 and mx[0].isdigit() and
domain_re.match(mx[1])):
raise ValidationError(_("Bad MX address format. "
"Should be: <priority>:<hostname>"))
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) val_ipv4(ipv4)
......
from django.contrib import auth
from firewall import models from firewall import models
import os
import django.conf import django.conf
import subprocess import subprocess
import re import re
import json
from datetime import datetime, timedelta from datetime import datetime, timedelta
from django.db.models import Q from django.db.models import Q
settings = django.conf.settings.FIREWALL_SETTINGS settings = django.conf.settings.FIREWALL_SETTINGS
class Firewall: class Firewall:
IPV6=False IPV6 = False
RULES = None RULES = None
RULES_NAT = [] RULES_NAT = []
vlans = None vlans = None
dmz = None
pub = None pub = None
hosts = None hosts = None
fw = None fw = None
...@@ -36,7 +34,6 @@ class Firewall: ...@@ -36,7 +34,6 @@ class Firewall:
retval = '-p %s ' % rule.proto retval = '-p %s ' % rule.proto
return retval return retval
def iptables(self, s): def iptables(self, s):
"""Append rule to filter table.""" """Append rule to filter table."""
self.RULES.append(s) self.RULES.append(s)
...@@ -71,12 +68,13 @@ class Firewall: ...@@ -71,12 +68,13 @@ class Firewall:
action = 'LOG_DROP' action = 'LOG_DROP'
if rule.direction == '1': # going TO host if rule.direction == '1': # going TO host
self.iptables('-A %s_%s -d %s %s %s -g %s' % (vlan, self.iptables('-A %s_%s -d %s %s %s -g %s' %
host.vlan, ipaddr, dport_sport, rule.extra, action)) (vlan, host.vlan, ipaddr, dport_sport,
rule.extra, action))
else: else:
self.iptables('-A %s_%s -s %s %s %s -g %s' % (host.vlan, self.iptables('-A %s_%s -s %s %s %s -g %s' %
vlan, ipaddr, dport_sport, rule.extra, action)) (host.vlan, vlan, ipaddr, dport_sport,
rule.extra, action))
def fw2vlan(self, rule): def fw2vlan(self, rule):
if not rule.foreign_network: if not rule.foreign_network:
...@@ -110,12 +108,12 @@ class Firewall: ...@@ -110,12 +108,12 @@ class Firewall:
action = 'LOG_DROP' action = 'LOG_DROP'
if rule.direction == '1': # going TO host if rule.direction == '1': # going TO host
self.iptables('-A %s_%s %s %s -g %s' % (vlan, l_vlan, self.iptables('-A %s_%s %s %s -g %s' %
dport_sport, rule.extra, action)) (vlan, l_vlan, dport_sport, rule.extra, action))
else: else:
self.iptables('-A %s_%s %s %s -g %s' % (l_vlan, vlan, self.iptables('-A %s_%s %s %s -g %s' % (l_vlan, vlan,
dport_sport, rule.extra, action)) dport_sport,
rule.extra, action))
def prerun(self): def prerun(self):
self.iptables('*filter') self.iptables('*filter')
...@@ -138,7 +136,8 @@ class Firewall: ...@@ -138,7 +136,8 @@ class Firewall:
self.iptables('-N PUB_OUT') self.iptables('-N PUB_OUT')
self.iptables('-A FORWARD -m set --match-set blacklist src,dst -j DROP') self.iptables('-A FORWARD -m set --match-set blacklist src,dst '
'-j DROP')
self.iptables('-A FORWARD -m state --state INVALID -g LOG_DROP') self.iptables('-A FORWARD -m state --state INVALID -g LOG_DROP')
self.iptables('-A FORWARD -m state --state ESTABLISHED,RELATED ' self.iptables('-A FORWARD -m state --state ESTABLISHED,RELATED '
'-j ACCEPT') '-j ACCEPT')
...@@ -156,7 +155,6 @@ class Firewall: ...@@ -156,7 +155,6 @@ class Firewall:
self.iptables('-A OUTPUT -m state --state ESTABLISHED,RELATED ' self.iptables('-A OUTPUT -m state --state ESTABLISHED,RELATED '
'-j ACCEPT') '-j ACCEPT')
def postrun(self): def postrun(self):
self.iptables('-A PUB_OUT -s 152.66.243.160/27 -p tcp --dport 25 ' self.iptables('-A PUB_OUT -s 152.66.243.160/27 -p tcp --dport 25 '
'-j LOG_ACC') '-j LOG_ACC')
...@@ -172,9 +170,6 @@ class Firewall: ...@@ -172,9 +170,6 @@ class Firewall:
self.iptables('-A OUTPUT -g LOG_DROP') self.iptables('-A OUTPUT -g LOG_DROP')
self.iptables('COMMIT') self.iptables('COMMIT')
def ipt_nat(self): def ipt_nat(self):
self.iptablesnat('*nat') self.iptablesnat('*nat')
self.iptablesnat(':PREROUTING ACCEPT [0:0]') self.iptablesnat(':PREROUTING ACCEPT [0:0]')
...@@ -188,34 +183,37 @@ class Firewall: ...@@ -188,34 +183,37 @@ class Firewall:
dport_sport = self.dportsport(rule, False) dport_sport = self.dportsport(rule, False)
if host.vlan.snat_ip: if host.vlan.snat_ip:
self.iptablesnat('-A PREROUTING -d %s %s %s -j DNAT ' self.iptablesnat('-A PREROUTING -d %s %s %s -j DNAT '
'--to-destination %s:%s' % (host.pub_ipv4, '--to-destination %s:%s' %
dport_sport, rule.extra, host.ipv4, (host.pub_ipv4, dport_sport, rule.extra,
rule.nat_dport)) host.ipv4, rule.nat_dport))
# rules for machines with dedicated public IP # rules for machines with dedicated public IP
for host in self.hosts.exclude(shared_ip=True): for host in self.hosts.exclude(shared_ip=True):
if host.pub_ipv4: if host.pub_ipv4:
self.iptablesnat('-A PREROUTING -d %s -j DNAT ' self.iptablesnat('-A PREROUTING -d %s -j DNAT '
'--to-destination %s' % (host.pub_ipv4, host.ipv4)) '--to-destination %s' %
(host.pub_ipv4, host.ipv4))
self.iptablesnat('-A POSTROUTING -s %s -j SNAT ' self.iptablesnat('-A POSTROUTING -s %s -j SNAT '
'--to-source %s' % (host.ipv4, host.pub_ipv4)) '--to-source %s' %
(host.ipv4, host.pub_ipv4))
# default NAT rules for VLANs # default NAT rules for VLANs
for s_vlan in self.vlans: for s_vlan in self.vlans:
if s_vlan.snat_ip: if s_vlan.snat_ip:
for d_vlan in s_vlan.snat_to.all(): for d_vlan in s_vlan.snat_to.all():
self.iptablesnat('-A POSTROUTING -s %s -o %s -j SNAT ' self.iptablesnat('-A POSTROUTING -s %s -o %s -j SNAT '
'--to-source %s' % (s_vlan.net_ipv4(), '--to-source %s' %
d_vlan.interface, s_vlan.snat_ip)) (s_vlan.net_ipv4(), d_vlan.interface,
s_vlan.snat_ip))
# hard-wired rules # hard-wired rules
self.iptablesnat('-A POSTROUTING -s 10.5.0.0/16 -o vlan0003 -j SNAT ' self.iptablesnat('-A POSTROUTING -s 10.5.0.0/16 -o vlan0003 -j SNAT '
'--to-source 10.3.255.254') # man elerheto legyen '--to-source 10.3.255.254') # man elerheto legyen
self.iptablesnat('-A POSTROUTING -s 10.5.0.0/16 -o vlan0008 -j SNAT ' self.iptablesnat('-A POSTROUTING -o vlan0008 -j SNAT '
'--to-source 10.0.0.247') # wolf network for printing '--to-source 10.0.0.247') # wolf network for printing
self.iptablesnat('-A POSTROUTING -s 10.3.0.0/16 -o vlan0002 -j SNAT ' self.iptablesnat('-A POSTROUTING -s 10.3.0.0/16 -p udp --dport 53 '
'--to-source %s' % self.pub.ipv4) # kulonben nemmegy a du '-o vlan0002 -j SNAT ''--to-source %s' %
self.pub.ipv4) # kulonben nem megy a dns man-ban
self.iptablesnat('COMMIT') self.iptablesnat('COMMIT')
...@@ -235,7 +233,8 @@ class Firewall: ...@@ -235,7 +233,8 @@ class Firewall:
for d_vlan in self.vlans: for d_vlan in self.vlans:
self.iptables('-N %s_%s' % (s_vlan, d_vlan)) self.iptables('-N %s_%s' % (s_vlan, d_vlan))
self.iptables('-A FORWARD -i %s -o %s -g %s_%s' % self.iptables('-A FORWARD -i %s -o %s -g %s_%s' %
(s_vlan.interface, d_vlan.interface, s_vlan, d_vlan)) (s_vlan.interface, d_vlan.interface, s_vlan,
d_vlan))
# hosts' rules # hosts' rules
for i_vlan in self.vlans: for i_vlan in self.vlans:
...@@ -264,12 +263,11 @@ class Firewall: ...@@ -264,12 +263,11 @@ class Firewall:
self.RULES = [x.replace('icmp', 'icmpv6') for x in self.RULES] self.RULES = [x.replace('icmp', 'icmpv6') for x in self.RULES]
def __init__(self, IPV6=False): def __init__(self, IPV6=False):
self.RULES=[] self.RULES = []
self.RULES_NAT=[] self.RULES_NAT = []
self.IPV6 = IPV6 self.IPV6 = IPV6
self.vlans = models.Vlan.objects.all() self.vlans = models.Vlan.objects.all()
self.hosts = models.Host.objects.all() self.hosts = models.Host.objects.all()
self.dmz = models.Vlan.objects.get(name='DMZ')
self.pub = models.Vlan.objects.get(name='PUB') self.pub = models.Vlan.objects.get(name='PUB')
self.fw = models.Firewall.objects.all() self.fw = models.Firewall.objects.all()
self.ipt_filter() self.ipt_filter()
...@@ -279,21 +277,23 @@ class Firewall: ...@@ -279,21 +277,23 @@ class Firewall:
def reload(self): def reload(self):
if self.IPV6: if self.IPV6:
process = subprocess.Popen(['/usr/bin/ssh', 'fw2', process = subprocess.Popen(['/usr/bin/ssh', 'fw2',
'/usr/bin/sudo', '/sbin/ip6tables-restore', '-c'], '/usr/bin/sudo',
'/sbin/ip6tables-restore', '-c'],
shell=False, stdin=subprocess.PIPE) shell=False, stdin=subprocess.PIPE)
process.communicate('\n'.join(self.RULES) + '\n') process.communicate('\n'.join(self.RULES) + '\n')
else: else:
process = subprocess.Popen(['/usr/bin/ssh', 'fw2', process = subprocess.Popen(['/usr/bin/ssh', 'fw2',
'/usr/bin/sudo', '/sbin/iptables-restore', '-c'], '/usr/bin/sudo',
'/sbin/iptables-restore', '-c'],
shell=False, stdin=subprocess.PIPE) shell=False, stdin=subprocess.PIPE)
process.communicate('\n'.join(self.RULES) + '\n' + process.communicate('\n'.join(self.RULES) + '\n' +
'\n'.join(self.RULES_NAT) + '\n') '\n'.join(self.RULES_NAT) + '\n')
def get(self): def get(self):
if self.IPV6: if self.IPV6:
return { 'filter': self.RULES, } return {'filter': self.RULES, }
else: else:
return { 'filter': self.RULES, 'nat': self.RULES_NAT } return {'filter': self.RULES, 'nat': self.RULES_NAT}
def show(self): def show(self):
if self.IPV6: if self.IPV6:
...@@ -302,9 +302,12 @@ class Firewall: ...@@ -302,9 +302,12 @@ class Firewall:
return ('\n'.join(self.RULES) + '\n' + return ('\n'.join(self.RULES) + '\n' +
'\n'.join(self.RULES_NAT) + '\n') '\n'.join(self.RULES_NAT) + '\n')
def ipset(): def ipset():
week = datetime.now()-timedelta(days=2) week = datetime.now() - timedelta(days=2)
return models.Blacklist.objects.filter(Q(type='tempban', modified_at__gte=week) | Q(type='permban')).values('ipv4', 'reason') filter_ban = (Q(type='tempban', modified_at__gte=week) |
Q(type='permban')).values('ipv4', 'reason')
return models.Blacklist.objects.filter(filter_ban)
def ipv6_to_octal(ipv6): def ipv6_to_octal(ipv6):
...@@ -321,6 +324,7 @@ def ipv6_to_octal(ipv6): ...@@ -321,6 +324,7 @@ def ipv6_to_octal(ipv6):
octets.append(int(part[2:], 16)) octets.append(int(part[2:], 16))
return '\\' + '\\'.join(['%03o' % x for x in octets]) return '\\' + '\\'.join(['%03o' % x for x in octets])
def ipv4_to_arpa(ipv4, cname=False): def ipv4_to_arpa(ipv4, cname=False):
m2 = re.search(r'^([0-9]+)\.([0-9]+)\.([0-9]+)\.([0-9]+)$', ipv4) m2 = re.search(r'^([0-9]+)\.([0-9]+)\.([0-9]+)\.([0-9]+)$', ipv4)
if cname: if cname:
...@@ -330,6 +334,7 @@ def ipv4_to_arpa(ipv4, cname=False): ...@@ -330,6 +334,7 @@ def ipv4_to_arpa(ipv4, cname=False):
return ('%s.%s.%s.%s.in-addr.arpa' % return ('%s.%s.%s.%s.in-addr.arpa' %
(m2.group(4), m2.group(3), m2.group(2), m2.group(1))) (m2.group(4), m2.group(3), m2.group(2), m2.group(1)))
def ipv6_to_arpa(ipv6): def ipv6_to_arpa(ipv6):
while len(ipv6.split(':')) < 8: while len(ipv6.split(':')) < 8:
ipv6 = ipv6.replace('::', ':::') ipv6 = ipv6.replace('::', ':::')
...@@ -357,11 +362,11 @@ def ipv6_to_arpa(ipv6): ...@@ -357,11 +362,11 @@ def ipv6_to_arpa(ipv6):
def dns(): def dns():
vlans = models.Vlan.objects.all() vlans = models.Vlan.objects.all()
regex = re.compile(r'^([0-9]+)\.([0-9]+)\.([0-9]+)\.([0-9]+)$') # regex = re.compile(r'^([0-9]+)\.([0-9]+)\.([0-9]+)\.([0-9]+)$')
DNS = [] DNS = []
for i_vlan in vlans: for i_vlan in vlans:
m = regex.search(i_vlan.net4) # m = regex.search(i_vlan.net4)
rev = i_vlan.reverse_domain rev = i_vlan.reverse_domain
for i_host in i_vlan.host_set.all(): for i_host in i_vlan.host_set.all():
...@@ -374,8 +379,8 @@ def dns(): ...@@ -374,8 +379,8 @@ def dns():
# ipv4 # ipv4
if i_host.ipv4: if i_host.ipv4:
DNS.append("^%s:%s:%s" % ( DNS.append("^%s:%s:%s" % (
(rev % { 'a': int(i[0]), 'b': int(i[1]), 'c': int(i[2]), (rev % {'a': int(i[0]), 'b': int(i[1]), 'c': int(i[2]),
'd': int(i[3]) }), 'd': int(i[3])}),
reverse, models.settings['dns_ttl'])) reverse, models.settings['dns_ttl']))
# ipv6 # ipv6
...@@ -384,16 +389,17 @@ def dns(): ...@@ -384,16 +389,17 @@ def dns():
reverse, models.settings['dns_ttl'])) reverse, models.settings['dns_ttl']))
for domain in models.Domain.objects.all(): for domain in models.Domain.objects.all():
DNS.append("Z%s:%s:support.ik.bme.hu::::::%s" % (domain.name, DNS.append("Z%s:%s:support.ik.bme.hu::::::%s" %
settings['dns_hostname'], models.settings['dns_ttl'])) (domain.name, settings['dns_hostname'],
models.settings['dns_ttl']))
for r in models.Record.objects.all(): for r in models.Record.objects.all():
d = r.get_data() d = r.get_data()
if d['type'] == 'A': if d['type'] == 'A':
DNS.append("+%s:%s:%s" % (d['name'], d['address'], d['ttl'])) DNS.append("+%s:%s:%s" % (d['name'], d['address'], d['ttl']))
elif d['type'] == 'AAAA': elif d['type'] == 'AAAA':
DNS.append(":%s:28:%s:%s" % (d['name'], DNS.append(":%s:28:%s:%s" %
ipv6_to_octal(d['address']), d['ttl'])) (d['name'], ipv6_to_octal(d['address']), d['ttl']))
elif d['type'] == 'NS': elif d['type'] == 'NS':
DNS.append("&%s::%s:%s" % (d['name'], d['address'], d['ttl'])) DNS.append("&%s::%s:%s" % (d['name'], d['address'], d['ttl']))
elif d['type'] == 'CNAME': elif d['type'] == 'CNAME':
...@@ -408,8 +414,9 @@ def dns(): ...@@ -408,8 +414,9 @@ def dns():
return DNS return DNS
process = subprocess.Popen(['/usr/bin/ssh', 'tinydns@%s' % process = subprocess.Popen(['/usr/bin/ssh', 'tinydns@%s' %
settings['dns_hostname']], shell=False, stdin=subprocess.PIPE) settings['dns_hostname']],
process.communicate("\n".join(DNS)+"\n") shell=False, stdin=subprocess.PIPE)
process.communicate("\n".join(DNS) + "\n")
# print "\n".join(DNS)+"\n" # print "\n".join(DNS)+"\n"
...@@ -422,6 +429,7 @@ def prefix_to_mask(prefix): ...@@ -422,6 +429,7 @@ def prefix_to_mask(prefix):
t[i] = 256 - (2 ** ((i + 1) * 8 - prefix)) t[i] = 256 - (2 ** ((i + 1) * 8 - prefix))
return ".".join([str(i) for i in t]) return ".".join([str(i) for i in t])
def dhcp(): def dhcp():
vlans = models.Vlan.objects.all() vlans = models.Vlan.objects.all()
regex = re.compile(r'^([0-9]+)\.([0-9]+)\.[0-9]+\.[0-9]+\s+' regex = re.compile(r'^([0-9]+)\.([0-9]+)\.[0-9]+\.[0-9]+\s+'
...@@ -434,7 +442,7 @@ def dhcp(): ...@@ -434,7 +442,7 @@ def dhcp():
if(i_vlan.dhcp_pool): if(i_vlan.dhcp_pool):
m = regex.search(i_vlan.dhcp_pool) m = regex.search(i_vlan.dhcp_pool)
if(m or i_vlan.dhcp_pool == "manual"): if(m or i_vlan.dhcp_pool == "manual"):
DHCP.append (''' DHCP.append('''
# %(name)s - %(interface)s # %(name)s - %(interface)s
subnet %(net)s netmask %(netmask)s { subnet %(net)s netmask %(netmask)s {
%(extra)s; %(extra)s;
...@@ -461,7 +469,7 @@ def dhcp(): ...@@ -461,7 +469,7 @@ def dhcp():
}) })
for i_host in i_vlan.host_set.all(): for i_host in i_vlan.host_set.all():
DHCP.append (''' DHCP.append('''
host %(hostname)s { host %(hostname)s {
hardware ethernet %(mac)s; hardware ethernet %(mac)s;
fixed-address %(ipv4)s; fixed-address %(ipv4)s;
...@@ -474,22 +482,7 @@ def dhcp(): ...@@ -474,22 +482,7 @@ def dhcp():
return DHCP return DHCP
process = subprocess.Popen(['/usr/bin/ssh', 'fw2', process = subprocess.Popen(['/usr/bin/ssh', 'fw2',
'cat > /tools/dhcp3/dhcpd.conf.generated;' 'cat > /tools/dhcp3/dhcpd.conf.generated;'
'sudo /etc/init.d/isc-dhcp-server restart'], shell=False, 'sudo /etc/init.d/isc-dhcp-server restart'],
stdin=subprocess.PIPE) shell=False, stdin=subprocess.PIPE)
# print "\n".join(DHCP)+"\n" # print "\n".join(DHCP)+"\n"
process.communicate("\n".join(DHCP)+"\n") process.communicate("\n".join(DHCP) + "\n")
'''
i=2
for mac, name, ipend in [("18:a9:05:64:19:aa", "mega6", 16), ("00:1e:0b:e9:79:1e", "blade1", 21), ("00:22:64:9c:fd:34", "blade2", 22), ("00:1e:0b:ec:65:46", "blade3", 23), ("b4:b5:2f:61:d2:5a", "cloud-man", 1)]:
h1 = models.Host(hostname= name, vlan=models.Vlan.objects.get(vid=3), mac=mac, ipv4="10.3.1.%d" % ipend, ipv6="2001:738:2001:4031:3:1:%d:0" % ipend, owner=auth.models.User.objects.get(username="bd"))
try:
h1.save()
h1.groups.add(models.Group.objects.get(name="netezhet manbol"))
h1.save()
# i = i + 1
except:
print "nemok %s" % name
'''
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2013-08-21 12:29+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1)\n"
#: fields.py:19
msgid "Enter a valid MAC address."
msgstr ""
#: fields.py:46
#, python-format
msgid "%s - only letters, numbers, underscores and hyphens are allowed!"
msgstr ""
#: fields.py:58
#, python-format
msgid "%s - invalid domain name"
msgstr ""
#: fields.py:80
#, python-format
msgid "%s - not an IPv4 address"
msgstr "%s - nem egy IPv4 cím"
#: fields.py:86
#, python-format
msgid "%s - not an IPv6 address"
msgstr ""
#: fields.py:97
msgid "Bad MX address format. Should be: <priority>:<hostname>"
msgstr ""
#: models.py:33
msgid "direction"
msgstr ""
#: models.py:34
msgid "If the rule matches egress or ingress packets."
msgstr ""
#: models.py:36 models.py:222 models.py:284 models.py:307 models.py:366
#: models.py:591 models.py:615
msgid "description"
msgstr ""
#: models.py:37
msgid "Why is the rule needed, or how does it work."
msgstr ""
#: models.py:40
msgid "foreign network"
msgstr ""
#: models.py:41
msgid ""
"The group of vlans the matching packet goes to (direction out) or from (in)."
msgstr ""
#: models.py:45
msgid "dest. port"
msgstr ""
#: models.py:47
msgid "Destination port number of packets that match."
msgstr ""
#: models.py:49
msgid "source port"
msgstr ""
#: models.py:51
msgid "Source port number of packets that match."
msgstr ""
#: models.py:53
msgid "protocol"
msgstr ""
#: models.py:54
msgid "Protocol of packets that match."
msgstr ""
#: models.py:55
msgid "extra arguments"
msgstr ""
#: models.py:56
msgid "Additional arguments passed literally to the iptables-rule."
msgstr ""
#: models.py:58
msgid "accept"
msgstr ""
#: models.py:59
msgid "Accept the matching packets (or deny if not checked)."
msgstr ""
#: models.py:62 models.py:253 models.py:287 models.py:310 models.py:377
#: models.py:585 models.py:614
msgid "owner"
msgstr ""
#: models.py:63
msgid "The user responsible for this rule."
msgstr ""
#: models.py:65
msgid "Rule type"
msgstr ""
#: models.py:67
msgid "The type of entity the rule belongs to."
msgstr ""
#: models.py:69
msgid "NAT"
msgstr ""
#: models.py:70
msgid "If network address translation shoud be done."
msgstr ""
#: models.py:74
msgid "Rewrite destination port number to."
msgstr ""
#: models.py:79 models.py:251 models.py:289 models.py:312 models.py:385
msgid "created at"
msgstr ""
#: models.py:82 models.py:255 models.py:291 models.py:314 models.py:387
msgid "modified at"
msgstr ""
#: models.py:85 models.py:374
msgid "vlan"
msgstr ""
#: models.py:86
msgid "Vlan the rule applies to (if type is vlan)."
msgstr ""
#: models.py:90
msgid "vlan group"
msgstr ""
#: models.py:91
msgid "Group of vlans the rule applies to (if type is vlan)."
msgstr ""
#: models.py:94 models.py:608 models.py:730
msgid "host"
msgstr ""
#: models.py:95
msgid "Host the rule applies to (if type is host)."
msgstr ""
#: models.py:98
msgid "host group"
msgstr ""
#: models.py:99
msgid "Group of hosts the rule applies to (if type is host)."
msgstr ""
#: models.py:102
msgid "firewall"
msgstr ""
#: models.py:103
msgid "Firewall the rule applies to (if type is firewall)."
msgstr ""
#: models.py:115
msgid "Only one field can be selected."
msgstr ""
#: models.py:136
msgid "rule"
msgstr ""
#: models.py:137
msgid "rules"
msgstr ""
#: models.py:163
msgid "VID"
msgstr ""
#: models.py:164
msgid "The vlan ID of the subnet."
msgstr ""
#: models.py:169
msgid "Name"
msgstr ""
#: models.py:170
msgid "The short name of the subnet."
msgstr ""
#: models.py:173
msgid "IPv4 prefix length"
msgstr ""
#: models.py:174
msgid "The prefix length of the IPv4 subnet."
msgstr ""
#: models.py:176
msgid "IPv6 prefix length"
msgstr ""
#: models.py:177
msgid "The prefix length of the IPv6 subnet."
msgstr ""
#: models.py:179
msgid "interface"
msgstr ""
#: models.py:180
msgid ""
"The name of network interface the gateway should serve this network on. For "
"example vlan0004 or eth2."
msgstr ""
#: models.py:184
msgid "IPv4 network"
msgstr ""
#: models.py:185
msgid "The network address of the IPv4 subnet."
msgstr ""
#: models.py:188
msgid "IPv6 network"
msgstr ""
#: models.py:189
msgid "The network address of the IPv6 subnet."
msgstr ""
#: models.py:192 models.py:346
msgid "IPv4 address"
msgstr ""
#: models.py:194
msgid ""
"The IPv4 address of the gateway. Recommended value is the last valid address "
"of the subnet, for example 10.4.255.254 for 10.4.0.0/16."
msgstr ""
#: models.py:201 models.py:357
msgid "IPv6 address"
msgstr ""
#: models.py:203
msgid "The IPv6 address of the gateway."
msgstr ""
#: models.py:207
msgid "NAT IP address"
msgstr ""
#: models.py:209
msgid ""
"Common IPv4 address used for address translation of connections to the "
"networks selected below (typically to the internet)."
msgstr ""
#: models.py:215
msgid "NAT to"
msgstr ""
#: models.py:217
msgid ""
"Connections to these networks should be network address translated, i.e. "
"their source address is rewritten to the value of NAT IP address."
msgstr ""
#: models.py:224
msgid "Description of the goals and elements of the vlan network."
msgstr ""
#: models.py:226
msgid "comment"
msgstr ""
#: models.py:228
msgid "Notes, comments about the network"
msgstr ""
#: models.py:229
msgid "domain name"
msgstr ""
#: models.py:230
msgid "Domain name of the members of this network."
msgstr ""
#: models.py:234
msgid "reverse domain"
msgstr ""
#: models.py:235
#, python-format
msgid ""
"Template of the IPv4 reverse domain name that should be generated for each "
"host. The template should contain four tokens: \"%(a)d\", \"%(b)d\", \"%(c)d"
"\", and \"%(d)d\", representing the four bytes of the address, respectively, "
"in decimal notation. For example, the template for the standard reverse "
"address is: \"%(d)d.%(c)d.%(b)d.%(a)d.in-addr.arpa\"."
msgstr ""
#: models.py:243
msgid "DHCP pool"
msgstr ""
#: models.py:245
msgid ""
"The address range of the DHCP pool: empty for no DHCP service, \"manual\" "
"for no DHCP pool, or the first and last address of the range separated by a "
"space."
msgstr ""
#: models.py:278 models.py:305 models.py:576 models.py:584 models.py:605
msgid "name"
msgstr ""
#: models.py:279 models.py:306
msgid "The name of the group."
msgstr ""
#: models.py:281
msgid "vlans"
msgstr ""
#: models.py:282
msgid "The vlans which are members of the group."
msgstr ""
#: models.py:285 models.py:308
msgid "Description of the group."
msgstr ""
#: models.py:330
msgid "hostname"
msgstr ""
#: models.py:331
msgid "The alphanumeric hostname of the host, the first part of the FQDN."
msgstr ""
#: models.py:336
msgid "reverse"
msgstr ""
#: models.py:337
msgid ""
"The fully qualified reverse hostname of the host, if different than hostname."
"domain."
msgstr ""
#: models.py:341
msgid "MAC address"
msgstr ""
#: models.py:342
msgid ""
"The MAC (Ethernet) address of the network interface. For example: 99:AA:BB:"
"CC:DD:EE."
msgstr ""
#: models.py:348
msgid "The real IPv4 address of the host, for example 10.5.1.34."
msgstr ""
#: models.py:352
msgid "WAN IPv4 address"
msgstr ""
#: models.py:353
msgid ""
"The public IPv4 address of the host on the wide area network, if different."
msgstr ""
#: models.py:359
msgid "The global IPv6 address of the host, for example 2001:500:88:200::10."
msgstr ""
#: models.py:362
msgid "shared IP"
msgstr ""
#: models.py:364
msgid "If the given WAN IPv4 address is used by multiple hosts."
msgstr ""
#: models.py:367
msgid "What is this host for, what kind of machine is it."
msgstr ""
#: models.py:370
msgid "Notes"
msgstr ""
#: models.py:371
msgid "location"
msgstr ""
#: models.py:373
msgid "The physical location of the machine."
msgstr ""
#: models.py:376
msgid "Vlan network that the host is part of."
msgstr ""
#: models.py:379
msgid "The person responsible for this host."
msgstr ""
#: models.py:381
msgid "groups"
msgstr ""
#: models.py:383
msgid "Host groups the machine is part of."
msgstr ""
#: models.py:398
msgid "If shared_ip has been checked, pub_ipv4 has to be unique."
msgstr ""
#: models.py:401
msgid "You can't use another host's NAT'd address as your own IPv4."
msgstr ""
#: models.py:454
#, python-format
msgid "All %s ports are already in use."
msgstr ""
#: models.py:471
#, python-format
msgid "Port %(proto)s %(public)s is already in use."
msgstr ""
#: models.py:479
msgid "Only ports above 1024 can be used."
msgstr ""
#: models.py:587 models.py:617 models.py:741
msgid "created_at"
msgstr ""
#: models.py:589 models.py:619 models.py:743
msgid "modified_at"
msgstr ""
#: models.py:590 models.py:613
msgid "ttl"
msgstr ""
#: models.py:606
msgid "domain"
msgstr ""
#: models.py:610 models.py:738
msgid "type"
msgstr ""
#: models.py:612
msgid "address"
msgstr ""
#: models.py:627
msgid "(empty)"
msgstr ""
#: models.py:638
msgid "Can't specify address for A or AAAA records if host is set!"
msgstr ""
#: models.py:641
msgid "Can't specify name for A or AAAA records if host is set!"
msgstr ""
#: models.py:645
msgid "Name must be specified for CNAME records if host is set!"
msgstr ""
#: models.py:648
msgid "Can't specify address for CNAME records if host is set!"
msgstr ""
#: models.py:656
msgid "Address must be specified!"
msgstr ""
#: models.py:666
msgid "Unknown record type."
msgstr ""
#: models.py:731
msgid "reason"
msgstr ""
#: models.py:733
msgid "short message"
msgstr ""
#: views.py:26
#, python-format
msgid ""
"Dear %s, you've signed in as administrator!<br />Reloading in 10 seconds..."
msgstr ""
#: views.py:30
#, python-format
msgid "Dear %s, you've signed in!"
msgstr ""
#: views.py:32
msgid "Dear anonymous, you've not signed in yet!"
msgstr ""
#: views.py:43
msgid "Wrong password."
msgstr ""
#: views.py:77 views.py:118
msgid "OK"
msgstr ""
#: views.py:80
msgid "Only vm-net and war can be used."
msgstr ""
#: views.py:111
msgid "Unknown command."
msgstr ""
#: views.py:114
#, python-format
msgid ""
"Something went wrong!\n"
"%s\n"
msgstr ""
#: views.py:116
msgid "Something went wrong!\n"
msgstr ""
# -*- coding: utf8 -*- # -*- coding: utf-8 -*-
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.db import models from django.db import models
from django.forms import fields, ValidationError from django.forms import ValidationError
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from firewall.fields import * from firewall.fields import (MACAddressField, val_alfanum, val_reverse_domain,
from south.modelsinspector import add_introspection_rules val_domain, val_ipv4, val_ipv6, val_mx,
ipv4_2_ipv6)
from django.core.validators import MinValueValidator, MaxValueValidator from django.core.validators import MinValueValidator, MaxValueValidator
import django.conf import django.conf
from django.db.models.signals import post_save from django.db.models.signals import post_save
import re
import random import random
settings = django.conf.settings.FIREWALL_SETTINGS settings = django.conf.settings.FIREWALL_SETTINGS
class Rule(models.Model): class Rule(models.Model):
"""
Common firewall rule
Rule can be applied to: Host, Firewall, Vlan """
A rule of a packet filter, changing the behavior of a host, vlan or
firewall.
Some rules accept or deny packets matching some criteria.
Others set address translation or other free-form iptables parameters.
""" """
CHOICES_type = (('host', 'host'), ('firewall', 'firewall'), CHOICES_type = (('host', 'host'), ('firewall', 'firewall'),
('vlan', 'vlan')) ('vlan', 'vlan'))
...@@ -26,35 +30,78 @@ class Rule(models.Model): ...@@ -26,35 +30,78 @@ class Rule(models.Model):
CHOICES_dir = (('0', 'out'), ('1', 'in')) CHOICES_dir = (('0', 'out'), ('1', 'in'))
direction = models.CharField(max_length=1, choices=CHOICES_dir, direction = models.CharField(max_length=1, choices=CHOICES_dir,
blank=False) blank=False, verbose_name=_("direction"),
description = models.TextField(blank=True) help_text=_("If the rule matches egress "
foreign_network = models.ForeignKey('VlanGroup', "or ingress packets."))
description = models.TextField(blank=True, verbose_name=_('description'),
help_text=_("Why is the rule needed, "
"or how does it work."))
foreign_network = models.ForeignKey(
'VlanGroup', verbose_name=_("foreign network"),
help_text=_("The group of vlans the matching packet goes to "
"(direction out) or from (in)."),
related_name="ForeignRules") related_name="ForeignRules")
dport = models.IntegerField(blank=True, null=True, dport = models.IntegerField(
validators=[MinValueValidator(1), MaxValueValidator(65535)]) blank=True, null=True, verbose_name=_("dest. port"),
sport = models.IntegerField(blank=True, null=True, validators=[MinValueValidator(1), MaxValueValidator(65535)],
validators=[MinValueValidator(1), MaxValueValidator(65535)]) help_text=_("Destination port number of packets that match."))
sport = models.IntegerField(
blank=True, null=True, verbose_name=_("source port"),
validators=[MinValueValidator(1), MaxValueValidator(65535)],
help_text=_("Source port number of packets that match."))
proto = models.CharField(max_length=10, choices=CHOICES_proto, proto = models.CharField(max_length=10, choices=CHOICES_proto,
blank=True, null=True) blank=True, null=True, verbose_name=_("protocol"),
extra = models.TextField(blank=True) help_text=_("Protocol of packets that match."))
accept = models.BooleanField(default=False) extra = models.TextField(blank=True, verbose_name=_("extra arguments"),
owner = models.ForeignKey(User, blank=True, null=True) help_text=_("Additional arguments passed "
r_type = models.CharField(max_length=10, choices=CHOICES_type) "literally to the iptables-rule."))
nat = models.BooleanField(default=False) accept = models.BooleanField(default=False, verbose_name=_("accept"),
help_text=_("Accept the matching packets "
"(or deny if not checked)."))
owner = models.ForeignKey(User, blank=True, null=True,
verbose_name=_("owner"),
help_text=_("The user responsible for "
"this rule."))
r_type = models.CharField(max_length=10, verbose_name=_("Rule type"),
choices=CHOICES_type,
help_text=_("The type of entity the rule "
"belongs to."))
nat = models.BooleanField(default=False, verbose_name=_("NAT"),
help_text=_("If network address translation "
"shoud be done."))
nat_dport = models.IntegerField(blank=True, null=True, nat_dport = models.IntegerField(blank=True, null=True,
validators=[MinValueValidator(1), MaxValueValidator(65535)]) help_text=_(
created_at = models.DateTimeField(auto_now_add=True) "Rewrite destination port number to."),
modified_at = models.DateTimeField(auto_now=True) validators=[MinValueValidator(1),
MaxValueValidator(65535)])
created_at = models.DateTimeField(
auto_now_add=True,
verbose_name=_("created at"))
modified_at = models.DateTimeField(
auto_now=True,
verbose_name=_("modified at"))
vlan = models.ForeignKey('Vlan', related_name="rules", blank=True, vlan = models.ForeignKey('Vlan', related_name="rules", blank=True,
null=True) null=True, verbose_name=_("vlan"),
help_text=_("Vlan the rule applies to "
"(if type is vlan)."))
vlangroup = models.ForeignKey('VlanGroup', related_name="rules", vlangroup = models.ForeignKey('VlanGroup', related_name="rules",
blank=True, null=True) blank=True, null=True, verbose_name=_(
"vlan group"),
help_text=_("Group of vlans the rule "
"applies to (if type is vlan)."))
host = models.ForeignKey('Host', related_name="rules", blank=True, host = models.ForeignKey('Host', related_name="rules", blank=True,
null=True) verbose_name=_('host'), null=True,
hostgroup = models.ForeignKey('Group', related_name="rules", help_text=_("Host the rule applies to "
blank=True, null=True) "(if type is host)."))
firewall = models.ForeignKey('Firewall', related_name="rules", hostgroup = models.ForeignKey(
'Group', related_name="rules", verbose_name=_("host group"),
blank=True, null=True, help_text=_("Group of hosts the rule applies "
"to (if type is host)."))
firewall = models.ForeignKey(
'Firewall', related_name="rules", verbose_name=_("firewall"),
help_text=_("Firewall the rule applies to "
"(if type is firewall)."),
blank=True, null=True) blank=True, null=True)
def __unicode__(self): def __unicode__(self):
...@@ -68,6 +115,8 @@ class Rule(models.Model): ...@@ -68,6 +115,8 @@ class Rule(models.Model):
raise ValidationError(_('Only one field can be selected.')) raise ValidationError(_('Only one field can be selected.'))
def desc(self): def desc(self):
"""Return a short string representation of the current rule.
"""
return u'[%(type)s] %(src)s ▸ %(dst)s %(para)s %(desc)s' % { return u'[%(type)s] %(src)s ▸ %(dst)s %(para)s %(desc)s' % {
'type': self.r_type, 'type': self.r_type,
'src': (unicode(self.foreign_network) if self.direction == '1' 'src': (unicode(self.foreign_network) if self.direction == '1'
...@@ -79,82 +128,263 @@ class Rule(models.Model): ...@@ -79,82 +128,263 @@ class Rule(models.Model):
(("dport=%s " % self.dport) if self.dport else '')), (("dport=%s " % self.dport) if self.dport else '')),
'desc': self.description} 'desc': self.description}
@models.permalink
def get_absolute_url(self):
return ('network.rule', None, {'pk': self.pk})
class Meta:
verbose_name = _("rule")
verbose_name_plural = _("rules")
ordering = (
'r_type',
'direction',
'proto',
'sport',
'dport',
'nat_dport',
'host',
)
class Vlan(models.Model): class Vlan(models.Model):
vid = models.IntegerField(unique=True)
name = models.CharField(max_length=20, unique=True, """
A vlan of the network,
Networks controlled by this framework are split into separated subnets.
These networks are izolated by the vlan (virtual lan) technology, which is
commonly used by managed network switches to partition the network.
Each vlan network has a unique identifier, a name, a unique IPv4 and IPv6
range. The gateway also has an IP address in each range.
"""
vid = models.IntegerField(unique=True,
verbose_name=_('VID'),
help_text=_('The vlan ID of the subnet.'),
validators=[MinValueValidator(1),
MaxValueValidator(4095)])
name = models.CharField(max_length=20,
unique=True,
verbose_name=_('Name'),
help_text=_('The short name of the subnet.'),
validators=[val_alfanum]) validators=[val_alfanum])
prefix4 = models.IntegerField(default=16) prefix4 = models.IntegerField(
prefix6 = models.IntegerField(default=80) default=16, verbose_name=_('IPv4 prefix length'),
interface = models.CharField(max_length=20, unique=True) help_text=_('The prefix length of the IPv4 subnet.'))
net4 = models.GenericIPAddressField(protocol='ipv4', unique=True) prefix6 = models.IntegerField(
net6 = models.GenericIPAddressField(protocol='ipv6', unique=True) default=80, verbose_name=_('IPv6 prefix length'),
ipv4 = models.GenericIPAddressField(protocol='ipv4', unique=True) help_text=_('The prefix length of the IPv6 subnet.'))
ipv6 = models.GenericIPAddressField(protocol='ipv6', unique=True) interface = models.CharField(max_length=20, unique=True,
verbose_name=_('interface'), help_text=_(
'The name of network interface the '
'gateway should serve this network on. '
'For example vlan0004 or eth2.'))
net4 = models.GenericIPAddressField(protocol='ipv4', unique=True,
verbose_name=_('IPv4 network'),
help_text=_('The network address of '
'the IPv4 subnet.'))
net6 = models.GenericIPAddressField(protocol='ipv6', unique=True,
verbose_name=_('IPv6 network'),
help_text=_('The network address of '
'the IPv6 subnet.'))
ipv4 = models.GenericIPAddressField(protocol='ipv4', unique=True,
verbose_name=_('IPv4 address'),
help_text=_(
'The IPv4 address of the gateway. '
'Recommended value is the last '
'valid address of the subnet, '
'for example '
'10.4.255.254 for 10.4.0.0/16.'))
ipv6 = models.GenericIPAddressField(protocol='ipv6',
unique=True,
verbose_name=_('IPv6 address'),
help_text=_(
'The IPv6 address of the '
'gateway.'))
snat_ip = models.GenericIPAddressField(protocol='ipv4', blank=True, snat_ip = models.GenericIPAddressField(protocol='ipv4', blank=True,
null=True) null=True,
verbose_name=_('NAT IP address'),
help_text=_(
'Common IPv4 address used for '
'address translation of '
'connections to the networks '
'selected below '
'(typically to the internet).'))
snat_to = models.ManyToManyField('self', symmetrical=False, blank=True, snat_to = models.ManyToManyField('self', symmetrical=False, blank=True,
null=True) null=True, verbose_name=_('NAT to'),
description = models.TextField(blank=True) help_text=_(
comment = models.TextField(blank=True) 'Connections to these networks '
domain = models.ForeignKey('Domain') 'should be network address '
reverse_domain = models.TextField(validators=[val_reverse_domain]) 'translated, i.e. their source '
dhcp_pool = models.TextField(blank=True) 'address is rewritten to the value '
created_at = models.DateTimeField(auto_now_add=True) 'of NAT IP address.'))
owner = models.ForeignKey(User, blank=True, null=True) description = models.TextField(blank=True, verbose_name=_('description'),
modified_at = models.DateTimeField(auto_now=True) help_text=_(
'Description of the goals and elements '
'of the vlan network.'))
comment = models.TextField(blank=True, verbose_name=_('comment'),
help_text=_(
'Notes, comments about the network'))
domain = models.ForeignKey('Domain', verbose_name=_('domain name'),
help_text=_('Domain name of the members of '
'this network.'))
reverse_domain = models.TextField(
validators=[val_reverse_domain],
verbose_name=_('reverse domain'),
help_text=_('Template of the IPv4 reverse domain name that '
'should be generated for each host. The template '
'should contain four tokens: "%(a)d", "%(b)d", '
'"%(c)d", and "%(d)d", representing the four bytes '
'of the address, respectively, in decimal notation. '
'For example, the template for the standard reverse '
'address is: "%(d)d.%(c)d.%(b)d.%(a)d.in-addr.arpa".'),
default="%(d)d.%(c)d.%(b)d.%(a)d.in-addr.arpa")
dhcp_pool = models.TextField(blank=True, verbose_name=_('DHCP pool'),
help_text=_(
'The address range of the DHCP pool: '
'empty for no DHCP service, "manual" for '
'no DHCP pool, or the first and last '
'address of the range separated by a '
'space.'))
created_at = models.DateTimeField(auto_now_add=True,
verbose_name=_('created at'))
owner = models.ForeignKey(User, blank=True, null=True,
verbose_name=_('owner'))
modified_at = models.DateTimeField(auto_now=True,
verbose_name=_('modified at'))
def __unicode__(self): def __unicode__(self):
return self.name return self.name
def net_ipv6(self): def net_ipv6(self):
"""String representation of selected IPv6 network."""
return self.net6 + "/" + unicode(self.prefix6) return self.net6 + "/" + unicode(self.prefix6)
def net_ipv4(self): def net_ipv4(self):
"""String representation of selected IPv4 network."""
return self.net4 + "/" + unicode(self.prefix4) return self.net4 + "/" + unicode(self.prefix4)
@models.permalink
def get_absolute_url(self):
return ('network.vlan', None, {'vid': self.vid})
class VlanGroup(models.Model): class VlanGroup(models.Model):
name = models.CharField(max_length=20, unique=True) """
A group of Vlans.
"""
name = models.CharField(max_length=20, unique=True, verbose_name=_('name'),
help_text=_('The name of the group.'))
vlans = models.ManyToManyField('Vlan', symmetrical=False, blank=True, vlans = models.ManyToManyField('Vlan', symmetrical=False, blank=True,
null=True) null=True, verbose_name=_('vlans'),
description = models.TextField(blank=True) help_text=_('The vlans which are members '
owner = models.ForeignKey(User, blank=True, null=True) 'of the group.'))
created_at = models.DateTimeField(auto_now_add=True) description = models.TextField(blank=True, verbose_name=_('description'),
modified_at = models.DateTimeField(auto_now=True) help_text=_('Description of the group.'))
owner = models.ForeignKey(User, blank=True, null=True,
verbose_name=_('owner'))
created_at = models.DateTimeField(auto_now_add=True,
verbose_name=_('created at'))
modified_at = models.DateTimeField(auto_now=True,
verbose_name=_('modified at'))
def __unicode__(self): def __unicode__(self):
return self.name return self.name
@models.permalink
def get_absolute_url(self):
return ('network.vlan_group', None, {'pk': self.pk})
class Group(models.Model): class Group(models.Model):
name = models.CharField(max_length=20, unique=True) """
description = models.TextField(blank=True) A group of hosts.
owner = models.ForeignKey(User, blank=True, null=True) """
created_at = models.DateTimeField(auto_now_add=True) name = models.CharField(max_length=20, unique=True, verbose_name=_('name'),
modified_at = models.DateTimeField(auto_now=True) help_text=_('The name of the group.'))
description = models.TextField(blank=True, verbose_name=_('description'),
help_text=_('Description of the group.'))
owner = models.ForeignKey(User, blank=True, null=True,
verbose_name=_('owner'))
created_at = models.DateTimeField(auto_now_add=True,
verbose_name=_('created at'))
modified_at = models.DateTimeField(auto_now=True,
verbose_name=_('modified at'))
def __unicode__(self): def __unicode__(self):
return self.name return self.name
@models.permalink
def get_absolute_url(self):
return ('network.group', None, {'pk': self.pk})
class Host(models.Model): class Host(models.Model):
"""
A host of the network.
"""
hostname = models.CharField(max_length=40, unique=True, hostname = models.CharField(max_length=40, unique=True,
verbose_name=_('hostname'),
help_text=_('The alphanumeric hostname of '
'the host, the first part of '
'the FQDN.'),
validators=[val_alfanum]) validators=[val_alfanum])
reverse = models.CharField(max_length=40, validators=[val_domain], reverse = models.CharField(max_length=40, validators=[val_domain],
verbose_name=_('reverse'),
help_text=_('The fully qualified reverse '
'hostname of the host, if '
'different than hostname.domain.'),
blank=True, null=True) blank=True, null=True)
mac = MACAddressField(unique=True) mac = MACAddressField(unique=True, verbose_name=_('MAC address'),
ipv4 = models.GenericIPAddressField(protocol='ipv4', unique=True) help_text=_('The MAC (Ethernet) address of the '
pub_ipv4 = models.GenericIPAddressField(protocol='ipv4', blank=True, 'network interface. For example: '
null=True) '99:AA:BB:CC:DD:EE.'))
ipv4 = models.GenericIPAddressField(protocol='ipv4', unique=True,
verbose_name=_('IPv4 address'),
help_text=_(
'The real IPv4 address of the '
'host, for example 10.5.1.34.'))
pub_ipv4 = models.GenericIPAddressField(
protocol='ipv4', blank=True, null=True,
verbose_name=_('WAN IPv4 address'),
help_text=_('The public IPv4 address of the host on the wide '
'area network, if different.'))
ipv6 = models.GenericIPAddressField(protocol='ipv6', unique=True, ipv6 = models.GenericIPAddressField(protocol='ipv6', unique=True,
blank=True, null=True) blank=True, null=True,
shared_ip = models.BooleanField(default=False) verbose_name=_('IPv6 address'),
description = models.TextField(blank=True) help_text=_(
comment = models.TextField(blank=True) 'The global IPv6 address of the '
location = models.TextField(blank=True) 'host, for example '
vlan = models.ForeignKey('Vlan') '2001:500:88:200::10.'))
owner = models.ForeignKey(User) shared_ip = models.BooleanField(default=False, verbose_name=_('shared IP'),
help_text=_(
'If the given WAN IPv4 address is '
'used by multiple hosts.'))
description = models.TextField(blank=True, verbose_name=_('description'),
help_text=_('What is this host for, what '
'kind of machine is it.'))
comment = models.TextField(blank=True,
verbose_name=_('Notes'))
location = models.TextField(blank=True, verbose_name=_('location'),
help_text=_(
'The physical location of the machine.'))
vlan = models.ForeignKey('Vlan', verbose_name=_('vlan'),
help_text=_(
'Vlan network that the host is part of.'))
owner = models.ForeignKey(User, verbose_name=_('owner'),
help_text=_(
'The person responsible for this host.'))
groups = models.ManyToManyField('Group', symmetrical=False, blank=True, groups = models.ManyToManyField('Group', symmetrical=False, blank=True,
null=True) null=True, verbose_name=_('groups'),
created_at = models.DateTimeField(auto_now_add=True) help_text=_(
modified_at = models.DateTimeField(auto_now=True) 'Host groups the machine is part of.'))
created_at = models.DateTimeField(auto_now_add=True,
verbose_name=_('created at'))
modified_at = models.DateTimeField(auto_now=True,
verbose_name=_('modified at'))
def __unicode__(self): def __unicode__(self):
return self.hostname return self.hostname
...@@ -182,47 +412,93 @@ class Host(models.Model): ...@@ -182,47 +412,93 @@ class Host(models.Model):
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=None, private=None): def _get_ports_used(self, proto):
proto = "tcp" if proto == "tcp" else "udp" """
Gives a list of port numbers used for the public IP address of current
host for the given protocol.
:param proto: The transport protocol of the generated port (tcp|udp).
:type proto: str.
:returns: list -- list of int port numbers used.
"""
if self.shared_ip: if self.shared_ip:
used_ports = Rule.objects.filter(host__pub_ipv4=self.pub_ipv4, ports = Rule.objects.filter(host__pub_ipv4=self.pub_ipv4,
nat=True, proto=proto).values_list('dport', flat=True) nat=True, proto=proto)
else:
ports = self.rules.filter(proto=proto, )
return ports.values_list('dport', flat=True)
if public is None: def _get_random_port(self, proto, used_ports=None):
public = random.randint(1024, 21000) """
if public in used_ports: Get a random unused port for given protocol for current host's public
IP address.
:param proto: The transport protocol of the generated port (tcp|udp).
:type proto: str.
:param used_ports: Optional list of used ports returned by
_get_ports_used.
:returns: int -- the generated port number.
:raises: ValidationError
"""
if used_ports is None:
used_ports = self._get_ports_used(proto)
public = random.randint(1024, 21000) # pick a random port
if public in used_ports: # if it's in use, select smallest free one
for i in range(1024, 21000) + range(24000, 65535): for i in range(1024, 21000) + range(24000, 65535):
if i not in used_ports: if i not in used_ports:
public = i public = i
break break
else: else:
raise ValidationError(_("Port %s %s is already in use.") % raise ValidationError(
(proto, public)) _("All %s ports are already in use.") % proto)
def add_port(self, proto, public=None, private=None):
"""
Allow inbound traffic to a port.
If the host uses a shared IP address, also set up port forwarding.
:param proto: The transport protocol (tcp|udp).
:type proto: str.
:param public: Preferred public port number for forwarding (optional).
:param private: Port number of host in subject.
"""
assert proto in ('tcp', 'udp', )
if public:
if public in self._get_ports_used(proto):
raise ValidationError(
_("Port %(proto)s %(public)s is already in use.") %
{'proto': proto, 'public': public})
else: else:
public = self._get_random_port(proto)
vg = VlanGroup.objects.get(name=settings["default_vlangroup"])
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."))
if public in used_ports:
raise ValidationError(_("Port %s %s is already in use.") %
(proto, public))
rule = Rule(direction='1', owner=self.owner, dport=public, rule = Rule(direction='1', owner=self.owner, dport=public,
proto=proto, nat=True, accept=True, r_type="host", proto=proto, nat=True, accept=True, r_type="host",
nat_dport=private, host=self, foreign_network=VlanGroup. nat_dport=private, host=self, foreign_network=vg)
objects.get(name=settings["default_vlangroup"]))
else: else:
if self.rules.filter(proto=proto, dport=public):
raise ValidationError(_("Port %s %s is already in use.") %
(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", proto=proto, nat=False, accept=True, r_type="host",
host=self, foreign_network=VlanGroup.objects host=self, foreign_network=vg)
.get(name=settings["default_vlangroup"]))
rule.full_clean() rule.full_clean()
rule.save() rule.save()
def del_port(self, proto, private): def del_port(self, proto, private):
"""
Remove rules about inbound traffic to a given port.
If the host uses a shared IP address, also set up port forwarding.
:param proto: The transport protocol (tcp|udp).
:type proto: str.
:param private: Port number of host in subject.
"""
if self.shared_ip: if self.shared_ip:
self.rules.filter(owner=self.owner, proto=proto, host=self, self.rules.filter(owner=self.owner, proto=proto, host=self,
nat_dport=private).delete() nat_dport=private).delete()
...@@ -231,6 +507,13 @@ class Host(models.Model): ...@@ -231,6 +507,13 @@ class Host(models.Model):
dport=private).delete() dport=private).delete()
def get_hostname(self, proto): def get_hostname(self, proto):
"""
Get a hostname for public ip address.
:param proto: The IP version (ipv4|ipv6).
:type proto: str.
"""
assert proto in ('ipv6', 'ipv4', )
try: try:
if proto == 'ipv6': if proto == 'ipv6':
res = self.record_set.filter(type='AAAA') res = self.record_set.filter(type='AAAA')
...@@ -242,13 +525,15 @@ class Host(models.Model): ...@@ -242,13 +525,15 @@ class Host(models.Model):
res = self.record_set.filter(type='A') res = self.record_set.filter(type='A')
return unicode(res[0].get_data()['name']) return unicode(res[0].get_data()['name'])
except: except:
# raise
if self.shared_ip: if self.shared_ip:
return self.pub_ipv4 return self.pub_ipv4
else: else:
return self.ipv4 return self.ipv4
def list_ports(self): def list_ports(self):
"""
Return a list of ports with forwarding rules set.
"""
retval = [] retval = []
for rule in self.rules.filter(owner=self.owner): for rule in self.rules.filter(owner=self.owner):
private = rule.nat_dport if self.shared_ip else rule.dport private = rule.nat_dport if self.shared_ip else rule.dport
...@@ -276,40 +561,62 @@ class Host(models.Model): ...@@ -276,40 +561,62 @@ class Host(models.Model):
return retval return retval
def get_fqdn(self): def get_fqdn(self):
"""
Get fully qualified host name of host.
"""
return self.hostname + u'.' + unicode(self.vlan.domain) return self.hostname + u'.' + unicode(self.vlan.domain)
@models.permalink
def get_absolute_url(self):
return ('network.host', None, {'pk': self.pk})
class Firewall(models.Model): class Firewall(models.Model):
name = models.CharField(max_length=20, unique=True) name = models.CharField(max_length=20, unique=True,
verbose_name=_('name'))
def __unicode__(self): def __unicode__(self):
return self.name return self.name
class Domain(models.Model): class Domain(models.Model):
name = models.CharField(max_length=40, validators=[val_domain]) name = models.CharField(max_length=40, validators=[val_domain],
owner = models.ForeignKey(User) verbose_name=_('name'))
created_at = models.DateTimeField(auto_now_add=True) owner = models.ForeignKey(User, verbose_name=_('owner'))
modified_at = models.DateTimeField(auto_now=True) created_at = models.DateTimeField(auto_now_add=True,
ttl = models.IntegerField(default=600) verbose_name=_('created_at'))
description = models.TextField(blank=True) modified_at = models.DateTimeField(auto_now=True,
verbose_name=_('modified_at'))
ttl = models.IntegerField(default=600, verbose_name=_('ttl'))
description = models.TextField(blank=True, verbose_name=_('description'))
def __unicode__(self): def __unicode__(self):
return self.name return self.name
@models.permalink
def get_absolute_url(self):
return ('network.domain', None, {'pk': self.pk})
class Record(models.Model): class Record(models.Model):
CHOICES_type = (('A', 'A'), ('CNAME', 'CNAME'), ('AAAA', 'AAAA'), CHOICES_type = (('A', 'A'), ('CNAME', 'CNAME'), ('AAAA', 'AAAA'),
('MX', 'MX'), ('NS', 'NS'), ('PTR', 'PTR'), ('TXT', 'TXT')) ('MX', 'MX'), ('NS', 'NS'), ('PTR', 'PTR'), ('TXT', 'TXT'))
name = models.CharField(max_length=40, validators=[val_domain], name = models.CharField(max_length=40, validators=[val_domain],
blank=True, null=True) blank=True, null=True, verbose_name=_('name'))
domain = models.ForeignKey('Domain') domain = models.ForeignKey('Domain', verbose_name=_('domain'))
host = models.ForeignKey('Host', blank=True, null=True) host = models.ForeignKey('Host', blank=True, null=True,
type = models.CharField(max_length=6, choices=CHOICES_type) verbose_name=_('host'))
address = models.CharField(max_length=40, blank=True, null=True) type = models.CharField(max_length=6, choices=CHOICES_type,
ttl = models.IntegerField(default=600) verbose_name=_('type'))
owner = models.ForeignKey(User) address = models.CharField(max_length=40, blank=True, null=True,
description = models.TextField(blank=True) verbose_name=_('address'))
created_at = models.DateTimeField(auto_now_add=True) ttl = models.IntegerField(default=600, verbose_name=_('ttl'))
modified_at = models.DateTimeField(auto_now=True) owner = models.ForeignKey(User, verbose_name=_('owner'))
description = models.TextField(blank=True, verbose_name=_('description'))
created_at = models.DateTimeField(auto_now_add=True,
verbose_name=_('created_at'))
modified_at = models.DateTimeField(auto_now=True,
verbose_name=_('modified_at'))
def __unicode__(self): def __unicode__(self):
return self.desc() return self.desc()
...@@ -323,18 +630,16 @@ class Record(models.Model): ...@@ -323,18 +630,16 @@ class Record(models.Model):
self.full_clean() self.full_clean()
super(Record, self).save(*args, **kwargs) super(Record, self).save(*args, **kwargs)
def clean(self): def _validate_w_host(self):
if self.name: """Validate a record with host set."""
self.name = self.name.rstrip(".") # remove trailing dots assert self.host
if self.host:
if self.type in ['A', 'AAAA']: if self.type in ['A', 'AAAA']:
if self.address: if self.address:
raise ValidationError(_("Can't specify address for " raise ValidationError(_("Can't specify address for A "
"A or AAAA records if host is set!")) "or AAAA records if host is set!"))
if self.name: if self.name:
raise ValidationError(_("Can't specify name for " raise ValidationError(_("Can't specify name for A "
"A or AAAA records if host is set!")) "or AAAA records if host is set!"))
elif self.type == 'CNAME': elif self.type == 'CNAME':
if not self.name: if not self.name:
raise ValidationError(_("Name must be specified for " raise ValidationError(_("Name must be specified for "
...@@ -342,10 +647,13 @@ class Record(models.Model): ...@@ -342,10 +647,13 @@ class Record(models.Model):
if self.address: if self.address:
raise ValidationError(_("Can't specify address for " raise ValidationError(_("Can't specify address for "
"CNAME records if host is set!")) "CNAME records if host is set!"))
else: # if self.host is None
def _validate_wo_host(self):
"""Validate a record without a host set."""
assert self.host is None
if not self.address: if not self.address:
raise ValidationError(_("Address must be specified!")) raise ValidationError(_("Address must be specified!"))
if self.type == 'A': if self.type == 'A':
val_ipv4(self.address) val_ipv4(self.address)
elif self.type == 'AAAA': elif self.type == 'AAAA':
...@@ -353,16 +661,23 @@ class Record(models.Model): ...@@ -353,16 +661,23 @@ class Record(models.Model):
elif self.type in ['CNAME', 'NS', 'PTR', 'TXT']: elif self.type in ['CNAME', 'NS', 'PTR', 'TXT']:
val_domain(self.address) val_domain(self.address)
elif self.type == 'MX': elif self.type == 'MX':
mx = self.address.split(':', 1) val_mx(self.address)
if not (len(mx) == 2 and mx[0].isdigit() and
domain_re.match(mx[1])):
raise ValidationError(_("Bad address format. "
"Should be: <priority>:<hostname>"))
else: else:
raise ValidationError(_("Unknown record type.")) raise ValidationError(_("Unknown record type."))
def get_name(self):
return self.__get_name() def clean(self):
def __get_name(self): """Validate the Record to be saved.
"""
if self.name:
self.name = self.name.rstrip(".") # remove trailing dots
if self.host:
self._validate_w_host()
else:
self._validate_wo_host()
@property
def fqdn(self):
if self.host and self.type != 'MX': if self.host and self.type != 'MX':
if self.type in ['A', 'AAAA']: if self.type in ['A', 'AAAA']:
return self.host.get_fqdn() return self.host.get_fqdn()
...@@ -390,7 +705,7 @@ class Record(models.Model): ...@@ -390,7 +705,7 @@ class Record(models.Model):
return self.address return self.address
def get_data(self): def get_data(self):
name = self.__get_name() name = self.fqdn
address = self.__get_address() address = self.__get_address()
if self.host and self.type == 'AAAA' and not self.host.ipv6: if self.host and self.type == 'AAAA' and not self.host.ipv6:
return None return None
...@@ -402,22 +717,43 @@ class Record(models.Model): ...@@ -402,22 +717,43 @@ class Record(models.Model):
'ttl': self.ttl, 'ttl': self.ttl,
'address': address} 'address': address}
@models.permalink
def get_absolute_url(self):
return ('network.record', None, {'pk': self.pk})
class Blacklist(models.Model): class Blacklist(models.Model):
CHOICES_type = (('permban', 'permanent ban'), ('tempban', 'temporary ban'), ('whitelist', 'whitelist'), ('tempwhite', 'tempwhite')) CHOICES_type = (('permban', 'permanent ban'), ('tempban', 'temporary ban'),
('whitelist', 'whitelist'), ('tempwhite', 'tempwhite'))
ipv4 = models.GenericIPAddressField(protocol='ipv4', unique=True) ipv4 = models.GenericIPAddressField(protocol='ipv4', unique=True)
host = models.ForeignKey('Host', blank=True, null=True) host = models.ForeignKey('Host', blank=True, null=True,
reason = models.TextField(blank=True) verbose_name=_('host'))
snort_message = models.TextField(blank=True) reason = models.TextField(blank=True, verbose_name=_('reason'))
type = models.CharField(max_length=10, choices=CHOICES_type, default='tempban') snort_message = models.TextField(blank=True,
created_at = models.DateTimeField(auto_now_add=True) verbose_name=_('short message'))
modified_at = models.DateTimeField(auto_now=True) type = models.CharField(
max_length=10,
choices=CHOICES_type,
default='tempban',
verbose_name=_('type')
)
created_at = models.DateTimeField(auto_now_add=True,
verbose_name=_('created_at'))
modified_at = models.DateTimeField(auto_now=True,
verbose_name=_('modified_at'))
def save(self, *args, **kwargs): def save(self, *args, **kwargs):
self.full_clean() self.full_clean()
super(Blacklist, self).save(*args, **kwargs) super(Blacklist, self).save(*args, **kwargs)
def __unicode__(self): def __unicode__(self):
return self.ipv4 return self.ipv4
@models.permalink
def get_absolute_url(self):
return ('network.blacklist', None, {'pk': self.pk})
def send_task(sender, instance, created, **kwargs): def send_task(sender, instance, created, **kwargs):
from firewall.tasks import ReloadTask from firewall.tasks import ReloadTask
ReloadTask.apply_async(args=[sender.__name__]) ReloadTask.apply_async(args=[sender.__name__])
......
from celery.task import Task, PeriodicTask from celery.task import Task, PeriodicTask
import celery import celery
from django.core.cache import cache from django.core.cache import cache
import os
import time
from firewall.fw import * from firewall.fw import *
import django.conf import django.conf
settings = django.conf.settings.FIREWALL_SETTINGS settings = django.conf.settings.FIREWALL_SETTINGS
@celery.task @celery.task
def reload_dns_task(data): def reload_dns_task(data):
pass pass
@celery.task @celery.task
def reload_firewall_task(data4, data6): def reload_firewall_task(data4, data6):
pass pass
@celery.task @celery.task
def reload_dhcp_task(data): def reload_dhcp_task(data):
pass pass
@celery.task @celery.task
def reload_blacklist_task(data): def reload_blacklist_task(data):
pass pass
class Periodic(PeriodicTask): class Periodic(PeriodicTask):
run_every = timedelta(seconds=10) run_every = timedelta(seconds=10)
...@@ -48,10 +54,11 @@ class Periodic(PeriodicTask): ...@@ -48,10 +54,11 @@ class Periodic(PeriodicTask):
reload_blacklist_task.delay(list(ipset())) reload_blacklist_task.delay(list(ipset()))
print "blacklist ujratoltese kesz" print "blacklist ujratoltese kesz"
class ReloadTask(Task): class ReloadTask(Task):
def run(self, type='Host'): def run(self, type='Host'):
if type in ["Host", "Records", "Domain", "Vlan"]: if type in ["Host", "Record", "Domain", "Vlan"]:
cache.add("dns_lock", "true", 30) cache.add("dns_lock", "true", 30)
if type == "Host": if type == "Host":
...@@ -64,4 +71,3 @@ class ReloadTask(Task): ...@@ -64,4 +71,3 @@ class ReloadTask(Task):
cache.add("blacklist_lock", "true", 30) cache.add("blacklist_lock", "true", 30)
print type print type
from django.test import TestCase from django.test import TestCase
from admin import HostAdmin from admin import HostAdmin
class MockInstance: class MockInstance:
def __init__(self, groups): def __init__(self, groups):
self.groups = MockGroups(groups) self.groups = MockGroups(groups)
class MockGroup: class MockGroup:
def __init__(self, name): def __init__(self, name):
self.name = name self.name = name
class MockGroups: class MockGroups:
def __init__(self, groups): def __init__(self, groups):
self.groups = groups self.groups = groups
...@@ -16,6 +19,7 @@ class MockGroups: ...@@ -16,6 +19,7 @@ class MockGroups:
def all(self): def all(self):
return self.groups return self.groups
class HostAdminTestCase(TestCase): class HostAdminTestCase(TestCase):
def test_no_groups(self): def test_no_groups(self):
instance = MockInstance([]) instance = MockInstance([])
......
...@@ -2,12 +2,10 @@ import base64 ...@@ -2,12 +2,10 @@ import base64
import datetime import datetime
import json import json
import re import re
import sys
from django.conf import settings from django.conf import settings
from django.db import IntegrityError from django.db import IntegrityError
from django.http import HttpResponse from django.http import HttpResponse
from django.shortcuts import render_to_response
from django.template.loader import render_to_string from django.template.loader import render_to_string
from django.utils import translation from django.utils import translation
from django.utils.timezone import utc from django.utils.timezone import utc
...@@ -15,13 +13,13 @@ from django.utils.translation import ugettext_lazy as _ ...@@ -15,13 +13,13 @@ from django.utils.translation import ugettext_lazy as _
from django.views.decorators.csrf import csrf_exempt from django.views.decorators.csrf import csrf_exempt
from django.views.decorators.http import require_POST from django.views.decorators.http import require_POST
from celery.task.control import inspect
from tasks import * from tasks import *
from firewall.fw import * from firewall.fw import *
from firewall.models import * from firewall.models import *
from one.tasks import SendMailTask from one.tasks import SendMailTask
def reload_firewall(request): def reload_firewall(request):
if request.user.is_authenticated(): if request.user.is_authenticated():
if request.user.is_superuser: if request.user.is_superuser:
...@@ -34,36 +32,46 @@ def reload_firewall(request): ...@@ -34,36 +32,46 @@ def reload_firewall(request):
html = _("Dear anonymous, you've not signed in yet!") html = _("Dear anonymous, you've not signed in yet!")
return HttpResponse(html) return HttpResponse(html)
@csrf_exempt @csrf_exempt
@require_POST @require_POST
def firewall_api(request): def firewall_api(request):
try: try:
data=json.loads(base64.b64decode(request.POST["data"])) data = json.loads(base64.b64decode(request.POST["data"]))
command = request.POST["command"] command = request.POST["command"]
if data["password"] != "bdmegintelrontottaanetet": if data["password"] != "bdmegintelrontottaanetet":
raise Exception(_("Wrong password.")) raise Exception(_("Wrong password."))
if command == "blacklist": if command == "blacklist":
obj, created = Blacklist.objects.get_or_create(ipv4=data["ip"]) obj, created = Blacklist.objects.get_or_create(ipv4=data["ip"])
obj.reason=data["reason"] obj.reason = data["reason"]
obj.snort_message=data["snort_message"] obj.snort_message = data["snort_message"]
if created: if created:
try: try:
obj.host = models.Host.objects.get(ipv4=data["ip"]) obj.host = Host.objects.get(ipv4=data["ip"])
user = obj.host.owner user = obj.host.owner
lang = user.person_set.all()[0].language lang = user.person_set.all()[0].language
translation.activate(lang) translation.activate(lang)
msg = render_to_string('mails/notification-ban-now.txt', msg = render_to_string(
{ 'user': user, 'mails/notification-ban-now.txt',
{
'user': user,
'bl': obj, 'bl': obj,
'instance:': obj.host.instance_set.get(), 'instance:': obj.host.instance_set.get(),
'url': settings.CLOUD_URL} ) 'url': settings.CLOUD_URL
SendMailTask.delay(to=obj.host.owner.email, subject='[IK Cloud] %s' % obj.host.instance_set.get().name, msg=msg, sender=u'cloud@ik.bme.hu') })
except (Host.DoesNotExist, ValidationError, IntegrityError, AttributeError): SendMailTask.delay(
to=obj.host.owner.email,
subject='[IK Cloud] %s' %
obj.host.instance_set.get().name,
msg=msg, sender=u'cloud@ik.bme.hu')
except (Host.DoesNotExist, ValidationError,
IntegrityError, AttributeError):
pass pass
print obj.modified_at + datetime.timedelta(minutes=5)
print datetime.datetime.utcnow().replace(tzinfo=utc) modified = obj.modified_at + datetime.timedelta(minutes=1)
if obj.type == 'tempwhite' and obj.modified_at + datetime.timedelta(minutes=1) < datetime.datetime.utcnow().replace(tzinfo=utc): now = datetime.dateime.utcnow().replace(tzinfo=utc)
if obj.type == 'tempwhite' and modified < now:
obj.type = 'tempban' obj.type = 'tempban'
obj.save() obj.save()
return HttpResponse(unicode(_("OK"))) return HttpResponse(unicode(_("OK")))
...@@ -76,10 +84,10 @@ def firewall_api(request): ...@@ -76,10 +84,10 @@ def firewall_api(request):
if command == "create": if command == "create":
data["owner"] = "opennebula" data["owner"] = "opennebula"
owner = auth.models.User.objects.get(username=data["owner"]) owner = auth.models.User.objects.get(username=data["owner"])
host = models.Host(hostname=data["hostname"], host = Host(hostname=data["hostname"],
vlan=models.Vlan.objects.get(name=data["vlan"]), vlan=Vlan.objects.get(name=data["vlan"]),
mac=data["mac"], ipv4=data["ip"], owner=owner, mac=data["mac"], ipv4=data["ip"], owner=owner,
description=data["description"], pub_ipv4=models. description=data["description"], pub_ipv4=
Vlan.objects.get(name=data["vlan"]).snat_ip, Vlan.objects.get(name=data["vlan"]).snat_ip,
shared_ip=True) shared_ip=True)
host.full_clean() host.full_clean()
...@@ -88,15 +96,14 @@ def firewall_api(request): ...@@ -88,15 +96,14 @@ def firewall_api(request):
host.enable_net() host.enable_net()
for p in data["portforward"]: for p in data["portforward"]:
host.add_port(proto=p["proto"], host.add_port(proto=p["proto"], public=int(p["public_port"]),
public=int(p["public_port"]),
private=int(p["private_port"])) private=int(p["private_port"]))
elif command == "destroy": elif command == "destroy":
data["owner"] = "opennebula" data["owner"] = "opennebula"
print data["hostname"] print data["hostname"]
owner = auth.models.User.objects.get(username=data["owner"]) owner = auth.models.User.objects.get(username=data["owner"])
host = models.Host.objects.get(hostname=data["hostname"], host = Host.objects.get(hostname=data["hostname"],
owner=owner) owner=owner)
host.delete() host.delete()
......
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