Lab 08 โ chroot Jail: Filesystem Isolation the Old Way¶
| Course | SCIA-360 OS Security |
| Topic | Sandboxing & Isolation |
| Chapter | 8 |
| Difficulty | โญโญ Intermediate |
| Estimated Time | 45โ60 minutes |
| Prerequisites | Labs 05โ07 completed; Docker installed and running |
Overview¶
chroot โ change root โ is the original Unix filesystem isolation mechanism. It changes what a process sees as /, preventing it from accessing paths outside the designated jail directory. Introduced in Unix Version 7 (1979), it predates containers by four decades and is still in active use today.
In this lab you will:
- Build a functional
chrootjail from scratch, including shared library dependencies - Enter the jail and verify filesystem isolation
- Attempt (and understand why you cannot succeed from inside) to escape
- Identify the fundamental limitations of
chrootas a security boundary - Compare
chrootto modern container isolation and understand why Docker uses namespaces
Grading Rubric
| Component | Points |
|---|---|
| Screenshots (08a โ 08f) | 40 pts |
| chroot vs. container comparison table (completed) | 20 pts |
| Reflection questions (4 ร 10 pts) | 40 pts |
| Total | 100 pts |
Part 1 โ Building the Jail¶
Step 1.1 โ Launch the Lab Container¶
All steps in Parts 1โ4 run inside this container. The Docker container is your "host" for purposes of this lab; the chroot jail will be constructed inside it.
Step 1.2 โ Create the Jail Directory Structure¶
Expected output: bin etc lib lib64 tmp
The jail needs the same directory structure that a minimal Linux root filesystem requires. Programs expect to find libraries in /lib and /lib64, executables in /bin.
Step 1.3 โ Copy Binaries Into the Jail¶
We copy only four binaries. Anything not explicitly copied will be unavailable inside the jail โ this is intentional. The minimal surface area is part of the security model.
Step 1.4 โ Resolve and Copy Shared Library Dependencies¶
ELF binaries on Linux are dynamically linked โ they do not contain all their code. At runtime the dynamic linker (ld-linux) finds and loads shared libraries. Inside the jail, the library paths point to the jail's /lib, so those files must exist there.
ldd /bin/bash | grep -oP '/[^ ]+\.so[^ ]*' | sort -u | xargs -I{} cp {} /jail/lib/ 2>/dev/null
ldd /bin/ls | grep -oP '/[^ ]+\.so[^ ]*' | sort -u | xargs -I{} cp {} /jail/lib/ 2>/dev/null
ldd /bin/cat | grep -oP '/[^ ]+\.so[^ ]*' | sort -u | xargs -I{} cp {} /jail/lib/ 2>/dev/null
cp /lib64/ld-linux-x86-64.so.2 /jail/lib64/ 2>/dev/null
echo "Library count: $(ls /jail/lib/ | wc -l)"
Expected output: Library count: 15 to Library count: 25 depending on the Ubuntu version and library sharing between binaries.
ldd lists runtime library dependencies. The grep -oP extracts just the full filesystem paths. xargs cp copies each one into the jail.
The dynamic linker is critical
/lib64/ld-linux-x86-64.so.2 is the dynamic linker itself โ the first program that runs when you execute any dynamically linked binary. Without it in lib64, none of the copied binaries will execute inside the jail. The copy may silently fail if the path differs on your system โ verify with ls /jail/lib64/.
Step 1.5 โ Add Content and Verify the Structure¶
Expected output: A listing showing bin/, etc/, lib/, lib64/, tmp/ with appropriate sizes.
๐ธ Screenshot checkpoint 08a: Full ls -la /jail/ output showing all five subdirectories and their sizes.
Part 2 โ Entering and Testing the Jail¶
Step 2.1 โ Enter the Jail¶
You are now inside the jail. The shell prompt may change. From this point on, the process's root is /jail โ it cannot see anything outside.
Expected output:
ls / shows only the jail contents. /etc/passwd, /home, /proc, /sys โ none of the host filesystem is visible.
๐ธ Screenshot checkpoint 08b: Inside the jail: ls / output showing only the five jail directories, and cat /etc/message.txt output.
Step 2.2 โ Attempt to Escape with ../¶
Expected output: pwd still prints /. The kernel enforces the chroot boundary โ .. at the root of a chrooted process points back to the same root, so traversal is impossible at the kernel level.
Step 2.3 โ Observe the Limited Environment¶
id 2>/dev/null || echo 'id: not available in jail'
ps 2>/dev/null || echo 'ps: not available in jail'
ping 2>/dev/null || echo 'ping: not available in jail'
Expected output:
Only what we explicitly copied exists in the jail. id, ps, and ping were not copied, so they do not exist at /bin/id etc. within the jail's filesystem view.
๐ธ Screenshot checkpoint 08c: All three not available in jail lines in the same terminal output.
Step 2.4 โ Exit the Jail and Verify Host Access¶
Back on the host (inside the Docker container):
Expected output: pwd shows a path outside the jail (e.g., /), /etc/passwd exists and is readable, and the jail's message.txt is readable from outside โ confirming that the host sees everything, including jail contents.
๐ธ Screenshot checkpoint 08d: Post-exit terminal showing ls /etc/passwd (exists on host) and cat /jail/etc/message.txt readable from outside.
Part 3 โ chroot Limitations¶
Step 3.1 โ Root Can Escape (Conceptual Demonstration)¶
The most important limitation of chroot as a security boundary: a root process can always escape.
chroot /jail /bin/bash -c '
echo "Inside jail: root is at / but..."
echo "Key limitation: root (UID 0) can call chroot() again to escape"
echo "Root inside chroot = not a real security boundary"
echo "This is why Docker uses namespaces, not just chroot"
'
The escape mechanism: A root process inside a chroot can:
- Create a new directory anywhere in the jail
- Call
chroot()again to enter that directory as the new root - Call
chdir("../../../../etc")to navigate outside the original jail - Now have access to the original host filesystem
This is a documented kernel behaviour, not a bug โ chroot() was never designed as a security primitive for root processes.
๐ธ Screenshot checkpoint 08e: The four echo lines confirming the root escape limitation.
Step 3.2 โ /proc Is Not Isolated in a Basic chroot¶
mkdir -p /jail/proc
chroot /jail /bin/bash -c \
'ls /proc 2>/dev/null || echo "/proc not mounted โ shared with host in real chroot"'
Expected output: Either an empty listing (proc not mounted) or the message about it not being mounted.
The security implication: In a real chroot deployment (not this Docker-within-Docker lab), if an administrator binds /proc into the jail for tools like ps to work, the chrooted process can read /proc/<pid>/ for every host process โ leaking PIDs, command lines, environment variables, and open file descriptors of processes it should not be able to see. chroot provides zero PID namespace isolation.
Part 4 โ chroot vs. Container Comparison¶
Step 4.1 โ Side-by-Side Isolation Comparison¶
echo ''
echo '=== What chroot isolates ==='
echo 'โ Filesystem: process sees jail root as /'
echo ''
echo '=== What chroot does NOT isolate ==='
echo 'โ PID namespace: can see host processes via /proc'
echo 'โ Network: same interfaces as host'
echo 'โ IPC: same message queues and semaphores'
echo 'โ User IDs: same UIDs/GIDs as host'
echo 'โ Root escape: root inside can chroot() out'
echo ''
echo '=== What containers add on top of chroot ==='
echo '+ PID namespace: isolated process table'
echo '+ Network namespace: isolated interfaces'
echo '+ Mount namespace: isolated mounts'
echo '+ User namespace: UID remapping'
echo '+ seccomp: syscall filtering'
echo '+ capabilities: privilege reduction'
echo '+ cgroups: resource limits'
๐ธ Screenshot checkpoint 08f: Full comparison output with all three sections visible.
Step 4.2 โ Where chroot Is Still Used Today¶
echo 'Real use cases for chroot in modern systems:'
echo '1. dpkg/apt build environments (debootstrap creates chroot build roots)'
echo '2. System rescue (boot from live CD, chroot into broken OS to repair)'
echo '3. FTP server isolation (vsftpd --chroot_local_user option)'
echo '4. DNS servers (BIND can chroot to /var/named for log/config isolation)'
echo '5. Base layer concept in Docker (mount namespaces extend the chroot idea)'
chroot is not obsolete โ it is still a valid tool for its original purpose of filesystem path isolation in trusted environments. The key is understanding what it does not protect against.
chroot vs. Container Isolation Table¶
Complete and submit this table with your lab report:
| Isolation Property | chroot Jail | Docker Container |
|---|---|---|
Filesystem root (/) isolation | โ Yes | โ Yes (mount namespace) |
| PID namespace โ process visibility | โ No | โ Yes |
| Network namespace โ interface isolation | โ No | โ Yes |
| IPC namespace โ semaphores, message queues | โ No | โ Yes |
| User namespace โ UID remapping | โ No | โ Yes (optional) |
| Root process escape prevention | โ No | โ Partial (capabilities + seccomp) |
| Syscall filtering | โ No | โ Yes (seccomp) |
| Resource limits (CPU, memory) | โ No | โ Yes (cgroups) |
| Capability reduction | โ No | โ Yes |
Cleanup¶
Exit the Docker container (type exit if still inside), then prune Docker resources:
Assessment¶
Screenshot Checklist¶
| ID | Required Content |
|---|---|
| 08a | ls -la /jail/ showing all five subdirectories with sizes |
| 08b | Inside jail: ls / showing only jail contents + cat /etc/message.txt output |
| 08c | All three not available in jail messages for id, ps, ping |
| 08d | After exit: ls /etc/passwd confirming host filesystem accessible |
| 08e | The four root-escape limitation echo lines |
| 08f | Full three-section chroot vs. container comparison output |
Reflection Questions¶
Submission requirement
Answer each question in complete paragraphs (minimum 4โ6 sentences each). Reference specific commands, system calls, or kernel mechanisms where relevant.
Q1. What does chroot literally do at the system call level? Explain in terms of the process's view of the filesystem hierarchy. Why did you need to copy shared library files (.so files) into the jail in Step 1.4 โ why couldn't the jail binaries just use the libraries already present on the host filesystem?
Q2. chroot does not provide network namespace isolation. If you ran a web server inside a chroot jail listening on port 8080, could a process on the host connect to it? Why does the absence of network isolation matter for security sandboxing โ what kinds of attacks remain possible against a chrooted service that would be prevented by a full container with network namespace isolation?
Q3. Why is chroot not a security boundary for root processes? Describe the specific mechanism โ the sequence of system calls โ that allows a root process inside a chroot jail to break out of it. Why does this attack not work for non-root processes?
Q4. Docker uses Linux mount namespaces to give each container its own filesystem view, which is conceptually similar to chroot but substantially more powerful. Based on what you observed in this lab and your knowledge from Labs 06โ07, list and explain three specific security properties that mount namespaces provide that a plain chroot jail cannot.