Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions common/src/api/external/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -925,6 +925,7 @@ pub enum ResourceType {
Alert,
AlertReceiver,
AllowList,
AuditLogEntry,
BackgroundTask,
BgpConfig,
BgpAnnounceSet,
Expand Down
9 changes: 9 additions & 0 deletions nexus/auth/src/authn/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,15 @@ impl Context {
&self.schemes_tried
}

/// If the user is authenticated, return the last scheme in the list of
/// schemes tried, which is the one that worked.
pub fn scheme_used(&self) -> Option<&SchemeName> {
match &self.kind {
Kind::Authenticated(..) => self.schemes_tried().last(),
Kind::Unauthenticated => None,
}
}

/// Returns an unauthenticated context for use internally
pub fn internal_unauthenticated() -> Context {
Context { kind: Kind::Unauthenticated, schemes_tried: vec![] }
Expand Down
60 changes: 59 additions & 1 deletion nexus/auth/src/authz/api_resources.rs
Original file line number Diff line number Diff line change
Expand Up @@ -407,8 +407,66 @@ impl AuthorizedResource for IpPoolList {
roleset: &'fut mut RoleSet,
) -> futures::future::BoxFuture<'fut, Result<(), Error>> {
// There are no roles on the IpPoolList, only permissions. But we still
// need to load the Fleet-related roles to verify that the actor's role
// on the Fleet (possibly conferred from a Silo role).
load_roles_for_resource_tree(&FLEET, opctx, authn, roleset).boxed()
}

fn on_unauthorized(
&self,
_: &Authz,
error: Error,
_: AnyActor,
_: Action,
) -> Error {
error
}

fn polar_class(&self) -> oso::Class {
Self::get_polar_class()
}
}

// Similar to IpPoolList, the audit log is a collection that doesn't exist in
// the database as an entity distinct from its children (IP pools, or in this
// case, audit log entries). We need a dummy resource here because we need
// something to hang permissions off of. We need to be able to create audit log
// children (entries) for login attempts, when there is no authenticated user,
// as well as for normal requests with an authenticated user. For retrieval, we
// want (to start out) to allow only fleet viewers to list children.

#[derive(Clone, Copy, Debug)]
pub struct AuditLog;

/// Singleton representing the [`AuditLog`] for authz purposes
pub const AUDIT_LOG: AuditLog = AuditLog;

impl Eq for AuditLog {}

impl PartialEq for AuditLog {
fn eq(&self, _: &Self) -> bool {
true
}
}

impl oso::PolarClass for AuditLog {
fn get_polar_class_builder() -> oso::ClassBuilder<Self> {
oso::Class::builder()
.with_equality_check()
.add_attribute_getter("fleet", |_: &AuditLog| FLEET)
}
}

impl AuthorizedResource for AuditLog {
fn load_roles<'fut>(
&'fut self,
opctx: &'fut OpContext,
authn: &'fut authn::Context,
roleset: &'fut mut RoleSet,
) -> futures::future::BoxFuture<'fut, Result<(), Error>> {
// There are no roles on the AuditLog, only permissions. But we still
// need to load the Fleet-related roles to verify that the actor has the
// "admin" role on the Fleet (possibly conferred from a Silo role).
// viewer role on the Fleet (possibly conferred from a Silo role).
load_roles_for_resource_tree(&FLEET, opctx, authn, roleset).boxed()
}

Expand Down
25 changes: 25 additions & 0 deletions nexus/auth/src/authz/omicron.polar
Original file line number Diff line number Diff line change
Expand Up @@ -441,6 +441,31 @@ has_relation(fleet: Fleet, "parent_fleet", ip_pool_list: IpPoolList)
has_permission(actor: AuthenticatedActor, "create_child", ip_pool: IpPool)
if silo in actor.silo and silo.fleet = ip_pool.fleet;

# Describes the policy for reading and writing the audit log
resource AuditLog {
permissions = [
"list_children", # retrieve audit log
"create_child", # create audit log entry
];

relations = { parent_fleet: Fleet };

# Fleet viewers can read the audit log
"list_children" if "viewer" on "parent_fleet";
}

# Any actor should be able to write to the audit log because we need to be able
# to write to the log from any request, authenticated or not. Audit log writes
# are always a byproduct of other operations: there are no endpoints that allow
# the user to write to the log deliberately. Note we use AuthenticatedActor
# because we don't really mean unauthenticated -- in the case of login
# operations, we use the external authenticator actor that creates the session
# to authorize the audit log write.
has_permission(_actor: AuthenticatedActor, "create_child", _audit_log: AuditLog);

has_relation(fleet: Fleet, "parent_fleet", audit_log: AuditLog)
if audit_log.fleet = fleet;

# Describes the policy for creating and managing web console sessions.
resource ConsoleSessionList {
permissions = [ "create_child" ];
Expand Down
1 change: 1 addition & 0 deletions nexus/auth/src/authz/oso_generic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ pub fn make_omicron_oso(log: &slog::Logger) -> Result<OsoInit, anyhow::Error> {
let classes = [
// Hand-written classes
Action::get_polar_class(),
AuditLog::get_polar_class(),
AnyActor::get_polar_class(),
AuthenticatedActor::get_polar_class(),
BlueprintConfig::get_polar_class(),
Expand Down
Loading
Loading