From 3e285639c9ebd4a415d1abbb738eecf3445e14fd Mon Sep 17 00:00:00 2001 From: Christian Breunig Date: Sat, 15 Jun 2024 08:02:49 +0200 Subject: T6489: add vyos_configdir to the dictionary of default directories (cherry picked from commit f0923acffbef04c1f8cf2a6c8a9e2afd66c4a494) --- python/vyos/configsession.py | 10 ++++++---- python/vyos/defaults.py | 5 +++-- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/python/vyos/configsession.py b/python/vyos/configsession.py index beec6010b..ccf2ce8f2 100644 --- a/python/vyos/configsession.py +++ b/python/vyos/configsession.py @@ -1,5 +1,4 @@ -# configsession -- the write API for the VyOS running config -# Copyright (C) 2019-2023 VyOS maintainers and contributors +# Copyright (C) 2019-2024 VyOS maintainers and contributors # # This library is free software; you can redistribute it and/or modify it under the terms of # the GNU Lesser General Public License as published by the Free Software Foundation; @@ -12,11 +11,14 @@ # You should have received a copy of the GNU Lesser General Public License along with this library; # if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# configsession -- the write API for the VyOS running config + import os import re import sys import subprocess +from vyos.defaults import directories from vyos.utils.process import is_systemd_service_running from vyos.utils.dict import dict_to_paths @@ -58,7 +60,7 @@ def inject_vyos_env(env): env['VYOS_HEADLESS_CLIENT'] = 'vyos_http_api' env['vyatta_bindir']= '/opt/vyatta/bin' env['vyatta_cfg_templates'] = '/opt/vyatta/share/vyatta-cfg/templates' - env['vyatta_configdir'] = '/opt/vyatta/config' + env['vyatta_configdir'] = directories['vyos_configdir'] env['vyatta_datadir'] = '/opt/vyatta/share' env['vyatta_datarootdir'] = '/opt/vyatta/share' env['vyatta_libdir'] = '/opt/vyatta/lib' @@ -70,7 +72,7 @@ def inject_vyos_env(env): env['vyos_bin_dir'] = '/usr/bin' env['vyos_cfg_templates'] = '/opt/vyatta/share/vyatta-cfg/templates' env['vyos_completion_dir'] = '/usr/libexec/vyos/completion' - env['vyos_configdir'] = '/opt/vyatta/config' + env['vyos_configdir'] = directories['vyos_configdir'] env['vyos_conf_scripts_dir'] = '/usr/libexec/vyos/conf_mode' env['vyos_datadir'] = '/opt/vyatta/share' env['vyos_datarootdir']= '/opt/vyatta/share' diff --git a/python/vyos/defaults.py b/python/vyos/defaults.py index e7cd69a8b..9ccd925ce 100644 --- a/python/vyos/defaults.py +++ b/python/vyos/defaults.py @@ -1,4 +1,4 @@ -# Copyright 2018-2023 VyOS maintainers and contributors +# Copyright 2018-2024 VyOS maintainers and contributors # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public @@ -35,6 +35,7 @@ directories = { 'vyos_udev_dir' : '/run/udev/vyos', 'isc_dhclient_dir' : '/run/dhclient', 'dhcp6_client_dir' : '/run/dhcp6c', + 'vyos_configdir' : '/opt/vyatta/config' } config_status = '/tmp/vyos-config-status' @@ -44,7 +45,7 @@ cfg_group = 'vyattacfg' cfg_vintage = 'vyos' -commit_lock = '/opt/vyatta/config/.lock' +commit_lock = os.path.join(directories['vyos_configdir'], '.lock') component_version_json = os.path.join(directories['data'], 'component-versions.json') -- cgit v1.2.3 From fad0128567931fd32e568896a0ba789396ab9311 Mon Sep 17 00:00:00 2001 From: Christian Breunig Date: Sat, 15 Jun 2024 08:03:37 +0200 Subject: login: T6489: add smarter way to interact with the working config instead of my_set/my_delete (cherry picked from commit da29c9b3ab7b0cc23d64c8b033fc5a79c1b09174) --- data/configd-include.json | 1 + src/conf_mode/system_login.py | 59 +++++++++++++++++++------------------------ 2 files changed, 27 insertions(+), 33 deletions(-) diff --git a/data/configd-include.json b/data/configd-include.json index dcee50306..ac1c18ad1 100644 --- a/data/configd-include.json +++ b/data/configd-include.json @@ -92,6 +92,7 @@ "system_ip.py", "system_ipv6.py", "system_lcd.py", +"system_login.py", "system_login_banner.py", "system_logs.py", "system_option.py", diff --git a/src/conf_mode/system_login.py b/src/conf_mode/system_login.py index 20121f170..e616ec3db 100755 --- a/src/conf_mode/system_login.py +++ b/src/conf_mode/system_login.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2020-2023 VyOS maintainers and contributors +# Copyright (C) 2020-2024 VyOS maintainers and contributors # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 or later as @@ -21,6 +21,7 @@ from psutil import users from pwd import getpwall from pwd import getpwnam from pwd import getpwuid +from shutil import rmtree from sys import exit from time import sleep @@ -31,6 +32,7 @@ from vyos.template import render from vyos.template import is_ipv4 from vyos.utils.dict import dict_search from vyos.utils.file import chown +from vyos.utils.file import write_file from vyos.utils.process import cmd from vyos.utils.process import call from vyos.utils.process import rc_cmd @@ -47,6 +49,8 @@ tacacs_pam_config_file = "/etc/tacplus_servers" tacacs_nss_config_file = "/etc/tacplus_nss.conf" nss_config_file = "/etc/nsswitch.conf" +current_user = None + # Minimum UID used when adding system users MIN_USER_UID: int = 1000 # Maximim UID used when adding system users @@ -118,6 +122,9 @@ def get_config(config=None): rm_users = [tmp for tmp in all_users if tmp not in cli_users] if rm_users: login.update({'rm_users' : rm_users}) + if 'SUDO_USER' in os.environ: + current_user = os.environ['SUDO_USER'] + return login def verify(login): @@ -125,10 +132,8 @@ def verify(login): # This check is required as the script is also executed from vyos-router # init script and there is no SUDO_USER environment variable available # during system boot. - if 'SUDO_USER' in os.environ: - cur_user = os.environ['SUDO_USER'] - if cur_user in login['rm_users']: - raise ConfigError(f'Attempting to delete current user: {cur_user}') + if current_user in login['rm_users']: + raise ConfigError(f'Attempting to delete current user: {cur_user}') if 'user' in login: system_users = getpwall() @@ -214,6 +219,7 @@ def verify(login): def generate(login): # calculate users encrypted password if 'user' in login: + env = os.environ.copy() for user, user_config in login['user'].items(): tmp = dict_search('authentication.plaintext_password', user_config) if tmp: @@ -221,35 +227,22 @@ def generate(login): login['user'][user]['authentication']['encrypted_password'] = encrypted_password del login['user'][user]['authentication']['plaintext_password'] - # remove old plaintext password and set new encrypted password - env = os.environ.copy() - env['vyos_libexec_dir'] = directories['base'] - # Set default commands for re-adding user with encrypted password - del_user_plain = f"system login user {user} authentication plaintext-password" - add_user_encrypt = f"system login user {user} authentication encrypted-password '{encrypted_password}'" - - lvl = env['VYATTA_EDIT_LEVEL'] - # We're in config edit level, for example "edit system login" - # Change default commands for re-adding user with encrypted password - if lvl != '/': - # Replace '/system/login' to 'system login' - lvl = lvl.strip('/').split('/') - # Convert command str to list - del_user_plain = del_user_plain.split() - # New command exclude level, for example "edit system login" - del_user_plain = del_user_plain[len(lvl):] - # Convert string to list - del_user_plain = " ".join(del_user_plain) - - add_user_encrypt = add_user_encrypt.split() - add_user_encrypt = add_user_encrypt[len(lvl):] - add_user_encrypt = " ".join(add_user_encrypt) - - ret, out = rc_cmd(f"/opt/vyatta/sbin/my_delete {del_user_plain}", env=env) - if ret: raise ConfigError(out) - ret, out = rc_cmd(f"/opt/vyatta/sbin/my_set {add_user_encrypt}", env=env) - if ret: raise ConfigError(out) + del_user_plain = f'system login user {user} authentication plaintext-password' + add_user_encrypt = f'system login user {user} authentication encrypted-password' + + for config_dir in ['VYATTA_TEMP_CONFIG_DIR', 'VYATTA_CHANGES_ONLY_DIR']: + tmp = os.path.join(env[config_dir], '/'.join(del_user_plain.split())) + # delete temporary plaintext-password CLI node + if os.path.exists(tmp): + rmtree(tmp) + + # store encrypted password + tmp = os.path.join(env[config_dir], '/'.join(add_user_encrypt.split())) + write_file(f'{tmp}/node.val', encrypted_password, user=current_user, group='vyattacfg', mode=0o664) + if config_dir == 'VYATTA_CHANGES_ONLY_DIR': + write_file(f'{tmp}/.modified', encrypted_password, user=current_user, group='vyattacfg', mode=0o664) + else: try: if get_shadow_password(user) == dict_search('authentication.encrypted_password', user_config): -- cgit v1.2.3 From 6baee9809a0626f9f555060cfb7b173388377deb Mon Sep 17 00:00:00 2001 From: Christian Breunig Date: Sat, 15 Jun 2024 08:44:10 +0200 Subject: T6489: add abstraction vyos.utils.auth.get_current_user() (cherry picked from commit e1a34e661d3e5f0090550796ac266dac15e1e337) --- python/vyos/utils/auth.py | 12 +++++++++--- src/conf_mode/system_login.py | 17 ++++++----------- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/python/vyos/utils/auth.py b/python/vyos/utils/auth.py index a59858d72..d014f756f 100644 --- a/python/vyos/utils/auth.py +++ b/python/vyos/utils/auth.py @@ -1,6 +1,6 @@ # authutils -- miscelanneous functions for handling passwords and publis keys # -# Copyright (C) 2018 VyOS maintainers and contributors +# Copyright (C) 2023-2024 VyOS maintainers and contributors # # This library is free software; you can redistribute it and/or modify it under the terms of # the GNU Lesser General Public License as published by the Free Software Foundation; @@ -11,13 +11,12 @@ # See the GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License along with this library; -# if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA import re from vyos.utils.process import cmd - def make_password_hash(password): """ Makes a password hash for /etc/shadow using mkpasswd """ @@ -39,3 +38,10 @@ def split_ssh_public_key(key_string, defaultname=""): raise ValueError("Bad key type \'{0}\', must be one of must be one of ssh-rsa, ssh-dss, ecdsa-sha2-nistp<256|384|521> or ssh-ed25519".format(key_type)) return({"type": key_type, "data": key_data, "name": key_name}) + +def get_current_user() -> str: + import os + current_user = 'nobody' + if 'SUDO_USER' in os.environ: + current_user = os.environ['SUDO_USER'] + return current_user diff --git a/src/conf_mode/system_login.py b/src/conf_mode/system_login.py index e616ec3db..afddae4dc 100755 --- a/src/conf_mode/system_login.py +++ b/src/conf_mode/system_login.py @@ -30,6 +30,7 @@ from vyos.configverify import verify_vrf from vyos.defaults import directories from vyos.template import render from vyos.template import is_ipv4 +from vyos.utils.auth import get_current_user from vyos.utils.dict import dict_search from vyos.utils.file import chown from vyos.utils.file import write_file @@ -49,8 +50,6 @@ tacacs_pam_config_file = "/etc/tacplus_servers" tacacs_nss_config_file = "/etc/tacplus_nss.conf" nss_config_file = "/etc/nsswitch.conf" -current_user = None - # Minimum UID used when adding system users MIN_USER_UID: int = 1000 # Maximim UID used when adding system users @@ -122,9 +121,6 @@ def get_config(config=None): rm_users = [tmp for tmp in all_users if tmp not in cli_users] if rm_users: login.update({'rm_users' : rm_users}) - if 'SUDO_USER' in os.environ: - current_user = os.environ['SUDO_USER'] - return login def verify(login): @@ -132,8 +128,9 @@ def verify(login): # This check is required as the script is also executed from vyos-router # init script and there is no SUDO_USER environment variable available # during system boot. - if current_user in login['rm_users']: - raise ConfigError(f'Attempting to delete current user: {cur_user}') + tmp = get_current_user() + if tmp in login['rm_users']: + raise ConfigError(f'Attempting to delete current user: {tmp}') if 'user' in login: system_users = getpwall() @@ -239,9 +236,9 @@ def generate(login): # store encrypted password tmp = os.path.join(env[config_dir], '/'.join(add_user_encrypt.split())) - write_file(f'{tmp}/node.val', encrypted_password, user=current_user, group='vyattacfg', mode=0o664) + write_file(f'{tmp}/node.val', encrypted_password, user=get_current_user(), group='vyattacfg', mode=0o664) if config_dir == 'VYATTA_CHANGES_ONLY_DIR': - write_file(f'{tmp}/.modified', encrypted_password, user=current_user, group='vyattacfg', mode=0o664) + write_file(f'{tmp}/.modified', encrypted_password, user=get_current_user(), group='vyattacfg', mode=0o664) else: try: @@ -276,8 +273,6 @@ def generate(login): if os.path.isfile(tacacs_nss_config_file): os.unlink(tacacs_nss_config_file) - - # NSS must always be present on the system render(nss_config_file, 'login/nsswitch.conf.j2', login, permission=0o644, user='root', group='root') -- cgit v1.2.3 From 8603967cbd7eb1ecdbad2e0960d1a18c667d38a3 Mon Sep 17 00:00:00 2001 From: Christian Breunig Date: Sat, 15 Jun 2024 08:44:54 +0200 Subject: T6489: add abstraction vyos.utils.configfs to work natively with the config filesystem (cherry picked from commit d7a18a3da949bfa3df89661cc0871e8f23b18a10) --- python/vyos/utils/__init__.py | 1 + python/vyos/utils/configfs.py | 37 +++++++++++++++++++++++++++++++++++++ src/conf_mode/system_login.py | 26 +++++++------------------- 3 files changed, 45 insertions(+), 19 deletions(-) create mode 100644 python/vyos/utils/configfs.py diff --git a/python/vyos/utils/__init__.py b/python/vyos/utils/__init__.py index 1cd062a11..90620071b 100644 --- a/python/vyos/utils/__init__.py +++ b/python/vyos/utils/__init__.py @@ -17,6 +17,7 @@ from vyos.utils import assertion from vyos.utils import auth from vyos.utils import boot from vyos.utils import commit +from vyos.utils import configfs from vyos.utils import convert from vyos.utils import cpu from vyos.utils import dict diff --git a/python/vyos/utils/configfs.py b/python/vyos/utils/configfs.py new file mode 100644 index 000000000..8617f0129 --- /dev/null +++ b/python/vyos/utils/configfs.py @@ -0,0 +1,37 @@ +# Copyright 2024 VyOS maintainers and contributors +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library. If not, see . + +import os + +def delete_cli_node(cli_path: list): + from shutil import rmtree + for config_dir in ['VYATTA_TEMP_CONFIG_DIR', 'VYATTA_CHANGES_ONLY_DIR']: + tmp = os.path.join(os.environ[config_dir], '/'.join(cli_path)) + # delete CLI node + if os.path.exists(tmp): + rmtree(tmp) + +def add_cli_node(cli_path: list, value: str=None): + from vyos.utils.auth import get_current_user + from vyos.utils.file import write_file + + current_user = get_current_user() + for config_dir in ['VYATTA_TEMP_CONFIG_DIR', 'VYATTA_CHANGES_ONLY_DIR']: + # store new value + tmp = os.path.join(os.environ[config_dir], '/'.join(cli_path)) + write_file(f'{tmp}/node.val', value, user=current_user, group='vyattacfg', mode=0o664) + # mark CLI node as modified + if config_dir == 'VYATTA_CHANGES_ONLY_DIR': + write_file(f'{tmp}/.modified', '', user=current_user, group='vyattacfg', mode=0o664) diff --git a/src/conf_mode/system_login.py b/src/conf_mode/system_login.py index afddae4dc..439fa645b 100755 --- a/src/conf_mode/system_login.py +++ b/src/conf_mode/system_login.py @@ -21,22 +21,20 @@ from psutil import users from pwd import getpwall from pwd import getpwnam from pwd import getpwuid -from shutil import rmtree from sys import exit from time import sleep from vyos.config import Config from vyos.configverify import verify_vrf -from vyos.defaults import directories from vyos.template import render from vyos.template import is_ipv4 from vyos.utils.auth import get_current_user +from vyos.utils.configfs import delete_cli_node +from vyos.utils.configfs import add_cli_node from vyos.utils.dict import dict_search from vyos.utils.file import chown -from vyos.utils.file import write_file from vyos.utils.process import cmd from vyos.utils.process import call -from vyos.utils.process import rc_cmd from vyos.utils.process import run from vyos.utils.process import DEVNULL from vyos import ConfigError @@ -216,7 +214,6 @@ def verify(login): def generate(login): # calculate users encrypted password if 'user' in login: - env = os.environ.copy() for user, user_config in login['user'].items(): tmp = dict_search('authentication.plaintext_password', user_config) if tmp: @@ -225,20 +222,11 @@ def generate(login): del login['user'][user]['authentication']['plaintext_password'] # Set default commands for re-adding user with encrypted password - del_user_plain = f'system login user {user} authentication plaintext-password' - add_user_encrypt = f'system login user {user} authentication encrypted-password' - - for config_dir in ['VYATTA_TEMP_CONFIG_DIR', 'VYATTA_CHANGES_ONLY_DIR']: - tmp = os.path.join(env[config_dir], '/'.join(del_user_plain.split())) - # delete temporary plaintext-password CLI node - if os.path.exists(tmp): - rmtree(tmp) - - # store encrypted password - tmp = os.path.join(env[config_dir], '/'.join(add_user_encrypt.split())) - write_file(f'{tmp}/node.val', encrypted_password, user=get_current_user(), group='vyattacfg', mode=0o664) - if config_dir == 'VYATTA_CHANGES_ONLY_DIR': - write_file(f'{tmp}/.modified', encrypted_password, user=get_current_user(), group='vyattacfg', mode=0o664) + del_user_plain = ['system', 'login', 'user', user, 'authentication', 'plaintext-password'] + add_user_encrypt = ['system', 'login', 'user', user, 'authentication', 'encrypted-password'] + + delete_cli_node(del_user_plain) + add_cli_node(add_user_encrypt, value=encrypted_password) else: try: -- cgit v1.2.3