Skip to content

Commit 6678880

Browse files
Closes #9816: VPN tunnel support (#14276)
- Introduces a new `vpn` app with the following models: - Tunnel - TunnelTermination - IKEProposal - IKEPolicy - IPSecProposal - IPSecPolicy - IPSecProfile
1 parent 975a647 commit 6678880

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

58 files changed

+5656
-10
lines changed

docs/features/vpn-tunnels.md

+49
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
# Tunnels
2+
3+
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.
4+
5+
```mermaid
6+
flowchart TD
7+
Termination1[TunnelTermination]
8+
Termination2[TunnelTermination]
9+
Interface1[Interface]
10+
Interface2[Interface]
11+
Tunnel --> Termination1 & Termination2
12+
Termination1 --> Interface1
13+
Termination2 --> Interface2
14+
Interface1 --> Device
15+
Interface2 --> VirtualMachine
16+
17+
click Tunnel "../../models/vpn/tunnel/"
18+
click TunnelTermination1 "../../models/vpn/tunneltermination/"
19+
click TunnelTermination2 "../../models/vpn/tunneltermination/"
20+
```
21+
22+
# IPSec & IKE
23+
24+
NetBox includes robust support for modeling IPSec & IKE policies. These are used to define encryption and authentication parameters for IPSec tunnels.
25+
26+
```mermaid
27+
flowchart TD
28+
subgraph IKEProposals[Proposals]
29+
IKEProposal1[IKEProposal]
30+
IKEProposal2[IKEProposal]
31+
end
32+
subgraph IPSecProposals[Proposals]
33+
IPSecProposal1[IPSecProposal]
34+
IPSecProposal2[IPSecProposal]
35+
end
36+
IKEProposals --> IKEPolicy
37+
IPSecProposals --> IPSecPolicy
38+
IKEPolicy & IPSecPolicy--> IPSecProfile
39+
IPSecProfile --> Tunnel
40+
41+
click IKEProposal1 "../../models/vpn/ikeproposal/"
42+
click IKEProposal2 "../../models/vpn/ikeproposal/"
43+
click IKEPolicy "../../models/vpn/ikepolicy/"
44+
click IPSecProposal1 "../../models/vpn/ipsecproposal/"
45+
click IPSecProposal2 "../../models/vpn/ipsecproposal/"
46+
click IPSecPolicy "../../models/vpn/ipsecpolicy/"
47+
click IPSecProfile "../../models/vpn/ipsecprofile/"
48+
click Tunnel "../../models/vpn/tunnel/"
49+
```

docs/models/vpn/ikepolicy.md

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# IKE Policies
2+
3+
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).
4+
5+
## Fields
6+
7+
### Name
8+
9+
The unique user-assigned name for the policy.
10+
11+
### Version
12+
13+
The IKE version employed (v1 or v2).
14+
15+
### Mode
16+
17+
The IKE mode employed (main or aggressive).
18+
19+
### Proposals
20+
21+
One or more [IKE proposals](./ikeproposal.md) supported for use by this policy.
22+
23+
### Pre-shared Key
24+
25+
A pre-shared secret key associated with this policy (optional).

docs/models/vpn/ikeproposal.md

+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# IKE Proposals
2+
3+
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).
4+
5+
!!! note
6+
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.
7+
8+
## Fields
9+
10+
### Name
11+
12+
The unique user-assigned name for the proposal.
13+
14+
### Authentication Method
15+
16+
The strategy employed for authenticating the IKE peer. Available options are listed below.
17+
18+
| Name |
19+
|----------------|
20+
| Pre-shared key |
21+
| Certificate |
22+
| RSA signature |
23+
| DSA signature |
24+
25+
### Encryption Algorithm
26+
27+
The protocol employed for data encryption. Options include DES, 3DES, and various flavors of AES.
28+
29+
### Authentication Algorithm
30+
31+
The mechanism employed to ensure data integrity. Options include MD5 and SHA HMAC implementations.
32+
33+
### Group
34+
35+
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).
36+
37+
### SA Lifetime
38+
39+
The maximum lifetime for the IKE security association (SA), in seconds.

