FreeBSD Jail with Single IP

This guide assumes you already have a jail installed, I personally use ezjail-admin. Here are three guides regarding the installation of ezjail-admin. Cyberciti.biz, Erdgeist.org [creator of ezjail-admin], Secure-Computing.net.

The Scenario

You have a FreeBSD VPS with a single IP and you wish to create a FreeBSD jail for additional security and/or isolation. For this write up I’ll illustrate how you can use a single VPS with a jail create on an internal IP with both NAT access and port-forwarding to the jail for specific ports (web, ssh, etc).

Creating the local interface

In your rc.conf we’ll clone the loopback interface to lo1 so that we can use the 192.168.*, 10.*, or 172.16.* for our private jail network.

cloned_interfaces="lo1"
ipv4_addrs_lo1="192.168.0.1-9/29"

The above will create a lo1 loopback device with 192.168.0.1 thru 192.168.0.9 created on that interface. From here we’ll create a jail with 192.168.0.2. Then we’ll configure PF to allow outbound traffic (NAT) from those local addresses as well as pass web (80) and SSH port to a specific jail IP.

Make sure to change the external interface if it is not em0, but rather something like re0.

IP_PUB="Your Public IP Address Here"
IP_JAIL="192.168.0.2"
NET_JAIL="192.168.0.0/24"
PORT_JAIL="{80,443,2020}"
scrub in all
nat pass on em0 from $NET_JAIL to any -> $IP_PUB
rdr pass on em0 proto tcp from any to $IP_PUB port $PORT_WWW -> $IP_JAIL

Any Jail tied to the 192.168.* IP range will have outbound connectivity (needed for installing ports, updates or other packages), but for a specific jail such as your webserver we’ll pass inbound traffic on port 80/443 for the webserver, and 2020 for SSH. You will need to make sure to configure /etc/ssh/sshd_config to listen on 192.168.0.2, Port 2020 or a port of your choosing.

This way you can use the same IP to connect both to the main VPS on one SSH port, but connect to the jail directly on another port.

Once the above are saved in /etc/pf.conf, we need to test it and then start up pf (or restart).

pfctl -nf /etc/pf.conf
service pf start

We can verify the rules with the following command:

# pfctl -sn
nat pass on em0 inet from 192.168.0.0/24 to any -> YOUR_PUBLIC_IP
rdr pass on em0 inet proto tcp from any to YOUR_PUBLIC_IP port = http -> 192.168.0.2
rdr pass on em0 inet proto tcp from any to YOUR_PUBLIC_IP port = https -> 192.168.0.2
rdr pass on em0 inet proto tcp from any to YOUR_PUBLIC_IP port = 2264 -> 192.168.0.2

If you need to perform specific tasks from inside the jail such as ping, traceroute, sockstat and so forth, add the following to the main node’s /etc/sysctl.conf

security.jail.allow_raw_sockets=1

There you have it, once those rules are set up, all the private jails will have outbound traffic allowing you to install ports and download files from other servers, but will also allow specific ports to be forwarded to specific jails (such as forwarding 3306 to a separate database jail from outside of the VPS).

You can for all intents and purposes, forgo the port forwarding above in the pf.conf section, and simply create for example an IPv6-only Jail that still has the ability to to install packages and ports via the outbound NAT traffic thru the single IPv4 address, as not all ports provide an IPv6 compatible download link.

A space saving tip

If you would rather not have to update the ezjail port tree every time there’s an update, you can instead clone a read-only copy of the ports tree from your master node to each of your jails.

First we must shutdown the jail, and remove the symlinked ports folder from the jail, then create a blank folder to be mounted upon.

# cd /usr/jails/myjail.com/usr/
# rm -Rf ./ports
# mkdir ports

Then we’ll edit the jails’ fstab to include the master port tree located at /etc/fstab.myjail_com

/usr/jails/basejail /usr/jails/myjail.com/basejail nullfs ro 0 0
/usr/ports /usr/jails/myjail.com/usr/ports nullfs ro 0 0

Now to make sure that the jail will be able to download distfiles and create a working directory, we need to start up the jail, log into it, and then edit the /etc/make.conf

WRKDIRPREFIX=		/var/ports
DISTDIR=		/var/ports/distfiles
PACKAGES=		/var/ports/packages
INDEXDIR=		/var/ports

Make sure the /var/ports directory exists if it does not already. Once you have those placed in the /etc/make.conf you’ll be able to:

1) Update your ports tree from a single location (the master node)
2) Run ports updates and installation from within the jail without having to restart the jail, or to re-run ezjail-admin update -p.

Comments are closed.