Skip to content

Commit ad8a2cc

Browse files
committed
2 parents cc364a8 + 6ab7e96 commit ad8a2cc

File tree

58 files changed

+2920
-196
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

58 files changed

+2920
-196
lines changed

Cargo.lock

Lines changed: 52 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -428,7 +428,7 @@ digest = "0.10.7"
428428
dns-server = { path = "dns-server" }
429429
dns-server-api = { path = "dns-server-api" }
430430
dns-service-client = { path = "clients/dns-service-client" }
431-
dpd-client = { git = "https://github.com/oxidecomputer/dendrite" }
431+
dpd-client = { git = "https://github.com/oxidecomputer/dendrite", rev = "6e666520620e26432333d9060bc7d249ba9fa92e" }
432432
dropshot = { version = "0.16.2", features = [ "usdt-probes" ] }
433433
dyn-clone = "1.0.19"
434434
either = "1.15.0"

common/src/api/external/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -925,6 +925,7 @@ pub enum ResourceType {
925925
Alert,
926926
AlertReceiver,
927927
AllowList,
928+
AuditLogEntry,
928929
BackgroundTask,
929930
BgpConfig,
930931
BgpAnnounceSet,

dev-tools/omdb/src/bin/omdb/db/ereport.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -369,6 +369,7 @@ async fn cmd_db_ereport_info(
369369
println!("\n{:=<80}", "== EREPORT ");
370370
serde_json::to_writer_pretty(std::io::stdout(), &report)
371371
.with_context(|| format!("failed to serialize ereport: {report:?}"))?;
372+
println!();
372373

373374
Ok(())
374375
}

dev-tools/reconfigurator-cli/tests/output/cmds-example-stdout

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1030,8 +1030,9 @@ Sled serial0
10301030
part number: model0
10311031
power: A2
10321032
revision: 0
1033-
MGS slot: Sled 0 (cubby 0)
1033+
MGS slot: Sled 0
10341034
found at: <REDACTED_TIMESTAMP> from fake MGS 1
1035+
host phase 1 active slot: A
10351036
host phase 1 hashes:
10361037
SLOT HASH
10371038
A 0101010101010101010101010101010101010101010101010101010101010101
@@ -1053,8 +1054,9 @@ Sled serial1
10531054
part number: model1
10541055
power: A2
10551056
revision: 0
1056-
MGS slot: Sled 1 (cubby 1)
1057+
MGS slot: Sled 1
10571058
found at: <REDACTED_TIMESTAMP> from fake MGS 1
1059+
host phase 1 active slot: A
10581060
host phase 1 hashes:
10591061
SLOT HASH
10601062
A 0101010101010101010101010101010101010101010101010101010101010101
@@ -1076,8 +1078,9 @@ Sled serial2
10761078
part number: model2
10771079
power: A2
10781080
revision: 0
1079-
MGS slot: Sled 2 (cubby 2)
1081+
MGS slot: Sled 2
10801082
found at: <REDACTED_TIMESTAMP> from fake MGS 1
1083+
host phase 1 active slot: A
10811084
host phase 1 hashes:
10821085
SLOT HASH
10831086
A 0101010101010101010101010101010101010101010101010101010101010101

nexus/auth/src/authn/mod.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,15 @@ impl Context {
151151
&self.schemes_tried
152152
}
153153

154+
/// If the user is authenticated, return the last scheme in the list of
155+
/// schemes tried, which is the one that worked.
156+
pub fn scheme_used(&self) -> Option<&SchemeName> {
157+
match &self.kind {
158+
Kind::Authenticated(..) => self.schemes_tried().last(),
159+
Kind::Unauthenticated => None,
160+
}
161+
}
162+
154163
/// Returns an unauthenticated context for use internally
155164
pub fn internal_unauthenticated() -> Context {
156165
Context { kind: Kind::Unauthenticated, schemes_tried: vec![] }

nexus/auth/src/authz/api_resources.rs

Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -407,8 +407,66 @@ impl AuthorizedResource for IpPoolList {
407407
roleset: &'fut mut RoleSet,
408408
) -> futures::future::BoxFuture<'fut, Result<(), Error>> {
409409
// There are no roles on the IpPoolList, only permissions. But we still
410+
// need to load the Fleet-related roles to verify that the actor's role
411+
// on the Fleet (possibly conferred from a Silo role).
412+
load_roles_for_resource_tree(&FLEET, opctx, authn, roleset).boxed()
413+
}
414+
415+
fn on_unauthorized(
416+
&self,
417+
_: &Authz,
418+
error: Error,
419+
_: AnyActor,
420+
_: Action,
421+
) -> Error {
422+
error
423+
}
424+
425+
fn polar_class(&self) -> oso::Class {
426+
Self::get_polar_class()
427+
}
428+
}
429+
430+
// Similar to IpPoolList, the audit log is a collection that doesn't exist in
431+
// the database as an entity distinct from its children (IP pools, or in this
432+
// case, audit log entries). We need a dummy resource here because we need
433+
// something to hang permissions off of. We need to be able to create audit log
434+
// children (entries) for login attempts, when there is no authenticated user,
435+
// as well as for normal requests with an authenticated user. For retrieval, we
436+
// want (to start out) to allow only fleet viewers to list children.
437+
438+
#[derive(Clone, Copy, Debug)]
439+
pub struct AuditLog;
440+
441+
/// Singleton representing the [`AuditLog`] for authz purposes
442+
pub const AUDIT_LOG: AuditLog = AuditLog;
443+
444+
impl Eq for AuditLog {}
445+
446+
impl PartialEq for AuditLog {
447+
fn eq(&self, _: &Self) -> bool {
448+
true
449+
}
450+
}
451+
452+
impl oso::PolarClass for AuditLog {
453+
fn get_polar_class_builder() -> oso::ClassBuilder<Self> {
454+
oso::Class::builder()
455+
.with_equality_check()
456+
.add_attribute_getter("fleet", |_: &AuditLog| FLEET)
457+
}
458+
}
459+
460+
impl AuthorizedResource for AuditLog {
461+
fn load_roles<'fut>(
462+
&'fut self,
463+
opctx: &'fut OpContext,
464+
authn: &'fut authn::Context,
465+
roleset: &'fut mut RoleSet,
466+
) -> futures::future::BoxFuture<'fut, Result<(), Error>> {
467+
// There are no roles on the AuditLog, only permissions. But we still
410468
// need to load the Fleet-related roles to verify that the actor has the
411-
// "admin" role on the Fleet (possibly conferred from a Silo role).
469+
// viewer role on the Fleet (possibly conferred from a Silo role).
412470
load_roles_for_resource_tree(&FLEET, opctx, authn, roleset).boxed()
413471
}
414472

