-
Notifications
You must be signed in to change notification settings - Fork 595
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
o/state,daemon: add snap-run-inhibit notice #13769
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -16,6 +16,7 @@ package daemon | |
|
||
import ( | ||
"context" | ||
"encoding/json" | ||
"errors" | ||
"fmt" | ||
"math" | ||
|
@@ -25,19 +26,23 @@ import ( | |
"github.com/snapcore/snapd/dirs" | ||
"github.com/snapcore/snapd/overlord/auth" | ||
"github.com/snapcore/snapd/overlord/state" | ||
"github.com/snapcore/snapd/snap/naming" | ||
"github.com/snapcore/snapd/strutil" | ||
) | ||
|
||
var noticeReadInterfaces = map[state.NoticeType][]string{ | ||
state.ChangeUpdateNotice: {"snap-refresh-observe"}, | ||
state.RefreshInhibitNotice: {"snap-refresh-observe"}, | ||
state.SnapRunInhibitNotice: {"snap-refresh-observe"}, | ||
} | ||
|
||
var ( | ||
noticesCmd = &Command{ | ||
Path: "/v2/notices", | ||
GET: getNotices, | ||
ReadAccess: interfaceOpenAccess{Interfaces: []string{"snap-refresh-observe"}}, | ||
Path: "/v2/notices", | ||
GET: getNotices, | ||
POST: postNotices, | ||
ReadAccess: interfaceOpenAccess{Interfaces: []string{"snap-refresh-observe"}}, | ||
WriteAccess: openAccess{}, | ||
} | ||
|
||
noticeCmd = &Command{ | ||
|
@@ -47,6 +52,12 @@ var ( | |
} | ||
) | ||
|
||
// addedNotice is the result of adding a new notice. | ||
type addedNotice struct { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This needs a docstring There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good point, thanks! updated |
||
// ID is the id of the newly added notice. | ||
ID string `json:"id"` | ||
} | ||
|
||
func getNotices(c *Command, r *http.Request, user *auth.UserState) Response { | ||
query := r.URL.Query() | ||
|
||
|
@@ -232,6 +243,71 @@ func allowedNoticeTypesForInterface(iface string) []state.NoticeType { | |
return types | ||
} | ||
|
||
func postNotices(c *Command, r *http.Request, user *auth.UserState) Response { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If at all possible, I'd like to avoid being able to post to notices, at least for There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Not really, Thank you for pointing this out, it means that I should document this better. |
||
requestUID, err := uidFromRequest(r) | ||
if err != nil { | ||
return Forbidden("cannot determine UID of request, so cannot create notice") | ||
} | ||
|
||
decoder := json.NewDecoder(r.Body) | ||
var inst noticeInstruction | ||
if err := decoder.Decode(&inst); err != nil { | ||
return BadRequest("cannot decode request body into notice instruction: %v", err) | ||
} | ||
|
||
st := c.d.overlord.State() | ||
st.Lock() | ||
defer st.Unlock() | ||
|
||
if err := inst.validate(r); err != nil { | ||
return err | ||
} | ||
|
||
noticeId, err := st.AddNotice(&requestUID, state.SnapRunInhibitNotice, inst.Key, nil) | ||
if err != nil { | ||
return InternalError("%v", err) | ||
} | ||
|
||
return SyncResponse(addedNotice{ID: noticeId}) | ||
} | ||
|
||
type noticeInstruction struct { | ||
Action string `json:"action"` | ||
Type state.NoticeType `json:"type"` | ||
Key string `json:"key"` | ||
// NOTE: Data and RepeatAfter fields are not needed for snap-run-inhibit notices. | ||
} | ||
|
||
func (inst *noticeInstruction) validate(r *http.Request) *apiError { | ||
if inst.Action != "add" { | ||
return BadRequest("invalid action %q", inst.Action) | ||
} | ||
if err := state.ValidateNotice(inst.Type, inst.Key, nil); err != nil { | ||
return BadRequest("%s", err) | ||
} | ||
|
||
switch inst.Type { | ||
case state.SnapRunInhibitNotice: | ||
return inst.validateSnapRunInhibitNotice(r) | ||
default: | ||
return BadRequest(`cannot add notice with invalid type %q (can only add "snap-run-inhibit" notices)`, inst.Type) | ||
} | ||
} | ||
|
||
func (inst *noticeInstruction) validateSnapRunInhibitNotice(r *http.Request) *apiError { | ||
if fromSnapCmd, err := isRequestFromSnapCmd(r); err != nil { | ||
return InternalError("cannot check request source: %v", err) | ||
} else if !fromSnapCmd { | ||
return Forbidden("only snap command can record notices") | ||
} | ||
|
||
if err := naming.ValidateInstance(inst.Key); err != nil { | ||
return BadRequest("invalid key: %v", err) | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func getNotice(c *Command, r *http.Request, user *auth.UserState) Response { | ||
requestUID, err := uidFromRequest(r) | ||
if err != nil { | ||
|
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 perhaps a different type of access, something like
snapClientAccess{}
might be appropriate here? Again, for the time being, we don't want to allow anyone outside of snapd/the snap client to be able to create notices. Your solution of checking/proc/<pid>/exe
works, but the endpoint beingopenAccess
suggests to me that anyone should be able to create notices of any type, neither of which are true.Something like
snapClientAccess
could implement the check you added below while maintaining that this behaves differently from the wayopenAccess
works in other places, (afaik) without restrictions about the caller (beyond it not being a snap).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 slightly disagree with forcing something like
snapClientAccess{}
onPOST /v2/notices
. We cannot rule out the possibility of having other notices that are generated outside thesnap
command.I think of it more as validation per notice type, it just happens that we have only one notice type for now.
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.
That's a good point. Furthermore, in the future, we may wish to allow snaps to create notices, in which case it would become
interfaceOpenAccess
, which is definitely not compatible with asnapClientAccess
. UsingopenAccess
as you suggest is analogous to the wayinterfaceOpenAccess
grants access only to particular notice types depending on the connected interface, so there is precedent as well.So +1 from me for your current approach. Thanks!
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.
How is this endpoint currently protected in pebble?
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.
@Meulengracht It is not protected, it uses an access checker that similar to
openAccess
.