diff options
author | Christian Poessinger <christian@poessinger.com> | 2020-12-29 11:34:40 +0100 |
---|---|---|
committer | Christian Poessinger <christian@poessinger.com> | 2020-12-29 11:51:28 +0100 |
commit | d59354e52a8a7fbdd6bb0a020f50600d64c799a9 (patch) | |
tree | 5bc44f1b8ca1dbb38a138ac8fc62645bc308e831 | |
parent | 5e5e87467dd6b22d1378269f4a62825b7d122a5c (diff) | |
download | vyos-1x-d59354e52a8a7fbdd6bb0a020f50600d64c799a9.tar.gz vyos-1x-d59354e52a8a7fbdd6bb0a020f50600d64c799a9.zip |
ethernet: T1466: add EAPoL support
-rw-r--r-- | data/templates/ethernet/wpa_supplicant.conf.tmpl | 72 | ||||
-rw-r--r-- | interface-definitions/include/interface-eapol.xml.i | 12 | ||||
-rw-r--r-- | interface-definitions/interfaces-ethernet.xml.in | 3 | ||||
-rw-r--r-- | python/vyos/configverify.py | 9 | ||||
-rwxr-xr-x | src/conf_mode/interfaces-ethernet.py | 24 |
5 files changed, 118 insertions, 2 deletions
diff --git a/data/templates/ethernet/wpa_supplicant.conf.tmpl b/data/templates/ethernet/wpa_supplicant.conf.tmpl new file mode 100644 index 000000000..fe518ad45 --- /dev/null +++ b/data/templates/ethernet/wpa_supplicant.conf.tmpl @@ -0,0 +1,72 @@ +### Autogenerated by interfaces-ethernet.py ### + +# see full documentation: +# https://w1.fi/cgit/hostap/plain/wpa_supplicant/wpa_supplicant.conf + +# For UNIX domain sockets (default on Linux and BSD): This is a directory that +# will be created for UNIX domain sockets for listening to requests from +# external programs (CLI/GUI, etc.) for status information and configuration. +# The socket file will be named based on the interface name, so multiple +# wpa_supplicant processes can be run at the same time if more than one +# interface is used. +# /var/run/wpa_supplicant is the recommended directory for sockets and by +# default, wpa_cli will use it when trying to connect with wpa_supplicant. +ctrl_interface=/run/wpa_supplicant + +# IEEE 802.1X/EAPOL version +# wpa_supplicant is implemented based on IEEE Std 802.1X-2004 which defines +# EAPOL version 2. However, there are many APs that do not handle the new +# version number correctly (they seem to drop the frames completely). In order +# to make wpa_supplicant interoperate with these APs, the version number is set +# to 1 by default. This configuration value can be used to set it to the new +# version (2). +# Note: When using MACsec, eapol_version shall be set to 3, which is +# defined in IEEE Std 802.1X-2010. +eapol_version=2 + +# No need to scan for access points in EAPoL mode +ap_scan=0 + +# EAP fast re-authentication +fast_reauth=1 + +network={ +{% if eapol is defined and eapol is not none %} +{% if eapol.ca_cert_file is defined and eapol.ca_cert_file is not none %} + ca_cert="{{ eapol.ca_cert_file }}" +{% endif %} + client_cert="{{ eapol.cert_file }}" + private_key="{{ eapol.key_file }}" +{% endif %} + + # list of accepted authenticated key management protocols + key_mgmt=IEEE8021X + eap=TLS + +{% if mac is defined and mac is not none %} + identity="{{ mac }}" +{% else %} + identity="{{ hw_id }}" +{% endif %} + + # eapol_flags: IEEE 802.1X/EAPOL options (bit field) + # Dynamic WEP key required for non-WPA mode + # bit0 (1): require dynamically generated unicast WEP key + # bit1 (2): require dynamically generated broadcast WEP key + # (3) = require both keys; default) + # Note: When using wired authentication (including MACsec drivers), + # eapol_flags must be set to 0 for the authentication to be completed + # successfully. + eapol_flags=0 + + # For wired IEEE 802.1X authentication, "allow_canned_success=1" can be + # used to configure a mode that allows EAP-Success (and EAP-Failure) without + # going through authentication step. Some switches use such sequence when + # forcing the port to be authorized/unauthorized or as a fallback option if + # the authentication server is unreachable. By default, wpa_supplicant + # discards such frames to protect against potential attacks by rogue + # devices, but this option can be used to disable that protection for cases + # where the server/authenticator does not need to be authenticated. + phase1="allow_canned_success=1" +} + diff --git a/interface-definitions/include/interface-eapol.xml.i b/interface-definitions/include/interface-eapol.xml.i new file mode 100644 index 000000000..94476f0f1 --- /dev/null +++ b/interface-definitions/include/interface-eapol.xml.i @@ -0,0 +1,12 @@ +<!-- included start from interface-eapol.xml.i -->
+<node name="eapol">
+ <properties>
+ <help>Extensible Authentication Protocol over Local Area Network</help>
+ </properties>
+ <children>
+ #include <include/certificate.xml.i>
+ #include <include/certificate-ca.xml.i>
+ #include <include/certificate-key.xml.i>
+ </children>
+</node>
+<!-- included end -->
diff --git a/interface-definitions/interfaces-ethernet.xml.in b/interface-definitions/interfaces-ethernet.xml.in index 5dd685c37..9564f5ab8 100644 --- a/interface-definitions/interfaces-ethernet.xml.in +++ b/interface-definitions/interfaces-ethernet.xml.in @@ -28,7 +28,6 @@ </leafNode> #include <include/interface-disable-link-detect.xml.i> #include <include/interface-disable.xml.i> - #include <include/interface-vrf.xml.i> <leafNode name="duplex"> <properties> <help>Duplex mode</help> @@ -54,6 +53,7 @@ </properties> <defaultValue>auto</defaultValue> </leafNode> + #include <include/interface-eapol.xml.i> #include <include/interface-hw-id.xml.i> #include <include/interface-ipv4-options.xml.i> #include <include/interface-ipv6-options.xml.i> @@ -187,6 +187,7 @@ </node> #include <include/vif-s.xml.i> #include <include/vif.xml.i> + #include <include/interface-vrf.xml.i> #include <include/interface-xdp.xml.i> </children> </tagNode> diff --git a/python/vyos/configverify.py b/python/vyos/configverify.py index 675dac5b1..96eeb6bb1 100644 --- a/python/vyos/configverify.py +++ b/python/vyos/configverify.py @@ -89,6 +89,15 @@ def verify_vrf(config): 'Interface "{ifname}" cannot be both a member of VRF "{vrf}" ' 'and bridge "{is_bridge_member}"!'.format(**config)) +def verify_eapol(config): + """ + Common helper function used by interface implementations to perform + recurring validation of EAPoL configuration. + """ + if 'eapol' in config: + if not {'cert_file', 'key_file'} <= set(config['eapol']): + raise ConfigError('Both cert and key-file must be specified '\ + 'when using EAPoL!') def verify_address(config): """ diff --git a/src/conf_mode/interfaces-ethernet.py b/src/conf_mode/interfaces-ethernet.py index b358e9725..d8b637dd7 100755 --- a/src/conf_mode/interfaces-ethernet.py +++ b/src/conf_mode/interfaces-ethernet.py @@ -28,12 +28,18 @@ from vyos.configverify import verify_mtu from vyos.configverify import verify_mtu_ipv6 from vyos.configverify import verify_vlan_config from vyos.configverify import verify_vrf +from vyos.configverify import verify_eapol from vyos.ifconfig import EthernetIf +from vyos.template import render +from vyos.util import call from vyos.util import dict_search from vyos import ConfigError from vyos import airbag airbag.enable() +# XXX: wpa_supplicant works on the source interface +wpa_suppl_conf = '/run/wpa_supplicant/{ifname}.conf' + def get_config(config=None): """ Retrive CLI config as dictionary. Dictionary can never be empty, as at least the @@ -67,6 +73,7 @@ def verify(ethernet): verify_dhcpv6(ethernet) verify_address(ethernet) verify_vrf(ethernet) + verify_eapol(ethernet) # XDP requires multiple TX queues if 'xdp' in ethernet: @@ -83,16 +90,31 @@ def verify(ethernet): return None def generate(ethernet): + if 'eapol' in ethernet: + render(wpa_suppl_conf.format(**ethernet), + 'ethernet/wpa_supplicant.conf.tmpl', ethernet) + else: + # delete configuration on interface removal + if os.path.isfile(wpa_suppl_conf.format(**ethernet)): + os.unlink(wpa_suppl_conf.format(**ethernet)) + return None def apply(ethernet): - e = EthernetIf(ethernet['ifname']) + ifname = ethernet['ifname'] + # take care about EAPoL supplicant daemon + eapol_action='stop' + + e = EthernetIf(ifname) if 'deleted' in ethernet: # delete interface e.remove() else: e.update(ethernet) + if 'eapol' in ethernet: + eapol_action='restart' + call(f'systemctl {eapol_action} wpa_supplicant-macsec@{ifname}') if __name__ == '__main__': try: |