From 2fa7ef54d9d4447d079bd7bda54c89675ec34876 Mon Sep 17 00:00:00 2001 From: Sergio Costas Rodriguez Date: Mon, 2 Sep 2024 12:40:24 +0200 Subject: [PATCH 1/9] audio-playback: Add support for snapped pipewire A snapped pipewire requires being able to create the pulse folder inside the global XDG_RUNTIME_DIR folder (not the snap own folder). Also, it requires to be able to create both the pipewire-[0-9]-manager and its lock file. This patch adds that capability to the audio-playback slot; thus, a snap offering that slot will be able to create the corresponding global sockets. --- interfaces/builtin/audio_playback.go | 9 +- interfaces/builtin/audio_playback_test.go | 4 - interfaces/builtin/pipewire.go | 151 ++++++++++++++++ interfaces/builtin/pipewire_test.go | 201 ++++++++++++++++++++++ 4 files changed, 353 insertions(+), 12 deletions(-) create mode 100644 interfaces/builtin/pipewire.go create mode 100644 interfaces/builtin/pipewire_test.go diff --git a/interfaces/builtin/audio_playback.go b/interfaces/builtin/audio_playback.go index 9215aa3d149..f6ed888eed0 100644 --- a/interfaces/builtin/audio_playback.go +++ b/interfaces/builtin/audio_playback.go @@ -116,14 +116,7 @@ owner /{,var/}run/pulse/** rwk, owner /run/user/[0-9]*/ r, owner /run/user/[0-9]*/pulse/ rw, - -# This allows to share screen in Core Desktop -owner /run/user/[0-9]*/pipewire-[0-9] rwk, - -# This allows wireplumber to read the pulseaudio -# configuration if pipewire runs inside a container -/etc/pulse/ r, -/etc/pulse/** r, +owner /run/user/[0-9]*/pulse/** rw, ` const audioPlaybackPermanentSlotSecComp = ` diff --git a/interfaces/builtin/audio_playback_test.go b/interfaces/builtin/audio_playback_test.go index c6222924271..b505a3c41d9 100644 --- a/interfaces/builtin/audio_playback_test.go +++ b/interfaces/builtin/audio_playback_test.go @@ -140,7 +140,6 @@ func (s *AudioPlaybackInterfaceSuite) TestSecCompOnClassic(c *C) { c.Assert(spec.AddPermanentSlot(s.iface, s.classicSlotInfo), IsNil) c.Assert(spec.SecurityTags(), HasLen, 0) - c.Assert(spec.SnippetForTag("snap.audio-playback.app1"), Not(testutil.Contains), "owner /run/user/[0-9]*/pipewire-[0-9] rwk,\n") c.Check(spec.SnippetForTag("snap.audio-playback.app1"), Not(testutil.Contains), "/etc/pulse/ r,\n") c.Check(spec.SnippetForTag("snap.audio-playback.app1"), Not(testutil.Contains), "/etc/pulse/** r,\n") } @@ -168,9 +167,6 @@ func (s *AudioPlaybackInterfaceSuite) TestAppArmor(c *C) { c.Assert(spec.AddPermanentSlot(s.iface, s.coreSlotInfo), IsNil) c.Assert(spec.SecurityTags(), DeepEquals, []string{"snap.audio-playback.app1"}) c.Check(spec.SnippetForTag("snap.audio-playback.app1"), testutil.Contains, "capability setuid,\n") - c.Assert(spec.SnippetForTag("snap.audio-playback.app1"), testutil.Contains, "owner /run/user/[0-9]*/pipewire-[0-9] rwk,\n") - c.Check(spec.SnippetForTag("snap.audio-playback.app1"), testutil.Contains, "/etc/pulse/ r,\n") - c.Check(spec.SnippetForTag("snap.audio-playback.app1"), testutil.Contains, "/etc/pulse/** r,\n") } func (s *AudioPlaybackInterfaceSuite) TestAppArmorOnClassic(c *C) { diff --git a/interfaces/builtin/pipewire.go b/interfaces/builtin/pipewire.go new file mode 100644 index 00000000000..aea4f29c51e --- /dev/null +++ b/interfaces/builtin/pipewire.go @@ -0,0 +1,151 @@ +// -*- Mode: Go; indent-tabs-mode: t -*- + +/* + * Copyright (C) 2018 Canonical Ltd + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package builtin + +import ( + "strings" + + "github.com/snapcore/snapd/interfaces" + "github.com/snapcore/snapd/interfaces/apparmor" + "github.com/snapcore/snapd/interfaces/seccomp" + "github.com/snapcore/snapd/snap" +) + +// The audio-playback interface is the companion interface to the audio-record +// interface. The design of this interface is based on the idea that the slot +// implementation (eg pulseaudio) is expected to query snapd on if the +// audio-record slot is connected or not and the audio service will mediate +// recording (ie, the rules below allow connecting to the audio service, but do +// not implement enforcement rules; it is up to the audio service to provide +// enforcement). If other audio recording servers require different security +// policy for record and playback (eg, a different socket path), then those +// accesses will be added to this interface. + +const pipewireSummary = `allows access to the pipewire sockets, and offer them` + +const pipewireBaseDeclarationSlots = ` + pipewire: + deny-auto-connection: true +` + +const pipewireConnectedPlugAppArmor = ` +# Allow communicating with pipewire service +/{run,dev}/shm/pulse-shm-* mrwk, + +owner /run/user/[0-9]*/ r, +owner /run/user/[0-9]*/pipewire-[0-9] rw, +owner /run/user/[0-9]*/pipewire-[0-9]-manager rw, +` + +const pipewireConnectedPlugAppArmorCore = ` +owner /run/user/[0-9]*/###SLOT_SECURITY_TAGS###/pipewire-[0-9] rw, +owner /run/user/[0-9]*/###SLOT_SECURITY_TAGS###/pipewire-[0-9]-manager rw, +` + +const pipewireConnectedPlugSecComp = ` +shmctl +` + +const pipewirePermanentSlotAppArmor = ` +capability sys_nice, +capability sys_resource, + +owner @{PROC}/@{pid}/exe r, +/etc/machine-id r, + +# For udev +network netlink raw, +/sys/devices/virtual/dmi/id/sys_vendor r, +/sys/devices/virtual/dmi/id/bios_vendor r, +/sys/**/sound/** r, + +# Shared memory based communication with clients +/{run,dev}/shm/pulse-shm-* mrwk, + +owner /run/user/[0-9]*/pipewire-[0-9] rwk, +owner /run/user/[0-9]*/pipewire-[0-9]-manager rwk, + +# This allows wireplumber to read the pulseaudio +# configuration if pipewire runs inside a container +/etc/pulse/ r, +/etc/pulse/** r, +` + +const pipewirePermanentSlotSecComp = ` +# The following are needed for UNIX sockets +personality +setpriority +bind +listen +accept +accept4 +shmctl +# libudev +socket AF_NETLINK - NETLINK_KOBJECT_UEVENT +` + +type pipewireInterface struct{} + +func (iface *pipewireInterface) Name() string { + return "pipewire" +} + +func (iface *pipewireInterface) StaticInfo() interfaces.StaticInfo { + return interfaces.StaticInfo{ + Summary: pipewireSummary, + ImplicitOnClassic: false, + ImplicitOnCore: false, + BaseDeclarationSlots: pipewireBaseDeclarationSlots, + } +} + +func (iface *pipewireInterface) AppArmorConnectedPlug(spec *apparmor.Specification, plug *interfaces.ConnectedPlug, slot *interfaces.ConnectedSlot) error { + spec.AddSnippet(pipewireConnectedPlugAppArmor) + if !implicitSystemConnectedSlot(slot) { + old := "###SLOT_SECURITY_TAGS###" + new := "snap." + slot.Snap().InstanceName() // forms the snap-instance-specific subdirectory name of /run/user/*/ used for XDG_RUNTIME_DIR + snippet := strings.Replace(pipewireConnectedPlugAppArmorCore, old, new, -1) + spec.AddSnippet(snippet) + } + return nil +} + +func (iface *pipewireInterface) AppArmorPermanentSlot(spec *apparmor.Specification, slot *snap.SlotInfo) error { + spec.AddSnippet(pipewirePermanentSlotAppArmor) + return nil +} + +func (iface *pipewireInterface) SecCompConnectedPlug(spec *seccomp.Specification, plug *interfaces.ConnectedPlug, slot *interfaces.ConnectedSlot) error { + spec.AddSnippet(pipewireConnectedPlugSecComp) + return nil +} + +func (iface *pipewireInterface) SecCompPermanentSlot(spec *seccomp.Specification, slot *snap.SlotInfo) error { + spec.AddSnippet(pipewirePermanentSlotSecComp) + return nil +} + +func (iface *pipewireInterface) AutoConnect(*snap.PlugInfo, *snap.SlotInfo) bool { + return true +} + +func init() { + registerIface(&pipewireInterface{}) +} diff --git a/interfaces/builtin/pipewire_test.go b/interfaces/builtin/pipewire_test.go new file mode 100644 index 00000000000..688aa1cc44c --- /dev/null +++ b/interfaces/builtin/pipewire_test.go @@ -0,0 +1,201 @@ +// -*- Mode: Go; indent-tabs-mode: t -*- + +/* + * Copyright (C) 2018 Canonical Ltd + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package builtin_test + +import ( + . "gopkg.in/check.v1" + + "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/interfaces/udev" + "github.com/snapcore/snapd/release" + "github.com/snapcore/snapd/snap" + "github.com/snapcore/snapd/testutil" +) + +type pipewireInterfaceSuite struct { + iface interfaces.Interface + coreSlotInfo *snap.SlotInfo + coreSlot *interfaces.ConnectedSlot + classicSlotInfo *snap.SlotInfo + classicSlot *interfaces.ConnectedSlot + plugInfo *snap.PlugInfo + plug *interfaces.ConnectedPlug +} + +var _ = Suite(&pipewireInterfaceSuite{ + iface: builtin.MustInterface("pipewire"), +}) + +const pipewireMockPlugSnapInfoYaml = `name: consumer +version: 1.0 +apps: + app: + command: foo + plugs: [pipewire] +` + +// a pipewire slot on a pipewire snap (as installed on a core/all-snap system) +const pipewireMockCoreSlotSnapInfoYaml = `name: pipewire +version: 1.0 +apps: + app1: + command: foo + slots: [pipewire] +` + +// a pipewire slot on the core snap (as automatically added on classic) +const pipewireMockClassicSlotSnapInfoYaml = `name: core +version: 0 +type: os +slots: + pipewire: + interface: pipewire +` + +func (s *pipewireInterfaceSuite) SetUpTest(c *C) { + s.coreSlot, s.coreSlotInfo = MockConnectedSlot(c, pipewireMockCoreSlotSnapInfoYaml, nil, "pipewire") + s.classicSlot, s.classicSlotInfo = MockConnectedSlot(c, pipewireMockClassicSlotSnapInfoYaml, nil, "pipewire") + s.plug, s.plugInfo = MockConnectedPlug(c, pipewireMockPlugSnapInfoYaml, nil, "pipewire") +} + +func (s *pipewireInterfaceSuite) TestName(c *C) { + c.Assert(s.iface.Name(), Equals, "pipewire") +} + +func (s *pipewireInterfaceSuite) TestSanitizeSlot(c *C) { + c.Assert(interfaces.BeforePrepareSlot(s.iface, s.coreSlotInfo), IsNil) + c.Assert(interfaces.BeforePrepareSlot(s.iface, s.classicSlotInfo), IsNil) +} + +func (s *pipewireInterfaceSuite) TestSanitizePlug(c *C) { + c.Assert(interfaces.BeforePreparePlug(s.iface, s.plugInfo), IsNil) +} + +func (s *pipewireInterfaceSuite) TestSecComp(c *C) { + restore := release.MockOnClassic(false) + defer restore() + + // connected plug to core slot + spec := seccomp.NewSpecification(s.plug.AppSet()) + c.Assert(spec.AddConnectedPlug(s.iface, s.plug, s.coreSlot), IsNil) + c.Assert(spec.SecurityTags(), DeepEquals, []string{"snap.consumer.app"}) + c.Assert(spec.SnippetForTag("snap.consumer.app"), testutil.Contains, "shmctl\n") + + // connected core slot to plug + spec = seccomp.NewSpecification(s.coreSlot.AppSet()) + c.Assert(spec.AddConnectedSlot(s.iface, s.plug, s.coreSlot), IsNil) + c.Assert(spec.SecurityTags(), HasLen, 0) + + // permanent core slot + spec = seccomp.NewSpecification(s.coreSlot.AppSet()) + c.Assert(spec.AddPermanentSlot(s.iface, s.coreSlotInfo), IsNil) + c.Assert(spec.SecurityTags(), DeepEquals, []string{"snap.pipewire.app1"}) + c.Assert(spec.SnippetForTag("snap.pipewire.app1"), testutil.Contains, "listen\n") +} + +func (s *pipewireInterfaceSuite) TestSecCompOnClassic(c *C) { + restore := release.MockOnClassic(true) + defer restore() + + // connected plug to classic slot + spec := seccomp.NewSpecification(s.plug.AppSet()) + c.Assert(spec.AddConnectedPlug(s.iface, s.plug, s.classicSlot), IsNil) + c.Assert(spec.SecurityTags(), DeepEquals, []string{"snap.consumer.app"}) + c.Check(spec.SnippetForTag("snap.consumer.app"), testutil.Contains, "shmctl\n") + + c.Assert(spec.SnippetForTag("snap.consumer.app"), Not(testutil.Contains), "owner /run/user/[0-9]*/snap.pipewire/pulse/ r,\n") + c.Assert(spec.SnippetForTag("snap.consumer.app"), Not(testutil.Contains), "owner /run/user/[0-9]*/snap.pipewire/pulse/native rwk,\n") + c.Assert(spec.SnippetForTag("snap.consumer.app"), Not(testutil.Contains), "owner /run/user/[0-9]*/snap.pipewire/pulse/pid r,\n") + + // connected classic slot to plug + spec = seccomp.NewSpecification(s.classicSlot.AppSet()) + c.Assert(spec.AddConnectedSlot(s.iface, s.plug, s.classicSlot), IsNil) + c.Assert(spec.SecurityTags(), HasLen, 0) + + // permanent classic slot + spec = seccomp.NewSpecification(s.classicSlot.AppSet()) + c.Assert(spec.AddPermanentSlot(s.iface, s.classicSlotInfo), IsNil) + c.Assert(spec.SecurityTags(), HasLen, 0) + + c.Assert(spec.SnippetForTag("snap.pipewire.app1"), Not(testutil.Contains), "owner /run/user/[0-9]*/pipewire-[0-9] rwk,\n") + c.Check(spec.SnippetForTag("snap.pipewire.app1"), Not(testutil.Contains), "/etc/pulse/ r,\n") + c.Check(spec.SnippetForTag("snap.pipewire.app1"), Not(testutil.Contains), "/etc/pulse/** r,\n") +} + +func (s *pipewireInterfaceSuite) TestAppArmor(c *C) { + restore := release.MockOnClassic(false) + defer restore() + + // connected plug to core slot + spec := apparmor.NewSpecification(s.plug.AppSet()) + c.Assert(spec.AddConnectedPlug(s.iface, s.plug, s.coreSlot), IsNil) + c.Assert(spec.SecurityTags(), DeepEquals, []string{"snap.consumer.app"}) + c.Check(spec.SnippetForTag("snap.consumer.app"), testutil.Contains, "/{run,dev}/shm/pulse-shm-* mrwk,\n") + c.Assert(spec.SnippetForTag("snap.consumer.app"), testutil.Contains, "owner /run/user/[0-9]*/snap.pipewire/pipewire-[0-9] rw,\n") + c.Assert(spec.SnippetForTag("snap.consumer.app"), testutil.Contains, "owner /run/user/[0-9]*/snap.pipewire/pipewire-[0-9]-manager rw,\n") + + // connected core slot to plug + spec = apparmor.NewSpecification(s.coreSlot.AppSet()) + c.Assert(spec.AddConnectedSlot(s.iface, s.plug, s.coreSlot), IsNil) + c.Assert(spec.SecurityTags(), HasLen, 0) + + // permanent core slot + spec = apparmor.NewSpecification(s.coreSlot.AppSet()) + c.Assert(spec.AddPermanentSlot(s.iface, s.coreSlotInfo), IsNil) + c.Assert(spec.SecurityTags(), DeepEquals, []string{"snap.pipewire.app1"}) + c.Assert(spec.SnippetForTag("snap.pipewire.app1"), testutil.Contains, "owner /run/user/[0-9]*/pipewire-[0-9] rwk,\n") + c.Assert(spec.SnippetForTag("snap.pipewire.app1"), testutil.Contains, "owner /run/user/[0-9]*/pipewire-[0-9]-manager rwk,\n") +} + +func (s *pipewireInterfaceSuite) TestAppArmorOnClassic(c *C) { + restore := release.MockOnClassic(true) + defer restore() + + // connected plug to classic slot + spec := apparmor.NewSpecification(s.plug.AppSet()) + c.Assert(spec.AddConnectedPlug(s.iface, s.plug, s.classicSlot), IsNil) + c.Assert(spec.SecurityTags(), DeepEquals, []string{"snap.consumer.app"}) + c.Check(spec.SnippetForTag("snap.consumer.app"), testutil.Contains, "/{run,dev}/shm/pulse-shm-* mrwk,\n") + c.Check(spec.SnippetForTag("snap.consumer.app"), testutil.Contains, "owner /run/user/[0-9]*/pipewire-[0-9] rw,\n") + c.Check(spec.SnippetForTag("snap.consumer.app"), testutil.Contains, "owner /run/user/[0-9]*/pipewire-[0-9]-manager rw,\n") + + // connected classic slot to plug + spec = apparmor.NewSpecification(s.classicSlot.AppSet()) + c.Assert(spec.AddConnectedSlot(s.iface, s.plug, s.classicSlot), IsNil) + c.Assert(spec.SecurityTags(), HasLen, 0) + + // permanent classic slot + spec = apparmor.NewSpecification(s.classicSlot.AppSet()) + c.Assert(spec.AddPermanentSlot(s.iface, s.classicSlotInfo), IsNil) + c.Assert(spec.SecurityTags(), HasLen, 0) +} + +func (s *pipewireInterfaceSuite) TestUDev(c *C) { + spec := udev.NewSpecification(s.coreSlot.AppSet()) + c.Assert(spec.AddPermanentSlot(s.iface, s.coreSlotInfo), IsNil) + c.Assert(spec.Snippets(), HasLen, 0) +} + +func (s *pipewireInterfaceSuite) TestInterfaces(c *C) { + c.Check(builtin.Interfaces(), testutil.DeepContains, s.iface) +} From 421c8e8579bbe6be23ab26685256164cf4252a1a Mon Sep 17 00:00:00 2001 From: Sergio Costas Rodriguez Date: Fri, 6 Sep 2024 15:25:26 +0200 Subject: [PATCH 2/9] Add global tests --- tests/lib/snaps/test-snapd-policy-app-consumer/meta/snap.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/lib/snaps/test-snapd-policy-app-consumer/meta/snap.yaml b/tests/lib/snaps/test-snapd-policy-app-consumer/meta/snap.yaml index 52febeb934b..dc0a3500cb4 100644 --- a/tests/lib/snaps/test-snapd-policy-app-consumer/meta/snap.yaml +++ b/tests/lib/snaps/test-snapd-policy-app-consumer/meta/snap.yaml @@ -188,6 +188,9 @@ apps: physical-memory-control: command: bin/run plugs: [ physical-memory-control ] + pipewire: + command: bin/run + plugs: [ pipewire ] pkcs11: command: bin/run plugs: [ pkcs11 ] From 532a8eb98dd51e67e7eeddbe7f69796db0a71974 Mon Sep 17 00:00:00 2001 From: Sergio Costas Rodriguez Date: Fri, 6 Sep 2024 15:29:33 +0200 Subject: [PATCH 3/9] fix extra tests --- .../test-snapd-policy-app-provider-classic/meta/snap.yaml | 4 ++++ .../test-snapd-policy-app-provider-core/meta/snap.yaml | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/tests/main/interfaces-many-snap-provided/test-snapd-policy-app-provider-classic/meta/snap.yaml b/tests/main/interfaces-many-snap-provided/test-snapd-policy-app-provider-classic/meta/snap.yaml index a24097a4e48..b8a5d64a5cf 100644 --- a/tests/main/interfaces-many-snap-provided/test-snapd-policy-app-provider-classic/meta/snap.yaml +++ b/tests/main/interfaces-many-snap-provided/test-snapd-policy-app-provider-classic/meta/snap.yaml @@ -15,6 +15,7 @@ slots: mpris: name: test-policy-app-provider-classic online-accounts-service: null + pipewire: null storage-framework-service: null thumbnailer-service: null ubuntu-download-manager: null @@ -56,6 +57,9 @@ apps: online-accounts-service: command: bin/run slots: [ online-accounts-service ] + pipewire: + command: bin/run + slots: [ pipewire ] storage-framework-service: command: bin/run slots: [ storage-framework-service ] diff --git a/tests/main/interfaces-many-snap-provided/test-snapd-policy-app-provider-core/meta/snap.yaml b/tests/main/interfaces-many-snap-provided/test-snapd-policy-app-provider-core/meta/snap.yaml index c5683492cef..f91da4dfc6e 100644 --- a/tests/main/interfaces-many-snap-provided/test-snapd-policy-app-provider-core/meta/snap.yaml +++ b/tests/main/interfaces-many-snap-provided/test-snapd-policy-app-provider-core/meta/snap.yaml @@ -22,6 +22,7 @@ slots: network-manager-observe: null ofono: null online-accounts-service: null + pipewire: null storage-framework-service: null thumbnailer-service: null ubuntu-download-manager: null @@ -79,6 +80,9 @@ apps: online-accounts-service: command: bin/run slots: [ online-accounts-service ] + pipewire: + command: bin/run + slots: [ pipewire ] storage-framework-service: command: bin/run slots: [ storage-framework-service ] From ab22b649f9341b0bcfa0287cfd7dae1ea1f190eb Mon Sep 17 00:00:00 2001 From: Sergio Costas Rodriguez Date: Fri, 6 Sep 2024 15:43:09 +0200 Subject: [PATCH 4/9] Remove slot in non-core --- .../test-snapd-policy-app-provider-classic/meta/snap.yaml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/tests/main/interfaces-many-snap-provided/test-snapd-policy-app-provider-classic/meta/snap.yaml b/tests/main/interfaces-many-snap-provided/test-snapd-policy-app-provider-classic/meta/snap.yaml index b8a5d64a5cf..a24097a4e48 100644 --- a/tests/main/interfaces-many-snap-provided/test-snapd-policy-app-provider-classic/meta/snap.yaml +++ b/tests/main/interfaces-many-snap-provided/test-snapd-policy-app-provider-classic/meta/snap.yaml @@ -15,7 +15,6 @@ slots: mpris: name: test-policy-app-provider-classic online-accounts-service: null - pipewire: null storage-framework-service: null thumbnailer-service: null ubuntu-download-manager: null @@ -57,9 +56,6 @@ apps: online-accounts-service: command: bin/run slots: [ online-accounts-service ] - pipewire: - command: bin/run - slots: [ pipewire ] storage-framework-service: command: bin/run slots: [ storage-framework-service ] From b7cc3e6a9e17426384d9d0e5cb284d661da3fe87 Mon Sep 17 00:00:00 2001 From: Sergio Costas Rodriguez Date: Fri, 6 Sep 2024 15:58:02 +0200 Subject: [PATCH 5/9] Remove the other --- .../test-snapd-policy-app-provider-core/meta/snap.yaml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/tests/main/interfaces-many-snap-provided/test-snapd-policy-app-provider-core/meta/snap.yaml b/tests/main/interfaces-many-snap-provided/test-snapd-policy-app-provider-core/meta/snap.yaml index f91da4dfc6e..c5683492cef 100644 --- a/tests/main/interfaces-many-snap-provided/test-snapd-policy-app-provider-core/meta/snap.yaml +++ b/tests/main/interfaces-many-snap-provided/test-snapd-policy-app-provider-core/meta/snap.yaml @@ -22,7 +22,6 @@ slots: network-manager-observe: null ofono: null online-accounts-service: null - pipewire: null storage-framework-service: null thumbnailer-service: null ubuntu-download-manager: null @@ -80,9 +79,6 @@ apps: online-accounts-service: command: bin/run slots: [ online-accounts-service ] - pipewire: - command: bin/run - slots: [ pipewire ] storage-framework-service: command: bin/run slots: [ storage-framework-service ] From 64aafcd232e674ad034382556374ed040859d5aa Mon Sep 17 00:00:00 2001 From: Sergio Costas Rodriguez Date: Fri, 6 Sep 2024 17:18:49 +0200 Subject: [PATCH 6/9] Set install permissions --- interfaces/builtin/pipewire.go | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/interfaces/builtin/pipewire.go b/interfaces/builtin/pipewire.go index aea4f29c51e..da55d3b724f 100644 --- a/interfaces/builtin/pipewire.go +++ b/interfaces/builtin/pipewire.go @@ -28,20 +28,13 @@ import ( "github.com/snapcore/snapd/snap" ) -// The audio-playback interface is the companion interface to the audio-record -// interface. The design of this interface is based on the idea that the slot -// implementation (eg pulseaudio) is expected to query snapd on if the -// audio-record slot is connected or not and the audio service will mediate -// recording (ie, the rules below allow connecting to the audio service, but do -// not implement enforcement rules; it is up to the audio service to provide -// enforcement). If other audio recording servers require different security -// policy for record and playback (eg, a different socket path), then those -// accesses will be added to this interface. - const pipewireSummary = `allows access to the pipewire sockets, and offer them` const pipewireBaseDeclarationSlots = ` pipewire: + slot-snap-type: + - app + - core deny-auto-connection: true ` From 263dbd83b326bf02de27792d59d74bd5763dbb1e Mon Sep 17 00:00:00 2001 From: Sergio Costas Rodriguez Date: Thu, 12 Sep 2024 10:48:06 +0200 Subject: [PATCH 7/9] Re-work the /etc/pulse access --- interfaces/builtin/audio_playback.go | 5 +++++ interfaces/builtin/audio_playback_test.go | 2 ++ interfaces/builtin/pipewire.go | 5 ----- interfaces/builtin/pipewire_test.go | 2 -- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/interfaces/builtin/audio_playback.go b/interfaces/builtin/audio_playback.go index f6ed888eed0..9892637bd6f 100644 --- a/interfaces/builtin/audio_playback.go +++ b/interfaces/builtin/audio_playback.go @@ -117,6 +117,11 @@ owner /{,var/}run/pulse/** rwk, owner /run/user/[0-9]*/ r, owner /run/user/[0-9]*/pulse/ rw, owner /run/user/[0-9]*/pulse/** rw, + +# This allows wireplumber to read the pulseaudio +# configuration if pipewire runs inside a container +/etc/pulse/ r, +/etc/pulse/** r, ` const audioPlaybackPermanentSlotSecComp = ` diff --git a/interfaces/builtin/audio_playback_test.go b/interfaces/builtin/audio_playback_test.go index b505a3c41d9..06a2c44b5b8 100644 --- a/interfaces/builtin/audio_playback_test.go +++ b/interfaces/builtin/audio_playback_test.go @@ -167,6 +167,8 @@ func (s *AudioPlaybackInterfaceSuite) TestAppArmor(c *C) { c.Assert(spec.AddPermanentSlot(s.iface, s.coreSlotInfo), IsNil) c.Assert(spec.SecurityTags(), DeepEquals, []string{"snap.audio-playback.app1"}) c.Check(spec.SnippetForTag("snap.audio-playback.app1"), testutil.Contains, "capability setuid,\n") + c.Check(spec.SnippetForTag("snap.audio-playback.app1"), testutil.Contains, "/etc/pulse/ r,\n") + c.Check(spec.SnippetForTag("snap.audio-playback.app1"), testutil.Contains, "/etc/pulse/** r,\n") } func (s *AudioPlaybackInterfaceSuite) TestAppArmorOnClassic(c *C) { diff --git a/interfaces/builtin/pipewire.go b/interfaces/builtin/pipewire.go index da55d3b724f..080ab93bec2 100644 --- a/interfaces/builtin/pipewire.go +++ b/interfaces/builtin/pipewire.go @@ -74,11 +74,6 @@ network netlink raw, owner /run/user/[0-9]*/pipewire-[0-9] rwk, owner /run/user/[0-9]*/pipewire-[0-9]-manager rwk, - -# This allows wireplumber to read the pulseaudio -# configuration if pipewire runs inside a container -/etc/pulse/ r, -/etc/pulse/** r, ` const pipewirePermanentSlotSecComp = ` diff --git a/interfaces/builtin/pipewire_test.go b/interfaces/builtin/pipewire_test.go index 688aa1cc44c..60647c09754 100644 --- a/interfaces/builtin/pipewire_test.go +++ b/interfaces/builtin/pipewire_test.go @@ -138,8 +138,6 @@ func (s *pipewireInterfaceSuite) TestSecCompOnClassic(c *C) { c.Assert(spec.SecurityTags(), HasLen, 0) c.Assert(spec.SnippetForTag("snap.pipewire.app1"), Not(testutil.Contains), "owner /run/user/[0-9]*/pipewire-[0-9] rwk,\n") - c.Check(spec.SnippetForTag("snap.pipewire.app1"), Not(testutil.Contains), "/etc/pulse/ r,\n") - c.Check(spec.SnippetForTag("snap.pipewire.app1"), Not(testutil.Contains), "/etc/pulse/** r,\n") } func (s *pipewireInterfaceSuite) TestAppArmor(c *C) { From a7386ddc1c00ecc29962ad8524bf8529f8ad3969 Mon Sep 17 00:00:00 2001 From: Sergio Costas Rodriguez Date: Thu, 12 Sep 2024 17:49:28 +0200 Subject: [PATCH 8/9] Fix tests --- interfaces/builtin/pipewire.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/interfaces/builtin/pipewire.go b/interfaces/builtin/pipewire.go index 080ab93bec2..c37aefe9735 100644 --- a/interfaces/builtin/pipewire.go +++ b/interfaces/builtin/pipewire.go @@ -32,9 +32,9 @@ const pipewireSummary = `allows access to the pipewire sockets, and offer them` const pipewireBaseDeclarationSlots = ` pipewire: - slot-snap-type: - - app - - core + allow-installation: + slot-snap-type: + - core deny-auto-connection: true ` @@ -98,7 +98,7 @@ func (iface *pipewireInterface) Name() string { func (iface *pipewireInterface) StaticInfo() interfaces.StaticInfo { return interfaces.StaticInfo{ Summary: pipewireSummary, - ImplicitOnClassic: false, + ImplicitOnClassic: true, ImplicitOnCore: false, BaseDeclarationSlots: pipewireBaseDeclarationSlots, } From 5bfd3ff97f8e69a9e1a4fdcb2fccb5fd19ccfe58 Mon Sep 17 00:00:00 2001 From: Sergio Costas Rodriguez Date: Fri, 13 Sep 2024 12:09:03 +0200 Subject: [PATCH 9/9] Remove unneeded access to manager socket --- interfaces/builtin/pipewire.go | 4 +--- interfaces/builtin/pipewire_test.go | 2 -- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/interfaces/builtin/pipewire.go b/interfaces/builtin/pipewire.go index c37aefe9735..b95aec2f959 100644 --- a/interfaces/builtin/pipewire.go +++ b/interfaces/builtin/pipewire.go @@ -34,7 +34,7 @@ const pipewireBaseDeclarationSlots = ` pipewire: allow-installation: slot-snap-type: - - core + - app deny-auto-connection: true ` @@ -44,12 +44,10 @@ const pipewireConnectedPlugAppArmor = ` owner /run/user/[0-9]*/ r, owner /run/user/[0-9]*/pipewire-[0-9] rw, -owner /run/user/[0-9]*/pipewire-[0-9]-manager rw, ` const pipewireConnectedPlugAppArmorCore = ` owner /run/user/[0-9]*/###SLOT_SECURITY_TAGS###/pipewire-[0-9] rw, -owner /run/user/[0-9]*/###SLOT_SECURITY_TAGS###/pipewire-[0-9]-manager rw, ` const pipewireConnectedPlugSecComp = ` diff --git a/interfaces/builtin/pipewire_test.go b/interfaces/builtin/pipewire_test.go index 60647c09754..47766798f94 100644 --- a/interfaces/builtin/pipewire_test.go +++ b/interfaces/builtin/pipewire_test.go @@ -150,7 +150,6 @@ func (s *pipewireInterfaceSuite) TestAppArmor(c *C) { c.Assert(spec.SecurityTags(), DeepEquals, []string{"snap.consumer.app"}) c.Check(spec.SnippetForTag("snap.consumer.app"), testutil.Contains, "/{run,dev}/shm/pulse-shm-* mrwk,\n") c.Assert(spec.SnippetForTag("snap.consumer.app"), testutil.Contains, "owner /run/user/[0-9]*/snap.pipewire/pipewire-[0-9] rw,\n") - c.Assert(spec.SnippetForTag("snap.consumer.app"), testutil.Contains, "owner /run/user/[0-9]*/snap.pipewire/pipewire-[0-9]-manager rw,\n") // connected core slot to plug spec = apparmor.NewSpecification(s.coreSlot.AppSet()) @@ -175,7 +174,6 @@ func (s *pipewireInterfaceSuite) TestAppArmorOnClassic(c *C) { c.Assert(spec.SecurityTags(), DeepEquals, []string{"snap.consumer.app"}) c.Check(spec.SnippetForTag("snap.consumer.app"), testutil.Contains, "/{run,dev}/shm/pulse-shm-* mrwk,\n") c.Check(spec.SnippetForTag("snap.consumer.app"), testutil.Contains, "owner /run/user/[0-9]*/pipewire-[0-9] rw,\n") - c.Check(spec.SnippetForTag("snap.consumer.app"), testutil.Contains, "owner /run/user/[0-9]*/pipewire-[0-9]-manager rw,\n") // connected classic slot to plug spec = apparmor.NewSpecification(s.classicSlot.AppSet())