Skip to content

Instantly share code, notes, and snippets.

@hiway
Last active July 25, 2024 02:14
Show Gist options
  • Save hiway/2b526fc64748e8d6e9f36f289003f843 to your computer and use it in GitHub Desktop.
Save hiway/2b526fc64748e8d6e9f36f289003f843 to your computer and use it in GitHub Desktop.
Creating a single process FreeBSD (12.1) Jail

How to jail a single process web server on FreeBSD 12.1

You may have found many great resources on how to set up jails on latest FreeBSD, however there is scant information on how to go about creating a barebones jail with nothing but the one process that you want to isolate.

I was curious if I could contain a statically compiled web server that I wrote while learning Rust all by itself in a jail instead of having a full userland.

The benefits are:

  • Storage space requirement is trivial: 5MB
  • No need to update the jail separately since there is only one additional binary to daemonize the web app
  • Updating the web app is as simple as stopping the jail, replacing the binary and restarting, with a shell script on the host, that's fast.
  • If an intruder does manage to break in, there is no shell, utility programs or interpreters to leverage

Pre-requisites

  • Working FreeBSD 12.1 machine (this should work on older versions, but I have not tested)
  • Working internet connection to download a package (daemonize)
  • Static-linked binary web app (with Rust, use cargo build --release)
  • A bit of patience

Setup the Jail

First, create directories for the jail.

  • mkdir -p /usr/local/jail/www/{app,dev,lib,libexec,var}
  • mkdir -p /usr/local/jail/www/var/{log,run}

This will create the following tree structure:

[/usr/local/jail/www]# tree
.
├── app
├── dev
├── lib
├── libexec
└── var
    ├── log
    └── run

6 directories, 0 files

Now copy a few required binaries to the jail:

  • cp /libexec/ld-elf.so.1 /usr/local/jail/www/libexec/
  • cp /lib/{libc.so.7,libgcc_s.so.1,libm.so.5,libthr.so.3} /usr/local/jail/www/lib/

The directory tree should look like this:

[/usr/local/jail/www]# tree
.
├── app
├── dev
├── lib
│   ├── libc.so.7
│   ├── libgcc_s.so.1
│   ├── libm.so.5
│   └── libthr.so.3
├── libexec
│   └── ld-elf.so.1
└── var
    ├── log
    └── run

7 directories, 5 files

Install the package daemonize on host:

pkg install daemonize

Copy the daemonize binary to jail:

cp /usr/local/bin/daemonize /usr/local/jail/www/app/

Now copy your static-linked web app to jail. Here I'm copying the Rust web app created using Rocket.rs and compiled for release (cargo build --release)

cp /home/hiway/source/webapp/target/release/webapp /usr/local/jail/www/app/

The directory tree should look like this now:

[/usr/local/jail/www]# tree
.
├── app
│   ├── daemonize
│   └── webapp
├── dev
├── lib
│   ├── libc.so.7
│   ├── libgcc_s.so.1
│   ├── libm.so.5
│   └── libthr.so.3
├── libexec
│   └── ld-elf.so.1
└── var
    ├── log
    └── run

Next, we will set up jails service.

Edit or create /etc/jail.conf using your preferred text editor and append the following to it: Remember to review the values such as ip4.addr and replace with appropriate values for the interface, ip address and subnet.

www {
  host.hostname = "www";
  ip4.addr = "wlan0|192.168.0.99/24";
  path = "/usr/local/jail/www";
  mount.devfs;
  exec.start = "/app/daemonize -v -a \
                -o /var/log/www.stdout \
                -e /var/log/www.stderr \
                -p /var/run/www.pid /app/webapp";
  exec.stop = "";
}

Enable jail service:

sysrc jail_enable=YES

And finally, start the jail:

service jail start www

Visit http://192.168.0.99:8000/ from your browser and verify that it works.

@grahamperrin
Copy link

12.1 died more than a year ago; https://bokut.in/freebsd-patch-level-table/#unsupported.

Please, can you update this for a supported release of FreeBSD? Thanks.

@nvass
Copy link

nvass commented May 21, 2023

The procedure seems to work in the FreeBSD-13 branch.

@elliejs
Copy link

elliejs commented Jul 24, 2024

mostly works on 14.1. Newly required:

  • /root (empty)
  • /etc (all) -- still investigating how much is needed, probably just a few things related to the passwd file

Do note that this won't work if you need an init process, but this guide doesn't claim to in the first place. It's just true that many programs that are not built to run in an environment this minimal can rely on an init process to come reap grandchildren processes

@elliejs
Copy link

elliejs commented Jul 25, 2024

mostly works on 14.1. Newly required:

* /root (empty)

* /etc (all) -- still investigating how much is needed, probably just a few things related to the passwd file

Do note that this won't work if you need an init process, but this guide doesn't claim to in the first place. It's just true that many programs that are not built to run in an environment this minimal can rely on an init process to come reap grandchildren processes

I have distilled etc down to the following:

/etc:

  • master.passwd
  • passwd
  • pwd.db
  • spwd.db

Additionally, the only required shared lib in /lib is libc.so.7

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment