[pve-devel] [PATCH container] network permissions: implement checks

Fabian Grünbichler f.gruenbichler at proxmox.com
Fri Jun 9 09:51:41 CEST 2023


when creating a new container
when restoring a backup
when cloning a container
and obviously, when changing the nics of an existing container

Signed-off-by: Fabian Grünbichler <f.gruenbichler at proxmox.com>
---
 src/PVE/API2/LXC.pm   | 12 +++++++++++-
 src/PVE/LXC.pm        | 17 ++++++++++++++++-
 src/PVE/LXC/Create.pm |  5 +++++
 3 files changed, 32 insertions(+), 2 deletions(-)

diff --git a/src/PVE/API2/LXC.pm b/src/PVE/API2/LXC.pm
index 2d67997..ed68390 100644
--- a/src/PVE/API2/LXC.pm
+++ b/src/PVE/API2/LXC.pm
@@ -388,6 +388,14 @@ __PACKAGE__->register_method({
 			print "recovering backed-up configuration from '$archive'\n";
 			($orig_conf, $orig_mp_param) = PVE::LXC::Create::recover_config($storage_cfg, $archive, $vmid);
 
+			for my $opt (keys %$orig_conf) {
+			    # early check before disks are created
+			    # the "real" check is in later on when actually merging the configs
+			    if ($opt =~ /^net\d+$/ && !$param->{opt}) {
+				PVE::LXC::check_bridge_access($rpcenv, $authuser, $orig_conf->{$opt});
+			    }
+			}
+
 			$was_template = delete $orig_conf->{template};
 
 			# When we're root call 'restore_configuration' with restricted=0,
@@ -1532,7 +1540,7 @@ __PACKAGE__->register_method({
 	description => "You need 'VM.Clone' permissions on /vms/{vmid}, " .
 	    "and 'VM.Allocate' permissions " .
 	    "on /vms/{newid} (or on the VM pool /pool/{pool}). You also need " .
-	    "'Datastore.AllocateSpace' on any used storage.",
+	    "'Datastore.AllocateSpace' on any used storage, and 'SDN.Use' on any bridge.",
 	check =>
 	[ 'and',
 	  ['perm', '/vms/{vmid}', [ 'VM.Clone' ]],
@@ -1724,6 +1732,8 @@ __PACKAGE__->register_method({
 		    my $net = PVE::LXC::Config->parse_lxc_network($value);
 		    $net->{hwaddr} = PVE::Tools::random_ether_addr($dc->{mac_prefix});
 		    $newconf->{$opt} = PVE::LXC::Config->print_lxc_network($net);
+
+		    PVE::LXC::check_bridge_access($rpcenv, $authuser, $newconf->{$opt});
 		} else {
 		    # copy everything else
 		    $newconf->{$opt} = $value;
diff --git a/src/PVE/LXC.pm b/src/PVE/LXC.pm
index 92c1b60..9642f2e 100644
--- a/src/PVE/LXC.pm
+++ b/src/PVE/LXC.pm
@@ -18,7 +18,7 @@ use PVE::AccessControl;
 use PVE::CGroup;
 use PVE::CpuSet;
 use PVE::Exception qw(raise_perm_exc);
-use PVE::GuestHelpers qw(safe_string_ne safe_num_ne safe_boolean_ne);
+use PVE::GuestHelpers qw(check_vnet_access safe_string_ne safe_num_ne safe_boolean_ne);
 use PVE::INotify;
 use PVE::JSONSchema qw(get_standard_option);
 use PVE::Network;
@@ -1317,6 +1317,7 @@ sub check_ct_modify_config_perm {
 	} elsif ($opt =~ m/^net\d+$/ || $opt eq 'nameserver' ||
 		 $opt eq 'searchdomain' || $opt eq 'hostname') {
 	    $rpcenv->check_vm_perm($authuser, $vmid, $pool, ['VM.Config.Network']);
+	    PVE::LXC::check_bridge_access($rpcenv, $authuser, $newconf->{$opt});
 	} elsif ($opt eq 'features') {
 	    raise_perm_exc("changing feature flags for privileged container is only allowed for root\@pam")
 		if !$unprivileged;
@@ -1383,6 +1384,20 @@ sub check_ct_modify_config_perm {
     return 1;
 }
 
+sub check_bridge_access {
+    my ($rpcenv, $authuser, $raw) = @_;
+
+    return 1 if $authuser eq 'root at pam';
+
+    my $net = PVE::LXC::Config->parse_lxc_network($raw);
+    my $bridge = $net->{bridge};
+    my $tag = $net->{tag};
+    my $trunks = $net->{trunks};
+    check_vnet_access($rpcenv, $authuser, $bridge, $tag, $trunks);
+
+    return 1;
+};
+
 sub umount_all {
     my ($vmid, $storage_cfg, $conf, $noerr) = @_;
 
diff --git a/src/PVE/LXC/Create.pm b/src/PVE/LXC/Create.pm
index b2e3d00..981f92d 100644
--- a/src/PVE/LXC/Create.pm
+++ b/src/PVE/LXC/Create.pm
@@ -325,6 +325,7 @@ sub sanitize_and_merge_config {
     my ($conf, $oldconf, $restricted, $unique) = @_;
 
     my $rpcenv = PVE::RPCEnvironment::get();
+    my $authuser = $rpcenv->get_user();
 
     foreach my $key (keys %$oldconf) {
 	next if $key eq 'digest' || $key eq 'rootfs' || $key eq 'snapshots' || $key eq 'unprivileged' || $key eq 'parent';
@@ -354,6 +355,10 @@ sub sanitize_and_merge_config {
 	    next;
 	}
 
+	if ($key =~ /^net\d+$/ && !defined($conf->{$key})) {
+	    PVE::LXC::check_bridge_access($rpcenv, $authuser, $oldconf->{$key});
+	}
+
 	if ($unique && $key =~ /^net\d+$/) {
 	    my $net = PVE::LXC::Config->parse_lxc_network($oldconf->{$key});
 	    my $dc = PVE::Cluster::cfs_read_file('datacenter.cfg');
-- 
2.39.2






More information about the pve-devel mailing list