Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 65 additions & 9 deletions interfaces/builtin/microceph_support.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,15 @@

package builtin

import (
"fmt"

"github.com/snapcore/snapd/interfaces"
"github.com/snapcore/snapd/interfaces/apparmor"
"github.com/snapcore/snapd/interfaces/seccomp"
"github.com/snapcore/snapd/snap"
)

const microcephSupportSummary = `allows operating as the MicroCeph service`

const microcephSupportBaseDeclarationPlugs = `
Expand Down Expand Up @@ -58,14 +67,61 @@ const microcephSupportConnectedPlugAppArmor = `
deny /usr/bin/sudo x,
`

const microcephSupportConnectedPlugAppArmorUserIdentitySwitching = `
# Description: allow a confined SMB server to assume the identity of
# authenticated users when serving files.
capability setuid,
capability setgid,
`

const microcephSupportConnectedPlugSecCompUserIdentitySwitching = `
# smbd assumes the authenticated user's supplementary groups on every
# identity transition; the default template only allows the zero-length
# (clear groups) form of setgroups.
setgroups
setgroups32
`

type microcephSupportInterface struct {
commonInterface
}

func (iface *microcephSupportInterface) AppArmorConnectedPlug(spec *apparmor.Specification, plug *interfaces.ConnectedPlug, slot *interfaces.ConnectedSlot) error {
spec.AddSnippet(microcephSupportConnectedPlugAppArmor)

var userIdentitySwitching bool
_ = plug.Attr("user-identity-switching", &userIdentitySwitching)
if userIdentitySwitching {
spec.AddSnippet(microcephSupportConnectedPlugAppArmorUserIdentitySwitching)
}
return nil
}

func (iface *microcephSupportInterface) SecCompConnectedPlug(spec *seccomp.Specification, plug *interfaces.ConnectedPlug, slot *interfaces.ConnectedSlot) error {
var userIdentitySwitching bool
_ = plug.Attr("user-identity-switching", &userIdentitySwitching)
if userIdentitySwitching {
spec.AddSnippet(microcephSupportConnectedPlugSecCompUserIdentitySwitching)
}
return nil
}

func (iface *microcephSupportInterface) BeforePreparePlug(plug *snap.PlugInfo) error {
if v, ok := plug.Attrs["user-identity-switching"]; ok {
if _, ok = v.(bool); !ok {
return fmt.Errorf("microceph-support plug requires bool with 'user-identity-switching'")
}
}
return nil
}

func init() {
registerIface(&commonInterface{
name: "microceph-support",
summary: microcephSupportSummary,
implicitOnCore: true,
implicitOnClassic: true,
baseDeclarationSlots: microcephSupportBaseDeclarationSlots,
baseDeclarationPlugs: microcephSupportBaseDeclarationPlugs,
connectedPlugAppArmor: microcephSupportConnectedPlugAppArmor,
})
registerIface(&microcephSupportInterface{commonInterface{
name: "microceph-support",
summary: microcephSupportSummary,
implicitOnCore: true,
implicitOnClassic: true,
baseDeclarationSlots: microcephSupportBaseDeclarationSlots,
baseDeclarationPlugs: microcephSupportBaseDeclarationPlugs,
}})
}
93 changes: 88 additions & 5 deletions interfaces/builtin/microceph_support_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,16 +25,22 @@ import (
"github.com/snapcore/snapd/interfaces"
"github.com/snapcore/snapd/interfaces/apparmor"
"github.com/snapcore/snapd/interfaces/builtin"
"github.com/snapcore/snapd/interfaces/seccomp"
"github.com/snapcore/snapd/snap"
"github.com/snapcore/snapd/snap/snaptest"
"github.com/snapcore/snapd/testutil"
)

type MicrocephSupportInterfaceSuite struct {
iface interfaces.Interface
slotInfo *snap.SlotInfo
slot *interfaces.ConnectedSlot
plugInfo *snap.PlugInfo
plug *interfaces.ConnectedPlug
iface interfaces.Interface
slotInfo *snap.SlotInfo
slot *interfaces.ConnectedSlot
plugInfo *snap.PlugInfo
plug *interfaces.ConnectedPlug
identityPlugInfo *snap.PlugInfo
identityPlug *interfaces.ConnectedPlug
identityOffPlugInfo *snap.PlugInfo
identityOffPlug *interfaces.ConnectedPlug
}

var _ = Suite(&MicrocephSupportInterfaceSuite{
Expand All @@ -43,9 +49,26 @@ var _ = Suite(&MicrocephSupportInterfaceSuite{

const microcephSupportConsumerYaml = `name: consumer
version: 0
plugs:
smb-identity:
interface: microceph-support
user-identity-switching: true
apps:
app:
plugs: [microceph-support]
smbd:
plugs: [smb-identity]
`

const microcephSupportUserIdentitySwitchingFalseConsumerYaml = `name: consumer
version: 0
plugs:
smb-identity:
interface: microceph-support
user-identity-switching: false
apps:
smbd:
plugs: [smb-identity]
`

const microcephSupportCoreYaml = `name: core
Expand All @@ -57,6 +80,8 @@ slots:

func (s *MicrocephSupportInterfaceSuite) SetUpTest(c *C) {
s.plug, s.plugInfo = MockConnectedPlug(c, microcephSupportConsumerYaml, nil, "microceph-support")
s.identityPlug, s.identityPlugInfo = MockConnectedPlug(c, microcephSupportConsumerYaml, nil, "smb-identity")
s.identityOffPlug, s.identityOffPlugInfo = MockConnectedPlug(c, microcephSupportUserIdentitySwitchingFalseConsumerYaml, nil, "smb-identity")
s.slot, s.slotInfo = MockConnectedSlot(c, microcephSupportCoreYaml, nil, "microceph-support")
}

