summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorDaniil Baturin <daniil@vyos.io>2025-08-28 15:26:19 +0100
committerGitHub <noreply@github.com>2025-08-28 15:26:19 +0100
commit133cb0c65d5aa2ca5a96feba6ab7adc3a8f1de58 (patch)
treef395cec0aa9448a3b5d40eeeff361603f1d9b117 /src
parentd30039526bdd718f100a0f62986fb1e139d424f4 (diff)
parent021725628c5b6bdb00d9e853c649156813c17e57 (diff)
downloadvyos-1x-133cb0c65d5aa2ca5a96feba6ab7adc3a8f1de58.tar.gz
vyos-1x-133cb0c65d5aa2ca5a96feba6ab7adc3a8f1de58.zip
Merge pull request #4674 from dmbaturin/op-mode-permissions
op-mode: T7745: add a CLI for operator user command permissions
Diffstat (limited to 'src')
-rwxr-xr-xsrc/conf_mode/system_login.py39
-rw-r--r--src/opt/vyatta/etc/shell/level/users/allowed-op3
-rw-r--r--src/opt/vyatta/etc/shell/level/users/allowed-op.in3
-rw-r--r--src/opt/vyatta/share/vyatta-op/functions/interpreter/vyatta-op-run18
4 files changed, 60 insertions, 3 deletions
diff --git a/src/conf_mode/system_login.py b/src/conf_mode/system_login.py
index 234b24770..c2562be7b 100755
--- a/src/conf_mode/system_login.py
+++ b/src/conf_mode/system_login.py
@@ -14,7 +14,9 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
+import re
import os
+import json
from passlib.hosts import linux_context
from psutil import users
@@ -171,6 +173,15 @@ def verify(login):
if 'key' not in pubkey_options:
raise ConfigError(f'Missing key for public-key "{pubkey}"!')
+ if 'operator' in user_config:
+ op_groups = dict_search('operator.group', user_config)
+ if op_groups:
+ for og in op_groups:
+ if dict_search(f'operator_group.{og}', login) is None:
+ raise ConfigError(f'Operator group {og} does not exist')
+ else:
+ raise ConfigError(f'User {user} is configured as an operator but is not assigned to any operator groups')
+
if {'radius', 'tacacs'} <= set(login):
raise ConfigError('Using both RADIUS and TACACS at the same time is not supported!')
@@ -306,6 +317,28 @@ def generate(login):
if os.path.isfile(autologout_file):
os.unlink(autologout_file)
+ # Operator groups and group membership
+ operator_config = {'users': {}, 'groups': {}}
+ if 'user' in login:
+ for user, user_config in login['user'].items():
+ op_groups = dict_search('operator.group', user_config)
+ if op_groups:
+ operator_config['users'][user] = op_groups
+
+ if 'operator_group' in login:
+ operator_config['groups'] = login['operator_group']
+
+ # Convert permissions strings to list
+ # so that the operational command runner doesn't have to
+ for g in operator_config['groups']:
+ policy = dict_search(f'command_policy.allow', operator_config['groups'][g])
+ if policy is not None:
+ policy = list(map(lambda s: re.split(r'\s+', s), policy))
+ operator_config['groups'][g]['command_policy']['allow'] = policy
+
+ with open('/etc/vyos/operators.json', 'w') as of:
+ json.dump(operator_config, of)
+
return None
@@ -335,7 +368,11 @@ def apply(login):
if tmp: command += f" --home '{tmp}'"
else: command += f" --home '/home/{user}'"
- command += f' --groups frr,frrvty,vyattacfg,sudo,adm,dip,disk,_kea {user}'
+ if 'operator' not in user_config:
+ command += f' --groups frr,frrvty,vyattacfg,sudo,adm,dip,disk,_kea'
+
+ command += f' {user}'
+
try:
cmd(command)
# we should not rely on the value stored in user_config['home_directory'], as a
diff --git a/src/opt/vyatta/etc/shell/level/users/allowed-op b/src/opt/vyatta/etc/shell/level/users/allowed-op
index 381fd26e5..8d4749628 100644
--- a/src/opt/vyatta/etc/shell/level/users/allowed-op
+++ b/src/opt/vyatta/etc/shell/level/users/allowed-op
@@ -11,7 +11,10 @@ exit
force
monitor
ping
+poweroff
+reboot
reset
+restart
release
renew
set
diff --git a/src/opt/vyatta/etc/shell/level/users/allowed-op.in b/src/opt/vyatta/etc/shell/level/users/allowed-op.in
index 9752f99a2..e01559b1c 100644
--- a/src/opt/vyatta/etc/shell/level/users/allowed-op.in
+++ b/src/opt/vyatta/etc/shell/level/users/allowed-op.in
@@ -7,7 +7,10 @@ exit
force
monitor
ping
+poweroff
+reboot
reset
+restart
release
renew
set
diff --git a/src/opt/vyatta/share/vyatta-op/functions/interpreter/vyatta-op-run b/src/opt/vyatta/share/vyatta-op/functions/interpreter/vyatta-op-run
index 6bc77b61d..f43c85fa9 100644
--- a/src/opt/vyatta/share/vyatta-op/functions/interpreter/vyatta-op-run
+++ b/src/opt/vyatta/share/vyatta-op/functions/interpreter/vyatta-op-run
@@ -217,6 +217,20 @@ _vyatta_op_run ()
local run_cmd=$(_vyatta_op_get_node_def_field $tpath/node.def run)
run_cmd=$(_vyatta_op_conv_run_cmd "$run_cmd") # convert the positional parameters
local ret=0
+
+ if groups "$(whoami)" | grep -q -E ' vyattacfg(\s|$)'; then
+ # If the user is an admin,
+ # use sudo directly for now
+ op_runner="sudo"
+ else
+ # If the user is an operator,
+ # use the new operational command runner
+ # (operator users have no sudo permissions)
+ # and give it the original VyOS command
+ op_runner="vyos-op-run"
+ run_cmd="$@"
+ fi
+
# Exception for the `show file` command
local file_cmd='\$\{vyos_op_scripts_dir\}\/file\.py'
local cmd_regex="^(LESSOPEN=|less|pager|tail|(sudo )?$file_cmd).*"
@@ -234,9 +248,9 @@ _vyatta_op_run ()
# to be able to do their job.
eval "$run_cmd"
elif [[ -t 1 && "${args[1]}" == "show" && ! $run_cmd =~ $cmd_regex ]] ; then
- eval "(sudo $run_cmd) | ${VYATTA_PAGER:-cat}"
+ eval "($op_runner $run_cmd) | ${VYATTA_PAGER:-cat}"
else
- eval "sudo $run_cmd"
+ eval "$op_runner $run_cmd"
fi
else
echo -ne "\n Incomplete command: ${args[@]}\n\n" >&2