diff options
-rw-r--r-- | Makefile | 22 | ||||
-rw-r--r-- | README.md | 243 | ||||
-rw-r--r-- | voidnsrun.c | 9 | ||||
-rw-r--r-- | voidnsundo.c | 6 |
4 files changed, 211 insertions, 69 deletions
@@ -6,15 +6,13 @@ LDFLAGS = INSTALL = /usr/bin/env install PREFIX = /usr/local -all: voidnsrun voidnsundo - test: testserver testclient -voidnsrun: voidnsrun.o utils.o - $(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS) +run: voidnsrun.o utils.o + $(CC) $(CFLAGS) -o voidnsrun $^ $(LDFLAGS) -voidnsundo: voidnsundo.o utils.o - $(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS) +undo: voidnsundo.o utils.o + $(CC) $(CFLAGS) -o voidnsundo $^ $(LDFLAGS) testserver: test/testserver.o utils.o $(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS) @@ -22,9 +20,13 @@ testserver: test/testserver.o utils.o testclient: test/testclient.o utils.o $(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS) -install: voidnsrun voidnsundo - $(INSTALL) voidnsrun voidnsundo $(PREFIX)/bin - chmod u+s $(PREFIX)/bin/voidnsrun $(PREFIX)/bin/voidnsundo +install-run: run + $(INSTALL) voidnsrun $(PREFIX)/bin + chmod u+s $(PREFIX)/bin/voidnsrun + +install-undo: undo + $(INSTALL) voidnsundo $(PREFIX)/bin + chmod u+s $(PREFIX)/bin/voidnsundo clean: rm -f *.o test/*.o voidnsrun voidnsundo testserver testclient @@ -32,4 +34,4 @@ clean: %.o: %.c $(CC) $(CFLAGS) -c $^ -I. -o $@ -.PHONY: all install clean distclean
\ No newline at end of file +.PHONY: run undo install-run install-undo clean
\ No newline at end of file @@ -1,86 +1,168 @@ # voidnsrun -**voidnsrun** is utility for launching programs in an isolated namespace with -alternative `/usr` tree. Its primary goal is to run glibc programs in -musl-libc Void Linux environments or vice-versa. +**voidnsrun** is utility for launching programs in an isolated mount namespace +with alternative `/usr` tree. Its primary goal is to run glibc programs in +musl-libc Void Linux environments (or vice-versa, but who needs that?). It creates a new private mount namespace, transparently substitutes `/usr` and -some other directories with directories from your alternative root using bind +some other directories with directories from your glibc container using bind mounts, and launches your program. +**voidnsundo**, to the contrary, is utility for launching programs in the parent +mount namespace from within the mount namespace created by **voidnsrun**. + ## Installation -Just clone the repo, and then: +### Creating glibc container +As per the [Void documentation](https://docs.voidlinux.org/installation/musl.html#glibc-chroot), +perform glibc base system installation to a separate new directory: ``` -$ make -$ sudo make install +# mkdir /glibc +# XBPS_ARCH=x86_64 xbps-install --repository=http://alpha.de.repo.voidlinux.org/current -r /glibc -S base-voidstrap ``` -Note that installed binary must be owned by root and have suid bit. `make install` -should handle it. +### Installing voidnsrun -## Usage +Clone the repo, and then: ``` -voidnsrun [OPTIONS] PROGRAM [ARGS] +make run +sudo make install-run +``` -Options: - -m <path>: add bind mount - -r <path>: altroot path. If this option is not present, - VOIDNSRUN_DIR environment variable is used. - -h: print this help - -v: print version +This will install **voidnsrun** to `/usr/local/bin`. + +Export path to the container: +``` +export VOIDNSRUN_DIR=/glibc +``` + +Also export path to **voidnsundo**: +``` +export VOIDNSUNDO_BIN=/usr/local/bin/voidnsundo ``` -**voidnsrun** needs to know the path to your alternative root directory and it can -read it from the `VOIDNSRUN_DIR` environment variable or you can use `-r` -argument to specify it. +You may want to add these exports to your `~/.bashrc` or similar script. -You may want to add something like this to your `~/.bashrc` or similar script: +### Installing voidnsundo +**voidnsundo** is supposed to be used from within the glibc container, so it has +to be linked with glibc. First, let's use just installed **voidnsrun** to install +build dependencies into the container: ``` -export VOIDNSRUN_DIR=/glibc +sudo voidnsrun -r /glibc xbps-install -Su +sudo voidnsrun -r /glibc xbps-install make gcc ``` -By default, **voidnsrun** binds these paths from alternative root to the new -namespace: -- `/usr` -- `/var/db/xbps` -- `/etc/xbps.d` +Then enter the container (the current working directory will be preserved by +**voidnsrun** 1.2 or higher) and build, then install **voidnsundo**: +``` +voidnsrun bash +make clean +make undo +sudo make install-undo +``` -But if you're launching `xbps-install`, `xbps-remove` or `xbps-reconfigure` -and using **voidnsrun** version 1.1 or newer, this is what it will bind: -- `/usr` -- `/var` -- `/etc` +This will install **voidnsundo** to `/usr/local/bin` in the container (which is +`/glibc/usr/local/bin` in reality). -If you want to bind something else, use the `-m` argument. +Type `exit` or `Ctrl+D` to exit the container. -## Example +## Usage -Let's imagine you want to use some proprietary glibc app on your -musl-libc Void Linux box. Let it be Vivaldi browser for the example. You -unpacked it to `/opt/vivaldi` and it doesn't work, obviously. +### voidnsrun +``` +Usage: voidnsrun [OPTIONS] PROGRAM [ARGS] -First, you need to perform an alternative glibc base system installation to a -separate new directory: +Options: + -r <path>: Container path. When this option is not present, + VOIDNSRUN_DIR environment variable is used. + -m <path>: Add bind mount. You can add up to 50 paths. + -u <path>: Add undo bind mount. You can add up to 50 paths. + -U <path>: Path to voidnsundo. When this option is not present, + VOIDNSUNDO_BIN environment variable is used. + -i: Don't treat missing source or target for added mounts as error. + -V: Enable verbose output. + -h: Print this help. + -v: Print version. ``` -# mkdir /glibc -# XBPS_ARCH=x86_64 xbps-install --repository=http://alpha.de.repo.voidlinux.org/current -r /glibc -S base-voidstrap + +**voidnsrun** needs to know the path to your glibc installation directory (or +"container"), it can read it from the `VOIDNSRUN_DIR` environment variable or +you can use `-r` argument to specify it. + +By default, **voidnsrun** binds only `/usr` from the container. But if you're +launching `xbps-install`, `xbps-remove` or `xbps-reconfigure`and using +**voidnsrun** version 1.1 or higher, it will bind `/usr`, `/var` and `/etc`. + +To bind something else, use the `-m` option. You can add up to 50 binds as of +version 1.2. + +There's also the `-u` option. It adds bind mounts of the **voidnsundo** binary +inside the namespace. See more about this below in the **voidnsundo** bind mode +section. Just like with the `-m` option, you can add up to 50 binds as of version +1.2. + +To bind the **voidnsundo** binary, **voidnsrun** has to know its path, and, like +with the container's path, it reads it from the `VOIDNSUNDO_BIN` environment +variable and from the `-U` option. + +### voidnsundo + ``` +Usage: voidnsundo [OPTIONS] PROGRAM [ARGS] -Export path to this installation for **voidnsrun**: +Options: + -V: Enable verbose output. + -h: Print this help. + -v: Print version. ``` -export VOIDNSRUN_DIR=/glibc + +**voidnsundo** can be used in two modes. + +One is the **"normal" node**, when you invoke it like `voidnsundo <PROGRAM> [ARGS]` +and your `PROGRAM` will be launched from and in the original mount namespace. + +For example, if you don't have a glibc version of firefox installed (so there's +no `/usr/bin/firefox` in the container), but you want to launch the "real" (the +one installed in your root musl system) firefox while being in the mount +namespace, just do `voidnsundo /usr/bin/firefox`. + +The other mode is the **"bind" mode**. While in the container, and therefore in +the new mount namespace, you can bind mount **voidnsundo** to any path (don't +worry: it won't be visible outside the namespace), and, when invoked by that +path, it will launch the corresponding executable in your parent (root) +namespace. + +For example, being in the container, you can do this: +``` +touch /usr/bin/firefox +mount --bind /usr/local/bin/voidnsundo /usr/bin/firefox ``` +and while there was no `/usr/bin/firefox` in the glibc container, after this, +when you'll launch `/usr/bin/firefox`, the "real" firefox from the root musl +system will be launched. + +The creation of this bind mounts of **voidnsundo** can be automated by using +`-u` option of **voidnsrun**. + +## Examples -Try launching your app: +This section contains some real examples of how to use some proprietary glibc +apps on your musl-libc Void Linux box. + +### Vivaldi + +The first example is the Vivaldi browser. Let's assume you unpacked it to +`/opt/vivaldi` (from rpm or deb package) and, obviously, it doesn't work. + +Try launching it with **voidnsrun**: ``` -voidnsrun /opt/vivaldi/vivaldi +$ voidnsrun /opt/vivaldi/vivaldi ``` -It won't work just yet: +It won't work just yet, but it's a start: ``` /opt/vivaldi/vivaldi: error while loading shared libraries: libgobject-2.0.so.0: cannot open shared object file: No such file or directory ``` @@ -99,7 +181,7 @@ glib-devel-2.66.2_1 /usr/share/gdb/auto-load/usr/lib/libgobject-2.0.so.0.6600.2- libglib-devel-2.66.2_1 /usr/lib/libgobject-2.0.so -> /usr/lib/libgobject-2.0.so.0 ``` -Sync repos and install `glib`. You can use **voidnsrun** for this purpose too. +Sync repos and install `glib`: ``` $ sudo voidnsrun -r /glibc xbps-install -Su $ sudo voidnsrun -r /glibc xbps-install glib @@ -113,9 +195,46 @@ $ voidnsrun /opt/vivaldi/vivaldi As you can see, it no longer complains about missing `libgobject-2.0.so.0`, now it's `libnss3.so`. Repeat steps above for all missing dependencies, and in the -end, it will work. (If it's not, then something's still missing. In particular, -make sure to install fonts related packages: `xorg-fonts`, `freetype`, -`fontconfig`, `libXft`.) +end, it will work. + +Note that, for some reason, it doesn't complain about missing font related +libraries, such as freetype, so make sure to install them too, as well as some +base fonts: +``` +$ sudo voidnsrun -r /glibc xbps-install freetype fontconfig libXft xorg-fonts +``` + +If you're noticing performance issues with Vivaldi, check the `vivaldi://gpu` +page. If it turns out that hardware acceleration is unavailable, you're missing +some packages again. I don't know which ones exactly, but installing `xorg-minimal` +should fix it. + +### PhpStorm + +**PhpStorm** and other JetBrains IDEs should just work like this (of course, +replace `/opt/PhpStorm` with real path on your machine): +``` +voidnsrun /opt/PhpStorm/bin/phpstorm.sh +``` + +But it is only at first glance, everything works. After some time you may +notice all kinds of weird stuff caused by the fact that it runs inside the +"container" with different `/usr`. For instance, if you open built-in terminal +window, it will work, but... it will not be the shell you expect, it will be +glibc-linked shell from the container. Some programs that you have +installed on your root musl system will not be available there (like, it won't be +able to launch a browser because there's no browser), other may not work as +expected. + +In general, all programs that launch other programs will suffer from this. To +overcome this, the **voidnsundo** utility has been written and `-u` option added +to **voidnsrun**. + +To fix the built-in PhpStorm's terminal and the ability to launch browser as shown +in the above example, launch it like so: +``` +voidnsrun -u /bin/bash -u /usr/bin/firefox /opt/PhpStorm/bin/phpstorm.sh +``` ## FAQ @@ -130,10 +249,32 @@ Defaults env_keep += "VOIDNSRUN_DIR" A: If you installed fonts on your main system, applications that run in the mount namespace can't see them because of custom `/usr` directory. You need to install -them again into the altroot directory. +them again into the container directory. + +Some workaround to bind-mount `/usr/share/fonts` from the root system to the +namespace may be introduced in future, if Linux will allow such hacks. + +## Security + +**voidnsrun** and **voidnsundo** are setuid applications, meaning they are +actually started as root and then dropping privileges when they can. setuid is +generally bad, it's a common attack vector that allows local privilege +escalation by exploiting unsafe code of setuid programs. + +While these utilities have been written with this thought in mind, don't trust +me. Read the code, it's not too big. Place yourself in attacker's shoes and try +to find a hole. For every new discovered vulnerability in these utilities that +would allow privilege escalation or something similar I promise to pay $25 in +Bitcoin. Contact me if you find something. ## Changelog +#### 1.2 + +- Added **voidnsundo** utility for spawning programs in the parent mount + namespace from withing the namespace created by **voidnsrun**. +- Restore current working directory after changing namespace. + #### 1.1 - Bind whole `/etc` and `/var` when launching `xbps-install`, `xbps-remove` or diff --git a/voidnsrun.c b/voidnsrun.c index d82d00e..8516343 100644 --- a/voidnsrun.c +++ b/voidnsrun.c @@ -34,12 +34,11 @@ void usage(const char *progname) " -r <path>: Container path. When this option is not present,\n" " " CONTAINER_DIR_VAR " environment variable is used.\n" " -m <path>: Add bind mount. You can add up to %d paths.\n" - " -u <path>: Add undo utility bind mount. You can add up to %d paths.\n" - " -U <path>: Undo program path. When this option is not present,\n" + " -u <path>: Add undo bind mount. You can add up to %d paths.\n" + " -U <path>: Path to " VOIDNSUNDO_NAME ". When this option is not present,\n" " " UNDO_BIN_VAR " environment variable is used.\n" - " -i: Don't treat missing source or target for an added mount\n" - " as an error.\n" - " -V: Verbose output.\n" + " -i: Don't treat missing source or target for added mounts as error.\n" + " -V: Enable verbose output.\n" " -h: Print this help.\n" " -v: Print version.\n", USER_LISTS_MAX, USER_LISTS_MAX); diff --git a/voidnsundo.c b/voidnsundo.c index 89dfefc..6720b8d 100644 --- a/voidnsundo.c +++ b/voidnsundo.c @@ -24,9 +24,9 @@ void usage(const char *progname) printf("Usage: %s [OPTIONS] PROGRAM [ARGS]\n", progname); printf("\n" "Options:\n" - " -V: Verbose output.\n" - " -h: Print this help.\n" - " -v: Print version.\n"); + " -V: Enable verbose output.\n" + " -h: Print this help.\n" + " -v: Print version.\n"); } int main(int argc, char **argv) |