GOPHERSPACE.DE - P H O X Y
gophering on box.matto.nl
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/ : 
      directory where files from skeleton are copied to 
    /jails/ : 
      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/`. 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/
    /jails/files/
    

The `/jails/` will be populated by a nullfs mount

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

### Populate /jails/files/

    
    
    cpdup /jails/skeleton /jails/files/
    

### Edit /etc/fstab

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

    
    
    /jails/master   /jails/    nullfs  ro      0       0
    /jails/files/ /jails//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:

    
    
     {
        host.hostname = "";
        path = "/jails/";
        ip4.addr += "/32";
        exec.system_user = "root";
        exec.jail_user = "root";
        exec.consolelog = "/var/log/jail__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/
    chroot .
    passwd
    exit
    

And start the jail:

    
    
    jail -c 
    

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

    
    
    jls
    jexec  /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  "
        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 $