initramfs가 rootfs를 새 루트로 덮어써야 하는 이유는 무엇입니까?

initramfs가 rootfs를 새 루트로 덮어써야 하는 이유는 무엇입니까?

나는 읽었다initramfs에 관한 Linux 문서그리고소스 코드switch_root.

문서에는 다음과 같이 나와 있습니다.

다른 루트 장치로 전환할 때 initrdivov_root를 실행한 다음 램디스크를 제거합니다. 그러나 initramfs는 rootfs입니다. ivot_root rootfs를 사용하거나 마운트 해제할 수 없습니다. 대신, rootfs의 모든 항목을 삭제하여 공간을 확보하세요(-xdev / -exec rm '{}' ';' 찾기).새로운 루트로 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 unsharechroot 환경 내에서는 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 탈출이 실제로 방지되지는 않습니다.

루트 사용자는 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_rootover를 설치해야 합니다./

최소한의 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.
  • 프로세스 루트가 마운트된 네임스페이스의 루트가 아닌 경우 권한이 없는 프로세스는 사용자 네임스페이스를 생성할 수 없습니다.

관련 정보