Skip to content

Commit

Permalink
Revert "sandbox/apparmor: do not skip ABI 4.0 from host parser (#14167)…
Browse files Browse the repository at this point in the history
…" (#14223)

This reverts commit fa03549.

We cannot use host AppArmor with 4.0 ABI as there's no control mechanism
to shield us from broken implementation of mqueue mediation class.

We look for the right version of apparmor parser and correctly not emit
the mqueue permission but since the host parser (4.0.0~beta3) looks at
host's ABI file which contains:

    ipc {posix_mqueue {create read write open delete setattr getattr}

And similarly the kernel supports posix_mqueue, then the parser (with
the bug or without the bug) will correctly not emit any permissions
related to mqueue mediation class, while emitting the mediation class
root element, causing the kernel to rightfully deny operations:

    [Mon Jul 22 12:43:40 2024] audit: type=1400 audit(1721652220.385:212):
    apparmor="DENIED" operation="unlink" class="posix_mqueue"
    profile="snap.docker.dockerd" name="/" pid=35290 comm="runc:[2:INIT]"
    requested="getattr" denied="getattr"class="posix_mqueue" fsuid=0 ouid=0

As such we need to do one of two things to allow host apparmor to be
used in a world with re-executing snapd:

 - Create our own ABI feature files that understand broken features and
   mask them, so that from the point of view of the kernel mqueue
   is _not_ mediated by the binary profile.
 - Detect presence of 4.0 ABI but ignore it on known-broken parser
   versions, effectively doing the same thing as the earlier approach
   but without creating a new ABI file that only snapd uses (possibly
   experiencing fewer bugs).

Signed-off-by: Zygmunt Krynicki <[email protected]>
  • Loading branch information
zyga authored Jul 23, 2024
1 parent a6a9a81 commit ae4884d
Show file tree
Hide file tree
Showing 2 changed files with 8 additions and 75 deletions.
9 changes: 8 additions & 1 deletion sandbox/apparmor/apparmor.go
Original file line number Diff line number Diff line change
Expand Up @@ -819,8 +819,15 @@ func AppArmorParser() (cmd *exec.Cmd, internal bool, err error) {
for _, dir := range filepath.SplitList(parserSearchPath) {
path := filepath.Join(dir, "apparmor_parser")
if _, err := os.Stat(path); err == nil {
// Detect but ignore apparmor 4.0 ABI support.
//
// At present this causes some bugs with mqueue mediation that can
// be avoided by pinning to 3.0 (which is also supported on
// apparmor 4). Once the mqueue issue is analyzed and fixed, this
// can be replaced with a --policy-features=hostAbi40File pin like
// we do below.
if fi, err := os.Lstat(hostAbi40File); err == nil && !fi.IsDir() {
return exec.Command(path, "--policy-features", hostAbi40File), false, nil
logger.Debugf("apparmor 4.0 ABI detected but ignored")
}

// Perhaps 3.0?
Expand Down
74 changes: 0 additions & 74 deletions sandbox/apparmor/apparmor_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,80 +75,6 @@ func (*apparmorSuite) TestAppArmorParser(c *C) {
c.Check(err, Equals, nil)
}

func (*apparmorSuite) TestAppArmorHostAppArmorParserWithJustAbi3(c *C) {
fakeroot := c.MkDir()
dirs.SetRootDir(fakeroot)

mockParserCmd := testutil.MockCommand(c, "apparmor_parser", "")
defer mockParserCmd.Restore()

restore := apparmor.MockParserSearchPath(mockParserCmd.BinDir())
defer restore()

restore = apparmor.MockSnapdAppArmorSupportsReexec(func() bool { return false })
defer restore()

abiDir := filepath.Join(fakeroot, "etc", "apparmor.d", "abi")
c.Assert(os.MkdirAll(abiDir, 0755), IsNil)
c.Assert(os.WriteFile(filepath.Join(abiDir, "3.0"), nil, 0755), IsNil)

cmd, internal, err := apparmor.AppArmorParser()
c.Check(err, IsNil)
c.Check(cmd.Path, Equals, mockParserCmd.Exe())
c.Check(cmd.Args, DeepEquals, []string{mockParserCmd.Exe(), "--policy-features", filepath.Join(abiDir, "3.0")})
c.Check(internal, Equals, false)
}

func (*apparmorSuite) TestAppArmorHostAppArmorParserWithAbi3And4(c *C) {
fakeroot := c.MkDir()
dirs.SetRootDir(fakeroot)

mockParserCmd := testutil.MockCommand(c, "apparmor_parser", "")
defer mockParserCmd.Restore()

restore := apparmor.MockParserSearchPath(mockParserCmd.BinDir())
defer restore()

restore = apparmor.MockSnapdAppArmorSupportsReexec(func() bool { return false })
defer restore()

abiDir := filepath.Join(fakeroot, "etc", "apparmor.d", "abi")
c.Assert(os.MkdirAll(abiDir, 0755), IsNil)
c.Assert(os.WriteFile(filepath.Join(abiDir, "3.0"), nil, 0755), IsNil)
c.Assert(os.WriteFile(filepath.Join(abiDir, "4.0"), nil, 0755), IsNil)

cmd, internal, err := apparmor.AppArmorParser()
c.Check(err, IsNil)
c.Check(cmd.Path, Equals, mockParserCmd.Exe())
// When both are present, ABI 4 is preferred.
c.Check(cmd.Args, DeepEquals, []string{mockParserCmd.Exe(), "--policy-features", filepath.Join(abiDir, "4.0")})
c.Check(internal, Equals, false)
}

func (*apparmorSuite) TestAppArmorHostAppArmorParserWithJustAbi4(c *C) {
fakeroot := c.MkDir()
dirs.SetRootDir(fakeroot)

mockParserCmd := testutil.MockCommand(c, "apparmor_parser", "")
defer mockParserCmd.Restore()

restore := apparmor.MockParserSearchPath(mockParserCmd.BinDir())
defer restore()

restore = apparmor.MockSnapdAppArmorSupportsReexec(func() bool { return false })
defer restore()

abiDir := filepath.Join(fakeroot, "etc", "apparmor.d", "abi")
c.Assert(os.MkdirAll(abiDir, 0755), IsNil)
c.Assert(os.WriteFile(filepath.Join(abiDir, "4.0"), nil, 0755), IsNil)

cmd, internal, err := apparmor.AppArmorParser()
c.Check(err, IsNil)
c.Check(cmd.Path, Equals, mockParserCmd.Exe())
c.Check(cmd.Args, DeepEquals, []string{mockParserCmd.Exe(), "--policy-features", filepath.Join(abiDir, "4.0")})
c.Check(internal, Equals, false)
}

func (*apparmorSuite) TestAppArmorInternalAppArmorParserAbi3(c *C) {
fakeroot := c.MkDir()
dirs.SetRootDir(fakeroot)
Expand Down

0 comments on commit ae4884d

Please sign in to comment.