From 077bb5f4aa3fce5e59bf9ee21f0365f0ca127036 Mon Sep 17 00:00:00 2001 From: Mark Laing Date: Mon, 11 Nov 2024 18:40:44 +0000 Subject: [PATCH 1/7] lxd/auth/drivers: Add entitlement for viewing unmanaged networks. Signed-off-by: Mark Laing --- lxd/auth/drivers/openfga_model.openfga | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lxd/auth/drivers/openfga_model.openfga b/lxd/auth/drivers/openfga_model.openfga index ddd27c9b8f49..c128dc9a6d34 100644 --- a/lxd/auth/drivers/openfga_model.openfga +++ b/lxd/auth/drivers/openfga_model.openfga @@ -136,6 +136,9 @@ type server # Grants permission to view warnings. define can_view_warnings: [identity, service_account, group#member] or admin or viewer + + # Grants permission to view unmanaged networks on the LXD host machines. + define can_view_unmanaged_networks: [identity, service_account, group#member] or admin or viewer type certificate relations define server: [server] From 85536922331d555a89c413565af77fdc11ea554d Mon Sep 17 00:00:00 2001 From: Mark Laing Date: Mon, 11 Nov 2024 18:41:10 +0000 Subject: [PATCH 2/7] lxd/auth: Runs `make update-auth`. Signed-off-by: Mark Laing --- lxd/auth/entitlements_generated.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lxd/auth/entitlements_generated.go b/lxd/auth/entitlements_generated.go index 6b4e5ae86c96..56053b195d1a 100644 --- a/lxd/auth/entitlements_generated.go +++ b/lxd/auth/entitlements_generated.go @@ -109,6 +109,9 @@ const ( // EntitlementCanViewWarnings is the "can_view_warnings" entitlement. It applies to the following entities: entity.TypeServer. EntitlementCanViewWarnings Entitlement = "can_view_warnings" + // EntitlementCanViewUnmanagedNetworks is the "can_view_unmanaged_networks" entitlement. It applies to the following entities: entity.TypeServer. + EntitlementCanViewUnmanagedNetworks Entitlement = "can_view_unmanaged_networks" + // EntitlementOperator is the "operator" entitlement. It applies to the following entities: entity.TypeInstance, entity.TypeProject. EntitlementOperator Entitlement = "operator" @@ -561,6 +564,8 @@ var EntityTypeToEntitlements = map[entity.Type][]Entitlement{ EntitlementCanViewMetrics, // Grants permission to view warnings. EntitlementCanViewWarnings, + // Grants permission to view unmanaged networks on the LXD host machines. + EntitlementCanViewUnmanagedNetworks, }, entity.TypeStorageBucket: { // Grants permission to edit the storage bucket. From 3bfe23704ca26ad092f83ec70b3ffb0e2d1f6012 Mon Sep 17 00:00:00 2001 From: Mark Laing Date: Mon, 11 Nov 2024 18:41:40 +0000 Subject: [PATCH 3/7] {doc,lxd/metadata}: Runs `make update-metadata`. Signed-off-by: Mark Laing --- doc/metadata.txt | 3 +++ lxd/metadata/configuration.json | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/doc/metadata.txt b/doc/metadata.txt index 8e753e76e403..1606cc3d5ed2 100644 --- a/doc/metadata.txt +++ b/doc/metadata.txt @@ -6331,6 +6331,9 @@ container or containers that use it. This allows using the `zfs` command in the `can_view_warnings` : Grants permission to view warnings. +`can_view_unmanaged_networks` +: Grants permission to view unmanaged networks on the LXD host machines. + diff --git a/lxd/metadata/configuration.json b/lxd/metadata/configuration.json index 8b2d6ebdecd8..70bf348ba562 100644 --- a/lxd/metadata/configuration.json +++ b/lxd/metadata/configuration.json @@ -7167,6 +7167,10 @@ { "name": "can_view_warnings", "description": "Grants permission to view warnings." + }, + { + "name": "can_view_unmanaged_networks", + "description": "Grants permission to view unmanaged networks on the LXD host machines." } ] }, From 03dc344eaba224d10fbc96a97692dfbcf8c3efa0 Mon Sep 17 00:00:00 2001 From: Mark Laing Date: Mon, 11 Nov 2024 18:42:23 +0000 Subject: [PATCH 4/7] lxd/auth: Allow restricted TLS clients to view unmanaged networks. Signed-off-by: Mark Laing --- lxd/auth/drivers/tls.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lxd/auth/drivers/tls.go b/lxd/auth/drivers/tls.go index bac5b2b98759..c5f8c2029953 100644 --- a/lxd/auth/drivers/tls.go +++ b/lxd/auth/drivers/tls.go @@ -164,7 +164,7 @@ func (t *tls) allowProjectUnspecificEntityType(entitlement auth.Entitlement, ent switch entityType { case entity.TypeServer: // Restricted TLS certificates have the following entitlements on server. - return shared.ValueInSlice(entitlement, []auth.Entitlement{auth.EntitlementCanViewResources, auth.EntitlementCanViewMetrics}) + return shared.ValueInSlice(entitlement, []auth.Entitlement{auth.EntitlementCanViewResources, auth.EntitlementCanViewMetrics, auth.EntitlementCanViewUnmanagedNetworks}) case entity.TypeIdentity: // If the entity URL refers to the identity that made the request, then the second path argument of the URL is // the identifier of the identity. This line allows the caller to view their own identity and no one else's. From 3fc7a9521fbce2cbfac9a08304f011719af85a2a Mon Sep 17 00:00:00 2001 From: Mark Laing Date: Mon, 11 Nov 2024 18:42:55 +0000 Subject: [PATCH 5/7] lxd: Handle authorization for unmanaged networks. Signed-off-by: Mark Laing --- lxd/networks.go | 52 ++++++++++++++++++++++++++++++++++--------------- 1 file changed, 36 insertions(+), 16 deletions(-) diff --git a/lxd/networks.go b/lxd/networks.go index 285294ceaff4..ad38a10783f0 100644 --- a/lxd/networks.go +++ b/lxd/networks.go @@ -241,11 +241,17 @@ func networksGet(d *Daemon, r *http.Request) response.Response { recursion := util.IsRecursionRequest(r) - var networkNames []string + // networks holds the network names of the managed and unmanaged networks. They are in two different slices so that + // we can perform access control checks differently. + var networks [2][]string + const ( + managed = iota + unmanaged + ) err = s.DB.Cluster.Transaction(r.Context(), func(ctx context.Context, tx *db.ClusterTx) error { // Get list of managed networks (that may or may not have network interfaces on the host). - networkNames, err = tx.GetNetworks(ctx, effectiveProjectName) + networks[managed], err = tx.GetNetworks(ctx, effectiveProjectName) return err }) @@ -253,8 +259,18 @@ func networksGet(d *Daemon, r *http.Request) response.Response { return response.InternalError(err) } - // Get list of actual network interfaces on the host as well if the effective project is Default. + // Get list of actual network interfaces on the host if the effective project is default and the caller has permission. + var getUnmanagedNetworks bool if effectiveProjectName == api.ProjectDefaultName { + err := s.Authorizer.CheckPermission(r.Context(), entity.ServerURL(), auth.EntitlementCanViewUnmanagedNetworks) + if err == nil { + getUnmanagedNetworks = true + } else if !auth.IsDeniedError(err) { + return response.SmartError(err) + } + } + + if getUnmanagedNetworks { ifaces, err := net.Interfaces() if err != nil { return response.InternalError(err) @@ -267,12 +283,13 @@ func networksGet(d *Daemon, r *http.Request) response.Response { } // Append to the list of networks if a managed network of same name doesn't exist. - if !shared.ValueInSlice(iface.Name, networkNames) { - networkNames = append(networkNames, iface.Name) + if !shared.ValueInSlice(iface.Name, networks[managed]) { + networks[unmanaged] = append(networks[unmanaged], iface.Name) } } } + // Permission checker works for managed networks only, since they are present in the database. userHasPermission, err := s.Authorizer.GetPermissionChecker(r.Context(), auth.EntitlementCanView, entity.TypeNetwork) if err != nil { return response.InternalError(err) @@ -280,20 +297,23 @@ func networksGet(d *Daemon, r *http.Request) response.Response { resultString := []string{} resultMap := []api.Network{} - for _, networkName := range networkNames { - if !userHasPermission(entity.NetworkURL(requestProjectName, networkName)) { - continue - } - - if !recursion { - resultString = append(resultString, fmt.Sprintf("/%s/networks/%s", version.APIVersion, networkName)) - } else { - net, err := doNetworkGet(s, r, s.ServerClustered, requestProjectName, reqProject.Config, networkName) - if err != nil { + for kind, networkNames := range networks { + for _, networkName := range networkNames { + // Filter out managed networks that the caller doesn't have permission to view. + if kind == managed && !userHasPermission(entity.NetworkURL(requestProjectName, networkName)) { continue } - resultMap = append(resultMap, net) + if !recursion { + resultString = append(resultString, fmt.Sprintf("/%s/networks/%s", version.APIVersion, networkName)) + } else { + net, err := doNetworkGet(s, r, s.ServerClustered, requestProjectName, reqProject.Config, networkName) + if err != nil { + continue + } + + resultMap = append(resultMap, net) + } } } From b66435f4bf34c131613869bbeb90dbfeebf917e7 Mon Sep 17 00:00:00 2001 From: Mark Laing Date: Tue, 12 Nov 2024 07:37:46 +0000 Subject: [PATCH 6/7] test/suites: Include new server entitlement in tests. Signed-off-by: Mark Laing --- test/suites/auth.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/suites/auth.sh b/test/suites/auth.sh index 537852994e91..08e607439b5d 100644 --- a/test/suites/auth.sh +++ b/test/suites/auth.sh @@ -166,7 +166,7 @@ effective_permissions: []" echo "${list_output}" | grep -Fq 'project,/1.0/projects/default,"can_create_image_aliases,can_create_images,can_create_instances,..."' list_output="$(lxc auth permission list entity_type=server --format csv --max-entitlements 0)" - echo "${list_output}" | grep -Fq 'server,/1.0,"admin,can_create_groups,can_create_identities,can_create_identity_provider_groups,can_create_projects,can_create_storage_pools,can_delete_groups,can_delete_identities,can_delete_identity_provider_groups,can_delete_projects,can_delete_storage_pools,can_edit,can_edit_groups,can_edit_identities,can_edit_identity_provider_groups,can_edit_projects,can_edit_storage_pools,can_override_cluster_target_restriction,can_view_groups,can_view_identities,can_view_identity_provider_groups,can_view_metrics,can_view_permissions,can_view_privileged_events,can_view_projects,can_view_resources,can_view_warnings,permission_manager,project_manager,storage_pool_manager,viewer"' + echo "${list_output}" | grep -Fq 'server,/1.0,"admin,can_create_groups,can_create_identities,can_create_identity_provider_groups,can_create_projects,can_create_storage_pools,can_delete_groups,can_delete_identities,can_delete_identity_provider_groups,can_delete_projects,can_delete_storage_pools,can_edit,can_edit_groups,can_edit_identities,can_edit_identity_provider_groups,can_edit_projects,can_edit_storage_pools,can_override_cluster_target_restriction,can_view_groups,can_view_identities,can_view_identity_provider_groups,can_view_metrics,can_view_permissions,can_view_privileged_events,can_view_projects,can_view_resources,can_view_unmanaged_networks,can_view_warnings,permission_manager,project_manager,storage_pool_manager,viewer"' list_output="$(lxc auth permission list entity_type=project --format csv --max-entitlements 0)" echo "${list_output}" | grep -Fq 'project,/1.0/projects/default,"can_create_image_aliases,can_create_images,can_create_instances,can_create_network_acls,can_create_network_zones,can_create_networks,can_create_profiles,can_create_storage_buckets,can_create_storage_volumes,can_delete,can_delete_image_aliases,can_delete_images,can_delete_instances,can_delete_network_acls,can_delete_network_zones,can_delete_networks,can_delete_profiles,can_delete_storage_buckets,can_delete_storage_volumes,can_edit,can_edit_image_aliases,can_edit_images,can_edit_instances,can_edit_network_acls,can_edit_network_zones,can_edit_networks,can_edit_profiles,can_edit_storage_buckets,can_edit_storage_volumes,can_operate_instances,can_view,can_view_events,can_view_image_aliases,can_view_images,can_view_instances,can_view_metrics,can_view_network_acls,can_view_network_zones,can_view_networks,can_view_operations,can_view_profiles,can_view_storage_buckets,can_view_storage_volumes,image_alias_manager,image_manager,instance_manager,network_acl_manager,network_manager,network_zone_manager,operator,profile_manager,storage_bucket_manager,storage_volume_manager,viewer"' From 152584e0ab207c0c0df8bb5ed355750e9b6fd7ac Mon Sep 17 00:00:00 2001 From: Mark Laing Date: Tue, 12 Nov 2024 07:39:00 +0000 Subject: [PATCH 7/7] test/suites: Test that a server administrator can view unmanaged networks. Signed-off-by: Mark Laing --- test/suites/auth.sh | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test/suites/auth.sh b/test/suites/auth.sh index 08e607439b5d..498c711b64d3 100644 --- a/test/suites/auth.sh +++ b/test/suites/auth.sh @@ -448,6 +448,11 @@ user_is_server_admin() { lxc_remote storage set "${remote}:test-pool" rsync.compression=true lxc_remote storage show "${remote}:test-pool" | grep -Fq 'rsync.compression:' lxc_remote storage delete "${remote}:test-pool" + + # Should be able to view all managed and unmanaged networks + host_networks="$(ip a | grep -P '^\d+:' | cut -d' ' -f2 | tr -d ':' | grep -vP '^veth.*' | sort)" + lxd_networks="$(lxc_remote query "${remote}:/1.0/networks?recursion=1" | jq -r '.[].name' | sort)" + [ "${host_networks}" = "${lxd_networks}" ] } user_is_server_operator() {