Skip to content

Commit

Permalink
Merge pull request #3652 from c-po/T6489-unionfs
Browse files Browse the repository at this point in the history
T6489: Add support for CLI config scripts that change the underlaying working configuration
  • Loading branch information
c-po authored Jun 17, 2024
2 parents 4654c35 + d7a18a3 commit 14dd6e5
Show file tree
Hide file tree
Showing 7 changed files with 70 additions and 46 deletions.
1 change: 1 addition & 0 deletions data/configd-include.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
10 changes: 6 additions & 4 deletions python/vyos/configsession.py
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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

Expand Down Expand Up @@ -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'
Expand All @@ -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'
Expand Down
5 changes: 3 additions & 2 deletions python/vyos/defaults.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright 2018-2023 VyOS maintainers and contributors <[email protected]>
# Copyright 2018-2024 VyOS maintainers and contributors <[email protected]>
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
Expand Down Expand Up @@ -35,6 +35,7 @@
'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'
Expand All @@ -44,7 +45,7 @@

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')

Expand Down
1 change: 1 addition & 0 deletions python/vyos/utils/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
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
Expand Down
12 changes: 9 additions & 3 deletions python/vyos/utils/auth.py
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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 """

Expand All @@ -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
37 changes: 37 additions & 0 deletions python/vyos/utils/configfs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# Copyright 2024 VyOS maintainers and contributors <[email protected]>
#
# 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 <http://www.gnu.org/licenses/>.

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)
50 changes: 13 additions & 37 deletions src/conf_mode/system_login.py
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -26,14 +26,15 @@

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.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
Expand Down Expand Up @@ -125,10 +126,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 '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}')
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()
Expand Down Expand Up @@ -221,35 +221,13 @@ 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 = ['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:
if get_shadow_password(user) == dict_search('authentication.encrypted_password', user_config):
Expand Down Expand Up @@ -283,8 +261,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')
Expand Down

0 comments on commit 14dd6e5

Please sign in to comment.