Skip to content

Conversation

@charliepark
Copy link
Contributor

@charliepark charliepark commented Oct 28, 2025

This PR adds a new role, of a "limited collaborator". The role essentially has restrictions on networking resources, as specified below.

Backstory: We had a request for limitations that could be applied to certain users, in https://github.com/oxidecomputer/customer-support/issues/416. We explored an alternate option (#9227), involving a new silo-level restrict_network_actions flag in the database, but the complexity of that solution suggested there could be a better approach. @davepacheco suggested exploring an exclusively role-based approach via Polar rules, which this PR realizes.

The project.limited-collaborator role can read and list_children, but can not create_children, or modify (or delete) the following resources:

  • VPC
  • Subnet
  • Firewall Rule
  • Custom Router
  • Route
  • Internet Gateway (including attach/detach IP pools and IP addresses)

Limited Collaborators will still be allowed full create / modify / delete permissions on these resources:

  • Floating IP
  • Instance Network Interfaces

A quick note on silo-level permissions
silo.limited-collaborator has the same permissions as silo.viewer — both roles can list_children and read silo resources, and neither can modify or create_child. The key difference between the two is how they confer roles at the project level.

  • silo.viewer → grants project.viewer on all projects in the silo (read-only)
  • silo.limited-collaborator → grants project.limited-collaborator on all projects (can manage compute resources but not networking infrastructure)
  • silo.collaborator → grants project.admin on all projects (full control)

@charliepark charliepark marked this pull request as ready for review October 30, 2025 14:21
fleet-viewer ✘ ✔ ✘ ✔ ✘ ✘ ✘ ✘
silo1-admin ✘ ✔ ✔ ✔ ✔ ✔ ✔ ✔
silo1-collaborator ✘ ✔ ✔ ✔ ✘ ✘ ✔ ✘
silo1-limited-collaborator ✘ ✔ ✔ ✔ ✘ ✘ ✘ ✘
Copy link
Contributor Author

@charliepark charliepark Oct 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: a silo.limited-collaborator can not create projects, as our project-create sagas add default VPCs and other networking resources during project creation.

Copy link
Collaborator

@davepacheco davepacheco left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did not review the VPC integration test.

Comment on lines +148 to +152
// Creating a NIC doesn't create a child resource of the subnet itself;
// it creates a child of the Instance. We only need Read permission on
// the subnet to reference it. This allows limited-collaborators to
// create instances while still blocking them from modifying networking
// infrastructure.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the idea behind the original check was that you need to be able to allocate IPs on the subnet. That is: imagine a world where you might have privileges to use some subnets but not others. You'd need a privilege check that you can actually create an allocation in this subnet.

This cannot come up today since we don't support that level of granularity. But this code isn't supposed to be assuming that -- it's supposed to check the privileges it truly needs and let the policy deal with whether it should always have it or what.

I guess the right fix is to tweak the Polar rules for VpcSubnet to allow CreateChild even for limited collaborator. I imagine that's not worth doing right now given the urgency. This isn't a huge deal since it doesn't affect behavior right now and may not ever even come up, but it would be a surprising oversight if/when we ever did make this more fine-grained. Maybe file an issue?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will do; thank you

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@david-crespo
Copy link
Contributor

Probably not a blocker to merging, but these should be changed to do their authz check against VpcList.

pub(crate) async fn vpc_list(
&self,
opctx: &OpContext,
project_lookup: &lookup::Project<'_>,
pagparams: &PaginatedBy<'_>,
) -> ListResultVec<db::model::Vpc> {
let (.., authz_project) =
project_lookup.lookup_for(authz::Action::ListChildren).await?;
self.db_datastore.vpc_list(&opctx, &authz_project, pagparams).await
}

