interfaces: add classic-support interface #2604

Merged
merged 11 commits into from Feb 15, 2017

Conversation

Projects
None yet
5 participants
Collaborator

mvo5 commented Jan 11, 2017

This avoids the huge amount of logging that is currently generated.
It also allows to install classic in strict mode (with the caveat
that it can not do most privilidged stuff in the chroot).

Will not auto-connect by default, but we should update the package declaratior for the classic snap so that it will auto-connect.

LP: #1654642

add classic-dimension interface
This avoids the huge amount of logging that is currently generated.
It also allows to install classic in strict mode (with the caveat
that it can not do most privilidged stuff in the chroot).

LP: #1654642

zyga approved these changes Jan 11, 2017

Looks good. I wonder if we could scope that so that it only applies to the "classic" snap.

Contributor

zyga commented Jan 11, 2017

@mvo5 you also want to add this to snap/implicit.go

@zyga zyga requested a review from jdstrand Jan 11, 2017

In addition to inline comments, please add classic_dimension_test.go ala network_control_test.go.

interfaces/builtin/basedeclaration.go
+ allow-installation:
+ slot-snap-type:
+ - core
+ deny-auto-connection: true
@jdstrand

jdstrand Jan 11, 2017

Contributor

If there was ever a definition of a super-privileged interface, it is this one since it allows running /usr/bin/systemd-run unconfined (and thus can run any code) as well as allowing manipulating anything in systemd via unconfined systemctl. As such, please remove this part of the declaration and add to the 'plugs' part of the declaration:

  classic-dimension:
    allow-installation: false
    deny-auto-connection: true

With this, we'll grant a snap declaration to the classic snap for installation and auto-connection and it'll work like normal, but no other signed snaps will be able to be installed with this interface.

@mvo5

mvo5 Jan 11, 2017

Collaborator

Thanks! I updated the code to do it this way now.

+ "ppp": {"core"},
+ "pulseaudio": {"app", "core"},
+ "serial-port": {"core", "gadget"},
+ "udisks2": {"app"},
@jdstrand

jdstrand Jan 11, 2017

Contributor

This is a lot of whitespace changes-- is go fmt happy?

@mvo5

mvo5 Jan 11, 2017

Collaborator

That was all done by go fmt - I just added the classic-dimension: {"core"} change.

+ "ppp": {"core"},
+ "pulseaudio": {"app", "core"},
+ "serial-port": {"core", "gadget"},
+ "udisks2": {"app"},
// snowflakes
"docker": nil,
"lxd": nil,
@jdstrand

jdstrand Jan 11, 2017

Contributor

Please add corresponding tests in basedeclaration_test.go in TestPlugInstallation, TestAutoConnectPlugSlot and additional tests like there are with docker-support and lxd-support.

@mvo5

mvo5 Jan 11, 2017

Collaborator

Thanks! I added more testing like you suggested.

interfaces/builtin/classic_dimension.go
+)
+
+const classicDimensionPlugAppArmor = `
+# Description: Extra permissions to use the classic dimension on Ubuntu Core
@jdstrand

jdstrand Jan 11, 2017

Contributor

Please adjust to be:

# Description: permissions to use classic dimension. This policy is intentionally
# not restricted. This gives device ownership to connected snaps.
@mvo5

mvo5 Jan 11, 2017

Collaborator

Updated.

interfaces/builtin/classic_dimension.go
+/etc/sudoers.d/{,*} r,
+/usr/bin/systemd-run Uxr,
+/bin/systemctl Uxr,
+`
@jdstrand

jdstrand Jan 11, 2017

Contributor

You mentioned that you want this to run in strict mode. Here is the policy for that (tested on amd64):

# Description: permissions to use classic dimension. This policy is intentionally
# not restricted. This gives device ownership to connected snaps.

# for 'create'
/{,usr/}bin/unsquashfs ixr,
/var/lib/snapd/snaps/core_*.snap r,
capability chown,
capability fowner,
capability mknod,

# This allows running anything unconfined
/{,usr/}bin/sudo Uxr,
capability fsetid,
capability dac_override,

# Allow copying configuration to the chroot
/etc/{,**} r,
/var/lib/extrausers/{,*} r,

