Skip to content

Commit

Permalink
[req-changes] Improved zerotier ifname configuration
Browse files Browse the repository at this point in the history
  • Loading branch information
Aryamanz29 committed Aug 21, 2023
1 parent d8feb9f commit 4a47582
Show file tree
Hide file tree
Showing 8 changed files with 126 additions and 149 deletions.
60 changes: 10 additions & 50 deletions docs/source/backends/zerotier.rst
Expand Up @@ -274,29 +274,23 @@ Client specific settings

Required properties:

* id
* name
* nwid_ifname

+------------------------+---------+--------------+----------------------------------------------------------------------------------------------------+
| key name | type | default | description |
+========================+=========+==============+====================================================================================================+
| ``name`` | string | | name of the zerotier network |
+------------------------+---------+--------------+----------------------------------------------------------------------------------------------------+
| ``id`` | list | ``[]`` | list of strings containing **16-digit** hexadecimal network IDs to join |
| ``nwid_ifname`` | list | ``[{}]`` | list of dictionaries containing strings with **16-digit** hexadecimal network IDs for joining, |
| | | | |
| | | | **note:** must contain at least one network ID |
+------------------------+---------+--------------+----------------------------------------------------------------------------------------------------+
| ``config_path`` | string | | path to the persistent configuration folder |
+------------------------+---------+--------------+----------------------------------------------------------------------------------------------------+
| ``copy_config_path`` | string | ``'0'`` | specifies whether to copy the configuration file to RAM |
| | | | along with a corresponding custom **10-digit** ZeroTier interface name for each network |
| | | | |
| | | | ``'0'`` - No, ``'1'`` - Yes, this prevents writing to flash in zerotier controller mode |
+------------------------+---------+--------------+----------------------------------------------------------------------------------------------------+
| ``port`` | integer | ``9993`` | port number of the zerotier service |
| | | | **note:** ensure that the list includes at least one such dictionary |
+------------------------+---------+--------------+----------------------------------------------------------------------------------------------------+
| ``local_conf`` | boolean | | path of the local zerotier configuration |
| ``config_path`` | string | | path to the persistent configuration folder |
+------------------------+---------+--------------+----------------------------------------------------------------------------------------------------+
| ``secret`` | boolean | | secret key of the zerotier client (network member), leave it blank to be automatically determined |
| ``secret`` | string | | secret key of the zerotier client (network member), leave it blank to be automatically determined |
+------------------------+---------+--------------+----------------------------------------------------------------------------------------------------+

Working around schema limitations
Expand All @@ -315,21 +309,15 @@ Automatic generation of clients

.. automethod:: netjsonconfig.OpenWrt.zerotier_auto_client

**Example 1** (with custom ``zt interface name``):
**Example (with custom ``zerotier interface name``)**:

.. code-block:: python
from netjsonconfig import OpenWrt
server_config = {
"id": ["9536600adf654321"],
"name": "ow_zt",
}
nw_id = server_config['id'][0]
client_config = OpenWrt.zerotier_auto_client(
nwid=server_config['id'],
name=server_config['name'],
ifname=f'owzt{nw_id[-6:]}',
name='ow_zt',
nwid_ifname=[{"id": "9536600adf654321", "ifname": "owzt654321"}],
)
print(OpenWrt(client_config).render())
Expand All @@ -351,37 +339,9 @@ Will be rendered as:
# path: /etc/ow_zerotier/devicemap
# mode: 0644
# network_id=interface_name
9536600adf654321=owzt654321
**Example 2** (without custom ``zt interface name``):

.. code-block:: python
from netjsonconfig import OpenWrt
server_config = {
"id": ["9536600adf654321"],
"name": "ow_zt",
}
client_config = OpenWrt.zerotier_auto_client(
nwid=server_config['id'],
name=server_config['name'],
)
print(OpenWrt(client_config).render())
Will be rendered as:

.. code-block:: text
package zerotier
config zerotier 'ow_zt'
option config_path '/etc/ow_zerotier_extra'
option copy_config_path '0'
option enabled '1'
list join '9536600adf654321'
option secret '{{zt_identity_secret}}'
.. note::

