summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Poessinger <christian@poessinger.com>2020-05-21 13:55:32 +0200
committerChristian Poessinger <christian@poessinger.com>2020-05-21 14:52:10 +0200
commit3872f5995644a8a52358285d682a7103b54dde04 (patch)
tree3d4edb713d215c9b1fd4ee9f90288ad8751246b7
parent63a3110298e5f3f6d24d5ed57eff0a8abf27f6ac (diff)
downloadvyos-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.tmpl53
-rw-r--r--interface-definitions/interfaces-macsec.xml.in23
-rw-r--r--python/vyos/ifconfig/macsec.py13
-rwxr-xr-xsrc/conf_mode/interfaces-macsec.py36
-rw-r--r--src/systemd/wpa_supplicant-macsec@.service16
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