If you use jails (or want to use jails) but your pool of IP addresses is somewhat limited don’t worry. You can fully configure and use a jail in a private IP, and even assign port forwarding from the “outside” network to reach the jail.
First things first, create a loopback interface clone and assign it an IP address:
ifconfig lo1 create ifconfig lo1 inet 10.1.1.1/32
To make this live across reboots add the following lines to /etc/rc.conf:
cloned_interfaces="lo1" ifconfig_lo1="inet 10.1.1.1 netmask 0xffffffff"
Now, use ezjail to create and configure a new jail and assign this internal IP address. If you start the jail now you will be able to access it, but in the jail itself you will not be able to access the outside world… this is where NAT comes in.
There is at least 2 options, the natd daemon + ipfw or the pf route. I opted for the pf route simply because the configuration is much more simple (but if you are more pro-efficient with natd and ipfw probably it’s the best bet).
As always be careful when messing with a firewall, specially if you are working on a remote server, as you can lock yourself out of your own server. I usually set up an at job that reboots to the previous state in half an hour or so to test everything before committing the changes permanently to rc.conf (to start and stop services with no rc.conf entry you can use the onestart/onestop option).
This is the most economical version of /etc/pf.conf (adjust the external interface and the jail IP (the first two lines):
ext_if="em0" JAIL_SRV="10.1.1.1" set skip on lo0 scrub in all nat on $ext_if from lo1:network to any -> $ext_if pass all
and fire up pf
service pf start
and now from inside the jail you can access the world. Actually, the FreeBSD manual (in it’s current writing) states an additional step, that is to enable the sysctl gateway_enable=”YES” option to nat work, but I didn’t enable it on two machines running FreeBSD 10 and is working perfectly. In set-ups with natd + ipfw you have to enable it for sure, on old FreeBSD versions with pf I just don’t know… but if you can’t access the world from within the jail enable this would be on top of my list.
To make this permanently just have to add to /etc/rc.conf
pf_enable="YES"
Now, that you have the jail all set-up, It’s about time to expose a service to the world (let’s say for example a HTTP server running clear and ssl – ports 80 and 443), you just need a tweak in /etc/pf.conf:
ext_if="em0" JAIL_SRV="10.1.1.1" PORT_WWW="{80,443}" set skip on lo0 scrub in all nat on $ext_if from lo1:network to any -> $ext_if rdr pass on $ext_if proto tcp from any to $ext_if port $PORT_WWW -> $JAIL_SRV pass all
You can jail services without using external IPs, assign HDD space via ZFS or virtual disk files, set CPU core(s) affinity, or fine grained memory and CPU limits via rctl.
Pretty cool!