Multiple users and groups in snaps


#3

Design

This design has been discussed with Gustavo, the snapd team and the security team. Details remain and will be discussed in this topic.

Overall design

snapd will work in conjunction with the snap store to use a ‘global ID database’. This database will provide a predictable 1 to 1 mapping of numeric ID to name. When a snap is installed using one of these IDs, snapd will consult the global database and create a group with a name that matches the global name (prefixed with snap_) and gid that matches the global ID. It then creates a user with a name that matches the global name (prefixed with snap_) and uid that matches the global ID with the primary group set to the global name. In this manner, snaps may specify these usernames/groups in their yaml and use the uid/gid in the squashfs such that the uid/gids will resolve properly via getpwnam(), getpwuid(), getgrnam(), getgrgid(), etc both inside and outside the snap.

To avoid clashes with existing uids and gids, snapd will use very high numbered IDs in the 32-bit range. Historically, early on 15-bit uids were supported and later the more common 16-bit uids. Linux and modern Unix systems now all support 32-bit uids with all but the most specialized filesystems (eg, cramfs) also supporting 32-bit uids by default (eg, ext2 and ext4 supports the non-default nouid32 option for interoperability with older (ancient) kernels). Notably, squashfs is confirmed to support 32-bit uids and gids.

To use, a snap optionally declares the username/groups it wants to use in the snap.yaml (exact yaml TBD). Eg:

name: foo
apps:
  ...
global-ids:
- foo
- bar

The store will issue a snap declaration assertion allowing the use of the name.

Upon installation, snapd will verify the snap declaration to see if the snap should be able to use the ID. If not, ignore. Otherwise snapd will first create the group (--extrausers is used on Core only):

$ sudo groupadd [--extrausers] \
    --system \
    --gid <ID from global db> \
    snap_<name from global db>

then create the user with that group:

$ sudo useradd [--extrausers] \
    --system \
    --comment "snapd global user <name from global db>" \
    --home-dir /nonexistent -M \
    --shell /bin/false \
    --uid <ID from global db> \
    --gid <ID from global db> \
    snap_<name from global db>

then snapd will generate security policy that allows use of this username and group.

At this point there are predictable uids/gids on the system that can safely be used by the squashfs, however if the snap does getent passwd foo it won’t be found because snap_foo was created. snapd should create a mapping such that inside the snap, the user doesn’t need snap_ to be prefixed, but outside it does. While the implementation details are not finalized yet, in the core/base snap, ship a glibc NSS module and add it to /etc/nsswitch.conf (will need to bind mount this on classic). This module will appropriately map the getpwnam(), getpwuid(), etc calls correctly for the snap’s processes.

Locally unasserted snap installs won’t have a snap declaration assertion telling snapd what IDs to create/use. For these, snapd will use a private ID range and will assign the username/group from this pool, locally. This range will not intersect with the global ID range. For example, if the snap.yaml had (and the snap was installed with --dangerous):

global-ids:
- foo

then snapd will take a free ID from the private range and call groupadd/useradd with this this ID.

Use case 1: System user and groups

This case is mostly handled by the current design. The snap declares what it wants to use and it can then chown, setuid, etc to the requested user/group. It does not have to prefix snap_ due to the NSS module.

What is not covered are cases like docker, lxd, libvirt, etc where the application is checking the group membership of a connecting process’ uid and upstream documentation says to add the user to a particular group (that isn’t prefixed with snap_). For example, lxd might have upstream documentation that says to run sudo adduser <user> lxd for <user> to be able to access the lxd socket. The lxd snap might have:

name: lxd
global-ids:
- lxd

which will create the snap_lxd user. Administrators then must use sudo adduser <user> snap_lxd to grant access to the lxd socket to the <user>. The question remains in whether we force upstreams to modify their documentation for snaps or to support non-snap_-prefixed users and groups. One idea would be to use system-global-ids that operates exactly like global-ids except that snap_ isn’t prefixed.

Use case 2: Device access

For things not covered by udev and systemd automatically via shipped configuration, we use setfacl to tag devices via udev rules. Eg, today serial devices are root:dialout 0660 and have no udev ACLs on them. Moving forward, we want a new backend that would be used by interfaces like the serial-port interface to specify that all serial-ports can be accessed via a particular group (eg, snap_serial-port) and the backend would add a udev rule to setfacl the serial-port devices (or use the uaccess mechanism) to ‘snap_serial-port’ such that sudo adduser <user> snap_serial-port would grant access to the serial ports for that user.

For managing the groups, there are two choices:

  • put the group in the global ID database and have the backend create a group with the corresponding name and gid. Eg, have snap_serial-port in the global ID database
  • have the backend dynamically create the group specified by the interface. Eg, the serial-port interface tells the backend to create the snap_serial-port group

