diff --git a/nexus/auth/src/authn/mod.rs b/nexus/auth/src/authn/mod.rs index 08b27b97737..dd388e2cc59 100644 --- a/nexus/auth/src/authn/mod.rs +++ b/nexus/auth/src/authn/mod.rs @@ -34,6 +34,8 @@ pub use nexus_db_fixed_data::user_builtin::USER_DB_INIT; pub use nexus_db_fixed_data::user_builtin::USER_EXTERNAL_AUTHN; pub use nexus_db_fixed_data::user_builtin::USER_INTERNAL_API; pub use nexus_db_fixed_data::user_builtin::USER_INTERNAL_READ; +pub use nexus_db_fixed_data::user_builtin::USER_OMDB; +pub use nexus_db_fixed_data::user_builtin::USER_OMDB_READ; pub use nexus_db_fixed_data::user_builtin::USER_SAGA_RECOVERY; pub use nexus_db_fixed_data::user_builtin::USER_SERVICE_BALANCER; @@ -171,6 +173,16 @@ impl Context { Context::context_for_builtin_user(USER_INTERNAL_READ.id) } + /// Returns an authenticated context for use by the debugger + pub fn omdb_user() -> Context { + Context::context_for_builtin_user(USER_OMDB.id) + } + + /// Returns an authenticated context for use by the debugger (read-only) + pub fn omdb_read() -> Context { + Context::context_for_builtin_user(USER_OMDB_READ.id) + } + /// Returns an authenticated context for use for authenticating external /// requests pub fn external_authn() -> Context { @@ -284,6 +296,8 @@ mod test { use super::USER_DB_INIT; use super::USER_INTERNAL_API; use super::USER_INTERNAL_READ; + use super::USER_OMDB; + use super::USER_OMDB_READ; use super::USER_SAGA_RECOVERY; use super::USER_SERVICE_BALANCER; use super::USER_TEST_PRIVILEGED; @@ -312,6 +326,14 @@ mod test { let actor = authn.actor().unwrap(); assert_eq!(actor.actor_id(), USER_INTERNAL_READ.id); + let authn = Context::omdb_user(); + let actor = authn.actor().unwrap(); + assert_eq!(actor.actor_id(), USER_OMDB.id); + + let authn = Context::omdb_read(); + let actor = authn.actor().unwrap(); + assert_eq!(actor.actor_id(), USER_OMDB_READ.id); + let authn = Context::external_authn(); let actor = authn.actor().unwrap(); assert_eq!(actor.actor_id(), USER_EXTERNAL_AUTHN.id); diff --git a/nexus/auth/src/authz/actor.rs b/nexus/auth/src/authz/actor.rs index f1ce2695ac5..98b5395b759 100644 --- a/nexus/auth/src/authz/actor.rs +++ b/nexus/auth/src/authz/actor.rs @@ -122,6 +122,15 @@ impl oso::PolarClass for AuthenticatedActor { }, "USER_INTERNAL_API", ) + .add_constant( + AuthenticatedActor { + actor_id: authn::USER_OMDB.id, + silo_id: None, + roles: RoleSet::new(), + silo_policy: None, + }, + "USER_OMDB", + ) .add_attribute_getter("silo", |a: &AuthenticatedActor| { a.silo_id.map(|silo_id| { super::Silo::new( diff --git a/nexus/auth/src/authz/omicron.polar b/nexus/auth/src/authz/omicron.polar index f9382401fdd..69cbbe82bdf 100644 --- a/nexus/auth/src/authz/omicron.polar +++ b/nexus/auth/src/authz/omicron.polar @@ -579,3 +579,6 @@ has_role(USER_DB_INIT: AuthenticatedActor, "admin", _silo: Silo); # Allow the internal API admin permissions on all silos. has_role(USER_INTERNAL_API: AuthenticatedActor, "admin", _silo: Silo); + +# Allow the debugger API admin permissions on all silos. +has_role(USER_OMDB: AuthenticatedActor, "admin", _silo: Silo); diff --git a/nexus/db-fixed-data/src/role_assignment.rs b/nexus/db-fixed-data/src/role_assignment.rs index 25b26786f8d..638a48d3e3d 100644 --- a/nexus/db-fixed-data/src/role_assignment.rs +++ b/nexus/db-fixed-data/src/role_assignment.rs @@ -48,6 +48,26 @@ pub static BUILTIN_ROLE_ASSIGNMENTS: Lazy> = *FLEET_ID, role_builtin::FLEET_VIEWER.role_name, ), + // The OMDB user gets the "admin" role on the Fleet. + // This is needed to access siloed resources from all silos instead + // of reimplementing authenticated queries specifically for OMDB's use + RoleAssignment::new( + IdentityType::UserBuiltin, + user_builtin::USER_OMDB.id, + role_builtin::FLEET_ADMIN.resource_type, + *FLEET_ID, + role_builtin::FLEET_ADMIN.role_name, + ), + // The "USER_OMDB_READ" user gets the "viewer" role on the fleet. This is + // needed as a user separate from the read-write OMDB to avoid destructive + // actions unless forced to. + RoleAssignment::new( + IdentityType::UserBuiltin, + user_builtin::USER_OMDB_READ.id, + role_builtin::FLEET_VIEWER.resource_type, + *FLEET_ID, + role_builtin::FLEET_VIEWER.role_name, + ), // The "external-authenticator" user gets the "authenticator" role // on the sole fleet. This grants them the ability to create // sessions. diff --git a/nexus/db-fixed-data/src/user_builtin.rs b/nexus/db-fixed-data/src/user_builtin.rs index 1e968026831..ab1aa2711d7 100644 --- a/nexus/db-fixed-data/src/user_builtin.rs +++ b/nexus/db-fixed-data/src/user_builtin.rs @@ -68,6 +68,24 @@ pub static USER_INTERNAL_READ: Lazy = Lazy::new(|| { ) }); +/// Internal user used by OMDB +pub static USER_OMDB: Lazy = Lazy::new(|| { + UserBuiltinConfig::new_static( + "001de000-05e4-4000-8000-0000000009db", + "omdb-user", + "used by OMDB", + ) +}); + +/// Internal user used by OMDB to read privileged resources +pub static USER_OMDB_READ: Lazy = Lazy::new(|| { + UserBuiltinConfig::new_static( + "001de000-05e4-4000-8000-0000000003db", + "omdb-read", + "used by OMDB", + ) +}); + /// Internal user used by Nexus when recovering sagas pub static USER_SAGA_RECOVERY: Lazy = Lazy::new(|| { UserBuiltinConfig::new_static( @@ -94,6 +112,8 @@ mod test { use super::USER_EXTERNAL_AUTHN; use super::USER_INTERNAL_API; use super::USER_INTERNAL_READ; + use super::USER_OMDB; + use super::USER_OMDB_READ; use super::USER_SAGA_RECOVERY; use super::USER_SERVICE_BALANCER; @@ -104,6 +124,8 @@ mod test { assert_valid_uuid(&USER_INTERNAL_API.id); assert_valid_uuid(&USER_EXTERNAL_AUTHN.id); assert_valid_uuid(&USER_INTERNAL_READ.id); + assert_valid_uuid(&USER_OMDB.id); + assert_valid_uuid(&USER_OMDB_READ.id); assert_valid_uuid(&USER_SAGA_RECOVERY.id); } } diff --git a/nexus/db-queries/src/db/datastore/silo_user.rs b/nexus/db-queries/src/db/datastore/silo_user.rs index 2825e2a310f..70bb4913753 100644 --- a/nexus/db-queries/src/db/datastore/silo_user.rs +++ b/nexus/db-queries/src/db/datastore/silo_user.rs @@ -366,6 +366,8 @@ impl DataStore { &authn::USER_SERVICE_BALANCER, &authn::USER_INTERNAL_API, &authn::USER_INTERNAL_READ, + &authn::USER_OMDB, + &authn::USER_OMDB_READ, &authn::USER_EXTERNAL_AUTHN, &authn::USER_SAGA_RECOVERY, ] diff --git a/nexus/tests/integration_tests/users_builtin.rs b/nexus/tests/integration_tests/users_builtin.rs index 3df709c7f39..0b5f3182c96 100644 --- a/nexus/tests/integration_tests/users_builtin.rs +++ b/nexus/tests/integration_tests/users_builtin.rs @@ -37,6 +37,10 @@ async fn test_users_builtin(cptestctx: &ControlPlaneTestContext) { assert_eq!(u.identity.id, authn::USER_INTERNAL_API.id); let u = users.remove(&authn::USER_INTERNAL_READ.name.to_string()).unwrap(); assert_eq!(u.identity.id, authn::USER_INTERNAL_READ.id); + let u = users.remove(&authn::USER_OMDB.name.to_string()).unwrap(); + assert_eq!(u.identity.id, authn::USER_OMDB.id); + let u = users.remove(&authn::USER_OMDB_READ.name.to_string()).unwrap(); + assert_eq!(u.identity.id, authn::USER_OMDB_READ.id); let u = users.remove(&authn::USER_EXTERNAL_AUTHN.name.to_string()).unwrap(); assert_eq!(u.identity.id, authn::USER_EXTERNAL_AUTHN.id); let u = users.remove(&authn::USER_SAGA_RECOVERY.name.to_string()).unwrap();