# Allow bind mounting various directories
capability sys_admin,
/{,usr/}bin/mount ixr,
/{,usr/}bin/mountpoint ixr,
/run/mount/utab rw,
@{PROC}/[0-9]*/mountinfo r,
mount options=(rw bind) /home/ -> /var/snap/@{SNAP_NAME}/**/,
mount options=(rw bind) /run/ -> /var/snap/@{SNAP_NAME}/**/,
mount options=(rw bind) /proc/ -> /var/snap/@{SNAP_NAME}/**/,
mount options=(rw bind) /sys/ -> /var/snap/@{SNAP_NAME}/**/,
mount options=(rw bind) /dev/ -> /var/snap/@{SNAP_NAME}/**/,
mount options=(rw bind) / -> /var/snap/@{SNAP_NAME}/**/,
mount fstype=devpts options=(rw) devpts -> /dev/pts/,
mount options=(rw rprivate) -> /var/snap/@{SNAP_NAME}/**/,

# reset
/{,usr/}bin/umount ixr,
umount /var/snap/@{SNAP_NAME}/**/,

# These rules allow running anything unconfined as well as managing systemd
/usr/bin/systemd-run Uxr,
/bin/systemctl Uxr,

This was tested on amd64. Note, the policy adjustments are only needed for the setup/reset to get to systemd-run/systemctl since once you hit those commands, the policy goes unconfined.