docs/models/vpn/ipsecpolicy.md

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# IPSec Policy
2+
3+
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).
4+
5+
## Fields
6+
7+
### Name
8+
9+
The unique user-assigned name for the policy.
10+
11+
### Proposals
12+
13+
One or more [IPSec proposals](./ipsecproposal.md) supported for use by this policy.
14+
15+
### PFS Group
16+
17+
The [perfect forward secrecy (PFS)](https://en.wikipedia.org/wiki/Forward_secrecy) group supported by this policy (optional).

docs/models/vpn/ipsecprofile.md

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# IPSec Profile
2+
3+
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.
4+
5+
## Fields
6+
7+
### Name
8+
9+
The unique user-assigned name for the profile.
10+
11+
### Mode
12+
13+
The IPSec mode employed by the profile: Encapsulating Security Payload (ESP) or Authentication Header (AH).
14+
15+
### IKE Policy
16+
17+
The [IKE policy](./ikepolicy.md) associated with the profile.
18+
19+
### IPSec Policy
20+
21+
The [IPSec policy](./ipsecpolicy.md) associated with the profile.

docs/models/vpn/ipsecproposal.md

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# IPSec Proposal
2+
3+
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).
4+
5+
## Fields
6+
7+
### Name
8+
9+
The unique user-assigned name for the proposal.
10+
11+
### Encryption Algorithm
12+
13+
The protocol employed for data encryption. Options include DES, 3DES, and various flavors of AES.
14+
15+
### Authentication Algorithm
16+
17+
The mechanism employed to ensure data integrity. Options include MD5 and SHA HMAC implementations.
18+
19+
### SA Lifetime (Seconds)
20+
21+
The maximum amount of time for which the security association (SA) may be active, in seconds.
22+
23+
### SA Lifetime (Data)
24+
25+
The maximum amount of data which can be transferred within the security association (SA) before it must be rebuilt, in kilobytes.

