On Linux the chroot(2) system call can only be made by a process that is privileged. The capability the process needs is CAP_SYS_CHROOT.
The reason you can't chroot as a user is pretty simple. Assume you have a setuid program such as sudo that checks /etc/sudoers if you are allowed to do something. Now put it in a chroot chroot with your own /etc/sudoers. Suddenly you have an instant privilege escalation.
It is possible to design a program to chroot itself and run it as a setuid process, but this is generally considered bad design. The extra security of the chroot does not motivate the security issues with the setuid.
@imz--IvanZakharyaschev comments on pehrs's answer that it may be possible with the introduction of namespaces, but this hasn't been tested and posted as an answer. Yes, that does indeed make it possible for a non-root user to use chroot.
Given a statically-linked dash, and a statically-linked busybox, and a running bash shell running as non-root:
The root user ID in that namespace is mapped to the non-root user ID outside of that namespace, and vice versa, which is why the system shows files owned by the current user as owned by user ID 0. A regular ls -al root, without unshare, does show them as owned by the current user.
Note: it's well-known that processes that are capable of using chroot, are capable of breaking out of a chroot. Since unshare -r would grant chroot permissions to an ordinary user, it would be a security risk if that was allowed inside a chroot environment. Indeed, it is not allowed, and fails with:
CLONE_NEWUSER was specified in flags and the caller is in a chroot environment (i.e., the caller's root directory does not match the root directory of the mount namespace in which it resides).
These days, you want to be looking at LXC (Linux Containers) instead of chroot/BSD jail. It's somewhere between a chroot and a virtual machine, giving you a lot of security control and general configurability. I believe all you need to run it as a user is to be a member of the group that owns the necessary files/devices, but there might also be capabilities/system permissions involved. Either way, it should be very doable, since LXC is quite recent, long after SELinux etc. was added to the Linux kernel.
Also, bear in mind that you can just write scripts as root but give users secure permission to run those scripts (without a password if you like, but make sure the script is secure) using sudo.
The combination of fakeroot / fakechroot gives a simulacre of chroot for simple needs such as producing tar archives where files appear to be owned by root. Fakechroot manpage is http://linux.die.net/man/1/fakechroot.
You don't get any new permission though, but if you own a directory (e.g. fake-distro) before invoking
It seems that with user-namespaces it is in fact possible to chroot without root. Here is an example program which demonstrates that it is possible. I have only begun to explore how linux namespaces work and so I'm not entirely sure if this code is best practice or not.
Save as user_chroot.cc. Compile with g++ -o user_chroot user_chroot.cc. Usage is ./user_chroot /path/to/new_rootfs.
// references:
// [1]: http://man7.org/linux/man-pages/man7/user_namespaces.7.html
// [2]: http://man7.org/linux/man-pages/man2/unshare.2.html
#include <sched.h>
#include <sys/types.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <cerrno>
#include <cstdio>
#include <cstring>
int main(int argc, char** argv) {
if(argc < 2) {
printf("Usage: %s <rootfs>\n", argv[0]);
}
int uid = getuid();
int gid = getgid();
printf("Before unshare, uid=%d, gid=%d\n", uid, gid);
// First, unshare the user namespace and assume admin capability in the
// new namespace
int err = unshare(CLONE_NEWUSER);
if(err) {
printf("Failed to unshare user namespace\n");
return 1;
}
// write a uid/gid map
char file_path_buf[100];
int pid = getpid();
printf("My pid: %d\n", pid);
sprintf(file_path_buf, "/proc/%d/uid_map", pid);
int fd = open(file_path_buf, O_WRONLY);
if(fd == -1) {
printf("Failed to open %s for write [%d] %s\n", file_path_buf, errno,
strerror(errno));
} else {
printf("Writing : %s (fd=%d)\n", file_path_buf, fd);
err = dprintf(fd, "%d %d 1\n", uid, uid);
if(err == -1) {
printf("Failed to write contents [%d]: %s\n", errno,
strerror(errno));
}
close(fd);
}
sprintf(file_path_buf, "/proc/%d/setgroups", pid);
fd = open(file_path_buf, O_WRONLY);
if(fd == -1) {
printf("Failed to open %s for write [%d] %s\n", file_path_buf, errno,
strerror(errno));
} else {
dprintf(fd, "deny\n");
close(fd);
}
sprintf(file_path_buf, "/proc/%d/gid_map", pid);
fd = open(file_path_buf, O_WRONLY);
if(fd == -1) {
printf("Failed to open %s for write [%d] %s\n", file_path_buf, errno,
strerror(errno));
} else {
printf("Writing : %s (fd=%d)\n", file_path_buf, fd);
err = dprintf(fd, "%d %d 1\n", gid, gid);
if(err == -1) {
printf("Failed to write contents [%d]: %s\n", errno,
strerror(errno));
}
close(fd);
}
// Now chroot into the desired directory
err = chroot(argv[1]);
if(err) {
printf("Failed to chroot\n");
return 1;
}
// Now drop admin in our namespace
err = setresuid(uid, uid, uid);
if(err) {
printf("Failed to set uid\n");
}
err = setresgid(gid, gid, gid);
if(err) {
printf("Failed to set gid\n");
}
// and start a shell
char argv0[] = "bash";
char* new_argv[] = {
argv0,
NULL
};
err = execvp("/bin/bash", new_argv);
if(err) {
perror("Failed to start shell");
return -1;
}
}
I have tested this on a minimal rootfs generated with multistrap (executed as non-root). Some system files like /etc/passwd and /etc/groups were copied from the host rootfs into the guest rootfs.
No. If I recall correctly there is some kernel level thing that chroot does that prevents it. I don't recall what that thing was. I investigated it back when messing with Gentoo's Catalyst Build tool (and a chroot on gentoo is the same as a chroot on ubuntu). Though it would be possible to make it happen without a passwd... but such things are left to the realm of potential security vulnerabilities and making sure you know what you are doing.
On Linux the chroot(2) system call can only be made by a process that is privileged. The capability the process needs is CAP_SYS_CHROOT.
The reason you can't chroot as a user is pretty simple. Assume you have a setuid program such as sudo that checks /etc/sudoers if you are allowed to do something. Now put it in a chroot chroot with your own /etc/sudoers. Suddenly you have an instant privilege escalation.
It is possible to design a program to chroot itself and run it as a setuid process, but this is generally considered bad design. The extra security of the chroot does not motivate the security issues with the setuid.
@imz--IvanZakharyaschev comments on pehrs's answer that it may be possible with the introduction of namespaces, but this hasn't been tested and posted as an answer. Yes, that does indeed make it possible for a non-root user to use chroot.
Given a statically-linked
dash
, and a statically-linkedbusybox
, and a runningbash
shell running as non-root:The root user ID in that namespace is mapped to the non-root user ID outside of that namespace, and vice versa, which is why the system shows files owned by the current user as owned by user ID 0. A regular
ls -al root
, withoutunshare
, does show them as owned by the current user.Note: it's well-known that processes that are capable of using
chroot
, are capable of breaking out of achroot
. Sinceunshare -r
would grantchroot
permissions to an ordinary user, it would be a security risk if that was allowed inside achroot
environment. Indeed, it is not allowed, and fails with:which matches the unshare(2) documentation:
These days, you want to be looking at LXC (Linux Containers) instead of chroot/BSD jail. It's somewhere between a chroot and a virtual machine, giving you a lot of security control and general configurability. I believe all you need to run it as a user is to be a member of the group that owns the necessary files/devices, but there might also be capabilities/system permissions involved. Either way, it should be very doable, since LXC is quite recent, long after SELinux etc. was added to the Linux kernel.
Also, bear in mind that you can just write scripts as root but give users secure permission to run those scripts (without a password if you like, but make sure the script is secure) using sudo.
The combination of fakeroot / fakechroot gives a simulacre of chroot for simple needs such as producing tar archives where files appear to be owned by root. Fakechroot manpage is http://linux.die.net/man/1/fakechroot.
You don't get any new permission though, but if you own a directory (e.g. fake-distro) before invoking
it now look for some-command like you're root and owning everything within fake-distro.
It seems that with user-namespaces it is in fact possible to chroot without root. Here is an example program which demonstrates that it is possible. I have only begun to explore how linux namespaces work and so I'm not entirely sure if this code is best practice or not.
Save as
user_chroot.cc
. Compile withg++ -o user_chroot user_chroot.cc
. Usage is./user_chroot /path/to/new_rootfs
.I have tested this on a minimal rootfs generated with multistrap (executed as non-root). Some system files like
/etc/passwd
and/etc/groups
were copied from the host rootfs into the guest rootfs.No. If I recall correctly there is some kernel level thing that chroot does that prevents it. I don't recall what that thing was. I investigated it back when messing with Gentoo's Catalyst Build tool (and a chroot on gentoo is the same as a chroot on ubuntu). Though it would be possible to make it happen without a passwd... but such things are left to the realm of potential security vulnerabilities and making sure you know what you are doing.