Jails with nullfs mount of base system on FreeBSD 11
----------------------------------------------------------------------

Last edited: $Date: 2017/02/12 14:41:20 $

When setting up a fresh FreeBSD 11 server, I used the system with
nullfs jails as described on my page Jails with nullfs mount
of base system on FreeBSD 10 without buildworld.
See: gopher://box.matto.nl/0/freebsd10jails.txt

I did changed the basic file structure a bit, to make it less complex
to read.

This is the system that I use now:



   /jails :
     directory for every thing jail related
   /jails/master :
     directory with the base filesystem, that will get read only nullfs
     mounted in the jails
   /jails/skeleton :
     directory with the files that will be writeable, each jail gets it own set
   /jails/files/<jailname> :
     directory where files from skeleton are copied to
   /jails/<jailname> :
     empty directory, where everything gets nullfs mounted


The /jails/master is nullfs read only mounted into the jail directory
of every jail, so this part exists only once on the hard disk.

Every jail gets a number of files that are writeable, like
`/etc/rc.conf` and `/etc/ssh/sshd_config`. These files are copied once
from `/jails/skeleton` to `/jails/files/<jailname>`. Every jail will
have its own set, so this part exists as many times on your harddisk
as you have jails.

## Prepare base system

The base system will be mounted read-only into the jail.

We populate the base system from the install CDROM.



   mkdir -p /jails/master
   setenv DESTDIR /jails/master
   mount_cd9660 /dev/cd0 /mnt
   tar -xf /mnt/usr/freebsd-dist/base.txz -C $DESTDIR
   tar -xf /mnt/usr/freebsd-dist/doc.txz -C $DESTDIR
   cd
   umount /mnt
   cp /etc/resolv.conf $DESTDIR/etc/
   chroot $DESTDIR
   tzsetup
   pkg install cpdup


With this last step (`pkg install cpdup`) we force the system to
install the pkg utility. Later on in the process this will not be as
easy as this, because we are going to move directories like /etc out
of the base system.

## Prepare template for the read-write part of the jail

First setup the directory for the template:



   mkdir /jails/skeleton/ /jails/skeleton/home /jails/skeleton//usr-X11R6


Now populate it by moving parts of the base system into it:



   cd /jails/master
   mv etc /jails/skeleton/
   mv usr/local /jails/skeleton//usr-local
   mv tmp /jails/skeleton/
   mv var /jails/skeleton/
   mv root /jails/skeleton/


And create symlinks back into the base system:



   cd /jails/master
   mkdir s
   ln -s s/etc etc
   ln -s s/home home
   ln -s s/root root
   ln -s /s/usr-local usr/local
   ln -s /s/usr-X11R6 usr/X11R6
   ln -s s/tmp tmp
   ln -s s/var var


## Creating the directories for a new jail

Two directories are created for the new jail:



   /jails/<jailname>
   /jails/files/<jailname>


The `/jails/<jailname>` will be populated by a nullfs mount

The `/jails/files/<jailname>` will get real files by copying the skel
directory to it.

### Populate /jails/files/



   cpdup /jails/skeleton /jails/files/<jailname>


### Edit /etc/fstab

On the host add the following lines to /etc/fstab:



   /jails/master   /jails/<jailname>    nullfs  ro      0       0
   /jails/files/<jailname> /jails/<jailname>/s       nullfs  rw      0       0


### Edit /etc/jail.conf

/etc/jail.conf has a block with general configuration for all jails:



   devfs_ruleset = 4;
   allow.raw_sockets = 0;
   exec.clean;
   exec.start = "/bin/sh /etc/rc";
   exec.stop = "/bin/sh /etc/rc.shutdown";
   mount.devfs;
   allow.set_hostname = 0;
   allow.sysvipc = 0;


Below this part, for each jail a block has to be added to
/etc/jail.conf:



   <jailname> {
       host.hostname = "<jailname>";
       path = "/jails/<jailname>";
       ip4.addr += "<ipaddress>/32";
       exec.system_user = "root";
       exec.jail_user = "root";
       exec.consolelog = "/var/log/jail_<jailname>_console.log";
   }


## Edit sshd_config

The host will have a sshd_config, and every jail will have one, too.
It is important that every instance of the sshd-server will listen
only to one ip address.