pub async fn vpc_list(
&self,
opctx: &OpContext,
authz_project: &authz::Project,
pagparams: &PaginatedBy<'_>,
) -> ListResultVec<Vpc> {
opctx.authorize(authz::Action::ListChildren, authz_project).await?;

@david-crespo
Copy link
Contributor

Heh ok now that you have conflicts requiring another commit anyway, probably makes sense to do that one.

# Conflicts:
#	nexus/db-queries/tests/output/authz-roles.out
This updates the authorization roles test output to include both:
- The silo1-limited-collaborator and silo1-proj1-limited-collaborator
  users from the current branch
- The SCIM actor changes from main

Generated with: EXPECTORATE=overwrite cargo nextest run -p nexus-db-queries test_iam_roles_behavior
@david-crespo
Copy link
Contributor

Here's a more useful diff of the authz-roles.out after the most recent changes showing that it's all good. 9324a17 redoes the entire file, so it's unreadable.

e7b0ac1...9324a17#diff-f6735adfe2905d8dd2b8eec202c8aaeec0286b2f4521845bde545132664a8302

diff --git a/nexus/db-queries/tests/output/authz-roles.out b/nexus/db-queries/tests/output/authz-roles.out
index ef2bb59e9a..c11031b873 100644
--- a/nexus/db-queries/tests/output/authz-roles.out
+++ b/nexus/db-queries/tests/output/authz-roles.out
@@ -336,7 +336,24 @@
   silo1-proj1-limited-collaborator  ✘  ✘  ✔  ✘  ✘  ✘  ✘  ✘
   silo1-proj1-viewer                ✘  ✘  ✔  ✘  ✘  ✘  ✘  ✘
   unauthenticated                   !  !  !  !  !  !  !  !
-  scim                              ✘  ✘  ✘  ✘  ✘  ✘  ✘  ✘
+  scim                              ✘  ✘  ✔  ✘  ✘  ✘  ✔  ✘
+
+resource: Silo "silo1": group list
+
+  USER                              Q  R LC RP  M MP CC  D
+  fleet-admin                       ✘  ✘  ✔  ✘  ✘  ✘  ✔  ✘
+  fleet-collaborator                ✘  ✘  ✔  ✘  ✘  ✘  ✘  ✘
+  fleet-viewer                      ✘  ✘  ✔  ✘  ✘  ✘  ✘  ✘
+  silo1-admin                       ✘  ✘  ✔  ✘  ✘  ✘  ✔  ✘
+  silo1-collaborator                ✘  ✘  ✔  ✘  ✘  ✘  ✘  ✘
+  silo1-limited-collaborator        ✘  ✘  ✔  ✘  ✘  ✘  ✘  ✘
+  silo1-viewer                      ✘  ✘  ✔  ✘  ✘  ✘  ✘  ✘
+  silo1-proj1-admin                 ✘  ✘  ✔  ✘  ✘  ✘  ✘  ✘
+  silo1-proj1-collaborator          ✘  ✘  ✔  ✘  ✘  ✘  ✘  ✘
+  silo1-proj1-limited-collaborator  ✘  ✘  ✔  ✘  ✘  ✘  ✘  ✘
+  silo1-proj1-viewer                ✘  ✘  ✔  ✘  ✘  ✘  ✘  ✘
+  unauthenticated                   !  !  !  !  !  !  !  !
+  scim                              ✘  ✘  ✔  ✘  ✘  ✘  ✔  ✘
 
 resource: SiloUser "silo1-user"
 
@@ -353,7 +370,7 @@
   silo1-proj1-limited-collaborator  ✘  ✔  ✔  ✔  ✘  ✘  ✘  ✘
   silo1-proj1-viewer                ✘  ✔  ✔  ✔  ✘  ✘  ✘  ✘
   unauthenticated                   !  !  !  !  !  !  !  !
-  scim                              ✘  ✘  ✘  ✘  ✘  ✘  ✘  ✘
+  scim                              ✘  ✔  ✘  ✔  ✔  ✔  ✘  ✔
 
 resource: SshKey "silo1-user-ssh-key"
 
@@ -370,7 +387,7 @@
   silo1-proj1-limited-collaborator  ✘  ✔  ✘  ✔  ✘  ✘  ✘  ✘
   silo1-proj1-viewer                ✘  ✔  ✘  ✔  ✘  ✘  ✘  ✘
   unauthenticated                   !  !  !  !  !  !  !  !
-  scim                              ✘  ✘  ✘  ✘  ✘  ✘  ✘  ✘
+  scim                              ✘  ✔  ✘  ✔  ✘  ✘  ✘  ✘
 
 resource: SiloGroup "silo1-group"
 
@@ -387,7 +404,7 @@
   silo1-proj1-limited-collaborator  ✘  ✔  ✔  ✔  ✘  ✘  ✘  ✘
   silo1-proj1-viewer                ✘  ✔  ✔  ✔  ✘  ✘  ✘  ✘
   unauthenticated                   !  !  !  !  !  !  !  !
-  scim                              ✘  ✘  ✘  ✘  ✘  ✘  ✘  ✘
+  scim                              ✘  ✔  ✘  ✔  ✔  ✔  ✘  ✔
 
 resource: SiloImage "silo1-silo-image"
 
@@ -1120,6 +1137,23 @@
   unauthenticated                   !  !  !  !  !  !  !  !
   scim                              ✘  ✘  ✘  ✘  ✘  ✘  ✘  ✘
 
+resource: Silo "silo2": group list
+
+  USER                              Q  R LC RP  M MP CC  D
+  fleet-admin                       ✘  ✘  ✔  ✘  ✘  ✘  ✔  ✘
+  fleet-collaborator                ✘  ✘  ✔  ✘  ✘  ✘  ✘  ✘
+  fleet-viewer                      ✘  ✘  ✔  ✘  ✘  ✘  ✘  ✘
+  silo1-admin                       ✘  ✘  ✘  ✘  ✘  ✘  ✘  ✘
+  silo1-collaborator                ✘  ✘  ✘  ✘  ✘  ✘  ✘  ✘
+  silo1-limited-collaborator        ✘  ✘  ✘  ✘  ✘  ✘  ✘  ✘
+  silo1-viewer                      ✘  ✘  ✘  ✘  ✘  ✘  ✘  ✘
+  silo1-proj1-admin                 ✘  ✘  ✘  ✘  ✘  ✘  ✘  ✘
+  silo1-proj1-collaborator          ✘  ✘  ✘  ✘  ✘  ✘  ✘  ✘
+  silo1-proj1-limited-collaborator  ✘  ✘  ✘  ✘  ✘  ✘  ✘  ✘
+  silo1-proj1-viewer                ✘  ✘  ✘  ✘  ✘  ✘  ✘  ✘
+  unauthenticated                   !  !  !  !  !  !  !  !
+  scim                              ✘  ✘  ✘  ✘  ✘  ✘  ✘  ✘
+
 resource: SiloUser "silo2-user"
 
   USER                              Q  R LC RP  M MP CC  D

Copy link
Contributor

@david-crespo david-crespo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice work!

@charliepark charliepark merged commit a6e111c into main Nov 3, 2025
17 checks passed
@charliepark charliepark deleted the restrict_networking_actions_5 branch November 3, 2025 23:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants