Summary
The ‘Multiple users and groups in snaps’ topic outlines various problems related to users in snapd. The most requested feature from this topic is allowing the ability for a snap service to privilege drop to a specific user. As described from the other topic:
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).
For the first phase we want to introduce a single user that snaps can use. We’ve chosen the ‘daemon’ user which is defined by the LSB as a user and group that services may drop to. Furthermore, “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.”
Note that snapd “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.”. This is out of scope for phase 1.
Design
‘Multiple users and groups in snaps’ lists many details for how everything is meant to work together in the final implementation, but phase 1 will implement the relevant portions of the developer experience as documented but the backend implementation underneath will implement just enough to support one use case, the LSB-defined and required ‘daemon’ user.
From the other topic, the idea is that the snap would declare:
name: my-app
...
shared-users: # or global-ids (TBD)
- foo
then snapd would create the snap_foo
user and group and the snap may use foo
within the runtime of the snap (because an NSS module will translate foo
to snap_foo
and give back the uid/gid for snap_foo
; it is expected that the snap could always simply use snap_foo
if desired). It is understood that there are cases (eg, the lxd and docker snaps) where we don’t want to create a snap_-prefixed user, which is covered by the idea of system-users/system-global-ids
.
Because phase 1 will not implement the NSS module and the daemon
user already exists, the simplest option would be to expose this user via the system-users/system-global-ids
mechanism like so:
name: my-app
...
system-users: # or system-global-ids (TBD)
- daemon
In terms of implementation, the following will happen:
- because the
daemon
user already exists (see discussion points), we don’t need to create it, but we do want to perform a uid/gid lookup for inserting into the policy - abstract out the existing family of setuid/setgid system calls such that when opt-in users are not specified, these are added. This means that if snaps do not use the opt-in user feature, they get the same seccomp/apparmor policy we’ve always provided
- when a snap specifies using the feature, a new set of seccomp/apparmor rules is added to support privilege dropping/chown/chgrp
- snap-confine is updated to ensure it can still drop to the non-root calling user (this is needed because currently the drop in snap-confine happens after the seccomp policy is loaded)
Discussion points:
- the
system-users/system-global-ids
concept necessarily uses the system’s passwd/group databases and therefore the uid/gid is not guaranteed to be the same for the daemon user across distributions. This is only a problem for the “Use case 4: Chroot environments” which is out of scope of phase 1. IME,system-users/system-global-ids
cannot support the chroot use case; in the full solution we will simply document this and say thatshared-users/global-ids
must be used for the chroot use case. I’ve added a note for this to the other topic - Solus is known to not be LSB-compliant and doesn’t have the
daemon
user on the system (UPDATE: we will perform runtime detection and surface to the user)
UPDATE 2019-05-17:
- The original proposal used
global-ids
andsystem-global-ids
but after discussion these were changed toshared-users
andsystem-users
, respectively (in each case we kept the original proposal term as a reference) - as part of implementing https://github.com/snapcore/snapd/pull/6681, it was decided that snapd would detect at runtime if the daemon user was available to use, and it not, at snap install bubble up that the snap is uninstallable (with instructions on next steps)