linux chroot 命令

chroot,即 change root directory (更改 root 目錄)。在 linux 系統中,系統默認的目錄結構都是以 /,即以根 (root) 開始的。而在使用 chroot 以後,系統的目錄結構將以指定的位置做爲 / 位置。html

基本語法

chroot NEWROOT [COMMAND [ARG]...]node

具體用法請參考本文的 demo。linux

爲何要使用 chroot 命令

增長了系統的安全性,限制了用戶的權力:
在通過 chroot 以後,在新根下將訪問不到舊系統的根目錄結構和文件,這樣就加強了系統的安全性。通常會在用戶登陸前應用 chroot,把用戶的訪問能力控制在必定的範圍以內。docker

創建一個與原系統隔離的系統目錄結構,方便用戶的開發:
使用 chroot 後,系統讀取的是新根下的目錄和文件,這是一個與原系統根下文件不相關的目錄結構。在這個新的環境中,能夠用來測試軟件的靜態編譯以及一些與系統不相關的獨立開發。shell

切換系統的根目錄位置,引導 Linux 系統啓動以及急救系統等:
chroot 的做用就是切換系統的根位置,而這個做用最爲明顯的是在系統初始引導磁盤的處理過程當中使用,從初始 RAM 磁盤 (initrd) 切換系統的根位置並執行真正的 init,本文的最後一個 demo 會詳細的介紹這種用法。centos

經過 chroot 運行 busybox 工具

busybox 包含了豐富的工具,咱們能夠把這些工具放置在一個目錄下,而後經過 chroot 構造出一個 mini 系統。簡單起見咱們直接使用 docker 的 busybox 鏡像打包的文件系統。先在當前目錄下建立一個目錄 rootfs:安全

$ mkdir rootfs

而後把 busybox 鏡像中的文件釋放到這個目錄中:bash

$ (docker export $(docker create busybox) | tar -C rootfs -xvf -)

經過 ls 命令查看 rootfs 文件夾下的內容:app

$ ls rootfs

萬事俱備,讓咱們開始吧!函數

執行 chroot 後的 ls 命令

$ sudo chroot rootfs /bin/ls

雖然輸出結果與剛纔執行的 ls rootfs 命令形同,可是此次運行的命令倒是 rootfs/bin/ls。

運行 chroot 後的 pwd 命令

$ sudo chroot rootfs /bin/pwd

哈,pwd 命令真把 rootfs 目錄當根目錄了!

不帶命令執行 chroot

$ sudo chroot rootfs

此次出錯了,由於找不到 /bin/bash。咱們知道 busybox 中是不包含 bash 的,可是 chroot 命令爲何會找 bash 命令呢? 原來,若是不給 chroot 指定執行的命令,默認它會執行 '${SHELL} -i',而個人系統中 ${SHELL} 爲 /bin/bash。
既然 busybox 中沒有 bash,咱們只好指定 /bin/sh 來執行 shell 了。

$ sudo chroot rootfs /bin/sh

運行 sh 是沒有問題的,而且咱們打印出了當前進程的 PID。

檢查程序是否運行在 chroot 環境下

雖然咱們作了好幾個實驗,可是確定會有朋友心存疑問,怎麼能證實咱們運行的命令就是在 chroot 目錄後的路徑中呢?
其實,咱們能夠經過 /proc 目錄下的文件檢查進程的中的根目錄,好比咱們能夠經過下面的代碼檢查上面運行的 /bin/sh 命令的根目錄(請在另一個 shell 中執行):

$ pid=$(pidof -s sh)
$ sudo ls -ld /proc/$pid/root

輸出中的內容明確的指出 PID 爲 46644 的進程的根目錄被映射到了 /tmp/rootfs 目錄。

經過代碼理解 chroot 命令

下面咱們嘗試本身實現一個 chroot 程序,代碼中涉及到兩個函數,分別是 chroot() 函數和 chdir() 函數,其實真正的 chroot 命令也是經過調用它們實現的:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
 
int main(int argc, char *argv[])
{
    if(argc<2){
        printf("Usage: chroot NEWROOT [COMMAND...] \n");
        return 1;
    }

    if(chroot(argv[1])) {
        perror("chroot");
        return 1;
    }
 
    if(chdir("/")) {
        perror("chdir");
        return 1;
    }
 
    if(argc == 2) {
        // hardcode /bin/sh for my busybox tools.
        argv[0] = (char *)"/bin/sh";
 
        argv[1] = (char *) "-i";
        argv[2] = NULL;
    } else {
        argv += 2;
    }
 
    execvp (argv[0], argv);
    printf("chroot: cannot run command `%s`\n", *argv);
 
    return 0;
}