interfaces/builtin/classic_dimension.go
+func NewClassicDimensionInterface() interfaces.Interface {
+ return &commonInterface{
+ name: "classic-dimension",
+ connectedPlugAppArmor: classicDimensionPlugAppArmor,
@jdstrand

jdstrand Jan 11, 2017

Contributor

For strict mode you also need:

connectedPlugSecComp: classicDimensionPlugSecComp,

and to define this:

const classicDimensionPlugSecComp = `
# Description: permissions to use classic dimension. This policy is intentionally
# not restricted. This gives device ownership to connected snaps.
# create
chown
chown32
lchown
lchown32
fchown
fchown32
fchownat
mknod
chroot

# sudo
bind
sendmsg
sendmmsg
sendto
recvfrom
recvmsg
setgroups
setgroups32

# classic
mount
getsockopt

# reset
umount
umount2
@mvo5

mvo5 Jan 11, 2017

Collaborator

Thanks, added those now too!

Thanks for the excellent review!I addressed the raised points.

interfaces/builtin/basedeclaration.go
+ allow-installation:
+ slot-snap-type:
+ - core
+ deny-auto-connection: true
@jdstrand

jdstrand Jan 11, 2017

Contributor

If there was ever a definition of a super-privileged interface, it is this one since it allows running /usr/bin/systemd-run unconfined (and thus can run any code) as well as allowing manipulating anything in systemd via unconfined systemctl. As such, please remove this part of the declaration and add to the 'plugs' part of the declaration:

  classic-dimension:
    allow-installation: false
    deny-auto-connection: true

With this, we'll grant a snap declaration to the classic snap for installation and auto-connection and it'll work like normal, but no other signed snaps will be able to be installed with this interface.

@mvo5

mvo5 Jan 11, 2017

Collaborator

Thanks! I updated the code to do it this way now.

+ "ppp": {"core"},
+ "pulseaudio": {"app", "core"},
+ "serial-port": {"core", "gadget"},
+ "udisks2": {"app"},
@jdstrand

jdstrand Jan 11, 2017

Contributor

This is a lot of whitespace changes-- is go fmt happy?

@mvo5

mvo5 Jan 11, 2017

Collaborator

That was all done by go fmt - I just added the classic-dimension: {"core"} change.

+ "ppp": {"core"},
+ "pulseaudio": {"app", "core"},
+ "serial-port": {"core", "gadget"},
+ "udisks2": {"app"},
// snowflakes
"docker": nil,
"lxd": nil,
@jdstrand

jdstrand Jan 11, 2017

Contributor

Please add corresponding tests in basedeclaration_test.go in TestPlugInstallation, TestAutoConnectPlugSlot and additional tests like there are with docker-support and lxd-support.

@mvo5

mvo5 Jan 11, 2017

Collaborator

Thanks! I added more testing like you suggested.

interfaces/builtin/classic_dimension.go
+)
+
+const classicDimensionPlugAppArmor = `
+# Description: Extra permissions to use the classic dimension on Ubuntu Core
@jdstrand

jdstrand Jan 11, 2017

Contributor

Please adjust to be:

# Description: permissions to use classic dimension. This policy is intentionally
# not restricted. This gives device ownership to connected snaps.
@mvo5

mvo5 Jan 11, 2017

Collaborator

Updated.

interfaces/builtin/classic_dimension.go
+func NewClassicDimensionInterface() interfaces.Interface {
+ return &commonInterface{
+ name: "classic-dimension",
+ connectedPlugAppArmor: classicDimensionPlugAppArmor,
@jdstrand

jdstrand Jan 11, 2017

Contributor

For strict mode you also need:

connectedPlugSecComp: classicDimensionPlugSecComp,

and to define this:

const classicDimensionPlugSecComp = `
# Description: permissions to use classic dimension. This policy is intentionally
# not restricted. This gives device ownership to connected snaps.
# create
chown
chown32
lchown
lchown32
fchown
fchown32
fchownat
mknod
chroot

# sudo
bind
sendmsg
sendmmsg
sendto
recvfrom
recvmsg
setgroups
setgroups32

# classic
mount
getsockopt

# reset
umount
umount2
@mvo5

mvo5 Jan 11, 2017

Collaborator

Thanks, added those now too!

interfaces/builtin/basedeclaration.go
@@ -138,6 +138,9 @@ authority-id: canonical
series: 16
revision: 0
plugs:
+ classic-dimension:
+ allow-installation: false
+ deny-auto-connection: true
docker-support:
allow-installation: false
deny-auto-connection: true
@jdstrand

jdstrand Jan 11, 2017

Contributor

Whoops! We do still need the slot side to say this is ok when provided by the core snap. Sorry about not remembering this in the initial review. Please, in addition to the above, add:

slots:
  ...
  classic-dimension:
    allow-installation:
      slot-snap-type:
        - core
    deny-auto-connection: true
@mvo5

mvo5 Jan 11, 2017

Collaborator

Thanks! I added that now too.

interfaces/builtin/basedeclaration.go
@@ -190,6 +193,9 @@ slots:
slot-snap-type:
- core
deny-auto-connection: true
+ classic-dimension:
+ allow-installation: false
+ deny-auto-connection: true
@jdstrand

jdstrand Jan 11, 2017

Contributor

This isn't right for the slots side (see my previous comment). It should be:

  classic-dimension:
    allow-installation:
      slot-snap-type:
        - core
    deny-auto-connection: true
@mvo5

mvo5 Jan 12, 2017

Collaborator

Ups, sorry :/ And thanks for spotting this. I really should not push code late at night. Fixed now.

Contributor

jdstrand commented Jan 12, 2017

The policy and base declaration LGTM, so I am going to 'Approve changes' conditional on adding a classic_dimension_test.go that tests that the policy is applied when connected. See network-control for an example.

(I thought I asked for this before but I don't see it....)

mvo5 added some commits Jan 13, 2017

I don't understand the background here. It feels like redoing classic snaps as an interface?

Contributor

jdstrand commented Jan 17, 2017

@niemeyer - this is for the classic snap ('sudo snap install classic --devmode -edge') not classic confinement ('confinement: classic'). The classic snap currently uses devmode and as such triggers a lot of ALLOWED messages in the logs since devmode logs all policy violations, but allows the action. Michael's changes creates a 'classic' interface that adds the necessary policy for the classic snap to not only run in devmode with no logged policy violations, but to run in strict mode so one may simply use 'sudo snap install classic').

I outlined as an option using 'confinement: classic' for the classic snap instead of implementing the classic interface, but Michael said that due to the restriction that snaps using 'confinement: classic' are not installable on all-snaps images, this was a better option. You might want to review the discussion in the bug for more context:
https://bugs.launchpad.net/snappy/+bug/1654642/comments/1
https://bugs.launchpad.net/snappy/+bug/1654642/comments/3

Contributor

niemeyer commented Jan 19, 2017

Sounds good.

@mvo5 Let's please catch up next week about this one.

Also, this should probably be "classic-support" rather than dimension (note "docker-support", "lxd-support", etc).

Contributor

jdstrand commented Jan 20, 2017

+1 for 'classic-support'

@mvo5 mvo5 changed the title from interfaces: add classic-dimension interface to interfaces: add classic-support interface Jan 25, 2017

LGTM assuming the questions on the base-declaration below are cleared out.

@@ -138,6 +138,9 @@ authority-id: canonical
series: 16
revision: 0
plugs:
+ classic-support:
+ allow-installation: false
+ deny-auto-connection: true
@niemeyer

niemeyer Feb 13, 2017

Contributor

@jdstrand Do we actually need this? Won't the slot already prevent both the installation and the auto-connection unless it's the core snap?

Same question for docker-support below.

@jdstrand

jdstrand Feb 13, 2017

Contributor

This could be written different ways but the way we have done it here and with docker-support is that the 'slots' part looks at it from the slot implementation's pov and is therefore focused on the core snap and its implicit interface. Only core snaps are allowed to be installed and provide the implicit slot and because of that, we only disallow auto-connection here. With this, there is no snap declaration required for a core snap (the store already prompts for human review for 'type: os').

The 'plugs' part looks at it from the plugging snap's pov, so a plugging snap can't be installed if it plugs this and we are explicitly saying it can't auto-connect here for clarity (it could techinically be dropped since the constraint is covered on the slots stanza). With this, the classic snap must have a declaration to be installed at all and may have a declaration to auto-connect.

+ allow-installation:
+ slot-snap-type:
+ - core
+ deny-auto-connection: true
@niemeyer

niemeyer Feb 13, 2017

Contributor

deny-connection: true?

We don't want anyone but "classic" using this, whether auto or manual, right?

@pedronis

pedronis Feb 13, 2017

Contributor

deny-connection and deny-auto-connection are independent, so we would want both

@jdstrand

jdstrand Feb 13, 2017

Contributor

We could have 'deny-connection: true' but we've used that only for non-implicit interfaces historically. In this case, the interface is implicit and the base declaration says it may only be supplied by a core snap, so what is currently implemented was deemed sufficient.

@niemeyer

niemeyer Feb 13, 2017

Contributor

I may definitely be missing something, but I'd appreciate some insight into what it is. If this is allowed into the core slot, then arbitrary snaps will be able to connect to it, won't they? Because the search logic is "plug, slot, base plug, base slot". We don't want any snaps other than "classic" (the classic one, not other classic-confined ones) connecting to this slot.

+# connected snaps.
+
+# Description: permissions to use classic dimension. This policy is intentionally
+# not restricted. This gives device ownership to connected snaps.
@niemeyer

niemeyer Feb 13, 2017

Contributor

Seems duplicated (see right above).

Contributor

jdstrand commented Feb 13, 2017

Note that the current implementation is following the agreed to 'base declaration patterns' as documented in comments at the top of basedeclaration.go. Please see that for future reference. If we want to change what we are doing for the classic interface, we should re-review the pattern this is following and update the other interfaces that use this pattern (I don't think this is necessary, just saying if we are going to think about things differently, we should be consistent).

Contributor

niemeyer commented Feb 14, 2017

@jdstrand @mvo5 @pedronis Let's please try to organize a call about this today or tomorrow. We need to be in sync about the meaning of the snap declaration, and there is clearly misunderstandings here.

@mvo5 mvo5 added this to the 2.23 milestone Feb 15, 2017

Contributor

niemeyer commented Feb 15, 2017

We didn't find a time slot soon enough, so I'm merging this. Discussing online with @pedronis, the only thing we need in the base declaration is really the "allow-installation" bits, both in the slot and in the plug. Nothing else.

That works because it prevents the installation of snaps holding both slots and plugs of that kind, unless it's the core snap. So for anything else to be able to be installed, it will necessarily have to hold a more specific declaration which will define how the given case is supposed to be handled.

We also need to clean up the base-declarations. Many of them suffer from similar issues, which makes reading them quite misleading. The key thing to keep in mind is this: there's no merging of rules. In other words, if installation is denied, game over. The connection can't possibly be established because the snap isn't even installed! For the snap to be installed, a more specific declaration will need to allow it, and once we have a more specific declaration, it will also define when connections and auto-connections are performed. If the respective statements are missing, their defaults will be used.

As a reminder, the order of lookup when installing is:

  • plug → base plug
  • slot → base slot

and when establishing a connection (there are two ends, so we need to look in both):

  • plug → slot → base plug → base slot

In each of these cases, the rule set is used completely! In other words, if we're establishing a connection and there is a rule set for the plug of the interface at stake, this will define whether to establish the connection or not. Alone.

Contributor

niemeyer commented Feb 15, 2017

Fixed and improved comment above after more discussions.

@niemeyer niemeyer merged commit 9eafd17 into snapcore:master Feb 15, 2017

6 checks passed

continuous-integration/travis-ci/pr The Travis CI build passed
Details
xenial-amd64 autopkgtest finished (success)
Details
xenial-i386 autopkgtest finished (success)
Details
xenial-ppc64el autopkgtest finished (success)
Details
yakkety-amd64 autopkgtest finished (success)
Details
zesty-amd64 autopkgtest finished (success)
Details
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment