authz: first cut#346
Conversation
| } | ||
| } | ||
|
|
||
| // Define newtypes for the various types that we want to impl PolarClass for. |
There was a problem hiding this comment.
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. |
andrewjstone
left a comment
There was a problem hiding this comment.
@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.
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.
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.
| /// 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 |
| (set -o xtrace; curl -sSi "$OXAPI_URL$path" "$@" | json -ga) | ||
| } | ||
|
|
||
| function do_curl_authz |
There was a problem hiding this comment.
Seems like this should be do_curl_authn?
There was a problem hiding this comment.
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.
| #![allow(clippy::style)] | ||
|
|
||
| pub mod authn; // Public only for testing | ||
| mod authz; // Public only for testing |
luqmana
left a comment
There was a problem hiding this comment.
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.
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.
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.
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.
| /// 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.
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
| 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.
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
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.
I think that's all right.
|
|
||
| created_instant: Instant, | ||
| created_walltime: SystemTime, | ||
| metadata: BTreeMap<String, String>, |
There was a problem hiding this comment.
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.
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:
OpContextthan 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::Contextandauthz::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: