Skip to content
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

interfaces/apparmor: conditionally add explicit deny rules for ptrace #5980

Merged

Conversation

jdstrand
Copy link

@jdstrand jdstrand commented Oct 12, 2018

AppArmor deny rules cannot be undone by allow rules which makes deny
rules difficult to work with arbitrary combinations of interfaces.
Sometimes it is useful to suppress noisy denials and because that can
currently only be done with explicit deny rules, adding explicit deny
rules unconditionally makes it difficult for interfaces to be used in
combination. Define suppressPtraceTrace to allow an interface to request
suppression and define usesPtraceTrace to omit the explict deny rules
such that:

  if suppressPtraceTrace && !usesPtraceTrace {
      add 'deny ptrace (trace),'
  }

Add UsesPtraceTrace() and SuppressPtraceTrace() that can be called in
the interfaces to set the above and adjust addContent() to use the above
logic to conditionally add a single explicit deny ptrace (trace) rule.

Adjust system-observe and network-observe to suppress via the common
interface and browser-support to use the new SuppressPtraceTrace().

With the above, the policy is equivalent to what was generated before
(except we only have a single explicit denial rather than several when
the above 3 interfaces are all connected at once). Future PRs will
adjust other interfaces to use UsesPtraceTrace() as needed so these
interfaces can still ptrace but continue to use the other interfaces.

@chipaca
Copy link
Contributor

chipaca commented Oct 12, 2018

AppArmor deny rules cannot be undone by allow rules which makes deny
rules difficult to work with arbitrary combinations of interfaces.
Sometimes it is useful to suppress noisy denials and because that can
currently only be done with explicit deny rules, adding explicit deny
rules unconditionally makes it difficult for interfaces to be used in
combination. Define suppressPtraceTrace to allow an interface to request
suppression and define usesPtraceTrace to omit the explicit deny rules
such that:
  if suppressPtraceTrace && !usesPtraceTrace {
      add 'deny ptrace (trace),'
  }

Add UsesPtraceTrace() and SuppressPtraceTrace() that can be called in
the interfaces to set the above and adjust addContent() to use the above
logic to conditionally add a single explicit deny ptrace (trace) rule.

Adjust system-observe and network-observe to suppress via the common
interface and browser-support to use the new SuppressPtraceTrace().

With the above, the policy is equivalent to what was generated before
(except we only have a single explicit denial rather than several when
the above 3 interfaces are all connected at once). Future PRs will
adjust other interfaces to use UsesPtraceTrace() as needed so these
interfaces can still ptrace but continue to use the other interfaces.
Copy link
Collaborator

@zyga zyga left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM with some ideas on how to simplify the backend processing and a suggestion on naming the new functions and variables, IMO, more in the spirit of go naming. Please look at the question about jalimode though, I think this is a mistake given how jail mode was supposed to be a way to request strict.

@@ -419,12 +419,12 @@ func (b *Backend) deriveContent(spec *Specification, snapInfo *snap.Info, opts i
// Add profile for each app.
for _, appInfo := range snapInfo.Apps {
securityTag := appInfo.SecurityTag()
addContent(securityTag, snapInfo, opts, spec.SnippetForTag(securityTag), content)
addContent(securityTag, snapInfo, opts, spec.SnippetForTag(securityTag), content, spec)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we now pass both spec and securityTag into addContent we could make it call SnippetForTag internally. I can do this as a follow up, not to block this.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That sounds fine in a followup PR.

suppress: true,
expected: false,
},
// classic with jail, never suppress
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, wasn't classic with jail supposed to be the same as strict?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

@@ -477,3 +491,21 @@ func (spec *Specification) AddPermanentSlot(iface interfaces.Interface, slot *sn
}
return nil
}

