Join GitHub today
GitHub is home to over 20 million developers working together to host and review code, manage projects, and build software together.
daemon: allow polkit authorisation to install/remove snaps #3797
Conversation
pedronis
requested a review
from
niemeyer
Aug 24, 2017
|
With this patch alone, I can install and remove packages via gnome-software without providing Ubuntu One account credentials. This is because gnome-software currently only performs a store login if it gets an access denied error from snapd. Assuming snapd gives a similar access denied error if we attempt to install a paid snap, this should give us a way to seemlessly upgrade the user to macaroon auth when they need it. If this branch is used in conjunction with #3795, then it will also be necessary to apply snapcore/snapd-glib#14 so that gnome-software indicates that user interaction is allowed. |
codecov-io
commented
Aug 25, 2017
•
Codecov Report
@@ Coverage Diff @@
## master #3797 +/- ##
==========================================
+ Coverage 75.71% 75.89% +0.17%
==========================================
Files 413 416 +3
Lines 35546 35988 +442
==========================================
+ Hits 26914 27313 +399
- Misses 6729 6755 +26
- Partials 1903 1920 +17
Continue to review full report at Codecov.
|
mvo5
reviewed
Aug 28, 2017
Code wise this looks fine (and it is amazingly small). We need a policy/architectural review from @niemeyer here.
zyga
approved these changes
Aug 30, 2017
One comment about GET requests. Perhaps I'm missing some context but it feels like not something that needs special casing. It should be a per-endpoint decision IMO.
Otherwise LGTM
| @@ -100,7 +100,7 @@ func (c *Command) canAccess(r *http.Request, user *auth.UserState) bool { | ||
| return true | ||
| } | ||
| - if c.PolkitOK != "" { | ||
| + if r.Method != "GET" && c.PolkitOK != "" { |
jhenstridge
Aug 31, 2017
Contributor
Without this change, commands like "snap list" may pop up an interactive dialog requesting authorisation. The command will still work if the user dismisses the dialog, but we don't want to unnecessarily prompt the user.
Part of the problem here is that there is only a single security policy per resource exported via the API, but in reality we're applying different policies to different request methods sent to that resource.
For example, while /v2/snaps entry has the UserOK flag set, the real policy is that GET is available to regular users while POST requires admin access. We want to avoid the polkit check completely for the GET request. I could probably tighten things up with something like !(r.Method == "GET" && (c.UserOK || c.GuestOK)), but that's encoding even more details about the rest of the policy.
Perhaps things would be clearer if the permissions were explicitly specified on a per request method basis?
jhenstridge
added some commits
Sep 1, 2017
|
I've reorganised canAccess() a bit based on @zyga's question about special casing GET. It now grants access for guest and user commands first (provided it is a GET request, to match the existing policy), and handles admin access last. This ensures we only hit the polkit code path if the request actually needs admin privileges. |
niemeyer
requested changes
Sep 4, 2017
Looks great, thanks!
Only question here is about the strategy for the permission IDs.
| - POST: postSnaps, | ||
| + Path: "/v2/snaps", | ||
| + UserOK: true, | ||
| + PolkitOK: "io.snapcraft.snapd.manage-snaps", |
niemeyer
Sep 4, 2017
Contributor
How fine grained do we plan this to be? Gut feeling is that "manage-snaps" might pretty much cover the entirety of snapd's functionality, and as such simply "manage" might be enough. Alternatively, do we want to distinguish installations/removals from configuration? If so, we probably need more specific names.
So, either something more specific, or if we plan for it to be widely scoped (which sounds fine in practice), perhaps simply "manage" might be enough.
Perhaps going wide is the sanest route for now, actually, as many of the actions are in fact different parameters on the request, so we'll need to change this model a bit if we want to be more fine grained.
jhenstridge
Sep 5, 2017
Contributor
If you want, I can change the name of the polkit action to something more specific (any particular preference?), and we can look at splitting the other operations off into other action IDs if needed.
I guess the main question is whether admins would want to grant some of these permissions to users but not others. For the policy defaults, I don't think it matters much though.
| - return true | ||
| + if c.PolkitOK != "" { | ||
| + allow, err := strconv.ParseBool(r.Header.Get(client.AllowInteractionHeader)) | ||
| + if err != nil { |
niemeyer
Sep 4, 2017
Contributor
Can we please fix the change performed in the prior PR just before merging? The point was to use the default behavior (false) when the header was missing entirely, but keep the original logging if an error happens due to an invalid header.
/cc @mvo5
jhenstridge
Sep 5, 2017
Contributor
I've pushed a fix for this to add back the logging on garbage values, while not logging when the header is missing.
mvo5
reviewed
Sep 5, 2017
Code looks very nice. Two nitpicks inside but feel free to ignore.
| + allowHeader := r.Header.Get(client.AllowInteractionHeader) | ||
| + if allowHeader != "" { | ||
| + if allow, err := strconv.ParseBool(allowHeader); err != nil { | ||
| + logger.Noticef("error parsing %s header: %s", client.AllowInteractionHeader, err) |
mvo5
Sep 5, 2017
Collaborator
Not sure we want this logger.Noticef() here, we could just silently ignore if the client sends us silly data to avoid spamming syslog to much.
jhenstridge
Sep 5, 2017
Contributor
Above, @niemeyer specifically asked for the logging to be added back (although only if the client actually sends the request header: not if it is missing entirely). I've pushed an update to the test to verify that we're only logging on a garbage value now.
This isn't the only way clients can cause us to write log messages, and it is probably better to know that there is a misbehaving client.
| @@ -248,6 +264,11 @@ func (s *daemonSuite) TestPolkitInteractivity(c *check.C) { | ||
| put.Header.Set(client.AllowInteractionHeader, "true") | ||
| c.Check(cmd.canAccess(put, nil), check.Equals, true) | ||
| c.Check(s.lastPolkitFlags, check.Equals, polkit.CheckAllowInteraction) | ||
| + | ||
| + // bad values are logged and treated as false |
mvo5
Sep 5, 2017
Collaborator
(nitpick) the comment is slightly misleading. AFAICT it does not test that the value is actually logged(?).
jhenstridge
added some commits
Sep 5, 2017
mvo5
added this to the 2.28 milestone
Sep 8, 2017
|
We need to squash merge this when it lands so that we can cherry pick it to 2.28. |
jhenstridge commentedAug 24, 2017
•
Edited 1 time
-
jhenstridge
Aug 24, 2017
This branch extends polkit authorisation support to cover managing snaps (install, remove, refresh, enable, disable, etc).
The intention is to allow gnome-software to install and remove snaps without having to create a store account, similar to what is possible on the command line once the user has proven that they have administrative access with sudo.
I needed to alter the
canAccessmethod to only perform the polkit authorisation check for non-GET requests, since the same API paths are used for non-privileged operations via GET (e.g.snap list,snap info, etc) and we don't want to prompt in those cases.Here are some ways to manually test this:
the following operations should succeed without any prompts:
the following commands should result in a prompt from polkit