From 91d8e8d92814034dfb57758aad41a4193cf20376 Mon Sep 17 00:00:00 2001 From: Gagan Deep Date: Wed, 24 Jan 2024 20:28:15 +0530 Subject: [PATCH 1/3] [change] OpenWrt: updated WPA enterprise settings #291 - Generate "auth_sever", "auth_port" and "auth_secret" options from "server", "port" and "key" properties - Set "acct_secret" to the value of "key" if it is not configured in NetJSON - Add new options: "acct_inverval", "dae_client", "dae_port", "dae_secret" Closes #291 --- .../backends/openwrt/converters/wireless.py | 40 +++++++++++++---- netjsonconfig/backends/openwrt/schema.py | 44 +++++++++++++++++++ tests/openwrt/test_encryption.py | 25 +++++++++++ 3 files changed, 100 insertions(+), 9 deletions(-) diff --git a/netjsonconfig/backends/openwrt/converters/wireless.py b/netjsonconfig/backends/openwrt/converters/wireless.py index 66b365352..0cbbb049e 100644 --- a/netjsonconfig/backends/openwrt/converters/wireless.py +++ b/netjsonconfig/backends/openwrt/converters/wireless.py @@ -124,15 +124,8 @@ def __intermediate_encryption(self, wireless): if protocol == 'wep_open': uci['key1'] = 's:{0}'.format(uci['key1']) else: - if ( - 'enterprise' in protocol - and 'eap_type' in uci - and uci['eap_type'] == 'tls' - and 'auth' in uci - ): - # remove auth if not needed - # (not applicable to EAP-TLS) - del uci['auth'] + if 'enterprise' in protocol: + self.__intermediate_encryption_wpa_enterprise(wireless, encryption, uci) if 'key' in encryption: uci['key'] = encryption['key'] # add ciphers @@ -141,6 +134,19 @@ def __intermediate_encryption(self, wireless): uci['encryption'] += '+{0}'.format(cipher) return uci + def __intermediate_encryption_wpa_enterprise(self, wireless, encryption, uci): + if 'eap_type' in uci and uci['eap_type'] == 'tls' and 'auth' in uci: + # remove auth if not needed + # (not applicable to EAP-TLS) + del uci['auth'] + if wireless['mode'] == 'ap': + for option in ['server', 'port']: + if option in encryption: + uci[f'auth_{option}'] = encryption[option] + uci['auth_secret'] = encryption['key'] + if not encryption.get('acct_secret'): + uci['acct_secret'] = encryption['key'] + roaming_properties = ( 'ft_over_ds', 'ft_psk_generate_local', @@ -251,6 +257,8 @@ def __netjson_wifi_typecast(self, wifi): 'auth_cache', 'acct_port', 'acct_server', + 'acct_secret', + 'acct_interval', 'nasid', 'ownip', 'dae_client', @@ -325,6 +333,16 @@ def __netjson_encryption(self, wifi): # noqa: C901 if key.startswith('s:'): key = key[2:] settings['key'] = key + if 'enterprise' in settings['protocol']: + if 'auth_secret' in settings: + settings['key'] = settings.pop('auth_secret') + if settings.get('acct_secret') and settings['acct_secret'] == settings.get( + 'key' + ): + settings.pop('acct_secret') + for option in ['server', 'port']: + if f'auth_{option}' in settings: + settings[option] = settings.pop(f'auth_{option}') # Management Frame Protection if 'ieee80211w' in wifi: settings['ieee80211w'] = wifi.pop('ieee80211w') @@ -337,6 +355,10 @@ def __netjson_encryption_typecast(self, encryption): encryption['port'] = int(encryption['port']) if 'acct_port' in encryption: encryption['acct_port'] = int(encryption['acct_port']) + if 'dae_port' in encryption: + encryption['dae_port'] = int(encryption['dae_port']) + if 'acct_interval' in encryption: + encryption['acct_interval'] = int(encryption['acct_interval']) if 'wps_label' in encryption: encryption['wps_label'] = encryption['wps_label'] == '1' if 'wps_pushbutton' in encryption: diff --git a/netjsonconfig/backends/openwrt/schema.py b/netjsonconfig/backends/openwrt/schema.py index 32111cee7..b7efb0c76 100644 --- a/netjsonconfig/backends/openwrt/schema.py +++ b/netjsonconfig/backends/openwrt/schema.py @@ -704,6 +704,50 @@ {"$ref": "#/definitions/radio_60g_band"}, ] }, + "encryption_wpa_enterprise_ap_base_settings": { + "properties": { + "acct_secret": { + "title": "accounting shared secret", + "type": "string", + "propertyOrder": 9, + }, + "acct_interval": { + "type": "integer", + "title": "accounting interval", + "default": 600, + "propertyOrder": 10, + }, + "dae_client": { + "title": "DAE client", + "type": "string", + "description": ( + "Dynamic Authorization Extension client." + " This client can send \"Disconnect-Request\"" + " or \"CoA-Request\" packets to forcibly disconnect a client" + " or change connection parameters." + ), + "propertyOrder": 11, + }, + "dae_port": { + "type": "integer", + "title": "DAE port", + # "description": "port the Dynamic Authorization Extension server listens on.", + "default": 3799, + "propertyOrder": 12, + }, + "dae_secret": { + "title": "DAE secret", + "type": "string", + "propertyOrder": 13, + }, + "nasid": { + "title": "NAS ID", + "type": "string", + "description": "NAS ID for RADIUS authentication requests", + "propertyOrder": 13, + }, + } + }, }, "properties": { "general": { diff --git a/tests/openwrt/test_encryption.py b/tests/openwrt/test_encryption.py index aeaa25490..2496f710d 100644 --- a/tests/openwrt/test_encryption.py +++ b/tests/openwrt/test_encryption.py @@ -288,6 +288,9 @@ def test_parse_wpa_personal(self): package wireless config wifi-iface 'wifi_wlan0' + option acct_secret 'radius_secret' + option auth_secret 'radius_secret' + option auth_server '192.168.0.1' option device 'radio0' option encryption 'wpa3-mixed+ccmp' option ieee80211w '1' @@ -325,6 +328,10 @@ def test_parse_wpa2_enterprise_mixed_ap(self): "port": 1812, "acct_server": "192.168.0.2", "acct_port": 1813, + "acct_interval": 300, + "dae_client": "192.168.0.2", + "dae_port": 3799, + "dae_secret": "radius_secret", "nasid": "2", "wpa_group_rekey": "350", "ieee80211w": "2", @@ -345,8 +352,16 @@ def test_parse_wpa2_enterprise_mixed_ap(self): package wireless config wifi-iface 'wifi_wlan0' + option acct_interval '300' option acct_port '1813' + option acct_secret 'radius_secret' option acct_server '192.168.0.2' + option auth_port '1812' + option auth_secret 'radius_secret' + option auth_server '192.168.0.1' + option dae_client '192.168.0.2' + option dae_port '3799' + option dae_secret 'radius_secret' option device 'radio0' option encryption 'wpa3+ccmp' option ieee80211w '2' @@ -407,7 +422,11 @@ def test_parse_wpa3_enterprise(self): config wifi-iface 'wifi_wlan0' option acct_port '1813' + option acct_secret 'radius_secret' option acct_server '192.168.0.2' + option auth_port '1812' + option auth_secret 'radius_secret' + option auth_server '192.168.0.1' option device 'radio0' option encryption 'wpa2+tkip' option ifname 'wlan0' @@ -461,6 +480,9 @@ def test_parse_wpa2_enterprise(self): package wireless config wifi-iface 'wifi_wlan0' + option acct_secret 'radius_secret' + option auth_secret 'radius_secret' + option auth_server '192.168.0.1' option device 'radio0' option encryption 'wpa-mixed' option ifname 'wlan0' @@ -511,6 +533,9 @@ def test_parse_wpa_enterprise_mixed_ap(self): package wireless config wifi-iface 'wifi_wlan0' + option acct_secret 'radius_secret' + option auth_secret 'radius_secret' + option auth_server '192.168.0.1' option device 'radio0' option encryption 'wpa+ccmp' option ifname 'wlan0' From d6f19acd3a28d5085e146705dea12cf5e1ae607d Mon Sep 17 00:00:00 2001 From: Gagan Deep Date: Tue, 27 Feb 2024 22:40:32 +0530 Subject: [PATCH 2/3] [fix] Setting acct_server_port in NetJSON was not generating acct_port option in OpenWrt --- netjsonconfig/backends/openwrt/converters/wireless.py | 10 +++++++--- tests/openwrt/test_encryption.py | 7 +++++-- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/netjsonconfig/backends/openwrt/converters/wireless.py b/netjsonconfig/backends/openwrt/converters/wireless.py index 0cbbb049e..49fecb568 100644 --- a/netjsonconfig/backends/openwrt/converters/wireless.py +++ b/netjsonconfig/backends/openwrt/converters/wireless.py @@ -110,7 +110,7 @@ def __intermediate_encryption(self, wireless): return {'encryption': 'none'} # otherwise configure encryption uci = encryption.copy() - for option in ['protocol', 'key', 'cipher', 'disabled']: + for option in ['protocol', 'key', 'cipher', 'disabled', 'acct_server_port']: if option in uci: del uci[option] protocol = encryption['protocol'] @@ -146,6 +146,8 @@ def __intermediate_encryption_wpa_enterprise(self, wireless, encryption, uci): uci['auth_secret'] = encryption['key'] if not encryption.get('acct_secret'): uci['acct_secret'] = encryption['key'] + if encryption.get('acct_server_port'): + uci['acct_port'] = encryption.pop('acct_server_port') roaming_properties = ( 'ft_over_ds', @@ -343,6 +345,8 @@ def __netjson_encryption(self, wifi): # noqa: C901 for option in ['server', 'port']: if f'auth_{option}' in settings: settings[option] = settings.pop(f'auth_{option}') + if settings.get('acct_port'): + settings['acct_server_port'] = settings.pop('acct_port') # Management Frame Protection if 'ieee80211w' in wifi: settings['ieee80211w'] = wifi.pop('ieee80211w') @@ -353,8 +357,8 @@ def __netjson_encryption_typecast(self, encryption): # type casting if 'port' in encryption: encryption['port'] = int(encryption['port']) - if 'acct_port' in encryption: - encryption['acct_port'] = int(encryption['acct_port']) + if 'acct_server_port' in encryption: + encryption['acct_server_port'] = int(encryption['acct_server_port']) if 'dae_port' in encryption: encryption['dae_port'] = int(encryption['dae_port']) if 'acct_interval' in encryption: diff --git a/tests/openwrt/test_encryption.py b/tests/openwrt/test_encryption.py index 2496f710d..645f84315 100644 --- a/tests/openwrt/test_encryption.py +++ b/tests/openwrt/test_encryption.py @@ -327,7 +327,7 @@ def test_parse_wpa2_enterprise_mixed_ap(self): "server": "192.168.0.1", "port": 1812, "acct_server": "192.168.0.2", - "acct_port": 1813, + "acct_server_port": 1813, "acct_interval": 300, "dae_client": "192.168.0.2", "dae_port": 3799, @@ -383,6 +383,9 @@ def test_render_wpa3_enterprise(self): def test_parse_wpa3_enterprise(self): o = OpenWrt(native=self._wpa3_enterprise_ap_uci) + from pprint import pprint + + pprint(dict(o.config)) self.assertEqual(o.config, self._wpa3_enterprise_ap_netjson) _wpa2_enterprise_ap_netjson = { @@ -401,7 +404,7 @@ def test_parse_wpa3_enterprise(self): "server": "192.168.0.1", "port": 1812, "acct_server": "192.168.0.2", - "acct_port": 1813, + "acct_server_port": 1813, "nasid": "2", "wpa_group_rekey": "350", }, From e0049b6b83c294c0c326143030cb27ab260967b8 Mon Sep 17 00:00:00 2001 From: Gagan Deep Date: Tue, 19 Mar 2024 01:00:12 +0530 Subject: [PATCH 3/3] [req-changes] Removed pprint and refactor code --- netjsonconfig/backends/openwrt/converters/wireless.py | 8 ++++---- tests/openwrt/test_encryption.py | 3 --- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/netjsonconfig/backends/openwrt/converters/wireless.py b/netjsonconfig/backends/openwrt/converters/wireless.py index 49fecb568..6679c39fd 100644 --- a/netjsonconfig/backends/openwrt/converters/wireless.py +++ b/netjsonconfig/backends/openwrt/converters/wireless.py @@ -144,9 +144,9 @@ def __intermediate_encryption_wpa_enterprise(self, wireless, encryption, uci): if option in encryption: uci[f'auth_{option}'] = encryption[option] uci['auth_secret'] = encryption['key'] - if not encryption.get('acct_secret'): + if 'acct_secret' not in encryption: uci['acct_secret'] = encryption['key'] - if encryption.get('acct_server_port'): + if 'acct_server_port' in encryption: uci['acct_port'] = encryption.pop('acct_server_port') roaming_properties = ( @@ -338,14 +338,14 @@ def __netjson_encryption(self, wifi): # noqa: C901 if 'enterprise' in settings['protocol']: if 'auth_secret' in settings: settings['key'] = settings.pop('auth_secret') - if settings.get('acct_secret') and settings['acct_secret'] == settings.get( + if 'acct_secret' in settings and settings['acct_secret'] == settings.get( 'key' ): settings.pop('acct_secret') for option in ['server', 'port']: if f'auth_{option}' in settings: settings[option] = settings.pop(f'auth_{option}') - if settings.get('acct_port'): + if 'acct_port' in settings: settings['acct_server_port'] = settings.pop('acct_port') # Management Frame Protection if 'ieee80211w' in wifi: diff --git a/tests/openwrt/test_encryption.py b/tests/openwrt/test_encryption.py index 645f84315..ff81110ee 100644 --- a/tests/openwrt/test_encryption.py +++ b/tests/openwrt/test_encryption.py @@ -383,9 +383,6 @@ def test_render_wpa3_enterprise(self): def test_parse_wpa3_enterprise(self): o = OpenWrt(native=self._wpa3_enterprise_ap_uci) - from pprint import pprint - - pprint(dict(o.config)) self.assertEqual(o.config, self._wpa3_enterprise_ap_netjson) _wpa2_enterprise_ap_netjson = {