From 6d55f54afd56f57af4660e8d1b44d5f3fe9a3fab Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Mon, 20 Nov 2023 17:07:48 -0500 Subject: [PATCH] Refactor crypto models (WIP) --- netbox/netbox/navigation/menu.py | 4 + netbox/templates/vpn/ikepolicy.html | 45 ++++ netbox/templates/vpn/ikeproposal.html | 57 ++++++ netbox/templates/vpn/ipsecpolicy.html | 41 ++++ netbox/templates/vpn/ipsecproposal.html | 53 +++++ netbox/vpn/api/nested_serializers.py | 44 ++++ netbox/vpn/api/serializers.py | 120 ++++++++++- netbox/vpn/api/urls.py | 4 + netbox/vpn/api/views.py | 28 +++ netbox/vpn/choices.py | 50 +++-- netbox/vpn/filtersets.py | 143 +++++++++++-- netbox/vpn/forms/bulk_edit.py | 180 ++++++++++------ netbox/vpn/forms/bulk_import.py | 137 +++++++++---- netbox/vpn/forms/filtersets.py | 164 +++++++++------ netbox/vpn/forms/model_forms.py | 97 +++++++-- netbox/vpn/migrations/0001_initial.py | 98 --------- netbox/vpn/models/crypto.py | 260 ++++++++++++++++++------ netbox/vpn/models/tunnels.py | 4 - netbox/vpn/tables.py | 170 +++++++++++++--- netbox/vpn/urls.py | 32 +++ netbox/vpn/views.py | 180 ++++++++++++++++ 21 files changed, 1512 insertions(+), 399 deletions(-) create mode 100644 netbox/templates/vpn/ikepolicy.html create mode 100644 netbox/templates/vpn/ikeproposal.html create mode 100644 netbox/templates/vpn/ipsecpolicy.html create mode 100644 netbox/templates/vpn/ipsecproposal.html delete mode 100644 netbox/vpn/migrations/0001_initial.py diff --git a/netbox/netbox/navigation/menu.py b/netbox/netbox/navigation/menu.py index 904106ecf..6a9f2fa08 100644 --- a/netbox/netbox/navigation/menu.py +++ b/netbox/netbox/navigation/menu.py @@ -216,6 +216,10 @@ VPN_MENU = Menu( MenuGroup( label=_('Security'), items=( + get_model_item('vpn', 'ikeproposal', _('IKE Proposals')), + get_model_item('vpn', 'ikepolicy', _('IKE Policies')), + get_model_item('vpn', 'ipsecproposal', _('IPSec Proposals')), + get_model_item('vpn', 'ipsecpolicy', _('IPSec Policies')), get_model_item('vpn', 'ipsecprofile', _('IPSec Profiles')), ), ), diff --git a/netbox/templates/vpn/ikepolicy.html b/netbox/templates/vpn/ikepolicy.html new file mode 100644 index 000000000..1bf49818b --- /dev/null +++ b/netbox/templates/vpn/ikepolicy.html @@ -0,0 +1,45 @@ +{% extends 'generic/object.html' %} +{% load helpers %} +{% load plugins %} +{% load i18n %} + +{% block content %} +
+
+
+
{% trans "IKE Policy" %}
+
+ + + + + + + + + + + + + + + + + +
{% trans "Name" %}{{ object.name }}
{% trans "IKE Version" %}{{ object.get_version_display }}
{% trans "Mode" %}{{ object.get_mode_display }}
{% trans "Description" %}{{ object.description|placeholder }}
+
+
+ {% plugin_left_page object %} +
+
+ {% include 'inc/panels/custom_fields.html' %} + {% include 'inc/panels/tags.html' %} + {% plugin_right_page object %} +
+
+
+
+ {% plugin_full_width_page object %} +
+
+{% endblock %} diff --git a/netbox/templates/vpn/ikeproposal.html b/netbox/templates/vpn/ikeproposal.html new file mode 100644 index 000000000..6079f0771 --- /dev/null +++ b/netbox/templates/vpn/ikeproposal.html @@ -0,0 +1,57 @@ +{% extends 'generic/object.html' %} +{% load helpers %} +{% load plugins %} +{% load i18n %} + +{% block content %} +
+
+
+
{% trans "IKE Proposal" %}
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
{% trans "Name" %}{{ object.name }}
{% trans "Authentication method" %}{{ object.get_authentication_method_display }}
{% trans "Encryption algorithm" %}{{ object.get_encryption_algorithm_display }}
{% trans "Authentication algorithm" %}{{ object.get_authentication_algorithm_display }}
{% trans "DH group" %}{{ object.get_group_display }}
{% trans "SA lifetime (seconds)" %}{{ object.sa_lifetime|placeholder }}
{% trans "Description" %}{{ object.description|placeholder }}
+
+
+ {% plugin_left_page object %} +
+
+ {% include 'inc/panels/custom_fields.html' %} + {% include 'inc/panels/tags.html' %} + {% plugin_right_page object %} +
+
+
+
+ {% plugin_full_width_page object %} +
+
+{% endblock %} diff --git a/netbox/templates/vpn/ipsecpolicy.html b/netbox/templates/vpn/ipsecpolicy.html new file mode 100644 index 000000000..8b8343876 --- /dev/null +++ b/netbox/templates/vpn/ipsecpolicy.html @@ -0,0 +1,41 @@ +{% extends 'generic/object.html' %} +{% load helpers %} +{% load plugins %} +{% load i18n %} + +{% block content %} +
+
+
+
{% trans "IPSec Policy" %}
+
+ + + + + + + + + + + + + +
{% trans "Name" %}{{ object.name }}
{% trans "PFS group" %}{{ object.get_pfs_group_display|placeholder }}
{% trans "Description" %}{{ object.description|placeholder }}
+
+
+ {% plugin_left_page object %} +
+
+ {% include 'inc/panels/custom_fields.html' %} + {% include 'inc/panels/tags.html' %} + {% plugin_right_page object %} +
+
+
+
+ {% plugin_full_width_page object %} +
+
+{% endblock %} diff --git a/netbox/templates/vpn/ipsecproposal.html b/netbox/templates/vpn/ipsecproposal.html new file mode 100644 index 000000000..ad375f4e3 --- /dev/null +++ b/netbox/templates/vpn/ipsecproposal.html @@ -0,0 +1,53 @@ +{% extends 'generic/object.html' %} +{% load helpers %} +{% load plugins %} +{% load i18n %} + +{% block content %} +
+
+
+
{% trans "IPSec Proposal" %}
+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
{% trans "Name" %}{{ object.name }}
{% trans "Encryption algorithm" %}{{ object.get_encryption_algorithm_display }}
{% trans "Authentication algorithm" %}{{ object.get_authentication_algorithm_display }}
{% trans "SA lifetime (seconds)" %}{{ object.sa_lifetime_seconds|placeholder }}
{% trans "SA lifetime (KB)" %}{{ object.sa_lifetime_data|placeholder }}
{% trans "Description" %}{{ object.description|placeholder }}
+
+
+ {% plugin_left_page object %} +
+
+ {% include 'inc/panels/custom_fields.html' %} + {% include 'inc/panels/tags.html' %} + {% plugin_right_page object %} +
+
+
+
+ {% plugin_full_width_page object %} +
+
+{% endblock %} diff --git a/netbox/vpn/api/nested_serializers.py b/netbox/vpn/api/nested_serializers.py index 7ab1654b9..9e2dee8ba 100644 --- a/netbox/vpn/api/nested_serializers.py +++ b/netbox/vpn/api/nested_serializers.py @@ -4,7 +4,11 @@ from netbox.api.serializers import WritableNestedSerializer from vpn import models __all__ = ( + 'NestedIKEPolicySerializer', + 'NestedIKEProposalSerializer', + 'NestedIPSecPolicySerializer', 'NestedIPSecProfileSerializer', + 'NestedIPSecProposalSerializer', 'NestedTunnelSerializer', 'NestedTunnelTerminationSerializer', ) @@ -30,6 +34,46 @@ class NestedTunnelTerminationSerializer(WritableNestedSerializer): fields = ('id', 'url', 'display') +class NestedIKEProposalSerializer(WritableNestedSerializer): + url = serializers.HyperlinkedIdentityField( + view_name='vpn-api:ikeproposal-detail' + ) + + class Meta: + model = models.IKEProposal + fields = ('id', 'url', 'display', 'name') + + +class NestedIKEPolicySerializer(WritableNestedSerializer): + url = serializers.HyperlinkedIdentityField( + view_name='vpn-api:ikepolicy-detail' + ) + + class Meta: + model = models.IKEProposal + fields = ('id', 'url', 'display', 'name') + + +class NestedIPSecProposalSerializer(WritableNestedSerializer): + url = serializers.HyperlinkedIdentityField( + view_name='vpn-api:ipsecproposal-detail' + ) + + class Meta: + model = models.IPSecProposal + fields = ('id', 'url', 'display', 'name') + + +class NestedIPSecPolicySerializer(WritableNestedSerializer): + url = serializers.HyperlinkedIdentityField( + view_name='vpn-api:ipsecpolicy-detail' + ) + + class Meta: + model = models.IPSecProposal + fields = ('id', 'url', 'display', 'name') + + class NestedIPSecProfileSerializer(WritableNestedSerializer): url = serializers.HyperlinkedIdentityField( view_name='vpn-api:ipsecprofile-detail' diff --git a/netbox/vpn/api/serializers.py b/netbox/vpn/api/serializers.py index a65305dfe..0ebe28dba 100644 --- a/netbox/vpn/api/serializers.py +++ b/netbox/vpn/api/serializers.py @@ -3,7 +3,7 @@ from drf_spectacular.utils import extend_schema_field from rest_framework import serializers from ipam.api.nested_serializers import NestedIPAddressSerializer -from netbox.api.fields import ChoiceField, ContentTypeField +from netbox.api.fields import ChoiceField, ContentTypeField, SerializedPKRelatedField from netbox.api.serializers import NetBoxModelSerializer from netbox.constants import NESTED_SERIALIZER_PREFIX from tenancy.api.nested_serializers import NestedTenantSerializer @@ -13,7 +13,11 @@ from vpn.models import * from .nested_serializers import * __all__ = ( + 'IKEPolicySerializer', + 'IKEProposalSerializer', + 'IPSecPolicySerializer', 'IPSecProfileSerializer', + 'IPSecProposalSerializer', 'TunnelSerializer', 'TunnelTerminationSerializer', ) @@ -41,8 +45,8 @@ class TunnelSerializer(NetBoxModelSerializer): class Meta: model = Tunnel fields = ( - 'id', 'url', 'display', 'name', 'status', 'encapsulation', 'ipsec_profile', 'tenant', 'preshared_key', - 'tunnel_id', 'comments', 'tags', 'custom_fields', 'created', 'last_updated', + 'id', 'url', 'display', 'name', 'status', 'encapsulation', 'ipsec_profile', 'tenant', 'tunnel_id', + 'comments', 'tags', 'custom_fields', 'created', 'last_updated', ) @@ -79,30 +83,130 @@ class TunnelTerminationSerializer(NetBoxModelSerializer): return serializer(obj.interface, context=context).data +class IKEProposalSerializer(NetBoxModelSerializer): + url = serializers.HyperlinkedIdentityField( + view_name='vpn-api:ikeproposal-detail' + ) + authentication_method = ChoiceField( + choices=AuthenticationMethodChoices + ) + encryption_algorithm = ChoiceField( + choices=EncryptionAlgorithmChoices + ) + authentication_algorithm = ChoiceField( + choices=AuthenticationAlgorithmChoices + ) + group = ChoiceField( + choices=DHGroupChoices + ) + + class Meta: + model = IKEProposal + fields = ( + 'id', 'url', 'display', 'name', 'description', 'authentication_method', 'encryption_algorithm', + 'authentication_algorithm', 'group', 'sa_lifetime', 'tags', 'custom_fields', 'created', 'last_updated', + ) + + +class IKEPolicySerializer(NetBoxModelSerializer): + url = serializers.HyperlinkedIdentityField( + view_name='vpn-api:ikepolicy-detail' + ) + version = ChoiceField( + choices=IKEVersionChoices + ) + mode = ChoiceField( + choices=IKEModeChoices + ) + authentication_algorithm = ChoiceField( + choices=AuthenticationAlgorithmChoices + ) + group = ChoiceField( + choices=DHGroupChoices + ) + proposals = SerializedPKRelatedField( + queryset=IKEProposal.objects.all(), + serializer=NestedIKEProposalSerializer, + required=False, + many=True + ) + + class Meta: + model = IKEPolicy + fields = ( + 'id', 'url', 'display', 'name', 'description', 'version', 'mode', 'proposals', 'preshared_key', + 'certificate', 'tags', 'custom_fields', 'created', 'last_updated', + ) + + +class IPSecProposalSerializer(NetBoxModelSerializer): + url = serializers.HyperlinkedIdentityField( + view_name='vpn-api:ipsecproposal-detail' + ) + encryption_algorithm = ChoiceField( + choices=EncryptionAlgorithmChoices + ) + authentication_algorithm = ChoiceField( + choices=AuthenticationAlgorithmChoices + ) + group = ChoiceField( + choices=DHGroupChoices + ) + + class Meta: + model = IPSecProposal + fields = ( + 'id', 'url', 'display', 'name', 'description', 'encryption_algorithm', 'authentication_algorithm', + 'sa_lifetime_data', 'sa_lifetime_seconds', 'tags', 'custom_fields', 'created', 'last_updated', + ) + + +class IPSecPolicySerializer(NetBoxModelSerializer): + url = serializers.HyperlinkedIdentityField( + view_name='vpn-api:ipsecpolicy-detail' + ) + proposals = SerializedPKRelatedField( + queryset=IPSecProposal.objects.all(), + serializer=NestedIPSecProposalSerializer, + required=False, + many=True + ) + pfs_group = ChoiceField( + choices=DHGroupChoices + ) + + class Meta: + model = IPSecPolicy + fields = ( + 'id', 'url', 'display', 'name', 'description', 'proposals', 'pfs_group', 'tags', 'custom_fields', 'created', + 'last_updated', + ) + + class IPSecProfileSerializer(NetBoxModelSerializer): url = serializers.HyperlinkedIdentityField( view_name='vpn-api:ipsecprofile-detail' ) protocol = ChoiceField( - choices=IPSecProtocolChoices + choices=IPSecModeChoices ) ike_version = ChoiceField( choices=IKEVersionChoices ) phase1_encryption = ChoiceField( - choices=EncryptionChoices + choices=EncryptionAlgorithmChoices ) phase1_authentication = ChoiceField( - choices=AuthenticationChoices + choices=AuthenticationAlgorithmChoices ) phase1_group = ChoiceField( choices=DHGroupChoices ) phase2_encryption = ChoiceField( - choices=EncryptionChoices + choices=EncryptionAlgorithmChoices ) phase2_authentication = ChoiceField( - choices=AuthenticationChoices + choices=AuthenticationAlgorithmChoices ) phase2_group = ChoiceField( choices=DHGroupChoices diff --git a/netbox/vpn/api/urls.py b/netbox/vpn/api/urls.py index 084514e35..f646174d5 100644 --- a/netbox/vpn/api/urls.py +++ b/netbox/vpn/api/urls.py @@ -3,6 +3,10 @@ from . import views router = NetBoxRouter() router.APIRootView = views.VPNRootView +router.register('ike-policies', views.IKEPolicyViewSet) +router.register('ike-proposals', views.IKEProposalViewSet) +router.register('ipsec-policies', views.IPSecPolicyViewSet) +router.register('ipsec-proposals', views.IPSecProposalViewSet) router.register('ipsec-profiles', views.IPSecProfileViewSet) router.register('tunnels', views.TunnelViewSet) router.register('tunnel-terminations', views.TunnelTerminationViewSet) diff --git a/netbox/vpn/api/views.py b/netbox/vpn/api/views.py index 7d01c48cf..960bcb1b4 100644 --- a/netbox/vpn/api/views.py +++ b/netbox/vpn/api/views.py @@ -7,7 +7,11 @@ from vpn.models import * from . import serializers __all__ = ( + 'IKEPolicyViewSet', + 'IKEProposalViewSet', + 'IPSecPolicyViewSet', 'IPSecProfileViewSet', + 'IPSecProposalViewSet', 'TunnelTerminationViewSet', 'TunnelViewSet', 'VPNRootView', @@ -40,6 +44,30 @@ class TunnelTerminationViewSet(NetBoxModelViewSet): filterset_class = filtersets.TunnelTerminationFilterSet +class IKEProposalViewSet(NetBoxModelViewSet): + queryset = IKEProposal.objects.all() + serializer_class = serializers.IKEProposalSerializer + filterset_class = filtersets.IKEProposalFilterSet + + +class IKEPolicyViewSet(NetBoxModelViewSet): + queryset = IKEPolicy.objects.all() + serializer_class = serializers.IKEPolicySerializer + filterset_class = filtersets.IKEPolicyFilterSet + + +class IPSecProposalViewSet(NetBoxModelViewSet): + queryset = IPSecProposal.objects.all() + serializer_class = serializers.IPSecProposalSerializer + filterset_class = filtersets.IPSecProposalFilterSet + + +class IPSecPolicyViewSet(NetBoxModelViewSet): + queryset = IKEPolicy.objects.all() + serializer_class = serializers.IPSecPolicySerializer + filterset_class = filtersets.IPSecPolicyFilterSet + + class IPSecProfileViewSet(NetBoxModelViewSet): queryset = IPSecProfile.objects.all() serializer_class = serializers.IPSecProfileSerializer diff --git a/netbox/vpn/choices.py b/netbox/vpn/choices.py index 211dcb372..a932c5055 100644 --- a/netbox/vpn/choices.py +++ b/netbox/vpn/choices.py @@ -59,19 +59,9 @@ class TunnelTerminationRoleChoices(ChoiceSet): # -# IKE +# Crypto # -class IPSecProtocolChoices(ChoiceSet): - PROTOCOL_ESP = 'esp' - PROTOCOL_AH = 'ah' - - CHOICES = ( - (PROTOCOL_ESP, 'ESP'), - (PROTOCOL_AH, 'AH'), - ) - - class IKEVersionChoices(ChoiceSet): VERSION_1 = 1 VERSION_2 = 2 @@ -82,7 +72,41 @@ class IKEVersionChoices(ChoiceSet): ) -class EncryptionChoices(ChoiceSet): +class IKEModeChoices(ChoiceSet): + AGGRESSIVE = 'aggressive' + MAIN = 'main' + + CHOICES = ( + (AGGRESSIVE, _('Aggressive')), + (MAIN, _('Main')), + ) + + +class AuthenticationMethodChoices(ChoiceSet): + PRESHARED_KEYS = 'preshared-keys' + CERTIFICATES = 'certificates' + RSA_SIGNATURES = 'rsa-signatures' + DSA_SIGNATURES = 'dsa-signatures' + + CHOICES = ( + (PRESHARED_KEYS, _('Pre-shared keys')), + (CERTIFICATES, _('Certificates')), + (RSA_SIGNATURES, _('RSA signatures')), + (DSA_SIGNATURES, _('DSA signatures')), + ) + + +class IPSecModeChoices(ChoiceSet): + ESP = 'esp' + AH = 'ah' + + CHOICES = ( + (ESP, 'ESP'), + (AH, 'AH'), + ) + + +class EncryptionAlgorithmChoices(ChoiceSet): ENCRYPTION_AES128_CBC = 'aes-128-cbc' ENCRYPTION_AES128_GCM = 'aes-128-gcm' ENCRYPTION_AES192_CBC = 'aes-192-cbc' @@ -104,7 +128,7 @@ class EncryptionChoices(ChoiceSet): ) -class AuthenticationChoices(ChoiceSet): +class AuthenticationAlgorithmChoices(ChoiceSet): AUTH_HMAC_SHA1 = 'hmac-sha1' AUTH_HMAC_SHA256 = 'hmac-sha256' AUTH_HMAC_SHA384 = 'hmac-sha384' diff --git a/netbox/vpn/filtersets.py b/netbox/vpn/filtersets.py index 99dcde379..a417f4e32 100644 --- a/netbox/vpn/filtersets.py +++ b/netbox/vpn/filtersets.py @@ -6,12 +6,17 @@ from dcim.models import Interface from ipam.models import IPAddress from netbox.filtersets import NetBoxModelFilterSet from tenancy.filtersets import TenancyFilterSet +from utilities.filters import ContentTypeFilter, MultiValueNumberFilter from virtualization.models import VMInterface from .choices import * from .models import * __all__ = ( + 'IKEPolicyFilterSet', + 'IKEProposalFilterSet', + 'IPSecPolicyFilterSet', 'IPSecProfileFilterSet', + 'IPSecProposalFilterSet', 'TunnelFilterSet', 'TunnelTerminationFilterSet', ) @@ -37,7 +42,7 @@ class TunnelFilterSet(NetBoxModelFilterSet, TenancyFilterSet): class Meta: model = Tunnel - fields = ['id', 'name', 'preshared_key', 'tunnel_id'] + fields = ['id', 'name', 'tunnel_id'] def search(self, queryset, name, value): if not value.strip(): @@ -97,35 +102,129 @@ class TunnelTerminationFilterSet(NetBoxModelFilterSet): fields = ['id'] -class IPSecProfileFilterSet(NetBoxModelFilterSet): - protocol = django_filters.MultipleChoiceFilter( - choices=IPSecProtocolChoices +class IKEProposalFilterSet(NetBoxModelFilterSet): + authentication_method = django_filters.MultipleChoiceFilter( + choices=AuthenticationMethodChoices ) - ike_version = django_filters.MultipleChoiceFilter( - choices=IKEVersionChoices + encryption_algorithm = django_filters.MultipleChoiceFilter( + choices=EncryptionAlgorithmChoices ) - phase1_encryption = django_filters.MultipleChoiceFilter( - choices=EncryptionChoices + authentication_algorithm = django_filters.MultipleChoiceFilter( + choices=AuthenticationAlgorithmChoices ) - phase1_authentication = django_filters.MultipleChoiceFilter( - choices=AuthenticationChoices - ) - phase1_group = django_filters.MultipleChoiceFilter( - choices=DHGroupChoices - ) - phase2_encryption = django_filters.MultipleChoiceFilter( - choices=EncryptionChoices - ) - phase2_authentication = django_filters.MultipleChoiceFilter( - choices=AuthenticationChoices - ) - phase2_group = django_filters.MultipleChoiceFilter( + group = django_filters.MultipleChoiceFilter( choices=DHGroupChoices ) + class Meta: + model = IKEProposal + fields = ['id', 'name', 'sa_lifetime'] + + def search(self, queryset, name, value): + if not value.strip(): + return queryset + return queryset.filter( + Q(name__icontains=value) | + Q(description__icontains=value) + ) + + +class IKEPolicyFilterSet(NetBoxModelFilterSet): + version = django_filters.MultipleChoiceFilter( + choices=IKEVersionChoices + ) + mode = django_filters.MultipleChoiceFilter( + choices=IKEModeChoices + ) + proposal_id = MultiValueNumberFilter( + field_name='proposals__id' + ) + proposals = ContentTypeFilter() + + class Meta: + model = IKEPolicy + fields = ['id', 'name'] + + def search(self, queryset, name, value): + if not value.strip(): + return queryset + return queryset.filter( + Q(name__icontains=value) | + Q(description__icontains=value) + ) + + +class IPSecProposalFilterSet(NetBoxModelFilterSet): + encryption_algorithm = django_filters.MultipleChoiceFilter( + choices=EncryptionAlgorithmChoices + ) + authentication_algorithm = django_filters.MultipleChoiceFilter( + choices=AuthenticationAlgorithmChoices + ) + + class Meta: + model = IPSecProposal + fields = ['id', 'name', 'sa_lifetime_seconds', 'sa_lifetime_data'] + + def search(self, queryset, name, value): + if not value.strip(): + return queryset + return queryset.filter( + Q(name__icontains=value) | + Q(description__icontains=value) + ) + + +class IPSecPolicyFilterSet(NetBoxModelFilterSet): + pfs_group = django_filters.MultipleChoiceFilter( + choices=DHGroupChoices + ) + proposal_id = MultiValueNumberFilter( + field_name='proposals__id' + ) + proposals = ContentTypeFilter() + + class Meta: + model = IPSecPolicy + fields = ['id', 'name'] + + def search(self, queryset, name, value): + if not value.strip(): + return queryset + return queryset.filter( + Q(name__icontains=value) | + Q(description__icontains=value) + ) + + +class IPSecProfileFilterSet(NetBoxModelFilterSet): + mode = django_filters.MultipleChoiceFilter( + choices=IPSecModeChoices + ) + ike_policy_id = django_filters.ModelMultipleChoiceFilter( + queryset=IKEPolicy.objects.all(), + label=_('IKE policy (ID)'), + ) + ike_policy = django_filters.ModelMultipleChoiceFilter( + field_name='ike_policy__name', + queryset=IKEPolicy.objects.all(), + to_field_name='name', + label=_('IKE policy (name)'), + ) + ipsec_policy_id = django_filters.ModelMultipleChoiceFilter( + queryset=IPSecPolicy.objects.all(), + label=_('IPSec policy (ID)'), + ) + ipsec_policy = django_filters.ModelMultipleChoiceFilter( + field_name='ipsec_policy__name', + queryset=IPSecPolicy.objects.all(), + to_field_name='name', + label=_('IPSec policy (name)'), + ) + class Meta: model = IPSecProfile - fields = ['id', 'name', 'phase1_sa_lifetime', 'phase2_sa_lifetime', 'phase2_sa_lifetime_data'] + fields = ['id', 'name'] def search(self, queryset, name, value): if not value.strip(): diff --git a/netbox/vpn/forms/bulk_edit.py b/netbox/vpn/forms/bulk_edit.py index 6969235f7..d645ca02b 100644 --- a/netbox/vpn/forms/bulk_edit.py +++ b/netbox/vpn/forms/bulk_edit.py @@ -9,7 +9,11 @@ from vpn.choices import * from vpn.models import * __all__ = ( + 'IKEPolicyBulkEditForm', + 'IKEProposalBulkEditForm', + 'IPSecPolicyBulkEditForm', 'IPSecProfileBulkEditForm', + 'IPSecProposalBulkEditForm', 'TunnelBulkEditForm', 'TunnelTerminationBulkEditForm', ) @@ -31,10 +35,6 @@ class TunnelBulkEditForm(NetBoxModelBulkEditForm): label=_('IPSec profile'), required=False ) - preshared_key = forms.CharField( - label=_('Pre-shared key'), - required=False - ) tenant = DynamicModelChoiceField( label=_('Tenant'), queryset=Tenant.objects.all(), @@ -54,11 +54,11 @@ class TunnelBulkEditForm(NetBoxModelBulkEditForm): model = Tunnel fieldsets = ( (_('Tunnel'), ('status', 'encapsulation', 'tunnel_id', 'description')), - (_('Security'), ('ipsec_profile', 'preshared_key')), + (_('Security'), ('ipsec_profile',)), (_('Tenancy'), ('tenant',)), ) nullable_fields = ( - 'ipsec_profile', 'preshared_key', 'tunnel_id', 'tenant', 'description', 'comments', + 'ipsec_profile', 'tunnel_id', 'tenant', 'description', 'comments', ) @@ -75,61 +75,130 @@ class TunnelTerminationBulkEditForm(NetBoxModelBulkEditForm): ) -class IPSecProfileBulkEditForm(NetBoxModelBulkEditForm): - protocol = forms.ChoiceField( - label=_('Protocol'), - choices=add_blank_choice(IPSecProtocolChoices), +class IKEProposalBulkEditForm(NetBoxModelBulkEditForm): + authentication_method = forms.ChoiceField( + label=_('Authentication method'), + choices=add_blank_choice(AuthenticationMethodChoices), required=False ) - ike_version = forms.ChoiceField( - label=_('IKE version'), + encryption_algorithm = forms.ChoiceField( + label=_('Encryption algorithm'), + choices=add_blank_choice(EncryptionAlgorithmChoices), + required=False + ) + authentication_algorithm = forms.ChoiceField( + label=_('Authentication algorithm'), + choices=add_blank_choice(AuthenticationAlgorithmChoices), + required=False + ) + group = forms.ChoiceField( + label=_('Group'), + choices=add_blank_choice(DHGroupChoices), + required=False + ) + sa_lifetime = forms.IntegerField( + required=False + ) + + model = IKEProposal + fieldsets = ( + (None, ('name', 'description')), + (_('Parameters'), ( + 'authentication_method', 'encryption_algorithm', 'authentication_algorithm', 'group', 'sa_lifetime', + )), + ) + nullable_fields = ( + 'description', 'sa_lifetime', 'comments', + ) + + +class IKEPolicyBulkEditForm(NetBoxModelBulkEditForm): + version = forms.ChoiceField( + label=_('Version'), choices=add_blank_choice(IKEVersionChoices), required=False ) + mode = forms.ChoiceField( + label=_('Mode'), + choices=add_blank_choice(IKEModeChoices), + required=False + ) + preshared_key = forms.CharField( + label=_('Pre-shared key'), + required=False + ) + certificate = forms.CharField( + label=_('Certificate'), + required=False + ) + + model = IKEPolicy + fieldsets = ( + (None, ('name', 'description')), + (_('Parameters'), ( + 'version', 'mode', 'preshared_key', 'certificate', + )), + ) + nullable_fields = ( + 'description', 'preshared_key', 'certificate', 'comments', + ) + + +class IPSecProposalBulkEditForm(NetBoxModelBulkEditForm): + encryption_algorithm = forms.ChoiceField( + label=_('Encryption algorithm'), + choices=add_blank_choice(EncryptionAlgorithmChoices), + required=False + ) + authentication_algorithm = forms.ChoiceField( + label=_('Authentication algorithm'), + choices=add_blank_choice(AuthenticationAlgorithmChoices), + required=False + ) + sa_lifetime_seconds = forms.IntegerField( + required=False + ) + sa_lifetime_data = forms.IntegerField( + required=False + ) + + model = IPSecProposal + fieldsets = ( + (None, ('name', 'description')), + (_('Parameters'), ( + 'encryption_algorithm', 'authentication_algorithm', 'sa_lifetime_seconds', 'sa_lifetime_data', + )), + ) + nullable_fields = ( + 'description', 'sa_lifetime_seconds', 'sa_lifetime_data', 'comments', + ) + + +class IPSecPolicyBulkEditForm(NetBoxModelBulkEditForm): + pfs_group = forms.ChoiceField( + label=_('PFS group'), + choices=add_blank_choice(DHGroupChoices), + required=False + ) + + model = IPSecPolicy + fieldsets = ( + (None, ('name', 'description')), + (_('Parameters'), ( + 'pfs_group', + )), + ) + nullable_fields = ( + 'description', 'pfs_group', 'comments', + ) + + +class IPSecProfileBulkEditForm(NetBoxModelBulkEditForm): description = forms.CharField( label=_('Description'), max_length=200, required=False ) - phase1_encryption = forms.ChoiceField( - label=_('Encryption'), - choices=add_blank_choice(EncryptionChoices), - required=False - ) - phase1_authentication = forms.ChoiceField( - label=_('Authentication'), - choices=add_blank_choice(AuthenticationChoices), - required=False - ) - phase1_group = forms.ChoiceField( - label=_('Group'), - choices=add_blank_choice(DHGroupChoices), - required=False - ) - phase1_sa_lifetime = forms.IntegerField( - required=False - ) - phase2_encryption = forms.ChoiceField( - label=_('Encryption'), - choices=add_blank_choice(EncryptionChoices), - required=False - ) - phase2_authentication = forms.ChoiceField( - label=_('Authentication'), - choices=add_blank_choice(AuthenticationChoices), - required=False - ) - phase2_group = forms.ChoiceField( - label=_('Group'), - choices=add_blank_choice(DHGroupChoices), - required=False - ) - phase2_sa_lifetime = forms.IntegerField( - required=False - ) - phase2_sa_lifetime_data = forms.IntegerField( - required=False - ) comments = CommentField() model = IPSecProfile @@ -137,14 +206,7 @@ class IPSecProfileBulkEditForm(NetBoxModelBulkEditForm): (_('Profile'), ( 'protocol', 'ike_version', 'description', )), - (_('Phase 1 Parameters'), ( - 'phase1_encryption', 'phase1_authentication', 'phase1_group', 'phase1_sa_lifetime', - )), - (_('Phase 2 Parameters'), ( - 'phase2_encryption', 'phase2_authentication', 'phase2_group', 'phase2_sa_lifetime', - 'phase2_sa_lifetime_data', - )), ) nullable_fields = ( - 'description', 'phase1_sa_lifetime', 'phase2_sa_lifetime', 'phase2_sa_lifetime_data', 'comments', + 'description', 'comments', ) diff --git a/netbox/vpn/forms/bulk_import.py b/netbox/vpn/forms/bulk_import.py index db601b709..1b19af25f 100644 --- a/netbox/vpn/forms/bulk_import.py +++ b/netbox/vpn/forms/bulk_import.py @@ -10,7 +10,11 @@ from vpn.choices import * from vpn.models import * __all__ = ( + 'IKEPolicyImportForm', + 'IKEProposalImportForm', + 'IPSecPolicyImportForm', 'IPSecProfileImportForm', + 'IPSecProposalImportForm', 'TunnelImportForm', 'TunnelTerminationImportForm', ) @@ -43,8 +47,8 @@ class TunnelImportForm(NetBoxModelImportForm): class Meta: model = Tunnel fields = ( - 'name', 'status', 'encapsulation', 'ipsec_profile', 'tenant', 'preshared_key', 'tunnel_id', 'description', - 'comments', 'tags', + 'name', 'status', 'encapsulation', 'ipsec_profile', 'tenant', 'tunnel_id', 'description', 'comments', + 'tags', ) @@ -108,46 +112,109 @@ class TunnelTerminationImportForm(NetBoxModelImportForm): ) +class IKEProposalImportForm(NetBoxModelImportForm): + authentication_method = CSVChoiceField( + label=_('Authentication method'), + choices=AuthenticationMethodChoices + ) + encryption_algorithm = CSVChoiceField( + label=_('Encryption algorithm'), + choices=EncryptionAlgorithmChoices + ) + authentication_algorithmn = CSVChoiceField( + label=_('Authentication algorithm'), + choices=AuthenticationAlgorithmChoices + ) + group = CSVChoiceField( + label=_('Group'), + choices=DHGroupChoices + ) + + class Meta: + model = IKEProposal + fields = ( + 'name', 'description', 'authentication_method', 'encryption_algorithm', 'authentication_algorithmn', + 'group', 'sa_lifetime', 'tags', + ) + + +class IKEPolicyImportForm(NetBoxModelImportForm): + version = CSVChoiceField( + label=_('Version'), + choices=IKEVersionChoices + ) + mode = CSVChoiceField( + label=_('Mode'), + choices=IKEModeChoices + ) + # TODO: M2M field for proposals + + class Meta: + model = IKEPolicy + fields = ( + 'name', 'description', 'version', 'mode', 'proposals', 'preshared_key', 'certificate', 'tags', + ) + + +class IPSecProposalImportForm(NetBoxModelImportForm): + authentication_method = CSVChoiceField( + label=_('Authentication method'), + choices=AuthenticationMethodChoices + ) + encryption_algorithm = CSVChoiceField( + label=_('Encryption algorithm'), + choices=EncryptionAlgorithmChoices + ) + authentication_algorithmn = CSVChoiceField( + label=_('Authentication algorithm'), + choices=AuthenticationAlgorithmChoices + ) + group = CSVChoiceField( + label=_('Group'), + choices=DHGroupChoices + ) + + class Meta: + model = IPSecProposal + fields = ( + 'name', 'description', 'encryption_algorithm', 'authentication_algorithmn', 'sa_lifetime_seconds', + 'sa_lifetime_data', 'tags', + ) + + +class IPSecPolicyImportForm(NetBoxModelImportForm): + pfs_group = CSVChoiceField( + label=_('PFS group'), + choices=DHGroupChoices + ) + # TODO: M2M field for proposals + + class Meta: + model = IPSecPolicy + fields = ( + 'name', 'description', 'proposals', 'pfs_group', 'tags', + ) + + class IPSecProfileImportForm(NetBoxModelImportForm): - protocol = CSVChoiceField( - label=_('Protocol'), - choices=IPSecProtocolChoices, + mode = CSVChoiceField( + label=_('Mode'), + choices=IPSecModeChoices, help_text=_('IPSec protocol') ) - ike_version = CSVChoiceField( - label=_('IKE version'), - choices=IKEVersionChoices, - help_text=_('IKE version') + ike_policy = CSVModelChoiceField( + label=_('IKE policy'), + queryset=IKEPolicy.objects.all(), + to_field_name='name' ) - phase1_encryption = CSVChoiceField( - label=_('Phase 1 Encryption'), - choices=EncryptionChoices - ) - phase1_authentication = CSVChoiceField( - label=_('Phase 1 Authentication'), - choices=AuthenticationChoices - ) - phase1_group = CSVChoiceField( - label=_('Phase 1 Group'), - choices=DHGroupChoices - ) - phase2_encryption = CSVChoiceField( - label=_('Phase 2 Encryption'), - choices=EncryptionChoices - ) - phase2_authentication = CSVChoiceField( - label=_('Phase 2 Authentication'), - choices=AuthenticationChoices - ) - phase2_group = CSVChoiceField( - label=_('Phase 2 Group'), - choices=DHGroupChoices + ipsec_policy = CSVModelChoiceField( + label=_('IPSec policy'), + queryset=IPSecPolicy.objects.all(), + to_field_name='name' ) class Meta: model = IPSecProfile fields = ( - 'name', 'protocol', 'ike_version', 'phase1_encryption', 'phase1_authentication', 'phase1_group', - 'phase1_sa_lifetime', 'phase2_encryption', 'phase2_authentication', 'phase2_group', 'phase2_sa_lifetime', - 'phase2_sa_lifetime_data', 'description', 'comments', 'tags', + 'name', 'ike_policy', 'ipsec_policy', 'description', 'comments', 'tags', ) diff --git a/netbox/vpn/forms/filtersets.py b/netbox/vpn/forms/filtersets.py index 44ad79b7e..98a626857 100644 --- a/netbox/vpn/forms/filtersets.py +++ b/netbox/vpn/forms/filtersets.py @@ -8,7 +8,11 @@ from vpn.choices import * from vpn.models import * __all__ = ( + 'IKEPolicyFilterForm', + 'IKEProposalFilterForm', + 'IPSecPolicyFilterForm', 'IPSecProfileFilterForm', + 'IPSecProposalFilterForm', 'TunnelFilterForm', 'TunnelTerminationFilterForm', ) @@ -19,7 +23,7 @@ class TunnelFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm): fieldsets = ( (None, ('q', 'filter_id', 'tag')), (_('Tunnel'), ('status', 'encapsulation', 'tunnel_id')), - (_('Security'), ('ipsec_profile_id', 'preshared_key')), + (_('Security'), ('ipsec_profile_id',)), (_('Tenancy'), ('tenant_group_id', 'tenant_id')), ) status = forms.MultipleChoiceField( @@ -37,10 +41,6 @@ class TunnelFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm): required=False, label=_('IPSec profile') ) - preshared_key = forms.CharField( - required=False, - label=_('Pre-shared key') - ) tunnel_id = forms.IntegerField( required=False, label=_('Tunnel ID') @@ -67,6 +67,97 @@ class TunnelTerminationFilterForm(NetBoxModelFilterSetForm): tag = TagFilterField(model) +class IKEProposalFilterForm(NetBoxModelFilterSetForm): + model = IKEProposal + fieldsets = ( + (None, ('q', 'filter_id', 'tag')), + (_('Parameters'), ('authentication_method', 'encryption_algorithm', 'authentication_algorithm', 'group')), + ) + authentication_method = forms.MultipleChoiceField( + label=_('Authentication method'), + choices=AuthenticationMethodChoices, + required=False + ) + encryption_algorithm = forms.MultipleChoiceField( + label=_('Encryption algorithm'), + choices=EncryptionAlgorithmChoices, + required=False + ) + authentication_algorithm = forms.MultipleChoiceField( + label=_('Authentication algorithm'), + choices=AuthenticationAlgorithmChoices, + required=False + ) + group = forms.MultipleChoiceField( + label=_('Group'), + choices=DHGroupChoices, + required=False + ) + tag = TagFilterField(model) + + +class IKEPolicyFilterForm(NetBoxModelFilterSetForm): + model = IKEPolicy + fieldsets = ( + (None, ('q', 'filter_id', 'tag')), + (_('Parameters'), ('version', 'mode', 'proposal_id')), + ) + version = forms.MultipleChoiceField( + label=_('IKE version'), + choices=IKEVersionChoices, + required=False + ) + mode = forms.MultipleChoiceField( + label=_('Mode'), + choices=IKEModeChoices, + required=False + ) + proposal_id = DynamicModelMultipleChoiceField( + queryset=IKEProposal.objects.all(), + required=False, + label=_('Proposal') + ) + tag = TagFilterField(model) + + +class IPSecProposalFilterForm(NetBoxModelFilterSetForm): + model = IPSecProposal + fieldsets = ( + (None, ('q', 'filter_id', 'tag')), + (_('Parameters'), ('encryption_algorithm', 'authentication_algorithm')), + ) + encryption_algorithm = forms.MultipleChoiceField( + label=_('Encryption algorithm'), + choices=EncryptionAlgorithmChoices, + required=False + ) + authentication_algorithm = forms.MultipleChoiceField( + label=_('Authentication algorithm'), + choices=AuthenticationAlgorithmChoices, + required=False + ) + tag = TagFilterField(model) + + +class IPSecPolicyFilterForm(NetBoxModelFilterSetForm): + model = IPSecPolicy + fieldsets = ( + (None, ('q', 'filter_id', 'tag')), + (_('Parameters'), ('proposal', 'pfs_group')), + ) + proposal_id = DynamicModelMultipleChoiceField( + queryset=IKEProposal.objects.all(), + required=False, + label=_('Proposal') + ) + pfs_group = forms.MultipleChoiceField( + label=_('Mode'), + choices=DHGroupChoices, + required=False + ) + tag = TagFilterField(model) + + class IPSecProfileFilterForm(NetBoxModelFilterSetForm): model = IPSecProfile fieldsets = ( @@ -80,64 +171,19 @@ class IPSecProfileFilterForm(NetBoxModelFilterSetForm): 'phase2_sa_lifetime_data', )), ) - protocol = forms.MultipleChoiceField( - label=_('Protocol'), - choices=IPSecProtocolChoices, + mode = forms.MultipleChoiceField( + label=_('Mode'), + choices=IPSecModeChoices, required=False ) - ike_version = forms.MultipleChoiceField( - label=_('IKE version'), - choices=IKEVersionChoices, - required=False - ) - ipsec_profile_id = DynamicModelMultipleChoiceField( - queryset=IPSecProfile.objects.all(), + ike_policy_id = DynamicModelMultipleChoiceField( + queryset=IKEPolicy.objects.all(), required=False, - label=_('IPSec profile') + label=_('IKE policy') ) - phase1_encryption = forms.MultipleChoiceField( - label=_('Encryption'), - choices=EncryptionChoices, - required=False - ) - phase1_authentication = forms.MultipleChoiceField( - label=_('Authentication'), - choices=AuthenticationChoices, - required=False - ) - phase1_group = forms.MultipleChoiceField( - label=_('Group'), - choices=DHGroupChoices, - required=False - ) - phase1_sa_lifetime = forms.IntegerField( + ipsec_policy_id = DynamicModelMultipleChoiceField( + queryset=IPSecPolicy.objects.all(), required=False, - min_value=0, - label=_('SA lifetime') - ) - phase2_encryption = forms.MultipleChoiceField( - label=_('Encryption'), - choices=EncryptionChoices, - required=False - ) - phase2_authentication = forms.MultipleChoiceField( - label=_('Authentication'), - choices=AuthenticationChoices, - required=False - ) - phase2_group = forms.MultipleChoiceField( - label=_('Group'), - choices=DHGroupChoices, - required=False - ) - phase2_sa_lifetime = forms.IntegerField( - required=False, - min_value=0, - label=_('SA lifetime') - ) - phase2_sa_lifetime_data = forms.IntegerField( - required=False, - min_value=0, - label=_('SA lifetime (data)') + label=_('IPSec policy') ) tag = TagFilterField(model) diff --git a/netbox/vpn/forms/model_forms.py b/netbox/vpn/forms/model_forms.py index cc8cb4b1f..6f289d26e 100644 --- a/netbox/vpn/forms/model_forms.py +++ b/netbox/vpn/forms/model_forms.py @@ -5,14 +5,18 @@ from dcim.models import Device, Interface from ipam.models import IPAddress from netbox.forms import NetBoxModelForm from tenancy.forms import TenancyForm -from utilities.forms.fields import CommentField, DynamicModelChoiceField +from utilities.forms.fields import CommentField, DynamicModelChoiceField, DynamicModelMultipleChoiceField from utilities.forms.widgets import HTMXSelect from virtualization.models import VirtualMachine, VMInterface from vpn.choices import * from vpn.models import * __all__ = ( + 'IKEPolicyForm', + 'IKEProposalForm', + 'IPSecPolicyForm', 'IPSecProfileForm', + 'IPSecProposalForm', 'TunnelCreateForm', 'TunnelForm', 'TunnelTerminationForm', @@ -30,15 +34,15 @@ class TunnelForm(TenancyForm, NetBoxModelForm): fieldsets = ( (_('Tunnel'), ('name', 'status', 'encapsulation', 'description', 'tunnel_id', 'tags')), - (_('Security'), ('ipsec_profile', 'preshared_key')), + (_('Security'), ('ipsec_profile',)), (_('Tenancy'), ('tenant_group', 'tenant')), ) class Meta: model = Tunnel fields = [ - 'name', 'status', 'encapsulation', 'description', 'tunnel_id', 'ipsec_profile', 'preshared_key', - 'tenant_group', 'tenant', 'comments', 'tags', + 'name', 'status', 'encapsulation', 'description', 'tunnel_id', 'ipsec_profile', 'tenant_group', 'tenant', + 'comments', 'tags', ] @@ -111,7 +115,7 @@ class TunnelCreateForm(TunnelForm): fieldsets = ( (_('Tunnel'), ('name', 'status', 'encapsulation', 'description', 'tunnel_id', 'tags')), - (_('Security'), ('ipsec_profile', 'preshared_key')), + (_('Security'), ('ipsec_profile',)), (_('Tenancy'), ('tenant_group', 'tenant')), (_('First Termination'), ( 'termination1_role', 'termination1_type', 'termination1_parent', 'termination1_interface', @@ -264,26 +268,87 @@ class TunnelTerminationCreateForm(NetBoxModelForm): self.instance.interface = self.cleaned_data['interface'] +class IKEProposalForm(NetBoxModelForm): + + fieldsets = ( + (_('Proposal'), ('name', 'description', 'tags')), + (_('Parameters'), ( + 'authentication_method', 'encryption_algorithm', 'authentication_algorithm', 'group', 'sa_lifetime', + )), + ) + + class Meta: + model = IKEProposal + fields = [ + 'name', 'description', 'authentication_method', 'encryption_algorithm', 'authentication_algorithm', 'group', + 'sa_lifetime', 'tags', + ] + + +class IKEPolicyForm(NetBoxModelForm): + proposals = DynamicModelMultipleChoiceField( + queryset=IKEProposal.objects.all() + ) + + fieldsets = ( + (_('Policy'), ('name', 'description', 'tags')), + (_('Parameters'), ('version', 'mode', 'proposals')), + (_('Authentication'), ('preshared_key', 'certificate')), + ) + + class Meta: + model = IKEPolicy + fields = [ + 'name', 'description', 'version', 'mode', 'proposals', 'preshared_key', 'certificate', 'tags', + ] + + +class IPSecProposalForm(NetBoxModelForm): + + fieldsets = ( + (_('Proposal'), ('name', 'description', 'tags')), + (_('Parameters'), ( + 'encryption_algorithm', 'authentication_algorithm', 'sa_lifetime_seconds', 'sa_lifetime_data', + )), + ) + + class Meta: + model = IPSecProposal + fields = [ + 'name', 'description', 'encryption_algorithm', 'authentication_algorithm', 'sa_lifetime_seconds', + 'sa_lifetime_data', 'tags', + ] + + +class IPSecPolicyForm(NetBoxModelForm): + proposals = DynamicModelMultipleChoiceField( + queryset=IPSecProposal.objects.all() + ) + + fieldsets = ( + (_('Policy'), ('name', 'description', 'tags')), + (_('Parameters'), ('proposals', 'pfs_group')), + ) + + class Meta: + model = IPSecPolicy + fields = [ + 'name', 'description', 'proposals', 'pfs_group', 'tags', + ] + + class IPSecProfileForm(NetBoxModelForm): comments = CommentField() fieldsets = ( (_('Profile'), ( - 'name', 'protocol', 'ike_version', 'description', 'tags', - )), - (_('Phase 1 Parameters'), ( - 'phase1_encryption', 'phase1_authentication', 'phase1_group', 'phase1_sa_lifetime', - )), - (_('Phase 2 Parameters'), ( - 'phase2_encryption', 'phase2_authentication', 'phase2_group', 'phase2_sa_lifetime', - 'phase2_sa_lifetime_data', + 'name', 'mode', 'description', 'tags', )), + (_('Policies'), ('ipsec_policy', 'description', 'tags')), ) class Meta: model = IPSecProfile fields = [ - 'name', 'protocol', 'ike_version', 'phase1_encryption', 'phase1_authentication', 'phase1_group', - 'phase1_sa_lifetime', 'phase2_encryption', 'phase2_authentication', 'phase2_group', 'phase2_sa_lifetime', - 'phase2_sa_lifetime_data', 'description', 'comments', 'tags', + 'name', 'description', 'mode', 'ipsec_policy', 'description', 'comments', 'tags', ] diff --git a/netbox/vpn/migrations/0001_initial.py b/netbox/vpn/migrations/0001_initial.py deleted file mode 100644 index e0753249c..000000000 --- a/netbox/vpn/migrations/0001_initial.py +++ /dev/null @@ -1,98 +0,0 @@ -# Generated by Django 4.2.7 on 2023-11-15 19:50 - -from django.db import migrations, models -import django.db.models.deletion -import taggit.managers -import utilities.json - - -class Migration(migrations.Migration): - - initial = True - - dependencies = [ - ('contenttypes', '0002_remove_content_type_name'), - ('tenancy', '0011_contactassignment_tags'), - ('ipam', '0067_ipaddress_index_host'), - ('extras', '0099_cachedvalue_ordering'), - ] - - operations = [ - migrations.CreateModel( - name='IPSecProfile', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)), - ('created', models.DateTimeField(auto_now_add=True, null=True)), - ('last_updated', models.DateTimeField(auto_now=True, null=True)), - ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=utilities.json.CustomFieldJSONEncoder)), - ('description', models.CharField(blank=True, max_length=200)), - ('comments', models.TextField(blank=True)), - ('name', models.CharField(max_length=100, unique=True)), - ('protocol', models.CharField()), - ('ike_version', models.PositiveSmallIntegerField(default=2)), - ('phase1_encryption', models.CharField()), - ('phase1_authentication', models.CharField()), - ('phase1_group', models.PositiveSmallIntegerField()), - ('phase1_sa_lifetime', models.PositiveIntegerField(blank=True, null=True)), - ('phase2_encryption', models.CharField()), - ('phase2_authentication', models.CharField()), - ('phase2_group', models.PositiveSmallIntegerField()), - ('phase2_sa_lifetime', models.PositiveIntegerField(blank=True, null=True)), - ('phase2_sa_lifetime_data', models.PositiveIntegerField(blank=True, null=True)), - ('tags', taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag')), - ], - options={ - 'verbose_name': 'tunnel', - 'verbose_name_plural': 'tunnels', - 'ordering': ('name',), - }, - ), - migrations.CreateModel( - name='Tunnel', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)), - ('created', models.DateTimeField(auto_now_add=True, null=True)), - ('last_updated', models.DateTimeField(auto_now=True, null=True)), - ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=utilities.json.CustomFieldJSONEncoder)), - ('description', models.CharField(blank=True, max_length=200)), - ('comments', models.TextField(blank=True)), - ('name', models.CharField(max_length=100, unique=True)), - ('status', models.CharField(default='active', max_length=50)), - ('encapsulation', models.CharField(max_length=50)), - ('preshared_key', models.TextField(blank=True)), - ('tunnel_id', models.PositiveBigIntegerField(blank=True, null=True)), - ('ipsec_profile', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='tunnels', to='vpn.ipsecprofile')), - ('tags', taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag')), - ('tenant', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='tunnels', to='tenancy.tenant')), - ], - options={ - 'verbose_name': 'tunnel', - 'verbose_name_plural': 'tunnels', - 'ordering': ('name',), - }, - ), - migrations.CreateModel( - name='TunnelTermination', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)), - ('created', models.DateTimeField(auto_now_add=True, null=True)), - ('last_updated', models.DateTimeField(auto_now=True, null=True)), - ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=utilities.json.CustomFieldJSONEncoder)), - ('role', models.CharField(default='peer', max_length=50)), - ('interface_id', models.PositiveBigIntegerField(blank=True, null=True)), - ('interface_type', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='+', to='contenttypes.contenttype')), - ('outside_ip', models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='tunnel_termination', to='ipam.ipaddress')), - ('tags', taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag')), - ('tunnel', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='terminations', to='vpn.tunnel')), - ], - options={ - 'verbose_name': 'tunnel termination', - 'verbose_name_plural': 'tunnel terminations', - 'ordering': ('tunnel', 'role', 'pk'), - }, - ), - migrations.AddConstraint( - model_name='tunneltermination', - constraint=models.UniqueConstraint(fields=('interface_type', 'interface_id'), name='vpn_tunneltermination_interface', violation_error_message='An interface may be terminated to only one tunnel at a time.'), - ), - ] diff --git a/netbox/vpn/models/crypto.py b/netbox/vpn/models/crypto.py index 059d8f2f4..dacd40f81 100644 --- a/netbox/vpn/models/crypto.py +++ b/netbox/vpn/models/crypto.py @@ -2,88 +2,228 @@ from django.db import models from django.urls import reverse from django.utils.translation import gettext_lazy as _ -from netbox.models import PrimaryModel +from netbox.models import NetBoxModel, PrimaryModel from vpn.choices import * __all__ = ( + 'IKEPolicy', + 'IKEProposal', + 'IPSecPolicy', 'IPSecProfile', + 'IPSecProposal', ) +# +# IKE +# + +class IKEProposal(NetBoxModel): + name = models.CharField( + verbose_name=_('name'), + max_length=100, + unique=True + ) + description = models.CharField( + verbose_name=_('description'), + max_length=200, + blank=True + ) + authentication_method = models.CharField( + verbose_name=('authentication method'), + choices=AuthenticationMethodChoices + ) + encryption_algorithm = models.CharField( + verbose_name=_('encryption algorithm'), + choices=EncryptionAlgorithmChoices + ) + authentication_algorithm = models.CharField( + verbose_name=_('authentication algorithm'), + choices=AuthenticationAlgorithmChoices + ) + group = models.PositiveSmallIntegerField( + verbose_name=_('group'), + choices=DHGroupChoices, + help_text=_('Diffie-Hellman group ID') + ) + sa_lifetime = models.PositiveIntegerField( + verbose_name=_('SA lifetime'), + blank=True, + null=True, + help_text=_('Security association lifetime (in seconds)') + ) + + class Meta: + ordering = ('name',) + verbose_name = _('IKE proposal') + verbose_name_plural = _('IKE proposals') + + def __str__(self): + return self.name + + def get_absolute_url(self): + return reverse('vpn:ikeproposal', args=[self.pk]) + + +class IKEPolicy(NetBoxModel): + name = models.CharField( + verbose_name=_('name'), + max_length=100, + unique=True + ) + description = models.CharField( + verbose_name=_('description'), + max_length=200, + blank=True + ) + version = models.PositiveSmallIntegerField( + verbose_name=_('version'), + choices=IKEVersionChoices, + default=IKEVersionChoices.VERSION_2 + ) + mode = models.CharField( + verbose_name=_('mode'), + choices=IKEModeChoices + ) + proposals = models.ManyToManyField( + to='vpn.IKEProposal', + related_name='ike_policies', + verbose_name=_('proposals') + ) + preshared_key = models.TextField( + verbose_name=_('pre-shared key'), + blank=True + ) + certificate = models.TextField( + verbose_name=_('certificate'), + blank=True + ) + + class Meta: + ordering = ('name',) + verbose_name = _('IKE policy') + verbose_name_plural = _('IKE policies') + + def __str__(self): + return self.name + + def get_absolute_url(self): + return reverse('vpn:ikeprofile', args=[self.pk]) + + +# +# IPSec +# + +class IPSecProposal(NetBoxModel): + name = models.CharField( + verbose_name=_('name'), + max_length=100, + unique=True + ) + description = models.CharField( + verbose_name=_('description'), + max_length=200, + blank=True + ) + encryption_algorithm = models.CharField( + verbose_name=_('encryption'), + choices=EncryptionAlgorithmChoices + ) + authentication_algorithm = models.CharField( + verbose_name=_('authentication'), + choices=AuthenticationAlgorithmChoices + ) + sa_lifetime_seconds = models.PositiveIntegerField( + verbose_name=_('SA lifetime (seconds)'), + blank=True, + null=True, + help_text=_('Security association lifetime (seconds)') + ) + sa_lifetime_data = models.PositiveIntegerField( + verbose_name=_('SA lifetime (KB)'), + blank=True, + null=True, + help_text=_('Security association lifetime (in kilobytes)') + ) + + class Meta: + ordering = ('name',) + verbose_name = _('IPSec proposal') + verbose_name_plural = _('IPSec proposals') + + def __str__(self): + return self.name + + def get_absolute_url(self): + return reverse('vpn:ipsecproposal', args=[self.pk]) + + +class IPSecPolicy(NetBoxModel): + name = models.CharField( + verbose_name=_('name'), + max_length=100, + unique=True + ) + description = models.CharField( + verbose_name=_('description'), + max_length=200, + blank=True + ) + proposals = models.ManyToManyField( + to='vpn.IPSecProposal', + related_name='ipsec_policies', + verbose_name=_('proposals') + ) + pfs_group = models.PositiveSmallIntegerField( + verbose_name=_('PFS group'), + choices=DHGroupChoices, + blank=True, + null=True, + help_text=_('Diffie-Hellman group for Perfect Forward Secrecy') + ) + + class Meta: + ordering = ('name',) + verbose_name = _('IPSec policy') + verbose_name_plural = _('IPSec policies') + + def __str__(self): + return self.name + + def get_absolute_url(self): + return reverse('vpn:ipsecpolicy', args=[self.pk]) + + class IPSecProfile(PrimaryModel): name = models.CharField( verbose_name=_('name'), max_length=100, unique=True ) - protocol = models.CharField( - verbose_name=_('protocol'), - choices=IPSecProtocolChoices + mode = models.CharField( + verbose_name=_('mode'), + choices=IPSecModeChoices ) - ike_version = models.PositiveSmallIntegerField( - verbose_name=_('IKE version'), - choices=IKEVersionChoices, - default=IKEVersionChoices.VERSION_2 + ike_policy = models.ForeignKey( + to='vpn.IKEPolicy', + on_delete=models.PROTECT, + related_name='ipsec_profiles' ) - - # Phase 1 parameters - phase1_encryption = models.CharField( - verbose_name=_('phase 1 encryption'), - choices=EncryptionChoices + ipsec_policy = models.ForeignKey( + to='vpn.IPSecPolicy', + on_delete=models.PROTECT, + related_name='ipsec_profiles' ) - phase1_authentication = models.CharField( - verbose_name=_('phase 1 authentication'), - choices=AuthenticationChoices - ) - phase1_group = models.PositiveSmallIntegerField( - verbose_name=_('phase 1 group'), - choices=DHGroupChoices, - help_text=_('Diffie-Hellman group') - ) - phase1_sa_lifetime = models.PositiveIntegerField( - verbose_name=_('phase 1 SA lifetime'), - blank=True, - null=True, - help_text=_('Security association lifetime (in seconds)') - ) - - # Phase 2 parameters - phase2_encryption = models.CharField( - verbose_name=_('phase 2 encryption'), - choices=EncryptionChoices - ) - phase2_authentication = models.CharField( - verbose_name=_('phase 2 authentication'), - choices=AuthenticationChoices - ) - phase2_group = models.PositiveSmallIntegerField( - verbose_name=_('phase 2 group'), - choices=DHGroupChoices, - help_text=_('Diffie-Hellman group') - ) - phase2_sa_lifetime = models.PositiveIntegerField( - verbose_name=_('phase 2 SA lifetime (seconds)'), - blank=True, - null=True, - help_text=_('Security association lifetime (seconds)') - ) - phase2_sa_lifetime_data = models.PositiveIntegerField( - verbose_name=_('phase 2 SA lifetime (KB)'), - blank=True, - null=True, - help_text=_('Security association lifetime (in kilobytes)') - ) - # TODO: Add PFS group? clone_fields = ( - 'protocol', 'ike_version', 'phase1_encryption', 'phase1_authentication', 'phase1_group', 'phase1_sa_lifetime', - 'phase2_encryption', 'phase2_authentication', 'phase2_group', 'phase2_sa_lifetime', 'phase2_sa_lifetime_data', + 'mode', 'ike_policy', 'ipsec_policy', ) class Meta: ordering = ('name',) - verbose_name = _('tunnel') - verbose_name_plural = _('tunnels') + verbose_name = _('IPSec profile') + verbose_name_plural = _('IPSec profiles') def __str__(self): return self.name diff --git a/netbox/vpn/models/tunnels.py b/netbox/vpn/models/tunnels.py index 4f3bd4110..4eba11ad6 100644 --- a/netbox/vpn/models/tunnels.py +++ b/netbox/vpn/models/tunnels.py @@ -45,10 +45,6 @@ class Tunnel(PrimaryModel): blank=True, null=True ) - preshared_key = models.TextField( - verbose_name=_('pre-shared key'), - blank=True - ) tunnel_id = models.PositiveBigIntegerField( verbose_name=_('tunnel ID'), blank=True, diff --git a/netbox/vpn/tables.py b/netbox/vpn/tables.py index 33eb6da85..3a358a972 100644 --- a/netbox/vpn/tables.py +++ b/netbox/vpn/tables.py @@ -1,5 +1,4 @@ import django_tables2 as tables -from django.contrib.contenttypes.fields import GenericRelation from django.utils.translation import gettext_lazy as _ from django_tables2.utils import Accessor @@ -8,6 +7,10 @@ from netbox.tables import NetBoxTable, columns from vpn.models import * __all__ = ( + 'IKEPolicyTable', + 'IKEProposalTable', + 'IPSecPolicyTable', + 'IPSecProposalTable', 'IPSecProfileTable', 'TunnelTable', 'TunnelTerminationTable', @@ -42,8 +45,8 @@ class TunnelTable(TenancyColumnsMixin, NetBoxTable): class Meta(NetBoxTable.Meta): model = Tunnel fields = ( - 'pk', 'id', 'name', 'status', 'encapsulation', 'ipsec_profile', 'tenant', 'tenant_group', 'preshared_key', - 'tunnel_id', 'termination_count', 'description', 'comments', 'tags', 'created', 'last_updated', + 'pk', 'id', 'name', 'status', 'encapsulation', 'ipsec_profile', 'tenant', 'tenant_group', 'tunnel_id', + 'termination_count', 'description', 'comments', 'tags', 'created', 'last_updated', ) default_columns = ('pk', 'name', 'status', 'encapsulation', 'tenant', 'terminations_count') @@ -89,47 +92,164 @@ class TunnelTerminationTable(TenancyColumnsMixin, NetBoxTable): default_columns = ('pk', 'tunnel', 'role', 'interface_parent', 'interface', 'ip_addresses', 'outside_ip') -class IPSecProfileTable(TenancyColumnsMixin, NetBoxTable): +class IKEProposalTable(NetBoxTable): name = tables.Column( verbose_name=_('Name'), linkify=True ) - protocol = columns.ChoiceFieldColumn( - verbose_name=_('Protocol') + authentication_method = columns.ChoiceFieldColumn( + verbose_name=_('Authentication Method') ) - ike_version = columns.ChoiceFieldColumn( - verbose_name=_('IKE Version') + encryption_algorithm = columns.ChoiceFieldColumn( + verbose_name=_('Encryption Algorithm') ) - phase1_encryption = columns.ChoiceFieldColumn( - verbose_name=_('Phase 1 Encryption') + authentication_algorithm = columns.ChoiceFieldColumn( + verbose_name=_('Authentication Algorithm') ) - phase1_authentication = columns.ChoiceFieldColumn( - verbose_name=_('Phase 1 Authentication') + group = columns.ChoiceFieldColumn( + verbose_name=_('Group') ) - phase1_group = columns.ChoiceFieldColumn( - verbose_name=_('Phase 1 Group') + sa_lifetime = tables.Column( + verbose_name=_('SA Lifetime') ) - phase2_encryption = columns.ChoiceFieldColumn( - verbose_name=_('Phase 2 Encryption') + tags = columns.TagColumn( + url_name='vpn:ikeproposal_list' ) - phase2_authentication = columns.ChoiceFieldColumn( - verbose_name=_('Phase 2 Authentication') + + class Meta(NetBoxTable.Meta): + model = IKEProposal + fields = ( + 'pk', 'id', 'name', 'authentication_method', 'encryption_algorithm', 'authentication_algorithm', + 'group', 'sa_lifetime', 'description', 'tags', 'created', 'last_updated', + ) + default_columns = ( + 'pk', 'name', 'authentication_method', 'encryption_algorithm', 'authentication_algorithm', 'group', + 'sa_lifetime', 'description', + ) + + +class IKEPolicyTable(NetBoxTable): + name = tables.Column( + verbose_name=_('Name'), + linkify=True ) - phase2_group = columns.ChoiceFieldColumn( - verbose_name=_('Phase 2 Group') + version = columns.ChoiceFieldColumn( + verbose_name=_('Version') + ) + mode = columns.ChoiceFieldColumn( + verbose_name=_('Mode') + ) + proposals = tables.ManyToManyColumn( + linkify_item=True, + verbose_name=_('Proposals') + ) + preshared_key = tables.Column( + verbose_name=_('Pre-shared Key') + ) + certificate = tables.Column( + verbose_name=_('Certificate') + ) + tags = columns.TagColumn( + url_name='vpn:ikepolicy_list' + ) + + class Meta(NetBoxTable.Meta): + model = IKEPolicy + fields = ( + 'pk', 'id', 'name', 'version', 'mode', 'proposals', 'preshared_key', 'certificate', 'description', 'tags', + 'created', 'last_updated', + ) + default_columns = ( + 'pk', 'name', 'version', 'mode', 'proposals', 'description', + ) + + +class IPSecProposalTable(NetBoxTable): + name = tables.Column( + verbose_name=_('Name'), + linkify=True + ) + encryption_algorithm = columns.ChoiceFieldColumn( + verbose_name=_('Encryption Algorithm') + ) + authentication_algorithm = columns.ChoiceFieldColumn( + verbose_name=_('Authentication Algorithm') + ) + sa_lifetime_seconds = tables.Column( + verbose_name=_('SA Lifetime (Seconds)') + ) + sa_lifetime_data = tables.Column( + verbose_name=_('SA Lifetime (KB)') + ) + tags = columns.TagColumn( + url_name='vpn:ipsecproposal_list' + ) + + class Meta(NetBoxTable.Meta): + model = IPSecProposal + fields = ( + 'pk', 'id', 'name', 'encryption_algorithm', 'authentication_algorithm', 'sa_lifetime_seconds', + 'sa_lifetime_data', 'description', 'tags', 'created', 'last_updated', + ) + default_columns = ( + 'pk', 'name', 'encryption_algorithm', 'authentication_algorithm', 'sa_lifetime_seconds', + 'sa_lifetime_data', 'description', + ) + + +class IPSecPolicyTable(NetBoxTable): + name = tables.Column( + verbose_name=_('Name'), + linkify=True + ) + proposals = tables.ManyToManyColumn( + linkify_item=True, + verbose_name=_('Proposals') + ) + pfs_group = columns.ChoiceFieldColumn( + verbose_name=_('PFS Group') + ) + tags = columns.TagColumn( + url_name='vpn:ipsecpolicy_list' + ) + + class Meta(NetBoxTable.Meta): + model = IPSecPolicy + fields = ( + 'pk', 'id', 'name', 'proposals', 'pfs_group', 'tags', 'created', 'last_updated', + ) + default_columns = ( + 'pk', 'name', 'proposals', 'pfs_group', 'description', + ) + + +class IPSecProfileTable(NetBoxTable): + name = tables.Column( + verbose_name=_('Name'), + linkify=True + ) + mode = columns.ChoiceFieldColumn( + verbose_name=_('Mode') + ) + ike_policy = tables.Column( + linkify=True, + verbose_name=_('IKE Policy') + ) + ipsec_policy = tables.Column( + linkify=True, + verbose_name=_('IPSec Policy') ) comments = columns.MarkdownColumn( verbose_name=_('Comments'), ) tags = columns.TagColumn( - url_name='vpn:tunnel_list' + url_name='vpn:ipsecprofile_list' ) class Meta(NetBoxTable.Meta): model = IPSecProfile fields = ( - 'pk', 'id', 'name', 'protocol', 'ike_version', 'phase1_encryption', 'phase1_authentication', 'phase1_group', - 'phase1_sa_lifetime', 'phase2_encryption', 'phase2_authentication', 'phase2_group', 'phase2_sa_lifetime', - 'phase2_sa_lifetime_data', 'description', 'comments', 'tags', 'created', 'last_updated', + 'pk', 'id', 'name', 'mode', 'ike_policy', 'ipsec_policy', 'description', 'comments', 'tags', 'created', + 'last_updated', ) - default_columns = ('pk', 'name', 'protocol', 'ike_version', 'description') + default_columns = ('pk', 'name', 'mode', 'ike_policy', 'ipsec_policy', 'description') diff --git a/netbox/vpn/urls.py b/netbox/vpn/urls.py index bfb348a14..7fe548245 100644 --- a/netbox/vpn/urls.py +++ b/netbox/vpn/urls.py @@ -22,6 +22,38 @@ urlpatterns = [ path('tunnel-terminations/delete/', views.TunnelTerminationBulkDeleteView.as_view(), name='tunneltermination_bulk_delete'), path('tunnel-terminations//', include(get_model_urls('vpn', 'tunneltermination'))), + # IKE proposals + path('ike-proposals/', views.IKEProposalListView.as_view(), name='ikeproposal_list'), + path('ike-proposals/add/', views.IKEProposalEditView.as_view(), name='ikeproposal_add'), + path('ike-proposals/import/', views.IKEProposalBulkImportView.as_view(), name='ikeproposal_import'), + path('ike-proposals/edit/', views.IKEProposalBulkEditView.as_view(), name='ikeproposal_bulk_edit'), + path('ike-proposals/delete/', views.IKEProposalBulkDeleteView.as_view(), name='ikeproposal_bulk_delete'), + path('ike-proposals//', include(get_model_urls('vpn', 'ikeproposal'))), + + # IKE policies + path('ike-policys/', views.IKEPolicyListView.as_view(), name='ikepolicy_list'), + path('ike-policys/add/', views.IKEPolicyEditView.as_view(), name='ikepolicy_add'), + path('ike-policys/import/', views.IKEPolicyBulkImportView.as_view(), name='ikepolicy_import'), + path('ike-policys/edit/', views.IKEPolicyBulkEditView.as_view(), name='ikepolicy_bulk_edit'), + path('ike-policys/delete/', views.IKEPolicyBulkDeleteView.as_view(), name='ikepolicy_bulk_delete'), + path('ike-policys//', include(get_model_urls('vpn', 'ikepolicy'))), + + # IPSec proposals + path('ipsec-proposals/', views.IPSecProposalListView.as_view(), name='ipsecproposal_list'), + path('ipsec-proposals/add/', views.IPSecProposalEditView.as_view(), name='ipsecproposal_add'), + path('ipsec-proposals/import/', views.IPSecProposalBulkImportView.as_view(), name='ipsecproposal_import'), + path('ipsec-proposals/edit/', views.IPSecProposalBulkEditView.as_view(), name='ipsecproposal_bulk_edit'), + path('ipsec-proposals/delete/', views.IPSecProposalBulkDeleteView.as_view(), name='ipsecproposal_bulk_delete'), + path('ipsec-proposals//', include(get_model_urls('vpn', 'ipsecproposal'))), + + # IPSec policies + path('ipsec-policys/', views.IPSecPolicyListView.as_view(), name='ipsecpolicy_list'), + path('ipsec-policys/add/', views.IPSecPolicyEditView.as_view(), name='ipsecpolicy_add'), + path('ipsec-policys/import/', views.IPSecPolicyBulkImportView.as_view(), name='ipsecpolicy_import'), + path('ipsec-policys/edit/', views.IPSecPolicyBulkEditView.as_view(), name='ipsecpolicy_bulk_edit'), + path('ipsec-policys/delete/', views.IPSecPolicyBulkDeleteView.as_view(), name='ipsecpolicy_bulk_delete'), + path('ipsec-policys//', include(get_model_urls('vpn', 'ipsecpolicy'))), + # IPSec profiles path('ipsec-profiles/', views.IPSecProfileListView.as_view(), name='ipsecprofile_list'), path('ipsec-profiles/add/', views.IPSecProfileEditView.as_view(), name='ipsecprofile_add'), diff --git a/netbox/vpn/views.py b/netbox/vpn/views.py index aa63d00da..fe6acb46a 100644 --- a/netbox/vpn/views.py +++ b/netbox/vpn/views.py @@ -112,6 +112,186 @@ class TunnelTerminationBulkDeleteView(generic.BulkDeleteView): table = tables.TunnelTerminationTable +# +# IKE proposals +# + +class IKEProposalListView(generic.ObjectListView): + queryset = IKEProposal.objects.all() + filterset = filtersets.IKEProposalFilterSet + filterset_form = forms.IKEProposalFilterForm + table = tables.IKEProposalTable + + +@register_model_view(IKEProposal) +class IKEProposalView(generic.ObjectView): + queryset = IKEProposal.objects.all() + + +@register_model_view(IKEProposal, 'edit') +class IKEProposalEditView(generic.ObjectEditView): + queryset = IKEProposal.objects.all() + form = forms.IKEProposalForm + + +@register_model_view(IKEProposal, 'delete') +class IKEProposalDeleteView(generic.ObjectDeleteView): + queryset = IKEProposal.objects.all() + + +class IKEProposalBulkImportView(generic.BulkImportView): + queryset = IKEProposal.objects.all() + model_form = forms.IKEProposalImportForm + + +class IKEProposalBulkEditView(generic.BulkEditView): + queryset = IKEProposal.objects.all() + filterset = filtersets.IKEProposalFilterSet + table = tables.IKEProposalTable + form = forms.IKEProposalBulkEditForm + + +class IKEProposalBulkDeleteView(generic.BulkDeleteView): + queryset = IKEProposal.objects.all() + filterset = filtersets.IKEProposalFilterSet + table = tables.IKEProposalTable + + +# +# IKE policies +# + +class IKEPolicyListView(generic.ObjectListView): + queryset = IKEPolicy.objects.all() + filterset = filtersets.IKEPolicyFilterSet + filterset_form = forms.IKEPolicyFilterForm + table = tables.IKEPolicyTable + + +@register_model_view(IKEPolicy) +class IKEPolicyView(generic.ObjectView): + queryset = IKEPolicy.objects.all() + + +@register_model_view(IKEPolicy, 'edit') +class IKEPolicyEditView(generic.ObjectEditView): + queryset = IKEPolicy.objects.all() + form = forms.IKEPolicyForm + + +@register_model_view(IKEPolicy, 'delete') +class IKEPolicyDeleteView(generic.ObjectDeleteView): + queryset = IKEPolicy.objects.all() + + +class IKEPolicyBulkImportView(generic.BulkImportView): + queryset = IKEPolicy.objects.all() + model_form = forms.IKEPolicyImportForm + + +class IKEPolicyBulkEditView(generic.BulkEditView): + queryset = IKEPolicy.objects.all() + filterset = filtersets.IKEPolicyFilterSet + table = tables.IKEPolicyTable + form = forms.IKEPolicyBulkEditForm + + +class IKEPolicyBulkDeleteView(generic.BulkDeleteView): + queryset = IKEPolicy.objects.all() + filterset = filtersets.IKEPolicyFilterSet + table = tables.IKEPolicyTable + + +# +# IPSec proposals +# + +class IPSecProposalListView(generic.ObjectListView): + queryset = IPSecProposal.objects.all() + filterset = filtersets.IPSecProposalFilterSet + filterset_form = forms.IPSecProposalFilterForm + table = tables.IPSecProposalTable + + +@register_model_view(IPSecProposal) +class IPSecProposalView(generic.ObjectView): + queryset = IPSecProposal.objects.all() + + +@register_model_view(IPSecProposal, 'edit') +class IPSecProposalEditView(generic.ObjectEditView): + queryset = IPSecProposal.objects.all() + form = forms.IPSecProposalForm + + +@register_model_view(IPSecProposal, 'delete') +class IPSecProposalDeleteView(generic.ObjectDeleteView): + queryset = IPSecProposal.objects.all() + + +class IPSecProposalBulkImportView(generic.BulkImportView): + queryset = IPSecProposal.objects.all() + model_form = forms.IPSecProposalImportForm + + +class IPSecProposalBulkEditView(generic.BulkEditView): + queryset = IPSecProposal.objects.all() + filterset = filtersets.IPSecProposalFilterSet + table = tables.IPSecProposalTable + form = forms.IPSecProposalBulkEditForm + + +class IPSecProposalBulkDeleteView(generic.BulkDeleteView): + queryset = IPSecProposal.objects.all() + filterset = filtersets.IPSecProposalFilterSet + table = tables.IPSecProposalTable + + +# +# IPSec policies +# + +class IPSecPolicyListView(generic.ObjectListView): + queryset = IPSecPolicy.objects.all() + filterset = filtersets.IPSecPolicyFilterSet + filterset_form = forms.IPSecPolicyFilterForm + table = tables.IPSecPolicyTable + + +@register_model_view(IPSecPolicy) +class IPSecPolicyView(generic.ObjectView): + queryset = IPSecPolicy.objects.all() + + +@register_model_view(IPSecPolicy, 'edit') +class IPSecPolicyEditView(generic.ObjectEditView): + queryset = IPSecPolicy.objects.all() + form = forms.IPSecPolicyForm + + +@register_model_view(IPSecPolicy, 'delete') +class IPSecPolicyDeleteView(generic.ObjectDeleteView): + queryset = IPSecPolicy.objects.all() + + +class IPSecPolicyBulkImportView(generic.BulkImportView): + queryset = IPSecPolicy.objects.all() + model_form = forms.IPSecPolicyImportForm + + +class IPSecPolicyBulkEditView(generic.BulkEditView): + queryset = IPSecPolicy.objects.all() + filterset = filtersets.IPSecPolicyFilterSet + table = tables.IPSecPolicyTable + form = forms.IPSecPolicyBulkEditForm + + +class IPSecPolicyBulkDeleteView(generic.BulkDeleteView): + queryset = IPSecPolicy.objects.all() + filterset = filtersets.IPSecPolicyFilterSet + table = tables.IPSecPolicyTable + + # # IPSec profiles #