Because the group isn’t needed by the snap itself, maintaining the group in the global ID database is not required and keeping them separate may provide a cleaner separation of the use cases.

An example workaround based on the above that might inform the implementation is documented.

Use case 3: Opt-in per-snap users/groups

This case is completely handled by the current design when shared global IDs (ie, where developers don’t mind that another snap might use the same IDs it requested) are ok. The snap declares what it wants to use and it can then chown, setuid, etc to the requested user/group. It does not have to prefix snap_ due to the NSS module.

The security team identified that a future improvement of the feature would be to support ‘private global IDs’ for security-concious developers who want guarantees that the IDs it uses are only used by the snap. For example, the snap.yaml might have:

name: example
private-ids:
- foo
- bar

which would create the snap_example_foo and snap_example_bar users which could only be used by this snap. To support squashfs files using predictable uids/gids, then this probably needs to be maintained in a private global ID list (to not expose private or brand snaps).

Use case 4: Chroot environments

This case is completely handled by the current design. The snap declares what it wants to use and the squashfs can reliably have files with uids/gids matching the requested global IDs.

Open questions

  1. What is the starting uid/gid number of the range. Perhaps 2147483648 (ie, 2^31)?
  2. What is the private uid/gid range? We don’t need many of these
  3. How will snapd handle clashes with existing uids/gids on the system and the global database? useradd uses --non-unique? Panic?
  4. Finalize yaml declaration
  5. Make decision on whether or not to support users/groups not prefixed with snap_
  6. Make a decision on using the global ID database or dynamically creating users for use case 2 (setfacl/uaccess)
  7. While not discussed above, consider exposing usernames/groups from the host in the snap’s runtime (eg, the NSS module prefixes classic_ to uids on the host). Consider how to handle exact overlaps (should classic_ be prepended or not?), uids/gids that have different usernames/group names between the host and snap and usernames/group names that have different uids/gids between the host and snap
  8. Consider ‘private global IDs’ and how they might be implemented

Components to modify

  • snapd
  • user backend for creating users/groups
  • backend for setfacl/uaccess
  • snap declaration verification
  • support private ID ranges
  • seccomp updates to allow using the uids/gids
  • store
  • easily view and update the global ID database (preseeded with archive grep of users and groups created in debs via adduser/addgroup/useradd/groupadd)
  • easily update snap declarations
  • pass global ID database to the review tools
  • review tools
  • warn if using uid/gid without snap decl (ie, standard human review, like with classic confinement)
  • don’t complain if not -all-root anymore, but do complain if uids, gids are not in the declared list
  • complain if use IDs from the private ID range

HDMI Output with ffplay
#4

@niemeyer, @mvo, @zyga, @tyhicks - please review this topic to see if I accurately captured the sprint agreements. There are also a number of open questions we should discuss.


#5

FYI when deciding on an ID range: https://bugs.launchpad.net/ubuntu/+source/util-linux/+bug/1707645. It seems that while they are supported, there may be some corner cases (like lastlog) where software is written to efficiently account for them.


#6

@jdstrand Thanks for putting this together! It does capture what we discussed well.

I’ll follow up with proposals for some of those questions soon.


#7

I don’t even have this module in Fedora in the repositories, much less enabled as a source in nsswitch.conf. A quick lookup indicates this was a module built several years ago by someone in Debian and it lives pretty much exclusively in Debian/Ubuntu archive. The only external source we have supported out of the box is the sss NSS module (which, because it’s SSSD, does actually support multiple backends and such, but there’s nothing we have that can interface with it right now).


#8

That is useful information. Thankfully, the extrausers module is only required in the core snap for Ubuntu Core (ie, not cross-distro). As such, it can live in the core snap and base snaps (eg, ‘Fedora base’) don’t have to worry about it. Only if someone wanted to create an alternate core snap based on a different distribution would extrausers (or an alternative) need to be considered. It would even be possible for that alternate core to skip the need entirely and make the passwd and group database read/write.


#9

Only when making all of core read write :wink: else UID/GID filesystem mappings will eventually fall apart …


#10

My point was that would ultimately be a decision for whoever was putting together ‘Alt Core’ devices, not that it would be a recommended thing to do.


#11

Hi,

I have a very strange behavior with my machine that is in AD (SSSD). For example, you can not list the files using hello-world.sh.

modolo@gsat019046:~$ snap run hello-world.sh
Launching a shell inside the default app confinement. Navigate to your
app-specific directories with:

  $ cd $SNAP
  $ cd $SNAP_DATA
  $ cd $SNAP_USER_DATA

