chroot

On this page 7

chroot is a Unix and Linux system call, and the command-line tool built on it, that changes the apparent root directory of a process and all of its children, so that the path / resolves to a chosen subdirectory. In Linux security it is one of the oldest filesystem confinement techniques, used to limit which files a service can see and to build minimal environments for recovery, packaging and testing.

The mechanism is narrow by design: chroot restricts path lookup only. It does not isolate process IDs, network interfaces, users or kernel resources, and a process that keeps root privileges inside the new root can usually escape it. For that reason modern Linux treats chroot as a building block, not a security boundary, and combines it with namespaces, capabilities and mandatory access control.

How chroot changes path resolution

The kernel stores a root directory reference for every process alongside its current working directory. When a program opens an absolute path, the lookup starts from that root reference instead of the real filesystem root. The chroot(2) system call replaces the reference, and the change is inherited by every child process created afterwards with fork and exec.

Only a process holding the CAP_SYS_CHROOT capability may call chroot. The call also does not change the current working directory, which is the origin of the classic escape: a directory handle that still points outside the new root remains usable. Careful programs therefore call chdir("/") immediately after chroot and close every inherited file descriptor that references the outside tree.

The following table summarizes what the call changes and what it leaves untouched.

Process attributeChanged by chrootPractical consequence
Root directoryYesAbsolute paths resolve inside the new tree
Working directoryNoMust be reset with chdir to avoid escapes
Open file descriptorsNoInherited handles still reach the host tree
User and group IDsNoRoot inside remains root on the host
PID, network, IPC viewsNoHost processes and sockets stay visible
Mounted filesystemsNoMounts must be prepared inside the tree
Effect of chroot on process attributes

Building a working chroot environment

A chroot directory must contain everything the confined program needs, because nothing outside it can be reached by path. Dynamically linked binaries require their shared libraries and the dynamic loader, name resolution needs files such as /etc/resolv.conf and /etc/nsswitch.conf, and many tools expect /dev/null, /dev/urandom and a mounted /proc.

Administrators usually populate the tree in one of three ways, each with different size and maintenance effort.

MethodTypical sizeBest suited for
Hand-copied binariesA few megabytesOne static or small service
debootstrap or dnf –installrootAbout 150 to 400 MBFull distribution userland for builds
Statically linked programUnder 10 MBMinimal jail with no library copies
Bind-mounted host directoriesNo extra spaceRecovery shells and package builds
Common ways to populate a chroot

For a rescue chroot, the typical sequence is to mount the damaged root partition, bind-mount /dev, /proc, /sys and /run into it, and run chroot /mnt /bin/bash. Tools such as arch-chroot automate these bind mounts. Package builders like pbuilder and mock use the same idea to compile software against a clean, reproducible userland.

Why chroot is not a security boundary

chroot was never designed to contain a hostile privileged process. A process with root inside the jail can create a new directory, chroot into it while keeping a handle on the old root, and walk upward with repeated .. lookups until it reaches the real root. It can also create device nodes with mknod, mount filesystems, load kernel modules or attach to host processes with ptrace.

Even an unprivileged process inside a chroot shares the host PID namespace and network stack, so it can see other processes, open sockets and reach local services. The table lists the main escape and leakage routes together with the control that closes each one.

Escape or leak routeRequiresMitigation
Nested chroot with open handleCAP_SYS_CHROOTDrop root and capabilities after chroot
Device node creationCAP_MKNODMount the tree with nodev
Mounting filesystemsCAP_SYS_ADMINUse a mount namespace and drop capabilities
ptrace on host processesSame UID or CAP_SYS_PTRACEPID namespace, Yama ptrace scope
Leaked file descriptorsCareless parent processClose descriptors before exec
Setuid binaries inside the treeWritable or copied binariesMount with nosuid, remove setuid bits
Escape routes and the controls that close them

The decisive rule is simple: drop privileges after entering the jail. A daemon that calls chroot, then changes to an unprivileged UID and discards its capabilities, gains real protection against path-based attacks, while a root shell in a chroot gains almost none.

chroot compared with other Linux isolation tools

Modern isolation on Linux builds on namespaces and seccomp, with chroot or its stronger relative pivot_root handling only the filesystem view. Neighboring Terms in Linux security such as AppArmor, SELinux, Seccomp and Linux capabilities each confine a different dimension, and they work best in combination.

MechanismWhat it restrictsStrength as a boundary
chrootFilesystem path lookupWeak; escapable with root
pivot_root in a mount namespaceEntire mount treeStrong when the old root is unmounted
NamespacesPIDs, network, users, IPC, mountsStrong, basis of containers
SeccompAllowed system callsStrong reduction of kernel attack surface
AppArmor or SELinuxFile, network and capability access by policyStrong, enforced by the kernel
Linux capabilitiesIndividual root privilegesStrong when unneeded ones are dropped
chroot and related confinement mechanisms

Container runtimes use pivot_root rather than chroot because it detaches the old root completely, which removes the nested-chroot escape. systemd offers RootDirectory= for chroot-style confinement and RootImage= together with ProtectSystem and PrivateTmp for a more complete sandbox.

Where chroot is still the right choice

chroot remains useful wherever the goal is a different filesystem view rather than strong isolation. Typical cases include repairing a system that no longer boots, installing a bootloader from live media, building packages, running a legacy userland on a newer kernel and restricting SFTP users to their home directories with the OpenSSH ChrootDirectory option.

  • System recovery: reinstalling GRUB or regenerating an initramfs from a live USB.
  • Privilege-separated daemons: OpenSSH, Postfix, BIND and NTP servers can chroot their unprivileged worker processes.
  • SFTP jails: the jail directory must be owned by root and not writable by the user, or sshd refuses the login.
  • Cross-architecture builds: combined with QEMU user-mode emulation for ARM or RISC-V userlands.

A common mistake is copying a setuid binary such as su or sudo into the jail, which hands an attacker a ready escalation path. Another is leaving the jail directory writable by the confined user, which allows planting hard links or crafted configuration files.

The short version

Imagine handing someone a building plan where one room is labeled as the whole building. Wherever they look on that plan, they only see the walls of that one room. chroot does the same for a program: it tells the program that a single folder is the top of the entire disk, so files outside that folder simply do not appear.

This is handy for repairs and for keeping a service away from files it has no business reading. It is not a locked vault, however. A program with administrator power can find its way out, so serious isolation also needs the other tools Linux provides.

Where it makes a difference

A hosting provider lets about 200 customers upload website files over SFTP. Without confinement, each customer could browse system files, including /etc, other customers’ home directories and log files on the shared server. The administrator adds a Match Group sftponly block to the sshd configuration with ChrootDirectory /srv/sftp/%u and ForceCommand internal-sftp.

Each jail directory is owned by root with mode 755, and a writable upload subdirectory belongs to the customer. Because internal-sftp runs inside the sshd process, no shell, libraries or device files need to be copied into the jail. After the change, a customer who logs in sees only their own tree, and an audit confirms that cross-customer file access is no longer possible through SFTP, while shell access remains disabled for the group.