-
Notifications
You must be signed in to change notification settings - Fork 47
authz: first cut #346
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: first cut #346
Conversation
nexus/src/authz/oso_types.rs
Outdated
} | ||
} | ||
|
||
// Define newtypes for the various types that we want to impl PolarClass for. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure the best way to deal with this. What I've done here works, but is a bunch of boilerplate per-resource. However, the PolarClass
derive that Oso provides is pretty limited. For example, it doesn't call set_equality_check
for types that impl PartialEq
and Eq
. We may want to write our own proc macro and apply it directly to the database types.
This definitely needs some work but I'm interested in feedback from anybody who wants to take a look before I go too far down this path. (Maybe @smklein?) If it looks okay, I'll plan to add some tests, integrate it, and then iterate on it. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@davepacheco This looks really good. It nicely wraps up authz and serves as a good intro to Oso. I think it's fine to continue with.
My only partial concern is the createXXX
actions. Something about them feels wrong to me, but I could be off. Perhaps we could ask an Oso engineer about this.
# "admin" role on Organizations they create. They cannot necessarily even see | ||
# Organizations created by other Fleet administrators.) | ||
resource Fleet { | ||
permissions = [ "create_organization" ]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This permission seems overly specific to me, but you may be correct here, because I can't see how you could make it more general on the Fleet
resource. My gut tells me you want just a create
permission, perhaps on an organization, which would then be authorized by the parent fleet, but I'm not sure if that's possible.
I just haven't seen any specific examples in Oso docs that are this specific or that actually create things in polar. I just looked around for a bit.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, this is a good question. There's a lot about the policy I'm not sure about. The reason I went with separate "create" permissions is mainly for consistency with Projects, where you can create Instances, Disks, VPCs, and other things that someone might want to control separately.
nexus/src/authz/oso_types.rs
Outdated
/// Polar configuration describing control plane authorization rules | ||
pub const OMICRON_AUTHZ_CONFIG: &str = include_str!("omicron.polar"); | ||
|
||
/// Returns an Oso handle suitable for authorizing using to Omicron's |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
typo: using to
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed in 7ef635e.
tools/oxapi_demo
Outdated
@@ -107,6 +120,16 @@ function do_curl | |||
(set -o xtrace; curl -sSi "$OXAPI_URL$path" "$@" | json -ga) | |||
} | |||
|
|||
function do_curl_authz |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems like this should be do_curl_authn
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ha -- fair point. I thought of this as "do_curl for an endpoint that's protected by authorization", but yeah, it makes more sense to think of it as "do_curl with authn". Fixed in 7ef635e.
nexus/src/lib.rs
Outdated
@@ -13,6 +13,7 @@ | |||
#![allow(clippy::style)] | |||
|
|||
pub mod authn; // Public only for testing | |||
mod authz; // Public only for testing |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Old comment?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks! Fixed in 7ef635e.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks Dave! Oso is pretty cool from what I can see so far! (Barring the whole, needing to clone thing I suppose). Seems like a good starting point for authz support.
Thanks @andrewjstone @bnaecker and @luqmana for taking a look! I'll plan to sync up, add some tests, and integrate soon. |
I'm just about ready to land this. Relative to my last comment, I made these changes:
I'd be interested in folks' feedback on these parts too. At the same time, with people feeling above like the change is a step in the right direction, I think it's important to get this into "main" sooner rather than later. If you've got feedback on this after I land it, please let me know! |
.await | ||
}; | ||
let authn_header = | ||
http::HeaderValue::from_static(omicron_nexus::authn::TEST_USER_UUID); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is in keeping with making everything work the same and ignore authz, which is appropriate for now, but I have a feeling we'll eventually want to specify this from outside in tests, not build it into the post helper. We'll have to find a way to make it ergonomic in the caller. Could be as simple as adding a headers
arg here and making a headers
var at the top of the test to use a bunch of times (in cases where we don't want to specify anything unusual.)
I see you've done something like this with try_create_organization
. Might be worth trying to unify the two using an optional headers
arg. This is related to the general need for better test helpers. oxidecomputer/dropshot#165
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hm. Seeing now how it's used, I can see we rely on creating an org for setup practically everywhere, and we definitely don't want to worry about auth in those contexts. That gives me a couple more ideas:
- If we rely on orgs and projects in basically every test, I wonder if it makes sense to bundle up seeding some basic set of resources into a single helper, which would obviate the question of making it ergonomic to create orgs one at a time in tests.
- If we don't want to do that, guess we should make it a convention that unqualified
create_*
helpers will just work
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think these are good points, and I'd like to see some more examples (i.e., convert some more endpoints to use authz) before trying to decide on an answer.
nexus/src/authn/mod.rs
Outdated
/// User id reserved for a test user that's granted many privileges for the | ||
/// purpose of running automated tests. | ||
// "4007" looks a bit like "root". | ||
pub const TEST_USER_UUID: &str = "001de000-05e4-0000-0000-000000004007"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
love the first class test UUIDs. I'd rather have something in the name here about this being intended to be a privileged user. the fact that it's not UNPRIVILEGED
isn't enough unless you're seeing both together
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will do.
let apictx = rqctx.context(); | ||
let authn = Arc::new(apictx.external_authn.authn_request(rqctx).await?); | ||
let authz = | ||
authz::Context::new(Arc::clone(&authn), Arc::clone(&apictx.authz)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like the idea of bundling these two calls together. I want to confirm my understanding of the behavior around failing early due to authn. Failed
is an error, so we'll exit early thanks to the ?
, but Unauthenticated
is not:
omicron/nexus/src/authn/external/mod.rs
Line 76 in 986a8b7
Ok(authn::Context { kind: authn::Kind::Unauthenticated, schemes_tried }) |
That means we'll get to the authz check even if we don't have a user, which we need because there may be actions that are allowed for unauthenticated users, and if we don't let unauthenticated users through to the authz check they'll never be allowed to do anything. Correct?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that's all right.
|
||
created_instant: Instant, | ||
created_walltime: SystemTime, | ||
metadata: BTreeMap<String, String>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it'll be important to avoid relying on anything in here for real logic (if my understanding is correct that's it's meant only for logging)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, though it's still a bit vague to me. Right now, the intent is just to look at this in the debugger or expose it in a DTrace probe or something like that. At some point we may support authz based on various key-value conditions (that's part of RFD 43) and we may want to use this for that. But if so I think we'll want guard rails around that.
This change:
OpContext
than what was there. (I left out the part of prototype OpContext #331 that uses the OpContext in saga recovery, but I'd like to do that in a follow-on PR -- and have it actually authorize all of the database queries!)POST /organizations
, just as an exampleMore details:
OpContext
, with an easy way to construct one from a Dropshot request. This authenticates the request.OpContext::authorize
, which I think is an ergonomic way to do authz checksauthz::Context
, a per-operation authorization context that bundles anauthn::Context
andauthz::Authz
, the server-wide handle used for authorization (which is basically just a wrapper around Oso)For a quick summary of what it looks like to protect an endpoint, take a look at these diffs:
https://github.com/oxidecomputer/omicron/pull/346/files#diff-40c86e07f5a9940ab4c92d785e701e507538a87d7f3c19045ef47a3885ad27e5L230-R234
https://github.com/oxidecomputer/omicron/pull/346/files#diff-21bcb54baeab3bca25f4c5bd8a2754d6d9bedb7347995dc1e1cbc487673c64a3R397-R403
https://github.com/oxidecomputer/omicron/pull/346/files#diff-f5f8df6872a0c2cc4cbc022eb186649c66c8aee85c51329b639736a82424d305R147-R162
Tasks: