Multiple users and groups in snaps

The previous (obsolete) discussion on the topic is here. To make the discussion clearer I was asked to start a new topic with the currently agreed to design so that we may work through any additional details. As such, I will copy the entire ‘Introduction’ and a description of the 4 use cases from the other topic, but leave the ‘Implementation Ideas’ in the other topic for historical reference.


Today, snappy’s notion of users (and groups) is primitive:

  • daemons run as root
  • commands run as the user invoking the command

For an initial implementation this has worked very well. The (untrusted) snaps run under root-strong confinement and the access to various resources is mediated via security policy.

There are quite a few important use cases that are not covered by the current implementation however:

  1. adding system users/groups
  2. supporting device access via ACLs for granting access of devices to (non-root) users.
  3. per-snap opt-in users to support things such as privilege dropping/separation within snaps.
  4. Supporting uids/gids in chroot environment

‘1’ and ‘2’ deal with users accessing resources that have been granted to the snap via the snap’s interface security policy and highlight the fact that while the snap may have access to a resource, that doesn’t necessarily mean that the user of the snap should have access to that resource.

‘3’ and ‘4’ deal with supporting a wider range of existing software.

This is a complicated topic and while all 4 cases don’t necessarily need to be implemented at the same time, they all should be considered when designing how users and groups are supposed to work in a snappy world. This topic is intended to help people assigned to implementing any or all of these cases to have the necessary design.

Let’s first consider each use case in a little more detail.

System user and groups

Existing software often uses the concept of users and groups as a security mechanism to grant access to some application resource via traditional users and groups. Examples include lxd, docker and libvirt which all create UNIX sockets with 0660 permissions with ownership of root:<some group>. They all do this because the socket provides privileged access to the host system that should only be granted to trusted users. In order for a non-root user to be able to use these services, the user must be added to the system group for the service.

Today, users that want to use snaps that use this security mechanism must either:

  • always run the command under sudo (eg, sudo -H lxc ...)
  • after snap installation, perform additional steps to create the system group manually, then add users to that group (eg, sudo addgroup lxd && sudo adduser foo lxd)

Device access

For many devices such as audio, UNIX group memberships are not used and instead ACLs are set on devices. Eg:

$ getfacl /dev/snd/seq
# file: dev/snd/seq
# owner: root
# group: audio

(note ‘user:jamie:rw-’ in the above).

In the distant past, all these devices were chmod 0660 with a group specific to that device (eg, ‘audio’) or a catchall group like ‘plugdev’. While some historic groups are still found on the system (eg, ‘audio’), adding users to these groups or creating new groups and using chgrp on the devices should be discouraged on modern systems. In the not so distant past (before systemd), device access is controlled via ACLs that were handled by consolekit via ‘udev-acl’. With systemd, this is handled by ‘uaccess’ via /lib/udev/rules.d/70-uaccess.rules, 71-seat.rules and 73-seat-late.rules where the “uaccess” builtin is used to set ACLs (and some historic groups for compatibility). Since the ‘acl’ package has been seeded in Core for some time (it wasn’t at first), anything that matches Core’s uaccess rules will just work today (eg, /dev/video*).

Unfortunately there are quite a few devices that are not covered either because there isn’t a uaccess rule for it (eg, a serial-port) or because the rules don’t apply for some reason (logging in via ssh and accessing /dev/video* or /dev/snd/* (seat rules don’t apply cause not local)).

Opt-in per-snap users/groups

A lot of existing applications are designed with the notion of privilege separation and/or permanently dropping privileges to secure their code. For example, postgresql, mysql, apache, nginx, etc. Some want to start as root to bind to a port and immediately permanently drop privileges and others want to fork processes under another user to (for example) handle untrusted input.

Today, all these daemon applications must run as root and are not allowed to drop privileges. While the security policy will keep the system safe and will keep the applications isolated, the security stance of the applications themselves is lessened because their security mechanisms can’t be used under snappy (eg, consider an application that handles untrusted input that would normally run a process under a separate user-- under snappy it is the same user so if there is a bug in processing the untrusted input, that process is able to attack the main application).

Soon (now that snap-confine is re-exec’d and seccomp arg filtering work can recommence) we’ll allow snaps to use the ‘daemon’ system user/group to drop privileges. While a shared user/group, this is no worse than the shared ‘root’ user/group and will allow applications to leverage their security mechanisms for up to one group.

Snappy should allow applications to use multiple users and groups. For example, it would be desirable for a complex snap that uses the LAMP stack to be able to let apache drop to a different user than mysql is dropping to so that successful attacks against the apache process don’t give access to mysql’s resources.

Chroot environments

Sometimes it is useful to take an existing chroot setup, put it into a snap and then have the snap chroot into the directory. There are several issues with this because processes running inside the chroot will use the chroot’s /etc/passwd, /etc/group, etc. If an application within the chroot tries to privilege drop or start as a user in the chroot’s /etc/passwd, it may not map in expected ways (eg, the security policy might allow dropping to the ‘daemon’ uid on the host, but ‘daemon’ in the chroot is a different uid so it is blocked).

Classic distro vs Core

An important consideration when designing users and groups for snappy is that:

  • Classic distro systems have writable passwd/group/shadow/etc databases
  • Core systems have read-only passwd/group/shadow/etc databases and writable ‘extrauser’ databases via libnss-extrausers
  • Classic distro systems don’t usually have libnss-extrausers installed
  • extrausers cannot be used to add users to system groups (ie, it may only be used to add users in the extrausers db to groups in the extrausers db)

Last updated 1 year, 3 months ago. Help improve this document in the forum.