Может ли пользователь без полномочий root запустить процесс chroot в Ubuntu?
В Linux chroot (2) системный вызов может быть выполнен только привилегированным процессом. Необходимая для процесса возможность - CAP_SYS_CHROOT.
Причина, по которой вы не можете chroot как пользователь, довольно проста. Предположим, у вас есть программа setuid, такая как sudo, которая проверяет / etc / sudoers, если вам разрешено что-то делать. Теперь поместите его в chroot chroot с вашим собственным / etc / sudoers. Внезапно вы получаете мгновенное повышение привилегий.
Можно разработать программу для chroot и запускать ее как процесс setuid, но это обычно считается плохим дизайном. Дополнительная безопасность chroot не вызывает проблем с безопасностью setuid.
@ imz - Иван Захарящев комментирует ответ pehrs, что это возможно с введением пространств имен, но это не было протестировано и опубликовано в качестве ответа. Да, это действительно позволяет пользователю без полномочий root использовать chroot.
Учитывая статически связанный dash
, и статически связанный busybox
, и бег bash
оболочка, работающая без полномочий root:
$ mkdir root
$ cp /path/to/dash root
$ cp /path/to/busybox root
$ unshare -r bash -c 'chroot root /dash -c "/busybox ls -al /"'
total 2700
drwxr-xr-x 2 0 0 4096 Dec 2 19:16 .
drwxr-xr-x 2 0 0 4096 Dec 2 19:16 ..
drwxr-xr-x 1 0 0 1905240 Dec 2 19:15 busybox
drwxr-xr-x 1 0 0 847704 Dec 2 19:15 dash
ID пользователя root в этом пространстве имен сопоставляется с ID пользователя без полномочий root вне этого пространства имен, и наоборот, поэтому система показывает файлы, принадлежащие текущему пользователю, как принадлежащие идентификатору пользователя 0. Обычный ls -al root
, без unshare
, показывает их как принадлежащие текущему пользователю.
Примечание: хорошо известно, что процессы, которые могут использовать chroot
, способны вырваться из chroot
. поскольку unshare -r
предоставит chroot
разрешения для обычного пользователя, это было бы угрозой безопасности, если бы это было разрешено внутри chroot
Окружающая среда. Действительно, это запрещено и не работает с:
unshare: unshare failed: операция запрещена
что соответствует не делиться (2) документация:
EPERM (начиная с Linux 3.9)
CLONE_NEWUSER было указано в флаги и вызывающий находится в среде chroot (т. е. корневой каталог вызывающего не соответствует корневому каталогу пространства имен монтирования, в котором он находится).
В наши дни вы захотите взглянуть на LXC (контейнеры Linux) вместо chroot / BSD jail. Это что-то среднее между chroot и виртуальной машиной, что дает вам большой контроль безопасности и общую настраиваемость. Я считаю, что все, что вам нужно для запуска его в качестве пользователя, - это быть членом группы, которая владеет необходимыми файлами / устройствами, но также могут быть задействованы возможности / системные разрешения. В любом случае, это должно быть вполне выполнимо, поскольку LXC появился довольно недавно, спустя много времени после того, как SELinux и т. Д. Были добавлены в ядро Linux.
Кроме того, имейте в виду, что вы можете просто писать сценарии с правами root, но давать пользователям безопасное разрешение на запуск этих сценариев (без пароля, если хотите, но убедитесь, что сценарий безопасен) с помощью sudo.
Комбинация fakeroot / fakechroot дает имитацию chroot для простых нужд, таких как создание tar-архивов, в которых файлы принадлежат пользователю root. Справочная страница Fakechroot http://linux.die.net/man/1/fakechroot.
Вы не получаете никаких новых разрешений, но если у вас есть каталог (например, fake-distro) перед вызовом
fakechroot fakeroot chroot ~/fake-distro some-command
теперь он ищет какую-то команду, как будто вы root и владеете всем в fake-distro.
Кажется, что с пользовательскими пространствами имён фактически возможно chroot без root. Вот пример программы, демонстрирующий, что это возможно. Я только начал изучать, как работают пространства имен linux, поэтому я не совсем уверен, является ли этот код наилучшей практикой или нет.
Сохранить как user_chroot.cc
. Скомпилировать с g++ -o user_chroot user_chroot.cc
. Использование ./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;
}
}
Я тестировал это на минимальном rootfs, созданном с помощью multiistrap (выполняемом как не root). Некоторые системные файлы, например /etc/passwd
и /etc/groups
были скопированы из rootfs хоста в гостевые rootfs.
Нет. Если я правильно помню, на уровне ядра есть кое-что, что предотвращает chroot. Я не помню, что это было. Я исследовал это, когда возился с инструментом Gentoo Catalyst Build (а chroot на gentoo - это то же самое, что chroot на ubuntu). Хотя это можно было бы сделать без пароля ... но такие вещи оставлены на усмотрение потенциальных уязвимостей безопасности и проверки того, что вы знаете, что делаете.