nexus/auth/src/authz/omicron.polar

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -441,6 +441,31 @@ has_relation(fleet: Fleet, "parent_fleet", ip_pool_list: IpPoolList)
441441
has_permission(actor: AuthenticatedActor, "create_child", ip_pool: IpPool)
442442
if silo in actor.silo and silo.fleet = ip_pool.fleet;
443443

444+
# Describes the policy for reading and writing the audit log
445+
resource AuditLog {
446+
permissions = [
447+
"list_children", # retrieve audit log
448+
"create_child", # create audit log entry
449+
];
450+
451+
relations = { parent_fleet: Fleet };
452+
453+
# Fleet viewers can read the audit log
454+
"list_children" if "viewer" on "parent_fleet";
455+
}
456+
457+
# Any actor should be able to write to the audit log because we need to be able
458+
# to write to the log from any request, authenticated or not. Audit log writes
459+
# are always a byproduct of other operations: there are no endpoints that allow
460+
# the user to write to the log deliberately. Note we use AuthenticatedActor
461+
# because we don't really mean unauthenticated -- in the case of login
462+
# operations, we use the external authenticator actor that creates the session
463+
# to authorize the audit log write.
464+
has_permission(_actor: AuthenticatedActor, "create_child", _audit_log: AuditLog);
465+
466+
has_relation(fleet: Fleet, "parent_fleet", audit_log: AuditLog)
467+
if audit_log.fleet = fleet;
468+
444469
# Describes the policy for creating and managing web console sessions.
445470
resource ConsoleSessionList {
446471
permissions = [ "create_child" ];

nexus/auth/src/authz/oso_generic.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ pub fn make_omicron_oso(log: &slog::Logger) -> Result<OsoInit, anyhow::Error> {
101101
let classes = [
102102
// Hand-written classes
103103
Action::get_polar_class(),
104+
AuditLog::get_polar_class(),
104105
AnyActor::get_polar_class(),
105106
AuthenticatedActor::get_polar_class(),
106107
BlueprintConfig::get_polar_class(),

0 commit comments

Comments
 (0)