public inbox for pve-devel@lists.proxmox.com
 help / color / mirror / Atom feed
* [pve-devel] [PATCH-SERIE pve-access-control/pve-manager/pve-guest-common/qemu-server/pve-network] check permissions on local bridge
@ 2023-06-06 13:19 Alexandre Derumier
  2023-06-06 13:19 ` [pve-devel] [PATCH v2 pve-access-control 1/3] access control: add /sdn/zones/<zone>/<vnet>/<vlan> path Alexandre Derumier
                   ` (9 more replies)
  0 siblings, 10 replies; 12+ messages in thread
From: Alexandre Derumier @ 2023-06-06 13:19 UTC (permalink / raw)
  To: pve-devel

add vnet/localbridge permissions management

Hi,
as we has discuted some weeks ago,
this patche serie introduce management of acl for vnets && local bridges

The permission path is:

/sdn/zones/<zone>/<vnet>

where the local vmbr are in a virtual "localnetwork" zone

/sdn/zones/localnetwork/<vnet>

Vlans permissions  are also handled with
/sdn/zones/<zone>/<vnet>/<tag>

if user have permissions on the vnet/tag, he have access to only the specific vlan.
if user have permissions on the vnet with propagate, he have access to all vlans of the vnet
if user have permissions on the vnet without propagate, he have access to bridge only without any vlan


I have reworked the sdn zone panel from the tree, to manage permissions
on displayed vnets. (patch 3 && 4 pve-manager)

some screenshots:

https://mutulin1.odiso.net/sdnzone-perm.png
https://mutulin1.odiso.net/localzone-perm.png



changelog v2:
 - use /vnets/vlan instead /vnets.vlan
 - rework the bridge filtering when user have access only to a specific vlan
 - api2 network: always check bridge access if no filter is defined