The current implementation of **ZeroTier VPN** backend is implemented with
Expand Down
32 changes: 25 additions & 7 deletions netjsonconfig/backends/openwrt/converters/zerotier.py
Expand Up @@ -8,39 +8,57 @@ class ZeroTier(OpenWrtConverter, BaseZeroTier):
_schema = schema['properties']['zerotier']['items']

def __intermediate_vpn(self, vpn):
nwid_ifnames = vpn.get('nwid_ifname', [])
files = self.netjson.get('files', [])
self.netjson['files'] = self.__get_zt_ifname_files(vpn, files)
vpn.update(
{
'.name': self._get_uci_name(vpn.pop('name')),
'.type': 'zerotier',
'join': vpn.pop('id'),
'config_path': vpn.get('config_path', '/etc/ow_zerotier_extra'),
'copy_config_path': vpn.get('copy_config_path', '1'),
'join': [nwid_ifname.get('id', '') for nwid_ifname in nwid_ifnames],
'enabled': not vpn.pop('disabled', False),
}
)
del vpn['nwid_ifname']
return super().__intermediate_vpn(vpn, remove=[''])

def __netjson_vpn(self, vpn):
vpn['id'] = vpn.pop('join')
nwids = vpn.pop('join')
vpn['name'] = vpn.pop('.name')
vpn['nwid_ifname'] = [
{"id": nwid, "ifname": f"owzt{nwid[-6:]}"} for nwid in nwids
]
# 'disabled' defaults to False in OpenWRT
vpn['disabled'] = vpn.pop('enabled', '0') == '0'
del vpn['.type']
return super().__netjson_vpn(vpn)

def __get_zt_ifname_files(self, vpn, files):
updated_files = []
config_path = vpn.get('config_path', '/etc/ow_zerotier_extra')
nwid_ifnames = vpn.get('nwid_ifname', [])
zt_file_contents = '# network_id=interface_name\n'

for nwid_ifname in nwid_ifnames:
nwid = nwid_ifname.get('id', '')
ifname = nwid_ifname.get('ifname', f'owzt{nwid[-6:]}')
zt_file_contents += f"{nwid}={ifname}\n"

zt_interface_map = {
'path': f"{vpn.get('config_path')}/devicemap",
'path': f"{config_path}/devicemap",
'mode': '0644',
'contents': '',
'contents': zt_file_contents,
}

if not files:
return [zt_interface_map]
updated_files = []
for file in files:
if file.get('path') == zt_interface_map.get('path'):
zt_interface_map['contents'] += file.get('contents') + '\n'
zt_interface_map['contents'] += '\n' + file['contents']
else:
updated_files.append(file)

if zt_interface_map.get('contents'):
updated_files.append(zt_interface_map)
return updated_files
20 changes: 1 addition & 19 deletions netjsonconfig/backends/openwrt/openwrt.py
Expand Up @@ -147,25 +147,7 @@ def vxlan_wireguard_auto_client(cls, **kwargs):
@classmethod
def zerotier_auto_client(cls, **kwargs):
data = ZeroTier.auto_client(**kwargs)
config_path = data.get('config_path')
copy_config_path = data.get('copy_config_path')
zt_network_id = data.get('id')[0]
zt_ifname = data.get('ifname')
del data['ifname']
config = {'zerotier': [data]}
if zt_ifname and copy_config_path == '1':
config.update(
{
'files': [
dict(
path=f"{config_path}/devicemap",
mode="0644",
contents=f"{zt_network_id}={zt_ifname}",
)
],
}
)
return config
return {'zerotier': [data]}

