A simple sandboxing script for FreeBSD

I wrote a very simple script to setup chroot sandboxes for running applications or development tools in FreeBSD:

https://fossil.tobykurien.com/freebsd-sandbox

This script creates a sandbox per directory, allowing you to run untrusted code while preventing access to your home directory. No setup or configuration needed, just cd into a directory and run "sandbox.sh". Also perfect for creating dev environments, simply add a "packages.pkg" file with the packages you need for your project, run "sandbox.sh" and voila! A sandbox with the packages you want installed, just inside your sandbox (doesn't pollute your host OS). Details at the repo linked above.
 
This script creates a sandbox per directory, allowing you to run untrusted code while preventing access to your home directory.
Chroot'ed processes are only limited on the filesystem, if you really want to "sandbox" untrusted code a jail(8) would provide a lot more separation.

A read-only copy of the base OS is mounted into the sandbox
So, you're almost there already, just swap chroot(8) for a jail(8).
 
Agreed, but for the common use cases, chroot is all I need, for example I want to run a development web server, I really don't want network isolation. I am experimenting with a separate jail-based one, but it requires more setup.
 
for example I want to run a development web server, I really don't want network isolation.
Jails also provide process separation. Jailed processes run in their own namespace on the kernel. Network isolation is only on VNET jails.
 
I've made some updates to my script. It now uses jails for isolation. In addition, you can customize the sandbox easily (just add a sandbox.args file to the directory) to do things like remove network access or mount directories into the sandbox as read-write or read-only. I find it very useful for viewing e-mail attachments safely by removing internet access.

Details at the repo:
https://fossil.tobykurien.com/freebsd-sandbox

I plan on using this to easily host services on FreeBSD the same way I currently do on Linux, by running them in sandboxes under tmux.
 
Fly-by comment:
In your script you call 'sudo'.
1. BSD has sort of shifted to use `doas` because it's easier to use and whatnot.
a. you shouldn't require dependencies like 'sudo' or 'doas' for your script(s).
2. You should check for root access and exit early.

Code:
sudo chown -R 0:0 ...

Check for root access and exit early in the script.
Code:
if [ $(id -u) -ne 0 ]; then
        echo "[$(date +'%Y-%m-%d %H:%M:%S')] *ERROR*: This script must be run as root." >&2
        exit 1
fi

So later in the script you can just call 'chown -R ...' (and the like) as you need.

Better yet, build helper functions for the things you do often (like report an error).

sh:
# err --
#   all error messages should be directed to `stderr`.
# EX
#   if ! do_something; then
#       err "unable to do_something"
#       exit 1
#   fi
err() {     #{{{
    echo "[$(date +'%Y-%m-%d %H:%M:%S')] *ERROR*: $*" >&2
    return 0
}

# Bail out if we don't have root privileges.
test_root() {
        if [ $(id -u) -ne 0 ]; then
                err "This script must be run as root."
                exit 1
        fi
}

When I get another moment, I'll talk about variables and arguments.
 
Thank you JohnK that's useful. I did it this way because I don't want the whole script to run as root, otherwise many files and mounts will end up being owned by root. In fact I would prefer not to need sudo/doas at all, or at the worst case, only need it for one command so that I can make that command passwordless (e.g. jail). Good point about doas though, might be good to check if it's avaiable and use it instead.
 
It's usual to consider trapping signals 0, 1, 2, 3, 13, and 15. Signal 0 is not actually a signal, it's the spacial case of shell executing an exit command. My shell scripts would generally start with something like:
Code:
XSTAT=0       # global value of exit status for the script, set by error handlers
TEMPFILES=""  # list of temporary files
trap '[ -n "$TEMPFILES" ] && rm -f $TEMPFILES; trap "" 0; exit $XSTAT' 0 1 2 3 13 15
Note that the shell variables in the trap are evaluated at the time the trap is entered (not at the time it is set).
The trap "" 0; is for clarity, and not technically necessary, as the shell won't recurse trapping "signal 0".
 
Back
Top
OSZAR »