Skip to content

Commit 91d5f23

Browse files
author
Buğra Aydoğar
committed
interfaces/builtin: add vsock-guest interface for VM guest services
1 parent 6a01a45 commit 91d5f23

3 files changed

Lines changed: 195 additions & 0 deletions

File tree

interfaces/builtin/vsock_guest.go

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
// -*- Mode: Go; indent-tabs-mode: t -*-
2+
3+
/*
4+
* Copyright (C) 2026 Canonical Ltd
5+
*
6+
* This program is free software: you can redistribute it and/or modify
7+
* it under the terms of the GNU General Public License version 3 as
8+
* published by the Free Software Foundation.
9+
*
10+
* This program is distributed in the hope that it will be useful,
11+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
* GNU General Public License for more details.
14+
*
15+
* You should have received a copy of the GNU General Public License
16+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
17+
*
18+
*/
19+
20+
package builtin
21+
22+
const vsockGuestSummary = `allows access to vsock sockets for VM guest to host/hypervisor communication`
23+
24+
const vsockGuestBaseDeclarationSlots = `
25+
vsock-guest:
26+
allow-installation:
27+
slot-snap-type:
28+
- core
29+
deny-auto-connection: true
30+
`
31+
32+
const vsockGuestConnectedPlugAppArmor = `
33+
# Description: Allow access to vsock sockets for communication between
34+
# a VM guest and the host or hypervisor (AF_VSOCK).
35+
network vsock,
36+
/dev/vsock rw,
37+
`
38+
39+
const vsockGuestConnectedPlugSecComp = `
40+
# Description: Allow access to vsock sockets for VM guest to host communication.
41+
# socket AF_VSOCK is already permitted by the default seccomp template.
42+
bind
43+
listen
44+
accept
45+
accept4
46+
`
47+
48+
var vsockGuestConnectedPlugUDev = []string{
49+
`KERNEL=="vsock"`,
50+
}
51+
52+
func init() {
53+
registerIface(&commonInterface{
54+
name: "vsock-guest",
55+
summary: vsockGuestSummary,
56+
implicitOnCore: true,
57+
implicitOnClassic: true,
58+
baseDeclarationSlots: vsockGuestBaseDeclarationSlots,
59+
connectedPlugAppArmor: vsockGuestConnectedPlugAppArmor,
60+
connectedPlugSecComp: vsockGuestConnectedPlugSecComp,
61+
connectedPlugUDev: vsockGuestConnectedPlugUDev,
62+
})
63+
}
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
// -*- Mode: Go; indent-tabs-mode: t -*-
2+
3+
/*
4+
* Copyright (C) 2026 Canonical Ltd
5+
*
6+
* This program is free software: you can redistribute it and/or modify
7+
* it under the terms of the GNU General Public License version 3 as
8+
* published by the Free Software Foundation.
9+
*
10+
* This program is distributed in the hope that it will be useful,
11+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
* GNU General Public License for more details.
14+
*
15+
* You should have received a copy of the GNU General Public License
16+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
17+
*
18+
*/
19+
20+
package builtin_test
21+
22+
import (
23+
"fmt"
24+
25+
. "gopkg.in/check.v1"
26+
27+
"github.com/snapcore/snapd/dirs"
28+
"github.com/snapcore/snapd/interfaces"
29+
"github.com/snapcore/snapd/interfaces/apparmor"
30+
"github.com/snapcore/snapd/interfaces/builtin"
31+
"github.com/snapcore/snapd/interfaces/seccomp"
32+
"github.com/snapcore/snapd/interfaces/udev"
33+
"github.com/snapcore/snapd/snap"
34+
"github.com/snapcore/snapd/testutil"
35+
)
36+
37+
type vsockGuestInterfaceSuite struct {
38+
testutil.BaseTest
39+
40+
iface interfaces.Interface
41+
slotInfo *snap.SlotInfo
42+
slot *interfaces.ConnectedSlot
43+
plugInfo *snap.PlugInfo
44+
plug *interfaces.ConnectedPlug
45+
}
46+
47+
var _ = Suite(&vsockGuestInterfaceSuite{
48+
iface: builtin.MustInterface("vsock-guest"),
49+
})
50+
51+
const vsockGuestConsumerYaml = `name: consumer
52+
version: 0
53+
apps:
54+
app:
55+
plugs: [vsock-guest]
56+
`
57+
58+
const vsockGuestCoreYaml = `name: core
59+
version: 0
60+
type: os
61+
slots:
62+
vsock-guest:
63+
`
64+
65+
func (s *vsockGuestInterfaceSuite) SetUpTest(c *C) {
66+
s.BaseTest.SetUpTest(c)
67+
s.plug, s.plugInfo = MockConnectedPlug(c, vsockGuestConsumerYaml, nil, "vsock-guest")
68+
s.slot, s.slotInfo = MockConnectedSlot(c, vsockGuestCoreYaml, nil, "vsock-guest")
69+
}
70+
71+
func (s *vsockGuestInterfaceSuite) TestName(c *C) {
72+
c.Assert(s.iface.Name(), Equals, "vsock-guest")
73+
}
74+
75+
func (s *vsockGuestInterfaceSuite) TestSanitizeSlot(c *C) {
76+
c.Assert(interfaces.BeforePrepareSlot(s.iface, s.slotInfo), IsNil)
77+
}
78+
79+
func (s *vsockGuestInterfaceSuite) TestSanitizePlug(c *C) {
80+
c.Assert(interfaces.BeforePreparePlug(s.iface, s.plugInfo), IsNil)
81+
}
82+
83+
func (s *vsockGuestInterfaceSuite) TestAppArmorSpec(c *C) {
84+
appSet, err := interfaces.NewSnapAppSet(s.plug.Snap(), nil)
85+
c.Assert(err, IsNil)
86+
spec := apparmor.NewSpecification(appSet)
87+
c.Assert(spec.AddConnectedPlug(s.iface, s.plug, s.slot), IsNil)
88+
c.Assert(spec.SecurityTags(), DeepEquals, []string{"snap.consumer.app"})
89+
c.Assert(spec.SnippetForTag("snap.consumer.app"), testutil.Contains, "network vsock,")
90+
c.Assert(spec.SnippetForTag("snap.consumer.app"), testutil.Contains, "/dev/vsock rw,")
91+
}
92+
93+
func (s *vsockGuestInterfaceSuite) TestUDevSpec(c *C) {
94+
appSet, err := interfaces.NewSnapAppSet(s.plug.Snap(), nil)
95+
c.Assert(err, IsNil)
96+
spec := udev.NewSpecification(appSet)
97+
c.Assert(spec.AddConnectedPlug(s.iface, s.plug, s.slot), IsNil)
98+
c.Assert(spec.Snippets(), testutil.Contains, "# vsock-guest\nKERNEL==\"vsock\", TAG+=\"snap_consumer_app\"")
99+
c.Assert(spec.Snippets(), testutil.Contains, fmt.Sprintf(`TAG=="snap_consumer_app", SUBSYSTEM!="module", SUBSYSTEM!="subsystem", RUN+="%s/snap-device-helper $env{ACTION} snap_consumer_app $devpath $major:$minor"`, dirs.DistroLibExecDir))
100+
}
101+
102+
func (s *vsockGuestInterfaceSuite) TestSecCompSpec(c *C) {
103+
appSet, err := interfaces.NewSnapAppSet(s.plug.Snap(), nil)
104+
c.Assert(err, IsNil)
105+
spec := seccomp.NewSpecification(appSet)
106+
c.Assert(spec.AddConnectedPlug(s.iface, s.plug, s.slot), IsNil)
107+
c.Assert(spec.SecurityTags(), DeepEquals, []string{"snap.consumer.app"})
108+
c.Assert(spec.SnippetForTag("snap.consumer.app"), testutil.Contains, "bind\n")
109+
c.Assert(spec.SnippetForTag("snap.consumer.app"), testutil.Contains, "listen\n")
110+
c.Assert(spec.SnippetForTag("snap.consumer.app"), testutil.Contains, "accept\n")
111+
c.Assert(spec.SnippetForTag("snap.consumer.app"), testutil.Contains, "accept4\n")
112+
}
113+
114+
func (s *vsockGuestInterfaceSuite) TestStaticInfo(c *C) {
115+
si := interfaces.StaticInfoOf(s.iface)
116+
c.Assert(si.ImplicitOnCore, Equals, true)
117+
c.Assert(si.ImplicitOnClassic, Equals, true)
118+
c.Assert(si.Summary, Equals, `allows access to vsock sockets for VM guest to host/hypervisor communication`)
119+
c.Assert(si.BaseDeclarationSlots, testutil.Contains, "vsock-guest")
120+
c.Assert(si.BaseDeclarationSlots, testutil.Contains, "deny-auto-connection: true")
121+
}
122+
123+
func (s *vsockGuestInterfaceSuite) TestAutoConnect(c *C) {
124+
c.Assert(s.iface.AutoConnect(s.plugInfo, s.slotInfo), Equals, true)
125+
}
126+
127+
func (s *vsockGuestInterfaceSuite) TestInterfaces(c *C) {
128+
c.Check(builtin.Interfaces(), testutil.DeepContains, s.iface)
129+
}

tests/lib/snaps/test-snapd-policy-app-consumer/meta/snap.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,9 @@ apps:
299299
vcio:
300300
command: bin/run
301301
plugs: [ vcio ]
302+
vsock-guest:
303+
command: bin/run
304+
plugs: [ vsock-guest ]
302305
x11:
303306
command: bin/run
304307
plugs: [ x11 ]

0 commit comments

Comments
 (0)