changelog v3:
 - use /sdn/zones/<zone>/vnets/vlan instead /sdn/vnets/vnets.vlan
 - add SDN.Use permission
 - pve-manager: split ui code (could be applied later)
 - remove check on zone (it's now propagate with new path)
 - move check_vnet_access to pve-guest-common for lxc reuse
 - pve-network: fix vnet/tag perm check

todo:
 - implement check permissions on trunks
 - qemu : check bridge permissions on restore. (how to check restore config before restore ?)
 - lxc: check bridge permissions


pve-access-control:

Alexandre Derumier (3):
  access control: add /sdn/zones/<zone>/<vnet>/<vlan> path
  rpcenvironnment: add check_sdn_bridge
  add new SDN.use privilege in PVESDNUser role

 src/PVE/AccessControl.pm  |  6 +++++-
 src/PVE/RPCEnvironment.pm | 18 ++++++++++++++++++
 2 files changed, 23 insertions(+), 1 deletion(-)

pve-manager:

Alexandre Derumier (4):
  api2: network: check permissions for local bridges
  api2: cluster: ressources: add "localnetwork" zone
  ui: add vnet permissions panel
  ui: add permissions management for "localnetwork" zone

 PVE/API2/Cluster.pm                  |  14 ++
 PVE/API2/Network.pm                  |  25 ++-
 www/manager6/Makefile                |   2 +
 www/manager6/sdn/Browser.js          |  17 +-
 www/manager6/sdn/VnetACLView.js      | 289 +++++++++++++++++++++++++++
 www/manager6/sdn/ZoneContentPanel.js |  41 ++++
 www/manager6/sdn/ZoneContentView.js  |  52 ++++-
 7 files changed, 411 insertions(+), 29 deletions(-)
 create mode 100644 www/manager6/sdn/VnetACLView.js
 create mode 100644 www/manager6/sdn/ZoneContentPanel.js


pve-guest-common:

Alexandre Derumier (1):
  helpers : add check_vnet_access

 src/PVE/GuestHelpers.pm | 25 +++++++++++++++++++++++++
 1 file changed, 25 insertions(+)

qemu-server:

Alexandre Derumier (1):
  api2: add check_bridge_access for create/update/clone vm

 PVE/API2/Qemu.pm | 25 ++++++++++++++++++++++---
 1 file changed, 22 insertions(+), 3 deletions(-)

pve-network:

Alexandre Derumier (1):
  get_local_vnets: fix permission path && perm

 PVE/Network/SDN.pm | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

-- 
2.30.2




^ permalink raw reply	[flat|nested] 12+ messages in thread

* [pve-devel] [PATCH v2 pve-access-control 1/3] access control: add /sdn/zones/<zone>/<vnet>/<vlan> path
  2023-06-06 13:19 [pve-devel] [PATCH-SERIE pve-access-control/pve-manager/pve-guest-common/qemu-server/pve-network] check permissions on local bridge Alexandre Derumier
@ 2023-06-06 13:19 ` Alexandre Derumier
  2023-06-06 13:19 ` [pve-devel] [PATCH v3 qemu-server 1/1] api2: add check_bridge_access for create/update/clone vm Alexandre Derumier
                   ` (8 subsequent siblings)
  9 siblings, 0 replies; 12+ messages in thread
From: Alexandre Derumier @ 2023-06-06 13:19 UTC (permalink / raw)
  To: pve-devel

Signed-off-by: Alexandre Derumier <aderumier@odiso.com>
---
 src/PVE/AccessControl.pm | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/src/PVE/AccessControl.pm b/src/PVE/AccessControl.pm
index 89b7d90..6a3d203 100644
--- a/src/PVE/AccessControl.pm
+++ b/src/PVE/AccessControl.pm
@@ -1283,7 +1283,8 @@ sub check_path {
 	|/pool/[[:alnum:]\.\-\_]+
 	|/sdn
 	|/sdn/zones/[[:alnum:]\.\-\_]+
-	|/sdn/vnets/[[:alnum:]\.\-\_]+
+	|/sdn/zones/[[:alnum:]\.\-\_]+/[[:alnum:]\.\-\_]+
+	|/sdn/zones/[[:alnum:]\.\-\_]+/[[:alnum:]\.\-\_]+/[1-9][0-9]{0,3}
 	|/storage
 	|/storage/[[:alnum:]\.\-\_]+
 	|/vms
-- 
2.30.2




^ permalink raw reply	[flat|nested] 12+ messages in thread

* [pve-devel] [PATCH v3 qemu-server 1/1] api2: add check_bridge_access for create/update/clone vm
  2023-06-06 13:19 [pve-devel] [PATCH-SERIE pve-access-control/pve-manager/pve-guest-common/qemu-server/pve-network] check permissions on local bridge Alexandre Derumier
  2023-06-06 13:19 ` [pve-devel] [PATCH v2 pve-access-control 1/3] access control: add /sdn/zones/<zone>/<vnet>/<vlan> path Alexandre Derumier
@ 2023-06-06 13:19 ` Alexandre Derumier
  2023-06-06 13:19 ` [pve-devel] [PATCH v3 pve-manager 1/4] api2: network: check permissions for local bridges Alexandre Derumier
                   ` (7 subsequent siblings)
  9 siblings, 0 replies; 12+ messages in thread
From: Alexandre Derumier @ 2023-06-06 13:19 UTC (permalink / raw)
  To: pve-devel

Signed-off-by: Alexandre Derumier <aderumier@odiso.com>
---
 PVE/API2/Qemu.pm | 25 ++++++++++++++++++++++---
 1 file changed, 22 insertions(+), 3 deletions(-)

diff --git a/PVE/API2/Qemu.pm b/PVE/API2/Qemu.pm
index 587bb22..c290c81 100644
--- a/PVE/API2/Qemu.pm
+++ b/PVE/API2/Qemu.pm
@@ -23,7 +23,7 @@ use PVE::Storage;
 use PVE::JSONSchema qw(get_standard_option);
 use PVE::RESTHandler;
 use PVE::ReplicationConfig;
-use PVE::GuestHelpers qw(assert_tag_permissions);
+use PVE::GuestHelpers qw(assert_tag_permissions check_vnet_access);
 use PVE::QemuConfig;
 use PVE::QemuServer;
 use PVE::QemuServer::Cloudinit;
@@ -601,6 +601,21 @@ my $check_vm_create_usb_perm = sub {
     return 1;
 };
 
+my $check_bridge_access = sub {
+    my ($rpcenv, $authuser, $param) = @_;
+
+    return 1 if $authuser eq 'root@pam';
+
+    foreach my $opt (keys %{$param}) {
+	next if $opt !~ m/^net\d+$/;
+	my $net = PVE::QemuServer::parse_net($param->{$opt});
+	my $bridge = $net->{bridge};
+	my $tag = $net->{tag};
+	check_vnet_access($rpcenv, $authuser, $bridge, $tag);
+    }
+    return 1;
+};
+
 my $check_vm_modify_config_perm = sub {
     my ($rpcenv, $authuser, $vmid, $pool, $key_list) = @_;
 
@@ -878,7 +893,7 @@ __PACKAGE__->register_method({
 
 	    &$check_vm_create_serial_perm($rpcenv, $authuser, $vmid, $pool, $param);
 	    &$check_vm_create_usb_perm($rpcenv, $authuser, $vmid, $pool, $param);
-
+	    &$check_bridge_access($rpcenv, $authuser, $param);
 	    &$check_cpu_model_access($rpcenv, $authuser, $param);
 
 	    $check_drive_param->($param, $storecfg);
@@ -1578,6 +1593,8 @@ my $update_vm_api  = sub {
 
     &$check_storage_access($rpcenv, $authuser, $storecfg, $vmid, $param);
 
+    &$check_bridge_access($rpcenv, $authuser, $param);
+
     my $updatefn =  sub {
 
 	my $conf = PVE::QemuConfig->load_config($vmid);
@@ -3355,7 +3372,7 @@ __PACKAGE__->register_method({
     permissions => {
 	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 used bridge/vnet",
 	check =>
 	[ 'and',
 	  ['perm', '/vms/{vmid}', [ 'VM.Clone' ]],
@@ -3489,6 +3506,8 @@ __PACKAGE__->register_method({
 
 	    my $sharedvm = &$check_storage_access_clone($rpcenv, $authuser, $storecfg, $oldconf, $storage);
 
+	    &$check_bridge_access($rpcenv, $authuser, $oldconf);
+
 	    die "can't clone VM to node '$target' (VM uses local storage)\n"
 		if $target && !$sharedvm;
 
-- 
2.30.2




^ permalink raw reply	[flat|nested] 12+ messages in thread

* [pve-devel] [PATCH v3 pve-manager 1/4] api2: network: check permissions for local bridges
  2023-06-06 13:19 [pve-devel] [PATCH-SERIE pve-access-control/pve-manager/pve-guest-common/qemu-server/pve-network] check permissions on local bridge Alexandre Derumier
  2023-06-06 13:19 ` [pve-devel] [PATCH v2 pve-access-control 1/3] access control: add /sdn/zones/<zone>/<vnet>/<vlan> path Alexandre Derumier
  2023-06-06 13:19 ` [pve-devel] [PATCH v3 qemu-server 1/1] api2: add check_bridge_access for create/update/clone vm Alexandre Derumier
@ 2023-06-06 13:19 ` Alexandre Derumier
  2023-06-06 13:19 ` [pve-devel] [PATCH pve-network 1/1] get_local_vnets: fix permission path && perm Alexandre Derumier
                   ` (6 subsequent siblings)
  9 siblings, 0 replies; 12+ messages in thread
From: Alexandre Derumier @ 2023-06-06 13:19 UTC (permalink / raw)
  To: pve-devel

always check permissions, also when not filtered

Signed-off-by: Alexandre Derumier <aderumier@odiso.com>
---
 PVE/API2/Network.pm | 25 +++++++++++++++----------
 1 file changed, 15 insertions(+), 10 deletions(-)

diff --git a/PVE/API2/Network.pm b/PVE/API2/Network.pm
index a43579fa..8dc56482 100644
--- a/PVE/API2/Network.pm
+++ b/PVE/API2/Network.pm
@@ -209,7 +209,7 @@ __PACKAGE__->register_method({
 	    type => {
 		description => "Only list specific interface types.",
 		type => 'string',
-		enum => [ @$network_type_enum, 'any_bridge' ],
+		enum => [ @$network_type_enum, 'any_bridge', 'any_local_bridge' ],
 		optional => 1,
 	    },
 	},
@@ -240,22 +240,17 @@ __PACKAGE__->register_method({
 
 	if (my $tfilter = $param->{type}) {
 	    my $vnets;
-	    my $vnet_cfg;
-	    my $can_access_vnet = sub { # only matters for the $have_sdn case, checked implict
-		return 1 if $authuser eq 'root@pam' || !defined($vnets);
-		return 1 if !defined(PVE::Network::SDN::Vnets::sdn_vnets_config($vnet_cfg, $_[0], 1)); # not a vnet
-		$rpcenv->check_any($authuser, "/sdn/vnets/$_[0]", ['SDN.Audit', 'SDN.Allocate'], 1)
-	    };
 
 	    if ($have_sdn && $param->{type} eq 'any_bridge') {
 		$vnets = PVE::Network::SDN::get_local_vnets(); # returns already access-filtered
-		$vnet_cfg = PVE::Network::SDN::Vnets::config();
 	    }
 
 	    for my $k (sort keys $ifaces->%*) {
 		my $type = $ifaces->{$k}->{type};
-		my $match = $tfilter eq $type || ($tfilter eq 'any_bridge' && ($type eq 'bridge' || $type eq 'OVSBridge'));
-		delete $ifaces->{$k} if !($match && $can_access_vnet->($k));
+		my $match = ($param->{type} eq $type) || (
+		    ($param->{type} =~ /^any(_local)?_bridge$/) &&
+		    ($type eq 'bridge' || $type eq 'OVSBridge'));
+		delete $ifaces->{$k} if !$match;
 	    }
 
 	    if (defined($vnets)) {
@@ -263,6 +258,16 @@ __PACKAGE__->register_method({
 	    }
 	}
 
+	#always check bridge access
+	my $can_access_vnet = sub {
+	    return 1 if $authuser eq 'root@pam';
+	    return 1 if $rpcenv->check_sdn_bridge($authuser, "localnetwork", $_[0], ['SDN.Audit', 'SDN.Use'], 1);
+	};
+	for my $k (sort keys $ifaces->%*) {
+	    my $type = $ifaces->{$k}->{type};
+	    delete $ifaces->{$k} if ($type eq 'bridge' || $type eq 'OVSBridge') && !$can_access_vnet->($k);
+	}
+
 	return PVE::RESTHandler::hash_to_array($ifaces, 'iface');
    }});
 
-- 
2.30.2




^ permalink raw reply	[flat|nested] 12+ messages in thread

* [pve-devel] [PATCH pve-network 1/1] get_local_vnets: fix permission path && perm
  2023-06-06 13:19 [pve-devel] [PATCH-SERIE pve-access-control/pve-manager/pve-guest-common/qemu-server/pve-network] check permissions on local bridge Alexandre Derumier
                   ` (2 preceding siblings ...)
  2023-06-06 13:19 ` [pve-devel] [PATCH v3 pve-manager 1/4] api2: network: check permissions for local bridges Alexandre Derumier
@ 2023-06-06 13:19 ` Alexandre Derumier
  2023-06-06 13:19 ` [pve-devel] [PATCH pve-guest-common 1/1] helpers : add check_vnet_access Alexandre Derumier
                   ` (5 subsequent siblings)
  9 siblings, 0 replies; 12+ messages in thread
From: Alexandre Derumier @ 2023-06-06 13:19 UTC (permalink / raw)
  To: pve-devel

new path is /zones/<zone>/<vnetid>

Signed-off-by: Alexandre Derumier <aderumier@odiso.com>
---
 PVE/Network/SDN.pm | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/PVE/Network/SDN.pm b/PVE/Network/SDN.pm
index b95dd5b..1ad85e5 100644
--- a/PVE/Network/SDN.pm
+++ b/PVE/Network/SDN.pm
@@ -190,10 +190,10 @@ sub get_local_vnets {
 	my $zoneid = $vnet->{zone};
 	my $comments = $vnet->{alias};
 
-	my $privs = [ 'SDN.Audit', 'SDN.Allocate' ];
+	my $privs = [ 'SDN.Audit', 'SDN.Use' ];
 
 	next if !$zoneid;
-	next if !$rpcenv->check_any($authuser, "/sdn/zones/$zoneid", $privs, 1) && !$rpcenv->check_any($authuser, "/sdn/vnets/$vnetid", $privs, 1);
+	next if !$rpcenv->check_sdn_bridge($authuser, $zoneid, $vnetid, $privs, 1);
 
 	my $zone_config = PVE::Network::SDN::Zones::sdn_zones_config($zones_cfg, $zoneid);
 
-- 
2.30.2




^ permalink raw reply	[flat|nested] 12+ messages in thread

* [pve-devel] [PATCH pve-guest-common 1/1] helpers : add check_vnet_access
  2023-06-06 13:19 [pve-devel] [PATCH-SERIE pve-access-control/pve-manager/pve-guest-common/qemu-server/pve-network] check permissions on local bridge Alexandre Derumier
                   ` (3 preceding siblings ...)
  2023-06-06 13:19 ` [pve-devel] [PATCH pve-network 1/1] get_local_vnets: fix permission path && perm Alexandre Derumier
@ 2023-06-06 13:19 ` Alexandre Derumier
  2023-06-06 13:19 ` [pve-devel] [PATCH v3 pve-manager 2/4] api2: cluster: ressources: add "localnetwork" zone Alexandre Derumier
                   ` (4 subsequent siblings)
  9 siblings, 0 replies; 12+ messages in thread
From: Alexandre Derumier @ 2023-06-06 13:19 UTC (permalink / raw)
  To: pve-devel

if a tag is defined, test if user have a specific access to the vlan (or propagate from full bridge acl or zone)
if no tag, test if user have access to full bridge. (if trunks are defined, it need also access to full bridge)

Signed-off-by: Alexandre Derumier <aderumier@odiso.com>
---
 src/PVE/GuestHelpers.pm | 25 +++++++++++++++++++++++++
 1 file changed, 25 insertions(+)

diff --git a/src/PVE/GuestHelpers.pm b/src/PVE/GuestHelpers.pm
index b4ccbaa..53c63e8 100644
--- a/src/PVE/GuestHelpers.pm
+++ b/src/PVE/GuestHelpers.pm
@@ -10,10 +10,17 @@ use PVE::Storage;
 use POSIX qw(strftime);
 use Scalar::Util qw(weaken);
 
+my $have_sdn;
+eval {
+    require PVE::Network::SDN;
+    $have_sdn = 1;
+};
+
 use base qw(Exporter);
 
 our @EXPORT_OK = qw(
 assert_tag_permissions
+check_vnet_access
 get_allowed_tags
 safe_boolean_ne
 safe_num_ne
@@ -366,4 +373,22 @@ sub get_unique_tags {
     return !$no_join_result ? join(';', $res->@*) : $res;
 }
 
+sub check_vnet_access {
+    my ($rpcenv, $authuser, $vnet, $tag) = @_;
+
+    my $zone = 'localnetwork';
+
+    if ($have_sdn) {
+	my $vnet_cfg = PVE::Network::SDN::Vnets::config();
+	if (defined(my $vnet = PVE::Network::SDN::Vnets::sdn_vnets_config($vnet_cfg, $vnet, 1))) {
+	    $zone = $vnet->{zone};
+	}
+    }
+
+    # if a tag is defined, test if user have a specific access to the vlan (or propagated from full bridge acl)
+    $rpcenv->check($authuser, "/sdn/zones/$zone/$vnet/$tag", ['SDN.Use']) if $tag;
+    # if no tag, test if user have access to full bridge. (if trunks are defined, it need also access to full bridge)
+    $rpcenv->check($authuser, "/sdn/zones/$zone/$vnet", ['SDN.Use']);
+}
+
 1;
-- 
2.30.2




^ permalink raw reply	[flat|nested] 12+ messages in thread

* [pve-devel] [PATCH v3 pve-manager 2/4] api2: cluster: ressources: add "localnetwork" zone
  2023-06-06 13:19 [pve-devel] [PATCH-SERIE pve-access-control/pve-manager/pve-guest-common/qemu-server/pve-network] check permissions on local bridge Alexandre Derumier
                   ` (4 preceding siblings ...)
  2023-06-06 13:19 ` [pve-devel] [PATCH pve-guest-common 1/1] helpers : add check_vnet_access Alexandre Derumier
@ 2023-06-06 13:19 ` Alexandre Derumier
  2023-06-06 13:19 ` [pve-devel] [PATCH v2 pve-access-control 2/3] rpcenvironnment: add check_sdn_bridge Alexandre Derumier
                   ` (3 subsequent siblings)
  9 siblings, 0 replies; 12+ messages in thread
From: Alexandre Derumier @ 2023-06-06 13:19 UTC (permalink / raw)
  To: pve-devel

Signed-off-by: Alexandre Derumier <aderumier@odiso.com>
---
 PVE/API2/Cluster.pm | 14 ++++++++++++++
 1 file changed, 14 insertions(+)

diff --git a/PVE/API2/Cluster.pm b/PVE/API2/Cluster.pm
index 2e942368..a7224d7f 100644
--- a/PVE/API2/Cluster.pm
+++ b/PVE/API2/Cluster.pm
@@ -474,6 +474,20 @@ __PACKAGE__->register_method({
 	    }
 	}
 
+	#add default "localnetwork" zone
+	if ($rpcenv->check($authuser, "/sdn/zones/localnetwork", [ 'SDN.Audit' ], 1)) {
+	    foreach my $node (@$nodelist) {
+		my $local_sdn = {
+		    id => "sdn/$node/localnetwork",
+		    sdn => 'localnetwork',
+		    node => $node,
+		    type => 'sdn',
+		    status => 'ok',
+	        };
+	        push @$res, $local_sdn;
+	    }
+	}
+
 	if ($have_sdn) {
 	    if (!$param->{type} || $param->{type} eq 'sdn') {
 
-- 
2.30.2




^ permalink raw reply	[flat|nested] 12+ messages in thread

* [pve-devel] [PATCH v2 pve-access-control 2/3] rpcenvironnment: add check_sdn_bridge
  2023-06-06 13:19 [pve-devel] [PATCH-SERIE pve-access-control/pve-manager/pve-guest-common/qemu-server/pve-network] check permissions on local bridge Alexandre Derumier
                   ` (5 preceding siblings ...)
  2023-06-06 13:19 ` [pve-devel] [PATCH v3 pve-manager 2/4] api2: cluster: ressources: add "localnetwork" zone Alexandre Derumier
@ 2023-06-06 13:19 ` Alexandre Derumier
  2023-06-06 13:19 ` [pve-devel] [PATCH v2 pve-access-control 3/3] add new SDN.use privilege in PVESDNUser role Alexandre Derumier
                   ` (2 subsequent siblings)
  9 siblings, 0 replies; 12+ messages in thread
From: Alexandre Derumier @ 2023-06-06 13:19 UTC (permalink / raw)
  To: pve-devel

check if user have access to 1 vlan of the bridge
or the bridge itself

Signed-off-by: Alexandre Derumier <aderumier@odiso.com>
---
 src/PVE/RPCEnvironment.pm | 18 ++++++++++++++++++
 1 file changed, 18 insertions(+)

diff --git a/src/PVE/RPCEnvironment.pm b/src/PVE/RPCEnvironment.pm
index 8586938..e0a101f 100644
--- a/src/PVE/RPCEnvironment.pm
+++ b/src/PVE/RPCEnvironment.pm
@@ -324,6 +324,24 @@ sub check_full {
     }
 }
 
+sub check_sdn_bridge {
+    my ($self, $username, $zone, $bridge, $privs, $noerr) = @_;
+
+    my $path = "/sdn/zones/$zone/$bridge";
+    my $cfg = $self->{user_cfg};
+    my $bridge_acl = PVE::AccessControl::find_acl_tree_node($cfg->{acl_root}, $path);
+    if ($bridge_acl) {
+	my $vlans = $bridge_acl->{children};
+	for my $vlan (keys %$vlans) {
+	    my $vlanpath = "$path/$vlan";
+	    return 1 if $self->check_any($username, $vlanpath, $privs, $noerr);
+	}
+	# check access to bridge itself
+	return 1 if $self->check_any($username, $path, $privs, $noerr);
+    }
+    return;
+}
+
 sub check_user_enabled {
     my ($self, $user, $noerr) = @_;
 
-- 
2.30.2




^ permalink raw reply	[flat|nested] 12+ messages in thread

* [pve-devel] [PATCH v2 pve-access-control 3/3] add new SDN.use privilege in PVESDNUser role
  2023-06-06 13:19 [pve-devel] [PATCH-SERIE pve-access-control/pve-manager/pve-guest-common/qemu-server/pve-network] check permissions on local bridge Alexandre Derumier
                   ` (6 preceding siblings ...)
  2023-06-06 13:19 ` [pve-devel] [PATCH v2 pve-access-control 2/3] rpcenvironnment: add check_sdn_bridge Alexandre Derumier
@ 2023-06-06 13:19 ` Alexandre Derumier
  2023-06-06 13:19 ` [pve-devel] [PATCH v3 pve-manager 3/4] ui: add vnet permissions panel Alexandre Derumier
  2023-06-06 13:19 ` [pve-devel] [PATCH v3 pve-manager 4/4] ui: add permissions management for "localnetwork" zone Alexandre Derumier
  9 siblings, 0 replies; 12+ messages in thread
From: Alexandre Derumier @ 2023-06-06 13:19 UTC (permalink / raw)
  To: pve-devel

Signed-off-by: Alexandre Derumier <aderumier@odiso.com>
---
 src/PVE/AccessControl.pm | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/src/PVE/AccessControl.pm b/src/PVE/AccessControl.pm
index 6a3d203..326ed5c 100644
--- a/src/PVE/AccessControl.pm
+++ b/src/PVE/AccessControl.pm
@@ -1131,6 +1131,9 @@ my $privgroups = {
 	    'SDN.Allocate',
 	    'SDN.Audit',
 	],
+	user => [
+	    'SDN.Use',
+	],
 	audit => [
 	    'SDN.Audit',
 	],
-- 
2.30.2




^ permalink raw reply	[flat|nested] 12+ messages in thread

* [pve-devel] [PATCH v3 pve-manager 3/4] ui: add vnet permissions panel
  2023-06-06 13:19 [pve-devel] [PATCH-SERIE pve-access-control/pve-manager/pve-guest-common/qemu-server/pve-network] check permissions on local bridge Alexandre Derumier
                   ` (7 preceding siblings ...)
  2023-06-06 13:19 ` [pve-devel] [PATCH v2 pve-access-control 3/3] add new SDN.use privilege in PVESDNUser role Alexandre Derumier
@ 2023-06-06 13:19 ` Alexandre Derumier
  2023-06-06 13:19 ` [pve-devel] [PATCH v3 pve-manager 4/4] ui: add permissions management for "localnetwork" zone Alexandre Derumier
  9 siblings, 0 replies; 12+ messages in thread
From: Alexandre Derumier @ 2023-06-06 13:19 UTC (permalink / raw)
  To: pve-devel

Signed-off-by: Alexandre Derumier <aderumier@odiso.com>
---
 www/manager6/Makefile                |   2 +
 www/manager6/sdn/Browser.js          |  17 +-
 www/manager6/sdn/VnetACLView.js      | 289 +++++++++++++++++++++++++++
 www/manager6/sdn/ZoneContentPanel.js |  41 ++++
 www/manager6/sdn/ZoneContentView.js  |  25 ++-
 5 files changed, 356 insertions(+), 18 deletions(-)
 create mode 100644 www/manager6/sdn/VnetACLView.js
 create mode 100644 www/manager6/sdn/ZoneContentPanel.js

diff --git a/www/manager6/Makefile b/www/manager6/Makefile
index 2b577c8e..fb9930af 100644
--- a/www/manager6/Makefile
+++ b/www/manager6/Makefile
@@ -251,10 +251,12 @@ JSSRC= 							\
 	sdn/StatusView.js				\
 	sdn/VnetEdit.js					\
 	sdn/VnetView.js					\
+	sdn/VnetACLView.js					\
 	sdn/VnetPanel.js				\
 	sdn/SubnetEdit.js				\
 	sdn/SubnetView.js				\
 	sdn/ZoneContentView.js				\
+	sdn/ZoneContentPanel.js				\
 	sdn/ZoneView.js					\
 	sdn/OptionsPanel.js				\
 	sdn/controllers/Base.js				\
diff --git a/www/manager6/sdn/Browser.js b/www/manager6/sdn/Browser.js
index 09b0c4fe..3dc5a5ad 100644
--- a/www/manager6/sdn/Browser.js
+++ b/www/manager6/sdn/Browser.js
@@ -25,14 +25,15 @@ Ext.define('PVE.sdn.Browser', {
 
 	const caps = Ext.state.Manager.get('GuiCap');
 
-	if (caps.sdn['SDN.Audit']) {
-	    me.items.push({
-		xtype: 'pveSDNZoneContentView',
-		title: gettext('Content'),
-		iconCls: 'fa fa-th',
-		itemId: 'content',
-	    });
-	}
+	me.items.push({
+	    nodename: nodename,
+	    zone: sdnId,
+	    xtype: 'pveSDNZoneContentPanel',
+	    title: gettext('Content'),
+	    iconCls: 'fa fa-th',
+	    itemId: 'content',
+	});
+
 	if (caps.sdn['Permissions.Modify']) {
 	    me.items.push({
 		xtype: 'pveACLView',
diff --git a/www/manager6/sdn/VnetACLView.js b/www/manager6/sdn/VnetACLView.js
new file mode 100644
index 00000000..af10d954
--- /dev/null
+++ b/www/manager6/sdn/VnetACLView.js
@@ -0,0 +1,289 @@
+Ext.define('PVE.sdn.VnetACLAdd', {
+    extend: 'Proxmox.window.Edit',
+    alias: ['widget.pveSDNVnetACLAdd'],
+
+    url: '/access/acl',
+    method: 'PUT',
+    isAdd: true,
+    isCreate: true,
+
+    width: 400,
+    initComponent: function() {
+        let me = this;
+
+	let items = [
+	    {
+		xtype: 'hiddenfield',
+		name: 'path',
+		value: me.path,
+		allowBlank: false,
+		fieldLabel: gettext('Path'),
+	    },
+	];
+
+	if (me.aclType === 'group') {
+	    me.subject = gettext("Group Permission");
+	    items.push({
+		xtype: 'pveGroupSelector',
+		name: 'groups',
+		fieldLabel: gettext('Group'),
+	    });
+	} else if (me.aclType === 'user') {
+	    me.subject = gettext("User Permission");
+	    items.push({
+		xtype: 'pmxUserSelector',
+		name: 'users',
+		fieldLabel: gettext('User'),
+	    });
+	} else if (me.aclType === 'token') {
+	    me.subject = gettext("API Token Permission");
+	    items.push({
+		xtype: 'pveTokenSelector',
+		name: 'tokens',
+		fieldLabel: gettext('API Token'),
+	    });
+	} else {
+	    throw "unknown ACL type";
+	}
+
+	items.push({
+	    xtype: 'pmxRoleSelector',
+	    name: 'roles',
+	    value: 'NoAccess',
+	    fieldLabel: gettext('Role'),
+	});
+
+	items.push({
+	    xtype: 'proxmoxintegerfield',
+	    name: 'vlan',
+	    minValue: 1,
+	    maxValue: 4096,
+            allowBlank: true,
+	    fieldLabel: 'Vlan',
+	    emptyText: gettext('All'),
+	});
+
+	let ipanel = Ext.create('Proxmox.panel.InputPanel', {
+	    items: items,
+	    onlineHelp: 'pveum_permission_management',
+	    onGetValues: function(values) {
+		if (values.vlan) {
+		    values.path = values.path + "/" + values.vlan;
+		    delete values.vlan;
+		}
+		return values;
+	    },
+	});
+
+	Ext.apply(me, {
+	    items: [ipanel],
+	});
+
+	me.callParent();
+    },
+});
+
+Ext.define('PVE.sdn.VnetACLView', {
+    extend: 'Ext.grid.GridPanel',
+
+    alias: ['widget.pveSDNVnetACLView'],
+
+    onlineHelp: 'chapter_user_management',
+
+    stateful: true,
+    stateId: 'grid-acls',
+
+    // use fixed path
+    path: undefined,
+
+    setPath: function(path) {
+        let me = this;
+
+        me.path = path;
+
+        if (path === undefined) {
+	    me.down('#groupmenu').setDisabled(true);
+	    me.down('#usermenu').setDisabled(true);
+	    me.down('#tokenmenu').setDisabled(true);
+        } else {
+	    me.down('#groupmenu').setDisabled(false);
+	    me.down('#usermenu').setDisabled(false);
+	    me.down('#tokenmenu').setDisabled(false);
+            me.store.load();
+        }
+    },
+    initComponent: function() {
+	let me = this;
+
+	let store = Ext.create('Ext.data.Store', {
+	    model: 'pve-acl',
+	    proxy: {
+                type: 'proxmox',
+		url: "/api2/json/access/acl",
+	    },
+	    sorters: {
+		property: 'path',
+		direction: 'ASC',
+	    },
+	});
+
+	store.addFilter(Ext.create('Ext.util.Filter', {
+	    filterFn: item => item.data.path.replace(/(\/sdn\/zones\/(.*)\/(.*))\/[0-9]*$/, '$1') === me.path,
+	}));
+
+	let render_ugid = function(ugid, metaData, record) {
+	    if (record.data.type === 'group') {
+		return '@' + ugid;
+	    }
+
+	    return Ext.String.htmlEncode(ugid);
+	};
+
+	let render_vlan = function(path, metaData, record) {
+	    let vlan = 'any';
+	    const match = path.match(/(\/sdn\/zones\/)(.*)\/(.*)\/([0-9]*)$/);
+	    if (match) {
+		vlan = match[4];
+	    }
+
+	    return Ext.String.htmlEncode(vlan);
+	};
+
+	let columns = [
+	    {
+		header: gettext('User') + '/' + gettext('Group') + '/' + gettext('API Token'),
+		flex: 1,
+		sortable: true,
+		renderer: render_ugid,
+		dataIndex: 'ugid',
+	    },
+	    {
+		header: gettext('Role'),
+		flex: 1,
+		sortable: true,
+		dataIndex: 'roleid',
+	    },
+	    {
+		header: gettext('Vlan'),
+		flex: 1,
+		sortable: true,
+		renderer: render_vlan,
+		dataIndex: 'path',
+	    },
+	];
+
+
+	let sm = Ext.create('Ext.selection.RowModel', {});
+
+	let remove_btn = new Proxmox.button.Button({
+	    text: gettext('Remove'),
+	    disabled: true,
+	    selModel: sm,
+	    confirmMsg: gettext('Are you sure you want to remove this entry'),
+	    handler: function(btn, event, rec) {
+		var params = {
+		    'delete': 1,
+		    path: rec.data.path,
+		    roles: rec.data.roleid,
+		};
+		if (rec.data.type === 'group') {
+		    params.groups = rec.data.ugid;
+		} else if (rec.data.type === 'user') {
+		    params.users = rec.data.ugid;
+		} else if (rec.data.type === 'token') {
+		    params.tokens = rec.data.ugid;
+		} else {
+		    throw 'unknown data type';
+		}
+
+		Proxmox.Utils.API2Request({
+		    url: '/access/acl',
+		    params: params,
+		    method: 'PUT',
+		    waitMsgTarget: me,
+		    callback: () => store.load(),
+		    failure: response => Ext.Msg.alert(gettext('Error'), response.htmlStatus),
+		});
+	    },
+	});
+
+	Proxmox.Utils.monStoreErrors(me, store);
+
+	Ext.apply(me, {
+	    store: store,
+	    selModel: sm,
+	    tbar: [
+		{
+		    text: gettext('Add'),
+		    menu: {
+			xtype: 'menu',
+			items: [
+			    {
+				text: gettext('Group Permission'),
+				disabled: !me.path,
+				itemId: 'groupmenu',
+				iconCls: 'fa fa-fw fa-group',
+				handler: function() {
+				    var win = Ext.create('PVE.sdn.VnetACLAdd', {
+					aclType: 'group',
+					path: me.path,
+				    });
+				    win.on('destroy', () => store.load());
+				    win.show();
+				},
+			    },
+			    {
+				text: gettext('User Permission'),
+				disabled: !me.path,
+				itemId: 'usermenu',
+				iconCls: 'fa fa-fw fa-user',
+				handler: function() {
+				    var win = Ext.create('PVE.sdn.VnetACLAdd', {
+					aclType: 'user',
+					path: me.path,
+				    });
+				    win.on('destroy', () => store.load());
+				    win.show();
+				},
+			    },
+			    {
+				text: gettext('API Token Permission'),
+				disabled: !me.path,
+				itemId: 'tokenmenu',
+				iconCls: 'fa fa-fw fa-user-o',
+				handler: function() {
+				    let win = Ext.create('PVE.sdn.VnetACLAdd', {
+					aclType: 'token',
+					path: me.path,
+				    });
+				    win.on('destroy', () => store.load());
+				    win.show();
+				},
+			    },
+			],
+		    },
+		},
+		remove_btn,
+	    ],
+	    viewConfig: {
+		trackOver: false,
+	    },
+	    columns: columns,
+	    listeners: {
+	    },
+	});
+
+	me.callParent();
+    },
+}, function() {
+    Ext.define('pve-acl-vnet', {
+	extend: 'Ext.data.Model',
+	fields: [
+	    'path', 'type', 'ugid', 'roleid',
+	    {
+		name: 'propagate',
+		type: 'boolean',
+	    },
+	],
+    });
+});
diff --git a/www/manager6/sdn/ZoneContentPanel.js b/www/manager6/sdn/ZoneContentPanel.js
new file mode 100644
index 00000000..5bb081bb
--- /dev/null
+++ b/www/manager6/sdn/ZoneContentPanel.js
@@ -0,0 +1,41 @@
+Ext.define('PVE.sdn.ZoneContentPanel', {
+    extend: 'Ext.panel.Panel',
+    alias: 'widget.pveSDNZoneContentPanel',
+
+    title: 'Vnet',
+
+    onlineHelp: 'pvesdn_config_vnet',
+
+    initComponent: function() {
+	var me = this;
+
+	var permissions_panel = Ext.createWidget('pveSDNVnetACLView', {
+	    title: gettext('Vnet Permissions'),
+	    region: 'center',
+	    border: false,
+	});
+
+	var vnetview_panel = Ext.createWidget('pveSDNZoneContentView', {
+	    title: 'Vnets',
+	    region: 'west',
+	    permissions_panel: permissions_panel,
+	    nodename: me.nodename,
+	    zone: me.zone,
+	    width: '50%',
+	    border: false,
+	    split: true,
+	});
+
+	Ext.apply(me, {
+	    layout: 'border',
+	    items: [vnetview_panel, permissions_panel],
+	    listeners: {
+		show: function() {
+		    permissions_panel.fireEvent('show', permissions_panel);
+		},
+	    },
+	});
+
+	me.callParent();
+    },
+});
diff --git a/www/manager6/sdn/ZoneContentView.js b/www/manager6/sdn/ZoneContentView.js
index 1ea65450..4bc92718 100644
--- a/www/manager6/sdn/ZoneContentView.js
+++ b/www/manager6/sdn/ZoneContentView.js
@@ -17,17 +17,15 @@ Ext.define('PVE.sdn.ZoneContentView', {
     initComponent: function() {
 	var me = this;
 
-	var nodename = me.pveSelNode.data.node;
-	if (!nodename) {
+	if (!me.nodename) {
 	    throw "no node name specified";
 	}
 
-	var zone = me.pveSelNode.data.sdn;
-	if (!zone) {
+	if (!me.zone) {
 	    throw "no zone ID specified";
 	}
 
-	var baseurl = "/nodes/" + nodename + "/sdn/zones/" + zone + "/content";
+	var baseurl = "/nodes/" + me.nodename + "/sdn/zones/" + me.zone + "/content";
 	var store = Ext.create('Ext.data.Store', {
 	    model: 'pve-sdnzone-content',
 	    groupField: 'content',
@@ -48,7 +46,6 @@ Ext.define('PVE.sdn.ZoneContentView', {
 	};
 
 	Proxmox.Utils.monStoreErrors(me, store);
-
 	Ext.apply(me, {
 	    store: store,
 	    selModel: sm,
@@ -79,11 +76,19 @@ Ext.define('PVE.sdn.ZoneContentView', {
 		    dataIndex: 'statusmsg',
 		},
 	    ],
-	    listeners: {
-		activate: reload,
-	    },
+            listeners: {
+                activate: reload,
+                show: reload,
+                select: function(_sm, rec) {
+                    let path = `/sdn/zones/${me.zone}/${rec.data.vnet}`;
+                    me.permissions_panel.setPath(path);
+                },
+                deselect: function() {
+                    me.permissions_panel.setPath(undefined);
+                },
+            },
 	});
-
+	store.load();
 	me.callParent();
     },
 }, function() {
-- 
2.30.2




^ permalink raw reply	[flat|nested] 12+ messages in thread

* [pve-devel] [PATCH v3 pve-manager 4/4] ui: add permissions management for "localnetwork" zone
  2023-06-06 13:19 [pve-devel] [PATCH-SERIE pve-access-control/pve-manager/pve-guest-common/qemu-server/pve-network] check permissions on local bridge Alexandre Derumier
                   ` (8 preceding siblings ...)
  2023-06-06 13:19 ` [pve-devel] [PATCH v3 pve-manager 3/4] ui: add vnet permissions panel Alexandre Derumier
@ 2023-06-06 13:19 ` Alexandre Derumier
  9 siblings, 0 replies; 12+ messages in thread
From: Alexandre Derumier @ 2023-06-06 13:19 UTC (permalink / raw)
  To: pve-devel

add a default virtual zone called 'localnetwork' in the ressource tree,
and handle permissions like a true sdn zone

(no conflict with true sdn zone is possible, as they have 8 characters max)

Signed-off-by: Alexandre Derumier <aderumier@odiso.com>
---
 www/manager6/sdn/ZoneContentView.js | 27 ++++++++++++++++++++++++++-
 1 file changed, 26 insertions(+), 1 deletion(-)

diff --git a/www/manager6/sdn/ZoneContentView.js b/www/manager6/sdn/ZoneContentView.js
index 4bc92718..2a3cbf52 100644
--- a/www/manager6/sdn/ZoneContentView.js
+++ b/www/manager6/sdn/ZoneContentView.js
@@ -26,6 +26,9 @@ Ext.define('PVE.sdn.ZoneContentView', {
 	}
 
 	var baseurl = "/nodes/" + me.nodename + "/sdn/zones/" + me.zone + "/content";
+	if (me.zone === 'localnetwork') {
+	    baseurl = "/nodes/" + me.nodename + "/network?type=any_local_bridge";
+	}
 	var store = Ext.create('Ext.data.Store', {
 	    model: 'pve-sdnzone-content',
 	    groupField: 'content',
@@ -95,7 +98,29 @@ Ext.define('PVE.sdn.ZoneContentView', {
     Ext.define('pve-sdnzone-content', {
 	extend: 'Ext.data.Model',
 	fields: [
-	    'vnet', 'status', 'statusmsg',
+	    {
+		name: 'iface',
+		convert: function(value, record) {
+		    //map local vmbr to vnet
+		    if (record.data.iface) {
+			record.data.vnet = record.data.iface;
+		    }
+		    return value;
+		},
+	    },
+	    {
+		name: 'comments',
+		convert: function(value, record) {
+		    //map local vmbr comments to vnet alias
+		    if (record.data.comments) {
+			record.data.alias = record.data.comments;
+		    }
+		    return value;
+		},
+	    },
+	    'vnet',
+	    'status',
+	    'statusmsg',
 	    {
 		name: 'text',
 		convert: function(value, record) {
-- 
2.30.2




^ permalink raw reply	[flat|nested] 12+ messages in thread

* [pve-devel] [PATCH v2 pve-access-control 2/3] rpcenvironnment: add check_sdn_bridge
  2023-06-07 12:03 [pve-devel] [PATCH-SERIE pve-access-control/pve-manager/pve-guest-common/qemu-server/pve-network] check permissions on local bridge Alexandre Derumier
@ 2023-06-07 12:03 ` Alexandre Derumier
  0 siblings, 0 replies; 12+ messages in thread
From: Alexandre Derumier @ 2023-06-07 12:03 UTC (permalink / raw)
  To: pve-devel

check if user have access to 1 vlan of the bridge
or the bridge itself

Signed-off-by: Alexandre Derumier <aderumier@odiso.com>
---
 src/PVE/RPCEnvironment.pm | 18 ++++++++++++++++++
 1 file changed, 18 insertions(+)

diff --git a/src/PVE/RPCEnvironment.pm b/src/PVE/RPCEnvironment.pm
index 8586938..e0a101f 100644
--- a/src/PVE/RPCEnvironment.pm
+++ b/src/PVE/RPCEnvironment.pm
@@ -324,6 +324,24 @@ sub check_full {
     }
 }
 
+sub check_sdn_bridge {
+    my ($self, $username, $zone, $bridge, $privs, $noerr) = @_;
+
+    my $path = "/sdn/zones/$zone/$bridge";
+    my $cfg = $self->{user_cfg};
+    my $bridge_acl = PVE::AccessControl::find_acl_tree_node($cfg->{acl_root}, $path);
+    if ($bridge_acl) {
+	my $vlans = $bridge_acl->{children};
+	for my $vlan (keys %$vlans) {
+	    my $vlanpath = "$path/$vlan";
+	    return 1 if $self->check_any($username, $vlanpath, $privs, $noerr);
+	}
+	# check access to bridge itself
+	return 1 if $self->check_any($username, $path, $privs, $noerr);
+    }
+    return;
+}
+
 sub check_user_enabled {
     my ($self, $user, $noerr) = @_;
 
-- 
2.30.2




^ permalink raw reply	[flat|nested] 12+ messages in thread

end of thread, other threads:[~2023-06-07 12:04 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-06-06 13:19 [pve-devel] [PATCH-SERIE pve-access-control/pve-manager/pve-guest-common/qemu-server/pve-network] check permissions on local bridge Alexandre Derumier
2023-06-06 13:19 ` [pve-devel] [PATCH v2 pve-access-control 1/3] access control: add /sdn/zones/<zone>/<vnet>/<vlan> path Alexandre Derumier
2023-06-06 13:19 ` [pve-devel] [PATCH v3 qemu-server 1/1] api2: add check_bridge_access for create/update/clone vm Alexandre Derumier
2023-06-06 13:19 ` [pve-devel] [PATCH v3 pve-manager 1/4] api2: network: check permissions for local bridges Alexandre Derumier
2023-06-06 13:19 ` [pve-devel] [PATCH pve-network 1/1] get_local_vnets: fix permission path && perm Alexandre Derumier
2023-06-06 13:19 ` [pve-devel] [PATCH pve-guest-common 1/1] helpers : add check_vnet_access Alexandre Derumier
2023-06-06 13:19 ` [pve-devel] [PATCH v3 pve-manager 2/4] api2: cluster: ressources: add "localnetwork" zone Alexandre Derumier
2023-06-06 13:19 ` [pve-devel] [PATCH v2 pve-access-control 2/3] rpcenvironnment: add check_sdn_bridge Alexandre Derumier
2023-06-06 13:19 ` [pve-devel] [PATCH v2 pve-access-control 3/3] add new SDN.use privilege in PVESDNUser role Alexandre Derumier
2023-06-06 13:19 ` [pve-devel] [PATCH v3 pve-manager 3/4] ui: add vnet permissions panel Alexandre Derumier
2023-06-06 13:19 ` [pve-devel] [PATCH v3 pve-manager 4/4] ui: add permissions management for "localnetwork" zone Alexandre Derumier
2023-06-07 12:03 [pve-devel] [PATCH-SERIE pve-access-control/pve-manager/pve-guest-common/qemu-server/pve-network] check permissions on local bridge Alexandre Derumier
2023-06-07 12:03 ` [pve-devel] [PATCH v2 pve-access-control 2/3] rpcenvironnment: add check_sdn_bridge Alexandre Derumier

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal