This is a draft.
macOS doesn’t have many of the advanced Linux or UNIX features that have come about in the past 20 years. So getting a proper chroot environment up and running takes a little more work.
The basics of chroot is to run some command (a shell in our case) such that its filesystem root is an arbitrary directory on the system.
For this explainer and on my system I put all my chroot environment
directories in my home under rts and each directory has .root
appended to the name. This convention is purely for bookkeeping.
So the absolute first thing to try is creating a empty chroot
environment, we’ll call it ~/rts/null.root
$ mkdir -p rts/null.root
$ chroot rts/null.rootFails with the following error
chroot: rts/null.root: Operation not permitted
We’ve just learned our first chroot leason. Only the superuser can create a chroot environment (something, something privilege escalation).
Now try to chroot our empty root using sudo.
$ sudo chroot rts/null.rootAgain this fails with a different error
chroot: /bin/sh: No such file or directory
Our empty root does not have access to anything since it is empty. Depending on your needs copying over the necessary files may be appropriate. For other circumstances it may be more appropriate to mount a directory from elsewhere on you system inside the new root.
macOS does not provide an out-of-the-box way to bind a directory
inside of another so we’ll turn to the venerable usespace filesystem
package FUSE. Specifically we’re going to use bindfs (one of the
many FUSE filesystems) to replicate directories.
As above we need sh from /bin so we’ll mount (or rather bind)
/bin to a new root that well place in rts/base.root.
But first we need to install bindfs. If you’re using Homebrew (lord
help you). This is what you need to do.
$ brew install macfuse
$ brew edit bindfsLocate the block that calls disable!.
on_macos do
disable! date: "2021-04-08", because: "requires closed-source macFUSE"
endKill that line with the call to disable!, so it looks like the
following snippet now
on_macos do
endSave and quit your editor. Now Homebrew will allow you to install bindfs.
$ brew install bindfsWe’re only going to bind /bin so we can demonstrate another error
you may encounter. After this we will bind enough to replicate a base
CLI environment.
$ mkdir -p rts/base.root
$ mkdir -p rts/base.root/bin
$ bindfs -r /bin rts/base.root/bin
$ sudo chroot rts/base.rootYou’ll see this fail with the cryptic Killed: 9 and nothing else.
/bin/sh depends on a shared library that also needs to be available
in the chroot. We can check this with otool
$ otool -L /bin/sh
/bin/sh:
/usr/lib/libSystem.B.dylib ...Every binary on macOS needs to link libSystem.B.dylib, even void
main() {} compiled with cc test.c
$ otool -L a.out
a.out:
/usr/lib/libSystem.B.dylibSo we can see that our chroot needs to bind /usr/lib so that any
binary can run.
$ mkdir -p rts/base.root/usr/lib
$ bindfs -r /usr/lib rts/base.root/usr/lib
$ sudo chroot rts/base.rootThen we bind the rest of the system
$ R=rts/base.root
$ mkdir -p $R/usr/libexec
$ mkdir -p $R/usr/sbin
$ mkdir -p $R/usr/share
$ mkdir -p $R/System
$ mkdir -p $R/Library
$ bindfs -r /usr/libexec $R/usr/libexec
$ bindfs -r /usr/libexec $R/usr/libexec
$ bindfs -r /usr/sbin $R/usr/sbin
$ bindfs -r /usr/share $R/usr/share
$ bindfs -r /System $R/System
$ bindfs -r /Library $R/Library
$ mkdir -p $R/etc $R/sbin $R/home $R/tmp $R/var
$ bindfs -r /etc $R/etc
$ bindfs -r /sbin $R/sbin
$ mkdir -p $R/dev
$ bindfs -o dev /dev $R/dev
$ mkdir -p $R/var/run
$ bindfs -r /var/run $R/var/run
$ mkdir -p $R/var/db
$ bindfs -r /var/db $R/var/dbThis works okay for basic UNIX stuff. There are some gotchas I haven’t
worked out. e.g. host, dig, and nslookup can resolve names, but
curl fails.
If you want to compile stuff you’ll need to have installed the Command
Line Tools (outside of the chroot environment since we’ve made it
readonly) or you’d have to bind /Applications/Xcode.app too.
Oh, to exit the chroot, just type exit at the prompt. To unbind the
directories use umount. So umount rts/base.root/bin would remove
the bin binding from the base.root environment.
Hi, I was wondering if you have been able to get chroot to work on macOS Monterey. I have not had any success so far