Expand All @@ -70,6 +95,8 @@ func (s *MicrocephSupportInterfaceSuite) TestSanitizeSlot(c *C) {

func (s *MicrocephSupportInterfaceSuite) TestSanitizePlug(c *C) {
c.Assert(interfaces.BeforePreparePlug(s.iface, s.plugInfo), IsNil)
c.Assert(interfaces.BeforePreparePlug(s.iface, s.identityPlugInfo), IsNil)
c.Assert(interfaces.BeforePreparePlug(s.iface, s.identityOffPlugInfo), IsNil)
}

func (s *MicrocephSupportInterfaceSuite) TestAppArmorSpec(c *C) {
Expand All @@ -79,6 +106,62 @@ func (s *MicrocephSupportInterfaceSuite) TestAppArmorSpec(c *C) {
c.Assert(spec.AddConnectedPlug(s.iface, s.plug, s.slot), IsNil)
c.Assert(spec.SecurityTags(), DeepEquals, []string{"snap.consumer.app"})
c.Assert(spec.SnippetForTag("snap.consumer.app"), testutil.Contains, "/sys/bus/rbd/add_single_major rwk, # add single major dev\n")
c.Assert(spec.SnippetForTag("snap.consumer.app"), Not(testutil.Contains), "capability setuid,")

// a plain plug adds no seccomp policy
seccompSpec := seccomp.NewSpecification(appSet)
c.Assert(seccompSpec.AddConnectedPlug(s.iface, s.plug, s.slot), IsNil)
c.Assert(seccompSpec.Snippets(), HasLen, 0)
}

func (s *MicrocephSupportInterfaceSuite) TestAppArmorSpecUserIdentitySwitching(c *C) {
appSet, err := interfaces.NewSnapAppSet(s.identityPlug.Snap(), nil)
c.Assert(err, IsNil)
spec := apparmor.NewSpecification(appSet)
c.Assert(spec.AddConnectedPlug(s.iface, s.identityPlug, s.slot), IsNil)
c.Assert(spec.SecurityTags(), DeepEquals, []string{"snap.consumer.smbd"})
c.Assert(spec.SnippetForTag("snap.consumer.smbd"), testutil.Contains, "capability setuid,")
c.Assert(spec.SnippetForTag("snap.consumer.smbd"), testutil.Contains, "capability setgid,")
c.Assert(spec.SnippetForTag("snap.consumer.smbd"), testutil.Contains, "/sys/bus/rbd/add_single_major rwk, # add single major dev\n")
}

func (s *MicrocephSupportInterfaceSuite) TestAppArmorSpecUserIdentitySwitchingFalse(c *C) {
appSet, err := interfaces.NewSnapAppSet(s.identityOffPlug.Snap(), nil)
c.Assert(err, IsNil)
spec := apparmor.NewSpecification(appSet)
c.Assert(spec.AddConnectedPlug(s.iface, s.identityOffPlug, s.slot), IsNil)
c.Assert(spec.SecurityTags(), DeepEquals, []string{"snap.consumer.smbd"})
c.Assert(spec.SnippetForTag("snap.consumer.smbd"), testutil.Contains, "/sys/bus/rbd/add_single_major rwk, # add single major dev\n")
c.Assert(spec.SnippetForTag("snap.consumer.smbd"), Not(testutil.Contains), "capability setuid,")
c.Assert(spec.SnippetForTag("snap.consumer.smbd"), Not(testutil.Contains), "capability setgid,")

// user-identity-switching: false behaves like an absent attribute for seccomp too
seccompSpec := seccomp.NewSpecification(appSet)
c.Assert(seccompSpec.AddConnectedPlug(s.iface, s.identityOffPlug, s.slot), IsNil)
c.Assert(seccompSpec.Snippets(), HasLen, 0)
}

func (s *MicrocephSupportInterfaceSuite) TestSecCompSpecUserIdentitySwitching(c *C) {
appSet, err := interfaces.NewSnapAppSet(s.identityPlug.Snap(), nil)
c.Assert(err, IsNil)
spec := seccomp.NewSpecification(appSet)
c.Assert(spec.AddConnectedPlug(s.iface, s.identityPlug, s.slot), IsNil)
c.Assert(spec.SecurityTags(), DeepEquals, []string{"snap.consumer.smbd"})
c.Assert(spec.SnippetForTag("snap.consumer.smbd"), testutil.Contains, "setgroups\n")
c.Assert(spec.SnippetForTag("snap.consumer.smbd"), testutil.Contains, "setgroups32\n")
}

func (s *MicrocephSupportInterfaceSuite) TestSanitizePlugUserIdentitySwitchingBad(c *C) {
const mockSnapYaml = `name: consumer
version: 0
plugs:
smb-identity:
interface: microceph-support
user-identity-switching: bad
`
info := snaptest.MockInfo(c, mockSnapYaml, nil)
plug := info.Plugs["smb-identity"]
c.Assert(interfaces.BeforePreparePlug(s.iface, plug), ErrorMatches, "microceph-support plug requires bool with 'user-identity-switching'")
}

func (s *MicrocephSupportInterfaceSuite) TestStaticInfo(c *C) {
Expand Down
Loading