24

I'm setting up a minimal chroot and want to avoid having sudo or su in it but still run my processes as non-root. This is a bit of a trick as running chroot requiers root. I could write a program that does this that would look something like:

uid = LookupUser(args[username])  // no /etc/passwd in jail
chroot(args[newroot])
cd("/")
setuids(uid)
execve(args[exe:])

Is that my best bet or is there a standard tool that does that for me?


I rolled my own here:

Community
  • 1
  • 1
BCS
  • 75,627
  • 68
  • 187
  • 294

6 Answers6

27

If you invoke chroot from root, the chroot option --userspec=USER:GROUP will run the command under the non-root UID/GID.

By the way, the option '--userspec' is first introduced in coreutils-7.5 according to a git repository git://git.sv.gnu.org/coreutils.

vy32
  • 28,461
  • 37
  • 122
  • 246
kamae
  • 1,825
  • 1
  • 16
  • 21
  • That looks like exactly what I want but I can't find documentation on it. (I'm looking at man and info pages.) Do you have a link to some docs? – BCS Jul 19 '11 at 14:28
  • I'm sorry but it seems that the option '--userspec' is introduced RHEL >= 6.0 or Fedora >= 13. – kamae Jul 19 '11 at 15:09
21

fakechroot, in combination with fakeroot, will allow you to do this. They'll make all programs that are running act as if they're being run in a chroot as root but they'll actually be running as you.

See also fakechroot's man page.

Eric Warmenhoven
  • 2,942
  • 20
  • 17
  • 2
    I need a real chroot as I'm going to be compiling and running untrusted code that could include inline ASM. – BCS Sep 17 '10 at 20:49
  • 3
    Since I stumbled upon this thread, even years later, I need to add: never use chroot for security. https://lwn.net/Articles/252794/ – domenukk May 10 '17 at 17:08
10

You can make use of linux capabilities to give your binary the ability to call chroot() w/o being root. As an example, you can do this to the chroot binary. As non-root, normally you'd get this:

$ chroot /tmp/
chroot: cannot change root directory to /tmp/: Operation not permitted

But after you run the setcap command:

sudo setcap cap_sys_chroot+ep /usr/sbin/chroot 

It will let you do the chroot call.

I don't recommend you do this to the system's chroot, that you instead do it to your own program and call chroot. That way you have more control over what is happening, and you can even drop the cap_sys_chroot privilege after you call it, so successive calls to chroot in your program will fail.

Corey Henderson
  • 7,239
  • 1
  • 39
  • 43
  • 1
    So that will allow an arbitrary user to invoke the chroot sys-call by way of any binary accessed as `/usr/sbin/chroot`? That would then allow any process inside a chroot to invoke the chroot sys-call provided it can create an executable file named `/usr/sbin/chroot` relative to the current root. -- I'd rather not do this by loosening the kernels security model. – BCS Jul 20 '11 at 15:43
  • I updated the answer to reflect that me setting it on the chroot binary was only an example. You'd want to do that setcap on your own binary. – Corey Henderson Jul 20 '11 at 16:15
9

A custom chrooter isn't at all hard to write:

#define _BSD_SOURCE
#include <stdio.h>
#include <unistd.h>
const char newroot[]="/path/to/chroot";
int main(int c, char **v, char **e) {
    int rc; const char *m;
    if ( (m="chdir" ,rc=chdir(newroot)) == 0
      && (m="chroot",rc=chroot(newroot)) == 0
      && (m="setuid",rc=setuid(getuid())) == 0 )
            m="execve", execve(v[1],v+2,e);
    perror(m);
    return 1;
}

Make that setuid root and owned by a custom group you add your favored user to (and no 'other' access).

jthill
  • 55,082
  • 5
  • 77
  • 137
2

You could use Linux Containers to create a chroot environment that is in a totally different namespace (IPC, filesytem, and even network)

There is even LXD which is able to manage the creation of image-based containers and configure them to run as unprivileged users so that if the untrusted code manages to somehow escape the container, it will only be able to execute code as the unprivileged user and not as the system's root.

Search 'Linux Containers' and 'LXD' on your favorite search engine ;)

mike510a
  • 2,102
  • 1
  • 11
  • 27
2

These days chrooting without root-permissions is possible with unshare command provided by mount namespaces.

Plain Unshare

Suppose you want to chroot into ~/Projects/my-backup directory, and run inside it the ~/Projects/my-backup/bin/bash binary . So you run:

$ unshare -mr chroot ~/Projects/my-backup/ /bin/bash

Here:

  • -m means you can use mount --bind inside the new chroot (note that mounts will not be visible to the outside world, only to your chroot)
  • -r makes it look as if you are root inside the chroot
  • chroot … is the usual chroot command.

Possible pitfalls:

  1. Make sure you have environment variables set correctly. For example, upon chrooting into an Ubuntu from Archlinux I was getting errors like bash: ls: command not found. Turned out, my $PATH did not contain /bin/ (on Archlinux it's a symlink to /usr/bin/), so I had to run:

    PATH=$PATH:/bin/ unshare -mr chroot ~/Projects/my-backup/ /bin/bash
    
  2. Note that /proc or /dev filesystems won't get populated, so running a binary that requires them will fail. There is a --mount-proc option, but it doesn't seem to be accessible to a non-root user.

  3. chown won't work unless you do some complicated setup with newuidmap and newgidmap commands.

Buildah Unshare

This has advantage compared to plain unshare in that it takes care of the needed setup for chown command to start working. You only need to make sure you have defined the ranges in /etc/subuid and /etc/subgid for your user.

You run it as:

$ buildah unshare chroot ~/Projects/my-backup/ /bin/bash
Hi-Angel
  • 4,933
  • 8
  • 63
  • 86