mirror of
https://github.com/netbox-community/netbox.git
synced 2024-05-10 07:54:54 +00:00
- Introduces a new `vpn` app with the following models: - Tunnel - TunnelTermination - IKEProposal - IKEPolicy - IPSecProposal - IPSecPolicy - IPSecProfile
This commit is contained in:
49
docs/features/vpn-tunnels.md
Normal file
49
docs/features/vpn-tunnels.md
Normal file
@ -0,0 +1,49 @@
|
||||
# Tunnels
|
||||
|
||||
NetBox can model private tunnels formed among virtual termination points across your network. Typical tunnel implementations include GRE, IP-in-IP, and IPSec. A tunnel may be terminated to two or more device or virtual machine interfaces.
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
Termination1[TunnelTermination]
|
||||
Termination2[TunnelTermination]
|
||||
Interface1[Interface]
|
||||
Interface2[Interface]
|
||||
Tunnel --> Termination1 & Termination2
|
||||
Termination1 --> Interface1
|
||||
Termination2 --> Interface2
|
||||
Interface1 --> Device
|
||||
Interface2 --> VirtualMachine
|
||||
|
||||
click Tunnel "../../models/vpn/tunnel/"
|
||||
click TunnelTermination1 "../../models/vpn/tunneltermination/"
|
||||
click TunnelTermination2 "../../models/vpn/tunneltermination/"
|
||||
```
|
||||
|
||||
# IPSec & IKE
|
||||
|
||||
NetBox includes robust support for modeling IPSec & IKE policies. These are used to define encryption and authentication parameters for IPSec tunnels.
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
subgraph IKEProposals[Proposals]
|
||||
IKEProposal1[IKEProposal]
|
||||
IKEProposal2[IKEProposal]
|
||||
end
|
||||
subgraph IPSecProposals[Proposals]
|
||||
IPSecProposal1[IPSecProposal]
|
||||
IPSecProposal2[IPSecProposal]
|
||||
end
|
||||
IKEProposals --> IKEPolicy
|
||||
IPSecProposals --> IPSecPolicy
|
||||
IKEPolicy & IPSecPolicy--> IPSecProfile
|
||||
IPSecProfile --> Tunnel
|
||||
|
||||
click IKEProposal1 "../../models/vpn/ikeproposal/"
|
||||
click IKEProposal2 "../../models/vpn/ikeproposal/"
|
||||
click IKEPolicy "../../models/vpn/ikepolicy/"
|
||||
click IPSecProposal1 "../../models/vpn/ipsecproposal/"
|
||||
click IPSecProposal2 "../../models/vpn/ipsecproposal/"
|
||||
click IPSecPolicy "../../models/vpn/ipsecpolicy/"
|
||||
click IPSecProfile "../../models/vpn/ipsecprofile/"
|
||||
click Tunnel "../../models/vpn/tunnel/"
|
||||
```
|
25
docs/models/vpn/ikepolicy.md
Normal file
25
docs/models/vpn/ikepolicy.md
Normal file
@ -0,0 +1,25 @@
|
||||
# IKE Policies
|
||||
|
||||
An [Internet Key Exhcnage (IKE)](https://en.wikipedia.org/wiki/Internet_Key_Exchange) policy defines an IKE version, mode, and set of [proposals](./ikeproposal.md) to be used in IKE negotiation. These policies are referenced by [IPSec profiles](./ipsecprofile.md).
|
||||
|
||||
## Fields
|
||||
|
||||
### Name
|
||||
|
||||
The unique user-assigned name for the policy.
|
||||
|
||||
### Version
|
||||
|
||||
The IKE version employed (v1 or v2).
|
||||
|
||||
### Mode
|
||||
|
||||
The IKE mode employed (main or aggressive).
|
||||
|
||||
### Proposals
|
||||
|
||||
One or more [IKE proposals](./ikeproposal.md) supported for use by this policy.
|
||||
|
||||
### Pre-shared Key
|
||||
|
||||
A pre-shared secret key associated with this policy (optional).
|
39
docs/models/vpn/ikeproposal.md
Normal file
39
docs/models/vpn/ikeproposal.md
Normal file
@ -0,0 +1,39 @@
|
||||
# IKE Proposals
|
||||
|
||||
An [Internet Key Exhcnage (IKE)](https://en.wikipedia.org/wiki/Internet_Key_Exchange) proposal defines a set of parameters used to establish a secure bidirectional connection across an untrusted medium, such as the Internet. IKE proposals defined in NetBox can be referenced by [IKE policies](./ikepolicy.md), which are in turn employed by [IPSec profiles](./ipsecprofile.md).
|
||||
|
||||
!!! note
|
||||
Some platforms refer to IKE proposals as [ISAKMP](https://en.wikipedia.org/wiki/Internet_Security_Association_and_Key_Management_Protocol), which is a framework for authentication and key exchange which employs IKE.
|
||||
|
||||
## Fields
|
||||
|
||||
### Name
|
||||
|
||||
The unique user-assigned name for the proposal.
|
||||
|
||||
### Authentication Method
|
||||
|
||||
The strategy employed for authenticating the IKE peer. Available options are listed below.
|
||||
|
||||
| Name |
|
||||
|----------------|
|
||||
| Pre-shared key |
|
||||
| Certificate |
|
||||
| RSA signature |
|
||||
| DSA signature |
|
||||
|
||||
### Encryption Algorithm
|
||||
|
||||
The protocol employed for data encryption. Options include DES, 3DES, and various flavors of AES.
|
||||
|
||||
### Authentication Algorithm
|
||||
|
||||
The mechanism employed to ensure data integrity. Options include MD5 and SHA HMAC implementations.
|
||||
|
||||
### Group
|
||||
|
||||
The [Diffie-Hellman group](https://en.wikipedia.org/wiki/Diffie%E2%80%93Hellman_key_exchange) supported by the proposal. Group IDs are [managed by IANA](https://www.iana.org/assignments/ikev2-parameters/ikev2-parameters.xhtml#ikev2-parameters-8).
|
||||
|
||||
### SA Lifetime
|
||||
|
||||
The maximum lifetime for the IKE security association (SA), in seconds.
|
17
docs/models/vpn/ipsecpolicy.md
Normal file
17
docs/models/vpn/ipsecpolicy.md
Normal file
@ -0,0 +1,17 @@
|
||||
# IPSec Policy
|
||||
|
||||
An [IPSec](https://en.wikipedia.org/wiki/IPsec) policy defines a set of [proposals](./ikeproposal.md) to be used in the formation of IPSec tunnels. A perfect forward secrecy (PFS) group may optionally also be defined. These policies are referenced by [IPSec profiles](./ipsecprofile.md).
|
||||
|
||||
## Fields
|
||||
|
||||
### Name
|
||||
|
||||
The unique user-assigned name for the policy.
|
||||
|
||||
### Proposals
|
||||
|
||||
One or more [IPSec proposals](./ipsecproposal.md) supported for use by this policy.
|
||||
|
||||
### PFS Group
|
||||
|
||||
The [perfect forward secrecy (PFS)](https://en.wikipedia.org/wiki/Forward_secrecy) group supported by this policy (optional).
|
21
docs/models/vpn/ipsecprofile.md
Normal file
21
docs/models/vpn/ipsecprofile.md
Normal file
@ -0,0 +1,21 @@
|
||||
# IPSec Profile
|
||||
|
||||
An [IPSec](https://en.wikipedia.org/wiki/IPsec) profile defines an [IKE policy](./ikepolicy.md), [IPSec policy](./ipsecpolicy.md), and IPSec mode used for establishing an IPSec tunnel.
|
||||
|
||||
## Fields
|
||||
|
||||
### Name
|
||||
|
||||
The unique user-assigned name for the profile.
|
||||
|
||||
### Mode
|
||||
|
||||
The IPSec mode employed by the profile: Encapsulating Security Payload (ESP) or Authentication Header (AH).
|
||||
|
||||
### IKE Policy
|
||||
|
||||
The [IKE policy](./ikepolicy.md) associated with the profile.
|
||||
|
||||
### IPSec Policy
|
||||
|
||||
The [IPSec policy](./ipsecpolicy.md) associated with the profile.
|
25
docs/models/vpn/ipsecproposal.md
Normal file
25
docs/models/vpn/ipsecproposal.md
Normal file
@ -0,0 +1,25 @@
|
||||
# IPSec Proposal
|
||||
|
||||
An [IPSec](https://en.wikipedia.org/wiki/IPsec) proposal defines a set of parameters used in negotiating security associations for IPSec tunnels. IPSec proposals defined in NetBox can be referenced by [IPSec policies](./ipsecpolicy.md), which are in turn employed by [IPSec profiles](./ipsecprofile.md).
|
||||
|
||||
## Fields
|
||||
|
||||
### Name
|
||||
|
||||
The unique user-assigned name for the proposal.
|
||||
|
||||
### Encryption Algorithm
|
||||
|
||||
The protocol employed for data encryption. Options include DES, 3DES, and various flavors of AES.
|
||||
|
||||
### Authentication Algorithm
|
||||
|
||||
The mechanism employed to ensure data integrity. Options include MD5 and SHA HMAC implementations.
|
||||
|
||||
### SA Lifetime (Seconds)
|
||||
|
||||
The maximum amount of time for which the security association (SA) may be active, in seconds.
|
||||
|
||||
### SA Lifetime (Data)
|
||||
|
||||
The maximum amount of data which can be transferred within the security association (SA) before it must be rebuilt, in kilobytes.
|
36
docs/models/vpn/tunnel.md
Normal file
36
docs/models/vpn/tunnel.md
Normal file
@ -0,0 +1,36 @@
|
||||
# Tunnels
|
||||
|
||||
A tunnel represents a private virtual connection established among two or more endpoints across a shared infrastructure by employing protocol encapsulation. Common encapsulation techniques include [Generic Routing Encapsulation (GRE)](https://en.wikipedia.org/wiki/Generic_Routing_Encapsulation), [IP-in-IP](https://en.wikipedia.org/wiki/IP_in_IP), and [IPSec](https://en.wikipedia.org/wiki/IPsec). NetBox supports modeling both peer-to-peer and hub-and-spoke tunnel topologies.
|
||||
|
||||
Device and virtual machine interfaces are associated to tunnels by creating [tunnel terminations](./tunneltermination.md).
|
||||
|
||||
## Fields
|
||||
|
||||
### Name
|
||||
|
||||
A unique name assigned to the tunnel for identification.
|
||||
|
||||
### Status
|
||||
|
||||
The operational status of the tunnel. By default, the following statuses are available:
|
||||
|
||||
| Name |
|
||||
|----------------|
|
||||
| Planned |
|
||||
| Active |
|
||||
| Disabled |
|
||||
|
||||
!!! tip "Custom tunnel statuses"
|
||||
Additional tunnel statuses may be defined by setting `Tunnel.status` under the [`FIELD_CHOICES`](../../configuration/data-validation.md#field_choices) configuration parameter.
|
||||
|
||||
### Encapsulation
|
||||
|
||||
The encapsulation protocol or technique employed to effect the tunnel. NetBox supports GRE, IP-in-IP, and IPSec encapsulations.
|
||||
|
||||
### Tunnel ID
|
||||
|
||||
An optional numeric identifier for the tunnel.
|
||||
|
||||
### IPSec Profile
|
||||
|
||||
For IPSec tunnels, this is the [IPSec Profile](./ipsecprofile.md) employed to negotiate security associations.
|
30
docs/models/vpn/tunneltermination.md
Normal file
30
docs/models/vpn/tunneltermination.md
Normal file
@ -0,0 +1,30 @@
|
||||
# Tunnel Terminations
|
||||
|
||||
A tunnel termination connects a device or virtual machine interface to a [tunnel](./tunnel.md). The tunnel must be created before any terminations may be added.
|
||||
|
||||
## Fields
|
||||
|
||||
### Tunnel
|
||||
|
||||
The [tunnel](./tunnel.md) to which this termination is made.
|
||||
|
||||
### Role
|
||||
|
||||
The functional role of the attached interface. The following options are available:
|
||||
|
||||
| Name | Description |
|
||||
|-------|--------------------------------------------------|
|
||||
| Peer | An endpoint in a point-to-point or mesh topology |
|
||||
| Hub | A central point in a hub-and-spoke topology |
|
||||
| Spoke | An edge point in a hub-and-spoke topology |
|
||||
|
||||
!!! note
|
||||
Multiple hub terminations may be attached to a tunnel.
|
||||
|
||||
### Termination
|
||||
|
||||
The device or virtual machine interface terminated to the tunnel.
|
||||
|
||||
### Outside IP
|
||||
|
||||
The public or underlay IP address with which this termination is associated. This is the IP to which peers will route tunneled traffic.
|
@ -74,6 +74,7 @@ nav:
|
||||
- Circuits: 'features/circuits.md'
|
||||
- Wireless: 'features/wireless.md'
|
||||
- Virtualization: 'features/virtualization.md'
|
||||
- VPN Tunnels: 'features/vpn-tunnels.md'
|
||||
- Tenancy: 'features/tenancy.md'
|
||||
- Contacts: 'features/contacts.md'
|
||||
- Search: 'features/search.md'
|
||||
@ -252,6 +253,14 @@ nav:
|
||||
- ClusterType: 'models/virtualization/clustertype.md'
|
||||
- VMInterface: 'models/virtualization/vminterface.md'
|
||||
- VirtualMachine: 'models/virtualization/virtualmachine.md'
|
||||
- VPN:
|
||||
- IKEPolicy: 'models/vpn/ikepolicy.md'
|
||||
- IKEProposal: 'models/vpn/ikeproposal.md'
|
||||
- IPSecPolicy: 'models/vpn/ipsecpolicy.md'
|
||||
- IPSecProfile: 'models/vpn/ipsecprofile.md'
|
||||
- IPSecProposal: 'models/vpn/ipsecproposal.md'
|
||||
- Tunnel: 'models/vpn/tunnel.md'
|
||||
- TunnelTermination: 'models/vpn/tunneltermination.md'
|
||||
- Wireless:
|
||||
- WirelessLAN: 'models/wireless/wirelesslan.md'
|
||||
- WirelessLANGroup: 'models/wireless/wirelesslangroup.md'
|
||||
|
@ -9,7 +9,7 @@ from django.contrib.auth import get_user_model
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.core.management.base import BaseCommand
|
||||
|
||||
APPS = ('circuits', 'core', 'dcim', 'extras', 'ipam', 'tenancy', 'users', 'virtualization', 'wireless')
|
||||
APPS = ('circuits', 'core', 'dcim', 'extras', 'ipam', 'tenancy', 'users', 'virtualization', 'vpn', 'wireless')
|
||||
|
||||
BANNER_TEXT = """### NetBox interactive shell ({node})
|
||||
### Python {python} | Django {django} | NetBox {netbox}
|
||||
|
@ -566,6 +566,10 @@ class BaseInterface(models.Model):
|
||||
|
||||
return super().save(*args, **kwargs)
|
||||
|
||||
@property
|
||||
def tunnel_termination(self):
|
||||
return self.tunnel_terminations.first()
|
||||
|
||||
@property
|
||||
def count_ipaddresses(self):
|
||||
return self.ip_addresses.count()
|
||||
@ -719,6 +723,12 @@ class Interface(ModularComponentModel, BaseInterface, CabledObjectModel, PathEnd
|
||||
object_id_field='interface_id',
|
||||
related_query_name='+'
|
||||
)
|
||||
tunnel_terminations = GenericRelation(
|
||||
to='vpn.TunnelTermination',
|
||||
content_type_field='termination_type',
|
||||
object_id_field='termination_id',
|
||||
related_query_name='interface'
|
||||
)
|
||||
l2vpn_terminations = GenericRelation(
|
||||
to='ipam.L2VPNTermination',
|
||||
content_type_field='assigned_object_type',
|
||||
|
@ -584,6 +584,12 @@ class BaseInterfaceTable(NetBoxTable):
|
||||
orderable=False,
|
||||
verbose_name=_('L2VPN')
|
||||
)
|
||||
tunnel = tables.Column(
|
||||
accessor=tables.A('tunnel_termination__tunnel'),
|
||||
linkify=True,
|
||||
orderable=False,
|
||||
verbose_name=_('Tunnel')
|
||||
)
|
||||
untagged_vlan = tables.Column(
|
||||
verbose_name=_('Untagged VLAN'),
|
||||
linkify=True
|
||||
@ -646,7 +652,8 @@ class InterfaceTable(ModularDeviceComponentTable, BaseInterfaceTable, PathEndpoi
|
||||
'speed', 'speed_formatted', 'duplex', 'mode', 'mac_address', 'wwn', 'poe_mode', 'poe_type', 'rf_role', 'rf_channel',
|
||||
'rf_channel_frequency', 'rf_channel_width', 'tx_power', 'description', 'mark_connected', 'cable',
|
||||
'cable_color', 'wireless_link', 'wireless_lans', 'link_peer', 'connection', 'tags', 'vdcs', 'vrf', 'l2vpn',
|
||||
'ip_addresses', 'fhrp_groups', 'untagged_vlan', 'tagged_vlans', 'inventory_items', 'created', 'last_updated',
|
||||
'tunnel', 'ip_addresses', 'fhrp_groups', 'untagged_vlan', 'tagged_vlans', 'inventory_items', 'created',
|
||||
'last_updated',
|
||||
)
|
||||
default_columns = ('pk', 'name', 'device', 'label', 'enabled', 'type', 'description')
|
||||
|
||||
@ -682,8 +689,8 @@ class DeviceInterfaceTable(InterfaceTable):
|
||||
'pk', 'id', 'name', 'module_bay', 'module', 'label', 'enabled', 'type', 'parent', 'bridge', 'lag',
|
||||
'mgmt_only', 'mtu', 'mode', 'mac_address', 'wwn', 'rf_role', 'rf_channel', 'rf_channel_frequency',
|
||||
'rf_channel_width', 'tx_power', 'description', 'mark_connected', 'cable', 'cable_color', 'wireless_link',
|
||||
'wireless_lans', 'link_peer', 'connection', 'tags', 'vdcs', 'vrf', 'l2vpn', 'ip_addresses', 'fhrp_groups',
|
||||
'untagged_vlan', 'tagged_vlans', 'actions',
|
||||
'wireless_lans', 'link_peer', 'connection', 'tags', 'vdcs', 'vrf', 'l2vpn', 'tunnel', 'ip_addresses',
|
||||
'fhrp_groups', 'untagged_vlan', 'tagged_vlans', 'actions',
|
||||
)
|
||||
default_columns = (
|
||||
'pk', 'name', 'label', 'enabled', 'type', 'parent', 'lag', 'mtu', 'mode', 'description', 'ip_addresses',
|
||||
|
@ -359,6 +359,16 @@ INTERFACE_BUTTONS = """
|
||||
<i class="mdi mdi-wifi-off" aria-hidden="true"></i>
|
||||
</a>
|
||||
{% endif %}
|
||||
{% elif record.type == 'virtual' %}
|
||||
{% if perms.vpn.add_tunnel and not record.tunnel_termination %}
|
||||
<a href="{% url 'vpn:tunnel_add' %}?termination1_type=dcim.device&termination1_parent={{ record.device.pk }}&termination1_interface={{ record.pk }}&return_url={% url 'dcim:device_interfaces' pk=object.pk %}" title="Create a tunnel" class="btn btn-success btn-sm">
|
||||
<i class="mdi mdi-tunnel-outline" aria-hidden="true"></i>
|
||||
</a>
|
||||
{% elif perms.vpn.delete_tunneltermination and record.tunnel_termination %}
|
||||
<a href="{% url 'vpn:tunneltermination_delete' pk=record.tunnel_termination.pk %}?return_url={% url 'dcim:device_interfaces' pk=object.pk %}" title="Remove tunnel" class="btn btn-danger btn-sm">
|
||||
<i class="mdi mdi-tunnel-outline" aria-hidden="true"></i>
|
||||
</a>
|
||||
{% endif %}
|
||||
{% elif record.is_wired and perms.dcim.add_cable %}
|
||||
<a href="#" class="btn btn-outline-dark btn-sm disabled"><i class="mdi mdi-transit-connection-variant" aria-hidden="true"></i></a>
|
||||
<a href="#" class="btn btn-outline-dark btn-sm disabled"><i class="mdi mdi-lan-connect" aria-hidden="true"></i></a>
|
||||
|
@ -39,6 +39,7 @@ class APIRootView(APIView):
|
||||
'tenancy': reverse('tenancy-api:api-root', request=request, format=format),
|
||||
'users': reverse('users-api:api-root', request=request, format=format),
|
||||
'virtualization': reverse('virtualization-api:api-root', request=request, format=format),
|
||||
'vpn': reverse('vpn-api:api-root', request=request, format=format),
|
||||
'wireless': reverse('wireless-api:api-root', request=request, format=format),
|
||||
})
|
||||
|
||||
|
@ -9,6 +9,7 @@ from netbox.registry import registry
|
||||
from tenancy.graphql.schema import TenancyQuery
|
||||
from users.graphql.schema import UsersQuery
|
||||
from virtualization.graphql.schema import VirtualizationQuery
|
||||
from vpn.graphql.schema import VPNQuery
|
||||
from wireless.graphql.schema import WirelessQuery
|
||||
|
||||
|
||||
@ -21,6 +22,7 @@ class Query(
|
||||
IPAMQuery,
|
||||
TenancyQuery,
|
||||
VirtualizationQuery,
|
||||
VPNQuery,
|
||||
WirelessQuery,
|
||||
*registry['plugins']['graphql_schemas'], # Append plugin schemas
|
||||
graphene.ObjectType
|
||||
|
@ -195,17 +195,34 @@ IPAM_MENU = Menu(
|
||||
),
|
||||
)
|
||||
|
||||
OVERLAY_MENU = Menu(
|
||||
label=_('Overlay'),
|
||||
VPN_MENU = Menu(
|
||||
label=_('VPN'),
|
||||
icon_class='mdi mdi-graph-outline',
|
||||
groups=(
|
||||
MenuGroup(
|
||||
label='L2VPNs',
|
||||
label=_('Tunnels'),
|
||||
items=(
|
||||
get_model_item('vpn', 'tunnel', _('Tunnels')),
|
||||
get_model_item('vpn', 'tunneltermination', _('Tunnel Terminations')),
|
||||
),
|
||||
),
|
||||
MenuGroup(
|
||||
label=_('L2VPNs'),
|
||||
items=(
|
||||
get_model_item('ipam', 'l2vpn', _('L2VPNs')),
|
||||
get_model_item('ipam', 'l2vpntermination', _('Terminations')),
|
||||
),
|
||||
),
|
||||
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')),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
@ -444,7 +461,7 @@ MENUS = [
|
||||
CONNECTIONS_MENU,
|
||||
WIRELESS_MENU,
|
||||
IPAM_MENU,
|
||||
OVERLAY_MENU,
|
||||
VPN_MENU,
|
||||
VIRTUALIZATION_MENU,
|
||||
CIRCUITS_MENU,
|
||||
POWER_MENU,
|
||||
|
@ -379,6 +379,7 @@ INSTALLED_APPS = [
|
||||
'users',
|
||||
'utilities',
|
||||
'virtualization',
|
||||
'vpn',
|
||||
'wireless',
|
||||
'django_rq', # Must come after extras to allow overriding management commands
|
||||
'drf_spectacular',
|
||||
|
@ -33,6 +33,7 @@ _patterns = [
|
||||
path('tenancy/', include('tenancy.urls')),
|
||||
path('users/', include('users.urls')),
|
||||
path('virtualization/', include('virtualization.urls')),
|
||||
path('vpn/', include('vpn.urls')),
|
||||
path('wireless/', include('wireless.urls')),
|
||||
|
||||
# Current user views
|
||||
@ -51,6 +52,7 @@ _patterns = [
|
||||
path('api/tenancy/', include('tenancy.api.urls')),
|
||||
path('api/users/', include('users.api.urls')),
|
||||
path('api/virtualization/', include('virtualization.api.urls')),
|
||||
path('api/vpn/', include('vpn.api.urls')),
|
||||
path('api/wireless/', include('wireless.api.urls')),
|
||||
path('api/status/', StatusView.as_view(), name='api-status'),
|
||||
|
||||
|
67
netbox/templates/vpn/ikepolicy.html
Normal file
67
netbox/templates/vpn/ikepolicy.html
Normal file
@ -0,0 +1,67 @@
|
||||
{% extends 'generic/object.html' %}
|
||||
{% load helpers %}
|
||||
{% load plugins %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row">
|
||||
<div class="col col-md-6">
|
||||
<div class="card">
|
||||
<h5 class="card-header">{% trans "IKE Policy" %}</h5>
|
||||
<div class="card-body">
|
||||
<table class="table table-hover attr-table">
|
||||
<tr>
|
||||
<th scope="row">{% trans "Name" %}</th>
|
||||
<td>{{ object.name }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">{% trans "Description" %}</th>
|
||||
<td>{{ object.description|placeholder }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">{% trans "IKE Version" %}</th>
|
||||
<td>{{ object.get_version_display }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">{% trans "Mode" %}</th>
|
||||
<td>{{ object.get_mode_display }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">{% trans "Pre-Shared Key" %}</th>
|
||||
<td>
|
||||
<span id="secret" class="font-monospace" data-secret="{{ object.preshared_key }}">{{ object.preshared_key|placeholder }}</span>
|
||||
{% if object.preshared_key %}
|
||||
<button type="button" class="btn btn-sm btn-primary toggle-secret float-end" data-bs-toggle="button">{% trans "Show Secret" %}</button>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">{% trans "IPSec Profiles" %}</th>
|
||||
<td>
|
||||
<a href="{% url 'vpn:ipsecprofile_list' %}?ike_policy_id={{ object.pk }}">{{ object.ipsec_profiles.count }}</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
{% plugin_left_page object %}
|
||||
</div>
|
||||
<div class="col col-md-6">
|
||||
{% include 'inc/panels/custom_fields.html' %}
|
||||
{% include 'inc/panels/tags.html' %}
|
||||
{% plugin_right_page object %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col col-md-12">
|
||||
<div class="card">
|
||||
<h5 class="card-header">{% trans "Proposals" %}</h5>
|
||||
<div class="card-body htmx-container table-responsive"
|
||||
hx-get="{% url 'vpn:ikeproposal_list' %}?ike_policy_id={{ object.pk }}"
|
||||
hx-trigger="load"
|
||||
></div>
|
||||
</div>
|
||||
{% plugin_full_width_page object %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
63
netbox/templates/vpn/ikeproposal.html
Normal file
63
netbox/templates/vpn/ikeproposal.html
Normal file
@ -0,0 +1,63 @@
|
||||
{% extends 'generic/object.html' %}
|
||||
{% load helpers %}
|
||||
{% load plugins %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row">
|
||||
<div class="col col-md-6">
|
||||
<div class="card">
|
||||
<h5 class="card-header">{% trans "IKE Proposal" %}</h5>
|
||||
<div class="card-body">
|
||||
<table class="table table-hover attr-table">
|
||||
<tr>
|
||||
<th scope="row">{% trans "Name" %}</th>
|
||||
<td>{{ object.name }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">{% trans "Description" %}</th>
|
||||
<td>{{ object.description|placeholder }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">{% trans "Authentication method" %}</th>
|
||||
<td>{{ object.get_authentication_method_display }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">{% trans "Encryption algorithm" %}</th>
|
||||
<td>{{ object.get_encryption_algorithm_display }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">{% trans "Authentication algorithm" %}</th>
|
||||
<td>{{ object.get_authentication_algorithm_display }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">{% trans "DH group" %}</th>
|
||||
<td>{{ object.get_group_display }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">{% trans "SA lifetime (seconds)" %}</th>
|
||||
<td>{{ object.sa_lifetime|placeholder }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">{% trans "IKE Policies" %}</th>
|
||||
<td>
|
||||
<a href="{% url 'vpn:ikepolicy_list' %}?proposal_id={{ object.pk }}">{{ object.ike_policies.count }}</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
{% plugin_left_page object %}
|
||||
</div>
|
||||
<div class="col col-md-6">
|
||||
{% include 'inc/panels/custom_fields.html' %}
|
||||
{% include 'inc/panels/tags.html' %}
|
||||
{% plugin_right_page object %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col col-md-12">
|
||||
{% plugin_full_width_page object %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
55
netbox/templates/vpn/ipsecpolicy.html
Normal file
55
netbox/templates/vpn/ipsecpolicy.html
Normal file
@ -0,0 +1,55 @@
|
||||
{% extends 'generic/object.html' %}
|
||||
{% load helpers %}
|
||||
{% load plugins %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row">
|
||||
<div class="col col-md-6">
|
||||
<div class="card">
|
||||
<h5 class="card-header">{% trans "IPSec Policy" %}</h5>
|
||||
<div class="card-body">
|
||||
<table class="table table-hover attr-table">
|
||||
<tr>
|
||||
<th scope="row">{% trans "Name" %}</th>
|
||||
<td>{{ object.name }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">{% trans "Description" %}</th>
|
||||
<td>{{ object.description|placeholder }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">{% trans "PFS group" %}</th>
|
||||
<td>{{ object.get_pfs_group_display|placeholder }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">{% trans "IPSec Profiles" %}</th>
|
||||
<td>
|
||||
<a href="{% url 'vpn:ipsecprofile_list' %}?ipsec_policy_id={{ object.pk }}">{{ object.ipsec_profiles.count }}</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
{% plugin_left_page object %}
|
||||
</div>
|
||||
<div class="col col-md-6">
|
||||
{% include 'inc/panels/custom_fields.html' %}
|
||||
{% include 'inc/panels/tags.html' %}
|
||||
{% plugin_right_page object %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col col-md-12">
|
||||
<div class="col col-md-12">
|
||||
<div class="card">
|
||||
<h5 class="card-header">{% trans "Proposals" %}</h5>
|
||||
<div class="card-body htmx-container table-responsive"
|
||||
hx-get="{% url 'vpn:ipsecproposal_list' %}?ipsec_policy_id={{ object.pk }}"
|
||||
hx-trigger="load"
|
||||
></div>
|
||||
</div>
|
||||
{% plugin_full_width_page object %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
112
netbox/templates/vpn/ipsecprofile.html
Normal file
112
netbox/templates/vpn/ipsecprofile.html
Normal file
@ -0,0 +1,112 @@
|
||||
{% extends 'generic/object.html' %}
|
||||
{% load helpers %}
|
||||
{% load plugins %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row">
|
||||
<div class="col col-md-6">
|
||||
<div class="card">
|
||||
<h5 class="card-header">{% trans "IPSec Profile" %}</h5>
|
||||
<div class="card-body">
|
||||
<table class="table table-hover attr-table">
|
||||
<tr>
|
||||
<th scope="row">{% trans "Name" %}</th>
|
||||
<td>{{ object.name }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">{% trans "Description" %}</th>
|
||||
<td>{{ object.description|placeholder }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">{% trans "Mode" %}</th>
|
||||
<td>{{ object.get_mode_display }}</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
{% include 'inc/panels/tags.html' %}
|
||||
{% include 'inc/panels/custom_fields.html' %}
|
||||
{% include 'inc/panels/comments.html' %}
|
||||
{% plugin_left_page object %}
|
||||
</div>
|
||||
<div class="col col-md-6">
|
||||
<div class="card">
|
||||
<h5 class="card-header">{% trans "IKE Policy" %}</h5>
|
||||
<div class="card-body">
|
||||
<table class="table table-hover attr-table">
|
||||
<tr>
|
||||
<th scope="row">{% trans "Name" %}</th>
|
||||
<td>{{ object.ike_policy|linkify }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">{% trans "Description" %}</th>
|
||||
<td>{{ object.ike_policy.description|placeholder }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">{% trans "Version" %}</th>
|
||||
<td>{{ object.ike_policy.get_version_display }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">{% trans "Mode" %}</th>
|
||||
<td>{{ object.ike_policy.get_mode_display }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">{% trans "Proposals" %}</th>
|
||||
<td>
|
||||
<ul class="list-unstyled mb-0">
|
||||
{% for proposal in object.ike_policy.proposals.all %}
|
||||
<li>
|
||||
<a href="{{ proposal.get_absolute_url }}">{{ proposal }}</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">{% trans "Pre-Shared Key" %}</th>
|
||||
<td>{% checkmark object.ike_policy.preshared_key %}</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card">
|
||||
<h5 class="card-header">{% trans "IPSec Policy" %}</h5>
|
||||
<div class="card-body">
|
||||
<table class="table table-hover attr-table">
|
||||
<tr>
|
||||
<th scope="row">{% trans "Name" %}</th>
|
||||
<td>{{ object.ipsec_policy|linkify }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">{% trans "Description" %}</th>
|
||||
<td>{{ object.ipsec_policy.description|placeholder }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">{% trans "Proposals" %}</th>
|
||||
<td>
|
||||
<ul class="list-unstyled mb-0">
|
||||
{% for proposal in object.ipsec_policy.proposals.all %}
|
||||
<li>
|
||||
<a href="{{ proposal.get_absolute_url }}">{{ proposal }}</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">{% trans "PFS Group" %}</th>
|
||||
<td>{{ object.ipsec_policy.get_pfs_group_display }}</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
{% plugin_right_page object %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col col-md-12">
|
||||
{% plugin_full_width_page object %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
59
netbox/templates/vpn/ipsecproposal.html
Normal file
59
netbox/templates/vpn/ipsecproposal.html
Normal file
@ -0,0 +1,59 @@
|
||||
{% extends 'generic/object.html' %}
|
||||
{% load helpers %}
|
||||
{% load plugins %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row">
|
||||
<div class="col col-md-6">
|
||||
<div class="card">
|
||||
<h5 class="card-header">{% trans "IPSec Proposal" %}</h5>
|
||||
<div class="card-body">
|
||||
<table class="table table-hover attr-table">
|
||||
<tr>
|
||||
<th scope="row">{% trans "Name" %}</th>
|
||||
<td>{{ object.name }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">{% trans "Description" %}</th>
|
||||
<td>{{ object.description|placeholder }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">{% trans "Encryption algorithm" %}</th>
|
||||
<td>{{ object.get_encryption_algorithm_display }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">{% trans "Authentication algorithm" %}</th>
|
||||
<td>{{ object.get_authentication_algorithm_display }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">{% trans "SA lifetime (seconds)" %}</th>
|
||||
<td>{{ object.sa_lifetime_seconds|placeholder }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">{% trans "SA lifetime (KB)" %}</th>
|
||||
<td>{{ object.sa_lifetime_data|placeholder }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">{% trans "IPSec Policies" %}</th>
|
||||
<td>
|
||||
<a href="{% url 'vpn:ipsecpolicy_list' %}?proposal_id={{ object.pk }}">{{ object.ipsec_policies.count }}</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
{% plugin_left_page object %}
|
||||
</div>
|
||||
<div class="col col-md-6">
|
||||
{% include 'inc/panels/custom_fields.html' %}
|
||||
{% include 'inc/panels/tags.html' %}
|
||||
{% plugin_right_page object %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col col-md-12">
|
||||
{% plugin_full_width_page object %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
85
netbox/templates/vpn/tunnel.html
Normal file
85
netbox/templates/vpn/tunnel.html
Normal file
@ -0,0 +1,85 @@
|
||||
{% extends 'generic/object.html' %}
|
||||
{% load helpers %}
|
||||
{% load plugins %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block extra_controls %}
|
||||
{% if perms.vpn.add_tunneltermination %}
|
||||
<a href="{% url 'vpn:tunneltermination_add' %}?tunnel={{ object.pk }}&return_url={{ object.get_absolute_url }}" class="btn btn-sm btn-primary">
|
||||
<i class="mdi mdi-plus-thick"></i> {% trans "Add Termination" %}
|
||||
</a>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row">
|
||||
<div class="col col-md-6">
|
||||
<div class="card">
|
||||
<h5 class="card-header">{% trans "Tunnel" %}</h5>
|
||||
<div class="card-body">
|
||||
<table class="table table-hover attr-table">
|
||||
<tr>
|
||||
<th scope="row">{% trans "Name" %}</th>
|
||||
<td>{{ object.name }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">{% trans "Status" %}</th>
|
||||
<td>{% badge object.get_status_display bg_color=object.get_status_color %}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">{% trans "Description" %}</th>
|
||||
<td>{{ object.description|placeholder }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">{% trans "Encapsulation" %}</th>
|
||||
<td>{{ object.get_encapsulation_display }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">{% trans "IPSec profile" %}</th>
|
||||
<td>{{ object.ipsec_profile|linkify|placeholder }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">{% trans "Tunnel ID" %}</th>
|
||||
<td>{{ object.tunnel_id|placeholder }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">{% trans "Tenant" %}</th>
|
||||
<td>
|
||||
{% if object.tenant.group %}
|
||||
{{ object.tenant.group|linkify }} /
|
||||
{% endif %}
|
||||
{{ object.tenant|linkify|placeholder }}
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
{% plugin_left_page object %}
|
||||
</div>
|
||||
<div class="col col-md-6">
|
||||
{% include 'inc/panels/custom_fields.html' %}
|
||||
{% include 'inc/panels/tags.html' %}
|
||||
{% include 'inc/panels/comments.html' %}
|
||||
{% plugin_right_page object %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col col-md-12">
|
||||
<div class="card">
|
||||
<h5 class="card-header">{% trans "Terminations" %}</h5>
|
||||
<div class="card-body htmx-container table-responsive"
|
||||
hx-get="{% url 'vpn:tunneltermination_list' %}?tunnel_id={{ object.pk }}"
|
||||
hx-trigger="load"
|
||||
></div>
|
||||
{% if perms.vpn.add_tunneltermination %}
|
||||
<div class="card-footer text-end noprint">
|
||||
<a href="{% url 'vpn:tunneltermination_add' %}?tunnel={{ object.pk }}&return_url={{ object.get_absolute_url }}" class="btn btn-primary btn-sm">
|
||||
<i class="mdi mdi-plus-thick" aria-hidden="true"></i> {% trans "Add a Termination" %}
|
||||
</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% plugin_full_width_page object %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
62
netbox/templates/vpn/tunneltermination.html
Normal file
62
netbox/templates/vpn/tunneltermination.html
Normal file
@ -0,0 +1,62 @@
|
||||
{% extends 'generic/object.html' %}
|
||||
{% load helpers %}
|
||||
{% load plugins %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row">
|
||||
<div class="col col-md-6">
|
||||
<div class="card">
|
||||
<h5 class="card-header">{% trans "Tunnel Termination" %}</h5>
|
||||
<div class="card-body">
|
||||
<table class="table table-hover attr-table">
|
||||
<tr>
|
||||
<th scope="row">{% trans "Tunnel" %}</th>
|
||||
<td>{{ object.tunnel|linkify }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">{% trans "Role" %}</th>
|
||||
<td>{% badge object.get_role_display bg_color=object.get_role_color %}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">
|
||||
{% if object.termination.device %}
|
||||
{% trans "Device" %}
|
||||
{% elif object.termination.virtual_machine %}
|
||||
{% trans "Virtual Machine" %}
|
||||
{% endif %}
|
||||
</th>
|
||||
<td>{{ object.termination.parent_object|linkify }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">{% trans "Interface" %}</th>
|
||||
<td>{{ object.termination|linkify }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">{% trans "Outside IP" %}</th>
|
||||
<td>{{ object.outside_ip|linkify|placeholder }}</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
{% plugin_left_page object %}
|
||||
</div>
|
||||
<div class="col col-md-6">
|
||||
{% include 'inc/panels/custom_fields.html' %}
|
||||
{% include 'inc/panels/tags.html' %}
|
||||
{% plugin_right_page object %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col col-md-12">
|
||||
<div class="card">
|
||||
<h5 class="card-header">{% trans "Peer Terminations" %}</h5>
|
||||
<div class="card-body htmx-container table-responsive"
|
||||
hx-get="{% url 'vpn:tunneltermination_list' %}?tunnel_id={{ object.tunnel.pk }}&id__n={{ object.pk }}"
|
||||
hx-trigger="load"
|
||||
></div>
|
||||
</div>
|
||||
{% plugin_full_width_page object %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
@ -351,6 +351,12 @@ class VMInterface(ComponentModel, BaseInterface, TrackingModelMixin):
|
||||
object_id_field='interface_id',
|
||||
related_query_name='+'
|
||||
)
|
||||
tunnel_terminations = GenericRelation(
|
||||
to='vpn.TunnelTermination',
|
||||
content_type_field='termination_type',
|
||||
object_id_field='termination_id',
|
||||
related_query_name='vminterface',
|
||||
)
|
||||
l2vpn_terminations = GenericRelation(
|
||||
to='ipam.L2VPNTermination',
|
||||
content_type_field='assigned_object_type',
|
||||
|
@ -131,7 +131,8 @@ class VMInterfaceTable(BaseInterfaceTable):
|
||||
model = VMInterface
|
||||
fields = (
|
||||
'pk', 'id', 'name', 'virtual_machine', 'enabled', 'mac_address', 'mtu', 'mode', 'description', 'tags',
|
||||
'vrf', 'l2vpn', 'ip_addresses', 'fhrp_groups', 'untagged_vlan', 'tagged_vlans', 'created', 'last_updated',
|
||||
'vrf', 'l2vpn', 'tunnel', 'ip_addresses', 'fhrp_groups', 'untagged_vlan', 'tagged_vlans', 'created',
|
||||
'last_updated',
|
||||
)
|
||||
default_columns = ('pk', 'name', 'virtual_machine', 'enabled', 'description')
|
||||
|
||||
@ -154,7 +155,7 @@ class VirtualMachineVMInterfaceTable(VMInterfaceTable):
|
||||
model = VMInterface
|
||||
fields = (
|
||||
'pk', 'id', 'name', 'enabled', 'parent', 'bridge', 'mac_address', 'mtu', 'mode', 'description', 'tags',
|
||||
'vrf', 'l2vpn', 'ip_addresses', 'fhrp_groups', 'untagged_vlan', 'tagged_vlans', 'actions',
|
||||
'vrf', 'l2vpn', 'tunnel', 'ip_addresses', 'fhrp_groups', 'untagged_vlan', 'tagged_vlans', 'actions',
|
||||
)
|
||||
default_columns = ('pk', 'name', 'enabled', 'mac_address', 'mtu', 'mode', 'description', 'ip_addresses')
|
||||
row_attrs = {
|
||||
|
0
netbox/vpn/__init__.py
Normal file
0
netbox/vpn/__init__.py
Normal file
3
netbox/vpn/admin.py
Normal file
3
netbox/vpn/admin.py
Normal file
@ -0,0 +1,3 @@
|
||||
from django.contrib import admin
|
||||
|
||||
# Register your models here.
|
0
netbox/vpn/api/__init__.py
Normal file
0
netbox/vpn/api/__init__.py
Normal file
84
netbox/vpn/api/nested_serializers.py
Normal file
84
netbox/vpn/api/nested_serializers.py
Normal file
@ -0,0 +1,84 @@
|
||||
from rest_framework import serializers
|
||||
|
||||
from netbox.api.serializers import WritableNestedSerializer
|
||||
from vpn import models
|
||||
|
||||
__all__ = (
|
||||
'NestedIKEPolicySerializer',
|
||||
'NestedIKEProposalSerializer',
|
||||
'NestedIPSecPolicySerializer',
|
||||
'NestedIPSecProfileSerializer',
|
||||
'NestedIPSecProposalSerializer',
|
||||
'NestedTunnelSerializer',
|
||||
'NestedTunnelTerminationSerializer',
|
||||
)
|
||||
|
||||
|
||||
class NestedTunnelSerializer(WritableNestedSerializer):
|
||||
url = serializers.HyperlinkedIdentityField(
|
||||
view_name='vpn-api:tunnel-detail'
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = models.Tunnel
|
||||
fields = ('id', 'url', 'display', 'name')
|
||||
|
||||
|
||||
class NestedTunnelTerminationSerializer(WritableNestedSerializer):
|
||||
url = serializers.HyperlinkedIdentityField(
|
||||
view_name='vpn-api:tunneltermination-detail'
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = models.TunnelTermination
|
||||
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.IKEPolicy
|
||||
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.IPSecPolicy
|
||||
fields = ('id', 'url', 'display', 'name')
|
||||
|
||||
|
||||
class NestedIPSecProfileSerializer(WritableNestedSerializer):
|
||||
url = serializers.HyperlinkedIdentityField(
|
||||
view_name='vpn-api:ipsecprofile-detail'
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = models.IPSecProfile
|
||||
fields = ('id', 'url', 'display', 'name')
|
193
netbox/vpn/api/serializers.py
Normal file
193
netbox/vpn/api/serializers.py
Normal file
@ -0,0 +1,193 @@
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
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, SerializedPKRelatedField
|
||||
from netbox.api.serializers import NetBoxModelSerializer
|
||||
from netbox.constants import NESTED_SERIALIZER_PREFIX
|
||||
from tenancy.api.nested_serializers import NestedTenantSerializer
|
||||
from utilities.api import get_serializer_for_model
|
||||
from vpn.choices import *
|
||||
from vpn.models import *
|
||||
from .nested_serializers import *
|
||||
|
||||
__all__ = (
|
||||
'IKEPolicySerializer',
|
||||
'IKEProposalSerializer',
|
||||
'IPSecPolicySerializer',
|
||||
'IPSecProfileSerializer',
|
||||
'IPSecProposalSerializer',
|
||||
'TunnelSerializer',
|
||||
'TunnelTerminationSerializer',
|
||||
)
|
||||
|
||||
|
||||
class TunnelSerializer(NetBoxModelSerializer):
|
||||
url = serializers.HyperlinkedIdentityField(
|
||||
view_name='vpn-api:tunnel-detail'
|
||||
)
|
||||
status = ChoiceField(
|
||||
choices=TunnelStatusChoices
|
||||
)
|
||||
encapsulation = ChoiceField(
|
||||
choices=TunnelEncapsulationChoices
|
||||
)
|
||||
ipsec_profile = NestedIPSecProfileSerializer(
|
||||
required=False,
|
||||
allow_null=True
|
||||
)
|
||||
tenant = NestedTenantSerializer(
|
||||
required=False,
|
||||
allow_null=True
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = Tunnel
|
||||
fields = (
|
||||
'id', 'url', 'display', 'name', 'status', 'encapsulation', 'ipsec_profile', 'tenant', 'tunnel_id',
|
||||
'description', 'comments', 'tags', 'custom_fields', 'created', 'last_updated',
|
||||
)
|
||||
|
||||
|
||||
class TunnelTerminationSerializer(NetBoxModelSerializer):
|
||||
url = serializers.HyperlinkedIdentityField(
|
||||
view_name='vpn-api:tunneltermination-detail'
|
||||
)
|
||||
tunnel = NestedTunnelSerializer()
|
||||
role = ChoiceField(
|
||||
choices=TunnelTerminationRoleChoices
|
||||
)
|
||||
termination_type = ContentTypeField(
|
||||
queryset=ContentType.objects.all()
|
||||
)
|
||||
termination = serializers.SerializerMethodField(
|
||||
read_only=True
|
||||
)
|
||||
outside_ip = NestedIPAddressSerializer(
|
||||
required=False,
|
||||
allow_null=True
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = TunnelTermination
|
||||
fields = (
|
||||
'id', 'url', 'display', 'tunnel', 'role', 'termination_type', 'termination_id', 'termination', 'outside_ip',
|
||||
'tags', 'custom_fields', 'created', 'last_updated',
|
||||
)
|
||||
|
||||
@extend_schema_field(serializers.JSONField(allow_null=True))
|
||||
def get_termination(self, obj):
|
||||
serializer = get_serializer_for_model(obj.termination, prefix=NESTED_SERIALIZER_PREFIX)
|
||||
context = {'request': self.context['request']}
|
||||
return serializer(obj.termination, 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
|
||||
)
|
||||
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', '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
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = IPSecProposal
|
||||
fields = (
|
||||
'id', 'url', 'display', 'name', 'description', 'encryption_algorithm', 'authentication_algorithm',
|
||||
'sa_lifetime_seconds', 'sa_lifetime_data', '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,
|
||||
required=False
|
||||
)
|
||||
|
||||
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'
|
||||
)
|
||||
mode = ChoiceField(
|
||||
choices=IPSecModeChoices
|
||||
)
|
||||
ike_policy = NestedIKEPolicySerializer()
|
||||
ipsec_policy = NestedIPSecPolicySerializer()
|
||||
|
||||
class Meta:
|
||||
model = IPSecProfile
|
||||
fields = (
|
||||
'id', 'url', 'display', 'name', 'description', 'mode', 'ike_policy', 'ipsec_policy', 'comments', 'tags',
|
||||
'custom_fields', 'created', 'last_updated',
|
||||
)
|
15
netbox/vpn/api/urls.py
Normal file
15
netbox/vpn/api/urls.py
Normal file
@ -0,0 +1,15 @@
|
||||
from netbox.api.routers import NetBoxRouter
|
||||
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)
|
||||
|
||||
app_name = 'vpn-api'
|
||||
urlpatterns = router.urls
|
74
netbox/vpn/api/views.py
Normal file
74
netbox/vpn/api/views.py
Normal file
@ -0,0 +1,74 @@
|
||||
from rest_framework.routers import APIRootView
|
||||
|
||||
from netbox.api.viewsets import NetBoxModelViewSet
|
||||
from utilities.utils import count_related
|
||||
from vpn import filtersets
|
||||
from vpn.models import *
|
||||
from . import serializers
|
||||
|
||||
__all__ = (
|
||||
'IKEPolicyViewSet',
|
||||
'IKEProposalViewSet',
|
||||
'IPSecPolicyViewSet',
|
||||
'IPSecProfileViewSet',
|
||||
'IPSecProposalViewSet',
|
||||
'TunnelTerminationViewSet',
|
||||
'TunnelViewSet',
|
||||
'VPNRootView',
|
||||
)
|
||||
|
||||
|
||||
class VPNRootView(APIRootView):
|
||||
"""
|
||||
VPN API root view
|
||||
"""
|
||||
def get_view_name(self):
|
||||
return 'VPN'
|
||||
|
||||
|
||||
#
|
||||
# Viewsets
|
||||
#
|
||||
|
||||
class TunnelViewSet(NetBoxModelViewSet):
|
||||
queryset = Tunnel.objects.prefetch_related('ipsec_profile', 'tenant').annotate(
|
||||
terminations_count=count_related(TunnelTermination, 'tunnel')
|
||||
)
|
||||
serializer_class = serializers.TunnelSerializer
|
||||
filterset_class = filtersets.TunnelFilterSet
|
||||
|
||||
|
||||
class TunnelTerminationViewSet(NetBoxModelViewSet):
|
||||
queryset = TunnelTermination.objects.prefetch_related('tunnel')
|
||||
serializer_class = serializers.TunnelTerminationSerializer
|
||||
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 = IPSecPolicy.objects.all()
|
||||
serializer_class = serializers.IPSecPolicySerializer
|
||||
filterset_class = filtersets.IPSecPolicyFilterSet
|
||||
|
||||
|
||||
class IPSecProfileViewSet(NetBoxModelViewSet):
|
||||
queryset = IPSecProfile.objects.all()
|
||||
serializer_class = serializers.IPSecProfileSerializer
|
||||
filterset_class = filtersets.IPSecProfileFilterSet
|
9
netbox/vpn/apps.py
Normal file
9
netbox/vpn/apps.py
Normal file
@ -0,0 +1,9 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class VPNConfig(AppConfig):
|
||||
name = 'vpn'
|
||||
verbose_name = 'VPN'
|
||||
|
||||
def ready(self):
|
||||
from . import search
|
201
netbox/vpn/choices.py
Normal file
201
netbox/vpn/choices.py
Normal file
@ -0,0 +1,201 @@
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from utilities.choices import ChoiceSet
|
||||
|
||||
|
||||
#
|
||||
# Tunnels
|
||||
#
|
||||
|
||||
class TunnelStatusChoices(ChoiceSet):
|
||||
key = 'Tunnel.status'
|
||||
|
||||
STATUS_PLANNED = 'planned'
|
||||
STATUS_ACTIVE = 'active'
|
||||
STATUS_DISABLED = 'disabled'
|
||||
|
||||
CHOICES = [
|
||||
(STATUS_PLANNED, _('Planned'), 'cyan'),
|
||||
(STATUS_ACTIVE, _('Active'), 'green'),
|
||||
(STATUS_DISABLED, _('Disabled'), 'red'),
|
||||
]
|
||||
|
||||
|
||||
class TunnelEncapsulationChoices(ChoiceSet):
|
||||
ENCAP_GRE = 'gre'
|
||||
ENCAP_IP_IP = 'ip-ip'
|
||||
ENCAP_IPSEC_TRANSPORT = 'ipsec-transport'
|
||||
ENCAP_IPSEC_TUNNEL = 'ipsec-tunnel'
|
||||
|
||||
CHOICES = [
|
||||
(ENCAP_IPSEC_TRANSPORT, _('IPsec - Transport')),
|
||||
(ENCAP_IPSEC_TUNNEL, _('IPsec - Tunnel')),
|
||||
(ENCAP_IP_IP, _('IP-in-IP')),
|
||||
(ENCAP_GRE, _('GRE')),
|
||||
]
|
||||
|
||||
|
||||
class TunnelTerminationTypeChoices(ChoiceSet):
|
||||
# For TunnelCreateForm
|
||||
TYPE_DEVICE = 'dcim.device'
|
||||
TYPE_VIRUTALMACHINE = 'virtualization.virtualmachine'
|
||||
|
||||
CHOICES = (
|
||||
(TYPE_DEVICE, _('Device')),
|
||||
(TYPE_VIRUTALMACHINE, _('Virtual Machine')),
|
||||
)
|
||||
|
||||
|
||||
class TunnelTerminationRoleChoices(ChoiceSet):
|
||||
ROLE_PEER = 'peer'
|
||||
ROLE_HUB = 'hub'
|
||||
ROLE_SPOKE = 'spoke'
|
||||
|
||||
CHOICES = [
|
||||
(ROLE_PEER, _('Peer'), 'green'),
|
||||
(ROLE_HUB, _('Hub'), 'blue'),
|
||||
(ROLE_SPOKE, _('Spoke'), 'orange'),
|
||||
]
|
||||
|
||||
|
||||
#
|
||||
# Crypto
|
||||
#
|
||||
|
||||
class IKEVersionChoices(ChoiceSet):
|
||||
VERSION_1 = 1
|
||||
VERSION_2 = 2
|
||||
|
||||
CHOICES = (
|
||||
(VERSION_1, 'IKEv1'),
|
||||
(VERSION_2, 'IKEv2'),
|
||||
)
|
||||
|
||||
|
||||
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'
|
||||
ENCRYPTION_AES192_GCM = 'aes-192-gcm'
|
||||
ENCRYPTION_AES256_CBC = 'aes-256-cbc'
|
||||
ENCRYPTION_AES256_GCM = 'aes-256-gcm'
|
||||
ENCRYPTION_3DES = '3des-cbc'
|
||||
ENCRYPTION_DES = 'des-cbc'
|
||||
|
||||
CHOICES = (
|
||||
(ENCRYPTION_AES128_CBC, '128-bit AES (CBC)'),
|
||||
(ENCRYPTION_AES128_GCM, '128-bit AES (GCM)'),
|
||||
(ENCRYPTION_AES192_CBC, '192-bit AES (CBC)'),
|
||||
(ENCRYPTION_AES192_GCM, '192-bit AES (GCM)'),
|
||||
(ENCRYPTION_AES256_CBC, '256-bit AES (CBC)'),
|
||||
(ENCRYPTION_AES256_GCM, '256-bit AES (GCM)'),
|
||||
(ENCRYPTION_3DES, '3DES'),
|
||||
(ENCRYPTION_3DES, 'DES'),
|
||||
)
|
||||
|
||||
|
||||
class AuthenticationAlgorithmChoices(ChoiceSet):
|
||||
AUTH_HMAC_SHA1 = 'hmac-sha1'
|
||||
AUTH_HMAC_SHA256 = 'hmac-sha256'
|
||||
AUTH_HMAC_SHA384 = 'hmac-sha384'
|
||||
AUTH_HMAC_SHA512 = 'hmac-sha512'
|
||||
AUTH_HMAC_MD5 = 'hmac-md5'
|
||||
|
||||
CHOICES = (
|
||||
(AUTH_HMAC_SHA1, 'SHA-1 HMAC'),
|
||||
(AUTH_HMAC_SHA256, 'SHA-256 HMAC'),
|
||||
(AUTH_HMAC_SHA384, 'SHA-384 HMAC'),
|
||||
(AUTH_HMAC_SHA512, 'SHA-512 HMAC'),
|
||||
(AUTH_HMAC_MD5, 'MD5 HMAC'),
|
||||
)
|
||||
|
||||
|
||||
class DHGroupChoices(ChoiceSet):
|
||||
# https://www.iana.org/assignments/ikev2-parameters/ikev2-parameters.xhtml#ikev2-parameters-8
|
||||
GROUP_1 = 1 # 768-bit MODP
|
||||
GROUP_2 = 2 # 1024-but MODP
|
||||
# Groups 3-4 reserved
|
||||
GROUP_5 = 5 # 1536-bit MODP
|
||||
# Groups 6-13 unassigned
|
||||
GROUP_14 = 14 # 2048-bit MODP
|
||||
GROUP_15 = 15 # 3072-bit MODP
|
||||
GROUP_16 = 16 # 4096-bit MODP
|
||||
GROUP_17 = 17 # 6144-bit MODP
|
||||
GROUP_18 = 18 # 8192-bit MODP
|
||||
GROUP_19 = 19 # 256-bit random ECP
|
||||
GROUP_20 = 20 # 384-bit random ECP
|
||||
GROUP_21 = 21 # 521-bit random ECP (521 is not a typo)
|
||||
GROUP_22 = 22 # 1024-bit MODP w/160-bit prime
|
||||
GROUP_23 = 23 # 2048-bit MODP w/224-bit prime
|
||||
GROUP_24 = 24 # 2048-bit MODP w/256-bit prime
|
||||
GROUP_25 = 25 # 192-bit ECP
|
||||
GROUP_26 = 26 # 224-bit ECP
|
||||
GROUP_27 = 27 # brainpoolP224r1
|
||||
GROUP_28 = 28 # brainpoolP256r1
|
||||
GROUP_29 = 29 # brainpoolP384r1
|
||||
GROUP_30 = 30 # brainpoolP512r1
|
||||
GROUP_31 = 31 # Curve25519
|
||||
GROUP_32 = 32 # Curve448
|
||||
GROUP_33 = 33 # GOST3410_2012_256
|
||||
GROUP_34 = 34 # GOST3410_2012_512
|
||||
|
||||
CHOICES = (
|
||||
# Strings are formatted in this manner to optimize translations
|
||||
(GROUP_1, _('Group {n}').format(n=1)),
|
||||
(GROUP_2, _('Group {n}').format(n=2)),
|
||||
(GROUP_5, _('Group {n}').format(n=5)),
|
||||
(GROUP_14, _('Group {n}').format(n=14)),
|
||||
(GROUP_16, _('Group {n}').format(n=16)),
|
||||
(GROUP_17, _('Group {n}').format(n=17)),
|
||||
(GROUP_18, _('Group {n}').format(n=18)),
|
||||
(GROUP_19, _('Group {n}').format(n=19)),
|
||||
(GROUP_20, _('Group {n}').format(n=20)),
|
||||
(GROUP_21, _('Group {n}').format(n=21)),
|
||||
(GROUP_22, _('Group {n}').format(n=22)),
|
||||
(GROUP_23, _('Group {n}').format(n=23)),
|
||||
(GROUP_24, _('Group {n}').format(n=24)),
|
||||
(GROUP_25, _('Group {n}').format(n=25)),
|
||||
(GROUP_26, _('Group {n}').format(n=26)),
|
||||
(GROUP_27, _('Group {n}').format(n=27)),
|
||||
(GROUP_28, _('Group {n}').format(n=28)),
|
||||
(GROUP_29, _('Group {n}').format(n=29)),
|
||||
(GROUP_30, _('Group {n}').format(n=30)),
|
||||
(GROUP_31, _('Group {n}').format(n=31)),
|
||||
(GROUP_32, _('Group {n}').format(n=32)),
|
||||
(GROUP_33, _('Group {n}').format(n=33)),
|
||||
(GROUP_34, _('Group {n}').format(n=34)),
|
||||
)
|
241
netbox/vpn/filtersets.py
Normal file
241
netbox/vpn/filtersets.py
Normal file
@ -0,0 +1,241 @@
|
||||
import django_filters
|
||||
from django.db.models import Q
|
||||
from django.utils.translation import gettext as _
|
||||
|
||||
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, MultiValueCharFilter, MultiValueNumberFilter
|
||||
from virtualization.models import VMInterface
|
||||
from .choices import *
|
||||
from .models import *
|
||||
|
||||
__all__ = (
|
||||
'IKEPolicyFilterSet',
|
||||
'IKEProposalFilterSet',
|
||||
'IPSecPolicyFilterSet',
|
||||
'IPSecProfileFilterSet',
|
||||
'IPSecProposalFilterSet',
|
||||
'TunnelFilterSet',
|
||||
'TunnelTerminationFilterSet',
|
||||
)
|
||||
|
||||
|
||||
class TunnelFilterSet(NetBoxModelFilterSet, TenancyFilterSet):
|
||||
status = django_filters.MultipleChoiceFilter(
|
||||
choices=TunnelStatusChoices
|
||||
)
|
||||
encapsulation = django_filters.MultipleChoiceFilter(
|
||||
choices=TunnelEncapsulationChoices
|
||||
)
|
||||
ipsec_profile_id = django_filters.ModelMultipleChoiceFilter(
|
||||
queryset=IPSecProfile.objects.all(),
|
||||
label=_('IPSec profile (ID)'),
|
||||
)
|
||||
ipsec_profile = django_filters.ModelMultipleChoiceFilter(
|
||||
field_name='ipsec_profile__name',
|
||||
queryset=IPSecProfile.objects.all(),
|
||||
to_field_name='name',
|
||||
label=_('IPSec profile (name)'),
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = Tunnel
|
||||
fields = ['id', 'name', 'tunnel_id']
|
||||
|
||||
def search(self, queryset, name, value):
|
||||
if not value.strip():
|
||||
return queryset
|
||||
return queryset.filter(
|
||||
Q(name__icontains=value) |
|
||||
Q(description__icontains=value) |
|
||||
Q(comments__icontains=value)
|
||||
)
|
||||
|
||||
|
||||
class TunnelTerminationFilterSet(NetBoxModelFilterSet):
|
||||
tunnel_id = django_filters.ModelMultipleChoiceFilter(
|
||||
field_name='tunnel',
|
||||
queryset=Tunnel.objects.all(),
|
||||
label=_('Tunnel (ID)'),
|
||||
)
|
||||
tunnel = django_filters.ModelMultipleChoiceFilter(
|
||||
field_name='tunnel__name',
|
||||
queryset=Tunnel.objects.all(),
|
||||
to_field_name='name',
|
||||
label=_('Tunnel (name)'),
|
||||
)
|
||||
role = django_filters.MultipleChoiceFilter(
|
||||
choices=TunnelTerminationRoleChoices
|
||||
)
|
||||
termination_type = ContentTypeFilter()
|
||||
interface = django_filters.ModelMultipleChoiceFilter(
|
||||
field_name='interface__name',
|
||||
queryset=Interface.objects.all(),
|
||||
to_field_name='name',
|
||||
label=_('Interface (name)'),
|
||||
)
|
||||
interface_id = django_filters.ModelMultipleChoiceFilter(
|
||||
field_name='interface',
|
||||
queryset=Interface.objects.all(),
|
||||
label=_('Interface (ID)'),
|
||||
)
|
||||
vminterface = django_filters.ModelMultipleChoiceFilter(
|
||||
field_name='vminterface__name',
|
||||
queryset=VMInterface.objects.all(),
|
||||
to_field_name='name',
|
||||
label=_('VM interface (name)'),
|
||||
)
|
||||
vminterface_id = django_filters.ModelMultipleChoiceFilter(
|
||||
field_name='vminterface',
|
||||
queryset=VMInterface.objects.all(),
|
||||
label=_('VM interface (ID)'),
|
||||
)
|
||||
outside_ip_id = django_filters.ModelMultipleChoiceFilter(
|
||||
field_name='outside_ip',
|
||||
queryset=IPAddress.objects.all(),
|
||||
label=_('Outside IP (ID)'),
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = TunnelTermination
|
||||
fields = ['id']
|
||||
|
||||
|
||||
class IKEProposalFilterSet(NetBoxModelFilterSet):
|
||||
authentication_method = django_filters.MultipleChoiceFilter(
|
||||
choices=AuthenticationMethodChoices
|
||||
)
|
||||
encryption_algorithm = django_filters.MultipleChoiceFilter(
|
||||
choices=EncryptionAlgorithmChoices
|
||||
)
|
||||
authentication_algorithm = django_filters.MultipleChoiceFilter(
|
||||
choices=AuthenticationAlgorithmChoices
|
||||
)
|
||||
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'
|
||||
)
|
||||
proposal = MultiValueCharFilter(
|
||||
field_name='proposals__name'
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = IKEPolicy
|
||||
fields = ['id', 'name', 'preshared_key']
|
||||
|
||||
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'
|
||||
)
|
||||
proposal = MultiValueCharFilter(
|
||||
field_name='proposals__name'
|
||||
)
|
||||
|
||||
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']
|
||||
|
||||
def search(self, queryset, name, value):
|
||||
if not value.strip():
|
||||
return queryset
|
||||
return queryset.filter(
|
||||
Q(name__icontains=value) |
|
||||
Q(description__icontains=value) |
|
||||
Q(comments__icontains=value)
|
||||
)
|
4
netbox/vpn/forms/__init__.py
Normal file
4
netbox/vpn/forms/__init__.py
Normal file
@ -0,0 +1,4 @@
|
||||
from .bulk_edit import *
|
||||
from .bulk_import import *
|
||||
from .filtersets import *
|
||||
from .model_forms import *
|
243
netbox/vpn/forms/bulk_edit.py
Normal file
243
netbox/vpn/forms/bulk_edit.py
Normal file
@ -0,0 +1,243 @@
|
||||
from django import forms
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from netbox.forms import NetBoxModelBulkEditForm
|
||||
from tenancy.models import Tenant
|
||||
from utilities.forms import add_blank_choice
|
||||
from utilities.forms.fields import CommentField, DynamicModelChoiceField, DynamicModelMultipleChoiceField
|
||||
from vpn.choices import *
|
||||
from vpn.models import *
|
||||
|
||||
__all__ = (
|
||||
'IKEPolicyBulkEditForm',
|
||||
'IKEProposalBulkEditForm',
|
||||
'IPSecPolicyBulkEditForm',
|
||||
'IPSecProfileBulkEditForm',
|
||||
'IPSecProposalBulkEditForm',
|
||||
'TunnelBulkEditForm',
|
||||
'TunnelTerminationBulkEditForm',
|
||||
)
|
||||
|
||||
|
||||
class TunnelBulkEditForm(NetBoxModelBulkEditForm):
|
||||
status = forms.ChoiceField(
|
||||
label=_('Status'),
|
||||
choices=add_blank_choice(TunnelStatusChoices),
|
||||
required=False
|
||||
)
|
||||
encapsulation = forms.ChoiceField(
|
||||
label=_('Encapsulation'),
|
||||
choices=add_blank_choice(TunnelEncapsulationChoices),
|
||||
required=False
|
||||
)
|
||||
ipsec_profile = DynamicModelMultipleChoiceField(
|
||||
queryset=IPSecProfile.objects.all(),
|
||||
label=_('IPSec profile'),
|
||||
required=False
|
||||
)
|
||||
tenant = DynamicModelChoiceField(
|
||||
label=_('Tenant'),
|
||||
queryset=Tenant.objects.all(),
|
||||
required=False
|
||||
)
|
||||
description = forms.CharField(
|
||||
label=_('Description'),
|
||||
max_length=200,
|
||||
required=False
|
||||
)
|
||||
tunnel_id = forms.IntegerField(
|
||||
label=_('Tunnel ID'),
|
||||
required=False
|
||||
)
|
||||
comments = CommentField()
|
||||
|
||||
model = Tunnel
|
||||
fieldsets = (
|
||||
(_('Tunnel'), ('status', 'encapsulation', 'tunnel_id', 'description')),
|
||||
(_('Security'), ('ipsec_profile',)),
|
||||
(_('Tenancy'), ('tenant',)),
|
||||
)
|
||||
nullable_fields = (
|
||||
'ipsec_profile', 'tunnel_id', 'tenant', 'description', 'comments',
|
||||
)
|
||||
|
||||
|
||||
class TunnelTerminationBulkEditForm(NetBoxModelBulkEditForm):
|
||||
role = forms.ChoiceField(
|
||||
label=_('Role'),
|
||||
choices=add_blank_choice(TunnelTerminationRoleChoices),
|
||||
required=False
|
||||
)
|
||||
|
||||
model = TunnelTermination
|
||||
|
||||
|
||||
class IKEProposalBulkEditForm(NetBoxModelBulkEditForm):
|
||||
authentication_method = forms.ChoiceField(
|
||||
label=_('Authentication method'),
|
||||
choices=add_blank_choice(AuthenticationMethodChoices),
|
||||
required=False
|
||||
)
|
||||
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(
|
||||
label=_('SA lifetime'),
|
||||
required=False
|
||||
)
|
||||
description = forms.CharField(
|
||||
label=_('Description'),
|
||||
max_length=200,
|
||||
required=False
|
||||
)
|
||||
comments = CommentField()
|
||||
|
||||
model = IKEProposal
|
||||
fieldsets = (
|
||||
(None, (
|
||||
'authentication_method', 'encryption_algorithm', 'authentication_algorithm', 'group', 'sa_lifetime',
|
||||
'description',
|
||||
)),
|
||||
)
|
||||
nullable_fields = (
|
||||
'sa_lifetime', 'description', '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
|
||||
)
|
||||
description = forms.CharField(
|
||||
label=_('Description'),
|
||||
max_length=200,
|
||||
required=False
|
||||
)
|
||||
comments = CommentField()
|
||||
|
||||
model = IKEPolicy
|
||||
fieldsets = (
|
||||
(None, (
|
||||
'version', 'mode', 'preshared_key', 'description',
|
||||
)),
|
||||
)
|
||||
nullable_fields = (
|
||||
'preshared_key', 'description', '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(
|
||||
label=_('SA lifetime (seconds)'),
|
||||
required=False
|
||||
)
|
||||
sa_lifetime_data = forms.IntegerField(
|
||||
label=_('SA lifetime (KB)'),
|
||||
required=False
|
||||
)
|
||||
description = forms.CharField(
|
||||
label=_('Description'),
|
||||
max_length=200,
|
||||
required=False
|
||||
)
|
||||
comments = CommentField()
|
||||
|
||||
model = IPSecProposal
|
||||
fieldsets = (
|
||||
(None, (
|
||||
'encryption_algorithm', 'authentication_algorithm', 'sa_lifetime_seconds', 'sa_lifetime_data',
|
||||
'description',
|
||||
)),
|
||||
)
|
||||
nullable_fields = (
|
||||
'sa_lifetime_seconds', 'sa_lifetime_data', 'description', 'comments',
|
||||
)
|
||||
|
||||
|
||||
class IPSecPolicyBulkEditForm(NetBoxModelBulkEditForm):
|
||||
pfs_group = forms.ChoiceField(
|
||||
label=_('PFS group'),
|
||||
choices=add_blank_choice(DHGroupChoices),
|
||||
required=False
|
||||
)
|
||||
description = forms.CharField(
|
||||
label=_('Description'),
|
||||
max_length=200,
|
||||
required=False
|
||||
)
|
||||
comments = CommentField()
|
||||
|
||||
model = IPSecPolicy
|
||||
fieldsets = (
|
||||
(None, ('pfs_group', 'description',)),
|
||||
)
|
||||
nullable_fields = (
|
||||
'pfs_group', 'description', 'comments',
|
||||
)
|
||||
|
||||
|
||||
class IPSecProfileBulkEditForm(NetBoxModelBulkEditForm):
|
||||
mode = forms.ChoiceField(
|
||||
label=_('Mode'),
|
||||
choices=add_blank_choice(IPSecModeChoices),
|
||||
required=False
|
||||
)
|
||||
ike_policy = DynamicModelChoiceField(
|
||||
label=_('IKE policy'),
|
||||
queryset=IKEPolicy.objects.all(),
|
||||
required=False
|
||||
)
|
||||
ipsec_policy = DynamicModelChoiceField(
|
||||
label=_('IPSec policy'),
|
||||
queryset=IPSecPolicy.objects.all(),
|
||||
required=False
|
||||
)
|
||||
description = forms.CharField(
|
||||
label=_('Description'),
|
||||
max_length=200,
|
||||
required=False
|
||||
)
|
||||
comments = CommentField()
|
||||
|
||||
model = IPSecProfile
|
||||
fieldsets = (
|
||||
(_('Profile'), (
|
||||
'mode', 'ike_policy', 'ipsec_policy', 'description',
|
||||
)),
|
||||
)
|
||||
nullable_fields = (
|
||||
'description', 'comments',
|
||||
)
|
230
netbox/vpn/forms/bulk_import.py
Normal file
230
netbox/vpn/forms/bulk_import.py
Normal file
@ -0,0 +1,230 @@
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from dcim.models import Device, Interface
|
||||
from ipam.models import IPAddress
|
||||
from netbox.forms import NetBoxModelImportForm
|
||||
from tenancy.models import Tenant
|
||||
from utilities.forms.fields import CSVChoiceField, CSVModelChoiceField, CSVModelMultipleChoiceField
|
||||
from virtualization.models import VirtualMachine, VMInterface
|
||||
from vpn.choices import *
|
||||
from vpn.models import *
|
||||
|
||||
__all__ = (
|
||||
'IKEPolicyImportForm',
|
||||
'IKEProposalImportForm',
|
||||
'IPSecPolicyImportForm',
|
||||
'IPSecProfileImportForm',
|
||||
'IPSecProposalImportForm',
|
||||
'TunnelImportForm',
|
||||
'TunnelTerminationImportForm',
|
||||
)
|
||||
|
||||
|
||||
class TunnelImportForm(NetBoxModelImportForm):
|
||||
status = CSVChoiceField(
|
||||
label=_('Status'),
|
||||
choices=TunnelStatusChoices,
|
||||
help_text=_('Operational status')
|
||||
)
|
||||
encapsulation = CSVChoiceField(
|
||||
label=_('Encapsulation'),
|
||||
choices=TunnelEncapsulationChoices,
|
||||
help_text=_('Tunnel encapsulation')
|
||||
)
|
||||
ipsec_profile = CSVModelChoiceField(
|
||||
label=_('IPSec profile'),
|
||||
queryset=IPSecProfile.objects.all(),
|
||||
required=False,
|
||||
to_field_name='name'
|
||||
)
|
||||
tenant = CSVModelChoiceField(
|
||||
label=_('Tenant'),
|
||||
queryset=Tenant.objects.all(),
|
||||
required=False,
|
||||
to_field_name='name',
|
||||
help_text=_('Assigned tenant')
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = Tunnel
|
||||
fields = (
|
||||
'name', 'status', 'encapsulation', 'ipsec_profile', 'tenant', 'tunnel_id', 'description', 'comments',
|
||||
'tags',
|
||||
)
|
||||
|
||||
|
||||
class TunnelTerminationImportForm(NetBoxModelImportForm):
|
||||
tunnel = CSVModelChoiceField(
|
||||
label=_('Tunnel'),
|
||||
queryset=Tunnel.objects.all(),
|
||||
to_field_name='name'
|
||||
)
|
||||
role = CSVChoiceField(
|
||||
label=_('Role'),
|
||||
choices=TunnelTerminationRoleChoices,
|
||||
help_text=_('Operational role')
|
||||
)
|
||||
device = CSVModelChoiceField(
|
||||
label=_('Device'),
|
||||
queryset=Device.objects.all(),
|
||||
required=False,
|
||||
to_field_name='name',
|
||||
help_text=_('Parent device of assigned interface')
|
||||
)
|
||||
virtual_machine = CSVModelChoiceField(
|
||||
label=_('Virtual machine'),
|
||||
queryset=VirtualMachine.objects.all(),
|
||||
required=False,
|
||||
to_field_name='name',
|
||||
help_text=_('Parent VM of assigned interface')
|
||||
)
|
||||
termination = CSVModelChoiceField(
|
||||
label=_('Termination'),
|
||||
queryset=Interface.objects.none(), # Can also refer to VMInterface
|
||||
required=False,
|
||||
to_field_name='name',
|
||||
help_text=_('Device or virtual machine interface')
|
||||
)
|
||||
outside_ip = CSVModelChoiceField(
|
||||
label=_('Outside IP'),
|
||||
queryset=IPAddress.objects.all(),
|
||||
required=False,
|
||||
to_field_name='name'
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = TunnelTermination
|
||||
fields = (
|
||||
'tunnel', 'role', 'outside_ip', 'tags',
|
||||
)
|
||||
|
||||
def __init__(self, data=None, *args, **kwargs):
|
||||
super().__init__(data, *args, **kwargs)
|
||||
|
||||
if data:
|
||||
|
||||
# Limit termination queryset by assigned device/VM
|
||||
if data.get('device'):
|
||||
self.fields['termination'].queryset = Interface.objects.filter(
|
||||
**{f"device__{self.fields['device'].to_field_name}": data['device']}
|
||||
)
|
||||
elif data.get('virtual_machine'):
|
||||
self.fields['termination'].queryset = VMInterface.objects.filter(
|
||||
**{f"virtual_machine__{self.fields['virtual_machine'].to_field_name}": data['virtual_machine']}
|
||||
)
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
|
||||
# Assign termination object
|
||||
if self.cleaned_data.get('termination'):
|
||||
self.instance.termination = self.cleaned_data['termination']
|
||||
|
||||
return super().save(*args, **kwargs)
|
||||
|
||||
|
||||
class IKEProposalImportForm(NetBoxModelImportForm):
|
||||
authentication_method = CSVChoiceField(
|
||||
label=_('Authentication method'),
|
||||
choices=AuthenticationMethodChoices
|
||||
)
|
||||
encryption_algorithm = CSVChoiceField(
|
||||
label=_('Encryption algorithm'),
|
||||
choices=EncryptionAlgorithmChoices
|
||||
)
|
||||
authentication_algorithm = CSVChoiceField(
|
||||
label=_('Authentication algorithm'),
|
||||
choices=AuthenticationAlgorithmChoices
|
||||
)
|
||||
group = CSVChoiceField(
|
||||
label=_('Group'),
|
||||
choices=DHGroupChoices
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = IKEProposal
|
||||
fields = (
|
||||
'name', 'description', 'authentication_method', 'encryption_algorithm', 'authentication_algorithm',
|
||||
'group', 'sa_lifetime', 'tags',
|
||||
)
|
||||
|
||||
|
||||
class IKEPolicyImportForm(NetBoxModelImportForm):
|
||||
version = CSVChoiceField(
|
||||
label=_('Version'),
|
||||
choices=IKEVersionChoices
|
||||
)
|
||||
mode = CSVChoiceField(
|
||||
label=_('Mode'),
|
||||
choices=IKEModeChoices
|
||||
)
|
||||
proposals = CSVModelMultipleChoiceField(
|
||||
queryset=IKEProposal.objects.all(),
|
||||
to_field_name='name',
|
||||
help_text=_('IKE proposal(s)'),
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = IKEPolicy
|
||||
fields = (
|
||||
'name', 'description', 'version', 'mode', 'proposals', 'preshared_key', 'tags',
|
||||
)
|
||||
|
||||
|
||||
class IPSecProposalImportForm(NetBoxModelImportForm):
|
||||
encryption_algorithm = CSVChoiceField(
|
||||
label=_('Encryption algorithm'),
|
||||
choices=EncryptionAlgorithmChoices
|
||||
)
|
||||
authentication_algorithm = CSVChoiceField(
|
||||
label=_('Authentication algorithm'),
|
||||
choices=AuthenticationAlgorithmChoices
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = IPSecProposal
|
||||
fields = (
|
||||
'name', 'description', 'encryption_algorithm', 'authentication_algorithm', 'sa_lifetime_seconds',
|
||||
'sa_lifetime_data', 'tags',
|
||||
)
|
||||
|
||||
|
||||
class IPSecPolicyImportForm(NetBoxModelImportForm):
|
||||
pfs_group = CSVChoiceField(
|
||||
label=_('Diffie-Hellman group for Perfect Forward Secrecy'),
|
||||
choices=DHGroupChoices
|
||||
)
|
||||
proposals = CSVModelMultipleChoiceField(
|
||||
queryset=IPSecProposal.objects.all(),
|
||||
to_field_name='name',
|
||||
help_text=_('IPSec proposal(s)'),
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = IPSecPolicy
|
||||
fields = (
|
||||
'name', 'description', 'proposals', 'pfs_group', 'tags',
|
||||
)
|
||||
|
||||
|
||||
class IPSecProfileImportForm(NetBoxModelImportForm):
|
||||
mode = CSVChoiceField(
|
||||
label=_('Mode'),
|
||||
choices=IPSecModeChoices,
|
||||
help_text=_('IPSec protocol')
|
||||
)
|
||||
ike_policy = CSVModelChoiceField(
|
||||
label=_('IKE policy'),
|
||||
queryset=IKEPolicy.objects.all(),
|
||||
to_field_name='name'
|
||||
)
|
||||
ipsec_policy = CSVModelChoiceField(
|
||||
label=_('IPSec policy'),
|
||||
queryset=IPSecPolicy.objects.all(),
|
||||
to_field_name='name'
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = IPSecProfile
|
||||
fields = (
|
||||
'name', 'mode', 'ike_policy', 'ipsec_policy', 'description', 'comments', 'tags',
|
||||
)
|
182
netbox/vpn/forms/filtersets.py
Normal file
182
netbox/vpn/forms/filtersets.py
Normal file
@ -0,0 +1,182 @@
|
||||
from django import forms
|
||||
from django.utils.translation import gettext as _
|
||||
|
||||
from netbox.forms import NetBoxModelFilterSetForm
|
||||
from tenancy.forms import TenancyFilterForm
|
||||
from utilities.forms.fields import DynamicModelMultipleChoiceField, TagFilterField
|
||||
from vpn.choices import *
|
||||
from vpn.models import *
|
||||
|
||||
__all__ = (
|
||||
'IKEPolicyFilterForm',
|
||||
'IKEProposalFilterForm',
|
||||
'IPSecPolicyFilterForm',
|
||||
'IPSecProfileFilterForm',
|
||||
'IPSecProposalFilterForm',
|
||||
'TunnelFilterForm',
|
||||
'TunnelTerminationFilterForm',
|
||||
)
|
||||
|
||||
|
||||
class TunnelFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm):
|
||||
model = Tunnel
|
||||
fieldsets = (
|
||||
(None, ('q', 'filter_id', 'tag')),
|
||||
(_('Tunnel'), ('status', 'encapsulation', 'tunnel_id')),
|
||||
(_('Security'), ('ipsec_profile_id',)),
|
||||
(_('Tenancy'), ('tenant_group_id', 'tenant_id')),
|
||||
)
|
||||
status = forms.MultipleChoiceField(
|
||||
label=_('Status'),
|
||||
choices=TunnelStatusChoices,
|
||||
required=False
|
||||
)
|
||||
encapsulation = forms.MultipleChoiceField(
|
||||
label=_('Encapsulation'),
|
||||
choices=TunnelEncapsulationChoices,
|
||||
required=False
|
||||
)
|
||||
ipsec_profile_id = DynamicModelMultipleChoiceField(
|
||||
queryset=IPSecProfile.objects.all(),
|
||||
required=False,
|
||||
label=_('IPSec profile')
|
||||
)
|
||||
tunnel_id = forms.IntegerField(
|
||||
required=False,
|
||||
label=_('Tunnel ID')
|
||||
)
|
||||
tag = TagFilterField(model)
|
||||
|
||||
|
||||
class TunnelTerminationFilterForm(NetBoxModelFilterSetForm):
|
||||
model = TunnelTermination
|
||||
fieldsets = (
|
||||
(None, ('q', 'filter_id', 'tag')),
|
||||
(_('Termination'), ('tunnel_id', 'role')),
|
||||
)
|
||||
tunnel_id = DynamicModelMultipleChoiceField(
|
||||
queryset=Tunnel.objects.all(),
|
||||
required=False,
|
||||
label=_('Tunnel')
|
||||
)
|
||||
role = forms.MultipleChoiceField(
|
||||
label=_('Role'),
|
||||
choices=TunnelTerminationRoleChoices,
|
||||
required=False
|
||||
)
|
||||
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_id', '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 = (
|
||||
(None, ('q', 'filter_id', 'tag')),
|
||||
(_('Profile'), ('mode', 'ike_policy_id', 'ipsec_policy_id')),
|
||||
)
|
||||
mode = forms.MultipleChoiceField(
|
||||
label=_('Mode'),
|
||||
choices=IPSecModeChoices,
|
||||
required=False
|
||||
)
|
||||
ike_policy_id = DynamicModelMultipleChoiceField(
|
||||
queryset=IKEPolicy.objects.all(),
|
||||
required=False,
|
||||
label=_('IKE policy')
|
||||
)
|
||||
ipsec_policy_id = DynamicModelMultipleChoiceField(
|
||||
queryset=IPSecPolicy.objects.all(),
|
||||
required=False,
|
||||
label=_('IPSec policy')
|
||||
)
|
||||
tag = TagFilterField(model)
|
357
netbox/vpn/forms/model_forms.py
Normal file
357
netbox/vpn/forms/model_forms.py
Normal file
@ -0,0 +1,357 @@
|
||||
from django import forms
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
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, DynamicModelMultipleChoiceField
|
||||
from utilities.forms.utils import add_blank_choice
|
||||
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',
|
||||
)
|
||||
|
||||
|
||||
class TunnelForm(TenancyForm, NetBoxModelForm):
|
||||
ipsec_profile = DynamicModelChoiceField(
|
||||
queryset=IPSecProfile.objects.all(),
|
||||
label=_('IPSec Profile'),
|
||||
required=False
|
||||
)
|
||||
comments = CommentField()
|
||||
|
||||
fieldsets = (
|
||||
(_('Tunnel'), ('name', 'status', 'encapsulation', 'description', 'tunnel_id', 'tags')),
|
||||
(_('Security'), ('ipsec_profile',)),
|
||||
(_('Tenancy'), ('tenant_group', 'tenant')),
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = Tunnel
|
||||
fields = [
|
||||
'name', 'status', 'encapsulation', 'description', 'tunnel_id', 'ipsec_profile', 'tenant_group', 'tenant',
|
||||
'comments', 'tags',
|
||||
]
|
||||
|
||||
|
||||
class TunnelCreateForm(TunnelForm):
|
||||
# First termination
|
||||
termination1_role = forms.ChoiceField(
|
||||
choices=add_blank_choice(TunnelTerminationRoleChoices),
|
||||
required=False,
|
||||
label=_('Role')
|
||||
)
|
||||
termination1_type = forms.ChoiceField(
|
||||
choices=TunnelTerminationTypeChoices,
|
||||
required=False,
|
||||
widget=HTMXSelect(),
|
||||
label=_('Type')
|
||||
)
|
||||
termination1_parent = DynamicModelChoiceField(
|
||||
queryset=Device.objects.all(),
|
||||
required=False,
|
||||
selector=True,
|
||||
label=_('Device')
|
||||
)
|
||||
termination1_termination = DynamicModelChoiceField(
|
||||
queryset=Interface.objects.all(),
|
||||
required=False,
|
||||
label=_('Interface'),
|
||||
query_params={
|
||||
'device_id': '$termination1_parent',
|
||||
}
|
||||
)
|
||||
termination1_outside_ip = DynamicModelChoiceField(
|
||||
queryset=IPAddress.objects.all(),
|
||||
label=_('Outside IP'),
|
||||
required=False,
|
||||
query_params={
|
||||
'device_id': '$termination1_parent',
|
||||
}
|
||||
)
|
||||
|
||||
# Second termination
|
||||
termination2_role = forms.ChoiceField(
|
||||
choices=add_blank_choice(TunnelTerminationRoleChoices),
|
||||
required=False,
|
||||
label=_('Role')
|
||||
)
|
||||
termination2_type = forms.ChoiceField(
|
||||
choices=TunnelTerminationTypeChoices,
|
||||
required=False,
|
||||
widget=HTMXSelect(),
|
||||
label=_('Type')
|
||||
)
|
||||
termination2_parent = DynamicModelChoiceField(
|
||||
queryset=Device.objects.all(),
|
||||
required=False,
|
||||
selector=True,
|
||||
label=_('Device')
|
||||
)
|
||||
termination2_termination = DynamicModelChoiceField(
|
||||
queryset=Interface.objects.all(),
|
||||
required=False,
|
||||
label=_('Interface'),
|
||||
query_params={
|
||||
'device_id': '$termination2_parent',
|
||||
}
|
||||
)
|
||||
termination2_outside_ip = DynamicModelChoiceField(
|
||||
queryset=IPAddress.objects.all(),
|
||||
required=False,
|
||||
label=_('Outside IP'),
|
||||
query_params={
|
||||
'device_id': '$termination2_parent',
|
||||
}
|
||||
)
|
||||
|
||||
fieldsets = (
|
||||
(_('Tunnel'), ('name', 'status', 'encapsulation', 'description', 'tunnel_id', 'tags')),
|
||||
(_('Security'), ('ipsec_profile',)),
|
||||
(_('Tenancy'), ('tenant_group', 'tenant')),
|
||||
(_('First Termination'), (
|
||||
'termination1_role', 'termination1_type', 'termination1_parent', 'termination1_termination',
|
||||
'termination1_outside_ip',
|
||||
)),
|
||||
(_('Second Termination'), (
|
||||
'termination2_role', 'termination2_type', 'termination2_parent', 'termination2_termination',
|
||||
'termination2_outside_ip',
|
||||
)),
|
||||
)
|
||||
|
||||
def __init__(self, *args, initial=None, **kwargs):
|
||||
super().__init__(*args, initial=initial, **kwargs)
|
||||
|
||||
if initial and initial.get('termination1_type') == TunnelTerminationTypeChoices.TYPE_VIRUTALMACHINE:
|
||||
self.fields['termination1_parent'].label = _('Virtual Machine')
|
||||
self.fields['termination1_parent'].queryset = VirtualMachine.objects.all()
|
||||
self.fields['termination1_termination'].queryset = VMInterface.objects.all()
|
||||
self.fields['termination1_termination'].widget.add_query_params({
|
||||
'virtual_machine_id': '$termination1_parent',
|
||||
})
|
||||
self.fields['termination1_outside_ip'].widget.add_query_params({
|
||||
'virtual_machine_id': '$termination1_parent',
|
||||
})
|
||||
|
||||
if initial and initial.get('termination2_type') == TunnelTerminationTypeChoices.TYPE_VIRUTALMACHINE:
|
||||
self.fields['termination2_parent'].label = _('Virtual Machine')
|
||||
self.fields['termination2_parent'].queryset = VirtualMachine.objects.all()
|
||||
self.fields['termination2_termination'].queryset = VMInterface.objects.all()
|
||||
self.fields['termination2_termination'].widget.add_query_params({
|
||||
'virtual_machine_id': '$termination2_parent',
|
||||
})
|
||||
self.fields['termination2_outside_ip'].widget.add_query_params({
|
||||
'virtual_machine_id': '$termination2_parent',
|
||||
})
|
||||
|
||||
def clean(self):
|
||||
super().clean()
|
||||
|
||||
# Validate attributes for each termination (if any)
|
||||
for term in ('termination1', 'termination2'):
|
||||
required_parameters = (
|
||||
f'{term}_role', f'{term}_parent', f'{term}_termination',
|
||||
)
|
||||
parameters = (
|
||||
*required_parameters,
|
||||
f'{term}_outside_ip',
|
||||
)
|
||||
if any([self.cleaned_data[param] for param in parameters]):
|
||||
for param in required_parameters:
|
||||
if not self.cleaned_data[param]:
|
||||
raise forms.ValidationError({
|
||||
param: _("This parameter is required when defining a termination.")
|
||||
})
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
instance = super().save(*args, **kwargs)
|
||||
|
||||
# Create first termination
|
||||
if self.cleaned_data['termination1_termination']:
|
||||
TunnelTermination.objects.create(
|
||||
tunnel=instance,
|
||||
role=self.cleaned_data['termination1_role'],
|
||||
termination=self.cleaned_data['termination1_termination'],
|
||||
outside_ip=self.cleaned_data['termination1_outside_ip'],
|
||||
)
|
||||
|
||||
# Create second termination, if defined
|
||||
if self.cleaned_data['termination2_termination']:
|
||||
TunnelTermination.objects.create(
|
||||
tunnel=instance,
|
||||
role=self.cleaned_data['termination2_role'],
|
||||
termination=self.cleaned_data['termination2_termination'],
|
||||
outside_ip=self.cleaned_data.get('termination1_outside_ip'),
|
||||
)
|
||||
|
||||
return instance
|
||||
|
||||
|
||||
class TunnelTerminationForm(NetBoxModelForm):
|
||||
tunnel = DynamicModelChoiceField(
|
||||
queryset=Tunnel.objects.all()
|
||||
)
|
||||
type = forms.ChoiceField(
|
||||
choices=TunnelTerminationTypeChoices,
|
||||
widget=HTMXSelect(),
|
||||
label=_('Type')
|
||||
)
|
||||
parent = DynamicModelChoiceField(
|
||||
queryset=Device.objects.all(),
|
||||
selector=True,
|
||||
label=_('Device')
|
||||
)
|
||||
termination = DynamicModelChoiceField(
|
||||
queryset=Interface.objects.all(),
|
||||
label=_('Interface'),
|
||||
query_params={
|
||||
'device_id': '$parent',
|
||||
}
|
||||
)
|
||||
outside_ip = DynamicModelChoiceField(
|
||||
queryset=IPAddress.objects.all(),
|
||||
label=_('Outside IP'),
|
||||
required=False,
|
||||
query_params={
|
||||
'device_id': '$parent',
|
||||
}
|
||||
)
|
||||
|
||||
fieldsets = (
|
||||
(None, ('tunnel', 'role', 'type', 'parent', 'termination', 'outside_ip', 'tags')),
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = TunnelTermination
|
||||
fields = [
|
||||
'tunnel', 'role', 'termination', 'outside_ip', 'tags',
|
||||
]
|
||||
|
||||
def __init__(self, *args, initial=None, **kwargs):
|
||||
super().__init__(*args, initial=initial, **kwargs)
|
||||
|
||||
if initial and initial.get('type') == TunnelTerminationTypeChoices.TYPE_VIRUTALMACHINE:
|
||||
self.fields['parent'].label = _('Virtual Machine')
|
||||
self.fields['parent'].queryset = VirtualMachine.objects.all()
|
||||
self.fields['termination'].queryset = VMInterface.objects.all()
|
||||
self.fields['termination'].widget.add_query_params({
|
||||
'virtual_machine_id': '$parent',
|
||||
})
|
||||
self.fields['outside_ip'].widget.add_query_params({
|
||||
'virtual_machine_id': '$parent',
|
||||
})
|
||||
|
||||
if self.instance.pk:
|
||||
self.fields['parent'].initial = self.instance.termination.parent_object
|
||||
self.fields['termination'].initial = self.instance.termination
|
||||
|
||||
def clean(self):
|
||||
super().clean()
|
||||
|
||||
# Set the terminated object
|
||||
self.instance.termination = self.cleaned_data.get('termination')
|
||||
|
||||
|
||||
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(),
|
||||
label=_('Proposals')
|
||||
)
|
||||
|
||||
fieldsets = (
|
||||
(_('Policy'), ('name', 'description', 'tags')),
|
||||
(_('Parameters'), ('version', 'mode', 'proposals', 'preshared_key')),
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = IKEPolicy
|
||||
fields = [
|
||||
'name', 'description', 'version', 'mode', 'proposals', 'preshared_key', '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(),
|
||||
label=_('Proposals')
|
||||
)
|
||||
|
||||
fieldsets = (
|
||||
(_('Policy'), ('name', 'description', 'tags')),
|
||||
(_('Parameters'), ('proposals', 'pfs_group')),
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = IPSecPolicy
|
||||
fields = [
|
||||
'name', 'description', 'proposals', 'pfs_group', 'tags',
|
||||
]
|
||||
|
||||
|
||||
class IPSecProfileForm(NetBoxModelForm):
|
||||
ike_policy = DynamicModelChoiceField(
|
||||
queryset=IKEPolicy.objects.all(),
|
||||
label=_('IKE policy')
|
||||
)
|
||||
ipsec_policy = DynamicModelChoiceField(
|
||||
queryset=IPSecPolicy.objects.all(),
|
||||
label=_('IPSec policy')
|
||||
)
|
||||
comments = CommentField()
|
||||
|
||||
fieldsets = (
|
||||
(_('Profile'), ('name', 'description', 'tags')),
|
||||
(_('Parameters'), ('mode', 'ike_policy', 'ipsec_policy')),
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = IPSecProfile
|
||||
fields = [
|
||||
'name', 'description', 'mode', 'ike_policy', 'ipsec_policy', 'description', 'comments', 'tags',
|
||||
]
|
0
netbox/vpn/graphql/__init__.py
Normal file
0
netbox/vpn/graphql/__init__.py
Normal file
51
netbox/vpn/graphql/schema.py
Normal file
51
netbox/vpn/graphql/schema.py
Normal file
@ -0,0 +1,51 @@
|
||||
import graphene
|
||||
|
||||
from netbox.graphql.fields import ObjectField, ObjectListField
|
||||
from utilities.graphql_optimizer import gql_query_optimizer
|
||||
from vpn import models
|
||||
from .types import *
|
||||
|
||||
|
||||
class VPNQuery(graphene.ObjectType):
|
||||
|
||||
ike_policy = ObjectField(IKEPolicyType)
|
||||
ike_policy_list = ObjectListField(IKEPolicyType)
|
||||
|
||||
def resolve_ike_policy_list(root, info, **kwargs):
|
||||
return gql_query_optimizer(models.IKEPolicy.objects.all(), info)
|
||||
|
||||
ike_proposal = ObjectField(IKEProposalType)
|
||||
ike_proposal_list = ObjectListField(IKEProposalType)
|
||||
|
||||
def resolve_ike_proposal_list(root, info, **kwargs):
|
||||
return gql_query_optimizer(models.IKEProposal.objects.all(), info)
|
||||
|
||||
ipsec_policy = ObjectField(IPSecPolicyType)
|
||||
ipsec_policy_list = ObjectListField(IPSecPolicyType)
|
||||
|
||||
def resolve_ipsec_policy_list(root, info, **kwargs):
|
||||
return gql_query_optimizer(models.IPSecPolicy.objects.all(), info)
|
||||
|
||||
ipsec_profile = ObjectField(IPSecProfileType)
|
||||
ipsec_profile_list = ObjectListField(IPSecProfileType)
|
||||
|
||||
def resolve_ipsec_profile_list(root, info, **kwargs):
|
||||
return gql_query_optimizer(models.IPSecProfile.objects.all(), info)
|
||||
|
||||
ipsec_proposal = ObjectField(IPSecProposalType)
|
||||
ipsec_proposal_list = ObjectListField(IPSecProposalType)
|
||||
|
||||
def resolve_ipsec_proposal_list(root, info, **kwargs):
|
||||
return gql_query_optimizer(models.IPSecProposal.objects.all(), info)
|
||||
|
||||
tunnel = ObjectField(TunnelType)
|
||||
tunnel_list = ObjectListField(TunnelType)
|
||||
|
||||
def resolve_tunnel_list(root, info, **kwargs):
|
||||
return gql_query_optimizer(models.Tunnel.objects.all(), info)
|
||||
|
||||
tunnel_termination = ObjectField(TunnelTerminationType)
|
||||
tunnel_termination_list = ObjectListField(TunnelTerminationType)
|
||||
|
||||
def resolve_tunnel_termination_list(root, info, **kwargs):
|
||||
return gql_query_optimizer(models.TunnelTermination.objects.all(), info)
|
69
netbox/vpn/graphql/types.py
Normal file
69
netbox/vpn/graphql/types.py
Normal file
@ -0,0 +1,69 @@
|
||||
from extras.graphql.mixins import CustomFieldsMixin, TagsMixin
|
||||
from netbox.graphql.types import ObjectType, OrganizationalObjectType, NetBoxObjectType
|
||||
from vpn import filtersets, models
|
||||
|
||||
__all__ = (
|
||||
'IKEPolicyType',
|
||||
'IKEProposalType',
|
||||
'IPSecPolicyType',
|
||||
'IPSecProfileType',
|
||||
'IPSecProposalType',
|
||||
'TunnelTerminationType',
|
||||
'TunnelType',
|
||||
)
|
||||
|
||||
|
||||
class TunnelTerminationType(CustomFieldsMixin, TagsMixin, ObjectType):
|
||||
|
||||
class Meta:
|
||||
model = models.TunnelTermination
|
||||
fields = '__all__'
|
||||
filterset_class = filtersets.TunnelTerminationFilterSet
|
||||
|
||||
|
||||
class TunnelType(NetBoxObjectType):
|
||||
|
||||
class Meta:
|
||||
model = models.Tunnel
|
||||
fields = '__all__'
|
||||
filterset_class = filtersets.TunnelFilterSet
|
||||
|
||||
|
||||
class IKEProposalType(OrganizationalObjectType):
|
||||
|
||||
class Meta:
|
||||
model = models.IKEProposal
|
||||
fields = '__all__'
|
||||
filterset_class = filtersets.IKEProposalFilterSet
|
||||
|
||||
|
||||
class IKEPolicyType(OrganizationalObjectType):
|
||||
|
||||
class Meta:
|
||||
model = models.IKEPolicy
|
||||
fields = '__all__'
|
||||
filterset_class = filtersets.IKEPolicyFilterSet
|
||||
|
||||
|
||||
class IPSecProposalType(OrganizationalObjectType):
|
||||
|
||||
class Meta:
|
||||
model = models.IPSecProposal
|
||||
fields = '__all__'
|
||||
filterset_class = filtersets.IPSecProposalFilterSet
|
||||
|
||||
|
||||
class IPSecPolicyType(OrganizationalObjectType):
|
||||
|
||||
class Meta:
|
||||
model = models.IPSecPolicy
|
||||
fields = '__all__'
|
||||
filterset_class = filtersets.IPSecPolicyFilterSet
|
||||
|
||||
|
||||
class IPSecProfileType(OrganizationalObjectType):
|
||||
|
||||
class Meta:
|
||||
model = models.IPSecProfile
|
||||
fields = '__all__'
|
||||
filterset_class = filtersets.IPSecProfileFilterSet
|
186
netbox/vpn/migrations/0001_initial.py
Normal file
186
netbox/vpn/migrations/0001_initial.py
Normal file
@ -0,0 +1,186 @@
|
||||
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'),
|
||||
('extras', '0099_cachedvalue_ordering'),
|
||||
('ipam', '0067_ipaddress_index_host'),
|
||||
('tenancy', '0012_contactassignment_custom_fields'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='IKEPolicy',
|
||||
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)),
|
||||
('name', models.CharField(max_length=100, unique=True)),
|
||||
('description', models.CharField(blank=True, max_length=200)),
|
||||
('version', models.PositiveSmallIntegerField(default=2)),
|
||||
('mode', models.CharField()),
|
||||
('preshared_key', models.TextField(blank=True)),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'IKE policy',
|
||||
'verbose_name_plural': 'IKE policies',
|
||||
'ordering': ('name',),
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='IPSecPolicy',
|
||||
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)),
|
||||
('name', models.CharField(max_length=100, unique=True)),
|
||||
('description', models.CharField(blank=True, max_length=200)),
|
||||
('pfs_group', models.PositiveSmallIntegerField(blank=True, null=True)),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'IPSec policy',
|
||||
'verbose_name_plural': 'IPSec policies',
|
||||
'ordering': ('name',),
|
||||
},
|
||||
),
|
||||
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)),
|
||||
('mode', models.CharField()),
|
||||
('ike_policy', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='ipsec_profiles', to='vpn.ikepolicy')),
|
||||
('ipsec_policy', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='ipsec_profiles', to='vpn.ipsecpolicy')),
|
||||
('tags', taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'IPSec profile',
|
||||
'verbose_name_plural': 'IPSec profiles',
|
||||
'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)),
|
||||
('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)),
|
||||
('termination_id', models.PositiveBigIntegerField(blank=True, null=True)),
|
||||
('termination_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.CreateModel(
|
||||
name='IPSecProposal',
|
||||
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)),
|
||||
('name', models.CharField(max_length=100, unique=True)),
|
||||
('description', models.CharField(blank=True, max_length=200)),
|
||||
('encryption_algorithm', models.CharField()),
|
||||
('authentication_algorithm', models.CharField()),
|
||||
('sa_lifetime_seconds', models.PositiveIntegerField(blank=True, null=True)),
|
||||
('sa_lifetime_data', models.PositiveIntegerField(blank=True, null=True)),
|
||||
('tags', taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'IPSec proposal',
|
||||
'verbose_name_plural': 'IPSec proposals',
|
||||
'ordering': ('name',),
|
||||
},
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='ipsecpolicy',
|
||||
name='proposals',
|
||||
field=models.ManyToManyField(related_name='ipsec_policies', to='vpn.ipsecproposal'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='ipsecpolicy',
|
||||
name='tags',
|
||||
field=taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag'),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='IKEProposal',
|
||||
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)),
|
||||
('name', models.CharField(max_length=100, unique=True)),
|
||||
('description', models.CharField(blank=True, max_length=200)),
|
||||
('authentication_method', models.CharField()),
|
||||
('encryption_algorithm', models.CharField()),
|
||||
('authentication_algorithm', models.CharField()),
|
||||
('group', models.PositiveSmallIntegerField()),
|
||||
('sa_lifetime', models.PositiveIntegerField(blank=True, null=True)),
|
||||
('tags', taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'IKE proposal',
|
||||
'verbose_name_plural': 'IKE proposals',
|
||||
'ordering': ('name',),
|
||||
},
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='ikepolicy',
|
||||
name='proposals',
|
||||
field=models.ManyToManyField(related_name='ike_policies', to='vpn.ikeproposal'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='ikepolicy',
|
||||
name='tags',
|
||||
field=taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag'),
|
||||
),
|
||||
migrations.AddConstraint(
|
||||
model_name='tunneltermination',
|
||||
constraint=models.UniqueConstraint(fields=('termination_type', 'termination_id'), name='vpn_tunneltermination_termination', violation_error_message='An object may be terminated to only one tunnel at a time.'),
|
||||
),
|
||||
]
|
0
netbox/vpn/migrations/__init__.py
Normal file
0
netbox/vpn/migrations/__init__.py
Normal file
2
netbox/vpn/models/__init__.py
Normal file
2
netbox/vpn/models/__init__.py
Normal file
@ -0,0 +1,2 @@
|
||||
from .crypto import *
|
||||
from .tunnels import *
|
254
netbox/vpn/models/crypto.py
Normal file
254
netbox/vpn/models/crypto.py
Normal file
@ -0,0 +1,254 @@
|
||||
from django.db import models
|
||||
from django.urls import reverse
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
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)')
|
||||
)
|
||||
|
||||
clone_fields = (
|
||||
'authentication_method', 'encryption_algorithm', 'authentication_algorithm', 'group', 'sa_lifetime',
|
||||
)
|
||||
|
||||
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
|
||||
)
|
||||
|
||||
clone_fields = (
|
||||
'version', 'mode', 'proposals',
|
||||
)
|
||||
prerequisite_models = (
|
||||
'vpn.IKEProposal',
|
||||
)
|
||||
|
||||
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:ikepolicy', 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)')
|
||||
)
|
||||
|
||||
clone_fields = (
|
||||
'encryption_algorithm', 'authentication_algorithm', 'sa_lifetime_seconds', 'sa_lifetime_data',
|
||||
)
|
||||
|
||||
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')
|
||||
)
|
||||
|
||||
clone_fields = (
|
||||
'proposals', 'pfs_group',
|
||||
)
|
||||
prerequisite_models = (
|
||||
'vpn.IPSecProposal',
|
||||
)
|
||||
|
||||
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
|
||||
)
|
||||
mode = models.CharField(
|
||||
verbose_name=_('mode'),
|
||||
choices=IPSecModeChoices
|
||||
)
|
||||
ike_policy = models.ForeignKey(
|
||||
to='vpn.IKEPolicy',
|
||||
on_delete=models.PROTECT,
|
||||
related_name='ipsec_profiles'
|
||||
)
|
||||
ipsec_policy = models.ForeignKey(
|
||||
to='vpn.IPSecPolicy',
|
||||
on_delete=models.PROTECT,
|
||||
related_name='ipsec_profiles'
|
||||
)
|
||||
|
||||
clone_fields = (
|
||||
'mode', 'ike_policy', 'ipsec_policy',
|
||||
)
|
||||
prerequisite_models = (
|
||||
'vpn.IKEPolicy',
|
||||
'vpn.IPSecPolicy',
|
||||
)
|
||||
|
||||
class Meta:
|
||||
ordering = ('name',)
|
||||
verbose_name = _('IPSec profile')
|
||||
verbose_name_plural = _('IPSec profiles')
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
def get_absolute_url(self):
|
||||
return reverse('vpn:ipsecprofile', args=[self.pk])
|
146
netbox/vpn/models/tunnels.py
Normal file
146
netbox/vpn/models/tunnels.py
Normal file
@ -0,0 +1,146 @@
|
||||
from django.contrib.contenttypes.fields import GenericForeignKey
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.db import models
|
||||
from django.urls import reverse
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from netbox.models import ChangeLoggedModel, PrimaryModel
|
||||
from netbox.models.features import CustomFieldsMixin, CustomLinksMixin, TagsMixin
|
||||
from vpn.choices import *
|
||||
|
||||
__all__ = (
|
||||
'Tunnel',
|
||||
'TunnelTermination',
|
||||
)
|
||||
|
||||
|
||||
class Tunnel(PrimaryModel):
|
||||
name = models.CharField(
|
||||
verbose_name=_('name'),
|
||||
max_length=100,
|
||||
unique=True
|
||||
)
|
||||
status = models.CharField(
|
||||
verbose_name=_('status'),
|
||||
max_length=50,
|
||||
choices=TunnelStatusChoices,
|
||||
default=TunnelStatusChoices.STATUS_ACTIVE
|
||||
)
|
||||
encapsulation = models.CharField(
|
||||
verbose_name=_('encapsulation'),
|
||||
max_length=50,
|
||||
choices=TunnelEncapsulationChoices
|
||||
)
|
||||
ipsec_profile = models.ForeignKey(
|
||||
to='vpn.IPSecProfile',
|
||||
on_delete=models.PROTECT,
|
||||
related_name='tunnels',
|
||||
blank=True,
|
||||
null=True
|
||||
)
|
||||
tenant = models.ForeignKey(
|
||||
to='tenancy.Tenant',
|
||||
on_delete=models.PROTECT,
|
||||
related_name='tunnels',
|
||||
blank=True,
|
||||
null=True
|
||||
)
|
||||
tunnel_id = models.PositiveBigIntegerField(
|
||||
verbose_name=_('tunnel ID'),
|
||||
blank=True,
|
||||
null=True
|
||||
)
|
||||
|
||||
clone_fields = (
|
||||
'status', 'encapsulation', 'ipsec_profile', 'tenant',
|
||||
)
|
||||
|
||||
class Meta:
|
||||
ordering = ('name',)
|
||||
verbose_name = _('tunnel')
|
||||
verbose_name_plural = _('tunnels')
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
def get_absolute_url(self):
|
||||
return reverse('vpn:tunnel', args=[self.pk])
|
||||
|
||||
def get_status_color(self):
|
||||
return TunnelStatusChoices.colors.get(self.status)
|
||||
|
||||
|
||||
class TunnelTermination(CustomFieldsMixin, CustomLinksMixin, TagsMixin, ChangeLoggedModel):
|
||||
tunnel = models.ForeignKey(
|
||||
to='vpn.Tunnel',
|
||||
on_delete=models.CASCADE,
|
||||
related_name='terminations'
|
||||
)
|
||||
role = models.CharField(
|
||||
verbose_name=_('role'),
|
||||
max_length=50,
|
||||
choices=TunnelTerminationRoleChoices,
|
||||
default=TunnelTerminationRoleChoices.ROLE_PEER
|
||||
)
|
||||
termination_type = models.ForeignKey(
|
||||
to='contenttypes.ContentType',
|
||||
on_delete=models.PROTECT,
|
||||
related_name='+'
|
||||
)
|
||||
termination_id = models.PositiveBigIntegerField(
|
||||
blank=True,
|
||||
null=True
|
||||
)
|
||||
termination = GenericForeignKey(
|
||||
ct_field='termination_type',
|
||||
fk_field='termination_id'
|
||||
)
|
||||
outside_ip = models.OneToOneField(
|
||||
to='ipam.IPAddress',
|
||||
on_delete=models.PROTECT,
|
||||
related_name='tunnel_termination',
|
||||
blank=True,
|
||||
null=True
|
||||
)
|
||||
|
||||
prerequisite_models = (
|
||||
'vpn.Tunnel',
|
||||
)
|
||||
|
||||
class Meta:
|
||||
ordering = ('tunnel', 'role', 'pk')
|
||||
constraints = (
|
||||
models.UniqueConstraint(
|
||||
fields=('termination_type', 'termination_id'),
|
||||
name='%(app_label)s_%(class)s_termination',
|
||||
violation_error_message=_("An object may be terminated to only one tunnel at a time.")
|
||||
),
|
||||
)
|
||||
verbose_name = _('tunnel termination')
|
||||
verbose_name_plural = _('tunnel terminations')
|
||||
|
||||
def __str__(self):
|
||||
return f'{self.tunnel}: Termination {self.pk}'
|
||||
|
||||
def get_absolute_url(self):
|
||||
return reverse('vpn:tunneltermination', args=[self.pk])
|
||||
|
||||
def get_role_color(self):
|
||||
return TunnelTerminationRoleChoices.colors.get(self.role)
|
||||
|
||||
def clean(self):
|
||||
super().clean()
|
||||
|
||||
# Check that the selected termination object is not already attached to a Tunnel
|
||||
if getattr(self.termination, 'tunnel_termination', None) and self.termination.tunnel_termination.pk != self.pk:
|
||||
raise ValidationError({
|
||||
'termination': _("{name} is already attached to a tunnel ({tunnel}).").format(
|
||||
name=self.termination.name,
|
||||
tunnel=self.termination.tunnel_termination.tunnel
|
||||
)
|
||||
})
|
||||
|
||||
def to_objectchange(self, action):
|
||||
objectchange = super().to_objectchange(action)
|
||||
objectchange.related_object = self.tunnel
|
||||
return objectchange
|
65
netbox/vpn/search.py
Normal file
65
netbox/vpn/search.py
Normal file
@ -0,0 +1,65 @@
|
||||
from netbox.search import SearchIndex, register_search
|
||||
from . import models
|
||||
|
||||
|
||||
@register_search
|
||||
class TunnelIndex(SearchIndex):
|
||||
model = models.Tunnel
|
||||
fields = (
|
||||
('name', 100),
|
||||
('tunnel_id', 300),
|
||||
('description', 500),
|
||||
('comments', 5000),
|
||||
)
|
||||
display_attrs = ('status', 'encapsulation', 'tenant', 'description')
|
||||
|
||||
|
||||
@register_search
|
||||
class IKEProposalIndex(SearchIndex):
|
||||
model = models.IKEProposal
|
||||
fields = (
|
||||
('name', 100),
|
||||
('description', 500),
|
||||
)
|
||||
display_attrs = ('description',)
|
||||
|
||||
|
||||
@register_search
|
||||
class IKEPolicyIndex(SearchIndex):
|
||||
model = models.IKEPolicy
|
||||
fields = (
|
||||
('name', 100),
|
||||
('description', 500),
|
||||
)
|
||||
display_attrs = ('description',)
|
||||
|
||||
|
||||
@register_search
|
||||
class IPSecProposalIndex(SearchIndex):
|
||||
model = models.IPSecProposal
|
||||
fields = (
|
||||
('name', 100),
|
||||
('description', 500),
|
||||
)
|
||||
display_attrs = ('description',)
|
||||
|
||||
|
||||
@register_search
|
||||
class IPSecPolicyIndex(SearchIndex):
|
||||
model = models.IPSecPolicy
|
||||
fields = (
|
||||
('name', 100),
|
||||
('description', 500),
|
||||
)
|
||||
display_attrs = ('description',)
|
||||
|
||||
|
||||
@register_search
|
||||
class IPSecProfileIndex(SearchIndex):
|
||||
model = models.IPSecProfile
|
||||
fields = (
|
||||
('name', 100),
|
||||
('description', 500),
|
||||
('comments', 5000),
|
||||
)
|
||||
display_attrs = ('description',)
|
254
netbox/vpn/tables.py
Normal file
254
netbox/vpn/tables.py
Normal file
@ -0,0 +1,254 @@
|
||||
import django_tables2 as tables
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django_tables2.utils import Accessor
|
||||
|
||||
from tenancy.tables import TenancyColumnsMixin
|
||||
from netbox.tables import NetBoxTable, columns
|
||||
from vpn.models import *
|
||||
|
||||
__all__ = (
|
||||
'IKEPolicyTable',
|
||||
'IKEProposalTable',
|
||||
'IPSecPolicyTable',
|
||||
'IPSecProposalTable',
|
||||
'IPSecProfileTable',
|
||||
'TunnelTable',
|
||||
'TunnelTerminationTable',
|
||||
)
|
||||
|
||||
|
||||
class TunnelTable(TenancyColumnsMixin, NetBoxTable):
|
||||
name = tables.Column(
|
||||
verbose_name=_('Name'),
|
||||
linkify=True
|
||||
)
|
||||
status = columns.ChoiceFieldColumn(
|
||||
verbose_name=_('Status')
|
||||
)
|
||||
ipsec_profile = tables.Column(
|
||||
verbose_name=_('IPSec profile'),
|
||||
linkify=True
|
||||
)
|
||||
terminations_count = columns.LinkedCountColumn(
|
||||
accessor=Accessor('count_terminations'),
|
||||
viewname='vpn:tunneltermination_list',
|
||||
url_params={'tunnel_id': 'pk'},
|
||||
verbose_name=_('Terminations')
|
||||
)
|
||||
comments = columns.MarkdownColumn(
|
||||
verbose_name=_('Comments'),
|
||||
)
|
||||
tags = columns.TagColumn(
|
||||
url_name='vpn:tunnel_list'
|
||||
)
|
||||
|
||||
class Meta(NetBoxTable.Meta):
|
||||
model = Tunnel
|
||||
fields = (
|
||||
'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', 'ipsec_profile', 'tenant', 'terminations_count')
|
||||
|
||||
|
||||
class TunnelTerminationTable(TenancyColumnsMixin, NetBoxTable):
|
||||
tunnel = tables.Column(
|
||||
verbose_name=_('Tunnel'),
|
||||
linkify=True
|
||||
)
|
||||
role = columns.ChoiceFieldColumn(
|
||||
verbose_name=_('Role')
|
||||
)
|
||||
termination_parent = tables.Column(
|
||||
accessor='termination__parent_object',
|
||||
linkify=True,
|
||||
orderable=False,
|
||||
verbose_name=_('Host')
|
||||
)
|
||||
termination = tables.Column(
|
||||
verbose_name=_('Termination'),
|
||||
linkify=True
|
||||
)
|
||||
ip_addresses = tables.ManyToManyColumn(
|
||||
accessor=tables.A('termination__ip_addresses'),
|
||||
orderable=False,
|
||||
linkify_item=True,
|
||||
verbose_name=_('IP Addresses')
|
||||
)
|
||||
outside_ip = tables.Column(
|
||||
verbose_name=_('Outside IP'),
|
||||
linkify=True
|
||||
)
|
||||
tags = columns.TagColumn(
|
||||
url_name='vpn:tunneltermination_list'
|
||||
)
|
||||
|
||||
class Meta(NetBoxTable.Meta):
|
||||
model = TunnelTermination
|
||||
fields = (
|
||||
'pk', 'id', 'tunnel', 'role', 'termination_parent', 'termination', 'ip_addresses', 'outside_ip', 'tags',
|
||||
'created', 'last_updated',
|
||||
)
|
||||
default_columns = (
|
||||
'pk', 'id', 'tunnel', 'role', 'termination_parent', 'termination', 'ip_addresses', 'outside_ip',
|
||||
)
|
||||
|
||||
|
||||
class IKEProposalTable(NetBoxTable):
|
||||
name = tables.Column(
|
||||
verbose_name=_('Name'),
|
||||
linkify=True
|
||||
)
|
||||
authentication_method = tables.Column(
|
||||
verbose_name=_('Authentication Method')
|
||||
)
|
||||
encryption_algorithm = tables.Column(
|
||||
verbose_name=_('Encryption Algorithm')
|
||||
)
|
||||
authentication_algorithm = tables.Column(
|
||||
verbose_name=_('Authentication Algorithm')
|
||||
)
|
||||
group = tables.Column(
|
||||
verbose_name=_('Group')
|
||||
)
|
||||
sa_lifetime = tables.Column(
|
||||
verbose_name=_('SA Lifetime')
|
||||
)
|
||||
tags = columns.TagColumn(
|
||||
url_name='vpn:ikeproposal_list'
|
||||
)
|
||||
|
||||
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
|
||||
)
|
||||
version = tables.Column(
|
||||
verbose_name=_('Version')
|
||||
)
|
||||
mode = tables.Column(
|
||||
verbose_name=_('Mode')
|
||||
)
|
||||
proposals = tables.ManyToManyColumn(
|
||||
linkify_item=True,
|
||||
verbose_name=_('Proposals')
|
||||
)
|
||||
preshared_key = tables.Column(
|
||||
verbose_name=_('Pre-shared Key')
|
||||
)
|
||||
tags = columns.TagColumn(
|
||||
url_name='vpn:ikepolicy_list'
|
||||
)
|
||||
|
||||
class Meta(NetBoxTable.Meta):
|
||||
model = IKEPolicy
|
||||
fields = (
|
||||
'pk', 'id', 'name', 'version', 'mode', 'proposals', 'preshared_key', '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 = tables.Column(
|
||||
verbose_name=_('Encryption Algorithm')
|
||||
)
|
||||
authentication_algorithm = tables.Column(
|
||||
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 = tables.Column(
|
||||
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', 'description', 'tags', 'created', 'last_updated',
|
||||
)
|
||||
default_columns = (
|
||||
'pk', 'name', 'proposals', 'pfs_group', 'description',
|
||||
)
|
||||
|
||||
|
||||
class IPSecProfileTable(NetBoxTable):
|
||||
name = tables.Column(
|
||||
verbose_name=_('Name'),
|
||||
linkify=True
|
||||
)
|
||||
mode = tables.Column(
|
||||
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:ipsecprofile_list'
|
||||
)
|
||||
|
||||
class Meta(NetBoxTable.Meta):
|
||||
model = IPSecProfile
|
||||
fields = (
|
||||
'pk', 'id', 'name', 'mode', 'ike_policy', 'ipsec_policy', 'description', 'comments', 'tags', 'created',
|
||||
'last_updated',
|
||||
)
|
||||
default_columns = ('pk', 'name', 'mode', 'ike_policy', 'ipsec_policy', 'description')
|
0
netbox/vpn/tests/__init__.py
Normal file
0
netbox/vpn/tests/__init__.py
Normal file
473
netbox/vpn/tests/test_api.py
Normal file
473
netbox/vpn/tests/test_api.py
Normal file
@ -0,0 +1,473 @@
|
||||
from django.urls import reverse
|
||||
|
||||
from dcim.choices import InterfaceTypeChoices
|
||||
from dcim.models import Interface
|
||||
from utilities.testing import APITestCase, APIViewTestCases, create_test_device
|
||||
from vpn.choices import *
|
||||
from vpn.models import *
|
||||
|
||||
|
||||
class AppTest(APITestCase):
|
||||
|
||||
def test_root(self):
|
||||
url = reverse('vpn-api:api-root')
|
||||
response = self.client.get('{}?format=api'.format(url), **self.header)
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
|
||||
class TunnelTest(APIViewTestCases.APIViewTestCase):
|
||||
model = Tunnel
|
||||
brief_fields = ['display', 'id', 'name', 'url']
|
||||
bulk_update_data = {
|
||||
'status': TunnelStatusChoices.STATUS_PLANNED,
|
||||
'encapsulation': TunnelEncapsulationChoices.ENCAP_GRE,
|
||||
'description': 'New description',
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
|
||||
tunnels = (
|
||||
Tunnel(
|
||||
name='Tunnel 1',
|
||||
status=TunnelStatusChoices.STATUS_ACTIVE,
|
||||
encapsulation=TunnelEncapsulationChoices.ENCAP_IP_IP
|
||||
),
|
||||
Tunnel(
|
||||
name='Tunnel 2',
|
||||
status=TunnelStatusChoices.STATUS_ACTIVE,
|
||||
encapsulation=TunnelEncapsulationChoices.ENCAP_IP_IP
|
||||
),
|
||||
Tunnel(
|
||||
name='Tunnel 3',
|
||||
status=TunnelStatusChoices.STATUS_ACTIVE,
|
||||
encapsulation=TunnelEncapsulationChoices.ENCAP_IP_IP
|
||||
),
|
||||
)
|
||||
Tunnel.objects.bulk_create(tunnels)
|
||||
|
||||
cls.create_data = [
|
||||
{
|
||||
'name': 'Tunnel 4',
|
||||
'status': TunnelStatusChoices.STATUS_DISABLED,
|
||||
'encapsulation': TunnelEncapsulationChoices.ENCAP_GRE,
|
||||
},
|
||||
{
|
||||
'name': 'Tunnel 5',
|
||||
'status': TunnelStatusChoices.STATUS_DISABLED,
|
||||
'encapsulation': TunnelEncapsulationChoices.ENCAP_GRE,
|
||||
},
|
||||
{
|
||||
'name': 'Tunnel 6',
|
||||
'status': TunnelStatusChoices.STATUS_DISABLED,
|
||||
'encapsulation': TunnelEncapsulationChoices.ENCAP_GRE,
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
class TunnelTerminationTest(APIViewTestCases.APIViewTestCase):
|
||||
model = TunnelTermination
|
||||
brief_fields = ['display', 'id', 'url']
|
||||
bulk_update_data = {
|
||||
'role': TunnelTerminationRoleChoices.ROLE_PEER,
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
device = create_test_device('Device 1')
|
||||
interfaces = (
|
||||
Interface(device=device, name='Interface 1', type=InterfaceTypeChoices.TYPE_VIRTUAL),
|
||||
Interface(device=device, name='Interface 2', type=InterfaceTypeChoices.TYPE_VIRTUAL),
|
||||
Interface(device=device, name='Interface 3', type=InterfaceTypeChoices.TYPE_VIRTUAL),
|
||||
Interface(device=device, name='Interface 4', type=InterfaceTypeChoices.TYPE_VIRTUAL),
|
||||
Interface(device=device, name='Interface 5', type=InterfaceTypeChoices.TYPE_VIRTUAL),
|
||||
Interface(device=device, name='Interface 6', type=InterfaceTypeChoices.TYPE_VIRTUAL),
|
||||
)
|
||||
Interface.objects.bulk_create(interfaces)
|
||||
|
||||
tunnel = Tunnel.objects.create(
|
||||
name='Tunnel 1',
|
||||
status=TunnelStatusChoices.STATUS_ACTIVE,
|
||||
encapsulation=TunnelEncapsulationChoices.ENCAP_IP_IP
|
||||
)
|
||||
|
||||
tunnel_terminations = (
|
||||
TunnelTermination(
|
||||
tunnel=tunnel,
|
||||
role=TunnelTerminationRoleChoices.ROLE_HUB,
|
||||
termination=interfaces[0]
|
||||
),
|
||||
TunnelTermination(
|
||||
tunnel=tunnel,
|
||||
role=TunnelTerminationRoleChoices.ROLE_HUB,
|
||||
termination=interfaces[1]
|
||||
),
|
||||
TunnelTermination(
|
||||
tunnel=tunnel,
|
||||
role=TunnelTerminationRoleChoices.ROLE_HUB,
|
||||
termination=interfaces[2]
|
||||
),
|
||||
)
|
||||
TunnelTermination.objects.bulk_create(tunnel_terminations)
|
||||
|
||||
cls.create_data = [
|
||||
{
|
||||
'tunnel': tunnel.pk,
|
||||
'role': TunnelTerminationRoleChoices.ROLE_PEER,
|
||||
'termination_type': 'dcim.interface',
|
||||
'termination_id': interfaces[3].pk,
|
||||
},
|
||||
{
|
||||
'tunnel': tunnel.pk,
|
||||
'role': TunnelTerminationRoleChoices.ROLE_PEER,
|
||||
'termination_type': 'dcim.interface',
|
||||
'termination_id': interfaces[4].pk,
|
||||
},
|
||||
{
|
||||
'tunnel': tunnel.pk,
|
||||
'role': TunnelTerminationRoleChoices.ROLE_PEER,
|
||||
'termination_type': 'dcim.interface',
|
||||
'termination_id': interfaces[5].pk,
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
class IKEProposalTest(APIViewTestCases.APIViewTestCase):
|
||||
model = IKEProposal
|
||||
brief_fields = ['display', 'id', 'name', 'url']
|
||||
bulk_update_data = {
|
||||
'authentication_method': AuthenticationMethodChoices.CERTIFICATES,
|
||||
'encryption_algorithm': EncryptionAlgorithmChoices.ENCRYPTION_AES192_CBC,
|
||||
'authentication_algorithm': AuthenticationAlgorithmChoices.AUTH_HMAC_MD5,
|
||||
'group': DHGroupChoices.GROUP_19,
|
||||
'description': 'New description',
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
|
||||
ike_proposals = (
|
||||
IKEProposal(
|
||||
name='IKE Proposal 1',
|
||||
authentication_method=AuthenticationMethodChoices.PRESHARED_KEYS,
|
||||
encryption_algorithm=EncryptionAlgorithmChoices.ENCRYPTION_AES128_CBC,
|
||||
authentication_algorithm=AuthenticationAlgorithmChoices.AUTH_HMAC_SHA1,
|
||||
group=DHGroupChoices.GROUP_14
|
||||
),
|
||||
IKEProposal(
|
||||
name='IKE Proposal 2',
|
||||
authentication_method=AuthenticationMethodChoices.PRESHARED_KEYS,
|
||||
encryption_algorithm=EncryptionAlgorithmChoices.ENCRYPTION_AES128_CBC,
|
||||
authentication_algorithm=AuthenticationAlgorithmChoices.AUTH_HMAC_SHA1,
|
||||
group=DHGroupChoices.GROUP_14
|
||||
),
|
||||
IKEProposal(
|
||||
name='IKE Proposal 3',
|
||||
authentication_method=AuthenticationMethodChoices.PRESHARED_KEYS,
|
||||
encryption_algorithm=EncryptionAlgorithmChoices.ENCRYPTION_AES128_CBC,
|
||||
authentication_algorithm=AuthenticationAlgorithmChoices.AUTH_HMAC_SHA1,
|
||||
group=DHGroupChoices.GROUP_14
|
||||
),
|
||||
)
|
||||
IKEProposal.objects.bulk_create(ike_proposals)
|
||||
|
||||
cls.create_data = [
|
||||
{
|
||||
'name': 'IKE Proposal 4',
|
||||
'authentication_method': AuthenticationMethodChoices.CERTIFICATES,
|
||||
'encryption_algorithm': EncryptionAlgorithmChoices.ENCRYPTION_AES256_CBC,
|
||||
'authentication_algorithm': AuthenticationAlgorithmChoices.AUTH_HMAC_SHA256,
|
||||
'group': DHGroupChoices.GROUP_19,
|
||||
},
|
||||
{
|
||||
'name': 'IKE Proposal 5',
|
||||
'authentication_method': AuthenticationMethodChoices.CERTIFICATES,
|
||||
'encryption_algorithm': EncryptionAlgorithmChoices.ENCRYPTION_AES256_CBC,
|
||||
'authentication_algorithm': AuthenticationAlgorithmChoices.AUTH_HMAC_SHA256,
|
||||
'group': DHGroupChoices.GROUP_19,
|
||||
},
|
||||
{
|
||||
'name': 'IKE Proposal 6',
|
||||
'authentication_method': AuthenticationMethodChoices.CERTIFICATES,
|
||||
'encryption_algorithm': EncryptionAlgorithmChoices.ENCRYPTION_AES256_CBC,
|
||||
'authentication_algorithm': AuthenticationAlgorithmChoices.AUTH_HMAC_SHA256,
|
||||
'group': DHGroupChoices.GROUP_19,
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
class IKEPolicyTest(APIViewTestCases.APIViewTestCase):
|
||||
model = IKEPolicy
|
||||
brief_fields = ['display', 'id', 'name', 'url']
|
||||
bulk_update_data = {
|
||||
'version': IKEVersionChoices.VERSION_1,
|
||||
'mode': IKEModeChoices.AGGRESSIVE,
|
||||
'description': 'New description',
|
||||
'preshared_key': 'New key',
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
|
||||
ike_proposals = (
|
||||
IKEProposal(
|
||||
name='IKE Proposal 1',
|
||||
authentication_method=AuthenticationMethodChoices.PRESHARED_KEYS,
|
||||
encryption_algorithm=EncryptionAlgorithmChoices.ENCRYPTION_AES128_CBC,
|
||||
authentication_algorithm=AuthenticationAlgorithmChoices.AUTH_HMAC_SHA1,
|
||||
group=DHGroupChoices.GROUP_14
|
||||
),
|
||||
IKEProposal(
|
||||
name='IKE Proposal 2',
|
||||
authentication_method=AuthenticationMethodChoices.PRESHARED_KEYS,
|
||||
encryption_algorithm=EncryptionAlgorithmChoices.ENCRYPTION_AES128_CBC,
|
||||
authentication_algorithm=AuthenticationAlgorithmChoices.AUTH_HMAC_SHA1,
|
||||
group=DHGroupChoices.GROUP_14
|
||||
),
|
||||
)
|
||||
IKEProposal.objects.bulk_create(ike_proposals)
|
||||
|
||||
ike_policies = (
|
||||
IKEPolicy(
|
||||
name='IKE Policy 1',
|
||||
version=IKEVersionChoices.VERSION_1,
|
||||
mode=IKEModeChoices.MAIN,
|
||||
),
|
||||
IKEPolicy(
|
||||
name='IKE Policy 2',
|
||||
version=IKEVersionChoices.VERSION_1,
|
||||
mode=IKEModeChoices.MAIN,
|
||||
),
|
||||
IKEPolicy(
|
||||
name='IKE Policy 3',
|
||||
version=IKEVersionChoices.VERSION_1,
|
||||
mode=IKEModeChoices.MAIN,
|
||||
),
|
||||
)
|
||||
IKEPolicy.objects.bulk_create(ike_policies)
|
||||
for ike_policy in ike_policies:
|
||||
ike_policy.proposals.set(ike_proposals)
|
||||
|
||||
cls.create_data = [
|
||||
{
|
||||
'name': 'IKE Policy 4',
|
||||
'version': IKEVersionChoices.VERSION_1,
|
||||
'mode': IKEModeChoices.MAIN,
|
||||
'proposals': [ike_proposals[0].pk, ike_proposals[1].pk],
|
||||
},
|
||||
{
|
||||
'name': 'IKE Policy 5',
|
||||
'version': IKEVersionChoices.VERSION_1,
|
||||
'mode': IKEModeChoices.MAIN,
|
||||
'proposals': [ike_proposals[0].pk, ike_proposals[1].pk],
|
||||
},
|
||||
{
|
||||
'name': 'IKE Policy 6',
|
||||
'version': IKEVersionChoices.VERSION_1,
|
||||
'mode': IKEModeChoices.MAIN,
|
||||
'proposals': [ike_proposals[0].pk, ike_proposals[1].pk],
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
class IPSecProposalTest(APIViewTestCases.APIViewTestCase):
|
||||
model = IPSecProposal
|
||||
brief_fields = ['display', 'id', 'name', 'url']
|
||||
bulk_update_data = {
|
||||
'encryption_algorithm': EncryptionAlgorithmChoices.ENCRYPTION_AES192_CBC,
|
||||
'authentication_algorithm': AuthenticationAlgorithmChoices.AUTH_HMAC_MD5,
|
||||
'description': 'New description',
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
|
||||
ipsec_proposals = (
|
||||
IPSecProposal(
|
||||
name='IPSec Proposal 1',
|
||||
encryption_algorithm=EncryptionAlgorithmChoices.ENCRYPTION_AES128_CBC,
|
||||
authentication_algorithm=AuthenticationAlgorithmChoices.AUTH_HMAC_SHA1
|
||||
),
|
||||
IPSecProposal(
|
||||
name='IPSec Proposal 2',
|
||||
encryption_algorithm=EncryptionAlgorithmChoices.ENCRYPTION_AES128_CBC,
|
||||
authentication_algorithm=AuthenticationAlgorithmChoices.AUTH_HMAC_SHA1
|
||||
),
|
||||
IPSecProposal(
|
||||
name='IPSec Proposal 3',
|
||||
encryption_algorithm=EncryptionAlgorithmChoices.ENCRYPTION_AES128_CBC,
|
||||
authentication_algorithm=AuthenticationAlgorithmChoices.AUTH_HMAC_SHA1
|
||||
),
|
||||
)
|
||||
IPSecProposal.objects.bulk_create(ipsec_proposals)
|
||||
|
||||
cls.create_data = [
|
||||
{
|
||||
'name': 'IPSec Proposal 4',
|
||||
'encryption_algorithm': EncryptionAlgorithmChoices.ENCRYPTION_AES256_CBC,
|
||||
'authentication_algorithm': AuthenticationAlgorithmChoices.AUTH_HMAC_SHA256,
|
||||
},
|
||||
{
|
||||
'name': 'IPSec Proposal 5',
|
||||
'encryption_algorithm': EncryptionAlgorithmChoices.ENCRYPTION_AES256_CBC,
|
||||
'authentication_algorithm': AuthenticationAlgorithmChoices.AUTH_HMAC_SHA256,
|
||||
},
|
||||
{
|
||||
'name': 'IPSec Proposal 6',
|
||||
'encryption_algorithm': EncryptionAlgorithmChoices.ENCRYPTION_AES256_CBC,
|
||||
'authentication_algorithm': AuthenticationAlgorithmChoices.AUTH_HMAC_SHA256,
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
class IPSecPolicyTest(APIViewTestCases.APIViewTestCase):
|
||||
model = IPSecPolicy
|
||||
brief_fields = ['display', 'id', 'name', 'url']
|
||||
bulk_update_data = {
|
||||
'pfs_group': DHGroupChoices.GROUP_5,
|
||||
'description': 'New description',
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
|
||||
ipsec_proposals = (
|
||||
IPSecProposal(
|
||||
name='IPSec Policy 1',
|
||||
encryption_algorithm=EncryptionAlgorithmChoices.ENCRYPTION_AES128_CBC,
|
||||
authentication_algorithm=AuthenticationAlgorithmChoices.AUTH_HMAC_SHA1
|
||||
),
|
||||
IPSecProposal(
|
||||
name='IPSec Proposal 2',
|
||||
encryption_algorithm=EncryptionAlgorithmChoices.ENCRYPTION_AES128_CBC,
|
||||
authentication_algorithm=AuthenticationAlgorithmChoices.AUTH_HMAC_SHA1
|
||||
),
|
||||
)
|
||||
IPSecProposal.objects.bulk_create(ipsec_proposals)
|
||||
|
||||
ipsec_policies = (
|
||||
IPSecPolicy(
|
||||
name='IPSec Policy 1',
|
||||
pfs_group=DHGroupChoices.GROUP_14
|
||||
),
|
||||
IPSecPolicy(
|
||||
name='IPSec Policy 2',
|
||||
pfs_group=DHGroupChoices.GROUP_14
|
||||
),
|
||||
IPSecPolicy(
|
||||
name='IPSec Policy 3',
|
||||
pfs_group=DHGroupChoices.GROUP_14
|
||||
),
|
||||
)
|
||||
IPSecPolicy.objects.bulk_create(ipsec_policies)
|
||||
for ipsec_policy in ipsec_policies:
|
||||
ipsec_policy.proposals.set(ipsec_proposals)
|
||||
|
||||
cls.create_data = [
|
||||
{
|
||||
'name': 'IPSec Policy 4',
|
||||
'pfs_group': DHGroupChoices.GROUP_16,
|
||||
'proposals': [ipsec_proposals[0].pk, ipsec_proposals[1].pk],
|
||||
},
|
||||
{
|
||||
'name': 'IPSec Policy 5',
|
||||
'pfs_group': DHGroupChoices.GROUP_16,
|
||||
'proposals': [ipsec_proposals[0].pk, ipsec_proposals[1].pk],
|
||||
},
|
||||
{
|
||||
'name': 'IPSec Policy 6',
|
||||
'pfs_group': DHGroupChoices.GROUP_16,
|
||||
'proposals': [ipsec_proposals[0].pk, ipsec_proposals[1].pk],
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
class IPSecProfileTest(APIViewTestCases.APIViewTestCase):
|
||||
model = IPSecProfile
|
||||
brief_fields = ['display', 'id', 'name', 'url']
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
|
||||
ike_proposal = IKEProposal.objects.create(
|
||||
name='IKE Proposal 1',
|
||||
authentication_method=AuthenticationMethodChoices.PRESHARED_KEYS,
|
||||
encryption_algorithm=EncryptionAlgorithmChoices.ENCRYPTION_AES128_CBC,
|
||||
authentication_algorithm=AuthenticationAlgorithmChoices.AUTH_HMAC_SHA1,
|
||||
group=DHGroupChoices.GROUP_14
|
||||
)
|
||||
|
||||
ipsec_proposal = IPSecProposal.objects.create(
|
||||
name='IPSec Proposal 1',
|
||||
encryption_algorithm=EncryptionAlgorithmChoices.ENCRYPTION_AES128_CBC,
|
||||
authentication_algorithm=AuthenticationAlgorithmChoices.AUTH_HMAC_SHA1
|
||||
)
|
||||
|
||||
ike_policies = (
|
||||
IKEPolicy(
|
||||
name='IKE Policy 1',
|
||||
version=IKEVersionChoices.VERSION_1,
|
||||
mode=IKEModeChoices.MAIN,
|
||||
),
|
||||
IKEPolicy(
|
||||
name='IKE Policy 2',
|
||||
version=IKEVersionChoices.VERSION_1,
|
||||
mode=IKEModeChoices.MAIN,
|
||||
),
|
||||
)
|
||||
IKEPolicy.objects.bulk_create(ike_policies)
|
||||
for ike_policy in ike_policies:
|
||||
ike_policy.proposals.add(ike_proposal)
|
||||
|
||||
ipsec_policies = (
|
||||
IPSecPolicy(
|
||||
name='IPSec Policy 1',
|
||||
pfs_group=DHGroupChoices.GROUP_14
|
||||
),
|
||||
IPSecPolicy(
|
||||
name='IPSec Policy 2',
|
||||
pfs_group=DHGroupChoices.GROUP_14
|
||||
),
|
||||
)
|
||||
IPSecPolicy.objects.bulk_create(ipsec_policies)
|
||||
for ipsec_policy in ipsec_policies:
|
||||
ipsec_policy.proposals.add(ipsec_proposal)
|
||||
|
||||
ipsec_profiles = (
|
||||
IPSecProfile(
|
||||
name='IPSec Profile 1',
|
||||
mode=IPSecModeChoices.ESP,
|
||||
ike_policy=ike_policies[0],
|
||||
ipsec_policy=ipsec_policies[0]
|
||||
),
|
||||
IPSecProfile(
|
||||
name='IPSec Profile 2',
|
||||
mode=IPSecModeChoices.ESP,
|
||||
ike_policy=ike_policies[0],
|
||||
ipsec_policy=ipsec_policies[0]
|
||||
),
|
||||
IPSecProfile(
|
||||
name='IPSec Profile 3',
|
||||
mode=IPSecModeChoices.ESP,
|
||||
ike_policy=ike_policies[0],
|
||||
ipsec_policy=ipsec_policies[0]
|
||||
),
|
||||
)
|
||||
IPSecProfile.objects.bulk_create(ipsec_profiles)
|
||||
|
||||
cls.create_data = [
|
||||
{
|
||||
'name': 'IPSec Profile 4',
|
||||
'mode': IPSecModeChoices.AH,
|
||||
'ike_policy': ike_policies[1].pk,
|
||||
'ipsec_policy': ipsec_policies[1].pk,
|
||||
},
|
||||
]
|
||||
|
||||
cls.bulk_update_data = {
|
||||
'mode': IPSecModeChoices.AH,
|
||||
'ike_policy': ike_policies[1].pk,
|
||||
'ipsec_policy': ipsec_policies[1].pk,
|
||||
'description': 'New description',
|
||||
}
|
592
netbox/vpn/tests/test_filtersets.py
Normal file
592
netbox/vpn/tests/test_filtersets.py
Normal file
@ -0,0 +1,592 @@
|
||||
from django.test import TestCase
|
||||
|
||||
from dcim.choices import InterfaceTypeChoices
|
||||
from dcim.models import Interface
|
||||
from ipam.models import IPAddress
|
||||
from virtualization.models import VMInterface
|
||||
from vpn.choices import *
|
||||
from vpn.filtersets import *
|
||||
from vpn.models import *
|
||||
from utilities.testing import ChangeLoggedFilterSetTests, create_test_device, create_test_virtualmachine
|
||||
|
||||
|
||||
class TunnelTestCase(TestCase, ChangeLoggedFilterSetTests):
|
||||
queryset = Tunnel.objects.all()
|
||||
filterset = TunnelFilterSet
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
ike_proposal = IKEProposal.objects.create(
|
||||
name='IKE Proposal 1',
|
||||
authentication_method=AuthenticationMethodChoices.PRESHARED_KEYS,
|
||||
encryption_algorithm=EncryptionAlgorithmChoices.ENCRYPTION_AES128_CBC,
|
||||
authentication_algorithm=AuthenticationAlgorithmChoices.AUTH_HMAC_SHA1,
|
||||
group=DHGroupChoices.GROUP_14
|
||||
)
|
||||
ike_policy = IKEPolicy.objects.create(
|
||||
name='IKE Policy 1',
|
||||
version=IKEVersionChoices.VERSION_1,
|
||||
mode=IKEModeChoices.MAIN,
|
||||
)
|
||||
ike_policy.proposals.add(ike_proposal)
|
||||
ipsec_proposal = IPSecProposal.objects.create(
|
||||
name='IPSec Proposal 1',
|
||||
encryption_algorithm=EncryptionAlgorithmChoices.ENCRYPTION_AES128_CBC,
|
||||
authentication_algorithm=AuthenticationAlgorithmChoices.AUTH_HMAC_SHA1
|
||||
)
|
||||
ipsec_policy = IPSecPolicy.objects.create(
|
||||
name='IPSec Policy 1',
|
||||
pfs_group=DHGroupChoices.GROUP_14
|
||||
)
|
||||
ipsec_policy.proposals.add(ipsec_proposal)
|
||||
ipsec_profiles = (
|
||||
IPSecProfile(
|
||||
name='IPSec Profile 1',
|
||||
mode=IPSecModeChoices.ESP,
|
||||
ike_policy=ike_policy,
|
||||
ipsec_policy=ipsec_policy
|
||||
),
|
||||
IPSecProfile(
|
||||
name='IPSec Profile 2',
|
||||
mode=IPSecModeChoices.ESP,
|
||||
ike_policy=ike_policy,
|
||||
ipsec_policy=ipsec_policy
|
||||
),
|
||||
)
|
||||
IPSecProfile.objects.bulk_create(ipsec_profiles)
|
||||
|
||||
tunnels = (
|
||||
Tunnel(
|
||||
name='Tunnel 1',
|
||||
status=TunnelStatusChoices.STATUS_ACTIVE,
|
||||
encapsulation=TunnelEncapsulationChoices.ENCAP_GRE,
|
||||
ipsec_profile=ipsec_profiles[0],
|
||||
tunnel_id=100
|
||||
),
|
||||
Tunnel(
|
||||
name='Tunnel 2',
|
||||
status=TunnelStatusChoices.STATUS_PLANNED,
|
||||
encapsulation=TunnelEncapsulationChoices.ENCAP_IP_IP,
|
||||
ipsec_profile=ipsec_profiles[0],
|
||||
tunnel_id=200
|
||||
),
|
||||
Tunnel(
|
||||
name='Tunnel 3',
|
||||
status=TunnelStatusChoices.STATUS_DISABLED,
|
||||
encapsulation=TunnelEncapsulationChoices.ENCAP_IPSEC_TUNNEL,
|
||||
ipsec_profile=None,
|
||||
tunnel_id=300
|
||||
),
|
||||
)
|
||||
Tunnel.objects.bulk_create(tunnels)
|
||||
|
||||
def test_name(self):
|
||||
params = {'name': ['Tunnel 1', 'Tunnel 2']}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||
|
||||
def test_status(self):
|
||||
params = {'status': [TunnelStatusChoices.STATUS_ACTIVE, TunnelStatusChoices.STATUS_PLANNED]}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||
|
||||
def test_encapsulation(self):
|
||||
params = {'encapsulation': [TunnelEncapsulationChoices.ENCAP_GRE, TunnelEncapsulationChoices.ENCAP_IP_IP]}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||
|
||||
def test_ipsec_profile(self):
|
||||
ipsec_profiles = IPSecProfile.objects.all()[:2]
|
||||
params = {'ipsec_profile_id': [ipsec_profiles[0].pk, ipsec_profiles[1].pk]}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||
params = {'ipsec_profile': [ipsec_profiles[0].name, ipsec_profiles[1].name]}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||
|
||||
def test_tunnel_id(self):
|
||||
params = {'tunnel_id': [100, 200]}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||
|
||||
|
||||
class TunnelTerminationTestCase(TestCase, ChangeLoggedFilterSetTests):
|
||||
queryset = TunnelTermination.objects.all()
|
||||
filterset = TunnelTerminationFilterSet
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
device = create_test_device('Device 1')
|
||||
interfaces = (
|
||||
Interface(device=device, name='Interface 1', type=InterfaceTypeChoices.TYPE_VIRTUAL),
|
||||
Interface(device=device, name='Interface 2', type=InterfaceTypeChoices.TYPE_VIRTUAL),
|
||||
Interface(device=device, name='Interface 3', type=InterfaceTypeChoices.TYPE_VIRTUAL),
|
||||
)
|
||||
Interface.objects.bulk_create(interfaces)
|
||||
|
||||
virtual_machine = create_test_virtualmachine('Virtual Machine 1')
|
||||
vm_interfaces = (
|
||||
VMInterface(virtual_machine=virtual_machine, name='Interface 1'),
|
||||
VMInterface(virtual_machine=virtual_machine, name='Interface 2'),
|
||||
VMInterface(virtual_machine=virtual_machine, name='Interface 3'),
|
||||
)
|
||||
VMInterface.objects.bulk_create(vm_interfaces)
|
||||
|
||||
ip_addresses = (
|
||||
IPAddress(address='192.168.0.1/32'),
|
||||
IPAddress(address='192.168.0.2/32'),
|
||||
IPAddress(address='192.168.0.3/32'),
|
||||
IPAddress(address='192.168.0.4/32'),
|
||||
IPAddress(address='192.168.0.5/32'),
|
||||
IPAddress(address='192.168.0.6/32'),
|
||||
)
|
||||
IPAddress.objects.bulk_create(ip_addresses)
|
||||
|
||||
tunnels = (
|
||||
Tunnel(
|
||||
name='Tunnel 1',
|
||||
status=TunnelStatusChoices.STATUS_ACTIVE,
|
||||
encapsulation=TunnelEncapsulationChoices.ENCAP_IP_IP
|
||||
),
|
||||
Tunnel(
|
||||
name='Tunnel 2',
|
||||
status=TunnelStatusChoices.STATUS_ACTIVE,
|
||||
encapsulation=TunnelEncapsulationChoices.ENCAP_IP_IP
|
||||
),
|
||||
Tunnel(
|
||||
name='Tunnel 3',
|
||||
status=TunnelStatusChoices.STATUS_ACTIVE,
|
||||
encapsulation=TunnelEncapsulationChoices.ENCAP_IP_IP
|
||||
),
|
||||
)
|
||||
Tunnel.objects.bulk_create(tunnels)
|
||||
|
||||
tunnel_terminations = (
|
||||
# Tunnel 1
|
||||
TunnelTermination(
|
||||
tunnel=tunnels[0],
|
||||
role=TunnelTerminationRoleChoices.ROLE_HUB,
|
||||
termination=interfaces[0],
|
||||
outside_ip=ip_addresses[0]
|
||||
),
|
||||
TunnelTermination(
|
||||
tunnel=tunnels[0],
|
||||
role=TunnelTerminationRoleChoices.ROLE_SPOKE,
|
||||
termination=vm_interfaces[0],
|
||||
outside_ip=ip_addresses[1]
|
||||
),
|
||||
# Tunnel 2
|
||||
TunnelTermination(
|
||||
tunnel=tunnels[1],
|
||||
role=TunnelTerminationRoleChoices.ROLE_HUB,
|
||||
termination=interfaces[1],
|
||||
outside_ip=ip_addresses[2]
|
||||
),
|
||||
TunnelTermination(
|
||||
tunnel=tunnels[1],
|
||||
role=TunnelTerminationRoleChoices.ROLE_SPOKE,
|
||||
termination=vm_interfaces[1],
|
||||
outside_ip=ip_addresses[3]
|
||||
),
|
||||
# Tunnel 3
|
||||
TunnelTermination(
|
||||
tunnel=tunnels[2],
|
||||
role=TunnelTerminationRoleChoices.ROLE_PEER,
|
||||
termination=interfaces[2],
|
||||
outside_ip=ip_addresses[4]
|
||||
),
|
||||
TunnelTermination(
|
||||
tunnel=tunnels[2],
|
||||
role=TunnelTerminationRoleChoices.ROLE_PEER,
|
||||
termination=vm_interfaces[2],
|
||||
outside_ip=ip_addresses[5]
|
||||
),
|
||||
)
|
||||
TunnelTermination.objects.bulk_create(tunnel_terminations)
|
||||
|
||||
def test_tunnel(self):
|
||||
tunnels = Tunnel.objects.all()[:2]
|
||||
params = {'tunnel_id': [tunnels[0].pk, tunnels[1].pk]}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
|
||||
params = {'tunnel': [tunnels[0].name, tunnels[1].name]}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
|
||||
|
||||
def test_role(self):
|
||||
params = {'role': [TunnelTerminationRoleChoices.ROLE_HUB, TunnelTerminationRoleChoices.ROLE_SPOKE]}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
|
||||
|
||||
def test_termination_type(self):
|
||||
params = {'termination_type': 'dcim.interface'}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 3)
|
||||
params = {'termination_type': 'virtualization.vminterface'}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 3)
|
||||
|
||||
def test_interface(self):
|
||||
interfaces = Interface.objects.all()[:2]
|
||||
params = {'interface_id': [interfaces[0].pk, interfaces[1].pk]}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||
params = {'interface': [interfaces[0].name, interfaces[1].name]}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||
|
||||
def test_vminterface(self):
|
||||
vm_interfaces = VMInterface.objects.all()[:2]
|
||||
params = {'vminterface_id': [vm_interfaces[0].pk, vm_interfaces[1].pk]}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||
params = {'vminterface': [vm_interfaces[0].name, vm_interfaces[1].name]}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||
|
||||
def test_outside_ip(self):
|
||||
ip_addresses = IPAddress.objects.all()[:2]
|
||||
params = {'outside_ip_id': [ip_addresses[0].pk, ip_addresses[1].pk]}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||
|
||||
|
||||
class IKEProposalTestCase(TestCase, ChangeLoggedFilterSetTests):
|
||||
queryset = IKEProposal.objects.all()
|
||||
filterset = IKEProposalFilterSet
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
ike_proposals = (
|
||||
IKEProposal(
|
||||
name='IKE Proposal 1',
|
||||
authentication_method=AuthenticationMethodChoices.PRESHARED_KEYS,
|
||||
encryption_algorithm=EncryptionAlgorithmChoices.ENCRYPTION_AES128_CBC,
|
||||
authentication_algorithm=AuthenticationAlgorithmChoices.AUTH_HMAC_SHA1,
|
||||
group=DHGroupChoices.GROUP_1,
|
||||
sa_lifetime=1000
|
||||
),
|
||||
IKEProposal(
|
||||
name='IKE Proposal 2',
|
||||
authentication_method=AuthenticationMethodChoices.CERTIFICATES,
|
||||
encryption_algorithm=EncryptionAlgorithmChoices.ENCRYPTION_AES192_CBC,
|
||||
authentication_algorithm=AuthenticationAlgorithmChoices.AUTH_HMAC_SHA256,
|
||||
group=DHGroupChoices.GROUP_2,
|
||||
sa_lifetime=2000
|
||||
),
|
||||
IKEProposal(
|
||||
name='IKE Proposal 3',
|
||||
authentication_method=AuthenticationMethodChoices.RSA_SIGNATURES,
|
||||
encryption_algorithm=EncryptionAlgorithmChoices.ENCRYPTION_AES256_CBC,
|
||||
authentication_algorithm=AuthenticationAlgorithmChoices.AUTH_HMAC_SHA512,
|
||||
group=DHGroupChoices.GROUP_5,
|
||||
sa_lifetime=3000
|
||||
),
|
||||
)
|
||||
IKEProposal.objects.bulk_create(ike_proposals)
|
||||
|
||||
def test_name(self):
|
||||
params = {'name': ['IKE Proposal 1', 'IKE Proposal 2']}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||
|
||||
def test_authentication_method(self):
|
||||
params = {'authentication_method': [
|
||||
AuthenticationMethodChoices.PRESHARED_KEYS, AuthenticationMethodChoices.CERTIFICATES
|
||||
]}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||
|
||||
def test_encryption_algorithm(self):
|
||||
params = {'encryption_algorithm': [
|
||||
EncryptionAlgorithmChoices.ENCRYPTION_AES128_CBC, EncryptionAlgorithmChoices.ENCRYPTION_AES192_CBC
|
||||
]}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||
|
||||
def test_authentication_algorithm(self):
|
||||
params = {'authentication_algorithm': [
|
||||
AuthenticationAlgorithmChoices.AUTH_HMAC_SHA1, AuthenticationAlgorithmChoices.AUTH_HMAC_SHA256
|
||||
]}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||
|
||||
def test_group(self):
|
||||
params = {'group': [DHGroupChoices.GROUP_1, DHGroupChoices.GROUP_2]}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||
|
||||
def test_sa_lifetime(self):
|
||||
params = {'sa_lifetime': [1000, 2000]}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||
|
||||
|
||||
class IKEPolicyTestCase(TestCase, ChangeLoggedFilterSetTests):
|
||||
queryset = IKEPolicy.objects.all()
|
||||
filterset = IKEPolicyFilterSet
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
ike_proposals = (
|
||||
IKEProposal(
|
||||
name='IKE Proposal 1',
|
||||
authentication_method=AuthenticationMethodChoices.PRESHARED_KEYS,
|
||||
encryption_algorithm=EncryptionAlgorithmChoices.ENCRYPTION_AES128_CBC,
|
||||
authentication_algorithm=AuthenticationAlgorithmChoices.AUTH_HMAC_SHA1,
|
||||
group=DHGroupChoices.GROUP_14
|
||||
),
|
||||
IKEProposal(
|
||||
name='IKE Proposal 2',
|
||||
authentication_method=AuthenticationMethodChoices.PRESHARED_KEYS,
|
||||
encryption_algorithm=EncryptionAlgorithmChoices.ENCRYPTION_AES128_CBC,
|
||||
authentication_algorithm=AuthenticationAlgorithmChoices.AUTH_HMAC_SHA1,
|
||||
group=DHGroupChoices.GROUP_14
|
||||
),
|
||||
IKEProposal(
|
||||
name='IKE Proposal 3',
|
||||
authentication_method=AuthenticationMethodChoices.PRESHARED_KEYS,
|
||||
encryption_algorithm=EncryptionAlgorithmChoices.ENCRYPTION_AES128_CBC,
|
||||
authentication_algorithm=AuthenticationAlgorithmChoices.AUTH_HMAC_SHA1,
|
||||
group=DHGroupChoices.GROUP_14
|
||||
),
|
||||
)
|
||||
IKEProposal.objects.bulk_create(ike_proposals)
|
||||
|
||||
ike_policies = (
|
||||
IKEPolicy(
|
||||
name='IKE Policy 1',
|
||||
version=IKEVersionChoices.VERSION_1,
|
||||
mode=IKEModeChoices.MAIN,
|
||||
),
|
||||
IKEPolicy(
|
||||
name='IKE Policy 2',
|
||||
version=IKEVersionChoices.VERSION_1,
|
||||
mode=IKEModeChoices.MAIN,
|
||||
),
|
||||
IKEPolicy(
|
||||
name='IKE Policy 3',
|
||||
version=IKEVersionChoices.VERSION_2,
|
||||
mode=IKEModeChoices.AGGRESSIVE,
|
||||
),
|
||||
)
|
||||
IKEPolicy.objects.bulk_create(ike_policies)
|
||||
ike_policies[0].proposals.add(ike_proposals[0])
|
||||
ike_policies[1].proposals.add(ike_proposals[1])
|
||||
ike_policies[2].proposals.add(ike_proposals[2])
|
||||
|
||||
def test_name(self):
|
||||
params = {'name': ['IKE Policy 1', 'IKE Policy 2']}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||
|
||||
def test_version(self):
|
||||
params = {'version': [IKEVersionChoices.VERSION_1]}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||
|
||||
def test_mode(self):
|
||||
params = {'mode': [IKEModeChoices.MAIN]}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||
|
||||
def test_proposal(self):
|
||||
proposals = IKEProposal.objects.all()[:2]
|
||||
params = {'proposal_id': [proposals[0].pk, proposals[1].pk]}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||
params = {'proposal': [proposals[0].name, proposals[1].name]}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||
|
||||
|
||||
class IPSecProposalTestCase(TestCase, ChangeLoggedFilterSetTests):
|
||||
queryset = IPSecProposal.objects.all()
|
||||
filterset = IPSecProposalFilterSet
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
ipsec_proposals = (
|
||||
IPSecProposal(
|
||||
name='IPSec Proposal 1',
|
||||
encryption_algorithm=EncryptionAlgorithmChoices.ENCRYPTION_AES128_CBC,
|
||||
authentication_algorithm=AuthenticationAlgorithmChoices.AUTH_HMAC_SHA1,
|
||||
sa_lifetime_seconds=1000,
|
||||
sa_lifetime_data=1000
|
||||
),
|
||||
IPSecProposal(
|
||||
name='IPSec Proposal 2',
|
||||
encryption_algorithm=EncryptionAlgorithmChoices.ENCRYPTION_AES192_CBC,
|
||||
authentication_algorithm=AuthenticationAlgorithmChoices.AUTH_HMAC_SHA256,
|
||||
sa_lifetime_seconds=2000,
|
||||
sa_lifetime_data=2000
|
||||
),
|
||||
IPSecProposal(
|
||||
name='IPSec Proposal 3',
|
||||
encryption_algorithm=EncryptionAlgorithmChoices.ENCRYPTION_AES256_CBC,
|
||||
authentication_algorithm=AuthenticationAlgorithmChoices.AUTH_HMAC_SHA512,
|
||||
sa_lifetime_seconds=3000,
|
||||
sa_lifetime_data=3000
|
||||
),
|
||||
)
|
||||
IPSecProposal.objects.bulk_create(ipsec_proposals)
|
||||
|
||||
def test_name(self):
|
||||
params = {'name': ['IPSec Proposal 1', 'IPSec Proposal 2']}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||
|
||||
def test_encryption_algorithm(self):
|
||||
params = {'encryption_algorithm': [
|
||||
EncryptionAlgorithmChoices.ENCRYPTION_AES128_CBC, EncryptionAlgorithmChoices.ENCRYPTION_AES192_CBC
|
||||
]}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||
|
||||
def test_authentication_algorithm(self):
|
||||
params = {'authentication_algorithm': [
|
||||
AuthenticationAlgorithmChoices.AUTH_HMAC_SHA1, AuthenticationAlgorithmChoices.AUTH_HMAC_SHA256
|
||||
]}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||
|
||||
def test_sa_lifetime_seconds(self):
|
||||
params = {'sa_lifetime_seconds': [1000, 2000]}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||
|
||||
def test_sa_lifetime_data(self):
|
||||
params = {'sa_lifetime_data': [1000, 2000]}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||
|
||||
|
||||
class IPSecPolicyTestCase(TestCase, ChangeLoggedFilterSetTests):
|
||||
queryset = IPSecPolicy.objects.all()
|
||||
filterset = IPSecPolicyFilterSet
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
ipsec_proposals = (
|
||||
IPSecProposal(
|
||||
name='IPSec Policy 1',
|
||||
encryption_algorithm=EncryptionAlgorithmChoices.ENCRYPTION_AES128_CBC,
|
||||
authentication_algorithm=AuthenticationAlgorithmChoices.AUTH_HMAC_SHA1
|
||||
),
|
||||
IPSecProposal(
|
||||
name='IPSec Proposal 2',
|
||||
encryption_algorithm=EncryptionAlgorithmChoices.ENCRYPTION_AES128_CBC,
|
||||
authentication_algorithm=AuthenticationAlgorithmChoices.AUTH_HMAC_SHA1
|
||||
),
|
||||
IPSecProposal(
|
||||
name='IPSec Proposal 3',
|
||||
encryption_algorithm=EncryptionAlgorithmChoices.ENCRYPTION_AES128_CBC,
|
||||
authentication_algorithm=AuthenticationAlgorithmChoices.AUTH_HMAC_SHA1
|
||||
),
|
||||
)
|
||||
IPSecProposal.objects.bulk_create(ipsec_proposals)
|
||||
|
||||
ipsec_policies = (
|
||||
IPSecPolicy(
|
||||
name='IPSec Policy 1',
|
||||
pfs_group=DHGroupChoices.GROUP_1
|
||||
),
|
||||
IPSecPolicy(
|
||||
name='IPSec Policy 2',
|
||||
pfs_group=DHGroupChoices.GROUP_2
|
||||
),
|
||||
IPSecPolicy(
|
||||
name='IPSec Policy 3',
|
||||
pfs_group=DHGroupChoices.GROUP_5
|
||||
),
|
||||
)
|
||||
IPSecPolicy.objects.bulk_create(ipsec_policies)
|
||||
ipsec_policies[0].proposals.add(ipsec_proposals[0])
|
||||
ipsec_policies[1].proposals.add(ipsec_proposals[1])
|
||||
ipsec_policies[2].proposals.add(ipsec_proposals[2])
|
||||
|
||||
def test_name(self):
|
||||
params = {'name': ['IPSec Policy 1', 'IPSec Policy 2']}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||
|
||||
def test_pfs_group(self):
|
||||
params = {'pfs_group': [DHGroupChoices.GROUP_1, DHGroupChoices.GROUP_2]}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||
|
||||
def test_proposal(self):
|
||||
proposals = IPSecProposal.objects.all()[:2]
|
||||
params = {'proposal_id': [proposals[0].pk, proposals[1].pk]}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||
params = {'proposal': [proposals[0].name, proposals[1].name]}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||
|
||||
|
||||
class IPSecProfileTestCase(TestCase, ChangeLoggedFilterSetTests):
|
||||
queryset = IPSecProfile.objects.all()
|
||||
filterset = IPSecProfileFilterSet
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
ike_proposal = IKEProposal.objects.create(
|
||||
name='IKE Proposal 1',
|
||||
authentication_method=AuthenticationMethodChoices.PRESHARED_KEYS,
|
||||
encryption_algorithm=EncryptionAlgorithmChoices.ENCRYPTION_AES128_CBC,
|
||||
authentication_algorithm=AuthenticationAlgorithmChoices.AUTH_HMAC_SHA1,
|
||||
group=DHGroupChoices.GROUP_14
|
||||
)
|
||||
ipsec_proposal = IPSecProposal.objects.create(
|
||||
name='IPSec Proposal 1',
|
||||
encryption_algorithm=EncryptionAlgorithmChoices.ENCRYPTION_AES128_CBC,
|
||||
authentication_algorithm=AuthenticationAlgorithmChoices.AUTH_HMAC_SHA1
|
||||
)
|
||||
|
||||
ike_policies = (
|
||||
IKEPolicy(
|
||||
name='IKE Policy 1',
|
||||
version=IKEVersionChoices.VERSION_1,
|
||||
mode=IKEModeChoices.MAIN,
|
||||
),
|
||||
IKEPolicy(
|
||||
name='IKE Policy 2',
|
||||
version=IKEVersionChoices.VERSION_1,
|
||||
mode=IKEModeChoices.MAIN,
|
||||
),
|
||||
IKEPolicy(
|
||||
name='IKE Policy 3',
|
||||
version=IKEVersionChoices.VERSION_1,
|
||||
mode=IKEModeChoices.MAIN,
|
||||
),
|
||||
)
|
||||
IKEPolicy.objects.bulk_create(ike_policies)
|
||||
for ike_policy in ike_policies:
|
||||
ike_policy.proposals.add(ike_proposal)
|
||||
|
||||
ipsec_policies = (
|
||||
IPSecPolicy(
|
||||
name='IPSec Policy 1',
|
||||
pfs_group=DHGroupChoices.GROUP_14
|
||||
),
|
||||
IPSecPolicy(
|
||||
name='IPSec Policy 2',
|
||||
pfs_group=DHGroupChoices.GROUP_14
|
||||
),
|
||||
IPSecPolicy(
|
||||
name='IPSec Policy 3',
|
||||
pfs_group=DHGroupChoices.GROUP_14
|
||||
),
|
||||
)
|
||||
IPSecPolicy.objects.bulk_create(ipsec_policies)
|
||||
for ipsec_policy in ipsec_policies:
|
||||
ipsec_policy.proposals.add(ipsec_proposal)
|
||||
|
||||
ipsec_profiles = (
|
||||
IPSecProfile(
|
||||
name='IPSec Profile 1',
|
||||
mode=IPSecModeChoices.ESP,
|
||||
ike_policy=ike_policies[0],
|
||||
ipsec_policy=ipsec_policies[0]
|
||||
),
|
||||
IPSecProfile(
|
||||
name='IPSec Profile 2',
|
||||
mode=IPSecModeChoices.ESP,
|
||||
ike_policy=ike_policies[1],
|
||||
ipsec_policy=ipsec_policies[1]
|
||||
),
|
||||
IPSecProfile(
|
||||
name='IPSec Profile 3',
|
||||
mode=IPSecModeChoices.AH,
|
||||
ike_policy=ike_policies[2],
|
||||
ipsec_policy=ipsec_policies[2]
|
||||
),
|
||||
)
|
||||
IPSecProfile.objects.bulk_create(ipsec_profiles)
|
||||
|
||||
def test_name(self):
|
||||
params = {'name': ['IPSec Profile 1', 'IPSec Profile 2']}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||
|
||||
def test_mode(self):
|
||||
params = {'mode': [IPSecModeChoices.ESP]}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||
|
||||
def test_ike_policy(self):
|
||||
ike_policies = IKEPolicy.objects.all()[:2]
|
||||
params = {'ike_policy_id': [ike_policies[0].pk, ike_policies[1].pk]}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||
params = {'ike_policy': [ike_policies[0].name, ike_policies[1].name]}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||
|
||||
def test_ipsec_policy(self):
|
||||
ipsec_policies = IPSecPolicy.objects.all()[:2]
|
||||
params = {'ipsec_policy_id': [ipsec_policies[0].pk, ipsec_policies[1].pk]}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||
params = {'ipsec_policy': [ipsec_policies[0].name, ipsec_policies[1].name]}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
508
netbox/vpn/tests/test_views.py
Normal file
508
netbox/vpn/tests/test_views.py
Normal file
@ -0,0 +1,508 @@
|
||||
from dcim.choices import InterfaceTypeChoices
|
||||
from dcim.models import Interface
|
||||
from vpn.choices import *
|
||||
from vpn.models import *
|
||||
from utilities.testing import ViewTestCases, create_tags, create_test_device
|
||||
|
||||
|
||||
class TunnelTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
||||
model = Tunnel
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
|
||||
tunnels = (
|
||||
Tunnel(
|
||||
name='Tunnel 1',
|
||||
status=TunnelStatusChoices.STATUS_ACTIVE,
|
||||
encapsulation=TunnelEncapsulationChoices.ENCAP_IP_IP
|
||||
),
|
||||
Tunnel(
|
||||
name='Tunnel 2',
|
||||
status=TunnelStatusChoices.STATUS_ACTIVE,
|
||||
encapsulation=TunnelEncapsulationChoices.ENCAP_IP_IP
|
||||
),
|
||||
Tunnel(
|
||||
name='Tunnel 3',
|
||||
status=TunnelStatusChoices.STATUS_ACTIVE,
|
||||
encapsulation=TunnelEncapsulationChoices.ENCAP_IP_IP
|
||||
),
|
||||
)
|
||||
Tunnel.objects.bulk_create(tunnels)
|
||||
|
||||
tags = create_tags('Alpha', 'Bravo', 'Charlie')
|
||||
|
||||
cls.form_data = {
|
||||
'name': 'Tunnel X',
|
||||
'description': 'New tunnel',
|
||||
'status': TunnelStatusChoices.STATUS_PLANNED,
|
||||
'encapsulation': TunnelEncapsulationChoices.ENCAP_GRE,
|
||||
'tags': [t.pk for t in tags],
|
||||
}
|
||||
|
||||
cls.csv_data = (
|
||||
"name,status,encapsulation",
|
||||
"Tunnel 4,planned,gre",
|
||||
"Tunnel 5,planned,gre",
|
||||
"Tunnel 6,planned,gre",
|
||||
)
|
||||
|
||||
cls.csv_update_data = (
|
||||
"id,status,encapsulation",
|
||||
f"{tunnels[0].pk},active,ip-ip",
|
||||
f"{tunnels[1].pk},active,ip-ip",
|
||||
f"{tunnels[2].pk},active,ip-ip",
|
||||
)
|
||||
|
||||
cls.bulk_edit_data = {
|
||||
'description': 'New description',
|
||||
'status': TunnelStatusChoices.STATUS_DISABLED,
|
||||
'encapsulation': TunnelEncapsulationChoices.ENCAP_GRE,
|
||||
}
|
||||
|
||||
|
||||
class TunnelTerminationTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
||||
model = TunnelTermination
|
||||
# TODO: Workaround for conflict between form field and GFK
|
||||
validation_excluded_fields = ('termination',)
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
device = create_test_device('Device 1')
|
||||
interfaces = (
|
||||
Interface(device=device, name='Interface 1', type=InterfaceTypeChoices.TYPE_VIRTUAL),
|
||||
Interface(device=device, name='Interface 2', type=InterfaceTypeChoices.TYPE_VIRTUAL),
|
||||
Interface(device=device, name='Interface 3', type=InterfaceTypeChoices.TYPE_VIRTUAL),
|
||||
Interface(device=device, name='Interface 4', type=InterfaceTypeChoices.TYPE_VIRTUAL),
|
||||
Interface(device=device, name='Interface 5', type=InterfaceTypeChoices.TYPE_VIRTUAL),
|
||||
Interface(device=device, name='Interface 6', type=InterfaceTypeChoices.TYPE_VIRTUAL),
|
||||
Interface(device=device, name='Interface 7', type=InterfaceTypeChoices.TYPE_VIRTUAL),
|
||||
)
|
||||
Interface.objects.bulk_create(interfaces)
|
||||
|
||||
tunnel = Tunnel.objects.create(
|
||||
name='Tunnel 1',
|
||||
status=TunnelStatusChoices.STATUS_ACTIVE,
|
||||
encapsulation=TunnelEncapsulationChoices.ENCAP_IP_IP
|
||||
)
|
||||
|
||||
tunnel_terminations = (
|
||||
TunnelTermination(
|
||||
tunnel=tunnel,
|
||||
role=TunnelTerminationRoleChoices.ROLE_HUB,
|
||||
termination=interfaces[0]
|
||||
),
|
||||
TunnelTermination(
|
||||
tunnel=tunnel,
|
||||
role=TunnelTerminationRoleChoices.ROLE_SPOKE,
|
||||
termination=interfaces[1]
|
||||
),
|
||||
TunnelTermination(
|
||||
tunnel=tunnel,
|
||||
role=TunnelTerminationRoleChoices.ROLE_SPOKE,
|
||||
termination=interfaces[2]
|
||||
),
|
||||
)
|
||||
TunnelTermination.objects.bulk_create(tunnel_terminations)
|
||||
|
||||
tags = create_tags('Alpha', 'Bravo', 'Charlie')
|
||||
|
||||
cls.form_data = {
|
||||
'tunnel': tunnel.pk,
|
||||
'role': TunnelTerminationRoleChoices.ROLE_PEER,
|
||||
'type': TunnelTerminationTypeChoices.TYPE_DEVICE,
|
||||
'parent': device.pk,
|
||||
'termination': interfaces[6].pk,
|
||||
'tags': [t.pk for t in tags],
|
||||
}
|
||||
|
||||
cls.csv_data = (
|
||||
"tunnel,role,device,termination",
|
||||
"Tunnel 1,peer,Device 1,Interface 4",
|
||||
"Tunnel 1,peer,Device 1,Interface 5",
|
||||
"Tunnel 1,peer,Device 1,Interface 6",
|
||||
)
|
||||
|
||||
cls.csv_update_data = (
|
||||
"id,role",
|
||||
f"{tunnel_terminations[0].pk},peer",
|
||||
f"{tunnel_terminations[1].pk},peer",
|
||||
f"{tunnel_terminations[2].pk},peer",
|
||||
)
|
||||
|
||||
cls.bulk_edit_data = {
|
||||
'role': TunnelTerminationRoleChoices.ROLE_PEER,
|
||||
}
|
||||
|
||||
|
||||
class IKEProposalTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
||||
model = IKEProposal
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
|
||||
ike_proposals = (
|
||||
IKEProposal(
|
||||
name='IKE Proposal 1',
|
||||
authentication_method=AuthenticationMethodChoices.PRESHARED_KEYS,
|
||||
encryption_algorithm=EncryptionAlgorithmChoices.ENCRYPTION_AES128_CBC,
|
||||
authentication_algorithm=AuthenticationAlgorithmChoices.AUTH_HMAC_SHA1,
|
||||
group=DHGroupChoices.GROUP_14
|
||||
),
|
||||
IKEProposal(
|
||||
name='IKE Proposal 2',
|
||||
authentication_method=AuthenticationMethodChoices.PRESHARED_KEYS,
|
||||
encryption_algorithm=EncryptionAlgorithmChoices.ENCRYPTION_AES128_CBC,
|
||||
authentication_algorithm=AuthenticationAlgorithmChoices.AUTH_HMAC_SHA1,
|
||||
group=DHGroupChoices.GROUP_14
|
||||
),
|
||||
IKEProposal(
|
||||
name='IKE Proposal 3',
|
||||
authentication_method=AuthenticationMethodChoices.PRESHARED_KEYS,
|
||||
encryption_algorithm=EncryptionAlgorithmChoices.ENCRYPTION_AES128_CBC,
|
||||
authentication_algorithm=AuthenticationAlgorithmChoices.AUTH_HMAC_SHA1,
|
||||
group=DHGroupChoices.GROUP_14
|
||||
),
|
||||
)
|
||||
IKEProposal.objects.bulk_create(ike_proposals)
|
||||
|
||||
tags = create_tags('Alpha', 'Bravo', 'Charlie')
|
||||
|
||||
cls.form_data = {
|
||||
'name': 'IKE Proposal X',
|
||||
'authentication_method': AuthenticationMethodChoices.CERTIFICATES,
|
||||
'encryption_algorithm': EncryptionAlgorithmChoices.ENCRYPTION_AES192_CBC,
|
||||
'authentication_algorithm': AuthenticationAlgorithmChoices.AUTH_HMAC_SHA256,
|
||||
'group': DHGroupChoices.GROUP_19,
|
||||
'tags': [t.pk for t in tags],
|
||||
}
|
||||
|
||||
cls.csv_data = (
|
||||
"name,authentication_method,encryption_algorithm,authentication_algorithm,group",
|
||||
"IKE Proposal 4,preshared-keys,aes-128-cbc,hmac-sha1,14",
|
||||
"IKE Proposal 5,preshared-keys,aes-128-cbc,hmac-sha1,14",
|
||||
"IKE Proposal 6,preshared-keys,aes-128-cbc,hmac-sha1,14",
|
||||
)
|
||||
|
||||
cls.csv_update_data = (
|
||||
"id,description",
|
||||
f"{ike_proposals[0].pk},New description",
|
||||
f"{ike_proposals[1].pk},New description",
|
||||
f"{ike_proposals[2].pk},New description",
|
||||
)
|
||||
|
||||
cls.bulk_edit_data = {
|
||||
'description': 'New description',
|
||||
'authentication_method': AuthenticationMethodChoices.CERTIFICATES,
|
||||
'encryption_algorithm': EncryptionAlgorithmChoices.ENCRYPTION_AES192_CBC,
|
||||
'authentication_algorithm': AuthenticationAlgorithmChoices.AUTH_HMAC_SHA256,
|
||||
'group': DHGroupChoices.GROUP_19
|
||||
}
|
||||
|
||||
|
||||
class IKEPolicyTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
||||
model = IKEPolicy
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
|
||||
ike_proposals = (
|
||||
IKEProposal(
|
||||
name='IKE Proposal 1',
|
||||
authentication_method=AuthenticationMethodChoices.PRESHARED_KEYS,
|
||||
encryption_algorithm=EncryptionAlgorithmChoices.ENCRYPTION_AES128_CBC,
|
||||
authentication_algorithm=AuthenticationAlgorithmChoices.AUTH_HMAC_SHA1,
|
||||
group=DHGroupChoices.GROUP_14
|
||||
),
|
||||
IKEProposal(
|
||||
name='IKE Proposal 2',
|
||||
authentication_method=AuthenticationMethodChoices.PRESHARED_KEYS,
|
||||
encryption_algorithm=EncryptionAlgorithmChoices.ENCRYPTION_AES128_CBC,
|
||||
authentication_algorithm=AuthenticationAlgorithmChoices.AUTH_HMAC_SHA1,
|
||||
group=DHGroupChoices.GROUP_14
|
||||
),
|
||||
)
|
||||
IKEProposal.objects.bulk_create(ike_proposals)
|
||||
|
||||
ike_policies = (
|
||||
IKEPolicy(
|
||||
name='IKE Policy 1',
|
||||
version=IKEVersionChoices.VERSION_1,
|
||||
mode=IKEModeChoices.MAIN,
|
||||
),
|
||||
IKEPolicy(
|
||||
name='IKE Policy 2',
|
||||
version=IKEVersionChoices.VERSION_1,
|
||||
mode=IKEModeChoices.MAIN,
|
||||
),
|
||||
IKEPolicy(
|
||||
name='IKE Policy 3',
|
||||
version=IKEVersionChoices.VERSION_1,
|
||||
mode=IKEModeChoices.MAIN,
|
||||
),
|
||||
)
|
||||
IKEPolicy.objects.bulk_create(ike_policies)
|
||||
for ike_policy in ike_policies:
|
||||
ike_policy.proposals.set(ike_proposals)
|
||||
|
||||
tags = create_tags('Alpha', 'Bravo', 'Charlie')
|
||||
|
||||
cls.form_data = {
|
||||
'name': 'IKE Policy X',
|
||||
'version': IKEVersionChoices.VERSION_2,
|
||||
'mode': IKEModeChoices.AGGRESSIVE,
|
||||
'proposals': [p.pk for p in ike_proposals],
|
||||
'tags': [t.pk for t in tags],
|
||||
}
|
||||
|
||||
ike_proposal_names = ','.join([p.name for p in ike_proposals])
|
||||
cls.csv_data = (
|
||||
"name,version,mode,proposals",
|
||||
f"IKE Proposal 4,2,aggressive,\"{ike_proposal_names}\"",
|
||||
f"IKE Proposal 5,2,aggressive,\"{ike_proposal_names}\"",
|
||||
f"IKE Proposal 6,2,aggressive,\"{ike_proposal_names}\"",
|
||||
)
|
||||
|
||||
cls.csv_update_data = (
|
||||
"id,description",
|
||||
f"{ike_policies[0].pk},New description",
|
||||
f"{ike_policies[1].pk},New description",
|
||||
f"{ike_policies[2].pk},New description",
|
||||
)
|
||||
|
||||
cls.bulk_edit_data = {
|
||||
'description': 'New description',
|
||||
'version': IKEVersionChoices.VERSION_2,
|
||||
'mode': IKEModeChoices.AGGRESSIVE,
|
||||
}
|
||||
|
||||
|
||||
class IPSecProposalTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
||||
model = IPSecProposal
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
|
||||
ipsec_proposals = (
|
||||
IPSecProposal(
|
||||
name='IPSec Proposal 1',
|
||||
encryption_algorithm=EncryptionAlgorithmChoices.ENCRYPTION_AES128_CBC,
|
||||
authentication_algorithm=AuthenticationAlgorithmChoices.AUTH_HMAC_SHA1,
|
||||
),
|
||||
IPSecProposal(
|
||||
name='IPSec Proposal 2',
|
||||
encryption_algorithm=EncryptionAlgorithmChoices.ENCRYPTION_AES128_CBC,
|
||||
authentication_algorithm=AuthenticationAlgorithmChoices.AUTH_HMAC_SHA1,
|
||||
),
|
||||
IPSecProposal(
|
||||
name='IPSec Proposal 3',
|
||||
encryption_algorithm=EncryptionAlgorithmChoices.ENCRYPTION_AES128_CBC,
|
||||
authentication_algorithm=AuthenticationAlgorithmChoices.AUTH_HMAC_SHA1,
|
||||
),
|
||||
)
|
||||
IPSecProposal.objects.bulk_create(ipsec_proposals)
|
||||
|
||||
tags = create_tags('Alpha', 'Bravo', 'Charlie')
|
||||
|
||||
cls.form_data = {
|
||||
'name': 'IPSec Proposal X',
|
||||
'encryption_algorithm': EncryptionAlgorithmChoices.ENCRYPTION_AES192_CBC,
|
||||
'authentication_algorithm': AuthenticationAlgorithmChoices.AUTH_HMAC_SHA256,
|
||||
'sa_lifetime_seconds': 3600,
|
||||
'sa_lifetime_data': 1000000,
|
||||
'tags': [t.pk for t in tags],
|
||||
}
|
||||
|
||||
cls.csv_data = (
|
||||
"name,encryption_algorithm,authentication_algorithm,sa_lifetime_seconds,sa_lifetime_data",
|
||||
"IKE Proposal 4,aes-128-cbc,hmac-sha1,3600,1000000",
|
||||
"IKE Proposal 5,aes-128-cbc,hmac-sha1,3600,1000000",
|
||||
"IKE Proposal 6,aes-128-cbc,hmac-sha1,3600,1000000",
|
||||
)
|
||||
|
||||
cls.csv_update_data = (
|
||||
"id,description",
|
||||
f"{ipsec_proposals[0].pk},New description",
|
||||
f"{ipsec_proposals[1].pk},New description",
|
||||
f"{ipsec_proposals[2].pk},New description",
|
||||
)
|
||||
|
||||
cls.bulk_edit_data = {
|
||||
'description': 'New description',
|
||||
'encryption_algorithm': EncryptionAlgorithmChoices.ENCRYPTION_AES192_CBC,
|
||||
'authentication_algorithm': AuthenticationAlgorithmChoices.AUTH_HMAC_SHA256,
|
||||
'sa_lifetime_seconds': 3600,
|
||||
'sa_lifetime_data': 1000000,
|
||||
}
|
||||
|
||||
|
||||
class IPSecPolicyTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
||||
model = IPSecPolicy
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
|
||||
ipsec_proposals = (
|
||||
IPSecProposal(
|
||||
name='IPSec Policy 1',
|
||||
encryption_algorithm=EncryptionAlgorithmChoices.ENCRYPTION_AES128_CBC,
|
||||
authentication_algorithm=AuthenticationAlgorithmChoices.AUTH_HMAC_SHA1
|
||||
),
|
||||
IPSecProposal(
|
||||
name='IPSec Proposal 2',
|
||||
encryption_algorithm=EncryptionAlgorithmChoices.ENCRYPTION_AES128_CBC,
|
||||
authentication_algorithm=AuthenticationAlgorithmChoices.AUTH_HMAC_SHA1
|
||||
),
|
||||
)
|
||||
IPSecProposal.objects.bulk_create(ipsec_proposals)
|
||||
|
||||
ipsec_policies = (
|
||||
IPSecPolicy(
|
||||
name='IPSec Policy 1',
|
||||
pfs_group=DHGroupChoices.GROUP_14
|
||||
),
|
||||
IPSecPolicy(
|
||||
name='IPSec Policy 2',
|
||||
pfs_group=DHGroupChoices.GROUP_14
|
||||
),
|
||||
IPSecPolicy(
|
||||
name='IPSec Policy 3',
|
||||
pfs_group=DHGroupChoices.GROUP_14
|
||||
),
|
||||
)
|
||||
IPSecPolicy.objects.bulk_create(ipsec_policies)
|
||||
for ipsec_policy in ipsec_policies:
|
||||
ipsec_policy.proposals.set(ipsec_proposals)
|
||||
|
||||
tags = create_tags('Alpha', 'Bravo', 'Charlie')
|
||||
|
||||
cls.form_data = {
|
||||
'name': 'IPSec Policy X',
|
||||
'pfs_group': DHGroupChoices.GROUP_5,
|
||||
'proposals': [p.pk for p in ipsec_proposals],
|
||||
'tags': [t.pk for t in tags],
|
||||
}
|
||||
|
||||
ipsec_proposal_names = ','.join([p.name for p in ipsec_proposals])
|
||||
cls.csv_data = (
|
||||
"name,pfs_group,proposals",
|
||||
f"IKE Proposal 4,19,\"{ipsec_proposal_names}\"",
|
||||
f"IKE Proposal 5,19,\"{ipsec_proposal_names}\"",
|
||||
f"IKE Proposal 6,19,\"{ipsec_proposal_names}\"",
|
||||
)
|
||||
|
||||
cls.csv_update_data = (
|
||||
"id,description",
|
||||
f"{ipsec_policies[0].pk},New description",
|
||||
f"{ipsec_policies[1].pk},New description",
|
||||
f"{ipsec_policies[2].pk},New description",
|
||||
)
|
||||
|
||||
cls.bulk_edit_data = {
|
||||
'description': 'New description',
|
||||
'pfs_group': DHGroupChoices.GROUP_5,
|
||||
}
|
||||
|
||||
|
||||
class IPSecProfileTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
||||
model = IPSecProfile
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
|
||||
ike_proposal = IKEProposal.objects.create(
|
||||
name='IKE Proposal 1',
|
||||
authentication_method=AuthenticationMethodChoices.PRESHARED_KEYS,
|
||||
encryption_algorithm=EncryptionAlgorithmChoices.ENCRYPTION_AES128_CBC,
|
||||
authentication_algorithm=AuthenticationAlgorithmChoices.AUTH_HMAC_SHA1,
|
||||
group=DHGroupChoices.GROUP_14
|
||||
)
|
||||
|
||||
ipsec_proposal = IPSecProposal.objects.create(
|
||||
name='IPSec Proposal 1',
|
||||
encryption_algorithm=EncryptionAlgorithmChoices.ENCRYPTION_AES128_CBC,
|
||||
authentication_algorithm=AuthenticationAlgorithmChoices.AUTH_HMAC_SHA1
|
||||
)
|
||||
|
||||
ike_policies = (
|
||||
IKEPolicy(
|
||||
name='IKE Policy 1',
|
||||
version=IKEVersionChoices.VERSION_1,
|
||||
mode=IKEModeChoices.MAIN,
|
||||
),
|
||||
IKEPolicy(
|
||||
name='IKE Policy 2',
|
||||
version=IKEVersionChoices.VERSION_1,
|
||||
mode=IKEModeChoices.MAIN,
|
||||
),
|
||||
)
|
||||
IKEPolicy.objects.bulk_create(ike_policies)
|
||||
for ike_policy in ike_policies:
|
||||
ike_policy.proposals.add(ike_proposal)
|
||||
|
||||
ipsec_policies = (
|
||||
IPSecPolicy(
|
||||
name='IPSec Policy 1',
|
||||
pfs_group=DHGroupChoices.GROUP_14
|
||||
),
|
||||
IPSecPolicy(
|
||||
name='IPSec Policy 2',
|
||||
pfs_group=DHGroupChoices.GROUP_14
|
||||
),
|
||||
)
|
||||
IPSecPolicy.objects.bulk_create(ipsec_policies)
|
||||
for ipsec_policy in ipsec_policies:
|
||||
ipsec_policy.proposals.add(ipsec_proposal)
|
||||
|
||||
ipsec_profiles = (
|
||||
IPSecProfile(
|
||||
name='IPSec Profile 1',
|
||||
mode=IPSecModeChoices.ESP,
|
||||
ike_policy=ike_policies[0],
|
||||
ipsec_policy=ipsec_policies[0]
|
||||
),
|
||||
IPSecProfile(
|
||||
name='IPSec Profile 2',
|
||||
mode=IPSecModeChoices.ESP,
|
||||
ike_policy=ike_policies[0],
|
||||
ipsec_policy=ipsec_policies[0]
|
||||
),
|
||||
IPSecProfile(
|
||||
name='IPSec Profile 3',
|
||||
mode=IPSecModeChoices.ESP,
|
||||
ike_policy=ike_policies[0],
|
||||
ipsec_policy=ipsec_policies[0]
|
||||
),
|
||||
)
|
||||
IPSecProfile.objects.bulk_create(ipsec_profiles)
|
||||
|
||||
tags = create_tags('Alpha', 'Bravo', 'Charlie')
|
||||
|
||||
cls.form_data = {
|
||||
'name': 'IPSec Profile X',
|
||||
'mode': IPSecModeChoices.AH,
|
||||
'ike_policy': ike_policies[1].pk,
|
||||
'ipsec_policy': ipsec_policies[1].pk,
|
||||
'tags': [t.pk for t in tags],
|
||||
}
|
||||
|
||||
cls.csv_data = (
|
||||
"name,mode,ike_policy,ipsec_policy",
|
||||
f"IKE Proposal 4,ah,IKE Policy 2,IPSec Policy 2",
|
||||
f"IKE Proposal 5,ah,IKE Policy 2,IPSec Policy 2",
|
||||
f"IKE Proposal 6,ah,IKE Policy 2,IPSec Policy 2",
|
||||
)
|
||||
|
||||
cls.csv_update_data = (
|
||||
"id,description",
|
||||
f"{ipsec_profiles[0].pk},New description",
|
||||
f"{ipsec_profiles[1].pk},New description",
|
||||
f"{ipsec_profiles[2].pk},New description",
|
||||
)
|
||||
|
||||
cls.bulk_edit_data = {
|
||||
'description': 'New description',
|
||||
'mode': IPSecModeChoices.AH,
|
||||
'ike_policy': ike_policies[1].pk,
|
||||
'ipsec_policy': ipsec_policies[1].pk,
|
||||
}
|
65
netbox/vpn/urls.py
Normal file
65
netbox/vpn/urls.py
Normal file
@ -0,0 +1,65 @@
|
||||
from django.urls import include, path
|
||||
|
||||
from utilities.urls import get_model_urls
|
||||
from . import views
|
||||
|
||||
app_name = 'vpn'
|
||||
urlpatterns = [
|
||||
|
||||
# Tunnels
|
||||
path('tunnels/', views.TunnelListView.as_view(), name='tunnel_list'),
|
||||
path('tunnels/add/', views.TunnelEditView.as_view(), name='tunnel_add'),
|
||||
path('tunnels/import/', views.TunnelBulkImportView.as_view(), name='tunnel_import'),
|
||||
path('tunnels/edit/', views.TunnelBulkEditView.as_view(), name='tunnel_bulk_edit'),
|
||||
path('tunnels/delete/', views.TunnelBulkDeleteView.as_view(), name='tunnel_bulk_delete'),
|
||||
path('tunnels/<int:pk>/', include(get_model_urls('vpn', 'tunnel'))),
|
||||
|
||||
# Tunnel terminations
|
||||
path('tunnel-terminations/', views.TunnelTerminationListView.as_view(), name='tunneltermination_list'),
|
||||
path('tunnel-terminations/add/', views.TunnelTerminationEditView.as_view(), name='tunneltermination_add'),
|
||||
path('tunnel-terminations/import/', views.TunnelTerminationBulkImportView.as_view(), name='tunneltermination_import'),
|
||||
path('tunnel-terminations/edit/', views.TunnelTerminationBulkEditView.as_view(), name='tunneltermination_bulk_edit'),
|
||||
path('tunnel-terminations/delete/', views.TunnelTerminationBulkDeleteView.as_view(), name='tunneltermination_bulk_delete'),
|
||||
path('tunnel-terminations/<int:pk>/', 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/<int:pk>/', 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/<int:pk>/', 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/<int:pk>/', 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/<int:pk>/', 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'),
|
||||
path('ipsec-profiles/import/', views.IPSecProfileBulkImportView.as_view(), name='ipsecprofile_import'),
|
||||
path('ipsec-profiles/edit/', views.IPSecProfileBulkEditView.as_view(), name='ipsecprofile_bulk_edit'),
|
||||
path('ipsec-profiles/delete/', views.IPSecProfileBulkDeleteView.as_view(), name='ipsecprofile_bulk_delete'),
|
||||
path('ipsec-profiles/<int:pk>/', include(get_model_urls('vpn', 'ipsecprofile'))),
|
||||
|
||||
]
|
334
netbox/vpn/views.py
Normal file
334
netbox/vpn/views.py
Normal file
@ -0,0 +1,334 @@
|
||||
from netbox.views import generic
|
||||
from utilities.utils import count_related
|
||||
from utilities.views import register_model_view
|
||||
from . import filtersets, forms, tables
|
||||
from .models import *
|
||||
|
||||
|
||||
#
|
||||
# Tunnels
|
||||
#
|
||||
|
||||
class TunnelListView(generic.ObjectListView):
|
||||
queryset = Tunnel.objects.annotate(
|
||||
count_terminations=count_related(TunnelTermination, 'tunnel')
|
||||
)
|
||||
filterset = filtersets.TunnelFilterSet
|
||||
filterset_form = forms.TunnelFilterForm
|
||||
table = tables.TunnelTable
|
||||
|
||||
|
||||
@register_model_view(Tunnel)
|
||||
class TunnelView(generic.ObjectView):
|
||||
queryset = Tunnel.objects.all()
|
||||
|
||||
|
||||
@register_model_view(Tunnel, 'edit')
|
||||
class TunnelEditView(generic.ObjectEditView):
|
||||
queryset = Tunnel.objects.all()
|
||||
form = forms.TunnelForm
|
||||
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
|
||||
# If creating a new Tunnel, use the creation form
|
||||
if 'pk' not in kwargs:
|
||||
self.form = forms.TunnelCreateForm
|
||||
|
||||
return super().dispatch(request, *args, **kwargs)
|
||||
|
||||
|
||||
@register_model_view(Tunnel, 'delete')
|
||||
class TunnelDeleteView(generic.ObjectDeleteView):
|
||||
queryset = Tunnel.objects.all()
|
||||
|
||||
|
||||
class TunnelBulkImportView(generic.BulkImportView):
|
||||
queryset = Tunnel.objects.all()
|
||||
model_form = forms.TunnelImportForm
|
||||
|
||||
|
||||
class TunnelBulkEditView(generic.BulkEditView):
|
||||
queryset = Tunnel.objects.annotate(
|
||||
count_terminations=count_related(TunnelTermination, 'tunnel')
|
||||
)
|
||||
filterset = filtersets.TunnelFilterSet
|
||||
table = tables.TunnelTable
|
||||
form = forms.TunnelBulkEditForm
|
||||
|
||||
|
||||
class TunnelBulkDeleteView(generic.BulkDeleteView):
|
||||
queryset = Tunnel.objects.annotate(
|
||||
count_terminations=count_related(TunnelTermination, 'tunnel')
|
||||
)
|
||||
filterset = filtersets.TunnelFilterSet
|
||||
table = tables.TunnelTable
|
||||
|
||||
|
||||
#
|
||||
# Tunnel terminations
|
||||
#
|
||||
|
||||
class TunnelTerminationListView(generic.ObjectListView):
|
||||
queryset = TunnelTermination.objects.all()
|
||||
filterset = filtersets.TunnelTerminationFilterSet
|
||||
filterset_form = forms.TunnelTerminationFilterForm
|
||||
table = tables.TunnelTerminationTable
|
||||
|
||||
|
||||
@register_model_view(TunnelTermination)
|
||||
class TunnelTerminationView(generic.ObjectView):
|
||||
queryset = TunnelTermination.objects.all()
|
||||
|
||||
|
||||
@register_model_view(TunnelTermination, 'edit')
|
||||
class TunnelTerminationEditView(generic.ObjectEditView):
|
||||
queryset = TunnelTermination.objects.all()
|
||||
form = forms.TunnelTerminationForm
|
||||
|
||||
|
||||
@register_model_view(TunnelTermination, 'delete')
|
||||
class TunnelTerminationDeleteView(generic.ObjectDeleteView):
|
||||
queryset = TunnelTermination.objects.all()
|
||||
|
||||
|
||||
class TunnelTerminationBulkImportView(generic.BulkImportView):
|
||||
queryset = TunnelTermination.objects.all()
|
||||
model_form = forms.TunnelTerminationImportForm
|
||||
|
||||
|
||||
class TunnelTerminationBulkEditView(generic.BulkEditView):
|
||||
queryset = TunnelTermination.objects.all()
|
||||
filterset = filtersets.TunnelTerminationFilterSet
|
||||
table = tables.TunnelTerminationTable
|
||||
form = forms.TunnelTerminationBulkEditForm
|
||||
|
||||
|
||||
class TunnelTerminationBulkDeleteView(generic.BulkDeleteView):
|
||||
queryset = TunnelTermination.objects.all()
|
||||
filterset = filtersets.TunnelTerminationFilterSet
|
||||
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
|
||||
#
|
||||
|
||||
class IPSecProfileListView(generic.ObjectListView):
|
||||
queryset = IPSecProfile.objects.all()
|
||||
filterset = filtersets.IPSecProfileFilterSet
|
||||
filterset_form = forms.IPSecProfileFilterForm
|
||||
table = tables.IPSecProfileTable
|
||||
|
||||
|
||||
@register_model_view(IPSecProfile)
|
||||
class IPSecProfileView(generic.ObjectView):
|
||||
queryset = IPSecProfile.objects.all()
|
||||
|
||||
|
||||
@register_model_view(IPSecProfile, 'edit')
|
||||
class IPSecProfileEditView(generic.ObjectEditView):
|
||||
queryset = IPSecProfile.objects.all()
|
||||
form = forms.IPSecProfileForm
|
||||
|
||||
|
||||
@register_model_view(IPSecProfile, 'delete')
|
||||
class IPSecProfileDeleteView(generic.ObjectDeleteView):
|
||||
queryset = IPSecProfile.objects.all()
|
||||
|
||||
|
||||
class IPSecProfileBulkImportView(generic.BulkImportView):
|
||||
queryset = IPSecProfile.objects.all()
|
||||
model_form = forms.IPSecProfileImportForm
|
||||
|
||||
|
||||
class IPSecProfileBulkEditView(generic.BulkEditView):
|
||||
queryset = IPSecProfile.objects.all()
|
||||
filterset = filtersets.IPSecProfileFilterSet
|
||||
table = tables.IPSecProfileTable
|
||||
form = forms.IPSecProfileBulkEditForm
|
||||
|
||||
|
||||
class IPSecProfileBulkDeleteView(generic.BulkDeleteView):
|
||||
queryset = IPSecProfile.objects.all()
|
||||
filterset = filtersets.IPSecProfileFilterSet
|
||||
table = tables.IPSecProfileTable
|
Reference in New Issue
Block a user