bash-4.3$ ls
ls: cannot open directory '.': Permission denied
bash-4.3$ id
uid=265601806 gid=265600513 groups=265600513,4(adm),7(lp),24(cdrom),27(sudo),30(dip),44(video),46(plugdev),113(lpadmin),128(sambashare),131(lxd),137(kvm),138(libvirtd),997(rkt),265602411,265602421,265604408,265604415,265609914,265613528,265614005,265619450,265619467,265619643,265619720,265619721,265626944,265626955,265629586,265630854,265632970,265632975,265632993,265633732,265633737,265636639,265636643,265636657,265636659,265636673,265637680,265638362,265639476,265639496,265641502,265641854,265641855,265643223,265643255,265643431,265645967,265646099,265646372,265646461,265646560,265646775,265646882,265647071,265651567,265651622,265653636,265653673,265653733,265653757,265653855,265654408,265654550,265654552,265654561,265654890,265656369,265656370,265656371,265656375,265656378,265656379,265656380,265656382,265656384,265656386,265656388,265656390,265657145,265658322,265659423,265659533,265659589,265660036,265660834,265661748,265662107,265662110,265662117,265662118,265662131,265662136,265662139,265662143,265662145,265662543,265662739,265662740,265664437,265664505,265664999,265665490,265665669,265665758,265665833,265665886,265665887,265666570,265666594,265666614,265666618,265666619,265666620,265666621
bash-4.3$ 

I have verified that it is not possible to consult domain users!

bash-4.3$ getent passwd modolo
bash-4.3$ getent passwd root  
root:x:0:0:root:/root:/bin/bash

#12

Today, domain users need the ‘network’ interface and the hello-world snap does not ‘plugs: [ network ]’.

To make this work in the default template (arguably something that should be done), fine-grained network mediation in AppArmor (future work) might be able to allow name lookups. Alternatively, an nss module could be made available to snaps (eg, a bind mount) that could talk to an out of process proxy on the system to do the lookups on behalf of the application.


Call for testing: libreoffice 5.4.2
#13

We could also do something similar to what I proposed for NFS /home: make a core config that unconditionally allows the network interface/apparmor nameservice abstraction. I wonder how useful this (and NFS /home) would be per-snap rather than global config…


Snaps and NFS /home
#14

The lack of possibility to list users of the domain prevents me for example from completing the installation of snap libreoffice!

On the same machine if I authenticate myself as a local user, I can fire the libreoffice (snap run libreoffice) and complete the installation!

I believe the libroffice needs to check local folders (~ / .config) that are not available since the permissions become wrong.


Device connection via USB to Ubuntu-core
#15

@niemeyer - I noticed the topic title changed to ‘multiple users and groups in snaps’. This title doesn’t cover all four use cases and is somewhat confusing. The use case are (taken from the introduction):

"

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

#16

The bug links above are broken, the right links are:


#17

Just to add some user feedback to this point, the key thing missing here for us is use case 3.

Various servers will just point blank refuse to run as uid 0, with an explicit check in the codebase. Examples include postgresql and squid, both exactly the kind of software you are likely to want to embed in a snap.

The only option for us right now with postgresql is to src patch those checks out, and compile custom postgresql binaries to ship with our snap, something we are currently not willing to do (our build times would skyrocket, amongst other issues).

We would be ok with running as one of the ~20 or so default users (daemon? www-data? nobody? proxy?) in this case, but no filtering on setuid makes this impossible for a strictly confined snap.


#18

Any progress or changes for this?


#19

How does this design relate to an app like syncthing which needs per-user services that are run as individual users already on the system, and not as root? In this case we wouldn’t be able to enumerate ahead of time in the snapcraft.yaml what users to run as, so I’m not sure if any of the 4 use cases presented would solve this problem. Perhaps this is entirely orthogonal to the design here…

Note that the associated launchpad bug for this particular use case is here : https://bugs.launchpad.net/snappy/+bug/1613420


#20

in that usecase you would likely use a session systemd unit (which only works on newer systems where systemd manages the desktop session) … i think there was a topic somewhere in the forum about autostarting session related services (which would automatically run as the logged in user)

alternatively you could start it as dbus session service … that would run as the user as well …


#21

@dmitriis Still on the roadmap, but not in progress right now as we have a few high-cost features in progress. We will evaluate again at the end of the cycle in October.

@ijohnson It’s not quite related. What you refer to may already be achieved with something like the autostart feature, while the ideas covered in the topic here are more about multiple users inside the snap proper.


#22

FYI, there is some information for what this will most likely look like for use case 2 (device acls) in HDMI Output with ffplay.