FreeBSD jail(8) provides lightweight, kernel-level containers for the secure isolation of one or more processes up to a complete userland.
Jails were introduced by Poul-Henning Kamp in March 2000 with FreeBSD 4.0-RELEASE.
Feature requests, directly within jail(8) or supporting resources:
The VNET is set up using an
if_epair(4)
interface bridged with the actual network interface, in my case re0.
Just like if_tap(4) interfaces, epairs can be used by one jail at a
time, so if you need to run more than one jail at the same time,
you have to make more epairs.
If you're using tap interfaces for bhyve VMs, you can just addm
them into the bridge.
Add the following lines to /etc/rc.conf:
cloned_interfaces="bridge0" ifconfig_bridge0="addm re0 up"
Add or edit the kld_list parameter to add the following module in /etc/rc.conf:
kld_list="if_bridge if_epair"
Apply changes:
# service netif restart && service routing restart
The jail needs to inherit /dev/bpf* from the host in order for
networking to work at all. Make a new /etc/devfs.rules ruleset:
[devfsrules_jails=5] add include $devfsrules_hide_all add include $devfsrules_unhide_basic add include $devfsrules_unhide_login add path 'bpf*' unhide
Restart devfs(8):
# service devfs restart
We'll name this jail foo and give it the other end of the epair
as its network interface. Its IP address will be acquired using
DHCP. The reason I'm manually calling dhclient(8) is because adding
ifconfig_epair0b="DHCP" in the jail's /etc/rc.conf doesn't
work. Options are detailed in
jail.conf(5).
/etc/jail.conf:
path = "/usr/local/jail/$name";
host.hostname="$name";
exec.clean;
exec.start = "/bin/sh /etc/rc";
exec.stop = "/bin/sh /etc/rc.shutdown";
allow.mount;
allow.raw_sockets = 1;
mount.devfs;
devfs_ruleset="5";
vnet;
sysvmsg=new;
sysvsem=new;
sysvshm=new;
foo {
vnet.interface = "epair0b";
exec.start += "dhclient epair0b";
}
There are a few things that should always be kept in mind:
/etc/jail.conf, and these are respected when doing parallel startup.# mkdir -p /usr/local/jail/foo # bsdinstall jail /usr/local/jail/foo ... # ifconfig epair0 create # ifconfig bridge0 addm epair0a # ifconfig epair0a up # service jail start foo
Test to see if the jail has networking:
# jexec foo ping google.com
On jail shutdown, destroy the epair:
# service jail onestop foo # ifconfig epair0a destroy
Deleting jails isn't as straight forward, so I'm leaving this here as well:
# service jail stop foo # chflags -R noschg /usr/local/jail/foo # rm -rf /usr/local/jail/foo
There are three ways to do networking with jails: (i) using IP addresses (aliases) from the host, (ii) using if_bridge(4) and epair(4) interfaces, and (iii) using netgraph. I will start this how-to with the third method as the second method has already been explained by Mason Loring Bliss.
This is quite confusing. See the example at https://people.freebsd.org/~julian/netgraph.html.
Let's start off with a small network consisting of two jails, called “test1” and “test2”, which are bridged to an new, virtual interface on the host machine. We thus need three new virtual interfaces. We will use ngctl to manage all netgraph nodes, hooks, and edges. This tool can be used interactively or you can supply the command on the command line. Below we will use it interactively. Note that this command must be run as the root user.
# ngctl
mkpeer eiface ether ether mkpeer eiface ether ether mkpeer eiface ether ether name ngeth0: ng_host name ngeth1: ng_test1 name ngeth2: ng_test2
We create three interfaces of type eiface as we need Ethernet interfaces (as opposed to L3 interfaces when using type iface). The man page state they are named ngeth0, etc. We rename them to indicate their purpose.
Next we need to bridge these interfaces together. We can either create the bridge first and then connect it to the three interfaces, or while creating the bridge, we can already connect it to an interface.
/* * OPTION A: * To only create the bridge but not connect it to any existing nodes */ mkpeer bridge link0 link0 /* * OPTION B: * Create a new node of type bridge and connect it to the * path 'ng_host:'. Use its hook called 'ether' to connect it to the * hook 'link0' of the new node (the bridge). */ mkpeer ng_host: bridge ether link0 /* * As opposed to the Ethernet interfaces, a bridge does not have a * name by default. So we name it here. */ name [1e]: br0 /* * Now connect the nodes together. The first connection is only needed * when option A was used. */ connect br0: ng_host: link0 ether connect br0: ng_test1: link1 ether connect br0: ng_test2: link2 ether
The above configuration is pretty straightforward, the exception being the name command. Since the bridge does not have a name, we can't use it to construct a path. Instead we must use its unique ID (between brackets). To determine this ID, we need the list command which I ommitted from the configuration above. Let's show it here.
+ list There are 6 total nodes: Name: ng_host Type: eiface ID: 00000007 Num hooks: 1 Name: ng_test1 Type: eiface ID: 00000009 Num hooks: 0 Name: ngctl85802 Type: socket ID: 0000002b Num hooks: 0 Name: <unnamed> Type: bridge ID: 0000001e Num hooks: 1 Name: ng_test2 Type: eiface ID: 0000000e Num hooks: 0 +
The above output shows the unique ID for the bridge (preceding zeros can be ommitted) as well as the number of hooks used, i.e. connections made.
I thus created the bridge by directly connecting it to the ng_host interface.
I assume you know how to setup a jail, i.e. create a base jail, clone it, etc. If not, please read the Jails chapter of the FreeBSD Handbook.
The below configuration only includes the networking-specific configuration for the two jails. Although shown per jail, you can easily move these two lines to the default configuration using the variable $name to customize the interface name.
test1 {
vnet;
vnet.interface = ng_test1;
}
test2 {
vnet;
vnet.interface = ng_test2;
}
Once the jails have been started, you can manually configure IP addresses on their interfaces, as well as on the host interface ng_host.