summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGiga Murphy <giga1699@gmail.com>2023-08-18 00:26:51 +0000
committerGiga Murphy <giga1699@gmail.com>2023-08-18 00:26:51 +0000
commit33b9bc55f9e3d7ce6a5ff447a6b545a96915daf3 (patch)
treee1f8072a5f86d3ad2f69cc819b1f5afb05832cee
parent65ea7cef9fe922581e286bc539c4dc1e223c9d32 (diff)
downloadvyos-1x-33b9bc55f9e3d7ce6a5ff447a6b545a96915daf3.tar.gz
vyos-1x-33b9bc55f9e3d7ce6a5ff447a6b545a96915daf3.zip
T5447: Initial support for MACsec static keys
-rw-r--r--interface-definitions/interfaces-macsec.xml.in46
-rw-r--r--python/vyos/ifconfig/macsec.py30
-rwxr-xr-xsrc/conf_mode/interfaces-macsec.py56
3 files changed, 117 insertions, 15 deletions
diff --git a/interface-definitions/interfaces-macsec.xml.in b/interface-definitions/interfaces-macsec.xml.in
index 6bc28e44b..b81c9b40c 100644
--- a/interface-definitions/interfaces-macsec.xml.in
+++ b/interface-definitions/interfaces-macsec.xml.in
@@ -52,6 +52,52 @@
<valueless/>
</properties>
</leafNode>
+ <node name="static">
+ <properties>
+ <help>Assign static MACSec keys instead of using MKA</help>
+ </properties>
+ <children>
+ <leafNode name="tx-key">
+ <properties>
+ <help>Set the static transmit key</help>
+ <valueHelp>
+ <format>txt</format>
+ <description>16-byte (128-bit) hex-string (32 hex-digits) for gcm-aes-128 or 32-byte (256-bit) hex-string (64 hex-digits) for gcm-aes-256</description>
+ </valueHelp>
+ <constraint>
+ <regex>[A-Fa-f0-9]{32}</regex>
+ <regex>[A-Fa-f0-9]{64}</regex>
+ </constraint>
+ </properties>
+ </leafNode>
+ <tagNode name="peer">
+ <properties>
+ <help>peer alias</help>
+ <constraint>
+ <regex>[^ ]{1,100}</regex>
+ </constraint>
+ <constraintErrorMessage>peer alias too long (limit 100 characters)</constraintErrorMessage>
+ </properties>
+ <children>
+ #include <include/generic-disable-node.xml.i>
+ #include <include/interface/mac.xml.i>
+ <leafNode name="rx-key">
+ <properties>
+ <help>Set the static receive key for peer</help>
+ <valueHelp>
+ <format>txt</format>
+ <description>16-byte (128-bit) hex-string (32 hex-digits) for gcm-aes-128 or 32-byte (256-bit) hex-string (64 hex-digits) for gcm-aes-256</description>
+ </valueHelp>
+ <constraint>
+ <regex>[A-Fa-f0-9]{32}</regex>
+ <regex>[A-Fa-f0-9]{64}</regex>
+ </constraint>
+ </properties>
+ </leafNode>
+ </children>
+ </tagNode>
+ </children>
+ </node>
<node name="mka">
<properties>
<help>MACsec Key Agreement protocol (MKA)</help>
diff --git a/python/vyos/ifconfig/macsec.py b/python/vyos/ifconfig/macsec.py
index 1a78d18d8..6318a1688 100644
--- a/python/vyos/ifconfig/macsec.py
+++ b/python/vyos/ifconfig/macsec.py
@@ -39,12 +39,36 @@ class MACsecIf(Interface):
def _create(self):
"""
Create MACsec interface in OS kernel. Interface is administrative
- down by default.
+ down by default when not using static keys.
"""
+
# create tunnel interface
cmd = 'ip link add link {source_interface} {ifname} type {type}'.format(**self.config)
cmd += f' cipher {self.config["security"]["cipher"]}'
self._cmd(cmd)
- # interface is always A/D down. It needs to be enabled explicitly
- self.set_admin_state('down')
+ # Check if using static keys
+ if 'static' in self.config["security"]:
+ # Set static TX key
+ cmd = 'ip macsec add {ifname} tx sa 0 pn 1 on key 00'.format(**self.config)
+ cmd += f' {self.config["security"]["static"]["tx_key"]}'
+ self._cmd(cmd)
+
+ for peer, peer_config in self.config["security"]["static"]["peer"].items():
+ if 'disable' in peer_config:
+ continue
+
+ # Create the address
+ cmd = 'ip macsec add {ifname} rx port 1 address'.format(**self.config)
+ cmd += f' {peer_config["mac"]}'
+ self._cmd(cmd)
+ # Add the rx-key to the address
+ cmd += f' sa 0 pn 1 on key 01 {peer_config["rx_key"]}'
+ self._cmd(cmd)
+
+ # Set admin state to up
+ self.set_admin_state('up')
+
+ else:
+ # interface is always A/D down. It needs to be enabled explicitly
+ self.set_admin_state('down')
diff --git a/src/conf_mode/interfaces-macsec.py b/src/conf_mode/interfaces-macsec.py
index 3f86e2638..45fa3f5af 100755
--- a/src/conf_mode/interfaces-macsec.py
+++ b/src/conf_mode/interfaces-macsec.py
@@ -89,18 +89,46 @@ def verify(macsec):
raise ConfigError('Cipher suite must be set for MACsec "{ifname}"'.format(**macsec))
if dict_search('security.encrypt', macsec) != None:
- if dict_search('security.mka.cak', macsec) == None or dict_search('security.mka.ckn', macsec) == None:
- raise ConfigError('Missing mandatory MACsec security keys as encryption is enabled!')
+ # Check that only static or MKA config is present
+ if dict_search('security.static', macsec) != None and (dict_search('security.mka.cak', macsec) != None or dict_search('security.mka.ckn', macsec) != None):
+ raise ConfigError('Only static or MKA can be used!')
- cak_len = len(dict_search('security.mka.cak', macsec))
+ # Logic to check static configuration
+ if dict_search('security.static', macsec) != None:
- if dict_search('security.cipher', macsec) == 'gcm-aes-128' and cak_len != 32:
- # gcm-aes-128 requires a 128bit long key - 32 characters (string) = 16byte = 128bit
- raise ConfigError('gcm-aes-128 requires a 128bit long key!')
+ tx_len = len(dict_search('security.static.tx_key', macsec))
- elif dict_search('security.cipher', macsec) == 'gcm-aes-256' and cak_len != 64:
- # gcm-aes-128 requires a 128bit long key - 64 characters (string) = 32byte = 256bit
- raise ConfigError('gcm-aes-128 requires a 256bit long key!')
+ if dict_search('security.cipher', macsec) == 'gcm-aes-128' and tx_len != 32:
+ # gcm-aes-128 requires a 128bit long key - 32 characters (string) = 16byte = 128bit
+ raise ConfigError('gcm-aes-128 requires a 128bit long key!')
+
+ if dict_search('security.cipher', macsec) == 'gcm-aes-256' and tx_len != 64:
+ # gcm-aes-256 requires a 256bit long key - 64 characters (string) = 32byte = 256bit
+ raise ConfigError('gcm-aes-256 requires a 256bit long key!')
+
+ # Make sure at least one peer is defined
+ if 'peer' not in macsec['security']['static']:
+ raise ConfigError('Must have at least one peer defined for static MACsec')
+
+ # For every enabled peer, make sure a MAC and rx-key is defined
+ for peer, peer_config in macsec['security']['static']['peer'].items():
+ if 'disable' not in peer_config and ('mac' not in peer_config or 'rx_key' not in peer_config):
+ raise ConfigError('Every enabled MACsec static peer must have a MAC address and rx-key defined.')
+
+ # Logic to check MKA configuration
+ else:
+ if dict_search('security.mka.cak', macsec) == None or dict_search('security.mka.ckn', macsec) == None:
+ raise ConfigError('Missing mandatory MACsec security keys as encryption is enabled!')
+
+ cak_len = len(dict_search('security.mka.cak', macsec))
+
+ if dict_search('security.cipher', macsec) == 'gcm-aes-128' and cak_len != 32:
+ # gcm-aes-128 requires a 128bit long key - 32 characters (string) = 16byte = 128bit
+ raise ConfigError('gcm-aes-128 requires a 128bit long key!')
+
+ elif dict_search('security.cipher', macsec) == 'gcm-aes-256' and cak_len != 64:
+ # gcm-aes-256 requires a 256bit long key - 64 characters (string) = 32byte = 256bit
+ raise ConfigError('gcm-aes-256 requires a 256bit long key!')
if 'source_interface' in macsec:
# MACsec adds a 40 byte overhead (32 byte MACsec + 8 bytes VLAN 802.1ad
@@ -115,7 +143,9 @@ def verify(macsec):
def generate(macsec):
- render(wpa_suppl_conf.format(**macsec), 'macsec/wpa_supplicant.conf.j2', macsec)
+ # Only generate wpa_supplicant config if using MKA
+ if dict_search('security.static', macsec) == None:
+ render(wpa_suppl_conf.format(**macsec), 'macsec/wpa_supplicant.conf.j2', macsec)
return None
@@ -142,8 +172,10 @@ def apply(macsec):
i = MACsecIf(**macsec)
i.update(macsec)
- if not is_systemd_service_running(systemd_service) or 'shutdown_required' in macsec:
- call(f'systemctl reload-or-restart {systemd_service}')
+ #Only reload/restart if not using static keys
+ if dict_search('security.static', macsec) == None:
+ if not is_systemd_service_running(systemd_service) or 'shutdown_required' in macsec:
+ call(f'systemctl reload-or-restart {systemd_service}')
return None