-
Notifications
You must be signed in to change notification settings - Fork 61
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
feat(daemon): use identities to auth API requests #434
Conversation
Let's do this one last. |
@benhoyt I think I want to review this one in isolation after the rest have been merged. |
the changes look good from the security point of view |
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.
Still reviewing, just some early comments.
// avoids holding the state lock for /v1/health in particular, which is | ||
// not good: https://github.com/canonical/pebble/pull/369 | ||
var user *UserState | ||
if _, isOpen := access.(OpenAccess); !isOpen { |
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 OK with this special case because it doesn't actually bypass the access checker. As long as we still actually call the access checker, we can have this optimisation.
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 should be fine. In case there's another implementation of AccessChecker
in the future that is not of concrete type OpenAccess
, but behaves the same, we will only "lose" the optimization (meaning we unnecessarily call userFromRequest
and populate user
properly), but it will still do the right thing.
Unrelated flaky test in internals/daemon
|
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.
@benhoyt, your work on this is much appreciated.
I've performed the QA that I think was suitable for this (integrating with my Juju PR) + testing the login directly + a series of stress tests on the unit tests (with and without -race).
I am happy to approve this now. I only had one thought around possible security violations that this change could bring and that is to do with revocation of privileges.
If you have a long running request, with an identity that had access to do an action (such as exec, logs, push, pull, etc.), then that action will go uninterrupted, despite changes to the access level for that identity.
We can address that in a future PR.
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.
Really nice!
Also move UID 0/daemon-uid checks to userFromRequest
@hpidcock That doesn't really concern me. If you were able to start a long running request, you had access at the time. |
This reverts commit bc64c76.
@thp-canonical Actually, after discussion with Harry, he was a bit concerned with that latest commit to always use Untrusted and a non-nil UserState. It may be good (in future) to distinguish between no auth and an untrusted identity. In addition, both of us had done manual testing on the previous iteration of the code, so any changes now are risky. We can always iterate further in future without user-facing changes. |
This adds the code to actually use the new state-based identities for API authorization and authentication (part of spec OP043).
The PR updates the daemon to actually use UserState (which now has access-level and UID feilds). The daemon now fetches the request's named identity if one exists, otherwise it populates UserState with default user based on the ucred UID. So UserState is always set -- this makes the access checkers simpler and the notices API code simpler.
If there is a named identity (a non-nil
UserState
), that identity is used, even if the "default UID-based auth" would let them in. So named identities trump the default ("uid==0 || uid==daemon_uid" means admin; any uid means read). For example, if you have a userbob: {access: untrusted, local: {user-id: 42}}
and are making a request with UID 42, it'll only work for untrusted/open endpoints (/v1/health
and/v1/system-info
), not the usual of any read API.