Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

authz: protect various endpoints #790

Merged
merged 4 commits into from
Mar 23, 2022
Merged

authz: protect various endpoints #790

merged 4 commits into from
Mar 23, 2022

Conversation

davepacheco
Copy link
Collaborator

@davepacheco davepacheco commented Mar 18, 2022

This started as a small PR to protect a handful of miscellaneous unprotected endpoints (in categories that were too small for their own PR). But it grew much larger than expected. If it would be helpful for me to break this up, I can do that.

I annotated the changes with some context to help sort through them. Key ones:

  • Added a new "internal-read" user for reading control plane data from Nexus
  • Added new "fleet viewer" role for the new user
  • Added authz::Sled
  • Added protection for the following endpoints:
hardware_racks_get
hardware_racks_get_rack
hardware_sleds_get
hardware_sleds_get_sled
sagas_get
sagas_get_saga
timeseries_schema_get
updates_refresh

@@ -29,6 +29,7 @@ pub mod saga;

pub use crate::db::fixed_data::user_builtin::USER_DB_INIT;
pub use crate::db::fixed_data::user_builtin::USER_INTERNAL_API;
pub use crate::db::fixed_data::user_builtin::USER_INTERNAL_READ;
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

The changes in this file are all part of adding a new "internal-read" user (described later).

/// [`crate::context::OpContext::authorize()`]. You construct one of these
/// using [`Fleet::sled()`].
#[derive(Clone, Debug)]
pub struct Sled {
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I wanted to keep things simple and put sled permissions under read permission on the fleet, but that doesn't trigger the correct 404 behavior when you ask for a Sled that doesn't exist (because it would say the fleet doesn't exist). So then I wanted to use authz::FleetChild, but that doesn't include an id (and can't, because it's used for built-in roles that don't have one). So I created a real new struct for Sled.

Note that I want to refactor all the authz API resources to be more type-safe, but that'll be a bigger (future) change.

@@ -75,6 +75,7 @@ has_role(actor: AuthenticatedActor, "init", _resource: Database)
#
# - fleet.admin (superuser for the whole system)
# - fleet.collaborator (can create and own orgs)
# - fleet.viewer (can read fleet-wide data)
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I added this new "Fleet Viewer" role for the new "internal-read" user (described later).

artifact: UpdateAvailableArtifact,
) -> CreateResult<UpdateAvailableArtifact> {
opctx.authorize(authz::Action::Modify, &authz::FLEET).await?;
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

@iliana here's one of the couple of authz checks added for updates. I started as restrictive as possible: you basically have to have a near-superuser privilege on the fleet-level ("Fleet Collaborator" or "Fleet Admin") to have this permission. I imagine we'll want to rethink this but I hope this will work for now.

current_targets_role_version: i64,
) -> DeleteResult {
// We use the `targets_role_version` column in the table to delete any old rows, keeping
// the table in sync with the current copy of artifacts.json.
opctx.authorize(authz::Action::Modify, &authz::FLEET).await?;
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

CC @iliana

artifact: &UpdateArtifact,
) -> LookupResult<UpdateAvailableArtifact> {
opctx.authorize(authz::Action::Read, &authz::FLEET).await?;
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

CC @iliana

@@ -47,6 +47,15 @@ lazy_static! {
"used by Nexus when handling internal API requests",
);

/// Internal user used by Nexus to read privileged control plane data
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Added a new built-in user called "internal-read". This is used by Nexus when it needs to read control plane data that an ordinary user wouldn't have access to. For example, when a user provisions an Instance (or really any time we need to make a request to a Sled Agent), we need to look up information about the Sled in the database. That's now protected by authz. But the end user making the request probably doesn't have permission to read information about sleds. Instead, Nexus uses this new "internal-read" user.

pagparams: &DataPageParams<'_, Uuid>,
) -> ListResult<db::model::Rack> {
opctx.authorize(authz::Action::Read, &authz::FLEET).await?;
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Racks are faked-up -- not in the database -- so their access check is right in nexus.rs.

nexus/src/nexus.rs Show resolved Hide resolved
&self,
opctx: &OpContext,
) -> Result<(), Error> {
opctx.authorize(authz::Action::Modify, &authz::FLEET).await?;
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

CC @iliana I put this check here to avoid querying the tuf repo unless the user is authorized. The database queries are separately authorized in datastore.rs.

Copy link
Contributor

Choose a reason for hiding this comment

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

I think this works for now. In the future, this code path will be called within Nexus when automatic updates are enabled (perhaps once every few hours, or some other regular cadence). What might this look like when we start doing that?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

For that, we could create an OpContext when Nexus starts up specifically for use to do automatic updates, similar to what we do for saga recovery:

omicron/nexus/src/nexus.rs

Lines 211 to 216 in c66d2f6

let opctx = OpContext::for_background(
log.new(o!("component" => "SagaRecoverer")),
Arc::clone(&authz),
authn::Context::internal_saga_recovery(),
Arc::clone(&db_datastore),
);

This uses OpContext::for_background:
pub fn for_background(

We could create a separate built-in user for that context that would only have whatever privileges are needed for it. Then whenever we did any of these operations, we'd use that background OpContext that's associated with that user.

@davepacheco davepacheco marked this pull request as ready for review March 19, 2022 00:04
&self,
opctx: &OpContext,
) -> Result<(), Error> {
opctx.authorize(authz::Action::Modify, &authz::FLEET).await?;
Copy link
Contributor

Choose a reason for hiding this comment

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

I think this works for now. In the future, this code path will be called within Nexus when automatic updates are enabled (perhaps once every few hours, or some other regular cadence). What might this look like when we start doing that?

@davepacheco davepacheco mentioned this pull request Mar 22, 2022
71 tasks
@davepacheco davepacheco enabled auto-merge (squash) March 23, 2022 18:03
@davepacheco
Copy link
Collaborator Author

Thanks for the reviews!

@davepacheco davepacheco merged commit 9102e10 into main Mar 23, 2022
@davepacheco davepacheco deleted the authz-misc branch March 23, 2022 18:47
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.

3 participants