Snap vendor lock-in approach to confinement is problematic

When new technology or ideas are looking for wide adoption, it’s imperative that development teams take a holistic approach to supporting the widest amount of use cases, and not partake in a distribution war that only further takes Linux distributions down the path of vendor lock in. The intent of linux has always been an open standards approach.

Take for instance the confinement case of snapd. Instead of using an open standard approach to security confinement based on the customer choice, it is locked-in to your distro of choice. While some may wish to debate the confinement choice of selinux vs apparmor (and that would be wrong because you can not compare an approach based on security science with a formal mathematical model vs one based on pseudo-science and security theater), this should be a choice and not hard coded. That only reduces the likelihood of snap adoption. The confinement choice is binary - you only run one or the other, and that should be a developer decision.

Remedies:

  1. modify autogen.sh, rules, control, and snapd.install file to identify the library installed as the configure options…not the distro. ie “if libselinux, then --enable-static-libselinux”, “if libapparmor, then --enable-static-libapparmor”. We have tested this on Ubuntu 20.04 with selinux in enforced mode and all unit tests pass.
  2. fix all unsquashfs in snapd/tests and squashfs.go files to use “unsquashfs -u” so that snapd can be compiled with user-xattrs support and you don’t have to compile as root (I don’t think I have to discuss why that is not a good approach). We have tested this on Ubuntu 20.04 with selinux in enforced mode and all unit tests pass.
  3. Include the snappy selinux policy in the standard selinux refpolicy. There is no reason why this should only be available in the fedora policy making it a RHEL/Fedora only option.

Let’s focus on democratizing sound security confinement capabilities and end the madness of vendor lock-in with new technology. This only helps the Linux cause, instead of giving yet another argument for the closed source pundits to use against the community.

I dont think the selinux policy used in fedora based distros has anything to do with confinement, it just allows snapd to run at all on these distros, if you use selinux, any file sytem based confinement (as apparmor brings along) is effectively disabled as there is no selinux support in the sandboxing at all yet … that you have to make a binary choice here is a kernel decision and can only be fixed once stackable LSM is implemented in the kernel …

you are always free to contribute selinux based sandboxing, i’m sure the snapd team will happily accept sane PRs, the snapd code is at https://github.com/snapcore/snapd/

1 Like

Looking at the selinux code for snappy doesn’t give me the impression that confinement isn’t implemented - it just doesn’t use a filesystem implementation. File system based confinement may be the apparmor approach, but that doesn’t suggest it is a superior or the only sound confinement approach.

Container-selinux implements the label attribute sandbox design used for podman, buildah, crio, and all supported container runtimes, isolating each container to its own sandbox domain (cXXX,sXXX).

It would be ideal for the fedora container-selinux policy to be ported to the standard refpolicy (looking for more developers there willing to contribute with me), and leverage such isolation for snapd with libselinux (contribute that as well).

I did not make any accusation of which is better than the other (unlike you :slight_smile: ). I’m not qualified to make such a statment and trust the choices of our security team, but the sandboxing isnt just wrapping a sandbox around executables, there is the whole interface system, udev tagging etc etc that is deeply integrated with the confinement and the chosen LSM. to make this work (and work in a non-intrusive way so the existing implementations does not regress) with selinux will be a whole lot of work for a few full time develpers i suppose …

1 Like

No accusation on your part - didn’t want to give that impression. Your engagement in the discussion is appreciated.

I surely have a view that is tainted by the Bell-Lapadula and Biba integrity security models and their implementation in the design of SELinux. You can test the formal proofs of the basic security theorem, the ss property, and the *-property in the implementation of SELinux where any formal model also requires possible means of subversion (side-channel attacks, timing attacks, KASLR bypass, etc.). There is significant research and science in such an approach that makes determining soundness formal vs arbitrary. While arguments are made about the complexity of SELinux, it’s evidence of the de-prioritization of full understanding of what rights and permissions are necessary to run a process and a de-privilege by default implementation of MAC policy enforcement, as well as opt-out vs opt-in policy confinement.

I don’t believe the security team is making a choice, but the approach of confinement is in the context of what the current development team can provide with the resources available. That I do understand. But it does limit the adoption - and lately more development is being done that is distro specific. SELinux strict policy enforcement DOES work on Ubuntu 20.04 (refpolicy 20210203 + 43 policy additions) and even works well with user confinement (no unconfined users) because we use it extensively and exclusively.

In terms of work - count me in. There are multiple approaches that were brought up previously in 2017 -


But there are more options available to achieve this now.

2 Likes

I’ve been trying to maintain the snapd policy module for a while now and I did the changes to the lower level bits so that they are aware of SELinux presence and try to act accordingly (or at least not break things in most obvious ways). However, there are at least 2 problems that a problem SELinux security backend in snapd would be facing, those are technical expertise and resources. While the security team is well versed in AppArmor and are active in the upstream, the snapd dev team are sufficiently familiar with it, I think I’m the only one with some working knowledge of SELinux on the team. I definitely do not feel an expert in the subject. Meaning, unless someone from the community who has the needed expertise steps forward, it will be rather complicated to review both code and policy. (As a side note, the universal lack of any useful resources on LSMs doesn’t make that any easier).

