非rootユーザーがUbuntuでchrootプロセスを実行することは可能ですか?
非rootユーザーがUbuntuでchrootプロセスを実行することは可能ですか?
回答:
Linuxでは、chroot(2)システムコールは特権のあるプロセスによってのみ実行できます。プロセスに必要な機能はCAP_SYS_CHROOTです。
ユーザーとしてchrootできない理由は非常に簡単です。何かを行うことが許可されている場合、/ etc / sudoersをチェックするsudoなどのsetuidプログラムがあるとします。次に、独自の/ etc / sudoersを使用してchroot chrootに配置します。突然、特権のエスカレーションが即座に発生します。
プログラムを自分自身をchrootしてsetuidプロセスとして実行するように設計することは可能ですが、これは一般に悪い設計と考えられています。chrootの追加のセキュリティは、setuidのセキュリティ問題を動機付けません。
chroot
。
@ imz--IvanZakharyaschevは、名前空間の導入によって可能になるかもしれないとpehrsの答えにコメントしていますが、これはテストされておらず、答えとして投稿されていません。はい、そうすることで、非rootユーザーがchrootを使用できるようになります。
静的にリンクされたdash
、静的にリンクされたbusybox
、およびbash
非ルートとして実行されている実行中のシェルがある場合:
$ 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は、ユーザID 0 A正規が所有するようにシステムが示すファイルは、現在のユーザーが所有する理由であり、その名前空間の非ルートユーザIDの外側にマッピングされ、その逆もさls -al root
せずにunshare
、い現在のユーザーが所有しているものとして表示します。
注:を使用できるプロセスchroot
は、から抜け出すことができることはよく知られていますchroot
。以来unshare -r
与えるだろうchroot
普通のユーザーに権限をそれが内部で許可された場合、それはセキュリティ上のリスクになるchroot
環境。実際、それは許可されておらず、次のように失敗します。
共有解除:共有に失敗しました:操作は許可されていません
unshare(2)のドキュメントと一致します:
EPERM(Linux 3.9以降)
CLONE_NEWUSERがフラグで指定され、呼び出し元がchroot環境にあります(つまり、呼び出し元のルートディレクトリが、それが存在するマウント名前空間のルートディレクトリと一致しません)。
最近では、chroot / BSD jailの代わりにLXC(Linux Containers)を見たいと思っています。chrootと仮想マシンの間のどこかにあり、多くのセキュリティ制御と一般的な構成可能性を提供します。ユーザーとして実行するために必要なのは、必要なファイル/デバイスを所有するグループのメンバーになることだけであると考えていますが、機能/システムのアクセス許可も含まれている可能性があります。いずれにしても、SELinuxなどがLinuxカーネルに追加されてからずっと経ったLXCはごく最近のものであるため、非常に実行可能です。
また、rootとしてスクリプトを作成するだけで、sudoを使用してそれらのスクリプトを実行するための安全なアクセス許可をユーザーに与えることができることに注意してください(必要な場合はパスワードなしで、スクリプトが安全であることを確認してください)。
fakeroot / fakechrootの組み合わせは、ファイルがルートによって所有されているように見えるtarアーカイブを作成するなどの単純なニーズに対して、chrootのシミュレーションを提供します。Fakechrootのマンページはhttp://linux.die.net/man/1/fakechrootです。
ただし、新しい権限は取得しませんが、呼び出す前にディレクトリ(fake-distroなど)を所有している場合
fakechroot fakeroot chroot ~/fake-distro some-command
rootであり、fake-distro内のすべてを所有しているようなコマンドを探します。
~/fake-distro
用途のbusyboxの、シンボリックリンクls
、mv
および他の一般的なユーティリティへ/bin/busybox
。を明示的に呼び出すと/bin/busybox mv ...
、動作しますが、呼び出すと、が/bin/mv ...
取得されsh: /bin/mv: not found
ます。export FAKECHROOT_EXCLUDE_PATH=/
fakechrootを実行する前に設定すると、その症状は修正されますが、他のシンボリックリンク(例:)で壊れ/usr/bin/vim -> /usr/bin/vim.vim
ます。
ユーザー名前空間では、実際にはルートなしでchrootできるようです。これが可能であることを示すサンプルプログラムです。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(非ルートとして実行)でテストしました。いくつかのシステムファイルが好き/etc/passwd
と/etc/groups
ゲストにrootfsにホストにrootfsからコピーされました。
Failed to unshare user namespace
Linux 4.12.10(Arch Linux)で失敗します。
unshare
。:また、この優れたエラーメッセージを持っているかもしれませんPythonのバージョン試すことができますgithub.com/cheshirekow/uchroot