Makefile changes, usage message changes, README updates

This commit is contained in:
Evgeny Zinoviev 2020-12-08 04:17:32 +03:00
parent 34a0a81ed2
commit 7adff40839
4 changed files with 227 additions and 85 deletions

View File

@ -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
.PHONY: run undo install-run install-undo clean

275
README.md
View 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
```
$ make
$ sudo make install
```
Note that installed binary must be owned by root and have suid bit. `make install`
should handle it.
## Usage
```
voidnsrun [OPTIONS] PROGRAM [ARGS]
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
```
**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 something like this to your `~/.bashrc` or similar script:
```
export VOIDNSRUN_DIR=/glibc
```
By default, **voidnsrun** binds these paths from alternative root to the new
namespace:
- `/usr`
- `/var/db/xbps`
- `/etc/xbps.d`
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`
If you want to bind something else, use the `-m` argument.
## Example
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.
First, you need to perform an alternative glibc base system installation to a
separate new directory:
As per the [Void documentation](https://docs.voidlinux.org/installation/musl.html#glibc-chroot),
perform glibc base system installation to a separate new directory:
```
# mkdir /glibc
# XBPS_ARCH=x86_64 xbps-install --repository=http://alpha.de.repo.voidlinux.org/current -r /glibc -S base-voidstrap
```
Export path to this installation for **voidnsrun**:
### Installing voidnsrun
Clone the repo, and then:
```
make run
sudo make install-run
```
This will install **voidnsrun** to `/usr/local/bin`.
Export path to the container:
```
export VOIDNSRUN_DIR=/glibc
```
Try launching your app:
Also export path to **voidnsundo**:
```
voidnsrun /opt/vivaldi/vivaldi
export VOIDNSUNDO_BIN=/usr/local/bin/voidnsundo
```
It won't work just yet:
You may want to add these exports 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:
```
sudo voidnsrun -r /glibc xbps-install -Su
sudo voidnsrun -r /glibc xbps-install make gcc
```
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
```
This will install **voidnsundo** to `/usr/local/bin` in the container (which is
`/glibc/usr/local/bin` in reality).
Type `exit` or `Ctrl+D` to exit the container.
## Usage
### voidnsrun
```
Usage: voidnsrun [OPTIONS] PROGRAM [ARGS]
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.
```
**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]
Options:
-V: Enable verbose output.
-h: Print this help.
-v: Print version.
```
**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
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
```
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

View File

@ -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);

View File

@ -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)