If you have any specific ideas about the technicalities of the implementation, please share them in the forum or IRC. I’ll be more than happy to discuss and help you prepare the changes for upstream.

2 Likes

The snapd sandbox is built from a number of technologies: AppArmor being just one part. The final AppArmor policy for a snap application is built from a common template:

https://github.com/snapcore/snapd/blob/master/interfaces/apparmor/template.go

… plus additional snippets generated by the plug and slot interfaces the snap may use:

https://github.com/snapcore/snapd/tree/master/interfaces/builtin

These additional snippets handle things like e.g. allowing snaps that plug the x11 interface to access the X11 socket. This is the code that we’d need an SELinux equivalent of to get the same level of protection.

The current “snappy” SELinux policy files you mentioned don’t even attempt to implement this part of the sandbox, so it is a non-trivial task.

Wow, huge article but very useful. Thanks.

I think the only reasonable way to introduce another or several LSM backends would be to rewrite the AppArmor rules into a generic DSL that gets compiled to the relevant configuration for each supported backend. Otherwise, we’d be spending time debugging issues where one LSM has different rules to AppArmor through oversight when adding or changing things.

This is all hypothetical though, because we don’t have the knowledge of other LSMs that we do of AppArmor.

2 Likes

That is a fascinating idea though!

I don’t think the security systems are similar enough to make that kind of thing possible to implement (beyond trivial policies). If it was, then I would expect that someone would have tried to port the user space utilities of either AppArmor or SELinux to the kernel side of the other.

For file-level access controls, an SELinux policy is written in terms of labels applied to file inodes. All the policies on the system act on that same list of labels, which imposes some consistency requirements between policies.

It’s pretty easy to introduce new labels for new files introduced by snapd (e.g. /var/snap and /var/lib/snapd). It’s harder if you need to work with the existing labelling of the rest of the file system.

In particular, things could get difficult if snapd needs to distinguish two groups of files that are currently labelled identically: you could change the labelling rules, but you’d also need to change all the existing system policies that relied on the old labelling. From the other end, any divergence in the labelling rules of two distros could introduce difficulties in writing a compatible policy.

Then there’s cases of functionality that is simply missing from one system or the other. One fairly large chunk is D-Bus message mediation: there is currently no SELinux equivalent of the AppArmor mediation built into dbus-daemon.

2 Likes

Last time I checked, SELinux also supports D-Bus mediation, but it’s not as fine-grained as AppArmor’s: it mediates up to the interface, whereas AppArmor rules also allow specifying method and signal names.

Thanks for checking up on that. Looking at the docs again, it looks like the SELinux support is limited to asking the kernel whether the two processes should be allowed to communicate. So it’s at an even lower level than D-Bus interfaces.

My main point was that these security systems aren’t simply different syntaxes for controlling the same security primitives: they express different things. It’s not clear an attempt to create a new language representing the common subset of their functionality would be particularly useful for writing real world policies.

Disclosure: I’m not even close to knowledgeable enough about either to have anything worthwhile to add to the discussion. :slight_smile:

But another possible approach would be to create a new language that represented the most detailed and expressive parts of both and when rendered to the other it just has to have that flavor’s “best”.

Isn’t that essentially what you get when you hand craft the rules anyway?

The problem is that SELinux profiles are not self-contained: unlike AppArmor, they are based on filesystem attributes to be set on those files whose access we want to mediate, and it’s not obvious to me whether snapd could/should change these labels all around the filesystem. Also, some rules are difficult to translate; think for example of a rule like:

  /home/**/.gitconfig rw,

This AppArmor rule applies to all .gitconfig files stored under /home, present and future: but I’m not sure if you can write a SELinux equivalent, since it could at most operate on the existing files (unless SELinux has some daemon which watches the filesystem and automatically applies labels based on the files’ path, but I’m not aware of that).

I’m afraid that the systems are just too different to be able to handle them with a common language.

Most of the time the label is inherited from the parent, i.e. your $HOME/foo has a user_home_t label, thus a new file under $HOME will also get the same label. The policy can also specify automatic transitions, such that your $HOME is actually user_home_dir_t, but files created inside by unconfined_t (the usual user), get transitioned to user_home_t, similarly ~/.ssh is labeled as ssh_home_t rather than user_home_t. This describes the transitions of user_home_dir_t -> user_home_t for the unconfined user:

type_transition unconfined_t user_home_dir_t:dir user_home_t;                          
type_transition unconfined_t user_home_dir_t:fifo_file user_home_t;                
type_transition unconfined_t user_home_dir_t:file user_home_t;             
type_transition unconfined_t user_home_dir_t:lnk_file user_home_t;                 
type_transition unconfined_t user_home_dir_t:sock_file user_home_t; 

TBH, the core policy is quite complex and I’m not really sure there’s many people who actually grasp all the details. Then add MLS/MCS on top. Oh and a fun fact, the policy is written in the a policy DSL policy, but the modern way to specify this is to use CIL.

And fianlly both LSMs have equally scarce documentation, which really doesn’t make reasoning about supporting both any easier.