diff options
author | Christian Poessinger <christian@poessinger.com> | 2020-05-21 13:55:32 +0200 |
---|---|---|
committer | Christian Poessinger <christian@poessinger.com> | 2020-05-21 14:52:10 +0200 |
commit | 3872f5995644a8a52358285d682a7103b54dde04 (patch) | |
tree | 3d4edb713d215c9b1fd4ee9f90288ad8751246b7 | |
parent | 63a3110298e5f3f6d24d5ed57eff0a8abf27f6ac (diff) | |
download | vyos-1x-3872f5995644a8a52358285d682a7103b54dde04.tar.gz vyos-1x-3872f5995644a8a52358285d682a7103b54dde04.zip |
macsec: T2023: use wpa_supplicant for key management
-rw-r--r-- | data/templates/macsec/wpa_supplicant.conf.tmpl | 53 | ||||
-rw-r--r-- | interface-definitions/interfaces-macsec.xml.in | 23 | ||||
-rw-r--r-- | python/vyos/ifconfig/macsec.py | 13 | ||||
-rwxr-xr-x | src/conf_mode/interfaces-macsec.py | 36 | ||||
-rw-r--r-- | src/systemd/wpa_supplicant-macsec@.service | 16 |
5 files changed, 122 insertions, 19 deletions
diff --git a/data/templates/macsec/wpa_supplicant.conf.tmpl b/data/templates/macsec/wpa_supplicant.conf.tmpl new file mode 100644 index 000000000..b73d4b863 --- /dev/null +++ b/data/templates/macsec/wpa_supplicant.conf.tmpl @@ -0,0 +1,53 @@ +# autogenerated by interfaces-macsec.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 + +# Note: When using MACsec, eapol_version shall be set to 3, which is +# defined in IEEE Std 802.1X-2010. +eapol_version=3 + +# No need to scan for access points in MACsec mode +ap_scan=0 + +# EAP fast re-authentication +fast_reauth=1 + +network={ + key_mgmt=NONE + + # 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 + + # macsec_policy: IEEE 802.1X/MACsec options + # This determines how sessions are secured with MACsec (only for MACsec + # drivers). + # 0: MACsec not in use (default) + # 1: MACsec enabled - Should secure, accept key server's advice to + # determine whether to use a secure session or not. + macsec_policy=1 + + # macsec_integ_only: IEEE 802.1X/MACsec transmit mode + # This setting applies only when MACsec is in use, i.e., + # - macsec_policy is enabled + # - the key server has decided to enable MACsec + # 0: Encrypt traffic (default) + # 1: Integrity only + macsec_integ_only={{ '0' if security_encrypt else '1' }} + + mka_cak={{ security_key_cak }} + mka_ckn={{ security_key_ckn }} +} + diff --git a/interface-definitions/interfaces-macsec.xml.in b/interface-definitions/interfaces-macsec.xml.in index 53a347f11..f76fef298 100644 --- a/interface-definitions/interfaces-macsec.xml.in +++ b/interface-definitions/interfaces-macsec.xml.in @@ -43,6 +43,29 @@ <valueless/> </properties> </leafNode> + <node name="key"> + <properties> + <help>Encryption keys</help> + </properties> + <children> + <leafNode name="cak"> + <properties> + <help>Secure Connectivity Association Key</help> + <constraint> + <regex>^[A-Fa-f0-9]{32}$</regex> + </constraint> + </properties> + </leafNode> + <leafNode name="ckn"> + <properties> + <help>Secure Connectivity Association Key Name</help> + <constraint> + <regex>^[A-Fa-f0-9]{64}$</regex> + </constraint> + </properties> + </leafNode> + </children> + </node> </children> </node> #include <include/interface-description.xml.i> diff --git a/python/vyos/ifconfig/macsec.py b/python/vyos/ifconfig/macsec.py index 1829df4ab..ea8c9807e 100644 --- a/python/vyos/ifconfig/macsec.py +++ b/python/vyos/ifconfig/macsec.py @@ -30,7 +30,7 @@ class MACsecIf(Interface): default = { 'type': 'macsec', - 'cipher': '', + 'security_cipher': '', 'source_interface': '' } definition = { @@ -41,7 +41,7 @@ class MACsecIf(Interface): }, } options = Interface.options + \ - ['cipher', 'source_interface'] + ['security_cipher', 'source_interface'] def _create(self): """ @@ -50,17 +50,12 @@ class MACsecIf(Interface): """ # create tunnel interface cmd = 'ip link add link {source_interface} {ifname} type {type}' - cmd += ' cipher {cipher}' + cmd += ' cipher {security_cipher}' self._cmd(cmd.format(**self.config)) # interface is always A/D down. It needs to be enabled explicitly self.set_admin_state('down') - def set_encryption(self, on_off): - ifname = self.config['ifname'] - cmd = f'ip link set {ifname} type macsec encrypt {on_off}' - return self._cmd(cmd) - @staticmethod def get_config(): """ @@ -72,7 +67,7 @@ class MACsecIf(Interface): >> dict = MACsecIf().get_config() """ config = { - 'cipher': '', + 'security_cipher': '', 'source_interface': '', } return config diff --git a/src/conf_mode/interfaces-macsec.py b/src/conf_mode/interfaces-macsec.py index fefc50d99..e59df6f90 100755 --- a/src/conf_mode/interfaces-macsec.py +++ b/src/conf_mode/interfaces-macsec.py @@ -22,19 +22,22 @@ from netifaces import interfaces from vyos.config import Config from vyos.configdict import list_diff -from vyos.ifconfig import MACsecIf +from vyos.ifconfig import MACsecIf, Interface from vyos.template import render +from vyos.util import call from vyos.validate import is_member from vyos import ConfigError default_config_data = { 'address': [], 'address_remove': [], - 'cipher': '', 'deleted': False, 'description': '', 'disable': False, - 'encrypt': 'off', + 'security_cipher': '', + 'security_encrypt': False, + 'security_key_cak': '', + 'security_key_ckn': '', 'intf': '', 'source_interface': '', 'is_bridge_member': False, @@ -77,11 +80,19 @@ def get_config(): # retrieve interface cipher if conf.exists(['security', 'cipher']): - macsec['cipher'] = conf.return_value(['security', 'cipher']) + macsec['security_cipher'] = conf.return_value(['security', 'cipher']) # Enable optional MACsec encryption if conf.exists(['security', 'encrypt']): - macsec['encrypt'] = 'on' + macsec['security_encrypt'] = True + + # Secure Connectivity Association Key + if conf.exists(['security', 'key', 'cak']): + macsec['security_key_cak'] = conf.return_value(['security', 'key', 'cak']) + + # Secure Connectivity Association Name + if conf.exists(['security', 'key', 'ckn']): + macsec['security_key_ckn'] = conf.return_value(['security', 'key', 'ckn']) # Physical interface if conf.exists(['source-interface']): @@ -112,7 +123,7 @@ def verify(macsec): raise ConfigError(( f'Physical source interface must be set for MACsec "{macsec["intf"]}"')) - if not macsec['cipher']: + if not macsec['security_cipher']: raise ConfigError(( f'Cipher suite is mandatory for MACsec "{macsec["intf"]}"')) @@ -134,12 +145,18 @@ def verify(macsec): return None def generate(macsec): + # XXX: wpa_supplicant works on the source interface not the resulting + # MACsec interface + conf = f'/run/wpa_supplicant/wpa_supplicant-{macsec["source_interface"]}.conf' + render(conf, 'macsec/wpa_supplicant.conf.tmpl', macsec, permission=0o640) return None def apply(macsec): # Remove macsec interface if macsec['deleted']: + call(f'systemctl stop wpa_supplicant-@{macsec["intf"]}.service') MACsecIf(macsec['intf']).remove() + else: # MACsec interfaces require a configuration when they are added using # iproute2. This static method will provide the configuration @@ -148,15 +165,12 @@ def apply(macsec): # Assign MACsec instance configuration parameters to config dict conf['source_interface'] = macsec['source_interface'] - conf['cipher'] = macsec['cipher'] + conf['security_cipher'] = macsec['security_cipher'] # It is safe to "re-create" the interface always, there is a sanity check # that the interface will only be create if its non existent i = MACsecIf(macsec['intf'], **conf) - # Configure optional encryption - i.set_encryption(macsec['encrypt']) - # update interface description used e.g. within SNMP i.set_alias(macsec['description']) @@ -177,6 +191,8 @@ def apply(macsec): if not macsec['disable']: i.set_admin_state('up') + call(f'systemctl restart wpa_supplicant-macsec@{macsec["source_interface"]}.service') + return None if __name__ == '__main__': diff --git a/src/systemd/wpa_supplicant-macsec@.service b/src/systemd/wpa_supplicant-macsec@.service new file mode 100644 index 000000000..7ad12e54e --- /dev/null +++ b/src/systemd/wpa_supplicant-macsec@.service @@ -0,0 +1,16 @@ +[Unit]
+Description=WPA supplicant daemon (macsec-specific version)
+Requires=sys-subsystem-net-devices-%i.device
+ConditionPathExists=/run/wpa_supplicant/wpa_supplicant-%I.conf
+After=vyos-router.service
+RequiresMountsFor=/run
+
+# NetworkManager users will probably want the dbus version instead.
+
+[Service]
+Type=simple
+WorkingDirectory=/run/wpa_supplicant
+ExecStart=/sbin/wpa_supplicant -c /run/wpa_supplicant/wpa_supplicant-%I.conf -Dmacsec_linux -i%I
+
+[Install]
+WantedBy=multi-user.target
|