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: add network-manager-observe interface (2.37) #6457

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
146 changes: 146 additions & 0 deletions interfaces/builtin/network_manager_observe.go
@@ -0,0 +1,146 @@
// -*- Mode: Go; indent-tabs-mode: t -*-

/*
* Copyright (C) 2019 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 <http://www.gnu.org/licenses/>.
*
*/

package builtin

import (
"strings"

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

const networkManagerObserveBaseDeclarationSlots = `
network-manager-observe:
allow-installation:
slot-snap-type:
- app
- core
deny-auto-connection: true
deny-connection:
on-classic: false
`

const networkManagerObserveSummary = `allows observing NetworkManager settings`

const networkManagerObserveConnectedSlotAppArmor = `
dbus (receive)
bus=system
path="/org/freedesktop/NetworkManager{,/{ActiveConnection,Devices}/*}"
interface="org.freedesktop.DBus.Properties"
member="Get{,All}"
peer=(label=###PLUG_SECURITY_TAGS###),
dbus (receive)
bus=system
path="/org/freedesktop/NetworkManager"
interface="org.freedesktop.NetworkManager"
member="GetDevices"
peer=(label=###PLUG_SECURITY_TAGS###),
dbus (receive)
bus=system
path="/org/freedesktop/NetworkManager/Settings"
interface="org.freedesktop.NetworkManager.Settings"
member="ListConnections"
peer=(label=###PLUG_SECURITY_TAGS###),
dbus (receive)
bus=system
path="/org/freedesktop/NetworkManager/Settings{,/*}"
interface="org.freedesktop.NetworkManager.Settings{,.Connection}"
member="GetSettings"
peer=(label=###PLUG_SECURITY_TAGS###),
`

const networkManagerObserveConnectedPlugAppArmor = `
# Description: allows observing NetworkManager settings. This grants access to
# listing MAC addresses, previous networks, etc but not secrets.
dbus (send)
bus=system
path="/org/freedesktop/NetworkManager{,/{ActiveConnection,Devices}/*}"
interface="org.freedesktop.DBus.Properties"
member="Get{,All}"
peer=(label=###SLOT_SECURITY_TAGS###),
dbus (send)
bus=system
path="/org/freedesktop/NetworkManager"
interface="org.freedesktop.NetworkManager"
member="GetDevices"
peer=(label=###SLOT_SECURITY_TAGS###),
dbus (send)
bus=system
path="/org/freedesktop/NetworkManager/Settings"
interface="org.freedesktop.NetworkManager.Settings"
member="ListConnections"
peer=(label=###SLOT_SECURITY_TAGS###),
dbus (send)
bus=system
path="/org/freedesktop/NetworkManager/Settings{,/*}"
interface="org.freedesktop.NetworkManager.Settings{,.Connection}"
member="GetSettings"
peer=(label=###SLOT_SECURITY_TAGS###),
`

type networkManagerObserveInterface struct{}

func (iface *networkManagerObserveInterface) Name() string {
return "network-manager-observe"
}

func (iface *networkManagerObserveInterface) StaticInfo() interfaces.StaticInfo {
return interfaces.StaticInfo{
Summary: networkManagerObserveSummary,
ImplicitOnClassic: true,
BaseDeclarationSlots: networkManagerObserveBaseDeclarationSlots,
}
}

func (iface *networkManagerObserveInterface) AppArmorConnectedPlug(spec *apparmor.Specification, plug *interfaces.ConnectedPlug, slot *interfaces.ConnectedSlot) error {
old := "###SLOT_SECURITY_TAGS###"
var new string
if release.OnClassic {
// If we're running on classic NetworkManager will be part
// of the OS and will run unconfined.
new = "unconfined"
} else {
new = slotAppLabelExpr(slot)
}
snippet := strings.Replace(networkManagerObserveConnectedPlugAppArmor, old, new, -1)
spec.AddSnippet(snippet)
return nil
}

func (iface *networkManagerObserveInterface) AppArmorConnectedSlot(spec *apparmor.Specification, plug *interfaces.ConnectedPlug, slot *interfaces.ConnectedSlot) error {
if !release.OnClassic {
old := "###PLUG_SECURITY_TAGS###"
new := plugAppLabelExpr(plug)
snippet := strings.Replace(networkManagerObserveConnectedSlotAppArmor, old, new, -1)
spec.AddSnippet(snippet)
}
return nil
}

func (iface *networkManagerObserveInterface) AutoConnect(*snap.PlugInfo, *snap.SlotInfo) bool {
// allow what declarations allowed
return true
}

func init() {
registerIface(&networkManagerObserveInterface{})
}
144 changes: 144 additions & 0 deletions interfaces/builtin/network_manager_observe_test.go
@@ -0,0 +1,144 @@
// -*- Mode: Go; indent-tabs-mode: t -*-

/*
* Copyright (C) 2019 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 <http://www.gnu.org/licenses/>.
*
*/

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/release"
"github.com/snapcore/snapd/snap"
"github.com/snapcore/snapd/testutil"
)

type NetworkManagerObserveInterfaceSuite struct {
iface interfaces.Interface
plug *interfaces.ConnectedPlug
plugInfo *snap.PlugInfo
appSlot *interfaces.ConnectedSlot
appSlotInfo *snap.SlotInfo
coreSlot *interfaces.ConnectedSlot
coreSlotInfo *snap.SlotInfo
}

var _ = Suite(&NetworkManagerObserveInterfaceSuite{
iface: builtin.MustInterface("network-manager-observe"),
})

const networkManagerObserveConsumerYaml = `name: consumer
version: 0
apps:
app:
plugs: [network-manager-observe]
`

const networkManagerObserveProducerYaml = `name: producer
version: 0
apps:
app:
slots: [network-manager-observe]
`

