As part of their fundamental, security-driven design, snaps are meant to run isolated from the underlying system. In most cases, the idea works well, and granular access to system resources using the mechanism of interfaces allows snap developers to ship their applications packaged with strict confinement.
However, there are some scenarios where even the liberal use of interface plugs cannot fully satisfy all of the functional requirements of specific applications. Certain programs need system-wide access to directories and files, and others may need to execute arbitrary binaries as part of their run. To that end, snaps can also be installed in the “classic” confinement mode, which gives them access similar to what the application would have if installed in the traditional way. The solution works, but now, there are proposals to make the classic mode even more robust and efficient.
The problem with classic confinement is that it takes away some of the predictability that exists in strictly confined snaps. Whereas one should expect a snap to behave the same way on all supported systems, classic snaps may rely on the host’s libraries to run, or may assume that certain libraries (and/or specific versions) are present. This can lead to potential conflicts in how applications are loaded and executed.
In order to run correctly, classically confined snap packages should require dynamic executables to load shared libraries from the appropriate base snap instead of using the host’s root filesystem. This means classic snaps would behave more like strictly confined snaps, which should lead to higher consistency and predictability of execution.
A new proposal in the works describes what is needed to verify dynamic linking parameters in binary files in a classic snap package.
Classic snap packages run on the host’s root filesystem, which may not match their build environment. To prevent incompatibilities, binaries in classic snaps must be built with appropriate linker parameters, or patched to allow loading shared libraries from their base snap. In the case of potential dynamic linking issues, the snap author must be aware that their package may not run as expected.
With the new proposal, the following dynamic linking parameters need to be covered:
To execute as expected, binaries in a classic snap application must be configured to look for shared libraries provided by the base snap or bundled as part of the application snap. This is achieved by setting the runtime path to shared libraries in all ELF binaries (except relocatable object files) that are present in the package payload.
The ELF file rpath must be properly set if:
The rpath value must be set to reach all NEEDED entries in the dynamic section of the ELF binary. If the binary already contains an rpath, then it should keep only those that mention $ORIGIN. Rpath entries that point to locations inside the payload must be changed to be relative to $ORIGIN. However, this does not include or support detecting paths to shared libraries loaded with dlopen().
There are various strategies to set rpath and the interpreter.
An ELF binary created during the parts lifecycle execution can have its rpath value set by using appropriate linker parameters. The linker is typically invoked indirectly via a compiler driver; in the gcc case, parameters can be passed to the linker using the -Wl option:
$ gcc -o foo foo.o -Wl,-rpath=$ORIGIN/lib,--disable-new-dtags -Llib -lbar
A similar strategy can be used to set rpath in a cgo binary:
package main
/*
#cgo LDFLAGS: -L${SRCDIR}/lib -Wl,-rpath=$ORIGIN/lib -Wl,--disable-new-dtags -lbar
#include "bar.h"
*/
import "C"
func main() {
C.bar()
}
In both cases, the inspection of the ELF dynamic section contents reveals the rpath value has been properly set:
Tag Type Name/Value
0x0000000000000001 (NEEDED) Shared library: [libbar.so]
0x0000000000000001 (NEEDED) Shared library: [libc.so.6]
0x000000000000000f (RPATH) Library rpath: [$ORIGIN/lib]
A snap payload may contain pre-built ELF binaries installed from arbitrary sources (typically from the distribution archive, after installing stage packages). In this case, rpath must be set by modifying the existing binary using a tool such as patchelf:
$ patchelf --force-rpath --set-rpath $ORIGIN/lib foo
$ readelf -d a | grep RPATH
0x000000000000000f (RPATH) Library rpath: [$ORIGIN/lib]
Patchelf can also be used to change the interpreter to a different dynamic linker:
$ patchelf --set-interpreter /lib64/ld-linux-x86-64.so.2 foo
$ readelf -p .interp a
String dump of section '.interp':
[ 0] /lib64/ld-linux-x86-64.so.2
Of course, there is no guarantee that every single binary can be patched, or that all use cases will be covered. Patching ELF binaries to modify rpath or interpreter entries may fail in certain scenarios, such as go executables linked with the go linker, or binaries using libc variants that require a nonstandard interpreter. Additionally, patching will cause signed binaries to fail validation.
As part of the improved developer experience, the snapcraft tool will also provide linter warnings, which should help the snap creators understand more accurately if there are any potential issues with the building of the snap using classic confinement. Specifically, the linter should issue warnings if the payload contains binaries that can load potentially incompatible shared libraries.
The classic confinement scenario is a difficult one, as it needs to provide repeatable, consistent behavior and results in an unpredictable environment (the user’s machine). The Snapcraft team is trying to make the experience as elegant as possible, so that developers can reliably package their applications as classic snaps, and ship them to their users. In this article, we covered some of the tools and methods used to improve this functionality. If you have any questions or ideas regarding classic snaps, please join our forum and tell us what you think.
Photo by Rustyness on Unsplash.
You’ve recently installed VMware Workstation on your Ubuntu system and encountered the frustrating “Could not…
Have you ever found yourself staring at a terminal full of 404 errors while trying…
One particularly frustrating error that many users face when trying to upgrade from Ubuntu 18.04 …
In the world of containerization, time synchronization issues can create unexpected roadblocks when working with…
If you’ve recently upgraded to Ubuntu 23.04 or newer, you might have encountered a frustrating…
Canonical announces the General Availability of Ubuntu for the NVIDIA® Jetson Orin™ for edge AI…