diff --git a/machines/admin.py b/machines/admin.py index 5fe4c49b..49b02a7e 100644 --- a/machines/admin.py +++ b/machines/admin.py @@ -26,7 +26,9 @@ from __future__ import unicode_literals from django.contrib import admin from reversion.admin import VersionAdmin -from .models import IpType, Machine, MachineType, Domain, IpList, Interface, Extension, Mx, Ns, Vlan, Text, Nas, Service +from .models import IpType, Machine, MachineType, Domain, IpList, Interface +from .models import Extension, Mx, Ns, Vlan, Text, Nas, Service, OuverturePort +from .models import OuverturePortList class MachineAdmin(VersionAdmin): pass @@ -58,6 +60,12 @@ class NasAdmin(VersionAdmin): class IpListAdmin(VersionAdmin): pass +class OuverturePortAdmin(VersionAdmin): + pass + +class OuverturePortListAdmin(VersionAdmin): + pass + class InterfaceAdmin(VersionAdmin): list_display = ('machine','type','mac_address','ipv4','details') @@ -80,3 +88,7 @@ admin.site.register(Domain, DomainAdmin) admin.site.register(Service, ServiceAdmin) admin.site.register(Vlan, VlanAdmin) admin.site.register(Nas, NasAdmin) +admin.site.register(OuverturePort, OuverturePortAdmin) +admin.site.register(OuverturePortList, OuverturePortListAdmin) + + diff --git a/machines/forms.py b/machines/forms.py index 9f5a96ca..18631651 100644 --- a/machines/forms.py +++ b/machines/forms.py @@ -22,9 +22,11 @@ from __future__ import unicode_literals +import re + from django.forms import ModelForm, Form, ValidationError from django import forms -from .models import Domain, Machine, Interface, IpList, MachineType, Extension, Mx, Text, Ns, Service, Vlan, Nas, IpType +from .models import Domain, Machine, Interface, IpList, MachineType, Extension, Mx, Text, Ns, Service, Vlan, Nas, IpType, OuverturePortList, OuverturePort from django.db.models import Q from django.core.validators import validate_email @@ -50,7 +52,8 @@ class BaseEditMachineForm(EditMachineForm): class EditInterfaceForm(ModelForm): class Meta: model = Interface - fields = '__all__' + # fields = '__all__' + exclude = ['port_lists'] def __init__(self, *args, **kwargs): super(EditInterfaceForm, self).__init__(*args, **kwargs) @@ -142,7 +145,7 @@ class DelMachineTypeForm(Form): class IpTypeForm(ModelForm): class Meta: model = IpType - fields = ['type','extension','need_infra','domaine_ip_start','domaine_ip_stop', 'vlan'] + fields = ['type','extension','need_infra','domaine_ip_start','domaine_ip_stop', 'prefix_v6', 'vlan'] def __init__(self, *args, **kwargs): @@ -151,7 +154,7 @@ class IpTypeForm(ModelForm): class EditIpTypeForm(IpTypeForm): class Meta(IpTypeForm.Meta): - fields = ['extension','type','need_infra', 'vlan'] + fields = ['extension','type','need_infra', 'prefix_v6', 'vlan'] class DelIpTypeForm(Form): iptypes = forms.ModelMultipleChoiceField(queryset=IpType.objects.all(), label="Types d'ip actuelles", widget=forms.CheckboxSelectMultiple) @@ -232,5 +235,13 @@ class VlanForm(ModelForm): class DelVlanForm(Form): vlan = forms.ModelMultipleChoiceField(queryset=Vlan.objects.all(), label="Vlan actuels", widget=forms.CheckboxSelectMultiple) +class EditOuverturePortConfigForm(ModelForm): + class Meta: + model = Interface + fields = ['port_lists'] +class EditOuverturePortListForm(ModelForm): + class Meta: + model = OuverturePortList + fields = '__all__' diff --git a/machines/migrations/0058_auto_20171002_0350.py b/machines/migrations/0058_auto_20171002_0350.py new file mode 100644 index 00000000..bc6b2508 --- /dev/null +++ b/machines/migrations/0058_auto_20171002_0350.py @@ -0,0 +1,43 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.7 on 2017-10-02 01:50 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('machines', '0057_nas_autocapture_mac'), + ] + + operations = [ + migrations.CreateModel( + name='OuverturePort', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('begin', models.IntegerField()), + ('end', models.IntegerField()), + ('protocole', models.CharField(choices=[('T', 'TCP'), ('U', 'UDP')], default='T', max_length=1)), + ('io', models.CharField(choices=[('I', 'IN'), ('O', 'OUT')], default='O', max_length=1)), + ], + ), + migrations.CreateModel( + name='OuverturePortList', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(help_text='Nom de la configuration des ports.', max_length=255)), + ], + ), + migrations.AddField( + model_name='ouvertureport', + name='port_list', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='machines.OuverturePortList'), + ), + migrations.AddField( + model_name='interface', + name='port_lists', + field=models.ManyToManyField(blank=True, to='machines.OuverturePortList'), + ), + ] diff --git a/machines/migrations/0059_iptype_prefix_v6.py b/machines/migrations/0059_iptype_prefix_v6.py new file mode 100644 index 00000000..464fc5e6 --- /dev/null +++ b/machines/migrations/0059_iptype_prefix_v6.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.7 on 2017-10-02 16:33 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('machines', '0058_auto_20171002_0350'), + ] + + operations = [ + migrations.AddField( + model_name='iptype', + name='prefix_v6', + field=models.GenericIPAddressField(blank=True, null=True, protocol='IPv6'), + ), + ] diff --git a/machines/models.py b/machines/models.py index 8eff89fc..8e7b1c3c 100644 --- a/machines/models.py +++ b/machines/models.py @@ -70,6 +70,7 @@ class IpType(models.Model): need_infra = models.BooleanField(default=False) domaine_ip_start = models.GenericIPAddressField(protocol='IPv4') domaine_ip_stop = models.GenericIPAddressField(protocol='IPv4') + prefix_v6 = models.GenericIPAddressField(protocol='IPv6', null=True, blank=True) vlan = models.ForeignKey('Vlan', on_delete=models.PROTECT, blank=True, null=True) @cached_property @@ -122,6 +123,9 @@ class IpType(models.Model): for element in IpType.objects.all().exclude(pk=self.pk): if not self.ip_set.isdisjoint(element.ip_set): raise ValidationError("Le range indiqué n'est pas disjoint des ranges existants") + # On formate le prefix v6 + if self.prefix_v6: + self.prefix_v6 = str(IPNetwork(self.prefix_v6 + '/64').network) return def save(self, *args, **kwargs): @@ -218,11 +222,11 @@ class Interface(models.Model): PRETTY_NAME = "Interface" ipv4 = models.OneToOneField('IpList', on_delete=models.PROTECT, blank=True, null=True) - #ipv6 = models.GenericIPAddressField(protocol='IPv6', null=True) mac_address = MACAddressField(integer=False, unique=True) machine = models.ForeignKey('Machine', on_delete=models.CASCADE) type = models.ForeignKey('MachineType', on_delete=models.PROTECT) details = models.CharField(max_length=255, blank=True) + port_lists = models.ManyToManyField('OuverturePortList', blank=True) @cached_property def is_active(self): @@ -231,6 +235,18 @@ class Interface(models.Model): user = self.machine.user return machine.active and user.has_access() + + @cached_property + def ipv6_object(self): + if self.type.ip_type.prefix_v6: + return EUI(self.mac_address).ipv6(IPNetwork(self.type.ip_type.prefix_v6).network) + else: + return None + + @cached_property + def ipv6(self): + return str(self.ipv6_object) + def mac_bare(self): return str(EUI(self.mac_address, dialect=mac_bare)).lower() @@ -278,6 +294,15 @@ class Interface(models.Model): domain = None return str(domain) + def has_private_ip(self): + if self.ipv4: + return IPAddress(str(self.ipv4)).is_private() + else: + return False + + def may_have_port_open(self): + return self.ipv4 and not self.has_private_ip() + class Domain(models.Model): PRETTY_NAME = "Domaine dns" @@ -406,6 +431,67 @@ class Service_link(models.Model): def __str__(self): return str(self.server) + " " + str(self.service) + +class OuverturePortList(models.Model): + """Liste des ports ouverts sur une interface.""" + name = models.CharField(help_text="Nom de la configuration des ports.", max_length=255) + + def __str__(self): + return self.name + + def tcp_ports_in(self): + return self.ouvertureport_set.filter(protocole=OuverturePort.TCP, io=OuverturePort.IN) + + def udp_ports_in(self): + return self.ouvertureport_set.filter(protocole=OuverturePort.UDP, io=OuverturePort.IN) + + def tcp_ports_out(self): + return self.ouvertureport_set.filter(protocole=OuverturePort.TCP, io=OuverturePort.OUT) + + def udp_ports_out(self): + return self.ouvertureport_set.filter(protocole=OuverturePort.UDP, io=OuverturePort.OUT) + + +class OuverturePort(models.Model): + """ + Représente un simple port ou une plage de ports. + + Les ports de la plage sont compris entre begin et en inclus. + Si begin == end alors on ne représente qu'un seul port. + """ + TCP = 'T' + UDP = 'U' + IN = 'I' + OUT = 'O' + begin = models.IntegerField() + end = models.IntegerField() + port_list = models.ForeignKey('OuverturePortList', on_delete=models.CASCADE) + protocole = models.CharField( + max_length=1, + choices=( + (TCP, 'TCP'), + (UDP, 'UDP'), + ), + default=TCP, + ) + io = models.CharField( + max_length=1, + choices=( + (IN, 'IN'), + (OUT, 'OUT'), + ), + default=OUT, + ) + + def __str__(self): + if self.begin == self.end : + return str(self.begin) + return '-'.join([str(self.begin), str(self.end)]) + + def show_port(self): + return str(self) + + @receiver(post_save, sender=Machine) def machine_post_save(sender, **kwargs): user = kwargs['instance'].user @@ -426,6 +512,9 @@ def interface_post_save(sender, **kwargs): interface = kwargs['instance'] user = interface.machine.user user.ldap_sync(base=False, access_refresh=False, mac_refresh=True) + if not interface.may_have_port_open() and interface.port_lists.all(): + interface.port_lists.clear() + # Regen services regen('dhcp') regen('mac_ip_list') diff --git a/machines/serializers.py b/machines/serializers.py index bfe5f295..51daa4b5 100644 --- a/machines/serializers.py +++ b/machines/serializers.py @@ -55,6 +55,25 @@ class InterfaceSerializer(serializers.ModelSerializer): def get_macaddress(self, obj): return str(obj.mac_address) +class FullInterfaceSerializer(serializers.ModelSerializer): + ipv4 = IpListSerializer(read_only=True) + mac_address = serializers.SerializerMethodField('get_macaddress') + domain = serializers.SerializerMethodField('get_dns') + extension = serializers.SerializerMethodField('get_interface_extension') + + class Meta: + model = Interface + fields = ('ipv4', 'ipv6', 'mac_address', 'domain', 'extension') + + def get_dns(self, obj): + return obj.domain.name + + def get_interface_extension(self, obj): + return obj.domain.extension.name + + def get_macaddress(self, obj): + return str(obj.mac_address) + class ExtensionNameField(serializers.RelatedField): def to_representation(self, value): return value.name diff --git a/machines/templates/machines/aff_iptype.html b/machines/templates/machines/aff_iptype.html index 46318c83..aafc4c1d 100644 --- a/machines/templates/machines/aff_iptype.html +++ b/machines/templates/machines/aff_iptype.html @@ -30,6 +30,7 @@ with this program; if not, write to the Free Software Foundation, Inc., Nécessite l'autorisation infra Début Fin + Préfixe v6 Sur vlan @@ -42,6 +43,7 @@ with this program; if not, write to the Free Software Foundation, Inc., {{ type.need_infra }} {{ type.domaine_ip_start }} {{ type.domaine_ip_stop }} + {{ type.prefix_v6 }} {{ type.vlan }} {% if is_infra %} diff --git a/machines/templates/machines/aff_machines.html b/machines/templates/machines/aff_machines.html index a09d8868..c22de7c9 100644 --- a/machines/templates/machines/aff_machines.html +++ b/machines/templates/machines/aff_machines.html @@ -34,7 +34,7 @@ with this program; if not, write to the Free Software Foundation, Inc., Nom dns Type Mac - Ipv4 + IP @@ -74,7 +74,11 @@ with this program; if not, write to the Free Software Foundation, Inc., {{ interface.type }} {{ interface.mac_address }} - {{ interface.ipv4 }} + {{ interface.ipv4 }} + {% if ipv6_enabled %} + {{ interface.ipv6 }} + {% endif %} +