|
| 1 | +--- |
| 2 | +title: Hover, the CLI tool to protect your home |
| 3 | +date: 2024-06-10T14:08:37Z |
| 4 | +tags: [] |
| 5 | +draft: false |
| 6 | +summary: Never again run apps fearing they will polute your files. |
| 7 | +--- |
| 8 | + |
| 9 | +Hey! 👋 |
| 10 | + |
| 11 | +I wrote `hover-rs`, a very cool CLI tool. When you enter a `hover`, the arranges |
| 12 | +some private mount namespaces. Every modification to files (creation, deletion |
| 13 | +or modification) is lost when you exit hover. |
| 14 | + |
| 15 | + |
| 16 | + |
| 17 | + |
| 18 | +``` |
| 19 | +$ hover # Enter hover by just running the command |
| 20 | +You are now hovering~ |
| 21 | + A layer is covering your /home/ayats |
| 22 | + You can find your top layer in: /home/ayats/.cache/hover-rs/layer-2024-06-11-0635-evCxznv |
| 23 | +
|
| 24 | +$ touch foo # File "foo" is created under the hover |
| 25 | +
|
| 26 | +$ exit # Hover is just a subprocess |
| 27 | +
|
| 28 | +Leaving hover |
| 29 | + You can find your top layer in: /home/ayats/.cache/hover-rs/layer-2024-06-11-0635-evCxznv |
| 30 | +
|
| 31 | +$ file foo # File "foo" is gone |
| 32 | +foo: cannot open `foo' (No such file or directory) |
| 33 | +``` |
| 34 | + |
| 35 | +## A note on namespaces |
| 36 | + |
| 37 | +When you call hover, it uses the |
| 38 | +[`clone`](https://man7.org/linux/man-pages/man2/clone.2.html) libc function, |
| 39 | +which creates a new subprocess from process. `clone` has some settings to |
| 40 | +customize how this process is created, and we use the namespace configuration. |
| 41 | + |
| 42 | +"Linux Namespaces" or simply namespaces, are a special machinery of the Linux |
| 43 | +kernel that allows the developer to dissociate parts of the execution of a whole |
| 44 | +process. There are 5 different namespaces: |
| 45 | + |
| 46 | +- User namespace |
| 47 | +- Mount namesapce |
| 48 | +- PID namespace |
| 49 | +- Network namesapce |
| 50 | +- IPC namesapce |
| 51 | +- UTS (domain name) namespace |
| 52 | + |
| 53 | +These are the primitives that all the container runtimes, like Docker or |
| 54 | +Singularity, use the isolation of the container from the host system. For hover, |
| 55 | +we only use the first the first two. |
| 56 | + |
| 57 | +The **mount namespace** lets us rearrange mountpoints in a subprocess, such that |
| 58 | +the parent process doesn't "see" these mounts. For hover, we arrange an |
| 59 | +`overlayfs` on top `$HOME`. |
| 60 | + |
| 61 | +The **user namespace** is only used because only root can setup a mount |
| 62 | +namespace. With a user namespace, we can become root (can't fight with this |
| 63 | +logic, right?). In reality, inside a user namespace, we can remap user and group |
| 64 | +ids, such that some functionality is allowed through. For example, we can |
| 65 | +arrange a mount namespace, and calls to `id` we show `root`, but we are not able |
| 66 | +to modify files in `/etc`. |
| 67 | + |
| 68 | +Another function that can arrange namespaces is |
| 69 | +[`unshare`](https://man7.org/linux/man-pages/man2/unshare.2.html), which has its |
| 70 | +own CLI tool that you can play with: |
| 71 | + |
| 72 | +``` |
| 73 | +$ unshare -Umr # -U => unshare user namespace |
| 74 | + # -r => map current user to root |
| 75 | + # -m => unshare mounts namespace |
| 76 | +
|
| 77 | +# id |
| 78 | +uid=0(root) gid=0(root) groups=0(root),65534(nogroup) |
| 79 | +
|
| 80 | +# mount -t tmpfs tmpfs /var/empty |
| 81 | +
|
| 82 | +# findmnt -T /var/empty |
| 83 | +TARGET SOURCE FSTYPE OPTIONS |
| 84 | +/var/empty tmpfs tmpfs rw,relatime,uid=1000,gid=100 |
| 85 | +
|
| 86 | +# exit |
| 87 | +
|
| 88 | +$ id |
| 89 | +uid=1000(ayats) gid=100(users) ... |
| 90 | +``` |
| 91 | + |
| 92 | +Once the new mounts are configured, you can `unshare`/`clone` again to become |
| 93 | +your original user and group. |
| 94 | + |
| 95 | +## A note on overlayfs |
| 96 | + |
| 97 | +"An overlay filesystem is a pseudo filesystem that implements a union mount for |
| 98 | +other filesystems." |
| 99 | +([`mount.8`](https://man7.org/linux/man-pages/man8/mount.8.html)) |
| 100 | + |
| 101 | +The overlayfs allows us to redirect writes into a path to another. The overlayfs |
| 102 | +is composed of a sandwhich with the following elements.: |
| 103 | + |
| 104 | +- An upperdir: where new files or modifications are written to. For example, |
| 105 | + after we do a `touch foo`, the file is actually stored at `upperdir/foo`. |
| 106 | +- A lowerdir: the overlayfs shows a read-only of the lowerdir, which can be |
| 107 | + configured to be multiple directories stacked on top of each other. |
| 108 | +- The mountpoint: this is where the overlayfs is exposed. |
| 109 | +- A workdir: this is the most uninteresting. An implementation detail of |
| 110 | + overlayfs which we need to setup. |
| 111 | + |
| 112 | +Overlayfs is also used by container runtimes like docker, to merge the read-only |
| 113 | +container image (lowerdir), with any files that you create, remove or modify |
| 114 | +within the container (upperdir). For example, you can do a `rm /usr/bin/cd` |
| 115 | +within a container of alpine, but the original image is left unmodified in your |
| 116 | +disk. |
| 117 | + |
| 118 | +## Limitations |
| 119 | + |
| 120 | +One of the problems of user namespaces, is that as an unprivileged user, you can |
| 121 | +only map 1 group. In my machine, my user is part of many groups, that within |
| 122 | +hover are unknown: |
| 123 | + |
| 124 | +``` |
| 125 | +$ id |
| 126 | +uid=1000(ayats) gid=100(users) groups=100(users),1(wheel),17(audio),19(uucp),26(video),27(dialout),57(networkmanager),62(systemd-journal),174(input),984(tss),994(incus-admin) |
| 127 | +
|
| 128 | +$ hover id |
| 129 | +uid=1000(ayats) gid=100(users) groups=100(users),65534(nogroup) |
| 130 | +``` |
| 131 | + |
| 132 | +This may be a problem if you interact with any of the programs that rely on this |
| 133 | +grups. One solution could be to code hover have a "rootful" mode, but that may |
| 134 | +defeat its purpose... |
| 135 | + |
| 136 | +And of course, this relies on the Linux machinery, so it is not a thing on MacOS. |
| 137 | + |
| 138 | + |
| 139 | +## Installation |
| 140 | + |
| 141 | +The hover repo provides a Nix flake where you can get the package from. |
| 142 | + |
| 143 | +``` |
| 144 | +nix build github:viperML/hover-rs |
| 145 | +``` |
| 146 | + |
| 147 | +I also build statically-linked binaries that are published into github releases: |
| 148 | +https://github.com/viperML/hover-rs/releases/tag/latest . |
| 149 | + |
| 150 | +``` |
| 151 | +$ curl -OL https://github.com/viperML/hover-rs/releases/download/latest/hover-static-x86_64-linux |
| 152 | +$ chmod +x hover-static-x86_64-linux |
| 153 | +$ ./hover-static-x86_64-linux |
| 154 | +``` |
| 155 | + |
| 156 | +Being a rust project, of course you can also build manually with `cargo`. |
| 157 | + |
| 158 | +Have fun! |
0 commit comments