const networkManagerObserveCoreYaml = `name: core
version: 0
slots:
network-manager-observe:
`

func (s *NetworkManagerObserveInterfaceSuite) SetUpTest(c *C) {
s.plug, s.plugInfo = MockConnectedPlug(c, networkManagerObserveConsumerYaml, nil, "network-manager-observe")
s.appSlot, s.appSlotInfo = MockConnectedSlot(c, networkManagerObserveProducerYaml, nil, "network-manager-observe")
s.coreSlot, s.coreSlotInfo = MockConnectedSlot(c, networkManagerObserveCoreYaml, nil, "network-manager-observe")
}

func (s *NetworkManagerObserveInterfaceSuite) TestName(c *C) {
c.Assert(s.iface.Name(), Equals, "network-manager-observe")
}

func (s *NetworkManagerObserveInterfaceSuite) TestSanitizeSlot(c *C) {
c.Assert(interfaces.BeforePrepareSlot(s.iface, s.coreSlotInfo), IsNil)
c.Assert(interfaces.BeforePrepareSlot(s.iface, s.appSlotInfo), IsNil)
// network-manager-observe slot can now be used on snap other than core.
slot := &snap.SlotInfo{
Snap: &snap.Info{SuggestedName: "some-snap"},
Name: "network-manager-observe",
Interface: "network-manager-observe",
}
c.Assert(interfaces.BeforePrepareSlot(s.iface, slot), IsNil)
}

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

func (s *NetworkManagerObserveInterfaceSuite) TestAppArmorSpec(c *C) {
// on a core system with NM slot coming from a regular app snap.
restore := release.MockOnClassic(false)
defer restore()

// connected plug to app slot
spec := &apparmor.Specification{}
c.Assert(spec.AddConnectedPlug(s.iface, s.plug, s.appSlot), IsNil)
c.Assert(spec.SecurityTags(), DeepEquals, []string{"snap.consumer.app"})
c.Assert(spec.SnippetForTag("snap.consumer.app"), testutil.Contains, `path="/org/freedesktop/NetworkManager{,/{ActiveConnection,Devices}/*}"`)
c.Assert(spec.SnippetForTag("snap.consumer.app"), testutil.Contains, `peer=(label="snap.producer.app"),`)

// connected app slot to plug
spec = &apparmor.Specification{}
c.Assert(spec.AddConnectedSlot(s.iface, s.plug, s.appSlot), IsNil)
c.Assert(spec.SecurityTags(), DeepEquals, []string{"snap.producer.app"})
c.Assert(spec.SnippetForTag("snap.producer.app"), testutil.Contains, `path="/org/freedesktop/NetworkManager{,/{ActiveConnection,Devices}/*}"`)
c.Assert(spec.SnippetForTag("snap.producer.app"), testutil.Contains, `peer=(label="snap.consumer.app"),`)

// on a classic system with NM slot coming from the core snap.
restore = release.MockOnClassic(true)
defer restore()

// connected plug to core slot
spec = &apparmor.Specification{}
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, `path="/org/freedesktop/NetworkManager{,/{ActiveConnection,Devices}/*}"`)
c.Assert(spec.SnippetForTag("snap.consumer.app"), testutil.Contains, "peer=(label=unconfined),")

// connected core slot to plug
spec = &apparmor.Specification{}
c.Assert(spec.AddConnectedSlot(s.iface, s.plug, s.coreSlot), IsNil)
c.Assert(spec.SecurityTags(), HasLen, 0)
}

func (s *NetworkManagerObserveInterfaceSuite) TestStaticInfo(c *C) {
si := interfaces.StaticInfoOf(s.iface)
c.Assert(si.ImplicitOnCore, Equals, false)
c.Assert(si.ImplicitOnClassic, Equals, true)
c.Assert(si.Summary, Equals, `allows observing NetworkManager settings`)
c.Assert(si.BaseDeclarationSlots, testutil.Contains, "network-manager-observe")
}

func (s *NetworkManagerObserveInterfaceSuite) TestAutoConnect(c *C) {
c.Assert(s.iface.AutoConnect(s.plugInfo, s.coreSlotInfo), Equals, true)
c.Assert(s.iface.AutoConnect(s.plugInfo, s.appSlotInfo), Equals, true)
}

func (s *NetworkManagerObserveInterfaceSuite) TestInterfaces(c *C) {
c.Check(builtin.Interfaces(), testutil.DeepContains, s.iface)
}
1 change: 1 addition & 0 deletions interfaces/policy/basedeclaration_test.go
Expand Up @@ -567,6 +567,7 @@ var (
"modem-manager": {"app", "core"},
"mpris": {"app"},
"network-manager": {"app", "core"},
"network-manager-observe": {"app", "core"},
"network-status": {"app"},
"ofono": {"app", "core"},
"online-accounts-service": {"app"},
Expand Down
3 changes: 3 additions & 0 deletions tests/lib/snaps/test-snapd-policy-app-consumer/meta/snap.yaml
Expand Up @@ -224,6 +224,9 @@ apps:
network-manager:
command: bin/run
plugs: [ network-manager ]
network-manager-observe:
command: bin/run
plugs: [ network-manager-observe ]
network-observe:
command: bin/run
plugs: [ network-observe ]
Expand Down
Expand Up @@ -31,6 +31,7 @@ slots:
mir: null
modem-manager: null
network-manager: null
network-manager-observe: null
network-status: null
ofono: null
online-accounts-service: null
Expand Down Expand Up @@ -95,6 +96,9 @@ apps:
network-manager:
command: bin/run
slots: [ network-manager ]
network-manager-observe:
command: bin/run
slots: [ network-manager-observe ]
network-status:
command: bin/run
slots: [ network-status ]
Expand Down