Skip to content

Commit 5a44002

Browse files
Aryamanz29nemesifier
authored andcommitted
[feature] Added support for ZeroTier VPN backend #604 #606 #801
Closes #604 Closes #606 Closes #801
1 parent 7b90341 commit 5a44002

File tree

11 files changed

+740
-61
lines changed

11 files changed

+740
-61
lines changed

README.rst

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1347,15 +1347,20 @@ from the `official website <https://www.zerotier.com/download/>`_.
13471347
be applied to. For this example, we will leave it to ``OpenWRT``.
13481348
4. Select the correct VPN server from the dropdown for the **VPN** field. Here
13491349
it is ``ZeroTier``.
1350-
5. Make sure to check the **Automatic tunnel provisioning** option.
1351-
This will enable OpenWISP to automatically provision an IP address
1352-
for each ZeroTier VPN client.
1350+
5. Ensure that the **Automatic tunnel provisioning** option is checked.
1351+
This will enable OpenWISP to automatically provision an IP address and
1352+
ZeroTier identity secrets (used for assigning member IDs) for each ZeroTier VPN client.
13531353
6. After clicking on **Save and continue editing** button, you will see details
13541354
of *ZeroTier* VPN server in **System Defined Variables**. The template
13551355
configuration will be automatically generated which you can tweak
13561356
accordingly. We will use the automatically generated VPN client configuration
13571357
for this example.
13581358

1359+
**Note:** OpenWISP uses `zerotier-idtool
1360+
<https://github.com/zerotier/ZeroTierOne/blob/dev/doc/zerotier-idtool.1.md>`_
1361+
to manage **ZeroTier identity secrets**. Please make sure that you have
1362+
`ZeroTier package installed <https://www.zerotier.com/download/>`_ on the server.
1363+
13591364
.. image:: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/zerotier-tutorial/template.png
13601365
:alt: ZeroTier VPN client template example
13611366

@@ -1367,12 +1372,19 @@ OpenWISP. Register or create a device before proceeding.
13671372

13681373
1. Open the **Configuration** tab of the concerned device.
13691374
2. Select the *ZeroTier Client* template.
1370-
3. Upon clicking on **Save and continue editing** button, you will see some
1371-
entries in **System Defined Variables**. It will contain internal IP address
1372-
for the ZeroTier client on the device along with details of VPN server.
1375+
3. Upon clicking the **Save and Continue Editing** button, you will see entries
1376+
in the **System Defined Variables** section. These entries will include **zerotier_member_id**, **identity_secret**,
1377+
and the internal **IP address** of the ZeroTier client (network member) on the device, along with details of the VPN server.
1378+
1379+
.. image:: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/zerotier-tutorial/device-configuration-1.png
1380+
:alt: ZeroTier VPN device configuration example 1
1381+
1382+
4. Once the configuration is successfully applied to the device, you will notice a new ZeroTier interface
1383+
that is up and running. This interface will have the name ``owzt89f498`` (where ``owzt`` is followed
1384+
by the last six hexadecimal characters of the ZeroTier **network ID**).
13731385

1374-
.. image:: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/zerotier-tutorial/device-configuration.png
1375-
:alt: ZeroTier VPN device configuration example
1386+
.. image:: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/zerotier-tutorial/device-configuration-2.png
1387+
:alt: ZeroTier VPN device configuration example 2
13761388

13771389
**Voila!** You have successfully configured OpenWISP
13781390
to manage ZeroTier tunnels for your devices.

openwisp_controller/config/api/zerotier_service.py

Lines changed: 95 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -51,36 +51,24 @@ def _get_repsonse(self, repsonse):
5151
return repsonse
5252

5353
def _add_routes_and_ip_assignment(self, config):
54+
"""
55+
Adds ZeroTier network routes
56+
and IP assignmentpools through OpenWISP subnet
57+
58+
Params:
59+
config (dict): ZeroTier network config dict
60+
"""
5461
config['routes'] = [{'target': str(self.subnet), 'via': ''}]
5562
ip_end = str(self.subnet.broadcast_address)
5663
ip_start = str(next(self.subnet.hosts()))
5764
config['ipAssignmentPools'] = [{"ipRangeEnd": ip_end, "ipRangeStart": ip_start}]
5865
return config
5966

60-
def join_network(self, network_id):
61-
url = f'{self.url}/network/{network_id}'
62-
response = requests.post(
63-
url, json={}, headers=self.headers, timeout=REQUEST_TIMEOUT
64-
)
65-
return response
66-
67-
def leave_network(self, network_id):
68-
url = f'{self.url}/network/{network_id}'
69-
response = requests.delete(url, headers=self.headers, timeout=REQUEST_TIMEOUT)
70-
return response
71-
72-
def update_network_member(self, node_id, network_id, member_ip):
73-
url = f'{self.url}/controller/network/{network_id}/member/{node_id}'
74-
# Authorize and assign ip to the network member
75-
response = requests.post(
76-
url,
77-
json={'authorized': True, 'ipAssignments': [str(member_ip)]},
78-
headers=self.headers,
79-
timeout=5,
80-
)
81-
return response
82-
8367
def get_node_status(self):
68+
"""
69+
Fetches the status of the running ZeroTier controller
70+
This method is used for host validation during VPN creation
71+
"""
8472
url = f'{self.url}/status'
8573
try:
8674
response = requests.get(url, headers=self.headers, timeout=REQUEST_TIMEOUT)
@@ -94,7 +82,41 @@ def get_node_status(self):
9482
}
9583
)
9684

