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.
‘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 ... global-ids: - 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
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
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-global-ids mechanism like so:
name: my-app ... system-global-ids: - daemon
In terms of implementation, the following will happen:
- because the
daemonuser 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; the exact changes are TBD but likely entail appending a couple seccomp rules to the pre-compiled policy for dropping to the calling user at runtime)
system-global-idsconcept 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-global-idscannot support the chroot use case; in the full solution we will simply document this and say that
global-idsmust 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
daemonuser on the system