So, first change the /etc/ssh/sshd_config file of the host. Change the
line with the ListenAddress by uncommenting it and adding the ip
address of the host to it.

Every jail needs also the right listen address in the sshd_config file
of the jail.

## Create ip address alias for the jail

The configuration of the network interface has to be extended with the
ip address of the new jail. So you have to make an alias for it. This
can be done in /etc/rc.conf, but then either the network has to be
restarted or the alias must also be made manually with the ifconfig
command.

Below is a example line for the /etc/rc.conf file:



   ifconfig_re0_alias0="inet 192.168.1.100 netmask 0xffffff00"


## Start the jail

When the directories are populated and the config files are edited, we
can start the jail.

First we mount the nullfs mounts:



   mount -a


Now we can chroot into the jail directory and set the root password.



   cd /jails/<jailname>
   chroot .
   passwd
   exit


And start the jail:



   jail -c <jailname>


See if the jail is started, and add a user.



   jls
   jexec <jailnumber> /bin/sh
   ps aux
   adduser
   exit


## Script to create a new jail

When the /jails/master and /jails/skeleton is populated, the following
script can be used to create a new jail.



   #!/bin/sh

   ########
   # script to set up nullfs jail
   # $1 is name of jail
   # $2 is ip address of jail
   ########

   ### Check that two arguments are given ###
   if [ $# -ne 2 ]
     then
       echo "Not two arguments supplied!"
       echo "Usage: "
       echo "   $0 <jailname> <jail ip-address>"
       exit
   fi

   ### Setup jail files ###
   mkdir -p /jails/$1
   mkdir -p /jails/files/$1
   cpdup /jails/skeleton /jails/files/$1

   ### Configure sshd to listen only to jail ip address ###
   echo "ListenAddress $2" >> /jails/files/$1/etc/ssh/sshd_config

   ### Add jail to /etc/jail.conf ###
   cat << EOB | sed "s/JAILNAME/$1/g" | sed "s/IPADDRESS/$2/g" >> /etc/jail.conf
   JAILNAME {
       host.hostname = "JAILNAME.example.com";
       path = "/jails/JAILNAME";
       ip4.addr += "IPADDRESS/32";
       exec.system_user = "root";
       exec.jail_user = "root";
       exec.consolelog = "/var/log/jail_JAILNAME_console.log";
   }
   EOB

   ### Add jail file system to /etc/fstab ###
   cat << EOB1 | sed "s/JAILNAME/$1/g" | sed "s/IPADDRESS/$2/g"  >> /etc/fstab

   # JAILNAME
   /jails/master   /jails/JAILNAME    nullfs  ro      0       0
   /jails/files/JAILNAME /jails/JAILNAME/s       nullfs  rw      0       0

   EOB1

   cat << EOB2 | sed "s/JAILNAME/$1/g" > /jails/files/$1/etc/rc.conf
   hostname="JAILNAME.example.com"
   defaultrouter="192.168.1.254"
   sshd_enable="YES"
   sendmail_enable="NO"
   sendmail_submit_enable="NO"
   sendmail_outbound_enable="NO"
   sendmail_msp_queue_enable="NO"
   EOB2

   ### Get latest alias line and calculate next alias ###
   ### Will only work for alias 1 to alias 10         ###
   ### alias0 has to exist, or this will fail         ###
   p=`grep alias /etc/rc.conf | tail -n 1 | cut -d'=' -f 1 | tail -c 2 | sed 's/ //g'`
   p=`expr $p + 1`
   echo "ifconfig_re0_alias$p=\"inet $2 netmask 0xffffff00\"" >> /etc/rc.conf

   echo "end of script"
   echo ""
   echo "Proceed with the following steps"
   echo "*  mount -a (to mount jail file system)"
   echo ""
   echo "*  ifconfig re0 $2 netmask 255.255.255.0 alias (to set alias manualy)"
   echo "   (or restart network with: /etc/rc.d/netif restart && /etc/rc.d/routing restart)"
   echo ""
   echo "*  chroot to /jails/$1 and set root password and add a user"
   echo ""
   echo "*  jail -c $1 (to start the new jail $1)"


Have fun!


$Id: freebsd11jails.txt,v 1.4 2017/02/12 14:41:20 matto Exp $