* [pve-devel] [PATCH v3 access-control 01/17] add "SuperAdministrator" role with the new "SuperUser" privilege
2022-04-06 11:57 [pve-devel] [PATCH v3 access-control++ 00/17] SuperUser privilege Oguz Bektas
@ 2022-04-06 11:57 ` Oguz Bektas
2022-04-06 11:57 ` [pve-devel] [PATCH v3 access-control 02/17] RPC env: add SuperUser API permission for GUI capabilities Oguz Bektas
` (15 subsequent siblings)
16 siblings, 0 replies; 18+ messages in thread
From: Oguz Bektas @ 2022-04-06 11:57 UTC (permalink / raw)
To: pve-devel
Signed-off-by: Oguz Bektas <o.bektas@proxmox.com>
---
v2->v3:
* no changes
src/PVE/AccessControl.pm | 9 ++++++---
src/PVE/RPCEnvironment.pm | 2 +-
2 files changed, 7 insertions(+), 4 deletions(-)
diff --git a/src/PVE/AccessControl.pm b/src/PVE/AccessControl.pm
index 1306576..1137756 100644
--- a/src/PVE/AccessControl.pm
+++ b/src/PVE/AccessControl.pm
@@ -1008,6 +1008,7 @@ my $privgroups = {
},
Sys => {
root => [
+ 'SuperUser',
'Sys.PowerMgmt',
'Sys.Modify', # edit/change node settings
],
@@ -1074,7 +1075,8 @@ my $valid_privs = {};
my $special_roles = {
'NoAccess' => {}, # no privileges
- 'Administrator' => $valid_privs, # all privileges
+ 'Administrator' => {}, # populated with all privs except superuser
+ 'SuperAdministrator' => $valid_privs, # can do everything
};
sub create_roles {
@@ -1101,6 +1103,7 @@ sub create_roles {
}
$special_roles->{"PVETemplateUser"} = { 'VM.Clone' => 1, 'VM.Audit' => 1 };
+ $special_roles->{"Administrator"} = { map { $_ => 1 } grep { $_ ne 'SuperUser' } keys %$valid_privs };
};
create_roles();
@@ -1581,7 +1584,7 @@ sub write_user_config {
$collect_rolelist_members->($d->{'groups'}, $rolelist_members, '@');
- # no need to save 'root@pam', it is always 'Administrator'
+ # no need to save 'root@pam', it is always 'SuperAdministrator'
$collect_rolelist_members->($d->{'users'}, $rolelist_members, '', 'root@pam');
$collect_rolelist_members->($d->{'tokens'}, $rolelist_members, '');
@@ -1635,7 +1638,7 @@ sub roles {
# corresponding user has.
# Use $rpcenv->permission() for any actual permission checks!
- return 'Administrator' if $user eq 'root@pam'; # root can do anything
+ return 'SuperAdministrator' if $user eq 'root@pam'; # root can do anything
if (pve_verify_tokenid($user, 1)) {
my $tokenid = $user;
diff --git a/src/PVE/RPCEnvironment.pm b/src/PVE/RPCEnvironment.pm
index f11517f..cd4a0a5 100644
--- a/src/PVE/RPCEnvironment.pm
+++ b/src/PVE/RPCEnvironment.pm
@@ -101,7 +101,7 @@ sub permissions {
if ($user eq 'root@pam') { # root can do anything
my $cfg = $self->{user_cfg};
- return { map { $_ => 1 } keys %{$cfg->{roles}->{'Administrator'}} };
+ return { map { $_ => 1 } keys %{$cfg->{roles}->{'SuperAdministrator'}} };
}
if (PVE::AccessControl::pve_verify_tokenid($user, 1)) {
--
2.30.2
^ permalink raw reply [flat|nested] 18+ messages in thread
* [pve-devel] [PATCH v3 access-control 02/17] RPC env: add SuperUser API permission for GUI capabilities
2022-04-06 11:57 [pve-devel] [PATCH v3 access-control++ 00/17] SuperUser privilege Oguz Bektas
2022-04-06 11:57 ` [pve-devel] [PATCH v3 access-control 01/17] add "SuperAdministrator" role with the new "SuperUser" privilege Oguz Bektas
@ 2022-04-06 11:57 ` Oguz Bektas
2022-04-06 11:57 ` [pve-devel] [PATCH v3 access-control 03/17] api: acl: only allow granting SU privilege if user already has it Oguz Bektas
` (14 subsequent siblings)
16 siblings, 0 replies; 18+ messages in thread
From: Oguz Bektas @ 2022-04-06 11:57 UTC (permalink / raw)
To: pve-devel
Signed-off-by: Oguz Bektas <o.bektas@proxmox.com>
---
v2->v3:
* it was missing from v2 series
src/PVE/RPCEnvironment.pm | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/src/PVE/RPCEnvironment.pm b/src/PVE/RPCEnvironment.pm
index cd4a0a5..34c8991 100644
--- a/src/PVE/RPCEnvironment.pm
+++ b/src/PVE/RPCEnvironment.pm
@@ -139,12 +139,12 @@ sub compute_api_permission {
my $res = {};
my $priv_re_map = {
- vms => qr/VM\.|Permissions\.Modify/,
- access => qr/(User|Group)\.|Permissions\.Modify/,
- storage => qr/Datastore\.|Permissions\.Modify/,
- nodes => qr/Sys\.|Permissions\.Modify/,
- sdn => qr/SDN\.|Permissions\.Modify/,
- dc => qr/Sys\.Audit|SDN\./,
+ vms => qr/VM\.|Permissions\.Modify|SuperUser/,
+ access => qr/(User|Group)\.|Permissions\.Modify|SuperUser/,
+ storage => qr/Datastore\.|Permissions\.Modify|SuperUser/,
+ nodes => qr/Sys\.|Permissions\.Modify|SuperUser/,
+ sdn => qr/SDN\.|Permissions\.Modify|SuperUser/,
+ dc => qr/Sys\.Audit|SDN\.|SuperUser/,
};
map { $res->{$_} = {} } keys %$priv_re_map;
--
2.30.2
^ permalink raw reply [flat|nested] 18+ messages in thread
* [pve-devel] [PATCH v3 access-control 03/17] api: acl: only allow granting SU privilege if user already has it
2022-04-06 11:57 [pve-devel] [PATCH v3 access-control++ 00/17] SuperUser privilege Oguz Bektas
2022-04-06 11:57 ` [pve-devel] [PATCH v3 access-control 01/17] add "SuperAdministrator" role with the new "SuperUser" privilege Oguz Bektas
2022-04-06 11:57 ` [pve-devel] [PATCH v3 access-control 02/17] RPC env: add SuperUser API permission for GUI capabilities Oguz Bektas
@ 2022-04-06 11:57 ` Oguz Bektas
2022-04-06 11:57 ` [pve-devel] [PATCH v3 access-control 04/17] api: roles: only allow modifying roles to add/remove SU if user has SU themselves Oguz Bektas
` (13 subsequent siblings)
16 siblings, 0 replies; 18+ messages in thread
From: Oguz Bektas @ 2022-04-06 11:57 UTC (permalink / raw)
To: pve-devel
Signed-off-by: Oguz Bektas <o.bektas@proxmox.com>
---
v2->v3:
* also check for 'propagate' bit on $param->{path}
src/PVE/API2/ACL.pm | 17 +++++++++++++++++
1 file changed, 17 insertions(+)
diff --git a/src/PVE/API2/ACL.pm b/src/PVE/API2/ACL.pm
index 857c672..7ecf8f0 100644
--- a/src/PVE/API2/ACL.pm
+++ b/src/PVE/API2/ACL.pm
@@ -134,6 +134,10 @@ __PACKAGE__->register_method ({
code => sub {
my ($param) = @_;
+ my $rpcenv = PVE::RPCEnvironment::get();
+ my $authuser = $rpcenv->get_user();
+ my $is_superuser = $rpcenv->check($authuser, $param->{path}, ['SuperUser'], 1);
+
if (!($param->{users} || $param->{groups} || $param->{tokens})) {
raise_param_exc({ map { $_ => "either 'users', 'groups' or 'tokens' is required." } qw(users groups tokens) });
}
@@ -160,6 +164,19 @@ __PACKAGE__->register_method ({
die "role '$role' does not exist\n"
if !$cfg->{roles}->{$role};
+ my $role_privs = $cfg->{roles}->{$role};
+ my $role_contains_superuser = grep { $_ eq 'SuperUser' } keys %$role_privs;
+ if ($role_contains_superuser) {
+ die "only superusers can grant/remove this role!\n"
+ if !$is_superuser;
+
+ # check if user has propagate bit
+ my $user_perms = $rpcenv->permissions($authuser, $param->{path});
+ my $has_propagate = $user_perms->{SuperUser};
+ die "cannot give away superuser on '$param->{path}' without 'propagate' bit!\n"
+ if !$has_propagate;
+ }
+
foreach my $group (split_list($param->{groups})) {
die "group '$group' does not exist\n"
--
2.30.2
^ permalink raw reply [flat|nested] 18+ messages in thread
* [pve-devel] [PATCH v3 access-control 04/17] api: roles: only allow modifying roles to add/remove SU if user has SU themselves
2022-04-06 11:57 [pve-devel] [PATCH v3 access-control++ 00/17] SuperUser privilege Oguz Bektas
` (2 preceding siblings ...)
2022-04-06 11:57 ` [pve-devel] [PATCH v3 access-control 03/17] api: acl: only allow granting SU privilege if user already has it Oguz Bektas
@ 2022-04-06 11:57 ` Oguz Bektas
2022-04-06 11:57 ` [pve-devel] [PATCH v3 access-control 05/17] api: allow superusers to edit tfa and password settings Oguz Bektas
` (12 subsequent siblings)
16 siblings, 0 replies; 18+ messages in thread
From: Oguz Bektas @ 2022-04-06 11:57 UTC (permalink / raw)
To: pve-devel
Signed-off-by: Oguz Bektas <o.bektas@proxmox.com>
---
v2->v3:
* also check if previous role contained SU privilege (when removing privilege)
src/PVE/API2/Role.pm | 21 +++++++++++++++++++++
1 file changed, 21 insertions(+)
diff --git a/src/PVE/API2/Role.pm b/src/PVE/API2/Role.pm
index 70a92b6..4a09ad6 100644
--- a/src/PVE/API2/Role.pm
+++ b/src/PVE/API2/Role.pm
@@ -4,6 +4,7 @@ use strict;
use warnings;
use PVE::Cluster qw (cfs_read_file cfs_write_file);
use PVE::AccessControl;
+use PVE::Tools qw (split_list);
use PVE::JSONSchema qw(get_standard_option register_standard_option);
use PVE::SafeSyslog;
@@ -90,11 +91,19 @@ __PACKAGE__->register_method ({
my $usercfg = cfs_read_file("user.cfg");
+ my $rpcenv = PVE::RPCEnvironment::get();
+ my $authuser = $rpcenv->get_user();
+ my $is_superuser = $rpcenv->check($authuser, "/", ['SuperUser'], 1);
+
my $role = $param->{roleid};
die "role '$role' already exists\n"
if $usercfg->{roles}->{$role};
+ my $contains_superuser = grep { $_ eq 'SuperUser' } split_list($param->{privs});
+ die "only superusers can add this privilege!\n"
+ if $contains_superuser && !$is_superuser;
+
$usercfg->{roles}->{$role} = {};
PVE::AccessControl::add_role_privs($role, $usercfg, $param->{privs});
@@ -136,9 +145,21 @@ __PACKAGE__->register_method ({
my $usercfg = cfs_read_file("user.cfg");
+ my $rpcenv = PVE::RPCEnvironment::get();
+ my $authuser = $rpcenv->get_user();
+ my $is_superuser = $rpcenv->check($authuser, "/", ['SuperUser'], 1);
+
die "role '$role' does not exist\n"
if !$usercfg->{roles}->{$role};
+ my @old_role_privs = keys(%{$usercfg->{roles}->{$role}});
+
+ my $contained_superuser = grep { $_ eq 'SuperUser' } @old_role_privs;
+ my $contains_superuser = grep { $_ eq 'SuperUser' } split_list($param->{privs});
+
+ die "only superusers can add/remove this privilege!\n"
+ if ($contains_superuser || $contained_superuser) && !$is_superuser;
+
$usercfg->{roles}->{$role} = {} if !$param->{append};
PVE::AccessControl::add_role_privs($role, $usercfg, $param->{privs});
--
2.30.2
^ permalink raw reply [flat|nested] 18+ messages in thread
* [pve-devel] [PATCH v3 access-control 05/17] api: allow superusers to edit tfa and password settings
2022-04-06 11:57 [pve-devel] [PATCH v3 access-control++ 00/17] SuperUser privilege Oguz Bektas
` (3 preceding siblings ...)
2022-04-06 11:57 ` [pve-devel] [PATCH v3 access-control 04/17] api: roles: only allow modifying roles to add/remove SU if user has SU themselves Oguz Bektas
@ 2022-04-06 11:57 ` Oguz Bektas
2022-04-06 11:57 ` [pve-devel] [PATCH v3 qemu-server 06/17] api: allow SU privileged users to edit root-only options for VM configs Oguz Bektas
` (11 subsequent siblings)
16 siblings, 0 replies; 18+ messages in thread
From: Oguz Bektas @ 2022-04-06 11:57 UTC (permalink / raw)
To: pve-devel
Signed-off-by: Oguz Bektas <o.bektas@proxmox.com>
---
v2->v3:
* added `has_superuser_anywhere` helper
* only allow changing TFA or password settings of SUs if user has SU on /access endpoint
src/PVE/API2/AccessControl.pm | 23 +++++++++++++++--------
src/PVE/API2/TFA.pm | 11 ++++++++++-
src/PVE/RPCEnvironment.pm | 15 +++++++++++++++
3 files changed, 40 insertions(+), 9 deletions(-)
diff --git a/src/PVE/API2/AccessControl.pm b/src/PVE/API2/AccessControl.pm
index 5d78c6f..4ba9dc5 100644
--- a/src/PVE/API2/AccessControl.pm
+++ b/src/PVE/API2/AccessControl.pm
@@ -378,17 +378,24 @@ __PACKAGE__->register_method ({
$rpcenv->check_user_exist($userid);
+ my $is_superuser = $rpcenv->check($authuser, "/access", ['SuperUser'], 1);
+
if ($authuser eq 'root@pam') {
# OK - root can change anything
+ } elsif ($authuser eq $userid) {
+ $rpcenv->check_user_enabled($userid);
+ # OK - each user can change its own password
} else {
- if ($authuser eq $userid) {
- $rpcenv->check_user_enabled($userid);
- # OK - each user can change its own password
- } else {
- # only root may change root password
- raise_perm_exc() if $userid eq 'root@pam';
- # do not allow to change system user passwords
- raise_perm_exc() if $realm eq 'pam';
+ # only root may change root password
+ raise_perm_exc() if $userid eq 'root@pam';
+ # do not allow to change system user passwords
+ raise_perm_exc() if $realm eq 'pam';
+
+ if ($is_superuser) {
+ # OK - superusers can change any user's password except root@pam
+ } elsif ($rpcenv->has_superuser_anywhere($userid)) {
+ die "only superusers can change another superuser's password!\n"
+ if !$is_superuser;
}
}
diff --git a/src/PVE/API2/TFA.pm b/src/PVE/API2/TFA.pm
index bee4dee..d7c0c4d 100644
--- a/src/PVE/API2/TFA.pm
+++ b/src/PVE/API2/TFA.pm
@@ -96,22 +96,31 @@ my $TFA_UPDATE_INFO_SCHEMA = {
};
# Only root may modify root, regular users need to specify their password.
+# Only users with SU on /access may modify other users with SU anywhere
#
# Returns the userid returned from `verify_username`.
# Or ($userid, $realm) in list context.
my sub root_permission_check : prototype($$$$) {
my ($rpcenv, $authuser, $userid, $password) = @_;
+ # authuser = the user making the change
+ # userid = the user to be changed
+ raise_perm_exc() if $userid eq 'root@pam' && $authuser ne 'root@pam'; # can be only edited by itself
($userid, undef, my $realm) = PVE::AccessControl::verify_username($userid);
$rpcenv->check_user_exist($userid);
- raise_perm_exc() if $userid eq 'root@pam' && $authuser ne 'root@pam';
+ my $is_superuser = $rpcenv->check($authuser, "/access", ['SuperUser'], 1);
# Regular users need to confirm their password to change TFA settings.
if ($authuser ne 'root@pam') {
raise_param_exc({ 'password' => 'password is required to modify TFA data' })
if !defined($password);
+ if (!$is_superuser && $authuser ne $userid) {
+ die "only superusers can change another superuser's TFA settings!\n"
+ if $rpcenv->has_superuser_anywhere($userid);
+ }
+
($authuser, my $auth_username, my $auth_realm) =
PVE::AccessControl::verify_username($authuser);
diff --git a/src/PVE/RPCEnvironment.pm b/src/PVE/RPCEnvironment.pm
index 34c8991..04c5633 100644
--- a/src/PVE/RPCEnvironment.pm
+++ b/src/PVE/RPCEnvironment.pm
@@ -479,6 +479,21 @@ sub check_api2_permissions {
raise_perm_exc();
}
+sub has_superuser_anywhere {
+ my ($self, $username) = @_;
+
+ return 1 if $username eq 'root@pam';
+
+ # get all ACL paths from config and check for SU privilege
+ my $acls = $self->{user_cfg}->{acl};
+ my @acl_paths = keys(%$acls);
+ @acl_paths = ['/'] if !@acl_paths;
+ foreach my $path (@acl_paths) {
+ return 1 if $self->check($username, $path, ['SuperUser'], 1);
+ }
+ return 0;
+}
+
sub log_cluster_msg {
my ($self, $pri, $user, $msg) = @_;
--
2.30.2
^ permalink raw reply [flat|nested] 18+ messages in thread
* [pve-devel] [PATCH v3 qemu-server 06/17] api: allow SU privileged users to edit root-only options for VM configs
2022-04-06 11:57 [pve-devel] [PATCH v3 access-control++ 00/17] SuperUser privilege Oguz Bektas
` (4 preceding siblings ...)
2022-04-06 11:57 ` [pve-devel] [PATCH v3 access-control 05/17] api: allow superusers to edit tfa and password settings Oguz Bektas
@ 2022-04-06 11:57 ` Oguz Bektas
2022-04-06 11:57 ` [pve-devel] [PATCH v3 qemu-server 07/17] migration tests: mock $rpcenv->check subroutine Oguz Bektas
` (10 subsequent siblings)
16 siblings, 0 replies; 18+ messages in thread
From: Oguz Bektas @ 2022-04-06 11:57 UTC (permalink / raw)
To: pve-devel
we now allow users with SU privilege to edit real device configurations
for VMs.
they still need the required privilege to edit the corresponding
configuration options (such as `VM.Config.HWType`), as well as the SU
privilege.
Signed-off-by: Oguz Bektas <o.bektas@proxmox.com>
---
v2->v3:
* changed condition styles
* fix inverted condition for real device configs to also check current value
PVE/API2/Qemu.pm | 62 ++++++++++++++++++++++++------------------------
1 file changed, 31 insertions(+), 31 deletions(-)
diff --git a/PVE/API2/Qemu.pm b/PVE/API2/Qemu.pm
index 1dd0cf2..7fc9a77 100644
--- a/PVE/API2/Qemu.pm
+++ b/PVE/API2/Qemu.pm
@@ -365,16 +365,17 @@ my $cloudinitoptions = {
my $check_vm_create_serial_perm = sub {
my ($rpcenv, $authuser, $vmid, $pool, $param) = @_;
+ # no need to check permissions for root@pam
return 1 if $authuser eq 'root@pam';
+ my $is_superuser = $rpcenv->check($authuser, "/vms/$vmid", ['SuperUser'], 1);
+
foreach my $opt (keys %{$param}) {
next if $opt !~ m/^serial\d+$/;
- if ($param->{$opt} eq 'socket') {
- $rpcenv->check_vm_perm($authuser, $vmid, $pool, ['VM.Config.HWType']);
- } else {
- die "only root can set '$opt' config for real devices\n";
- }
+ $rpcenv->check_vm_perm($authuser, $vmid, $pool, ['VM.Config.HWType']);
+ die "only superusers can set '$opt' config for real devices\n"
+ if $param->{$opt} ne 'socket' && !$is_superuser;
}
return 1;
@@ -383,16 +384,17 @@ my $check_vm_create_serial_perm = sub {
my $check_vm_create_usb_perm = sub {
my ($rpcenv, $authuser, $vmid, $pool, $param) = @_;
+ # no need to check permissions for root@pam
return 1 if $authuser eq 'root@pam';
+ my $is_superuser = $rpcenv->check($authuser, "/vms/$vmid", ['SuperUser'], 1);
+
foreach my $opt (keys %{$param}) {
next if $opt !~ m/^usb\d+$/;
- if ($param->{$opt} =~ m/spice/) {
- $rpcenv->check_vm_perm($authuser, $vmid, $pool, ['VM.Config.HWType']);
- } else {
- die "only root can set '$opt' config for real devices\n";
- }
+ $rpcenv->check_vm_perm($authuser, $vmid, $pool, ['VM.Config.HWType']);
+ die "only superusers can set '$opt' config for real devices\n"
+ if $param->{$opt} !~ m/spice/ && !$is_superuser;
}
return 1;
@@ -401,8 +403,11 @@ my $check_vm_create_usb_perm = sub {
my $check_vm_modify_config_perm = sub {
my ($rpcenv, $authuser, $vmid, $pool, $key_list) = @_;
+ # no need to check permissions for root@pam
return 1 if $authuser eq 'root@pam';
+ my $is_superuser = $rpcenv->check($authuser, "/vms/$vmid", ['SuperUser'], 1);
+
foreach my $opt (@$key_list) {
# some checks (e.g., disk, serial port, usb) need to be done somewhere
# else, as there the permission can be value dependend
@@ -438,7 +443,8 @@ my $check_vm_modify_config_perm = sub {
} else {
# catches hostpci\d+, args, lock, etc.
# new options will be checked here
- die "only root can set '$opt' config\n";
+ die "only superusers can set '$opt' config\n"
+ if !$is_superuser;
}
}
@@ -1140,6 +1146,8 @@ my $update_vm_api = sub {
push @paramarr, "-$key", $value;
}
+ my $is_superuser = $rpcenv->check($authuser, "/vms/$vmid", ['SuperUser'], 1);
+
my $skiplock = extract_param($param, 'skiplock');
raise_param_exc({ skiplock => "Only root may use this option." })
if $skiplock && $authuser ne 'root@pam';
@@ -1356,19 +1364,15 @@ my $update_vm_api = sub {
PVE::QemuConfig->add_to_pending_delete($conf, $opt, $force);
PVE::QemuConfig->write_config($vmid, $conf);
} elsif ($opt =~ m/^serial\d+$/) {
- if ($val eq 'socket') {
- $rpcenv->check_vm_perm($authuser, $vmid, undef, ['VM.Config.HWType']);
- } elsif ($authuser ne 'root@pam') {
- die "only root can delete '$opt' config for real devices\n";
- }
+ $rpcenv->check_vm_perm($authuser, $vmid, undef, ['VM.Config.HWType']);
+ die "only superusers can delete '$opt' config for real devices\n"
+ if $val ne 'socket' && !$is_superuser;
PVE::QemuConfig->add_to_pending_delete($conf, $opt, $force);
PVE::QemuConfig->write_config($vmid, $conf);
} elsif ($opt =~ m/^usb\d+$/) {
- if ($val =~ m/spice/) {
- $rpcenv->check_vm_perm($authuser, $vmid, undef, ['VM.Config.HWType']);
- } elsif ($authuser ne 'root@pam') {
- die "only root can delete '$opt' config for real devices\n";
- }
+ $rpcenv->check_vm_perm($authuser, $vmid, undef, ['VM.Config.HWType']);
+ die "only superusers can delete '$opt' config for real devices\n"
+ if $val !~ m/spice/ && !$is_superuser;
PVE::QemuConfig->add_to_pending_delete($conf, $opt, $force);
PVE::QemuConfig->write_config($vmid, $conf);
} else {
@@ -1418,18 +1422,14 @@ my $update_vm_api = sub {
}
}
} elsif ($opt =~ m/^serial\d+/) {
- if ((!defined($conf->{$opt}) || $conf->{$opt} eq 'socket') && $param->{$opt} eq 'socket') {
- $rpcenv->check_vm_perm($authuser, $vmid, undef, ['VM.Config.HWType']);
- } elsif ($authuser ne 'root@pam') {
- die "only root can modify '$opt' config for real devices\n";
- }
+ $rpcenv->check_vm_perm($authuser, $vmid, undef, ['VM.Config.HWType']); # always check hw config
+ die "only superusers can modify '$opt' config for real devices\n"
+ if !$is_superuser && ((defined($conf->{$opt}) && $conf->{$opt} ne 'socket') || $param->{$opt} ne 'socket');
$conf->{pending}->{$opt} = $param->{$opt};
} elsif ($opt =~ m/^usb\d+/) {
- if ((!defined($conf->{$opt}) || $conf->{$opt} =~ m/spice/) && $param->{$opt} =~ m/spice/) {
- $rpcenv->check_vm_perm($authuser, $vmid, undef, ['VM.Config.HWType']);
- } elsif ($authuser ne 'root@pam') {
- die "only root can modify '$opt' config for real devices\n";
- }
+ $rpcenv->check_vm_perm($authuser, $vmid, undef, ['VM.Config.HWType']);
+ die "only superusers can modify '$opt' config for real devices\n"
+ if !$is_superuser && ((defined($conf->{$opt}) && $conf->{$opt} !~ m/spice/) || $param->{$opt} !~ m/spice/);
$conf->{pending}->{$opt} = $param->{$opt};
} else {
$conf->{pending}->{$opt} = $param->{$opt};
--
2.30.2
^ permalink raw reply [flat|nested] 18+ messages in thread
* [pve-devel] [PATCH v3 qemu-server 07/17] migration tests: mock $rpcenv->check subroutine
2022-04-06 11:57 [pve-devel] [PATCH v3 access-control++ 00/17] SuperUser privilege Oguz Bektas
` (5 preceding siblings ...)
2022-04-06 11:57 ` [pve-devel] [PATCH v3 qemu-server 06/17] api: allow SU privileged users to edit root-only options for VM configs Oguz Bektas
@ 2022-04-06 11:57 ` Oguz Bektas
2022-04-06 11:57 ` [pve-devel] [PATCH v3 qemu-server 08/17] api: allow superusers to use 'skiplock' option Oguz Bektas
` (9 subsequent siblings)
16 siblings, 0 replies; 18+ messages in thread
From: Oguz Bektas @ 2022-04-06 11:57 UTC (permalink / raw)
To: pve-devel
missing mock routine is causes the tests to fail at build time
when $rpcenv->check is called.
previous assumption was returning 'root@pam' to get_user() so we can
do the same here.
Signed-off-by: Oguz Bektas <o.bektas@proxmox.com>
---
v2->v3:
* added new, since we need to run $rpcenv->check inside the unit tests for migration
test/MigrationTest/QmMock.pm | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/test/MigrationTest/QmMock.pm b/test/MigrationTest/QmMock.pm
index 2d5d5c6..eb6ea53 100644
--- a/test/MigrationTest/QmMock.pm
+++ b/test/MigrationTest/QmMock.pm
@@ -40,6 +40,11 @@ sub fork_worker {
return '123456';
}
+sub check {
+ my ($self, $authuser, $path, $priv, $noerr) = @_;
+ return 1 if $authuser eq 'root@pam';
+}
+
# mocked modules
my $inotify_module = Test::MockModule->new("PVE::INotify");
--
2.30.2
^ permalink raw reply [flat|nested] 18+ messages in thread
* [pve-devel] [PATCH v3 qemu-server 08/17] api: allow superusers to use 'skiplock' option
2022-04-06 11:57 [pve-devel] [PATCH v3 access-control++ 00/17] SuperUser privilege Oguz Bektas
` (6 preceding siblings ...)
2022-04-06 11:57 ` [pve-devel] [PATCH v3 qemu-server 07/17] migration tests: mock $rpcenv->check subroutine Oguz Bektas
@ 2022-04-06 11:57 ` Oguz Bektas
2022-04-06 11:57 ` [pve-devel] [PATCH v3 manager 09/17] api: backup: allow SUs to use 'tmpdir', 'dumpdir' and 'script' options Oguz Bektas
` (8 subsequent siblings)
16 siblings, 0 replies; 18+ messages in thread
From: Oguz Bektas @ 2022-04-06 11:57 UTC (permalink / raw)
To: pve-devel
also mark the intentionally root-only migration related options
in param descriptions and leave a reminder comment.
Signed-off-by: Oguz Bektas <o.bektas@proxmox.com>
---
v2->v3:
* mark migration-internal parameters inside param description
* added comment above get_root_param
* drop root@pam shortcuts and check SU privilege as normal
PVE/API2/Qemu.pm | 71 ++++++++++++++++++++++++++++++++----------------
1 file changed, 48 insertions(+), 23 deletions(-)
diff --git a/PVE/API2/Qemu.pm b/PVE/API2/Qemu.pm
index 7fc9a77..3eca222 100644
--- a/PVE/API2/Qemu.pm
+++ b/PVE/API2/Qemu.pm
@@ -1149,8 +1149,8 @@ my $update_vm_api = sub {
my $is_superuser = $rpcenv->check($authuser, "/vms/$vmid", ['SuperUser'], 1);
my $skiplock = extract_param($param, 'skiplock');
- raise_param_exc({ skiplock => "Only root may use this option." })
- if $skiplock && $authuser ne 'root@pam';
+ raise_param_exc({ skiplock => "Only superusers may use this option." })
+ if $skiplock && !$is_superuser;
my $delete_str = extract_param($param, 'delete');
@@ -1672,9 +1672,11 @@ __PACKAGE__->register_method({
my $authuser = $rpcenv->get_user();
my $vmid = $param->{vmid};
+ my $is_superuser = $rpcenv->check($authuser, "/vms/$vmid", ['SuperUser'], 1);
+
my $skiplock = $param->{skiplock};
- raise_param_exc({ skiplock => "Only root may use this option." })
- if $skiplock && $authuser ne 'root@pam';
+ raise_param_exc({ skiplock => "Only superusers may use this option." })
+ if $skiplock && !$is_superuser;
my $early_checks = sub {
# test if VM exists
@@ -2277,25 +2279,27 @@ __PACKAGE__->register_method({
migration_type => {
type => 'string',
enum => ['secure', 'insecure'],
- description => "Migration traffic is encrypted using an SSH " .
+ description => "Migration-internal parameter. Migration traffic is encrypted using an SSH " .
"tunnel by default. On secure, completely private networks " .
"this can be disabled to increase performance.",
optional => 1,
},
migration_network => {
type => 'string', format => 'CIDR',
- description => "CIDR of the (sub) network that is used for migration.",
+ description => "Migration-internal parameter. CIDR of the (sub)network " .
+ "that is used for migration.",
optional => 1,
},
machine => get_standard_option('pve-qemu-machine'),
'force-cpu' => {
- description => "Override QEMU's -cpu argument with the given string.",
+ description => "Migration-internal parameter. Override QEMU's" .
+ "-cpu argument with the given string.",
type => 'string',
optional => 1,
},
targetstorage => get_standard_option('pve-targetstorage'),
timeout => {
- description => "Wait maximal timeout seconds.",
+ description => "Migration-internal parameter. Wait maximal timeout seconds.",
type => 'integer',
minimum => 0,
default => 'max(30, vm memory in GiB)',
@@ -2317,6 +2321,14 @@ __PACKAGE__->register_method({
my $timeout = extract_param($param, 'timeout');
my $machine = extract_param($param, 'machine');
+ my $is_superuser = $rpcenv->check($authuser, "/vms/$vmid", ['SuperUser'], 1);
+
+ my $skiplock = extract_param($param, 'skiplock');
+ raise_param_exc({ skiplock => "Only superusers may use this option." })
+ if $skiplock && !$is_superuser;
+
+ # since they are only used for migration-internal flows,
+ # these parameters are still intentionally limited to root@pam
my $get_root_param = sub {
my $value = extract_param($param, $_[0]);
raise_param_exc({ "$_[0]" => "Only root may use this option." })
@@ -2325,7 +2337,6 @@ __PACKAGE__->register_method({
};
my $stateuri = $get_root_param->('stateuri');
- my $skiplock = $get_root_param->('skiplock');
my $migratedfrom = $get_root_param->('migratedfrom');
my $migration_type = $get_root_param->('migration_type');
my $migration_network = $get_root_param->('migration_network');
@@ -2463,9 +2474,11 @@ __PACKAGE__->register_method({
my $node = extract_param($param, 'node');
my $vmid = extract_param($param, 'vmid');
+ my $is_superuser = $rpcenv->check($authuser, "/vms/$vmid", ['SuperUser'], 1);
+
my $skiplock = extract_param($param, 'skiplock');
- raise_param_exc({ skiplock => "Only root may use this option." })
- if $skiplock && $authuser ne 'root@pam';
+ raise_param_exc({ skiplock => "Only superusers may use this option." })
+ if $skiplock && !$is_superuser;
my $keepActive = extract_param($param, 'keepActive');
raise_param_exc({ keepActive => "Only root may use this option." })
@@ -2540,9 +2553,11 @@ __PACKAGE__->register_method({
my $vmid = extract_param($param, 'vmid');
+ my $is_superuser = $rpcenv->check($authuser, "/vms/$vmid", ['SuperUser'], 1);
+
my $skiplock = extract_param($param, 'skiplock');
- raise_param_exc({ skiplock => "Only root may use this option." })
- if $skiplock && $authuser ne 'root@pam';
+ raise_param_exc({ skiplock => "Only superusers may use this option." })
+ if $skiplock && !$is_superuser;
die "VM $vmid not running\n" if !PVE::QemuServer::check_running($vmid);
@@ -2607,9 +2622,11 @@ __PACKAGE__->register_method({
my $node = extract_param($param, 'node');
my $vmid = extract_param($param, 'vmid');
+ my $is_superuser = $rpcenv->check($authuser, "/vms/$vmid", ['SuperUser'], 1);
+
my $skiplock = extract_param($param, 'skiplock');
- raise_param_exc({ skiplock => "Only root may use this option." })
- if $skiplock && $authuser ne 'root@pam';
+ raise_param_exc({ skiplock => "Only superusers may use this option." })
+ if $skiplock && !$is_superuser;
my $keepActive = extract_param($param, 'keepActive');
raise_param_exc({ keepActive => "Only root may use this option." })
@@ -2766,9 +2783,11 @@ __PACKAGE__->register_method({
my $statestorage = extract_param($param, 'statestorage');
+ my $is_superuser = $rpcenv->check($authuser, "/vms/$vmid", ['SuperUser'], 1);
+
my $skiplock = extract_param($param, 'skiplock');
- raise_param_exc({ skiplock => "Only root may use this option." })
- if $skiplock && $authuser ne 'root@pam';
+ raise_param_exc({ skiplock => "Only superusers may use this option." })
+ if $skiplock && !$is_superuser;
die "VM $vmid not running\n" if !PVE::QemuServer::check_running($vmid);
@@ -2838,9 +2857,11 @@ __PACKAGE__->register_method({
my $vmid = extract_param($param, 'vmid');
+ my $is_superuser = $rpcenv->check($authuser, "/vms/$vmid", ['SuperUser'], 1);
+
my $skiplock = extract_param($param, 'skiplock');
- raise_param_exc({ skiplock => "Only root may use this option." })
- if $skiplock && $authuser ne 'root@pam';
+ raise_param_exc({ skiplock => "Only superusers may use this option." })
+ if $skiplock && !$is_superuser;
my $nocheck = extract_param($param, 'nocheck');
raise_param_exc({ nocheck => "Only root may use this option." })
@@ -2910,9 +2931,11 @@ __PACKAGE__->register_method({
my $vmid = extract_param($param, 'vmid');
+ my $is_superuser = $rpcenv->check($authuser, "/vms/$vmid", ['SuperUser'], 1);
+
my $skiplock = extract_param($param, 'skiplock');
- raise_param_exc({ skiplock => "Only root may use this option." })
- if $skiplock && $authuser ne 'root@pam';
+ raise_param_exc({ skiplock => "Only superusers may use this option." })
+ if $skiplock && !$is_superuser;
PVE::QemuServer::vm_sendkey($vmid, $skiplock, $param->{key});
@@ -4163,9 +4186,11 @@ __PACKAGE__->register_method({
my $sizestr = extract_param($param, 'size');
+ my $is_superuser = $rpcenv->check($authuser, "/vms/$vmid", ['SuperUser'], 1);
+
my $skiplock = extract_param($param, 'skiplock');
- raise_param_exc({ skiplock => "Only root may use this option." })
- if $skiplock && $authuser ne 'root@pam';
+ raise_param_exc({ skiplock => "Only superusers may use this option." })
+ if $skiplock && !$is_superuser;
my $storecfg = PVE::Storage::config();
--
2.30.2
^ permalink raw reply [flat|nested] 18+ messages in thread
* [pve-devel] [PATCH v3 manager 09/17] api: backup: allow SUs to use 'tmpdir', 'dumpdir' and 'script' options
2022-04-06 11:57 [pve-devel] [PATCH v3 access-control++ 00/17] SuperUser privilege Oguz Bektas
` (7 preceding siblings ...)
2022-04-06 11:57 ` [pve-devel] [PATCH v3 qemu-server 08/17] api: allow superusers to use 'skiplock' option Oguz Bektas
@ 2022-04-06 11:57 ` Oguz Bektas
2022-04-06 11:57 ` [pve-devel] [PATCH v3 manager 10/17] api: vzdump: allow SUs to use 'bwlimit' and 'ionice' parameters Oguz Bektas
` (7 subsequent siblings)
16 siblings, 0 replies; 18+ messages in thread
From: Oguz Bektas @ 2022-04-06 11:57 UTC (permalink / raw)
To: pve-devel
previously limited to root@pam; we can allow SUs to use these options if
they have the privilege on the whole API path.
Signed-off-by: Oguz Bektas <o.bektas@proxmox.com>
---
v2->v3:
* change comment for SU check
PVE/API2/Backup.pm | 11 +++++++----
1 file changed, 7 insertions(+), 4 deletions(-)
diff --git a/PVE/API2/Backup.pm b/PVE/API2/Backup.pm
index 5d36789a..286996b5 100644
--- a/PVE/API2/Backup.pm
+++ b/PVE/API2/Backup.pm
@@ -41,10 +41,13 @@ my $vzdump_job_id_prop = {
my $assert_param_permission = sub {
my ($param, $user) = @_;
- return if $user eq 'root@pam'; # always OK
+ return if $user eq 'root@pam'; # root@pam always OK
+
+ my $rpcenv = PVE::RPCEnvironment::get();
+ return if $rpcenv->check($user, "/", ['SuperUser'], 1); # SuperUser on /, always OK
for my $key (qw(tmpdir dumpdir script)) {
- raise_param_exc({ $key => "Only root may set this option."}) if exists $param->{$key};
+ raise_param_exc({ $key => "Only superusers may set this option."}) if exists $param->{$key};
}
};
@@ -143,7 +146,7 @@ __PACKAGE__->register_method({
description => "Create new vzdump backup job.",
permissions => {
check => ['perm', '/', ['Sys.Modify']],
- description => "The 'tmpdir', 'dumpdir' and 'script' parameters are additionally restricted to the 'root\@pam' user.",
+ description => "The 'tmpdir', 'dumpdir' and 'script' parameters are additionally restricted to superusers.",
},
parameters => {
additionalProperties => 0,
@@ -345,7 +348,7 @@ __PACKAGE__->register_method({
description => "Update vzdump backup job definition.",
permissions => {
check => ['perm', '/', ['Sys.Modify']],
- description => "The 'tmpdir', 'dumpdir' and 'script' parameters are additionally restricted to the 'root\@pam' user.",
+ description => "The 'tmpdir', 'dumpdir' and 'script' parameters are additionally restricted to superusers.",
},
parameters => {
additionalProperties => 0,
--
2.30.2
^ permalink raw reply [flat|nested] 18+ messages in thread
* [pve-devel] [PATCH v3 manager 10/17] api: vzdump: allow SUs to use 'bwlimit' and 'ionice' parameters
2022-04-06 11:57 [pve-devel] [PATCH v3 access-control++ 00/17] SuperUser privilege Oguz Bektas
` (8 preceding siblings ...)
2022-04-06 11:57 ` [pve-devel] [PATCH v3 manager 09/17] api: backup: allow SUs to use 'tmpdir', 'dumpdir' and 'script' options Oguz Bektas
@ 2022-04-06 11:57 ` Oguz Bektas
2022-04-06 11:57 ` [pve-devel] [PATCH v3 manager 11/17] api: always show login prompt for non-root users on terminal proxy calls Oguz Bektas
` (6 subsequent siblings)
16 siblings, 0 replies; 18+ messages in thread
From: Oguz Bektas @ 2022-04-06 11:57 UTC (permalink / raw)
To: pve-devel
Signed-off-by: Oguz Bektas <o.bektas@proxmox.com>
---
v2->v3:
* no changes
PVE/API2/VZDump.pm | 8 +++++---
1 file changed, 5 insertions(+), 3 deletions(-)
diff --git a/PVE/API2/VZDump.pm b/PVE/API2/VZDump.pm
index 13b6cd46..99366212 100644
--- a/PVE/API2/VZDump.pm
+++ b/PVE/API2/VZDump.pm
@@ -27,7 +27,7 @@ __PACKAGE__->register_method ({
permissions => {
description => "The user needs 'VM.Backup' permissions on any VM, and 'Datastore.AllocateSpace'"
." on the backup storage. The 'maxfiles', 'prune-backups', 'tmpdir', 'dumpdir', 'script',"
- ." 'bwlimit' and 'ionice' parameters are restricted to the 'root\@pam' user.",
+ ." 'bwlimit' and 'ionice' parameters are restricted to the superusers.",
user => 'all',
},
protected => 1,
@@ -52,6 +52,8 @@ __PACKAGE__->register_method ({
my $nodename = PVE::INotify::nodename();
+ my $is_superuser = $user eq 'root@pam' || $rpcenv->check($user, "/", ['SuperUser'], 1);
+
if ($rpcenv->{type} ne 'cli') {
raise_param_exc({ node => "option is only allowed on the command line interface."})
if $param->{node} && $param->{node} ne $nodename;
@@ -61,8 +63,8 @@ __PACKAGE__->register_method ({
}
foreach my $key (qw(maxfiles prune-backups tmpdir dumpdir script bwlimit ionice)) {
- raise_param_exc({ $key => "Only root may set this option."})
- if defined($param->{$key}) && ($user ne 'root@pam');
+ raise_param_exc({ $key => "Only superusers may set this option."})
+ if defined($param->{$key}) && !$is_superuser;
}
PVE::VZDump::verify_vzdump_parameters($param, 1);
--
2.30.2
^ permalink raw reply [flat|nested] 18+ messages in thread
* [pve-devel] [PATCH v3 manager 11/17] api: always show login prompt for non-root users on terminal proxy calls
2022-04-06 11:57 [pve-devel] [PATCH v3 access-control++ 00/17] SuperUser privilege Oguz Bektas
` (9 preceding siblings ...)
2022-04-06 11:57 ` [pve-devel] [PATCH v3 manager 10/17] api: vzdump: allow SUs to use 'bwlimit' and 'ionice' parameters Oguz Bektas
@ 2022-04-06 11:57 ` Oguz Bektas
2022-04-06 11:57 ` [pve-devel] [PATCH v3 manager 12/17] ui: include "SuperUser" in privilege selector Oguz Bektas
` (5 subsequent siblings)
16 siblings, 0 replies; 18+ messages in thread
From: Oguz Bektas @ 2022-04-06 11:57 UTC (permalink / raw)
To: pve-devel
we have a SU privilege now, but we still drop to a login prompt on our
spice/vnc/termproxy for such users.
Signed-off-by: Oguz Bektas <o.bektas@proxmox.com>
---
v2->v3:
* limit non-root users to 'login' shellcmd on vncproxy, termproxy and spiceproxy
PVE/API2/Nodes.pm | 11 ++++++++---
1 file changed, 8 insertions(+), 3 deletions(-)
diff --git a/PVE/API2/Nodes.pm b/PVE/API2/Nodes.pm
index 655493a3..52dae854 100644
--- a/PVE/API2/Nodes.pm
+++ b/PVE/API2/Nodes.pm
@@ -870,7 +870,7 @@ sub get_shell_command {
$cmd = [ '/bin/login', '-f', 'root' ];
}
} else {
- # non-root must always login for now, we do not have a superuser role!
+ # non-root must always login, even with SU privilege
$cmd = [ '/bin/login' ];
}
return $cmd;
@@ -960,7 +960,7 @@ __PACKAGE__->register_method ({
raise_perm_exc("realm != pam") if $realm ne 'pam';
- if (defined($param->{cmd}) && $param->{cmd} eq 'upgrade' && $user ne 'root@pam') {
+ if (defined($param->{cmd}) && $param->{cmd} eq 'login' && $user ne 'root@pam') {
raise_perm_exc('user != root@pam');
}
@@ -1077,8 +1077,13 @@ __PACKAGE__->register_method ({
my $rpcenv = PVE::RPCEnvironment::get();
my ($user, undef, $realm) = PVE::AccessControl::verify_username($rpcenv->get_user());
+
raise_perm_exc("realm $realm != pam") if $realm ne 'pam';
+ if (defined($param->{cmd}) && $param->{cmd} eq 'login' && $user ne 'root@pam') {
+ raise_perm_exc('user != root@pam');
+ }
+
my $node = $param->{node};
my $authpath = "/nodes/$node";
my $ticket = PVE::AccessControl::assemble_vnc_ticket($user, $authpath);
@@ -1208,7 +1213,7 @@ __PACKAGE__->register_method ({
raise_perm_exc("realm != pam") if $realm ne 'pam';
- if (defined($param->{cmd}) && $param->{cmd} eq 'upgrade' && $user ne 'root@pam') {
+ if (defined($param->{cmd}) && $param->{cmd} eq 'login' && $user ne 'root@pam') {
raise_perm_exc('user != root@pam');
}
--
2.30.2
^ permalink raw reply [flat|nested] 18+ messages in thread
* [pve-devel] [PATCH v3 manager 12/17] ui: include "SuperUser" in privilege selector
2022-04-06 11:57 [pve-devel] [PATCH v3 access-control++ 00/17] SuperUser privilege Oguz Bektas
` (10 preceding siblings ...)
2022-04-06 11:57 ` [pve-devel] [PATCH v3 manager 11/17] api: always show login prompt for non-root users on terminal proxy calls Oguz Bektas
@ 2022-04-06 11:57 ` Oguz Bektas
2022-04-06 11:57 ` [pve-devel] [PATCH v3 manager 13/17] ui: lxc features: check for SU instead of 'root@pam' Oguz Bektas
` (4 subsequent siblings)
16 siblings, 0 replies; 18+ messages in thread
From: Oguz Bektas @ 2022-04-06 11:57 UTC (permalink / raw)
To: pve-devel
Signed-off-by: Oguz Bektas <o.bektas@proxmox.com>
---
v2->v3:
* added, we get all the privileges for SuperAdministrator so that the priv selector includes SuperUser
www/manager6/form/PrivilegesSelector.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/www/manager6/form/PrivilegesSelector.js b/www/manager6/form/PrivilegesSelector.js
index a2427c36..5494e7c2 100644
--- a/www/manager6/form/PrivilegesSelector.js
+++ b/www/manager6/form/PrivilegesSelector.js
@@ -10,7 +10,7 @@ Ext.define('PVE.form.PrivilegesSelector', {
me.callParent();
Proxmox.Utils.API2Request({
- url: '/access/roles/Administrator',
+ url: '/access/roles/SuperAdministrator',
method: 'GET',
success: function(response, options) {
let data = Object.keys(response.result.data).map(key => [key, key]);
--
2.30.2
^ permalink raw reply [flat|nested] 18+ messages in thread
* [pve-devel] [PATCH v3 manager 13/17] ui: lxc features: check for SU instead of 'root@pam'
2022-04-06 11:57 [pve-devel] [PATCH v3 access-control++ 00/17] SuperUser privilege Oguz Bektas
` (11 preceding siblings ...)
2022-04-06 11:57 ` [pve-devel] [PATCH v3 manager 12/17] ui: include "SuperUser" in privilege selector Oguz Bektas
@ 2022-04-06 11:57 ` Oguz Bektas
2022-04-06 11:57 ` [pve-devel] [PATCH v3 manager 14/17] ui: adapt sensible 'root@pam' checks to SU Oguz Bektas
` (3 subsequent siblings)
16 siblings, 0 replies; 18+ messages in thread
From: Oguz Bektas @ 2022-04-06 11:57 UTC (permalink / raw)
To: pve-devel
Signed-off-by: Oguz Bektas <o.bektas@proxmox.com>
---
v2->v3:
* added new, previously the editor was still double-clickable eventhough the 'edit'
button was grayed out
* check for SU instead of root@pam
www/manager6/lxc/Options.js | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/www/manager6/lxc/Options.js b/www/manager6/lxc/Options.js
index d0a53fc7..7aa18447 100644
--- a/www/manager6/lxc/Options.js
+++ b/www/manager6/lxc/Options.js
@@ -175,9 +175,13 @@ Ext.define('PVE.lxc.Options', {
if (key === 'features') {
let unprivileged = me.getStore().getById('unprivileged').data.value;
- let root = Proxmox.UserName === 'root@pam';
+ let superuser = caps.vms['SuperUser']; // eslint-disable-line
let vmalloc = caps.vms['VM.Allocate'];
- edit_btn.setDisabled(!(root || (vmalloc && unprivileged)));
+ let disabled = !(superuser || (vmalloc && unprivileged));
+ edit_btn.setDisabled(disabled);
+ if (disabled) {
+ rowdef.editor = undefined;
+ }
} else {
edit_btn.setDisabled(!rowdef.editor);
}
--
2.30.2
^ permalink raw reply [flat|nested] 18+ messages in thread
* [pve-devel] [PATCH v3 manager 14/17] ui: adapt sensible 'root@pam' checks to SU
2022-04-06 11:57 [pve-devel] [PATCH v3 access-control++ 00/17] SuperUser privilege Oguz Bektas
` (12 preceding siblings ...)
2022-04-06 11:57 ` [pve-devel] [PATCH v3 manager 13/17] ui: lxc features: check for SU instead of 'root@pam' Oguz Bektas
@ 2022-04-06 11:57 ` Oguz Bektas
2022-04-06 11:57 ` [pve-devel] [PATCH v3 container 15/17] fix #2582: api: add checks for 'SuperUser' privilege for root-only options Oguz Bektas
` (2 subsequent siblings)
16 siblings, 0 replies; 18+ messages in thread
From: Oguz Bektas @ 2022-04-06 11:57 UTC (permalink / raw)
To: pve-devel
Signed-off-by: Oguz Bektas <o.bektas@proxmox.com>
---
www/manager6/lxc/Resources.js | 2 +-
www/manager6/window/Migrate.js | 4 ++--
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/www/manager6/lxc/Resources.js b/www/manager6/lxc/Resources.js
index 683be526..85d3889e 100644
--- a/www/manager6/lxc/Resources.js
+++ b/www/manager6/lxc/Resources.js
@@ -268,7 +268,7 @@ Ext.define('PVE.lxc.RessourceView', {
var isUsedDisk = isDisk && !isUnusedDisk;
var noedit = rec.data.delete || !rowdef.editor;
- if (!noedit && Proxmox.UserName !== 'root@pam' && key.match(/^mp\d+$/)) {
+ if (!noedit && !caps.vms['SuperUser'] && key.match(/^mp\d+$/)) { // eslint-disable-line
var mp = PVE.Parser.parseLxcMountPoint(value);
if (mp.type !== 'volume') {
noedit = true;
diff --git a/www/manager6/window/Migrate.js b/www/manager6/window/Migrate.js
index 1c23abb3..597e3b0b 100644
--- a/www/manager6/window/Migrate.js
+++ b/www/manager6/window/Migrate.js
@@ -52,8 +52,8 @@ Ext.define('PVE.window.Migrate', {
}
},
setLocalResourceCheckboxHidden: function(get) {
- if (get('running') || !get('migration.hasLocalResources') ||
- Proxmox.UserName !== 'root@pam') {
+ let caps = Ext.state.Manager.get('GuiCap');
+ if (get('running') || !get('migration.hasLocalResources') || !caps.vms['SuperUser']) { // eslint-disable-line
return true;
} else {
return false;
--
2.30.2
^ permalink raw reply [flat|nested] 18+ messages in thread
* [pve-devel] [PATCH v3 container 15/17] fix #2582: api: add checks for 'SuperUser' privilege for root-only options
2022-04-06 11:57 [pve-devel] [PATCH v3 access-control++ 00/17] SuperUser privilege Oguz Bektas
` (13 preceding siblings ...)
2022-04-06 11:57 ` [pve-devel] [PATCH v3 manager 14/17] ui: adapt sensible 'root@pam' checks to SU Oguz Bektas
@ 2022-04-06 11:57 ` Oguz Bektas
2022-04-06 11:57 ` [pve-devel] [PATCH v3 storage 16/17] check_volume_access: allow superusers to pass arbitrary fs paths Oguz Bektas
2022-04-06 11:57 ` [pve-devel] [PATCH v3 docs 17/17] pveum: add SU privilege and SA role Oguz Bektas
16 siblings, 0 replies; 18+ messages in thread
From: Oguz Bektas @ 2022-04-06 11:57 UTC (permalink / raw)
To: pve-devel
this way we can allow regular users to act as superuser on specific
paths by giving them the (new) builtin 'SuperAdministrator' role or a
custom role with the 'SuperUser' privilege
Signed-off-by: Oguz Bektas <o.bektas@proxmox.com>
---
v2->v3:
* update comments
* drop shortcuts for 'root@pam', just check for SU privilege
src/PVE/API2/LXC.pm | 19 +++++++++----------
src/PVE/API2/LXC/Config.pm | 2 +-
src/PVE/API2/LXC/Status.pm | 12 ++++++++----
src/PVE/LXC.pm | 21 ++++++++++++---------
src/PVE/LXC/Create.pm | 2 +-
5 files changed, 31 insertions(+), 25 deletions(-)
diff --git a/src/PVE/API2/LXC.pm b/src/PVE/API2/LXC.pm
index ea4827f..4517b99 100644
--- a/src/PVE/API2/LXC.pm
+++ b/src/PVE/API2/LXC.pm
@@ -258,7 +258,7 @@ __PACKAGE__->register_method({
$skip_fw_config_restore = 1;
# error out if a user tries to change from unprivileged to privileged
- # explicit change is checked here, implicit is checked down below or happening in root-only paths
+ # explicit change is checked here, implicit is checked down below or happening in superuser-only paths
my $conf = PVE::LXC::Config->load_config($vmid);
if ($conf->{unprivileged} && defined($unprivileged) && !$unprivileged) {
raise_perm_exc("cannot change from unprivileged to privileged without VM.Allocate");
@@ -312,7 +312,7 @@ __PACKAGE__->register_method({
my $conf = {};
- my $is_root = $authuser eq 'root@pam';
+ my $is_superuser = $rpcenv->check($authuser, "/vms/$vmid", ['SuperUser'], 1);
my $no_disk_param = {};
my $mp_param = {};
@@ -347,8 +347,8 @@ __PACKAGE__->register_method({
my $mp = $mountpoint->{mp};
if ($mountpoint->{type} ne 'volume') { # bind or device
- die "Only root can pass arbitrary filesystem paths.\n"
- if !$is_root;
+ die "Only superusers can pass arbitrary filesystem paths.\n"
+ if !$is_superuser;
} else {
my ($sid, $volname) = PVE::Storage::parse_volume_id($volid);
&$check_and_activate_storage($sid);
@@ -384,11 +384,11 @@ __PACKAGE__->register_method({
$was_template = delete $orig_conf->{template};
- # When we're root call 'restore_configuration' with restricted=0,
+ # call 'restore_configuration' with restricted=0 when we have superuser,
# causing it to restore the raw lxc entries, among which there may be
# 'lxc.idmap' entries. We need to make sure that the extracted contents
# of the container match up with the restored configuration afterwards:
- $conf->{lxc} = $orig_conf->{lxc} if $is_root;
+ $conf->{lxc} = $orig_conf->{lxc} if $is_superuser;
$conf->{unprivileged} = $orig_conf->{unprivileged}
if !defined($unprivileged) && defined($orig_conf->{unprivileged});
@@ -422,8 +422,7 @@ __PACKAGE__->register_method({
my $type = $mountpoint->{type};
die "restoring rootfs to $type mount is only possible by specifying -rootfs manually!\n"
if ($ms eq 'rootfs');
- die "restoring '$ms' to $type mount is only possible for root\n"
- if !$is_root;
+ die "restoring '$ms' to $type mount is only possible for superusers\n" if !$is_superuser;
if ($mountpoint->{backup}) {
warn "WARNING - unsupported configuration!\n";
@@ -464,7 +463,7 @@ __PACKAGE__->register_method({
if ($restore) {
print "merging backed-up and given configuration..\n";
- PVE::LXC::Create::restore_configuration($vmid, $storage_cfg, $archive, $rootdir, $conf, !$is_root, $unique, $skip_fw_config_restore);
+ PVE::LXC::Create::restore_configuration($vmid, $storage_cfg, $archive, $rootdir, $conf, !$is_superuser, $unique, $skip_fw_config_restore);
my $lxc_setup = PVE::LXC::Setup->new($conf, $rootdir);
$lxc_setup->template_fixup($conf);
} else {
@@ -2224,7 +2223,7 @@ __PACKAGE__->register_method({
raise_param_exc({ 'target-vmid' => $msg, 'storage' => $msg });
} elsif ($target_vmid) {
$rpcenv->check_vm_perm($authuser, $target_vmid, undef, ['VM.Config.Disk'])
- if $authuser ne 'root@pam';
+ if $authuser ne 'root@pam'; # no need to check for root@pam
if ($vmid eq $target_vmid) {
my $msg = "must be different than source VMID to move disk to another container";
diff --git a/src/PVE/API2/LXC/Config.pm b/src/PVE/API2/LXC/Config.pm
index 1fec048..6278b8a 100644
--- a/src/PVE/API2/LXC/Config.pm
+++ b/src/PVE/API2/LXC/Config.pm
@@ -99,7 +99,7 @@ __PACKAGE__->register_method({
description => "Set container options.",
permissions => {
check => ['perm', '/vms/{vmid}', $vm_config_perm_list, any => 1],
- description => 'non-volume mount points in rootfs and mp[n] are restricted to root@pam',
+ description => 'non-volume mount points in rootfs and mp[n] are restricted to superusers',
},
parameters => {
additionalProperties => 0,
diff --git a/src/PVE/API2/LXC/Status.pm b/src/PVE/API2/LXC/Status.pm
index f7e3128..ecd77e5 100644
--- a/src/PVE/API2/LXC/Status.pm
+++ b/src/PVE/API2/LXC/Status.pm
@@ -150,9 +150,11 @@ __PACKAGE__->register_method({
my $node = extract_param($param, 'node');
my $vmid = extract_param($param, 'vmid');
+ my $is_superuser = $rpcenv->check($authuser, "/vms/$vmid", ['SuperUser'], 1);
+
my $skiplock = extract_param($param, 'skiplock');
- raise_param_exc({ skiplock => "Only root may use this option." })
- if $skiplock && $authuser ne 'root@pam';
+ raise_param_exc({ skiplock => "Only superusers may use this option." })
+ if $skiplock && !$is_superuser;
die "CT $vmid already running\n" if PVE::LXC::check_running($vmid);
@@ -234,9 +236,11 @@ __PACKAGE__->register_method({
my $node = extract_param($param, 'node');
my $vmid = extract_param($param, 'vmid');
+ my $is_superuser = $rpcenv->check($authuser, "/vms/$vmid", ['SuperUser'], 1);
+
my $skiplock = extract_param($param, 'skiplock');
- raise_param_exc({ skiplock => "Only root may use this option." })
- if $skiplock && $authuser ne 'root@pam';
+ raise_param_exc({ skiplock => "Only superusers may use this option." })
+ if $skiplock && !$is_superuser;
die "CT $vmid not running\n" if !PVE::LXC::check_running($vmid);
diff --git a/src/PVE/LXC.pm b/src/PVE/LXC.pm
index fe63087..c93b314 100644
--- a/src/PVE/LXC.pm
+++ b/src/PVE/LXC.pm
@@ -1254,7 +1254,10 @@ sub template_create {
sub check_ct_modify_config_perm {
my ($rpcenv, $authuser, $vmid, $pool, $oldconf, $newconf, $delete, $unprivileged) = @_;
- return 1 if $authuser eq 'root@pam';
+ return 1 if $authuser eq 'root@pam'; # early exit for root@pam
+
+ my $is_superuser = $rpcenv->check($authuser, "/vms/$vmid", ['SuperUser'], 1);
+
my $storage_cfg = PVE::Storage::config();
my $check = sub {
@@ -1265,8 +1268,8 @@ sub check_ct_modify_config_perm {
$rpcenv->check_vm_perm($authuser, $vmid, $pool, ['VM.Config.Disk']);
return if $delete;
my $data = PVE::LXC::Config->parse_volume($opt, $newconf->{$opt});
- raise_perm_exc("mount point type $data->{type} is only allowed for root\@pam")
- if $data->{type} ne 'volume';
+ raise_perm_exc("mount point type $data->{type} is only allowed for superusers")
+ if $data->{type} ne 'volume' && !$is_superuser;
my $volid = $data->{volume};
if ($volid =~ $NEW_DISK_RE) {
my $sid = $1;
@@ -1287,8 +1290,8 @@ sub check_ct_modify_config_perm {
$opt eq 'searchdomain' || $opt eq 'hostname') {
$rpcenv->check_vm_perm($authuser, $vmid, $pool, ['VM.Config.Network']);
} elsif ($opt eq 'features') {
- raise_perm_exc("changing feature flags for privileged container is only allowed for root\@pam")
- if !$unprivileged;
+ raise_perm_exc("changing feature flags for privileged container is only allowed for superusers")
+ if !$unprivileged && !$is_superuser;
my $nesting_changed = 0;
my $other_changed = 0;
@@ -1326,13 +1329,13 @@ sub check_ct_modify_config_perm {
$other_changed = 1;
}
}
- raise_perm_exc("changing feature flags (except nesting) is only allowed for root\@pam")
- if $other_changed;
+ raise_perm_exc("changing feature flags (except nesting) is only allowed for superusers")
+ if $other_changed && !$is_superuser;
$rpcenv->check_vm_perm($authuser, $vmid, $pool, ['VM.Allocate'])
if $nesting_changed;
} elsif ($opt eq 'hookscript') {
- # For now this is restricted to root@pam
- raise_perm_exc("changing the hookscript is only allowed for root\@pam");
+ raise_perm_exc("changing the hookscript is only allowed for superusers")
+ if !$is_superuser;
} else {
$rpcenv->check_vm_perm($authuser, $vmid, $pool, ['VM.Config.Options']);
}
diff --git a/src/PVE/LXC/Create.pm b/src/PVE/LXC/Create.pm
index 460df57..4602678 100644
--- a/src/PVE/LXC/Create.pm
+++ b/src/PVE/LXC/Create.pm
@@ -331,7 +331,7 @@ sub sanitize_and_merge_config {
if ($key eq 'lxc' && $restricted) {
my $lxc_list = $oldconf->{'lxc'};
- my $msg = "skipping custom lxc options, restore manually as root:\n";
+ my $msg = "skipping custom lxc options, restore manually as superuser:\n";
$msg .= "--------------------------------\n";
foreach my $lxc_opt (@$lxc_list) {
$msg .= "$lxc_opt->[0]: $lxc_opt->[1]\n"
--
2.30.2
^ permalink raw reply [flat|nested] 18+ messages in thread
* [pve-devel] [PATCH v3 storage 16/17] check_volume_access: allow superusers to pass arbitrary fs paths
2022-04-06 11:57 [pve-devel] [PATCH v3 access-control++ 00/17] SuperUser privilege Oguz Bektas
` (14 preceding siblings ...)
2022-04-06 11:57 ` [pve-devel] [PATCH v3 container 15/17] fix #2582: api: add checks for 'SuperUser' privilege for root-only options Oguz Bektas
@ 2022-04-06 11:57 ` Oguz Bektas
2022-04-06 11:57 ` [pve-devel] [PATCH v3 docs 17/17] pveum: add SU privilege and SA role Oguz Bektas
16 siblings, 0 replies; 18+ messages in thread
From: Oguz Bektas @ 2022-04-06 11:57 UTC (permalink / raw)
To: pve-devel
Signed-off-by: Oguz Bektas <o.bektas@proxmox.com>
---
v2->v3:
* no changes
PVE/Storage.pm | 9 +++++++--
1 file changed, 7 insertions(+), 2 deletions(-)
diff --git a/PVE/Storage.pm b/PVE/Storage.pm
index 3b86956..32d90b7 100755
--- a/PVE/Storage.pm
+++ b/PVE/Storage.pm
@@ -475,6 +475,11 @@ sub parse_volume_id {
sub check_volume_access {
my ($rpcenv, $user, $cfg, $vmid, $volid, $type) = @_;
+ return if $user eq 'root@pam'; # always OK
+
+ # SU on "/" path is needed for passing arbitrary filesystem paths
+ my $is_superuser = $rpcenv->check($user, "/", ['SuperUser'], 1);
+
my ($sid, $volname) = parse_volume_id($volid, 1);
if ($sid) {
my ($vtype, undef, $ownervm) = parse_volname($cfg, $volid);
@@ -500,8 +505,8 @@ sub check_volume_access {
die "missing privileges to access $volid\n";
}
} else {
- die "Only root can pass arbitrary filesystem paths."
- if $user ne 'root@pam';
+ die "Only superusers can pass arbitrary filesystem paths."
+ if !$is_superuser;
}
return undef;
--
2.30.2
^ permalink raw reply [flat|nested] 18+ messages in thread
* [pve-devel] [PATCH v3 docs 17/17] pveum: add SU privilege and SA role
2022-04-06 11:57 [pve-devel] [PATCH v3 access-control++ 00/17] SuperUser privilege Oguz Bektas
` (15 preceding siblings ...)
2022-04-06 11:57 ` [pve-devel] [PATCH v3 storage 16/17] check_volume_access: allow superusers to pass arbitrary fs paths Oguz Bektas
@ 2022-04-06 11:57 ` Oguz Bektas
16 siblings, 0 replies; 18+ messages in thread
From: Oguz Bektas @ 2022-04-06 11:57 UTC (permalink / raw)
To: pve-devel
Signed-off-by: Oguz Bektas <o.bektas@proxmox.com>
---
v2->v3:
* separate SU and SA from the general privileges list (since they're not categorized easily)
* put actual notes inside and warn about potential danger and limitations regarding the privilege/role
pveum.adoc | 12 +++++++++++-
1 file changed, 11 insertions(+), 1 deletion(-)
diff --git a/pveum.adoc b/pveum.adoc
index a5c8906..60c357d 100644
--- a/pveum.adoc
+++ b/pveum.adoc
@@ -684,7 +684,11 @@ Roles
A role is simply a list of privileges. Proxmox VE comes with a number
of predefined roles, which satisfy most requirements.
-* `Administrator`: has full privileges
+* `SuperAdministrator`: has full privileges including `SuperUser`
+
+NOTE: `SuperAdministrator` role is equivalent to 'root@pam', be careful when giving it to a user!
+
+* `Administrator`: has all privileges except `SuperUser`
* `NoAccess`: has no privileges (used to forbid access)
* `PVEAdmin`: can do most tasks, but has no rights to modify system settings (`Sys.PowerMgmt`, `Sys.Modify`, `Realm.Allocate`)
* `PVEAuditor`: has read only access
@@ -725,6 +729,12 @@ assigned to users and paths without being part of a role.
We currently support the following privileges:
+* `SuperUser`: modify root-only configuration options (dangerous! don't give this privilege to untrusted users)
+
+NOTE: the `SuperUser` privilege alone is not enough to provide root-equivalent access to a user.
+
+NOTE: certain actions on users with this privilege are restricted, such as modifying password or 2FA settings.
+
Node / System related privileges::
* `Permissions.Modify`: modify access permissions
--
2.30.2
^ permalink raw reply [flat|nested] 18+ messages in thread