// UsesPtraceTrace records when to omit explicit ptrace deny rules
func (spec *Specification) UsesPtraceTrace() {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would have expected UsesPtraceTrace to return bool and not set any internal state. How about calling the set UsePtraceTrace, SupressPtraceTrace. Since this is all a part of the package we could drop the getter functions entirely and just either expose a single public function that computes uses && !suppress or just access the fields directly.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree with zyga, this is a bit unusual in terms of go style. Most getters of the form "GetFoo() bool" are simply called "Foo() bool". Maybe something along the lines of what zyga suggested, e.g. http://paste.ubuntu.com/p/chSKC2jSBp/ ? This diff would add an explicit Set{Uses,Suppresses}PtraceTrace which simplifies some if/else. I kept the Get* to keep the diff small but I would like to rename them as zyga suggested. Happy to work on this fwiw.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm fine with reorganizing as requested. I'd prefer not to have Set* allow setting both ways because the interfaces will only need to call it with true. If they call it with anything else, then it could introduce difficult to diagnose bugs where sometimes the rule is added and sometimes not based on on interface connection ordering. Eg, interface foo uses SetSuppressPtraceTrace(true), then interface bar does SetSuppressPtraceTrace(false). We'll get different behavior depending on which one snapd decides to connect first. Unless you strongly feel otherwise, I'll leave the one-way behavior (but still name it Set*).

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@zyga - regarding dropping getter, I use them in common_test.go and felt this was cleaner.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I noticed but for such cases we can use export_test.go, though at this stage it's not a strong feeling.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@zyga - I recall having some trouble with mocking certain things in the PRs though I can't recall if this was one of them. If you don't have a strong feeling that it is a blocker, I suggest letting it in as is (it might be useful down the line). If it is strong enough for you wanting me to do a followup PR, I can.

@@ -566,6 +566,19 @@ var overlayRootSnippet = `
"###UPPERDIR###/{,**/}" r,
`

var ptraceTraceDenySnippet = `
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment is very nice and informational. Thank you! I love that you included the commit ID for the relevant mainline bug fix.

@@ -55,6 +55,9 @@ type commonInterface struct {
connectedSlotKModModules []string
permanentPlugKModModules []string
permanentSlotKModModules []string

usesPtraceTrace bool
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you follow my naming suggestion above this could become

usePtraceTrace bool
suppressPtraceTrace bool

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I chose 'usesPtraceTrace' since the interface is declaring it uses it. I chose 'suppressPtraceTrace' because the interface is saying "please supress the ptrace trace denials if you can". I can rename things if you still want.

@@ -106,6 +106,14 @@ execute: |
fi

if [ "$(snap debug confinement)" = strict ] ; then
if [ "$ALLOW_SANDBOX" = "false" ]; then
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for updating the spread tests to capture the improvement!

Copy link
Contributor

@mvo5 mvo5 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great, thanks for working on this! I have some inline ideas to make slightly more go-ish, happy to work on those and push to this (or a follow) if you like them.

@@ -477,3 +491,21 @@ func (spec *Specification) AddPermanentSlot(iface interfaces.Interface, slot *sn
}
return nil
}

// UsesPtraceTrace records when to omit explicit ptrace deny rules
func (spec *Specification) UsesPtraceTrace() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree with zyga, this is a bit unusual in terms of go style. Most getters of the form "GetFoo() bool" are simply called "Foo() bool". Maybe something along the lines of what zyga suggested, e.g. http://paste.ubuntu.com/p/chSKC2jSBp/ ? This diff would add an explicit Set{Uses,Suppresses}PtraceTrace which simplifies some if/else. I kept the Get* to keep the diff small but I would like to rename them as zyga suggested. Happy to work on this fwiw.

c.Assert(spec.GetSuppressPtraceTrace(), Equals, true)

// setting both, only uses is set
iface = &commonInterface{
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See above, not sure we need this special case.

@@ -86,6 +89,11 @@ func (iface *commonInterface) BeforePrepareSlot(slot *snap.SlotInfo) error {
}

func (iface *commonInterface) AppArmorConnectedPlug(spec *apparmor.Specification, plug *interfaces.ConnectedPlug, slot *interfaces.ConnectedSlot) error {
if iface.usesPtraceTrace {
spec.UsesPtraceTrace()
} else if iface.suppressPtraceTrace {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure why we need this? It seems like the apparmor/backend.go is doing the right thing if both are set. Not sure what this extra special case buys us. Or am I missing something?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did it this way because there is no point setting uses with suppress since uses always trumps suppress and figured the common interface should reflect best practices. Happy to change if you prefer.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should the interface complain that both are set? I think this is orthogonal to the problem at hand, just about internal API usability. I think I agree with mvo, it's probably better as a "bunt" pusher of options than extra logic.

I can reduce this in the follow-up PR.

@jdstrand
Copy link
Author

@mvo5 and @zyga - I updated the PR and responded to feedback. I think the is ready for another review.

@zyga
Copy link
Collaborator

zyga commented Oct 16, 2018

I think this is fine now but let me look quickly again.

Copy link
Collaborator

@zyga zyga left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, some comments for follow-up

@@ -553,6 +553,17 @@ func addContent(securityTag string, snapInfo *snap.Info, opts interfaces.Confine
tagSnippets += snippet
}
}

if !ignoreSnippets {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is fine, it is done after we've seen all the snippets for a given snap.

suppress: true,
expected: false,
},
// classic, never suppress
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice :-)

@@ -86,6 +89,11 @@ func (iface *commonInterface) BeforePrepareSlot(slot *snap.SlotInfo) error {
}

func (iface *commonInterface) AppArmorConnectedPlug(spec *apparmor.Specification, plug *interfaces.ConnectedPlug, slot *interfaces.ConnectedSlot) error {
if iface.usesPtraceTrace {
spec.UsesPtraceTrace()
} else if iface.suppressPtraceTrace {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should the interface complain that both are set? I think this is orthogonal to the problem at hand, just about internal API usability. I think I agree with mvo, it's probably better as a "bunt" pusher of options than extra logic.

I can reduce this in the follow-up PR.

@zyga zyga merged commit fa139d0 into snapcore:master Oct 16, 2018
@jdstrand
Copy link
Author

Thanks for the reviews and merge! :)

@jdstrand jdstrand deleted the conditionally-add-explicit-ptrace-deny-rules branch December 4, 2018 14:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
4 participants