docs/models/vpn/tunnel.md

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# Tunnels
2+
3+
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.
4+
5+
Device and virtual machine interfaces are associated to tunnels by creating [tunnel terminations](./tunneltermination.md).
6+
7+
## Fields
8+
9+
### Name
10+
11+
A unique name assigned to the tunnel for identification.
12+
13+
### Status
14+
15+
The operational status of the tunnel. By default, the following statuses are available:
16+
17+
| Name |
18+
|----------------|
19+
| Planned |
20+
| Active |
21+
| Disabled |
22+
23+
!!! tip "Custom tunnel statuses"
24+
Additional tunnel statuses may be defined by setting `Tunnel.status` under the [`FIELD_CHOICES`](../../configuration/data-validation.md#field_choices) configuration parameter.
25+
26+
### Encapsulation
27+
28+
The encapsulation protocol or technique employed to effect the tunnel. NetBox supports GRE, IP-in-IP, and IPSec encapsulations.
29+
30+
### Tunnel ID
31+
32+
An optional numeric identifier for the tunnel.
33+
34+
### IPSec Profile
35+
36+
For IPSec tunnels, this is the [IPSec Profile](./ipsecprofile.md) employed to negotiate security associations.

docs/models/vpn/tunneltermination.md

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# Tunnel Terminations
2+
3+
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.
4+
5+
## Fields
6+
7+
### Tunnel
8+
9+
The [tunnel](./tunnel.md) to which this termination is made.
10+
11+
### Role
12+
13+
The functional role of the attached interface. The following options are available:
14+
15+
| Name | Description |
16+
|-------|--------------------------------------------------|
17+
| Peer | An endpoint in a point-to-point or mesh topology |
18+
| Hub | A central point in a hub-and-spoke topology |
19+
| Spoke | An edge point in a hub-and-spoke topology |
20+
21+
!!! note
22+
Multiple hub terminations may be attached to a tunnel.
23+
24+
### Termination
25+
26+
The device or virtual machine interface terminated to the tunnel.
27+
28+
### Outside IP
29+
30+
The public or underlay IP address with which this termination is associated. This is the IP to which peers will route tunneled traffic.

mkdocs.yml

+9
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ nav:
7474
- Circuits: 'features/circuits.md'
7575
- Wireless: 'features/wireless.md'
7676
- Virtualization: 'features/virtualization.md'
77+
- VPN Tunnels: 'features/vpn-tunnels.md'
7778
- Tenancy: 'features/tenancy.md'
7879
- Contacts: 'features/contacts.md'
7980
- Search: 'features/search.md'
@@ -252,6 +253,14 @@ nav:
252253
- ClusterType: 'models/virtualization/clustertype.md'
253254
- VMInterface: 'models/virtualization/vminterface.md'
254255
- VirtualMachine: 'models/virtualization/virtualmachine.md'
256+
- VPN:
257+
- IKEPolicy: 'models/vpn/ikepolicy.md'
258+
- IKEProposal: 'models/vpn/ikeproposal.md'
259+
- IPSecPolicy: 'models/vpn/ipsecpolicy.md'
260+
- IPSecProfile: 'models/vpn/ipsecprofile.md'
261+
- IPSecProposal: 'models/vpn/ipsecproposal.md'
262+
- Tunnel: 'models/vpn/tunnel.md'
263+
- TunnelTermination: 'models/vpn/tunneltermination.md'
255264
- Wireless:
256265
- WirelessLAN: 'models/wireless/wirelesslan.md'
257266
- WirelessLANGroup: 'models/wireless/wirelesslangroup.md'

netbox/core/management/commands/nbshell.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
from django.contrib.contenttypes.models import ContentType
1010
from django.core.management.base import BaseCommand
1111

12-
APPS = ('circuits', 'core', 'dcim', 'extras', 'ipam', 'tenancy', 'users', 'virtualization', 'wireless')
12+
APPS = ('circuits', 'core', 'dcim', 'extras', 'ipam', 'tenancy', 'users', 'virtualization', 'vpn', 'wireless')
1313

1414
BANNER_TEXT = """### NetBox interactive shell ({node})
1515
### Python {python} | Django {django} | NetBox {netbox}

netbox/dcim/models/device_components.py

+10
Original file line numberDiff line numberDiff line change
@@ -566,6 +566,10 @@ def save(self, *args, **kwargs):
566566

567567
return super().save(*args, **kwargs)
568568

569+
@property
570+
def tunnel_termination(self):
571+
return self.tunnel_terminations.first()
572+
569573
@property
570574
def count_ipaddresses(self):
571575
return self.ip_addresses.count()
@@ -719,6 +723,12 @@ class Interface(ModularComponentModel, BaseInterface, CabledObjectModel, PathEnd
719723
object_id_field='interface_id',
720724
related_query_name='+'
721725
)
726+
tunnel_terminations = GenericRelation(
727+
to='vpn.TunnelTermination',
728+
content_type_field='termination_type',
729+
object_id_field='termination_id',
730+
related_query_name='interface'
731+
)
722732
l2vpn_terminations = GenericRelation(
723733
to='ipam.L2VPNTermination',
724734
content_type_field='assigned_object_type',

netbox/dcim/tables/devices.py

+10-3
Original file line numberDiff line numberDiff line change
@@ -584,6 +584,12 @@ class BaseInterfaceTable(NetBoxTable):
584584
orderable=False,
585585
verbose_name=_('L2VPN')
586586
)
587+
tunnel = tables.Column(
588+
accessor=tables.A('tunnel_termination__tunnel'),
589+
linkify=True,
590+
orderable=False,
591+
verbose_name=_('Tunnel')
592+
)
587593
untagged_vlan = tables.Column(
588594
verbose_name=_('Untagged VLAN'),
589595
linkify=True
@@ -646,7 +652,8 @@ class Meta(DeviceComponentTable.Meta):
646652
'speed', 'speed_formatted', 'duplex', 'mode', 'mac_address', 'wwn', 'poe_mode', 'poe_type', 'rf_role', 'rf_channel',
647653
'rf_channel_frequency', 'rf_channel_width', 'tx_power', 'description', 'mark_connected', 'cable',
648654
'cable_color', 'wireless_link', 'wireless_lans', 'link_peer', 'connection', 'tags', 'vdcs', 'vrf', 'l2vpn',
649-
'ip_addresses', 'fhrp_groups', 'untagged_vlan', 'tagged_vlans', 'inventory_items', 'created', 'last_updated',
655+
'tunnel', 'ip_addresses', 'fhrp_groups', 'untagged_vlan', 'tagged_vlans', 'inventory_items', 'created',
656+
'last_updated',
650657
)
651658
default_columns = ('pk', 'name', 'device', 'label', 'enabled', 'type', 'description')
652659

@@ -682,8 +689,8 @@ class Meta(DeviceComponentTable.Meta):
682689
'pk', 'id', 'name', 'module_bay', 'module', 'label', 'enabled', 'type', 'parent', 'bridge', 'lag',
683690
'mgmt_only', 'mtu', 'mode', 'mac_address', 'wwn', 'rf_role', 'rf_channel', 'rf_channel_frequency',
684691
'rf_channel_width', 'tx_power', 'description', 'mark_connected', 'cable', 'cable_color', 'wireless_link',
685-
'wireless_lans', 'link_peer', 'connection', 'tags', 'vdcs', 'vrf', 'l2vpn', 'ip_addresses', 'fhrp_groups',
686-
'untagged_vlan', 'tagged_vlans', 'actions',
692+
'wireless_lans', 'link_peer', 'connection', 'tags', 'vdcs', 'vrf', 'l2vpn', 'tunnel', 'ip_addresses',
693+
'fhrp_groups', 'untagged_vlan', 'tagged_vlans', 'actions',
687694
)
688695
default_columns = (
689696
'pk', 'name', 'label', 'enabled', 'type', 'parent', 'lag', 'mtu', 'mode', 'description', 'ip_addresses',

netbox/dcim/tables/template_code.py

+10
Original file line numberDiff line numberDiff line change
@@ -359,6 +359,16 @@
359359
<i class="mdi mdi-wifi-off" aria-hidden="true"></i>
360360
</a>
361361
{% endif %}
362+
{% elif record.type == 'virtual' %}
363+
{% if perms.vpn.add_tunnel and not record.tunnel_termination %}
364+
<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">
365+
<i class="mdi mdi-tunnel-outline" aria-hidden="true"></i>
366+
</a>
367+
{% elif perms.vpn.delete_tunneltermination and record.tunnel_termination %}
368+
<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">
369+
<i class="mdi mdi-tunnel-outline" aria-hidden="true"></i>
370+
</a>
371+
{% endif %}
362372
{% elif record.is_wired and perms.dcim.add_cable %}
363373
<a href="#" class="btn btn-outline-dark btn-sm disabled"><i class="mdi mdi-transit-connection-variant" aria-hidden="true"></i></a>
364374
<a href="#" class="btn btn-outline-dark btn-sm disabled"><i class="mdi mdi-lan-connect" aria-hidden="true"></i></a>

netbox/netbox/api/views.py

+1
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ def get(self, request, format=None):
3939
'tenancy': reverse('tenancy-api:api-root', request=request, format=format),
4040
'users': reverse('users-api:api-root', request=request, format=format),
4141
'virtualization': reverse('virtualization-api:api-root', request=request, format=format),
42+
'vpn': reverse('vpn-api:api-root', request=request, format=format),
4243
'wireless': reverse('wireless-api:api-root', request=request, format=format),
4344
})
4445

netbox/netbox/graphql/schema.py

+2
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from tenancy.graphql.schema import TenancyQuery
1010
from users.graphql.schema import UsersQuery
1111
from virtualization.graphql.schema import VirtualizationQuery
12+
from vpn.graphql.schema import VPNQuery
1213
from wireless.graphql.schema import WirelessQuery
1314

1415

@@ -21,6 +22,7 @@ class Query(
2122
IPAMQuery,
2223
TenancyQuery,
2324
VirtualizationQuery,
25+
VPNQuery,
2426
WirelessQuery,
2527
*registry['plugins']['graphql_schemas'], # Append plugin schemas
2628
graphene.ObjectType

0 commit comments

Comments
 (0)