[pve-devel] [RFC container 3/3] implement permission checks for feature flags

Wolfgang Bumiller w.bumiller at proxmox.com
Tue Jul 31 14:50:00 CEST 2018


To disable a feature it is enough to be generally allowed
to edit the configuration. Enabling a feature requires more
privileges. For now: root at pam.

Signed-off-by: Wolfgang Bumiller <w.bumiller at proxmox.com>
---
 src/PVE/API2/LXC.pm        |  4 ++--
 src/PVE/API2/LXC/Config.pm |  5 ++---
 src/PVE/LXC.pm             | 42 +++++++++++++++++++++++++++++++++++++++++-
 src/PVE/LXC/Config.pm      |  2 +-
 4 files changed, 46 insertions(+), 7 deletions(-)

diff --git a/src/PVE/API2/LXC.pm b/src/PVE/API2/LXC.pm
index 55e6310..dcdca3e 100644
--- a/src/PVE/API2/LXC.pm
+++ b/src/PVE/API2/LXC.pm
@@ -244,7 +244,7 @@ __PACKAGE__->register_method({
 	my $ostemplate = extract_param($param, 'ostemplate');
 	my $storage = extract_param($param, 'storage') // 'local';
 
-	PVE::LXC::check_ct_modify_config_perm($rpcenv, $authuser, $vmid, $pool, $param, []);
+	PVE::LXC::check_ct_modify_config_perm($rpcenv, $authuser, $vmid, $pool, {}, $param, []);
 
 	my $storage_cfg = cfs_read_file("storage.cfg");
 
@@ -1567,7 +1567,7 @@ __PACKAGE__->register_method({
 
 	die "no options specified\n" if !scalar(keys %$param);
 
-	PVE::LXC::check_ct_modify_config_perm($rpcenv, $authuser, $vmid, undef, $param, []);
+	PVE::LXC::check_ct_modify_config_perm($rpcenv, $authuser, $vmid, undef, {}, $param, []);
 
 	my $storage_cfg = cfs_read_file("storage.cfg");
 
diff --git a/src/PVE/API2/LXC/Config.pm b/src/PVE/API2/LXC/Config.pm
index 2b622b3..bfe6bb1 100644
--- a/src/PVE/API2/LXC/Config.pm
+++ b/src/PVE/API2/LXC/Config.pm
@@ -112,7 +112,7 @@ __PACKAGE__->register_method({
 	my $delete_str = extract_param($param, 'delete');
 	my @delete = PVE::Tools::split_list($delete_str);
 
-	PVE::LXC::check_ct_modify_config_perm($rpcenv, $authuser, $vmid, undef, {}, [@delete]);
+	PVE::LXC::check_ct_modify_config_perm($rpcenv, $authuser, $vmid, undef, {}, {}, [@delete]);
 
 	foreach my $opt (@delete) {
 	    raise_param_exc({ delete => "you can't use '-$opt' and " .
@@ -124,8 +124,6 @@ __PACKAGE__->register_method({
 	    }
 	}
 
-	PVE::LXC::check_ct_modify_config_perm($rpcenv, $authuser, $vmid, undef, $param, []);
-
 	my $storage_cfg = cfs_read_file("storage.cfg");
 
 	my $repl_conf = PVE::ReplicationConfig->new();
@@ -156,6 +154,7 @@ __PACKAGE__->register_method({
 
 	    my $conf = PVE::LXC::Config->load_config($vmid);
 	    PVE::LXC::Config->check_lock($conf);
+	    PVE::LXC::check_ct_modify_config_perm($rpcenv, $authuser, $vmid, undef, $conf, $param, []);
 
 	    PVE::Tools::assert_if_modified($digest, $conf->{digest});
 
diff --git a/src/PVE/LXC.pm b/src/PVE/LXC.pm
index 46222ba..d9ecbed 100644
--- a/src/PVE/LXC.pm
+++ b/src/PVE/LXC.pm
@@ -1026,8 +1026,41 @@ sub template_create {
     PVE::LXC::Config->write_config($vmid, $conf);
 }
 
+sub check_feature_modify_perm {
+    my ($rpcenv, $authuser, $vmid, $pool, $old, $new) = @_;
+
+    my $features_desc = $PVE::LXC::Config::features_desc;
+
+    my $old_features = PVE::LXC::Config->parse_features($old);
+    my $new_features = PVE::LXC::Config->parse_features($new);
+
+    foreach my $feature (keys %$new_features) {
+	my $desc = $features_desc->{$feature};
+	my $type = $desc->{type};
+	my $new_value = $new_features->{$feature};
+	# Explicitly disabling a feature is like removing a feature (Also see
+	# the end of the function.)
+	next if !defined($new_value) ||
+		($type eq 'boolean' && !$new_value) ||
+		!length($new_value);
+
+	my $old_value = $old_features->{$feature};
+	# If we're not changing the feature's value, that's fine, too
+	next if defined($old_value) && $new_value eq $old_value;
+
+	# Otherwise, we can have feature-specific checks here.
+	# For now: require root at pam
+	my $action = defined($old_value) ? 'modify' : 'enable';
+	raise_perm_exc("only root\@pam can $action feature '$feature'")
+	    if $authuser ne 'root at pam';
+    }
+
+    # Whatever was in the old feature set abut not in the new feature set is
+    # being removed, which requires no special permissions.
+}
+
 sub check_ct_modify_config_perm {
-    my ($rpcenv, $authuser, $vmid, $pool, $newconf, $delete) = @_;
+    my ($rpcenv, $authuser, $vmid, $pool, $oldconf, $newconf, $delete) = @_;
 
     return 1 if $authuser eq 'root at pam';
 
@@ -1044,6 +1077,13 @@ sub check_ct_modify_config_perm {
 		if $data->{type} ne 'volume';
 	} elsif ($opt eq 'memory' || $opt eq 'swap') {
 	    $rpcenv->check_vm_perm($authuser, $vmid, $pool, ['VM.Config.Memory']);
+	} elsif ($opt eq 'features') {
+	    # In any case we need the usual VM.Config.Options
+	    $rpcenv->check_vm_perm($authuser, $vmid, $pool, ['VM.Config.Options']);
+	    # But *adding* features requires special permissions:
+	    if (!$delete) {
+		check_feature_modify_perm($rpcenv, $authuser, $vmid, $pool, $oldconf->{$opt}, $newconf->{$opt});
+	    }
 	} elsif ($opt =~ m/^net\d+$/ || $opt eq 'nameserver' ||
 		 $opt eq 'searchdomain' || $opt eq 'hostname') {
 	    $rpcenv->check_vm_perm($authuser, $vmid, $pool, ['VM.Config.Network']);
diff --git a/src/PVE/LXC/Config.pm b/src/PVE/LXC/Config.pm
index 9f6765e..61c3126 100644
--- a/src/PVE/LXC/Config.pm
+++ b/src/PVE/LXC/Config.pm
@@ -272,7 +272,7 @@ PVE::JSONSchema::register_standard_option('pve-lxc-snapshot-name', {
     maxLength => 40,
 });
 
-my $features_desc = {
+our $features_desc = {
     mount => {
 	optional => 1,
 	type => 'string',
-- 
2.11.0





More information about the pve-devel mailing list