FreeBSD jails fixed address

2021-05-24
2 min read

In illumos, zones (which are based on FreeBSD jails) are configured through ZONECFG(1M). On which you could specify an option called allowed-address, this option gives a zone a fixed IP address that users cannot change.

In FreeBSD, there is no such JAIL(2) property, but we could create nested jails with which could accomplish the same.
To do this, we need to create a parent jail that will use VNET(9), and the children will just inherit its ip4/ip6 configuration. For creating interfaces on jails I’m using the jng script which uses NETGRAPH(3) to generate the interfaces inside the jail which are not visible to the host, that feature does not pollute the host which multiple interfaces.

Creating the parent and child jails

First copy the jng script from /usr/share/examples/jails/jng to /usr/local/sbin Then create the jails and use vnet for networking. You will need to load the ng_ether kernel module for this to work.

# kldload ng_ether

Now for example create the following I used the following /etc/jail.conf

demojail {
        vnet=new;
        vnet.interface = "ng0_eth0";
        path=/jails/outer;
        persist;
        exec.system_user = "root";
        exec.jail_user = "root";
        exec.prestart += "jng bridge eth0 vtnet0";
        exec.start += "/sbin/ifconfig ng0_eth0 192.168.1.233 netmask 255.255.255.0 up";
        exec.start += "/sbin/ifconfig lo0 127.0.0.1  up";
        exec.start += "/sbin/route add default 192.168.1.1";
        exec.poststop += "jng shutdown demojail";
        allow.raw_sockets;   
        allow.set_hostname;
        devfs_ruleset="11";   
        mount.devfs;
}
demojail.j0 {
        host.hostname = "demojail";   # hostname
        path = "/jails/demojail";     # root directory
        ip4 = inherit;
        ip6 = inherit;
        allow.raw_sockets;
        devfs_ruleset="0";
        exec.start += "/bin/sh /etc/rc";
        exec.stop = "/bin/sh /etc/rc.shutdown";
        persist;
}

/etc/devfs.rules

[devfsrules_jail=11]
add include $devfsrules_hide_all
add include $devfsrules_unhide_basic
add include $devfsrules_unhide_login
add path 'bpf*' unhide

the children.max option on jail.conf allows this jail to have at most 1 child jail. Then to enable the jail to be started at startup, type:

# sysrc jail_enable=YES

Now simply start the jails

# service jail start 

then enter the jail, first get the jail id using jls, in my case the child jail id is 48.

neirac@fbsd-dev:~ $ sudo jexec 48 sh
# ifconfig -a
lo0: flags=8049<UP,LOOPBACK,RUNNING,MULTICAST> metric 0 mtu 16384
	options=680003<RXCSUM,TXCSUM,LINKSTATE,RXCSUM_IPV6,TXCSUM_IPV6>
	inet6 ::1 prefixlen 128
	inet6 fe80::1%lo0 prefixlen 64 scopeid 0x1
	inet 127.0.0.1 netmask 0xff000000
	groups: lo
	nd6 options=21<PERFORMNUD,AUTO_LINKLOCAL>
ng0_eth0: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> metric 0 mtu 1500
	options=28<VLAN_MTU,JUMBO_MTU>
	ether 0a:a0:8d:81:9f:d3
	hwaddr 58:9c:fc:10:ff:bb
	inet 192.168.1.233 netmask 0xffffff00 broadcast 192.168.1.255
	media: Ethernet autoselect (1000baseT <full-duplex>)
	status: active
	nd6 options=29<PERFORMNUD,IFDISABLED,AUTO_LINKLOCAL>
# 

Trying to change the ip address of the jail errors out with

# ifconfig ng0_eth0 192.168.1.234 netmask 255.255.255.0 
ifconfig: ioctl (SIOCDIFADDR): permission denied

References

https://lists.freebsd.org/archives/freebsd-jail/2017-July/003407.html
https://www.cyberciti.biz/faq/configuring-freebsd-12-vnet-jail-using-bridgeepair-zfs/
https://lists.freebsd.org/archives/freebsd-jail/2017-July/003407.html