With version 2.26.8 we introduced a new snap-seccomp tool to compile the seccomp profiles directly to the binary bpf representation when installing a snap. This works fine, however there is an issue on ubuntu 14.04 (and other distros from that time).
$ sudo snap install --beta core
on 14.04 there is an error:
error: cannot create seccomp filter: could not create filter- error setting tsync bit: file exists
The reason for this failure is that libseccomp on trusty is version 2.1.1 with some patches. The version in core is 2.3. Now the following happens:
- new core gets downloaded
- on install, snapd re-execs into the new core
- with the new core, security profiles are re-generated
- /snap/core/$num/usr/lib/snapd/snap-seccomp is run
- that snap-seccomp is build with libseccomp-golang with a version of libseccomp 2.3
- in the code of libseccomp-golang there is a check to only set the tsync bit if the libseccomp version is > 2.2
- however this code thinks it runs on 2.3 because the version is set at build time, not at runtime
- snap-seccomp tries to set the tsync bit, the old libseccomp does not know about it and errors
There are multiple ways forward:
Can we patch
libseccomp-golang to load the version dynamically at runtime and then do the right thing? If no then I think we must definitely choose one of the two alternatives. Out of those I’d lean towards
osutil.CommandFromCore since we want to rely on it in other places and this is a nice application to it.
Porting the new
seccomp_version() to trusty is straightforward: http://paste.ubuntu.com/25060932/. Once that has landed we will need to tweak our version of libseccomp-golang: http://paste.ubuntu.com/25060957/ and rebuild snapd on trusty only. I will update the parent to include this information
It turns out this port will also have to be done for xenial, the seccomp_version() function was added in libseccomp 2.3 but xenial has 2.2.
Another option is to backport libseccomp to trusty and require 2.3 everywhere, but I don’t think this is the best approach since we are trying to have snapd run on older releases, and it is probably not suitable for an SRU.
From the options you listed (in order of preference):
- CommandFromCoreWithRoot is nice because there are no code changes in the seccomp bits, just a change saying “this thing needs stuff from core, so run it with that”. I can’t really think of a disadvantage to this…
- I’m also not opposed to static linking since it is effectively doing the same thing, but at build time. Please make sure that in Debian/Ubuntu/derivatives we end up with the proper Built-Using if going this route.
- I don’t care for changing libseccomp in trusty/xenial because it means that alternate core snaps will have to be aware of this. Plus the tsync check seems somewhat valid though it seems like it would be better to have a runtime rather than build time check (I don’t know if that is possible)
I slightly prefer CommandFromCoreWithRoot at this time (unless I am missing something), but if others prefer static, that’s fine with me.
Gustavo suggested that I look into one additional option:
- embed libseccomp itself and link that statically as part of snap-seccomp
I created https://github.com/snapcore/snapd/compare/master...mvo5:embedded-libseccomp?expand=1 that does that. The relevant commit is: https://github.com/mvo5/snappy/commit/f77a3869ba46be31ddb72f1215af5105eee31faa.
The advantage of this solution is that we can drop using the custom fork of libseccomp we are using in trusty and our custom version of libseccomp-golang that is only needed because we have the custom libseccomp in trusty.
I’m not entirely opposed to this and it has a number of advantages, but it will also have its own costs. Normally I really hate embedding code (indeed, it is against MIR policy in Ubuntu in general and something that is strongly discouraged across other distributions) but in this case we are getting rid of one embedded copy (libseccomp-golang) for another (libseccomp), which I guess is ‘ok’.
Putting libseccomp from upstream in there does mean that we have to track upstream for security support, etc, which has a cost. Do you really want libseccomp upstream as opposed to using the source from xenial? If you pick something in the Ubuntu archive (especially an LTS like xenial), it will be something that you can more easily keep up to date with fewer regressions. Security/bug fix support then is straightforward-- pull back the updated sources from the archive and you should have less chance of regression. When 18.04 comes out, if wanted you could refresh your embedded copy to use that.
@jdstrand Good points, and it’s also worth noting that in this case what is being embedded is only the bits that compile the profiles which are generated by snapd itself into the binary BPF format, so I’d say the potential for abuse or even that an usptream security issue really affects that code is really low.
@mvo This looks good. I think we can move forward assuming this works across all distributions.
I worked on the option to embed libseccomp in https://github.com/snapcore/snapd/compare/master...mvo5:embedded-libseccomp?expand=1 (relevant commit https://github.com/mvo5/snappy/commit/6de6e4e260663c57037a846e6bd68beaad75d533) last night and this morning.
I got quite far but I think I hit a roadblock now: *_test.go files can not use cgo which means I cannot link against my copy of libseccomp in the tests (I can not add //#cgo LDFLAGS: -Bstatic -lseccomp -Bdynamic). But libseccomp-golang will be built with the embedded version of libseccomp and when the tests run there will be missing symbols on platform that use libseccomp < 2.3 (like trusty, xenial). I think this is still worth exploring further, but I want to avoid spending too much time on it.
I updated https://github.com/snapcore/snapd/compare/master...mvo5:build-statis-snap-seccomp?expand=1 and would suggest we do 2.26.9 with that (it has the nice benefit that the diff is very small and controlled). We can then explore options further, either work harder on the embedding or maybe we can make snapd always use snap-seccomp from the core snap instead of from the package. This way we would control the seccomp version and we can still drop the forked github.com/mvo5/libseccomp-golang
I also created https://bugs.launchpad.net/ubuntu/+source/libseccomp/+bug/1703580 and pushed a fix to trusty-proposed (wait-for-approval).
Fwiw, PR#3579 is green now and ready to go if everyone involved is happy.