私は読んだinitramfsに関するLinuxドキュメントそしてソースコードのswitch_root
。
文書には次のように記載されています。
別のルートデバイスに切り替えるときは、initrdivov_rootを実行してRAMディスクを取り外します。しかし、initramfsはrootfsです。 ivot_root rootfsを使用またはマウント解除できません。代わりに、rootfsのすべてのエントリを削除してスペースを解放してください(-xdev / -exec rm '{}' ';'を探す)。新しい root で rootfs を上書きする (cd /newmount; mount --move ./; chroot .), stdin/stdout/stderr を新しい /dev/console に接続し、新しい init を実行します。
そしてswitch_root
次のことをします:
if (chdir(newroot)) {
warn(_("failed to change directory to %s"), newroot);
return -1;
}
...
if (mount(newroot, "/", NULL, MS_MOVE, NULL) < 0) {
close(cfd);
warn(_("failed to mount moving %s to /"), newroot);
return -1;
}
...
if (chroot(".")) {
close(cfd);
warn(_("failed to change root"));
return -1;
}
なぜマウントポイントを別の場所に移動するのですか/
?
new_rootのchrootだけでは不十分な理由は何ですか?
答え1
編集する:編集していただいた@timothy-baldwinに感謝します。
new_root
ハイパーマウントは/
マウントされた名前空間のルートディレクトリを変更し、ハイパーマウントなしでルートを変更すると/
システムが環境になりますchroot
(ルートディレクトリはマウントされた名前空間のルートディレクトリと一致しません)。
これにより、次のようないくつかの問題が発生する可能性があります。
1. chroot内では、ユーザーの名前空間の作成は許可されません。
によると、man 2 unshare
chroot環境内ではunshare
ユーザーの名前空間を取得する操作が失敗します。EPERM
EPERM (since Linux 3.9) 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).
$ unshare -U
unshare: unshare failed: Operation not permitted
2.マウントネームスペースを入力すると、ルートディレクトリがネームスペースのルートディレクトリに設定されます。
マウントネームスペースを入力すると、プロセスのルートがマウントネームスペースのルートに設定されるため、マウントsetns
ネームスペースで操作を実行すると、ルートがrootfsディレクトリに設定されます。
$ nsenter -m/proc/self/ns/mnt /bin/sh
$ ls -ld /new_root
new_root
new_rootディレクトリが私のchrootの外にあることがわかります。
上にマウントしても、/
chroot エスケープは実際には防止されません。
rootユーザーは、umount
このディレクトリにマウントネームスペース()を再入力しsetns
てrootfsを表示できます。
#define _GNU_SOURCE
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mount.h>
#include <unistd.h>
#include <fcntl.h>
#include <sched.h>
#include <stdio.h>
int main() {
int ns = open("/proc/self/ns/mnt", O_RDONLY);
if (ns == -1) {
perror("open");
goto out;
}
if (umount2("/", MNT_DETACH)) {
perror("umount2");
goto out;
}
if (setns(ns, CLONE_NEWNS)) {
perror("setns");
goto out;
}
char *a[] = { "/bin/sh", NULL };
char *e[] = { NULL };
execve(a[0], a, e);
perror("execve");
out:
return 1;
}
$ gcc -o main main.c
$ unshare -m ./main
/ # ls -d new_root
new_root
/ # mount -t proc proc /proc
/ # cat /proc/mounts
none / rootfs rw 0 0
proc /proc proc rw,relatime 0 0
chroot の逸脱を防ぐには、new_root
over をインストールする必要があります。/
最小限のinitramfsを作成し、switch_root
バイナリを次のシェルスクリプトに置き換えてシェルを取得しました。
#!/bin/sh
exec /bin/sh
/bin/sh
また、initramfs内の静的リンクに変更されましたbusybox
。
次のコードをコンパイルして静的にリンクします。
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
int main() {
int fd = open(".", O_RDONLY | O_CLOEXEC);
if (fd < 0) {
perror("open");
goto out0;
}
if (chroot("tmp")) {
perror("chroot");
goto out1;
}
if (fchdir(fd)) {
perror("fchdir");
goto out1;
}
if (chdir("..")) {
perror("chdir");
goto out1;
}
char *const argvp[] = { "sh", NULL };
char *const envp[] = { NULL };
execve("bin/sh", argvp, envp);
perror("execve");
out1:
close(fd);
out0:
return 1;
}
私の実際のルートファイルシステムのルートディレクトリに/escape
。
switch_root
これが起こる前に再起動してシェルを入手してください。
ルートを過度にインストールする必要はありません。
$ mount --move proc new_root/proc
$ mount --move dev new_root/dev
$ mount --move sys new_root/sys
$ mount --move run new_root/run
$ exec chroot new_root
$ ./escape
$ ls -d new_root
new_root
私はchrootから脱出しました。
過負荷ルートを含む
$ mount --move proc new_root/proc
$ mount --move dev new_root/dev
$ mount --move sys new_root/sys
$ mount --move run new_root/run
$ cd new_root
$ mount --move . /
$ exec chroot .
$ ./escape
$ ls -d new_root
ls: cannot access 'new_root': No such file or directory
私はchrootから抜け出すことはできません。
答え2
rootfsをオーバーロードしないと、ユーザーとマウントの名前空間が破損します。
setns
呼び出し元のルートをマウントされた名前空間のルートに設定すると、システムコールはキャンセルされますchroot
。- プロセスのルートがマウントされた名前空間のルートではない場合、権限のないプロセスはユーザーの名前空間を作成できません。