把上面的代碼保存到文件 mychroot.c 文件中,並執行下面的命令進行編譯:

$ gcc -Wall mychroot.c -o mychroot

mychroot 的用法和 chroot 基本相同:

$ sudo ./mychroot ./rootfs

特別之處是咱們的 mychroot 在沒有傳遞命令的狀況下執行了 /bin/sh,緣由固然是爲了支持咱們的 busybox 工具集,筆者在代碼中 hardcode 了默認的 shell:

argv[0] = (char *)"/bin/sh";

從代碼中咱們也能夠看到,實現 chroot 命令的核心邏輯其實並不複雜。

實例:經過 chroot 從新設置 root 密碼

忘記了 root 密碼該怎麼辦?接下來的 demo 將演示如何經過 chroot 命令從新設置 centos7 中被忘記了的 root 密碼。
systemd 的管理機制中,rescure 模式和 emeryency 模式是沒法直接取得 root 權限的,須要使用 root 密碼才能進入 rescure 和 emeryency 環境。因此咱們須要經過其餘方式來設置 root 密碼。咱們能夠爲內核的啓動指定 "rd.break" 參數,從而讓系統在啓動的早期停下來,此時咱們能夠經過使用 root 權限並結合 chroot 命令完成設置 root 密碼的操做。下面咱們一塊兒來看具體的操做過程。

在系統啓動過程當中進入開機菜單時按下字母鍵 e 進程開機菜單的編輯模式:

這就是系統的開機菜單,按下 e 後進入編輯界面:

找到以 "linux16 /vmlinuz-" 開頭的行。若是默認沒有看到該行,須要按向下鍵把它滾動出來。
而後定位到該行結尾處,輸入一個空格和字符串 "rd.break",以下圖所示:

接着按下 ctrl + x 以該設置繼續啓動,啓動過程當中操做系統會停下來,這是系統啓動過程當中的一個很是早的時間點:

因此係統的根目錄還掛載在 RAM disk 上(就是內存中的一個文件系統),咱們能夠經過 mount 命令檢查系統當前掛載的文件系統,下面是咱們比較關心的兩條:

上圖中 mount 命令輸出的第一行說明此時的根目錄在一個 RAM disk 中, 即 rootfs。
圖中輸出的第二行說明咱們的文件系統此時被掛載到了 /sysroot 目錄,而且是隻讀的模式:

/dev/mapper/centos-root on /sysroot type xfs (ro,relatime,attr2,inode64,noquota)

而在咱們正常登錄系統的狀況下,系統根目錄的掛載狀況以下:

/dev/mapper/centos-root on / type xfs (rw,relatime,seclabel,attr2,inode64,noquota)

該時間點的最大優點是咱們具備 root 權限!因此讓咱們開始設置新的 root 密碼吧。

先經過下面的命令把 /sysroot 從新掛載爲可讀寫的模式:

switch_root:/# mount -o remount,rw /sysroot

而後用下面 chroot 命令把根目錄切換到咱們原來的環境中:

switch_root:/# chroot /sysroot

此時能夠理解爲:咱們以 root 權限登陸了原來的系統,修改密碼就很容易了!用下面的命令爲 root 用戶設置新的密碼:

sh-4.2# echo "new_root_pw" | passwd --stdin root

接下來還要處理 SELinux 相關的問題。因爲當前的環境中 SELinux 並未啓動,因此咱們對文件的修改可能形成文件的 context 不正確。爲了確保開機時從新設定 SELinux context,必須在根目錄下添加隱藏文件 .autorelabel:

sh-4.2# touch /.autorelabel

最後從 chroot 中退出,並重啓系統:

sh-4.2# exit
switch_root:/# reboot

從新進入登錄界面時就可使用剛纔設置的密碼以 root 登錄了!

總結

chroot 是一個頗有意思的命令,咱們能夠用它來簡單的實現文件系統的隔離。但在一個容器技術繁榮的時代,用 chroot 來進行資源的隔離實在是 low 了點。因此 chroot 的主要用途仍是集中在系統救援、維護等一些特殊的場景中。

參考:
理解 chroot
Linux – RedHat7 / CentOS 7 忘記root密碼修改

相關文章
相關標籤/搜索