def validate(self):
self._validate_radios()
Expand Down
73 changes: 33 additions & 40 deletions netjsonconfig/backends/openwrt/schema.py
Expand Up @@ -1008,9 +1008,9 @@
"propertyOrder": 14,
"items": {
"type": "object",
"title": "Network Member",
"title": "Network Member Configuration",
"additionalProperties": True,
"required": ["id", "name"],
"required": ["name", "nwid_ifname"],
"properties": {
# ZeroTier customization (disabled) for OpenWRT
"disabled": {
Expand All @@ -1024,59 +1024,52 @@
"name": {
"type": "string",
"propertyOrder": 2,
"default": "openwisp_zerotier_network",
"description": "Name of the zerotier network",
"default": "ow_zt",
"minLength": 1,
"description": "Name of the zerotier network member configuration",
},
"id": {
"nwid_ifname": {
"type": "array",
"title": "16-digit hexadecimal network IDs to join",
"title": "Network ID and Interface name",
"minItems": 1,
"propertyOrder": 3,
"uniqueItems": True,
"additionalProperties": True,
"items": {
"type": "string",
"title": "Network ID",
"maxLength": 16,
"minLength": 16,
"type": "object",
"title": "Network Member",
"properties": {
"id": {
"type": "string",
"title": "Network ID",
"maxLength": 16,
"minLength": 16,
"description": "Network ID to join",
},
"ifname": {
"type": "string",
"title": "Interface name",
"maxLength": 10,
"minLength": 10,
"description": "Name of zerotier interface",
},
},
},
},
"config_path": {
"secret": {
"type": "string",
"propertyOrder": 4,
"description": (
"Path to the persistent configuration "
"folder (for zerotier controller mode)"
),
},
"copy_config_path": {
"type": "string",
"propertyOrder": 5,
"enum": ["0", "1"],
"description": (
"Specifies whether to copy the configuration "
"file to RAM ('0' - No, '1' - Yes), this prevents "
"writing to flash in zerotier controller mode"
"Secret key of the zerotier client (network member), "
"leave it blank to be automatically determined"
),
},
"port": {
"type": "integer",
"minimum": 1,
"maximum": 65535,
"default": 9993,
"propertyOrder": 6,
"description": "Port number of the zerotier service",
},
"local_conf": {
"type": "string",
"propertyOrder": 7,
"description": "Path of the local zerotier configuration",
},
"secret": {
"config_path": {
"type": "string",
"propertyOrder": 8,
"propertyOrder": 4,
"description": (
"Secret key of the zerotier client (network member), "
"leave it blank to be automatically determined"
"Path to the persistent configuration "
"folder (for zerotier controller mode)"
),
},
},
Expand Down
11 changes: 3 additions & 8 deletions netjsonconfig/backends/zerotier/zerotier.py
Expand Up @@ -18,21 +18,16 @@ class ZeroTier(BaseVpnBackend):
def auto_client(
cls,
name='ow_zt',
nwid=None,
nwid_ifname=None,
identity_secret='{{zt_identity_secret}}',
config_path='/etc/ow_zerotier_extra',
copy_config_path='0',
ifname='',
disabled=False,
):
nwid = nwid or ['']
copy_config_path = '1' if ifname else copy_config_path
nwid_ifname = nwid_ifname or []
return {
'id': nwid,
'name': name,
'nwid_ifname': nwid_ifname,
'secret': identity_secret,
'config_path': config_path,
'copy_config_path': copy_config_path,
'ifname': ifname,
'disabled': disabled,
}
20 changes: 6 additions & 14 deletions tests/openwrt/test_backend.py
Expand Up @@ -552,10 +552,9 @@ def test_zerotier_auto_client(self):
'zerotier': [
{
'name': 'ow_zt',
'id': [''],
'nwid_ifname': [],
'secret': '{{zt_identity_secret}}',
'config_path': '/etc/ow_zerotier_extra',
'copy_config_path': '0',
'disabled': False,
}
]
Expand All @@ -565,29 +564,22 @@ def test_zerotier_auto_client(self):
expected = {
'zerotier': [
{
'id': ['9536600adf654321'],
'name': 'ow_zt',
'nwid_ifname': [
{'id': '9536600adf654321', 'ifname': 'owzt654321'}
],
'secret': '{{zt_identity_secret}}',
'config_path': '/etc/ow_zerotier_test',
'copy_config_path': '1',
'disabled': False,
}
],
'files': [
{
'path': '/etc/ow_zerotier_test/devicemap',
'mode': '0644',
'contents': '9536600adf654321=owzt654321',
}
],
]
}
nw_id = '9536600adf654321'
self.assertDictEqual(
OpenWrt.zerotier_auto_client(
nwid=[nw_id],
# Test it is possible to change default `config_path`
config_path='/etc/ow_zerotier_test',
ifname=f'owzt{nw_id[-6:]}',
nwid_ifname=[{'id': nw_id, 'ifname': f'owzt{nw_id[-6:]}'}],
),
expected,
)

0 comments on commit 4a47582

Please sign in to comment.