summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--data/templates/macsec/wpa_supplicant.conf.tmpl21
-rw-r--r--interface-definitions/interfaces-macsec.xml.in16
-rw-r--r--op-mode-definitions/connect-disconnect.xml4
-rw-r--r--op-mode-definitions/show-interfaces-pppoe.xml4
-rwxr-xr-xsrc/completion/list_pppoe_peers.sh6
-rwxr-xr-xsrc/conf_mode/interfaces-macsec.py55
-rwxr-xr-xsrc/conf_mode/interfaces-pppoe.py2
-rwxr-xr-xsrc/conf_mode/system-login.py93
8 files changed, 137 insertions, 64 deletions
diff --git a/data/templates/macsec/wpa_supplicant.conf.tmpl b/data/templates/macsec/wpa_supplicant.conf.tmpl
index eee215418..a614d23f5 100644
--- a/data/templates/macsec/wpa_supplicant.conf.tmpl
+++ b/data/templates/macsec/wpa_supplicant.conf.tmpl
@@ -47,6 +47,7 @@ network={
# 1: Integrity only
macsec_integ_only={{ '0' if security_encrypt else '1' }}
+{% if security_encrypt %}
# mka_cak, mka_ckn, and mka_priority: IEEE 802.1X/MACsec pre-shared key mode
# This allows to configure MACsec with a pre-shared key using a (CAK,CKN) pair.
# In this mode, instances of wpa_supplicant can act as MACsec peers. The peer
@@ -61,5 +62,25 @@ network={
# mka_priority (Priority of MKA Actor) is in 0..255 range with 255 being
# default priority
mka_priority={{ security_mka_priority }}
+{% endif %}
+{% if security_replay_window %}
+ # macsec_replay_protect: IEEE 802.1X/MACsec replay protection
+ # This setting applies only when MACsec is in use, i.e.,
+ # - macsec_policy is enabled
+ # - the key server has decided to enable MACsec
+ # 0: Replay protection disabled (default)
+ # 1: Replay protection enabled
+ macsec_replay_protect={{ '1' if security_replay_window else '0' }}
+
+ # macsec_replay_window: IEEE 802.1X/MACsec replay protection window
+ # This determines a window in which replay is tolerated, to allow receipt
+ # of frames that have been misordered by the network.
+ # This setting applies only when MACsec replay protection active, i.e.,
+ # - macsec_replay_protect is enabled
+ # - the key server has decided to enable MACsec
+ # 0: No replay window, strict check (default)
+ # 1..2^32-1: number of packets that could be misordered
+ macsec_replay_window={{ security_replay_window }}
+{% endif %}
}
diff --git a/interface-definitions/interfaces-macsec.xml.in b/interface-definitions/interfaces-macsec.xml.in
index 3bd0a6fd5..36605ab59 100644
--- a/interface-definitions/interfaces-macsec.xml.in
+++ b/interface-definitions/interfaces-macsec.xml.in
@@ -86,6 +86,22 @@
</leafNode>
</children>
</node>
+ <leafNode name="replay-window">
+ <properties>
+ <help>IEEE 802.1X/MACsec replay protection window</help>
+ <valueHelp>
+ <format>0</format>
+ <description>No replay window, strict check</description>
+ </valueHelp>
+ <valueHelp>
+ <format>1-4294967295</format>
+ <description>Number of packets that could be misordered</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 0-4294967295" />
+ </constraint>
+ </properties>
+ </leafNode>
</children>
</node>
#include <include/interface-description.xml.i>
diff --git a/op-mode-definitions/connect-disconnect.xml b/op-mode-definitions/connect-disconnect.xml
index 77c334180..cbce2d9ef 100644
--- a/op-mode-definitions/connect-disconnect.xml
+++ b/op-mode-definitions/connect-disconnect.xml
@@ -9,7 +9,7 @@
<properties>
<help>Bring up a connection-oriented network interface</help>
<completionHelp>
- <script>${vyos_completion_dir}/list_pppoe_peers.sh</script>
+ <path>interfaces pppoe</path>
</completionHelp>
</properties>
<command>sudo ${vyos_op_scripts_dir}/connect_disconnect.py --connect "$3"</command>
@@ -25,7 +25,7 @@
<properties>
<help>Take down a connection-oriented network interface</help>
<completionHelp>
- <script>${vyos_completion_dir}/list_pppoe_peers.sh</script>
+ <path>interfaces pppoe</path>
</completionHelp>
</properties>
<command>sudo ${vyos_op_scripts_dir}/connect_disconnect.py --disconnect "$3"</command>
diff --git a/op-mode-definitions/show-interfaces-pppoe.xml b/op-mode-definitions/show-interfaces-pppoe.xml
index f9903ee08..cd72bff7d 100644
--- a/op-mode-definitions/show-interfaces-pppoe.xml
+++ b/op-mode-definitions/show-interfaces-pppoe.xml
@@ -8,7 +8,7 @@
<properties>
<help>Show PPPoE interface information</help>
<completionHelp>
- <script>${vyos_completion_dir}/list_pppoe_peers.sh</script>
+ <path>interfaces pppoe</path>
</completionHelp>
</properties>
<command>${vyos_op_scripts_dir}/show_interfaces.py --intf="$4"</command>
@@ -17,7 +17,7 @@
<properties>
<help>Show specified wirelessmodem interface statistics</help>
<completionHelp>
- <script>${vyos_completion_dir}/list_pppoe_peers.sh</script>
+ <path>interfaces pppoe</path>
</completionHelp>
</properties>
<command>/usr/sbin/pppstats $4</command>
diff --git a/src/completion/list_pppoe_peers.sh b/src/completion/list_pppoe_peers.sh
deleted file mode 100755
index 382a29264..000000000
--- a/src/completion/list_pppoe_peers.sh
+++ /dev/null
@@ -1,6 +0,0 @@
-#!/bin/sh
-
-if [ -d /etc/ppp/peers ]; then
- cd /etc/ppp/peers
- ls pppoe*
-fi
diff --git a/src/conf_mode/interfaces-macsec.py b/src/conf_mode/interfaces-macsec.py
index 6a3bb49fe..7d6f238f3 100755
--- a/src/conf_mode/interfaces-macsec.py
+++ b/src/conf_mode/interfaces-macsec.py
@@ -22,7 +22,7 @@ from netifaces import interfaces
from vyos.config import Config
from vyos.configdict import list_diff
-from vyos.ifconfig import MACsecIf, Interface
+from vyos.ifconfig import MACsecIf
from vyos.template import render
from vyos.util import call
from vyos.validate import is_member
@@ -39,6 +39,7 @@ default_config_data = {
'security_mka_cak': '',
'security_mka_ckn': '',
'security_mka_priority': '255',
+ 'security_replay_window': '',
'intf': '',
'source_interface': '',
'is_bridge_member': False,
@@ -48,6 +49,7 @@ default_config_data = {
# XXX: wpa_supplicant works on the source interface
wpa_suppl_conf = '/run/wpa_supplicant/{source_interface}.conf'
+
def get_config():
macsec = deepcopy(default_config_data)
conf = Config()
@@ -68,7 +70,8 @@ def get_config():
# When stopping wpa_supplicant we need to stop it via the physical
# interface - thus we need to retrieve ir from the effective config
if conf.exists_effective(base_path + ['source-interface']):
- macsec['source_interface'] = conf.return_effective_value(base_path + ['source-interface'])
+ macsec['source_interface'] = conf.return_effective_value(
+ base_path + ['source-interface'])
return macsec
@@ -97,15 +100,23 @@ def get_config():
# Secure Connectivity Association Key
if conf.exists(['security', 'mka', 'cak']):
- macsec['security_mka_cak'] = conf.return_value(['security', 'mka', 'cak'])
+ macsec['security_mka_cak'] = conf.return_value(
+ ['security', 'mka', 'cak'])
# Secure Connectivity Association Name
if conf.exists(['security', 'mka', 'ckn']):
- macsec['security_mka_ckn'] = conf.return_value(['security', 'mka', 'ckn'])
+ macsec['security_mka_ckn'] = conf.return_value(
+ ['security', 'mka', 'ckn'])
# MACsec Key Agreement protocol (MKA) actor priority
if conf.exists(['security', 'mka', 'priority']):
- macsec['security_mka_priority'] = conf.return_value(['security', 'mka', 'priority'])
+ macsec['security_mka_priority'] = conf.return_value(
+ ['security', 'mka', 'priority'])
+
+ # IEEE 802.1X/MACsec replay protection
+ if conf.exists(['security', 'replay-window']):
+ macsec['security_replay_window'] = conf.return_value(
+ ['security', 'replay-window'])
# Physical interface
if conf.exists(['source-interface']):
@@ -123,18 +134,19 @@ def get_config():
return macsec
+
def verify(macsec):
if macsec['deleted']:
if macsec['is_bridge_member']:
raise ConfigError(
- f'Interface "{intf}" cannot be deleted as it is a '
- f'member of bridge "{is_bridge_member}"!'.format(**macsec))
+ 'Interface "{intf}" cannot be deleted as it is a '
+ 'member of bridge "{is_bridge_member}"!'.format(**macsec))
return None
if not macsec['source_interface']:
- raise ConfigError(
- 'Physical source interface must be set for MACsec "{intf}"'.format(**macsec))
+ raise ConfigError('Physical source interface must be set for '
+ 'MACsec "{intf}"'.format(**macsec))
if not macsec['security_cipher']:
raise ConfigError(
@@ -142,16 +154,17 @@ def verify(macsec):
if macsec['security_encrypt']:
if not (macsec['security_mka_cak'] and macsec['security_mka_ckn']):
- raise ConfigError('MACsec security keys mandartory when encryption is enabled')
+ raise ConfigError(
+ 'MACsec security keys mandartory when encryption is enabled')
if macsec['vrf']:
if macsec['vrf'] not in interfaces():
raise ConfigError('VRF "{vrf}" does not exist'.format(**macsec))
if macsec['is_bridge_member']:
- raise ConfigError(
- 'Interface "{intf}" cannot be member of VRF "{vrf}" and '
- 'bridge "{is_bridge_member}" at the same time!'.format(**macsec))
+ raise ConfigError('Interface "{intf}" cannot be member of VRF '
+ '"{vrf}" and bridge "{is_bridge_member}" at '
+ 'the same time!'.format(**macsec))
if macsec['is_bridge_member'] and macsec['address']:
raise ConfigError(
@@ -160,14 +173,18 @@ def verify(macsec):
return None
+
def generate(macsec):
- render(wpa_suppl_conf.format(**macsec), 'macsec/wpa_supplicant.conf.tmpl', macsec, permission=0o640)
+ render(wpa_suppl_conf.format(**macsec),
+ 'macsec/wpa_supplicant.conf.tmpl', macsec, permission=0o640)
return None
+
def apply(macsec):
# Remove macsec interface
if macsec['deleted']:
- call('systemctl stop wpa_supplicant-macsec@{source_interface}.service'.format(**macsec))
+ call('systemctl stop wpa_supplicant-macsec@{source_interface}'
+ .format(**macsec))
MACsecIf(macsec['intf']).remove()
# delete configuration on interface removal
@@ -184,8 +201,8 @@ def apply(macsec):
conf['source_interface'] = macsec['source_interface']
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
+ # 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)
# update interface description used e.g. within SNMP
@@ -208,10 +225,12 @@ def apply(macsec):
if not macsec['disable']:
i.set_admin_state('up')
- call('systemctl restart wpa_supplicant-macsec@{source_interface}.service'.format(**macsec))
+ call('systemctl restart wpa_supplicant-macsec@{source_interface}'
+ .format(**macsec))
return None
+
if __name__ == '__main__':
try:
c = get_config()
diff --git a/src/conf_mode/interfaces-pppoe.py b/src/conf_mode/interfaces-pppoe.py
index d58ce99cc..6cde850c9 100755
--- a/src/conf_mode/interfaces-pppoe.py
+++ b/src/conf_mode/interfaces-pppoe.py
@@ -236,7 +236,7 @@ def apply(pppoe):
# bail out early
return None
- if pppoe['disable']:
+ if not pppoe['disable']:
# Dial PPPoE connection
call('systemctl restart ppp@{intf}.service'.format(**pppoe))
diff --git a/src/conf_mode/system-login.py b/src/conf_mode/system-login.py
index 09c5422eb..e6dfd544b 100755
--- a/src/conf_mode/system-login.py
+++ b/src/conf_mode/system-login.py
@@ -24,13 +24,11 @@ from stat import S_IRUSR, S_IWUSR, S_IRWXU, S_IRGRP, S_IXGRP
from sys import exit
from vyos.config import Config
-from vyos.configdict import list_diff
-from vyos import ConfigError
+from vyos.template import render
from vyos.util import cmd
from vyos.util import call
from vyos.util import DEVNULL
-from vyos.template import render
-
+from vyos import ConfigError
radius_config_file = "/etc/pam_radius_auth.conf"
@@ -43,8 +41,9 @@ default_config_data = {
'radius_vrf': ''
}
+
def get_local_users():
- """Returns list of dynamically allocated users (see Debian Policy Manual)"""
+ """Return list of dynamically allocated users (see Debian Policy Manual)"""
local_users = []
for p in getpwall():
username = p[0]
@@ -55,6 +54,7 @@ def get_local_users():
return local_users
+
def get_config():
login = default_config_data
conf = Config()
@@ -71,7 +71,7 @@ def get_config():
user = {
'name': username,
'password_plaintext': '',
- 'password_encrypted': '!',
+ 'password_encred': '!',
'public_keys': [],
'full_name': '',
'home_dir': '/home/' + username,
@@ -80,11 +80,13 @@ def get_config():
# Plaintext password
if conf.exists(['authentication', 'plaintext-password']):
- user['password_plaintext'] = conf.return_value(['authentication', 'plaintext-password'])
+ user['password_plaintext'] = conf.return_value(
+ ['authentication', 'plaintext-password'])
# Encrypted password
if conf.exists(['authentication', 'encrypted-password']):
- user['password_encrypted'] = conf.return_value(['authentication', 'encrypted-password'])
+ user['password_encrypted'] = conf.return_value(
+ ['authentication', 'encrypted-password'])
# User real name
if conf.exists(['full-name']):
@@ -102,7 +104,8 @@ def get_config():
'options': '',
'type': ''
}
- conf.set_level(base_level + ['user', username, 'authentication', 'public-keys', id])
+ conf.set_level(base_level + ['user', username, 'authentication',
+ 'public-keys', id])
# Public Key portion
if conf.exists(['key']):
@@ -174,21 +177,24 @@ def get_config():
# system is rebooted.
login['del_users'] = [tmp for tmp in all_users if tmp not in cli_users]
-
return login
+
def verify(login):
cur_user = os.environ['SUDO_USER']
if cur_user in login['del_users']:
- raise ConfigError('Attempting to delete current user: {}'.format(cur_user))
+ raise ConfigError(
+ 'Attempting to delete current user: {}'.format(cur_user))
for user in login['add_users']:
for key in user['public_keys']:
if not key['type']:
- raise ConfigError('SSH public key type missing for "{}"!'.format(key['name']))
+ raise ConfigError(
+ 'SSH public key type missing for "{name}"!'.format(**key))
if not key['key']:
- raise ConfigError('SSH public key for id "{}" missing!'.format(key['name']))
+ raise ConfigError(
+ 'SSH public key for id "{name}" missing!'.format(**key))
# At lease one RADIUS server must not be disabled
if len(login['radius_server']) > 0:
@@ -205,28 +211,35 @@ def verify(login):
return None
+
def generate(login):
# calculate users encrypted password
for user in login['add_users']:
if user['password_plaintext']:
- user['password_encrypted'] = crypt(user['password_plaintext'], METHOD_SHA512)
+ user['password_encrypted'] = crypt(
+ user['password_plaintext'], METHOD_SHA512)
user['password_plaintext'] = ''
- # remove old plaintext password
- # and set new encrypted password
- os.system("vyos_libexec_dir=/usr/libexec/vyos /opt/vyatta/sbin/my_set system login user '{}' authentication plaintext-password '' >/dev/null".format(user['name']))
- os.system("vyos_libexec_dir=/usr/libexec/vyos /opt/vyatta/sbin/my_set system login user '{}' authentication encrypted-password '{}' >/dev/null".format(user['name'], user['password_encrypted']))
+ # remove old plaintext password and set new encrypted password
+ env = os.environ.copy()
+ env['vyos_libexec_dir'] = '/usr/libexec/vyos'
+
+ call("/opt/vyatta/sbin/my_set system login user '{name}' "
+ "authentication plaintext-password '{password_plaintext}'"
+ .format(**user), env=env)
- # env = os.environ.copy()
- # env['vyos_libexec_dir'] = '/usr/libexec/vyos'
+ call("/opt/vyatta/sbin/my_set system login user '{name}' "
+ "authentication encrypted-password '{password_encrypted}'"
+ .format(**user), env=env)
- # call("/opt/vyatta/sbin/my_set system login user '{}' authentication plaintext-password ''".format(user['name']),
- # env=env)
- # call("/opt/vyatta/sbin/my_set system login user '{}' authentication encrypted-password '{}'".format(user['name'], user['password_encrypted']),
- # env=env)
+ elif user['password_encrypted']:
+ # unset encrypted password so we do not update it with the same
+ # value again and thus it will not appear in system logs
+ user['password_encrypted'] = ''
if len(login['radius_server']) > 0:
- render(radius_config_file, 'system-login/pam_radius_auth.conf.tmpl', login, trim_blocks=True)
+ render(radius_config_file, 'system-login/pam_radius_auth.conf.tmpl',
+ login, trim_blocks=True)
uid = getpwnam('root').pw_uid
gid = getpwnam('root').pw_gid
@@ -238,6 +251,7 @@ def generate(login):
return None
+
def apply(login):
for user in login['add_users']:
# make new user using vyatta shell and make home directory (-m),
@@ -248,10 +262,13 @@ def apply(login):
# update existing account
command = "usermod"
+ # all accounts use /bin/vbash
+ command += " -s /bin/vbash"
# we need to use '' quotes when passing formatted data to the shell
# else it will not work as some data parts are lost in translation
- command += " -p '{}'".format(user['password_encrypted'])
- command += " -s /bin/vbash"
+ if user['password_encrypted']:
+ command += " -p '{}'".format(user['password_encrypted'])
+
if user['full_name']:
command += " -c '{}'".format(user['full_name'])
@@ -267,10 +284,11 @@ def apply(login):
uid = getpwnam(user['name']).pw_uid
gid = getpwnam(user['name']).pw_gid
- # we should not rely on the home dir value stored in user['home_dir']
- # as if a crazy user will choose username root or any other system
- # user this will fail. should be deny using root at all?
+ # we should not rely on the value stored in user['home_dir'], as a
+ # crazy user will choose username root or any other system user
+ # which will fail. Should we deny using root at all?
home_dir = getpwnam(user['name']).pw_dir
+
# install ssh keys
ssh_key_dir = home_dir + '/.ssh'
if not os.path.isdir(ssh_key_dir):
@@ -278,7 +296,7 @@ def apply(login):
os.chown(ssh_key_dir, uid, gid)
os.chmod(ssh_key_dir, S_IRWXU | S_IRGRP | S_IXGRP)
- ssh_key_file = ssh_key_dir + '/authorized_keys';
+ ssh_key_file = ssh_key_dir + '/authorized_keys'
with open(ssh_key_file, 'w') as f:
f.write("# Automatically generated by VyOS\n")
f.write("# Do not edit, all changes will be lost\n")
@@ -288,14 +306,17 @@ def apply(login):
if id['options']:
line = '{} '.format(id['options'])
- line += '{} {} {}\n'.format(id['type'], id['key'], id['name'])
+ line += '{} {} {}\n'.format(id['type'],
+ id['key'], id['name'])
f.write(line)
os.chown(ssh_key_file, uid, gid)
os.chmod(ssh_key_file, S_IRUSR | S_IWUSR)
except Exception as e:
- raise ConfigError('Adding user "{}" raised an exception: {}'.format(user['name'], e))
+ print(e)
+ raise ConfigError('Adding user "{name}" raised exception'
+ .format(**user))
for user in login['del_users']:
try:
@@ -308,7 +329,7 @@ def apply(login):
call(f'userdel -r {user}', stderr=DEVNULL)
except Exception as e:
- raise ConfigError('Deleting user "{}" raised an exception: {}'.format(user, e))
+ raise ConfigError(f'Deleting user "{user}" raised exception: {e}')
#
# RADIUS configuration
@@ -351,10 +372,12 @@ def apply(login):
cmd(command)
except Exception as e:
- raise ConfigError('Removing RADIUS configuration failed.\n{}'.format(e))
+ raise ConfigError(
+ 'Removing RADIUS configuration failed.\n{}'.format(e))
return None
+
if __name__ == '__main__':
try:
c = get_config()