Recently my free time has mostly been dedicated to learning more about parallel computing. But I did take a little detour into the world of old-timey chroot(8) jails on OS X. They work, even on 10.8.4, even running a shell. The big trick on OS X is that you need to include the dynamic link editor /usr/lib/dyld in the jail.

Of course you also need to include the programs you want to run in the jail and their dependencies. The otool -L operation on OS X and the ldd command on linux are key to finding these dependencies. There are a lot, and you’ll need to recursively resolve them. Additionally on OS X, when you are recursively finding the dependencies, you’ll need to account for Mach-O’s @executable_path, @loader_path, and @rpath variables. This can get a little tricky. On Linux, the libraries are easier but things like PAM can be tricky. This is why you want software like jailkit, which can help automate the process of creating and running jails.

While jails aren’t so good for security applications, they can be very helpful in trying to reign-in a proprietary installer.

For my purposes, jailkit wasn’t getting the library dependencies on OS X. So I made a little python module and a script to get that part of the job done.

$ ./mkjail.py -h
usage: mkjail.py [-h] [-l] jail_name jail_programs [jail_programs ...]
    
Create a "jail" directory appropriate for chroot, with copies of the specified
programs and their dependencies
    
positional arguments:
  jail_name      the name of the jail to create
  jail_programs  full path programs to import into the jail
    
optional arguments:
  -h, --help     show this help message and exit
  -l, --link     use hard links instead of copies. !!!WARNING!!!: Changes made
                 to hard-linked files in a jail will also affect the
                 "original" (and probably important) files outside the jail.
                 Additionally, hard links cannot span filesystems. DO NOT USE
                 THIS OPTION UNLESS YOU KNOW WHAT YOU ARE DOING!

An example of creating and entering a jail with everything in /bin and a couple commands from /usr/bin looks like this:

sh-3.2$ sudo ./mkjail.py alcatraz /bin/* /usr/bin/find /usr/bin/whoami
sh-3.2$ sudo chroot -u nobody -g nobody alcatraz
bash-3.2$ pwd
/
bash-3.2$ whoami
nobody
bash-3.2$ find /
/
/Applications
/Applications/Utilities
/bin
/bin/[
/bin/bash
/bin/cat
/bin/chmod
[... and everything else from /bin ...]
/bin/zsh
/dev
/etc
/sbin
/System
/System/Library
/System/Library/Frameworks
/System/Library/Frameworks/CoreFoundation.framework
/System/Library/Frameworks/CoreFoundation.framework/Versions
/System/Library/Frameworks/CoreFoundation.framework/Versions/A
/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation
/System/Library/Frameworks/IOKit.framework
/System/Library/Frameworks/IOKit.framework/Versions
/System/Library/Frameworks/IOKit.framework/Versions/A
/System/Library/Frameworks/IOKit.framework/Versions/A/IOKit
/tmp
/Users
/Users/Shared
/usr
/usr/bin
/usr/bin/find
/usr/bin/whoami
/usr/include
/usr/lib
/usr/lib/dyld
/usr/lib/libauditd.0.dylib
/usr/lib/libauto.dylib
/usr/lib/libbsm.0.dylib
/usr/lib/libc++.1.dylib
/usr/lib/libc++abi.dylib
/usr/lib/libDiagnosticMessagesClient.dylib
/usr/lib/libedit.3.dylib
/usr/lib/libiconv.2.dylib
/usr/lib/libicucore.A.dylib
/usr/lib/libncurses.5.4.dylib
/usr/lib/libobjc.A.dylib
/usr/lib/libstdc++.6.dylib
/usr/lib/libSystem.B.dylib
/usr/lib/libutil.dylib
/usr/lib/libz.1.dylib
/usr/lib/system
/usr/lib/system/libcache.dylib
/usr/lib/system/libcommonCrypto.dylib
/usr/lib/system/libcompiler_rt.dylib
/usr/lib/system/libcopyfile.dylib
/usr/lib/system/libcorecrypto.dylib
/usr/lib/system/libdispatch.dylib
/usr/lib/system/libdnsinfo.dylib
/usr/lib/system/libdyld.dylib
/usr/lib/system/libkeymgr.dylib
/usr/lib/system/libkxld.dylib
/usr/lib/system/liblaunch.dylib
/usr/lib/system/libmacho.dylib
/usr/lib/system/libquarantine.dylib
/usr/lib/system/libremovefile.dylib
/usr/lib/system/libsystem_blocks.dylib
/usr/lib/system/libsystem_c.dylib
/usr/lib/system/libsystem_dnssd.dylib
/usr/lib/system/libsystem_info.dylib
/usr/lib/system/libsystem_kernel.dylib
/usr/lib/system/libsystem_m.dylib
/usr/lib/system/libsystem_network.dylib
/usr/lib/system/libsystem_notify.dylib
/usr/lib/system/libsystem_sandbox.dylib
/usr/lib/system/libunc.dylib
/usr/lib/system/libunwind.dylib
/usr/lib/system/libxpc.dylib
/usr/libexec
/usr/local
/usr/sbin
/usr/share
/var
/var/db
/var/folders
/var/log
/var/run
/var/tmp
bash-3.2$ exit

Then you can use jailkit to put all the config files and non-library dependencies into the jail. Something like this:

jk_init -v -j alcatraz basicshell editors extendedshell netutils ssh sftp scp

Hopefully I’ll find some time to port these changes to jailkit itself.

You can find the code for mkjail on github.