-
Notifications
You must be signed in to change notification settings - Fork 56
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
chore(daemon): port AccessChecker interface from snapd #358
Conversation
a513795
to
aa74f21
Compare
This ports canonical/snapd#10126 from snapd, so that ucrednetGet() has the same return type. This change is in preparation for porting the accessChecker API from snapd (canonical/snapd#10127): #358
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.
Looking good! Let's just update from the master branch and resolve conflicts, add those tests, and I've commented on some minor issues.
I'd also like to get @flotter's review on this.
accessChecker
interface from snapdAccessChecker
interface from snapd
|
||
func (s *daemonSuite) TestLoggedInUserAccess(c *C) { |
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.
The TestLoggedInUserAccess
test was removed, as daemon.userFromRequest(...)
doesn't do anything yet, and will always return a nil
user state. We can re-add those test cases when an AccessChecker
is implemented that accesses the user
parameter.
I've left the user
parameter in AccessChecker.CheckAccess(...)
, as we do have a UserState
struct, and this might be useful for @hpidcock when implementing token support.
@benhoyt This is now ready for review. It ended up spilling into many areas. Let me know if you want me to split off any or all of those topics into separate PRs for easier review: Split off already: |
As part of working on #358, we found out that the untrusted socket is not used, and so can be removed (in preparation for porting the `AccessChecker` changes from snapd in #358). **Indicators that it's not used:** If we look at how `canAccess` works, if we match on `untrustedSocketPath` (`isUntrusted`), the only way for `canAccess` to allow the request is when `c.UntrustedOK` is `true` (otherwise it unconditionally returns `accessUnauthorized` immediately): ```golang if isUntrusted { if c.UntrustedOK { return accessOK } return accessUnauthorized } ``` So in order for any API calls to be allowed with the untrusted socket (assuming all API calls go through `canAccess`), we would need to have a `Command` defined with `UntrustedOK: true`. Checking the Pebble codebase, no such `Command` definition exists, which means that even if any application would use the untrusted socket currently, all API calls would return `accessUnauthorized` unconditionally for this socket. The untrusted socket as well as `UntrustedOK` in `Command` were already part of the initial import commit (50466ba), so seem to be an inheritance from snapd that haven't seen use in Pebble since then. The corresponding [snapd sources from around November 10th, 2020](https://github.com/snapcore/snapd/tree/e2581af241a941856a755035d816047ff9aa15d8) seem to call these [`SnapOK`](https://github.com/snapcore/snapd/blob/e2581af241a941856a755035d816047ff9aa15d8/daemon/daemon.go#L139) (`UntrustedOK`), [`dirs.SnapSocket`](https://github.com/snapcore/snapd/blob/e2581af241a941856a755035d816047ff9aa15d8/daemon/daemon.go#L160C23-L160C38) (`untrustedSocketPath`) and [`snapListener`](https://github.com/snapcore/snapd/blob/489358223f0bd03da01e62a4062174eb7e9e0ffa/daemon/daemon.go#L72) (`untrustedListener`). Due to `gofmt` and removal of struct members with the longest names, this PR is best reviewed with the "hide whitespace" option.
This brings the error response namings in line with snapd: [`daemon/errors.go` in snapd](https://github.com/snapcore/snapd/blob/489358223f0bd03da01e62a4062174eb7e9e0ffa/daemon/errors.go#L112). Split off from #358. While currently the error responses are used exclusively in the `daemon` package, once `AccessChecker` is available, it should be possible for other packages to create error responders to implement their `AccessChecker` variants.
@thp-canonical Can you please bring this branch up to date and resolve conflicts, now that #360 and #361 are merged? |
@benhoyt PR is now updated against the latest |
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 looks good -- just a couple of minor changes requested in the tests.
AccessChecker
interface from snapdAccessChecker
interface from snapd
AccessChecker
interface from snapdThere 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 for the tweaks. Looks good now -- let's just fix the bracing style.
I've also asked for @hpidcock's review, as he's pretty close to Pebble and has been part of these discussions. |
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'd retain the delete method as a write only method, but it should be allowed as it is a useful concept, even if we don't use it.
LGTM thank-you.
case "POST": | ||
rspf = c.POST | ||
case "DELETE": |
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.
What happened to DELETE here?
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.
Earlier I had suggested removing it as snapd removed it some time ago (and Pebble wasn't using it either). We can always add it back if we need it, but this was to be more in line with snapd.
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.
OK, happy for it to go, since it isn't used and inline with snapd.
Most of this was introduced in PR canonical#358, when we ported the AccessChecker changes from snapd, but accidentally set all the WriteAccess fields to UserAccess{} instead of AdminAccess{}. Previously there was a r.Method=="GET" check in Command.canAccess that handled this case. Additionally: - We lock down the files "pull" API to require admin. Even though it's a read (GET), this meant any user could potentially read sensitive files. - We lock down the task-websocket endpoint to admin. This is a GET endpoint, but these websockets are used by exec to send stdin/out/err and commands to the exec'd process, so they should require admin too. I've added some tests for these to ensure we don't accidentally change them in future, without noticing. How valuable these tests are I'm not sure, as they only cover a subset of the API endpoints, but it seems better than nothing.
Most of this was introduced in PR #358, when we ported the AccessChecker changes from snapd, but accidentally set all the WriteAccess fields to UserAccess{} instead of AdminAccess{}. Previously there was a r.Method=="GET" check in Command.canAccess that handled this case. Additionally: - We lock down the files "pull" API to require admin. Even though it's a read (GET), this meant any user could potentially read sensitive files. - We lock down the task-websocket endpoint to admin. This is a GET endpoint, but these websockets are used by exec to send stdin/out/err and commands to the exec'd process, so they should require admin too. I've added some tests for these to ensure we don't accidentally change them in future, without noticing. How valuable these tests are I'm not sure, as they only cover a subset of the API endpoints, but it seems better than nothing.
Most of this was introduced in PR #358, when we ported the AccessChecker changes from snapd, but accidentally set all the WriteAccess fields to UserAccess{} instead of AdminAccess{}. Previously there was a r.Method=="GET" check in Command.canAccess that handled this case. Additionally: - We lock down the files "pull" API to require admin. Even though it's a read (GET), this meant any user could potentially read sensitive files. - We lock down the task-websocket endpoint to admin. This is a GET endpoint, but these websockets are used by exec to send stdin/out/err and commands to the exec'd process, so they should require admin too. I've added some tests for these to ensure we don't accidentally change them in future, without noticing. How valuable these tests are I'm not sure, as they only cover a subset of the API endpoints, but it seems better than nothing.
Port the
accessChecker
API from snapd for easier customization and more fine-grained access control.UserState
is currently empty, but it might sense to drag it along (to be in line with snapd's function signature) and allow user-specific state data to be extracted from the HTTP request in a central place (userFromRequest()
indaemon.go
).