7

I'm using unshare to perform things like bind mounts local to a certain process without requiring root access, e.g.:

unshare -mr bash mount --bind a b

(Yes, this seems kinda dumb; in my actual use case; unshare is running a bash script that performs the bind mount. I didn't do that here so that it's a smaller example.)

However, if I try a loop mount, it fails:

ryan@DevPC-LX ~/stuff/util-linux master $ unshare -mr mount -o loop x.img a
mount: no permission to look at /dev/loop<N>

:/

I've tried using mknod to create a fake loop device (requires privileges not available to non-root), manually running losetup (still requires root privileges), and a bunch of other stuff that doesn't work.

Of course, I could just do chown myuser /dev/loop*, but that seems like it could become a major security issue.

In addition, guestmount is too slow for my use case, and fuseext2 has a warning about possible data loss in write mode (and it's also too slow).

Is there any way to do...this? At all?

kirbyfan64sos
  • 236
  • 2
  • 7
  • "Of course I could just do chown..." nope. "Note however, that mounting block-based filesystems can be done only by a process that holds CAP_SYS_ADMIN in the initial user namespace." – sourcejedi May 12 '17 at 22:30

2 Answers2

5

As you have clearly understood, creating a loop mount consists of two steps:

  1. Setting up the loop device
  2. Mounting it

Of course, I could just do chown myuser /dev/loop*, but that seems like it could become a major security issue.

I believe this would permit the creation of the appropriate loop device (by giving access to /dev/loopcontrol). I don't know if there's also some sort of namespacing available that affects the view of loop devices. This would presumably allow doing this more securely.

Step 2 is still a no-go though: User namespaces do allow the creation of new mount namespaces in which it's possible for users to create new mounts, but it's fairly limited — CAP_SYS_ADMIN in the initial namespace is still required to mount block devices: as user_namespaces(7) says…

Note however, that mounting block-based filesystems can be done only by a process that holds CAP_SYS_ADMIN in the initial user namespace.

Loop devices are block devices backed by files, so that's still a no-go. This is unfortunate, I do think there should be a way for this to work securely. But I imagine there are a lot of intricacies to it (particularly with setuid) which are the reason it hasn't been implemented yet.

So as far as I understand, all you can really do is work around the issue. Maybe you could extract the files from the image (if worst comes to worst, i.e. there are no tools available for working with the particular format directly, you could mount it in a temporary VM to do this), then bind-mount the resulting directory.

  • If all they wanted was to extract files from a mountable image, like an 'iso', they can use 7z. It treats "iso"s as a type of archive and can extract files or the whole image into a directory (w/files owned by the running user). It's also the case that running 7z doesn't take root-privs. But since the original Q didn't say they wanted to extract files from the image, but instead "mount it "privately" w/o needing root access", finding a workaround to extract files wouldn't answer the question either. – Astara May 23 '17 at 20:03
3

In order to run unshare, you have to have root capabilities to create a separate mount space.

I tried this that seems to do what you want (I think):

Ishtar:> mkdir -p /tmp/unshare/home
Ishtar:> cd /tmp/unshare
Ishtar:/tmp/unshare> sudo unshare -m /bin/bash
Ishtar:/tmp/unshare# mount --rbind /home/packages /tmp/unshare/home
Ishtar:/tmp/unshare# tty
/dev/pts/4
Ishtar:/tmp/unshare# # ls home
BUILD@      RPMS@     build/           linux@    sources/           tmp/
BUILDROOT@  SOURCES@  buildroot/       logs/     specs/
OSbuild/    SPECS@    config-scripts/  perlsrc/  srpms/
OTHER/      SRPMS@    debug@           rpms/     sysvinit-288.spec

So the above process has '/home/packages mounted @ /tmp/unshare/home.

In another tty-window, with any user, I can try to see: what is in /tmp/unshare/home:

Ishtar:/> tty
/dev/pts/5
Ishtar:/> ll /tmp/unshare/home
total 0
Ishtar:/> cd tmp/unshare
Ishtar:/tmp/unshare> sudo
Ishtar:/tmp/unshare# ls home
Ishtar:/tmp/unshare# ll home
total 0
# create file in original "bound" dir from 1st usr above:
Ishtar:/tmp/unshare# touch /home/packages/PACKAGES.DIR 
Ishtar:/tmp/unshare# ll home  #home still empty
total 0
Ishtar:/> tty
/dev/pts/5
# now on other user again
Ishtar:/tmp/unshare# tty
/dev/pts/4
Ishtar:/tmp/unshare# ls home
BUILD@        RPMS@     buildroot/       perlsrc/  sysvinit-288.spec
BUILDROOT@    SOURCES@  config-scripts/  rpms/     tmp/
OSbuild/      SPECS@    debug@           sources/
OTHER/        SRPMS@    linux@           specs/
PACKAGES.DIR  build/    logs/            srpms/
#^^^ see PACKAGES.DIR appear (as created in original dir by another
# user

Once you have your "private dir mounted for user in "pts/4" you can change to the UID you want to run under for the program:

Ishtar:/tmp/unshare# su astara
Ishtar:/tmp/unshare> whoami
astara
Ishtar:/tmp/unshare> ls home/PACK*
home/PACKAGES.DIR

Note mount is still there for the unpriv'd user.

To be save, I'd put the 'su to other user' in a script file followed by a 'unmount /tmp/unshare/home', (since when su OTHERUSER exits, it will become root again, and unmount the file in the private space,). Then you can exit.

Does this come close to what you want? -- You have to use 'root' to setup your child env, but then run the child -- and only it has access to the mount created in the new mount namespace.

(update) BTW -- just noticed unshare has a --map-root-user that to specifically allow using either root or caps to setup options in the new namespace. The manpage says (about this switch):

....This makes it possible to  conveniently
gain  capabilities needed to manage various aspects of the newly
created namespaces (such as configuring interfaces in  the  net-
work  namespace  or mounting filesystems in the mount namespace)
even when run unprivileged.

This could allow you to manage your loop dev w/o being root (or so says the manpage). Likely CAP_SYS_ADMIN is the cap needed for doing this.

Astara
  • 264
  • 1
  • 8
  • Well, actually, no, you don't need to be root to use unshare...and bind mounts already work. It's loop mounts that are giving me trouble. – kirbyfan64sos May 15 '17 at 13:18
  • hmmm....for some reason I got a perm-denied (never used it before, so was probably something else). There is a switch to allow gaining caps in the new namespace to do specifically what you want to do...will update my answer to mention that @ end. – Astara May 17 '17 at 19:41
  • I'm already using `--map-root-user` (`-r` in the original question) and it still doesn't work because `/dev` is still off limits. – kirbyfan64sos May 19 '17 at 00:04
  • refi64: I just rechecked running unshare as a user. Only the "-U" switch (for a new user space) could be run as a normal user. You can't use an unprivileged unshare as a normal user. If you don't have root, you would need caps set on your 'unshare' program. Could you look at the caps set on your unshare program? I.e. run 'filecap $(whence unshare)'. – Astara May 19 '17 at 01:27
  • 1
    If you create a new user namespace, this makes it possible to create a new mount namespace (and other kinds). This is how the example in the question works. – linuxhackerman May 23 '17 at 08:30
  • @Anonymouse: Where do you see 'unshare' being invoked with the '-U' switch? You don't want to create a complete new (with nothing mounted) namespace. Then you have to mount 'everything' (dev, proc, needed filesystems). They are using 'unshare' to create an unshared mount-space (-m) and using -r to remap root. Their question doesn't give the steps needed to reproduce their problem, since "unshare -mr" fails unless you start with root capabilities. If they created a new user namespace, we'd see a usage of '-U' in the question. – Astara May 23 '17 at 19:58
  • 1
    As far as I understand `--map-root-user` implies `-U`. – linuxhackerman May 24 '17 at 11:20
  • It may, but it still doesn't work without root capabilities. I.e. running 'unshare' with the "-r", "-m", or both ("-rm") switches causes unshare to immediately fail with a "Permission Denied" or "Operation not permitted" error message. It isn't the case that '-U' "enables" one to run unshare, but that -U is the only flag that doesn't, immediately fail due to insufficient privilege. That's why I maintain that the example, as given, won't run. Now if "unshare" is installed as setUID-root, or similar, that might be leverageable to give root caps, but then root access is involved. – Astara May 24 '17 at 21:22
  • Also, one can't argue that having root caps through setUID or setCAP isn't really 'root access' -- but then one needs to explain how those "setX" commands were enabled on the file in the first place. Anyway, that's why I asked for more information on how 'unshare' was installed on their system... – Astara May 24 '17 at 21:26
  • @Astara probably because you are using a kernel with the Debian patch? https://superuser.com/questions/1094597/enable-user-namespaces-in-debian-kernel `unshare -r` works fine for me on Fedora Linux. – sourcejedi May 29 '17 at 08:53
  • @sourcejedi - Not using a distro kernel nor a patched one. The message I get for the -r switch is different that the others that only report a permission denied. This one has the message: unshare: write failed /proc/self/gid_map: Operation not permitted – Astara May 29 '17 at 14:18
  • Huh. There are Google results for that error like https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=780841 but I don't expect your version of unshare is that out of date. – sourcejedi May 29 '17 at 15:10