85+
def join_network(self, network_id):
86+
"""
87+
Adds ZeroTier Controller to the specified network
88+
89+
Params:
90+
network_id (str): ID of the network to join
91+
"""
92+
url = f'{self.url}/network/{network_id}'
93+
response = requests.post(
94+
url, json={}, headers=self.headers, timeout=REQUEST_TIMEOUT
95+
)
96+
return response
97+
98+
def leave_network(self, network_id):
99+
"""
100+
Removes ZeroTier Controller from the specified network
101+
102+
Params:
103+
network_id (str): ID of the network to leave
104+
"""
105+
url = f'{self.url}/network/{network_id}'
106+
response = requests.delete(url, headers=self.headers, timeout=REQUEST_TIMEOUT)
107+
return response
108+
97109
def create_network(self, node_id, config):
110+
"""
111+
Creates a new network in the ZeroTier Controller
112+
113+
Params:
114+
node_id (str): ID of the controller node
115+
config (dict): Configuration of the new network
116+
117+
Returns:
118+
network_config(dict): Filtered response from the ZeroTier Controller API
119+
"""
98120
url = f"{self.url}{self._get_endpoint('network', 'create', node_id)}"
99121
config = self._add_routes_and_ip_assignment(config)
100122
try:
@@ -110,6 +132,13 @@ def create_network(self, node_id, config):
110132
)
111133

112134
def update_network(self, config, network_id):
135+
"""
136+
Update configuration of an existing ZeroTier Controller network
137+
138+
Params:
139+
config (dict): New configuration data for the network
140+
network_id (str): ID of the network to update
141+
"""
113142
url = f"{self.url}{self._get_endpoint('network', 'update', network_id)}"
114143
config = self._add_routes_and_ip_assignment(config)
115144
response = requests.post(
@@ -118,6 +147,49 @@ def update_network(self, config, network_id):
118147
return response, self._get_repsonse(response.json())
119148

120149
def delete_network(self, network_id):
150+
"""
151+
Deletes ZeroTier Controller network
152+
153+
Params:
154+
network_id (str): ID of the ZeroTier network to be deleted
155+
"""
121156
url = f"{self.url}{self._get_endpoint('network', 'delete', network_id)}"
122157
response = requests.delete(url, headers=self.headers, timeout=REQUEST_TIMEOUT)
123158
return response
159+
160+
def update_network_member(self, node_id, network_id, member_ip):
161+
"""
162+
Update ZeroTier Network Member Configuration
163+
164+
This method is currently used to authorize, enable the bridge
165+
and assign an IP address to a network member
166+
167+
Params:
168+
node_id (str): Node ID of the network member
169+
network_id (str): Network ID to which the member belongs
170+
member_ip (str): IP address to be assigned to the network member
171+
"""
172+
url = f'{self.url}/controller/network/{network_id}/member/{node_id}'
173+
response = requests.post(
174+
url,
175+
json={
176+
'authorized': True,
177+
'activeBridge': True,
178+
'ipAssignments': [str(member_ip)],
179+
},
180+
headers=self.headers,
181+
timeout=5,
182+
)
183+
return response
184+
185+
def remove_network_member(self, node_id, network_id):
186+
"""
187+
Remove a member from ZeroTier network
188+
189+
Params:
190+
node_id (str): ID of the network member
191+
network_id (str): ID of the ZeroTier network
192+
"""
193+
url = f'{self.url}/controller/network/{network_id}/member/{node_id}'
194+
response = requests.delete(url, headers=self.headers, timeout=5)
195+
return response

openwisp_controller/config/base/config.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -263,7 +263,9 @@ def manage_vpn_clients(cls, action, instance, pk_set, **kwargs):
263263
).exists():
264264
continue
265265
client = vpn_client_model(
266-
config=instance, vpn=template.vpn, auto_cert=template.auto_cert
266+
config=instance,
267+
vpn=template.vpn,
268+
auto_cert=template.auto_cert,
267269
)
268270
client.full_clean()
269271
client.save()
@@ -601,6 +603,11 @@ def get_vpn_context(self):
601603
context[
602604
vpn_context_keys['vni']
603605
] = f'{vpnclient.vni or vpnclient.vpn._vxlan_vni}'
606+
if vpnclient.secret:
607+
context[
608+
vpn_context_keys['zerotier_member_id']
609+
] = vpnclient.zerotier_member_id
610+
context[vpn_context_keys['secret']] = vpnclient.secret
604611
return context
605612

606613
def get_context(self, system=False):

0 commit comments

Comments
 (0)