diff options
| author | Christian Breunig <christian@breunig.cc> | 2023-08-23 20:20:23 +0200 | 
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-08-23 20:20:23 +0200 | 
| commit | db67bc204e77fb2dd7d4a76672c63bc9bbfc07e1 (patch) | |
| tree | b7e61ea893f7ff0768abbdb8f02f7c7842b3a4c4 | |
| parent | f25e1c9fa166c22d2a329ae435ae8be4f80985ef (diff) | |
| parent | 35a46e4abfcece1beefb898ebd7fcb688fc55de5 (diff) | |
| download | vyos-1x-db67bc204e77fb2dd7d4a76672c63bc9bbfc07e1.tar.gz vyos-1x-db67bc204e77fb2dd7d4a76672c63bc9bbfc07e1.zip | |
Merge pull request #2159 from c-po/t5491-wifi
wifi: T5491: allow white-/blacklisting station MAC addresses for security
| -rw-r--r-- | data/templates/wifi/hostapd.conf.j2 | 12 | ||||
| -rw-r--r-- | data/templates/wifi/hostapd_accept_station.conf.j2 | 7 | ||||
| -rw-r--r-- | data/templates/wifi/hostapd_deny_station.conf.j2 | 7 | ||||
| -rw-r--r-- | interface-definitions/include/interface/mac-multi.xml.i | 15 | ||||
| -rw-r--r-- | interface-definitions/interfaces-wireless.xml.in | 43 | ||||
| -rwxr-xr-x | smoketest/scripts/cli/test_interfaces_wireless.py | 44 | ||||
| -rwxr-xr-x | src/conf_mode/interfaces-wireless.py | 29 | ||||
| -rw-r--r-- | src/etc/netplug/netplugd.conf | 1 | 
8 files changed, 145 insertions, 13 deletions
| diff --git a/data/templates/wifi/hostapd.conf.j2 b/data/templates/wifi/hostapd.conf.j2 index f2312d2d4..613038597 100644 --- a/data/templates/wifi/hostapd.conf.j2 +++ b/data/templates/wifi/hostapd.conf.j2 @@ -430,14 +430,22 @@ ieee80211n={{ '1' if 'n' in mode or 'ac' in mode else '0' }}  ignore_broadcast_ssid=1  {% endif %} -# Station MAC address -based authentication +{% if type is vyos_defined('access-point') %} +# Station MAC address-based authentication  # Please note that this kind of access control requires a driver that uses  # hostapd to take care of management frame processing and as such, this can be  # used with driver=hostap or driver=nl80211, but not with driver=atheros.  # 0 = accept unless in deny list  # 1 = deny unless in accept list  # 2 = use external RADIUS server (accept/deny lists are searched first) -macaddr_acl=0 +macaddr_acl={{ '0' if security.station_address.mode is vyos_defined('accept') else '1' }} + +# Accept/deny lists are read from separate files (containing list of +# MAC addresses, one per line). Use absolute path name to make sure that the +# files can be read on SIGHUP configuration reloads. +accept_mac_file={{ hostapd_accept_station_conf }} +deny_mac_file={{ hostapd_deny_station_conf }} +{% endif %}  {% if max_stations is vyos_defined %}  # Maximum number of stations allowed in station table. New stations will be diff --git a/data/templates/wifi/hostapd_accept_station.conf.j2 b/data/templates/wifi/hostapd_accept_station.conf.j2 new file mode 100644 index 000000000..a381c947c --- /dev/null +++ b/data/templates/wifi/hostapd_accept_station.conf.j2 @@ -0,0 +1,7 @@ +# List of MAC addresses that are allowed to authenticate (IEEE 802.11) +# with the AP +{% if security.station_address.accept.mac is vyos_defined %} +{%     for mac in security.station_address.accept.mac %} +{{ mac | lower }} +{%     endfor %} +{% endif %} diff --git a/data/templates/wifi/hostapd_deny_station.conf.j2 b/data/templates/wifi/hostapd_deny_station.conf.j2 new file mode 100644 index 000000000..fb2950dda --- /dev/null +++ b/data/templates/wifi/hostapd_deny_station.conf.j2 @@ -0,0 +1,7 @@ +# List of MAC addresses that are not allowed to authenticate +# (IEEE 802.11) with the access point +{% if security.station_address.deny.mac is vyos_defined %} +{%     for mac in security.station_address.deny.mac %} +{{ mac | lower }} +{%     endfor %} +{% endif %} diff --git a/interface-definitions/include/interface/mac-multi.xml.i b/interface-definitions/include/interface/mac-multi.xml.i new file mode 100644 index 000000000..458372e67 --- /dev/null +++ b/interface-definitions/include/interface/mac-multi.xml.i @@ -0,0 +1,15 @@ +<!-- include start from interface/mac-multi.xml.i --> +<leafNode name="mac"> +  <properties> +    <help>Media Access Control (MAC) address</help> +    <valueHelp> +      <format>macaddr</format> +      <description>Hardware (MAC) address</description> +    </valueHelp> +    <constraint> +      <validator name="mac-address"/> +    </constraint> +    <multi/> +  </properties> +</leafNode> +<!-- include end --> diff --git a/interface-definitions/interfaces-wireless.xml.in b/interface-definitions/interfaces-wireless.xml.in index 421d46c6e..88b858c07 100644 --- a/interface-definitions/interfaces-wireless.xml.in +++ b/interface-definitions/interfaces-wireless.xml.in @@ -595,6 +595,49 @@                <help>Wireless security settings</help>              </properties>              <children> +              <node name="station-address"> +                <properties> +                  <help>Station MAC address based authentication</help> +                </properties> +                <children> +                  <leafNode name="mode"> +                    <properties> +                      <help>Select security operation mode</help> +                      <completionHelp> +                        <list>accept deny</list> +                      </completionHelp> +                      <valueHelp> +                        <format>accept</format> +                        <description>Accept all clients unless found in deny list</description> +                      </valueHelp> +                      <valueHelp> +                        <format>deny</format> +                        <description>Deny all clients unless found in accept list</description> +                      </valueHelp> +                      <constraint> +                        <regex>(accept|deny)</regex> +                      </constraint> +                    </properties> +                    <defaultValue>accept</defaultValue> +                  </leafNode> +                  <node name="accept"> +                    <properties> +                      <help>Accept station MAC address</help> +                    </properties> +                    <children> +                      #include <include/interface/mac-multi.xml.i> +                    </children> +                  </node> +                  <node name="deny"> +                    <properties> +                      <help>Deny station MAC address</help> +                    </properties> +                    <children> +                      #include <include/interface/mac-multi.xml.i> +                    </children> +                  </node> +                </children> +              </node>                <node name="wep">                  <properties>                    <help>Wired Equivalent Privacy (WEP) parameters</help> diff --git a/smoketest/scripts/cli/test_interfaces_wireless.py b/smoketest/scripts/cli/test_interfaces_wireless.py index 875ca9dc6..f8686edd8 100755 --- a/smoketest/scripts/cli/test_interfaces_wireless.py +++ b/smoketest/scripts/cli/test_interfaces_wireless.py @@ -234,9 +234,51 @@ class WirelessInterfaceTest(BasicInterfaceTest.TestCase):          self.assertIn(interface, bridge_members)          self.cli_delete(bridge_path) -        self.cli_delete(self._base_path) + +    def test_wireless_security_station_address(self): +        interface = 'wlan0' +        ssid = 'VyOS-ACL' + +        hostapd_accept_station_conf = f'/run/hostapd/{interface}_station_accept.conf' +        hostapd_deny_station_conf = f'/run/hostapd/{interface}_station_deny.conf' + +        accept_mac = ['00:00:00:00:ac:01', '00:00:00:00:ac:02', '00:00:00:00:ac:03', '00:00:00:00:ac:04'] +        deny_mac = ['00:00:00:00:de:01', '00:00:00:00:de:02', '00:00:00:00:de:03', '00:00:00:00:de:04'] + +        self.cli_set(self._base_path + [interface, 'ssid', ssid]) +        self.cli_set(self._base_path + [interface, 'country-code', 'se']) +        self.cli_set(self._base_path + [interface, 'type', 'access-point']) +        self.cli_set(self._base_path + [interface, 'security', 'station-address', 'mode', 'accept']) + +        for mac in accept_mac: +            self.cli_set(self._base_path + [interface, 'security', 'station-address', 'accept', 'mac', mac]) +        for mac in deny_mac: +            self.cli_set(self._base_path + [interface, 'security', 'station-address', 'deny', 'mac', mac]) +          self.cli_commit() +        # in accept mode all addresses are allowed unless specified in the deny list +        tmp = get_config_value(interface, 'macaddr_acl') +        self.assertEqual(tmp, '0') + +        accept_list = read_file(hostapd_accept_station_conf) +        for mac in accept_mac: +            self.assertIn(mac, accept_list) + +        deny_list = read_file(hostapd_deny_station_conf) +        for mac in deny_mac: +            self.assertIn(mac, deny_list) + +        #  Switch mode accept -> deny +        self.cli_set(self._base_path + [interface, 'security', 'station-address', 'mode', 'deny']) +        self.cli_commit() +        # In deny mode all addresses are denied unless specified in the allow list +        tmp = get_config_value(interface, 'macaddr_acl') +        self.assertEqual(tmp, '1') + +        # Check for running process +        self.assertTrue(process_named_running('hostapd')) +  if __name__ == '__main__':      check_kmod('mac80211_hwsim')      unittest.main(verbosity=2) diff --git a/src/conf_mode/interfaces-wireless.py b/src/conf_mode/interfaces-wireless.py index 29ab9713f..02b4a2500 100755 --- a/src/conf_mode/interfaces-wireless.py +++ b/src/conf_mode/interfaces-wireless.py @@ -25,8 +25,6 @@ from vyos.configdict import get_interface_dict  from vyos.configdict import dict_merge  from vyos.configverify import verify_address  from vyos.configverify import verify_bridge_delete -from vyos.configverify import verify_dhcpv6 -from vyos.configverify import verify_source_interface  from vyos.configverify import verify_mirror_redirect  from vyos.configverify import verify_vlan_config  from vyos.configverify import verify_vrf @@ -42,6 +40,8 @@ airbag.enable()  # XXX: wpa_supplicant works on the source interface  wpa_suppl_conf = '/run/wpa_supplicant/{ifname}.conf'  hostapd_conf = '/run/hostapd/{ifname}.conf' +hostapd_accept_station_conf = '/run/hostapd/{ifname}_station_accept.conf' +hostapd_deny_station_conf = '/run/hostapd/{ifname}_station_deny.conf'  def find_other_stations(conf, base, ifname):      """ @@ -81,10 +81,12 @@ def get_config(config=None):      if 'deleted' not in wifi:          # then get_interface_dict provides default keys -        if wifi.from_defaults(['security']): # if not set by user -            del wifi['security'] +        if wifi.from_defaults(['security', 'wep']): # if not set by user +            del wifi['security']['wep'] +        if wifi.from_defaults(['security', 'wpa']): # if not set by user +            del wifi['security']['wpa'] -    if 'security' in wifi and 'wpa' in wifi['security']: +    if dict_search('security.wpa', wifi) != None:          wpa_cipher = wifi['security']['wpa'].get('cipher')          wpa_mode = wifi['security']['wpa'].get('mode')          if not wpa_cipher: @@ -102,6 +104,10 @@ def get_config(config=None):      tmp = find_other_stations(conf, base, wifi['ifname'])      if tmp: wifi['station_interfaces'] = tmp +    # used in hostapt.conf.j2 +    wifi['hostapd_accept_station_conf'] = hostapd_accept_station_conf.format(**wifi) +    wifi['hostapd_deny_station_conf'] = hostapd_deny_station_conf.format(**wifi) +      return wifi  def verify(wifi): @@ -189,7 +195,10 @@ def generate(wifi):      if 'deleted' in wifi:          if os.path.isfile(hostapd_conf.format(**wifi)):              os.unlink(hostapd_conf.format(**wifi)) - +        if os.path.isfile(hostapd_accept_station_conf.format(**wifi)): +            os.unlink(hostapd_accept_station_conf.format(**wifi)) +        if os.path.isfile(hostapd_deny_station_conf.format(**wifi)): +            os.unlink(hostapd_deny_station_conf.format(**wifi))          if os.path.isfile(wpa_suppl_conf.format(**wifi)):              os.unlink(wpa_suppl_conf.format(**wifi)) @@ -224,12 +233,12 @@ def generate(wifi):      # render appropriate new config files depending on access-point or station mode      if wifi['type'] == 'access-point': -        render(hostapd_conf.format(**wifi), 'wifi/hostapd.conf.j2', -               wifi) +        render(hostapd_conf.format(**wifi), 'wifi/hostapd.conf.j2', wifi) +        render(hostapd_accept_station_conf.format(**wifi), 'wifi/hostapd_accept_station.conf.j2', wifi) +        render(hostapd_deny_station_conf.format(**wifi), 'wifi/hostapd_deny_station.conf.j2', wifi)      elif wifi['type'] == 'station': -        render(wpa_suppl_conf.format(**wifi), 'wifi/wpa_supplicant.conf.j2', -               wifi) +        render(wpa_suppl_conf.format(**wifi), 'wifi/wpa_supplicant.conf.j2', wifi)      return None diff --git a/src/etc/netplug/netplugd.conf b/src/etc/netplug/netplugd.conf index ab4d826d6..7da3c67e8 100644 --- a/src/etc/netplug/netplugd.conf +++ b/src/etc/netplug/netplugd.conf @@ -1,3 +1,4 @@  eth*  br*  bond* +wlan* | 
