1. Overview

Scripts use chroot to enter an environment and execute commands within it. The commands and processes stay within the environment. chroot assigns a directory as the root for processes, isolating them from the filesystem’s default root.

So, incorporating chroot in scripts enhances security, creates containment, and confines command execution within the designated directory.

In this tutorial, we’ll learn all about using the chroot command in a script.

2. Understanding the chroot Command

In simple terms, the chroot command allows us to change the root directory of the current running process and its child processes.

Therefore, this means that the process created can’t see the directory above it, but only the ones below it in the filesystem hierarchy.

We can safely say that using the chroot command is like creating a sandbox for our process. Consequently, the process is isolated from every other process in the system.

Let’s explore the prerequisites for using chroot.

3. chroot Prerequisites

First, we need to create a root directory for chroot and add binaries along with their respective dependencies. Subsequently, we can create a script that will chroot to the directory and perform functions using binaries added to this root directory.

3.1. Creating a Directory for chroot

Following best practice, we should have a dedicated directory that the chroot command can use as the root directory. The directory then serves as an isolated environment.

However, there’s no strict rule for the location of the chroot directory within the Linux operating system. In fact, /<directory_name>, /var/<directory_name>, and /mnt/<directory_name> are the most common paths.

Let’s get started with the creation of our chroot directory:

$ cd /mnt
$ sudo mkdir chrootDir
$ ls
chrootDir

The cd command changes our current directory to the /mnt directory. Subsequently, we use sudo to gain superuser privileges and create a new directory using mkdir. Finally, we check our newly created directory using ls.

3.2. Adding Binaries to the chroot Directory

Now that we’ve established our chroot directory, we need to add the binaries of commands we want within the environment, such as the bash command for starting an interactive shell within the chroot environment. Let’s make a copy of the bash binary in the chrootDir directory. First, we need to change directories to chrootDir:

$ cd chrootDir

Now, let’s create the /bin directory for the binaries that we need in our chroot root directory:

$ sudo mkdir -v bin 
mkdir: created directory 'bin'

Next, let’s copy the binaries that we need into our /bin directory from the default filesystem’s /bin:

$ sudo cp -v /bin/bash /mnt/chrootDir/bin
'/bin/bash' -> '/mnt/chrootDir/bin/bash'

We include the -v option to observe the actions of the cp command. Furthermore, since we elevate our privileges to superuser using sudo, the operating system prompts us to enter our user password.

In addition, we’ll add some basic common command binaries, such as touch, ls, and pwd to the /mnt/chrootDir/bin directory.

Let’s do this using the brace expansion method:

$ sudo cp -v /bin/{touch,ls,pwd} /mnt/chrootDir/bin
'/bin/touch' -> '/mnt/chrootDir/bin/touch'
'/bin/ls' -> '/mnt/chrootDir/bin/ls'
'/bin/pwd' -> '/mnt/chrootDir/bin/pwd'

The result above shows that the method was successful, and our binaries were copied to the /mnt/chrootDir/bin directory.

3.3. Adding Dependencies for Binaries

Dependencies are contained in the /lib and /lib64 directories. So, let’s create two directories inside chrootDir:

$ sudo mkdir lib
$ sudo mkdir lib64
$ ls 
bin lib lib64

Next, we need to locate the dependencies for the binaries and copy them into the respective directories for the commands to function.

So, let’s check for bash using the ldd command:

$ ldd /bin/bash
linux-vdso.so.1 (0x00007ffd868d7000)
libtinfo.so.6 => /lib/x86_64-linux-gnu/libtinfo.so.6 (0x00007fac64b98000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fac649b7000)
/lib64/ld-linux-x86-64.so.2 (0x00007fac64d23000)

Let’s continue by creating lists of the respective dependencies for each binary. We’ll start with bash:

$ list="$(ldd /bin/bash | egrep -o '/lib.*\.[0-9]')"

To check the content of our list, we echo the value of the list variable:

$ echo "$list"
/lib/x86_64-linux-gnu/libtinfo.so.6
/lib/x86_64-linux-gnu/libc.so.6
/lib64/ld-linux-x86-64.so.2

Next, let’s copy each of the dependencies into our chroot root directory:

$ sudo cp -v --parents $list /mnt/chrootDir
'/lib/x86_64-linux-gnu/libtinfo.so.6' -> '/mnt/chrootDir/lib/x86_64-linux-gnu/libtinfo.so.6'
'/lib/x86_64-linux-gnu/libc.so.6' -> '/mnt/chrootDir/lib/x86_64-linux-gnu/libc.so.6'
'/lib64/ld-linux-x86-64.so.2' -> '/mnt/chrootDir/lib64/ld-linux-x86-64.so.2'

We’ve done a great job so far!

Let’s add dependencies for touch, the command used for creating files:

$ sudo ldd /bin/touch
linux-vdso.so.1 (0x00007ffe061fe000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fdfe05a1000)
/lib64/ld-linux-x86-64.so.2 (0x00007fdfe07ba000)

Next, we’ll pull out the paths we need from the above result:

$ list="$(ldd /bin/touch | egrep -o '/lib.*\.[0-9]')"

Then, we echo out the list:

$ echo "$list"
/lib/x86_64-linux-gnu/libc.so.6
/lib64/ld-linux-x86-64.so.2

Next, we need to copy each of the dependencies into our chroot directory:

$ sudo cp -v --parents $list /mnt/chrootDir
'/lib/x86_64-linux-gnu/libc.so.6' -> '/mnt/chrootDir/lib/x86_64-linux-gnu/libc.so.6'
'/lib64/ld-linux-x86-64.so.2' -> '/mnt/chrootDir/lib64/ld-linux-x86-64.so.2'

We’ve successfully added the required dependencies for the touch command.

Similarly, we can follow the same procedure to add the dependencies for the ls and pwd commands.

4. Using chroot in a Script

We’ve laid the foundation for this final stage from the previous section, so the heavy lifting has been done.

Now, let’s create a Bash script that uses chroot:

$ cat test.sh
#!/bin/bash

# Store the chroot directory path
export dir_chroot=/mnt/chrootDir

# chroot to root directory
chroot "$dir_chroot" bash -c '

# create a txt file
touch Ourtestfile.txt

# write into the created file
echo -n "This command was all run inside $dir_chroot and this is amazing!!!!!" > Ourtestfile.txt

cat Ourtestfile.txt
# Add space
echo ""

echo ""
echo "Pay attention to the path, it is not /mnt/chrootDir/"
pwd

echo "list the content of the present working directory"
ls
'

The script runs a series of commands within the chroot directory. Notably, we’ve added binaries for touch, ls, pwd, and echo using the procedure we stated previously, but we excluded the cat command. This was deliberately done to show that trying to run binaries that aren’t included within the chroot directory will result in an error.

Let’s run the script:

$ sudo bash test.sh 
bash: line 9: cat: command not found 
Pay attention to the path, it is not /mnt/chrootDir/ 
/ 
list the content of the present working directory 
Ourtestfile.txt bin lib lib64

As mentioned previously, the filesystem of the chroot directory is isolated from the default filesystem of the operating system. Notably, the pwd command prints /, which indicates the topmost hierarchy of the chroot directory, further confirming the current directory within the script.

5. Conclusion

In this article, we explored how to use chroot in a Bash script. As a prerequisite, we need to prepare the chroot directory by adding all the necessary binaries and their respective dependencies.

Alternatively, the process of creating the chroot directory, as well as adding the binaries and dependencies, can all be applied within the script before calling the chroot command. However, it’s best to manually prepare the chroot directory and all the needed commands to avoid errors.