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
 | 
